111 lines
2.9 KiB
C++
111 lines
2.9 KiB
C++
|
|
#include "screenshot.h"
|
|
#include "file.h"
|
|
|
|
#define kTgaImageTypeUncompressedTrueColor 2
|
|
#define kTgaImageTypeRunLengthEncodedTrueColor 10
|
|
#define kTgaDirectionTop (1 << 5)
|
|
|
|
void saveTGA(const char *filename, const uint8_t *rgba, int w, int h) {
|
|
static const uint8_t kImageType = kTgaImageTypeRunLengthEncodedTrueColor;
|
|
File f;
|
|
if (f.open(filename, "wb", ".")) {
|
|
|
|
f.writeByte(0); // ID Length
|
|
f.writeByte(0); // ColorMap Type
|
|
f.writeByte(kImageType);
|
|
f.writeUint16LE(0); // ColorMap Start
|
|
f.writeUint16LE(0); // ColorMap Length
|
|
f.writeByte(0); // ColorMap Bits
|
|
f.writeUint16LE(0); // X-origin
|
|
f.writeUint16LE(0); // Y-origin
|
|
f.writeUint16LE(w); // Image Width
|
|
f.writeUint16LE(h); // Image Height
|
|
f.writeByte(24); // Pixel Depth
|
|
f.writeByte(kTgaDirectionTop); // Descriptor
|
|
|
|
if (kImageType == kTgaImageTypeUncompressedTrueColor) {
|
|
for (int i = 0; i < w * h; ++i) {
|
|
f.writeByte(rgba[0]);
|
|
f.writeByte(rgba[1]);
|
|
f.writeByte(rgba[2]);
|
|
rgba += 4;
|
|
}
|
|
} else {
|
|
assert(kImageType == kTgaImageTypeRunLengthEncodedTrueColor);
|
|
int prevColor = rgba[2] + (rgba[1] << 8) + (rgba[0] << 16); rgba += 4;
|
|
int count = 0;
|
|
for (int i = 1; i < w * h; ++i) {
|
|
int color = rgba[2] + (rgba[1] << 8) + (rgba[0] << 16); rgba += 4;
|
|
if (prevColor == color && count < 127) {
|
|
++count;
|
|
continue;
|
|
}
|
|
f.writeByte(count | 0x80);
|
|
f.writeByte((prevColor >> 16) & 255);
|
|
f.writeByte((prevColor >> 8) & 255);
|
|
f.writeByte( prevColor & 255);
|
|
count = 0;
|
|
prevColor = color;
|
|
}
|
|
if (count != 0) {
|
|
f.writeByte(count | 0x80);
|
|
f.writeByte((prevColor >> 16) & 255);
|
|
f.writeByte((prevColor >> 8) & 255);
|
|
f.writeByte( prevColor & 255);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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 f;
|
|
if (f.open(filename, "wb", ".")) {
|
|
const int alignWidth = (w + 3) & ~3;
|
|
const int imageSize = alignWidth * h;
|
|
|
|
// Write file header
|
|
f.writeUint16LE(TAG_BM);
|
|
f.writeUint32LE(14 + 40 + 4 * 256 + imageSize);
|
|
f.writeUint16LE(0); // reserved1
|
|
f.writeUint16LE(0); // reserved2
|
|
f.writeUint32LE(14 + 40 + 4 * 256);
|
|
|
|
// Write info header
|
|
f.writeUint32LE(40);
|
|
f.writeUint32LE(w);
|
|
f.writeUint32LE(h);
|
|
f.writeUint16LE(1); // planes
|
|
f.writeUint16LE(8); // bit_count
|
|
f.writeUint32LE(0); // compression
|
|
f.writeUint32LE(imageSize); // size_image
|
|
f.writeUint32LE(0); // x_pels_per_meter
|
|
f.writeUint32LE(0); // y_pels_per_meter
|
|
f.writeUint32LE(0); // num_colors_used
|
|
f.writeUint32LE(0); // num_colors_important
|
|
|
|
// Write palette data
|
|
for (int i = 0; i < 256; ++i) {
|
|
f.writeByte(pal[2]);
|
|
f.writeByte(pal[1]);
|
|
f.writeByte(pal[0]);
|
|
f.writeByte(0);
|
|
pal += 3;
|
|
}
|
|
|
|
// Write bitmap data
|
|
const int pitch = w;
|
|
bits += h * pitch;
|
|
for (int i = 0; i < h; ++i) {
|
|
bits -= pitch;
|
|
f.write(bits, w);
|
|
int pad = alignWidth - w;
|
|
while (pad--) {
|
|
f.writeByte(0);
|
|
}
|
|
}
|
|
}
|
|
}
|