From c6f7e557c5e4a57560b900bd96b42a77e384f7b7 Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sun, 8 Oct 2017 01:09:01 +1100 Subject: [PATCH] Fix mistakes in PNG decoder --- src/Client/Bitmap.c | 77 ++++++++++++++++++++---------------------- src/Client/Bitmap.h | 5 +++ src/Client/Constants.h | 1 + src/Client/Stream.c | 2 +- src/Client/Stream.h | 4 +-- 5 files changed, 45 insertions(+), 44 deletions(-) diff --git a/src/Client/Bitmap.c b/src/Client/Bitmap.c index 4e62408ea..1a6d695bc 100644 --- a/src/Client/Bitmap.c +++ b/src/Client/Bitmap.c @@ -65,20 +65,15 @@ void Png_CheckHeader(Stream* stream) { } } -void Png_Filter(UInt8 type, UInt8 bpp, UInt8* line, UInt8* prior, UInt32 lineLen) { - UInt32 i; - Int32 p, pa, pb, pc; - UInt8 a, b, c; - +void Png_Filter(UInt8 type, UInt8 bytesPerPixel, UInt8* line, UInt8* prior, UInt32 lineLen) { + UInt32 i, j; switch (type) { case PNG_FILTER_NONE: return; case PNG_FILTER_SUB: - a = 0; - for (i = 0; i < lineLen; i++) { - line[i] = (UInt8)(line[i] + a); - a = line[i]; + for (i = bytesPerPixel, j = 0; i < lineLen; i++, j++) { + line[i] = (UInt8)(line[i] + line[j]); } return; @@ -89,27 +84,26 @@ void Png_Filter(UInt8 type, UInt8 bpp, UInt8* line, UInt8* prior, UInt32 lineLen return; case PNG_FILTER_AVERAGE: - a = 0; - for (i = 0; i < lineLen; i++) { - line[i] = (UInt8)(line[i] + ((prior[i] + a) >> 1)); - a = line[i]; + for (i = 0; i < bytesPerPixel; i++) { + line[i] = (UInt8)(line[i] + (prior[i] >> 1)); + } + for (j = 0; i < lineLen; i++, j++) { + line[i] = (UInt8)(line[i] + ((prior[i] + line[j]) >> 1)); } return; case PNG_FILTER_PAETH: - a = 0; c = 0; - for (i = 0; i < lineLen; i++) { - b = prior[i]; - p = a + b - c; - pa = Math_AbsI(p - a); - pb = Math_AbsI(p - b); - pc = Math_AbsI(p - c); + /* TODO: verify this is right */ + for (i = bytesPerPixel, j = 0; i < lineLen; i++, j++) { + UInt8 a = line[j], b = prior[i], c = prior[j]; + 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) { line[i] = (UInt8)(line[i] + a); } else if (pb <= pc) { line[i] = (UInt8)(line[i] + b); } else { line[i] = (UInt8)(line[i] + c); } - - a = line[i]; c = b; } return; @@ -119,14 +113,14 @@ void Png_Filter(UInt8 type, UInt8 bpp, UInt8* line, UInt8* prior, UInt32 lineLen } } -void Png_Expand_GRAYSCALE(UInt8 bpp, Int32 width, UInt32* palette, UInt8* src, UInt32* dst) { +void Png_Expand_GRAYSCALE(UInt8 bitsPerSample, Int32 width, UInt32* palette, UInt8* src, UInt32* dst) { Int32 i, j; #define PNG_DO_GRAYSCALE(tmp, dstI, srcI, scale) tmp = src[srcI] * scale; dst[dstI] = PackedCol_ARGB(tmp, tmp, tmp, 255); #define PNG_DO_GRAYSCALE_X(tmp, dstI, srcI) tmp = src[srcI]; dst[dstI] = PackedCol_ARGB(tmp, tmp, tmp, 255); UInt8 cur, rgb1, rgb2, rgb3, rgb4; Int32 mask; - switch (bpp) { + switch (bitsPerSample) { case 1: for (i = 0, j = 0; i < (width & ~0x7); i += 8, j++) { cur = src[j]; @@ -174,12 +168,12 @@ void Png_Expand_GRAYSCALE(UInt8 bpp, Int32 width, UInt32* palette, UInt8* src, U } } -void Png_Expand_RGB(UInt8 bpp, Int32 width, UInt32* palette, UInt8* src, UInt32* dst) { +void Png_Expand_RGB(UInt8 bitsPerSample, Int32 width, UInt32* palette, UInt8* src, UInt32* dst) { Int32 i, j; #define PNG_DO_RGB__8(dstI, srcI) dst[dstI] = PackedCol_ARGB(src[srcI], src[srcI + 1], src[srcI + 2], 255); #define PNG_DO_RGB_16(dstI, srcI) dst[dstI] = PackedCol_ARGB(src[srcI], src[srcI + 3], src[srcI + 5], 255); - if (bpp == 8) { + if (bitsPerSample == 8) { for (i = 0, j = 0; i < (width & ~0x03); i += 4, j += 12) { PNG_DO_RGB__8(i , j ); PNG_DO_RGB__8(i + 1, j + 3); PNG_DO_RGB__8(i + 2, j + 6); PNG_DO_RGB__8(i + 3, j + 9); @@ -190,13 +184,13 @@ void Png_Expand_RGB(UInt8 bpp, Int32 width, UInt32* palette, UInt8* src, UInt32* } } -void Png_Expand_INDEXED(UInt8 bpp, Int32 width, UInt32* palette, UInt8* src, UInt32* dst) { +void Png_Expand_INDEXED(UInt8 bitsPerSample, Int32 width, UInt32* palette, UInt8* src, UInt32* dst) { Int32 i, j; #define PNG_DO_INDEXED(dstI, srcI) dst[dstI] = palette[srcI]; UInt8 cur; Int32 mask; - switch (bpp) { + switch (bitsPerSample) { case 1: for (i = 0, j = 0; i < (width & ~0x7); i += 8, j++) { cur = src[j]; @@ -241,13 +235,13 @@ void Png_Expand_INDEXED(UInt8 bpp, Int32 width, UInt32* palette, UInt8* src, UIn } } -void Png_Expand_GRAYSCALE_A(UInt8 bpp, Int32 width, UInt32* palette, UInt8* src, UInt32* dst) { +void Png_Expand_GRAYSCALE_A(UInt8 bitsPerSample, Int32 width, UInt32* palette, UInt8* src, UInt32* dst) { Int32 i, j; #define PNG_DO_GRAYSCALE_A__8(tmp, dstI, srcI) tmp = src[srcI]; dst[dstI] = PackedCol_ARGB(tmp, tmp, tmp, src[srcI + 1]); #define PNG_DO_GRAYSCALE_A_16(tmp, dstI, srcI) tmp = src[srcI]; dst[dstI] = PackedCol_ARGB(tmp, tmp, tmp, src[srcI + 2]); UInt8 rgb1, rgb2, rgb3, rgb4; - if (bpp == 8) { + if (bitsPerSample == 8) { for (i = 0, j = 0; i < (width & ~0x3); i += 4, j += 8) { PNG_DO_GRAYSCALE_A__8(rgb1, i , j ); PNG_DO_GRAYSCALE_A__8(rgb2, i + 1, j + 2); PNG_DO_GRAYSCALE_A__8(rgb3, i + 2, j + 4); PNG_DO_GRAYSCALE_A__8(rgb4, i + 3, j + 5); @@ -258,12 +252,12 @@ void Png_Expand_GRAYSCALE_A(UInt8 bpp, Int32 width, UInt32* palette, UInt8* src, } } -void Png_Expand_RGB_A(UInt8 bpp, Int32 width, UInt32* palette, UInt8* src, UInt32* dst) { +void Png_Expand_RGB_A(UInt8 bitsPerSample, Int32 width, UInt32* palette, UInt8* src, UInt32* dst) { Int32 i, j; #define PNG_DO_RGB_A__8(dstI, srcI) dst[dstI] = PackedCol_ARGB(src[srcI], src[srcI + 1], src[srcI + 2], src[srcI + 3]); #define PNG_DO_RGB_A_16(dstI, srcI) dst[dstI] = PackedCol_ARGB(src[srcI], src[srcI + 3], src[srcI + 5], src[srcI + 7]); - if (bpp == 8) { + if (bitsPerSample == 8) { for (i = 0, j = 0; i < (width & ~0x3); i += 4, j += 16) { PNG_DO_RGB_A__8(i , j ); PNG_DO_RGB_A__8(i + 1, j + 4 ); PNG_DO_RGB_A__8(i + 2, j + 8 ); PNG_DO_RGB_A__8(i + 3, j + 12); @@ -287,12 +281,13 @@ void Png_ComputeTransparency(Bitmap* bmp, UInt32 transparentCol) { } } +/* TODO: Test a lot of .png files and ensure output is right */ #define PNG_MAX_DIMS 0x10000L void Bitmap_DecodePng(Bitmap* bmp, Stream* stream) { Png_CheckHeader(stream); Bitmap_Create(bmp, 0, 0, NULL); UInt32 transparentCol = PackedCol_ARGB(0, 0, 0, 255); - UInt8 col, bpp; + UInt8 col, bitsPerSample, bytesPerPixel; Png_RowExpander rowExpander; UInt32 palette[PNG_PALETTE]; @@ -329,19 +324,20 @@ void Bitmap_DecodePng(Bitmap* bmp, Stream* stream) { bmp->Scan0 = Platform_MemAlloc(Bitmap_DataSize(bmp->Width, bmp->Height)); if (bmp->Scan0 == NULL) ErrorHandler_Fail("Failed to allocate memory for PNG bitmap"); - bpp = Stream_ReadUInt8(stream); - if (bpp > 16 || !Math_IsPowOf2(bpp)) ErrorHandler_Fail("PNG has invalid bits per pixel"); + bitsPerSample = Stream_ReadUInt8(stream); + if (bitsPerSample > 16 || !Math_IsPowOf2(bitsPerSample)) ErrorHandler_Fail("PNG has invalid bits per pixel"); col = Stream_ReadUInt8(stream); if (col == 1 || col == 3 || col > 6) ErrorHandler_Fail("PNG has invalid colour type"); - if (bpp < 8 && (col >= PNG_COL_RGB && col != PNG_COL_INDEXED)) ErrorHandler_Fail("PNG has invalid bpp for this colour type"); - if (bpp == 16 && col == PNG_COL_INDEXED) ErrorHandler_Fail("PNG has invalid bpp for this colour type"); + if (bitsPerSample < 8 && (col >= PNG_COL_RGB && col != PNG_COL_INDEXED)) ErrorHandler_Fail("PNG has invalid bpp for this colour type"); + if (bitsPerSample == 16 && col == PNG_COL_INDEXED) ErrorHandler_Fail("PNG has invalid bpp for this colour type"); if (Stream_ReadUInt8(stream) != 0) ErrorHandler_Fail("PNG compression method must be DEFLATE"); if (Stream_ReadUInt8(stream) != 0) ErrorHandler_Fail("PNG filter method must be ADAPTIVE"); if (Stream_ReadUInt8(stream) != 0) ErrorHandler_Fail("PNG interlacing not supported"); UInt32 samplesPerPixel[7] = { 1,0,3,1,2,0,4 }; - scanlineSize = ((samplesPerPixel[col] * bpp * bmp->Width) + 7) >> 3; + scanlineSize = ((samplesPerPixel[col] * bitsPerSample * bmp->Width) + 7) >> 3; + bytesPerPixel = ((samplesPerPixel[col] * bitsPerSample) + 7) >> 3; Platform_MemSet(scanlineA, 0, scanlineSize + 1); /* Prior row should be 0 per PNG spec */ switch (col) { @@ -406,7 +402,6 @@ void Bitmap_DecodePng(Bitmap* bmp, Stream* stream) { while (scanlineY < bmp->Height) { UInt32 toRead = scanlineBytes - scanlineIndex, read; UInt8* scanline = (scanlineY & 1) == 0 ? scanlineB : scanlineA; - UInt32 pos = Platform_FilePosition(stream->Data); ReturnCode code = compStream.Read(&compStream, &scanline[scanlineIndex], toRead, &read); if (code != 0) ErrorHandler_FailWithCode(code, "PNG - reading image bulk data"); @@ -414,8 +409,8 @@ void Bitmap_DecodePng(Bitmap* bmp, Stream* stream) { scanlineIndex += read; if (scanlineIndex == scanlineBytes) { UInt8* prior = (scanlineY & 1) == 0 ? scanlineA : scanlineB; - Png_Filter(scanline[0], bpp, &scanline[1], &prior[1], scanlineSize); - rowExpander(bpp, bmp->Width, palette, &scanline[1], Bitmap_GetRow(bmp, scanlineY)); + Png_Filter(scanline[0], bytesPerPixel, &scanline[1], &prior[1], scanlineSize); + rowExpander(bitsPerSample, bmp->Width, palette, &scanline[1], Bitmap_GetRow(bmp, scanlineY)); scanlineIndex = 0; scanlineY++; diff --git a/src/Client/Bitmap.h b/src/Client/Bitmap.h index a4e3fb7c6..5ce8f5311 100644 --- a/src/Client/Bitmap.h +++ b/src/Client/Bitmap.h @@ -33,6 +33,11 @@ void Bitmap_CopyBlock(Int32 srcX, Int32 srcY, Int32 dstX, Int32 dstY, Bitmap* sr void Bitmap_CopyRow(Int32 srcY, Int32 dstY, Bitmap* src, Bitmap* dst, Int32 width); /* Allocates a new bitmap of the given dimensions. You are responsible for freeing its memory! */ void Bitmap_Allocate(Bitmap* bmp, Int32 width, Int32 height); +/* + Partially based off information from + https://handmade.network/forums/wip/t/2363-implementing_a_basic_png_reader_the_handmade_way + https://github.com/nothings/stb/blob/master/stb_image.h +*/ /* Decodes a PNG bitmap from the given stream. */ void Bitmap_DecodePng(Bitmap* bmp, Stream* stream); #endif \ No newline at end of file diff --git a/src/Client/Constants.h b/src/Client/Constants.h index ea0d84c87..34aaae155 100644 --- a/src/Client/Constants.h +++ b/src/Client/Constants.h @@ -10,6 +10,7 @@ /* Max number of characters strings can have. */ #define STRING_SIZE 64 +#define FILENAME_SIZE 260 /* Chunk axis length, in blocks. */ #define CHUNK_SIZE 16 diff --git a/src/Client/Stream.c b/src/Client/Stream.c index a530d02f1..75e174481 100644 --- a/src/Client/Stream.c +++ b/src/Client/Stream.c @@ -50,7 +50,7 @@ Int32 Stream_TryReadByte(Stream* stream) { void Stream_SetName(Stream* stream, STRING_TRANSIENT String* name) { - stream->Name = String_FromRawBuffer(stream->NameBuffer, STREAM_NAME_LEN); + stream->Name = String_FromRawBuffer(stream->NameBuffer, FILENAME_SIZE); String_AppendString(&stream->Name, name); } diff --git a/src/Client/Stream.h b/src/Client/Stream.h index 86b883ac7..31d27e3e2 100644 --- a/src/Client/Stream.h +++ b/src/Client/Stream.h @@ -3,6 +3,7 @@ #include "Typedefs.h" #include "String.h" #include "ErrorHandler.h" +#include "Constants.h" /* Defines an abstract way of reading and writing data in a streaming manner. Also provides common helper methods for reading/writing data to/from streams. Copyright 2017 ClassicalSharp | Licensed under BSD-3 @@ -11,7 +12,6 @@ #define STREAM_SEEKFROM_BEGIN 0 #define STREAM_SEEKFROM_CURRENT 1 #define STREAM_SEEKFROM_END 2 -#define STREAM_NAME_LEN 256 typedef ReturnCode (*Stream_Operation)(struct Stream_* stream, UInt8* data, UInt32 count, UInt32* modified); typedef ReturnCode (*Stream_Seek)(struct Stream_* stream, Int32 offset, Int32 seekType); @@ -32,7 +32,7 @@ typedef struct Stream_ { /* General purpose numerical metadata for the stream. */ UInt32 Data2; /* Raw name buffer */ - UInt8 NameBuffer[String_BufferSize(STREAM_NAME_LEN)]; + UInt8 NameBuffer[String_BufferSize(FILENAME_SIZE)]; /* The name of the stream. */ String Name; } Stream;