mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-15 10:35:11 -04:00
Working screenshot save for C client (still very slow)
This commit is contained in:
parent
564a2ea51b
commit
f7210f8f77
@ -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 |
Loading…
x
Reference in New Issue
Block a user