mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-17 19:45:23 -04:00
Fix mistakes in PNG decoder
This commit is contained in:
parent
75f031fd90
commit
c6f7e557c5
@ -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++;
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user