forked from cyxx/rawgl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
screenshot.cpp
131 lines (115 loc) · 3.35 KB
/
screenshot.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#include <assert.h>
#include "screenshot.h"
#include "file.h"
static void TO_LE16(uint8_t *dst, uint16_t value) {
for (int i = 0; i < 2; ++i) {
dst[i] = value & 255;
value >>= 8;
}
}
#define kTgaImageTypeUncompressedTrueColor 2
#define kTgaImageTypeRunLengthEncodedTrueColor 10
#define kTgaDirectionTop (1 << 5)
static const int TGA_HEADER_SIZE = 18;
void saveTGA(const char *filename, const uint16_t *rgb555, int w, int h) {
static const uint8_t kImageType = kTgaImageTypeRunLengthEncodedTrueColor;
uint8_t buffer[TGA_HEADER_SIZE];
buffer[0] = 0; // ID Length
buffer[1] = 0; // ColorMap Type
buffer[2] = kImageType;
TO_LE16(buffer + 3, 0); // ColorMap Start
TO_LE16(buffer + 5, 0); // ColorMap Length
buffer[7] = 0; // ColorMap Bits
TO_LE16(buffer + 8, 0); // X-origin
TO_LE16(buffer + 10, 0); // Y-origin
TO_LE16(buffer + 12, w); // Image Width
TO_LE16(buffer + 14, h); // Image Height
buffer[16] = 16; // Pixel Depth
buffer[17] = kTgaDirectionTop; // Descriptor
File f;
if (f.openForWriting(filename)) {
f.write(buffer, sizeof(buffer));
if (kImageType == kTgaImageTypeUncompressedTrueColor) {
for (int i = 0; i < w * h; ++i) {
const uint16_t color = *rgb555++;
f.writeByte(color & 255);
f.writeByte(color >> 8);
}
} else {
assert(kImageType == kTgaImageTypeRunLengthEncodedTrueColor);
uint16_t prevColor = *rgb555++;
int count = 0;
for (int i = 1; i < w * h; ++i) {
const uint16_t color = *rgb555++;
if (prevColor == color && count < 127) {
++count;
continue;
}
f.writeByte(count | 0x80);
f.writeByte(prevColor & 255);
f.writeByte(prevColor >> 8);
count = 0;
prevColor = color;
}
if (count != 0) {
f.writeByte(count | 0x80);
f.writeByte(prevColor & 255);
f.writeByte(prevColor >> 8);
}
}
}
}
static void fwriteUint16LE(FILE *fp, uint16_t n) {
fputc(n & 0xFF, fp);
fputc(n >> 8, fp);
}
static void fwriteUint32LE(FILE *fp, uint32_t n) {
fwriteUint16LE(fp, n & 0xFFFF);
fwriteUint16LE(fp, n >> 16);
}
static const uint16_t TAG_BM = 0x4D42;
void saveBMP(const char *filename, const uint8_t *bits, const uint8_t *pal, int w, int h) {
FILE *fp = fopen(filename, "wb");
if (fp) {
int alignWidth = (w + 3) & ~3;
int imageSize = alignWidth * h;
// Write file header
fwriteUint16LE(fp, TAG_BM);
fwriteUint32LE(fp, 14 + 40 + 4 * 256 + imageSize);
fwriteUint16LE(fp, 0); // reserved1
fwriteUint16LE(fp, 0); // reserved2
fwriteUint32LE(fp, 14 + 40 + 4 * 256);
// Write info header
fwriteUint32LE(fp, 40);
fwriteUint32LE(fp, w);
fwriteUint32LE(fp, h);
fwriteUint16LE(fp, 1); // planes
fwriteUint16LE(fp, 8); // bit_count
fwriteUint32LE(fp, 0); // compression
fwriteUint32LE(fp, imageSize); // size_image
fwriteUint32LE(fp, 0); // x_pels_per_meter
fwriteUint32LE(fp, 0); // y_pels_per_meter
fwriteUint32LE(fp, 0); // num_colors_used
fwriteUint32LE(fp, 0); // num_colors_important
// Write palette data
for (int i = 0; i < 256; ++i) {
fputc(pal[2], fp);
fputc(pal[1], fp);
fputc(pal[0], fp);
fputc(0, fp);
pal += 3;
}
// Write bitmap data
const int pitch = w;
bits += h * pitch;
for (int i = 0; i < h; ++i) {
bits -= pitch;
fwrite(bits, w, 1, fp);
int pad = alignWidth - w;
while (pad--) {
fputc(0, fp);
}
}
fclose(fp);
}
}