Working screenshot save for C client (still very slow)

This commit is contained in:
UnknownShadow200 2018-05-03 15:53:18 +10:00
parent 564a2ea51b
commit f7210f8f77
3 changed files with 104 additions and 30 deletions

View File

@ -49,17 +49,15 @@ void Bitmap_AllocateClearedPow2(Bitmap* bmp, Int32 width, Int32 height) {
#define PNG_PALETTE 256 #define PNG_PALETTE 256
#define PNG_FourCC(a, b, c, d) ((UInt32)a << 24) | ((UInt32)b << 16) | ((UInt32)c << 8) | (UInt32)d #define PNG_FourCC(a, b, c, d) ((UInt32)a << 24) | ((UInt32)b << 16) | ((UInt32)c << 8) | (UInt32)d
#define PNG_COL_GRAYSCALE 0 enum PNG_COL {
#define PNG_COL_RGB 2 PNG_COL_GRAYSCALE = 0, PNG_COL_RGB = 2, PNG_COL_INDEXED = 3,
#define PNG_COL_INDEXED 3 PNG_COL_GRAYSCALE_A = 4, PNG_COL_RGB_A = 6,
#define PNG_COL_GRAYSCALE_A 4 };
#define PNG_COL_RGB_A 6
#define PNG_FILTER_NONE 0 enum PNG_FILTER {
#define PNG_FILTER_SUB 1 PNG_FILTER_NONE, PNG_FILTER_SUB, PNG_FILTER_UP,
#define PNG_FILTER_UP 2 PNG_FILTER_AVERAGE, PNG_FILTER_PAETH
#define PNG_FILTER_AVERAGE 3 };
#define PNG_FILTER_PAETH 4
typedef void(*Png_RowExpander)(UInt8 bpp, Int32 width, UInt32* palette, UInt8* src, UInt32* dst); typedef void(*Png_RowExpander)(UInt8 bpp, Int32 width, UInt32* palette, UInt8* src, UInt32* dst);
UInt8 png_sig[PNG_SIG_SIZE] = { 137, 80, 78, 71, 13, 10, 26, 10 }; UInt8 png_sig[PNG_SIG_SIZE] = { 137, 80, 78, 71, 13, 10, 26, 10 };
@ -73,7 +71,7 @@ void Png_CheckHeader(Stream* stream) {
} }
} }
void Png_Filter(UInt8 type, UInt8 bytesPerPixel, UInt8* line, UInt8* prior, UInt32 lineLen) { void Png_Reconstruct(UInt8 type, UInt8 bytesPerPixel, UInt8* line, UInt8* prior, UInt32 lineLen) {
UInt32 i, j; UInt32 i, j;
switch (type) { switch (type) {
case PNG_FILTER_NONE: case PNG_FILTER_NONE:
@ -81,22 +79,22 @@ void Png_Filter(UInt8 type, UInt8 bytesPerPixel, UInt8* line, UInt8* prior, UInt
case PNG_FILTER_SUB: case PNG_FILTER_SUB:
for (i = bytesPerPixel, j = 0; i < lineLen; i++, j++) { for (i = bytesPerPixel, j = 0; i < lineLen; i++, j++) {
line[i] = (UInt8)(line[i] + line[j]); line[i] += line[j];
} }
return; return;
case PNG_FILTER_UP: case PNG_FILTER_UP:
for (i = 0; i < lineLen; i++) { for (i = 0; i < lineLen; i++) {
line[i] = (UInt8)(line[i] + prior[i]); line[i] += prior[i];
} }
return; return;
case PNG_FILTER_AVERAGE: case PNG_FILTER_AVERAGE:
for (i = 0; i < bytesPerPixel; i++) { for (i = 0; i < bytesPerPixel; i++) {
line[i] = (UInt8)(line[i] + (prior[i] >> 1)); line[i] += (prior[i] >> 1);
} }
for (j = 0; i < lineLen; i++, j++) { for (j = 0; i < lineLen; i++, j++) {
line[i] = (UInt8)(line[i] + ((prior[i] + line[j]) >> 1)); line[i] += ((prior[i] + line[j]) >> 1);
} }
return; return;
@ -109,9 +107,9 @@ void Png_Filter(UInt8 type, UInt8 bytesPerPixel, UInt8* line, UInt8* prior, UInt
Int32 pb = Math_AbsI(p - b); Int32 pb = Math_AbsI(p - b);
Int32 pc = Math_AbsI(p - c); Int32 pc = Math_AbsI(p - c);
if (pa <= pb && pa <= pc) { line[i] = (UInt8)(line[i] + a); } if (pa <= pb && pa <= pc) { line[i] += a; }
else if (pb <= pc) { line[i] = (UInt8)(line[i] + b); } else if (pb <= pc) { line[i] += b; }
else { line[i] = (UInt8)(line[i] + c); } else { line[i] += c; }
} }
return; return;
@ -430,7 +428,7 @@ void Bitmap_DecodePng(Bitmap* bmp, Stream* stream) {
UInt8* prior = &buffer[scanlineIndices[0]]; UInt8* prior = &buffer[scanlineIndices[0]];
UInt8* scanline = &buffer[scanlineIndices[1]]; UInt8* scanline = &buffer[scanlineIndices[1]];
Png_Filter(scanline[0], bytesPerPixel, &scanline[1], &prior[1], scanlineSize); Png_Reconstruct(scanline[0], bytesPerPixel, &scanline[1], &prior[1], scanlineSize);
rowExpander(bitsPerSample, bmp->Width, palette, &scanline[1], Bitmap_GetRow(bmp, curY)); rowExpander(bitsPerSample, bmp->Width, palette, &scanline[1], Bitmap_GetRow(bmp, curY));
curY++; curY++;
@ -491,14 +489,91 @@ void Bitmap_Crc32Stream(Stream* stream, Stream* underlying) {
stream->Close = Stream_UnsupportedClose; stream->Close = Stream_UnsupportedClose;
} }
void Bitmap_EncodePngRow(UInt8* src, UInt8* cur, UInt8* prev, UInt8* best, Int32 width) { void Png_Filter(UInt8 filter, UInt8* cur, UInt8* prior, UInt8* best, Int32 lineLen) {
UInt8* dst = best + 1; /* 3 bytes per pixel constant */
Int32 i;
switch (filter) {
case PNG_FILTER_NONE:
Platform_MemCpy(best, cur, lineLen);
break;
case PNG_FILTER_SUB:
best[0] = cur[0]; best[1] = cur[1]; best[2] = cur[2];
for (i = 3; i < lineLen; i++) {
best[i] = cur[i] - cur[i - 3];
}
break;
case PNG_FILTER_UP:
for (i = 0; i < lineLen; i++) {
best[i] = cur[i] - prior[i];
}
break;
case PNG_FILTER_AVERAGE:
best[0] = cur[0] - (prior[0] >> 1);
best[1] = cur[1] - (prior[1] >> 1);
best[2] = cur[2] - (prior[2] >> 1);
for (i = 3; i < lineLen; i++) {
best[i] = cur[i] - ((prior[i] + cur[i - 3]) >> 1);
}
break;
case PNG_FILTER_PAETH:
best[0] = cur[0] - prior[0];
best[1] = cur[1] - prior[1];
best[2] = cur[2] - prior[2];
for (i = 3; i < lineLen; i++) {
UInt8 a = cur[i - 3], b = prior[i], c = prior[i - 3];
Int32 p = a + b - c;
Int32 pa = Math_AbsI(p - a);
Int32 pb = Math_AbsI(p - b);
Int32 pc = Math_AbsI(p - c);
if (pa <= pb && pa <= pc) { best[i] = cur[i] - a; }
else if (pb <= pc) { best[i] = cur[i] - b; }
else { best[i] = cur[i] - c; }
}
break;
}
}
static int counter;
void Png_EncodeRow(UInt8* src, UInt8* cur, UInt8* prior, UInt8* best, Int32 lineLen) {
UInt8* dst = cur;
Int32 x; Int32 x;
for (x = 0; x < width; x++) { for (x = 0; x < lineLen; x += 3) {
dst[0] = src[2]; dst[1] = src[1]; dst[2] = src[0]; dst[0] = src[2]; dst[1] = src[1]; dst[2] = src[0];
src += 4; dst += 3; src += 4; dst += 3;
} }
dst[0] = 0; /* filter type 0 */
/* Waste of time checking the PNG_NONE filter */
Int32 filter, bestFilter = PNG_FILTER_SUB, bestEstimate = Int32_MaxValue;
dst = best + 1;
for (filter = PNG_FILTER_SUB; filter <= PNG_FILTER_PAETH; filter++) {
Png_Filter(filter, cur, prior, dst, lineLen);
/* Estimate how well this filtered line will compress */
/* Uses simple sum of magnitude of each byte in the line */
Int32 estimate = 0;
for (x = 0; x < lineLen; x++) {
estimate += Math_AbsI((Int8)dst[x]);
}
if (estimate > bestEstimate) continue;
bestEstimate = estimate;
bestFilter = filter;
}
/* Since this filter is last checked, can avoid running it twice */
if (bestFilter != PNG_FILTER_PAETH) {
Png_Filter(bestFilter, cur, prior, dst, lineLen);
}
best[0] = bestFilter;
} }
void Bitmap_EncodePng(Bitmap* bmp, Stream* stream) { void Bitmap_EncodePng(Bitmap* bmp, Stream* stream) {
@ -533,9 +608,8 @@ void Bitmap_EncodePng(Bitmap* bmp, Stream* stream) {
stream = &crc32Stream; stream = &crc32Stream;
Bitmap_Crc32Stream(&crc32Stream, underlying); Bitmap_Crc32Stream(&crc32Stream, underlying);
Stream_WriteU32_BE(stream, PNG_FourCC('I', 'D', 'A', 'T')); Stream_WriteU32_BE(stream, PNG_FourCC('I', 'D', 'A', 'T'));
UInt32 start = Platform_FilePosition(underlying->Meta_File);
{ {
Int32 y, lineSize = bmp->Width * 3 + 1; Int32 y, lineSize = bmp->Width * 3;
ZLibState zlState; ZLibState zlState;
Stream zlStream; Stream zlStream;
ZLib_MakeStream(&zlStream, &zlState, stream); ZLib_MakeStream(&zlStream, &zlState, stream);
@ -544,12 +618,12 @@ void Bitmap_EncodePng(Bitmap* bmp, Stream* stream) {
UInt8* src = (UInt8*)Bitmap_GetRow(bmp, y); UInt8* src = (UInt8*)Bitmap_GetRow(bmp, y);
UInt8* prev = (y & 1) == 0 ? prevLine : curLine; UInt8* prev = (y & 1) == 0 ? prevLine : curLine;
UInt8* cur = (y & 1) == 0 ? curLine : prevLine; UInt8* cur = (y & 1) == 0 ? curLine : prevLine;
Bitmap_EncodePngRow(src, cur, prev, bestLine, bmp->Width); Png_EncodeRow(src, cur, prev, bestLine, lineSize);
Stream_Write(&zlStream, bestLine, lineSize); Stream_Write(&zlStream, bestLine, lineSize + 1); /* +1 for filter byte */
} }
zlStream.Close(&zlStream); zlStream.Close(&zlStream);
} }
UInt32 end = Platform_FilePosition(underlying->Meta_File); UInt32 dataEnd = Platform_FilePosition(underlying->Meta_File);
stream = underlying; stream = underlying;
Stream_WriteU32_BE(stream, crc32Stream.Meta_CRC32 ^ 0xFFFFFFFFUL); Stream_WriteU32_BE(stream, crc32Stream.Meta_CRC32 ^ 0xFFFFFFFFUL);
@ -559,7 +633,7 @@ void Bitmap_EncodePng(Bitmap* bmp, Stream* stream) {
Stream_WriteU32_BE(stream, 0xAE426082UL); /* crc32 of iend */ Stream_WriteU32_BE(stream, 0xAE426082UL); /* crc32 of iend */
/* Come back to write size of data chunk */ /* Come back to write size of data chunk */
ReturnCode result = stream->Seek(stream, 25, STREAM_SEEKFROM_BEGIN); ReturnCode result = stream->Seek(stream, 33, STREAM_SEEKFROM_BEGIN);
ErrorHandler_CheckOrFail(result, "PNG - seeking to write data size"); ErrorHandler_CheckOrFail(result, "PNG - seeking to write data size");
Stream_WriteU32_BE(stream, end - start); Stream_WriteU32_BE(stream, dataEnd - 41);
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB