mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-01-22 17:12:25 -05:00
PNG decoding: Slightly optimise decoding first row, drop support for 16 bpp images
This commit is contained in:
parent
53225568af
commit
92b2db3ce1
3 changed files with 40 additions and 43 deletions
81
src/Bitmap.c
81
src/Bitmap.c
|
@ -88,12 +88,35 @@ cc_bool Png_Detect(const cc_uint8* data, cc_uint32 len) {
|
|||
|
||||
/* 9 Filtering */
|
||||
/* 13.9 Filtering */
|
||||
static void Png_Reconstruct(cc_uint8 type, cc_uint8 bytesPerPixel, cc_uint8* line, cc_uint8* prior, cc_uint32 lineLen) {
|
||||
static void Png_ReconstructFirst(cc_uint8 type, cc_uint8 bytesPerPixel, cc_uint8* line, cc_uint32 lineLen) {
|
||||
/* First scanline is a special case, where all values in prior array are 0 */
|
||||
cc_uint32 i, j;
|
||||
|
||||
switch (type) {
|
||||
case PNG_FILTER_NONE:
|
||||
case PNG_FILTER_SUB:
|
||||
for (i = bytesPerPixel, j = 0; i < lineLen; i++, j++) {
|
||||
line[i] += line[j];
|
||||
}
|
||||
return;
|
||||
|
||||
case PNG_FILTER_AVERAGE:
|
||||
for (i = bytesPerPixel, j = 0; i < lineLen; i++, j++) {
|
||||
line[i] += (line[j] >> 1);
|
||||
}
|
||||
return;
|
||||
|
||||
case PNG_FILTER_PAETH:
|
||||
for (i = bytesPerPixel, j = 0; i < lineLen; i++, j++) {
|
||||
line[i] += line[j];
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void Png_Reconstruct(cc_uint8 type, cc_uint8 bytesPerPixel, cc_uint8* line, cc_uint8* prior, cc_uint32 lineLen) {
|
||||
cc_uint32 i, j;
|
||||
|
||||
switch (type) {
|
||||
case PNG_FILTER_SUB:
|
||||
for (i = bytesPerPixel, j = 0; i < lineLen; i++, j++) {
|
||||
line[i] += line[j];
|
||||
|
@ -181,13 +204,6 @@ static void Png_Expand_GRAYSCALE_8(int width, BitmapCol* palette, cc_uint8* src,
|
|||
for (; i < width; i++) { PNG_Do_Grayscale_8(i, i); }
|
||||
}
|
||||
|
||||
static void Png_Expand_GRAYSCALE_16(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) {
|
||||
int i; cc_uint8 rgb; /* NOTE: not optimised */
|
||||
for (i = 0; i < width; i++) {
|
||||
rgb = src[i * 2]; Bitmap_Set(dst[i], rgb, rgb, rgb, 255);
|
||||
}
|
||||
}
|
||||
|
||||
static void Png_Expand_RGB_8(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) {
|
||||
int i, j;
|
||||
|
||||
|
@ -198,13 +214,6 @@ static void Png_Expand_RGB_8(int width, BitmapCol* palette, cc_uint8* src, Bitma
|
|||
for (; i < width; i++, j += 3) { PNG_Do_RGB__8(i, j); }
|
||||
}
|
||||
|
||||
static void Png_Expand_RGB_16(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) {
|
||||
int i, j; /* NOTE: not optimised */
|
||||
for (i = 0, j = 0; i < width; i++, j += 6) {
|
||||
Bitmap_Set(dst[i], src[j], src[j + 2], src[j + 4], 255);
|
||||
}
|
||||
}
|
||||
|
||||
static void Png_Expand_INDEXED_1(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) {
|
||||
int i; /* NOTE: not optimised */
|
||||
for (i = 0; i < width; i++) { dst[i] = palette[PNG_Get__1(i)]; }
|
||||
|
@ -247,13 +256,6 @@ static void Png_Expand_GRAYSCALE_A_8(int width, BitmapCol* palette, cc_uint8* sr
|
|||
for (; i < width; i++, j += 2) { PNG_Do_Grayscale_A__8(i, j); }
|
||||
}
|
||||
|
||||
static void Png_Expand_GRAYSCALE_A_16(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) {
|
||||
int i; cc_uint8 rgb; /* NOTE: not optimised*/
|
||||
for (i = 0; i < width; i++) {
|
||||
rgb = src[i * 4]; Bitmap_Set(dst[i], rgb, rgb, rgb, src[i * 4 + 2]);
|
||||
}
|
||||
}
|
||||
|
||||
static void Png_Expand_RGB_A_8(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) {
|
||||
int i, j;
|
||||
|
||||
|
@ -264,13 +266,6 @@ static void Png_Expand_RGB_A_8(int width, BitmapCol* palette, cc_uint8* src, Bit
|
|||
for (; i < width; i++, j += 4) { PNG_Do_RGB_A__8(i, j); }
|
||||
}
|
||||
|
||||
static void Png_Expand_RGB_A_16(int width, BitmapCol* palette, cc_uint8* src, BitmapCol* dst) {
|
||||
int i, j; /* NOTE: not optimised*/
|
||||
for (i = 0, j = 0; i < width; i++, j += 8) {
|
||||
Bitmap_Set(dst[i], src[j], src[j + 2], src[j + 4], src[j + 6]);
|
||||
}
|
||||
}
|
||||
|
||||
static Png_RowExpander Png_GetExpander(cc_uint8 col, cc_uint8 bitsPerSample) {
|
||||
switch (col) {
|
||||
case PNG_COLOR_GRAYSCALE:
|
||||
|
@ -279,14 +274,12 @@ static Png_RowExpander Png_GetExpander(cc_uint8 col, cc_uint8 bitsPerSample) {
|
|||
case 2: return Png_Expand_GRAYSCALE_2;
|
||||
case 4: return Png_Expand_GRAYSCALE_4;
|
||||
case 8: return Png_Expand_GRAYSCALE_8;
|
||||
case 16: return Png_Expand_GRAYSCALE_16;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
case PNG_COLOR_RGB:
|
||||
switch (bitsPerSample) {
|
||||
case 8: return Png_Expand_RGB_8;
|
||||
case 16: return Png_Expand_RGB_16;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
|
@ -302,14 +295,12 @@ static Png_RowExpander Png_GetExpander(cc_uint8 col, cc_uint8 bitsPerSample) {
|
|||
case PNG_COLOR_GRAYSCALE_A:
|
||||
switch (bitsPerSample) {
|
||||
case 8: return Png_Expand_GRAYSCALE_A_8;
|
||||
case 16: return Png_Expand_GRAYSCALE_A_16;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
case PNG_COLOR_RGB_A:
|
||||
switch (bitsPerSample) {
|
||||
case 8: return Png_Expand_RGB_A_8;
|
||||
case 16: return Png_Expand_RGB_A_16;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@ -399,6 +390,8 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) {
|
|||
if (!bmp->scan0) return ERR_OUT_OF_MEMORY;
|
||||
|
||||
bitsPerSample = tmp[8]; col = tmp[9];
|
||||
if (bitsPerSample == 16) return PNG_ERR_16BITSAMPLES;
|
||||
|
||||
rowExpander = Png_GetExpander(col, bitsPerSample);
|
||||
if (!rowExpander) return PNG_ERR_INVALID_COL_BPP;
|
||||
|
||||
|
@ -436,7 +429,6 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) {
|
|||
if (res) return res;
|
||||
|
||||
/* RGB is always two bytes */
|
||||
/* TODO is this right for 16 bits per channel images? */
|
||||
trnsCol = BitmapCol_Make(buffer[1], buffer[1], buffer[1], 0);
|
||||
} else if (col == PNG_COLOR_INDEXED) {
|
||||
if (dataSize > PNG_PALETTE) return PNG_ERR_TRANS_COUNT;
|
||||
|
@ -456,7 +448,6 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) {
|
|||
if (res) return res;
|
||||
|
||||
/* R,G,B are always two bytes */
|
||||
/* TODO is this right for 16 bits per channel images? */
|
||||
trnsCol = BitmapCol_Make(buffer[1], buffer[3], buffer[5], 0);
|
||||
} else {
|
||||
return PNG_ERR_TRANS_INVALID;
|
||||
|
@ -476,8 +467,7 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) {
|
|||
|
||||
/* Initialise buffer for decoding */
|
||||
if (!initedBuffer) {
|
||||
Mem_Set(buffer, 0, scanlineBytes); /* Prior row should be 0 per PNG spec */
|
||||
bufferIdx = scanlineBytes;
|
||||
bufferIdx = 0;
|
||||
bufferRows = PNG_BUFFER_SIZE / scanlineBytes;
|
||||
bufferLen = bufferRows * scanlineBytes;
|
||||
initedBuffer = true;
|
||||
|
@ -496,9 +486,10 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) {
|
|||
|
||||
begY = bufferIdx / scanlineBytes;
|
||||
left = bufferLen - bufferIdx;
|
||||
/* if row is at 0, last row in buffer is prior row */
|
||||
/* hence subtract a row, as don't want to overwrite it */
|
||||
if (begY == 0) left -= scanlineBytes;
|
||||
/* if start row is at 0, last row in buffer is 'prior' row */
|
||||
/* - hence subtract a row for max read size, as don't want to overwrite that */
|
||||
/* (unless at first row of image, where 'prior' row is an array of 0) */
|
||||
if (begY == 0 && curY > 0) left -= scanlineBytes;
|
||||
|
||||
res = compStream.Read(&compStream, &buffer[bufferIdx], left, &read);
|
||||
if (res) return res;
|
||||
|
@ -514,9 +505,13 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) {
|
|||
cc_uint32 priorY = rowY == 0 ? bufferRows : rowY;
|
||||
cc_uint8* prior = &buffer[(priorY - 1) * scanlineBytes];
|
||||
cc_uint8* scanline = &buffer[rowY * scanlineBytes];
|
||||
|
||||
if (scanline[0] > PNG_FILTER_PAETH) return PNG_ERR_INVALID_SCANLINE;
|
||||
Png_Reconstruct(scanline[0], bytesPerPixel, &scanline[1], &prior[1], scanlineSize);
|
||||
|
||||
if (curY == 0) {
|
||||
Png_ReconstructFirst(scanline[0], bytesPerPixel, &scanline[1], scanlineSize);
|
||||
} else {
|
||||
Png_Reconstruct(scanline[0], bytesPerPixel, &scanline[1], &prior[1], scanlineSize);
|
||||
}
|
||||
rowExpander(bmp->width, palette, &scanline[1], Bitmap_GetRow(bmp, curY));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -135,5 +135,6 @@ enum CC_ERRORS {
|
|||
HTTP_ERR_TRUNCATED = 0xCCDED06CUL, /* HTTP respone header was truncated due to being too long */
|
||||
|
||||
SSL_ERR_CONTEXT_DEAD = 0xCCDED070UL, /* Server shutdown the SSL context and it must be recreated */
|
||||
PNG_ERR_16BITSAMPLES = 0xCCDED071UL, /* Image uses 16 bit samples, which is unimplemented */
|
||||
};
|
||||
#endif
|
||||
|
|
|
@ -87,6 +87,7 @@ static const char* GetCCErrorDesc(cc_result res) {
|
|||
case PNG_ERR_REACHED_IEND: return "Incomplete PNG image data";
|
||||
case PNG_ERR_NO_DATA: return "No image in PNG";
|
||||
case PNG_ERR_INVALID_SCANLINE: return "Invalid PNG scanline type";
|
||||
case PNG_ERR_16BITSAMPLES: return "16 bpp PNGs unsupported";
|
||||
|
||||
case NBT_ERR_UNKNOWN: return "Unknown NBT tag type";
|
||||
case CW_ERR_ROOT_TAG: return "Invalid root NBT tag";
|
||||
|
|
Loading…
Reference in a new issue