diff --git a/src/r_picformats.c b/src/r_picformats.c index dbd090fc7..d48bbaaf2 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -798,13 +798,24 @@ static void PNG_warn(png_structp PNG, png_const_charp pngtext) CONS_Debug(DBG_RENDER, "libpng warning at %p: %s", PNG, pngtext); } -static png_bytep *PNG_Read(const UINT8 *png, INT32 *w, INT32 *h, INT16 *topoffset, INT16 *leftoffset, size_t size) +static png_bytep *PNG_Read( + const UINT8 *png, + INT32 *w, INT32 *h, INT16 *topoffset, INT16 *leftoffset, + boolean *use_palette, size_t size) { png_structp png_ptr; png_infop png_info_ptr; png_uint_32 width, height; int bit_depth, color_type; png_uint_32 y; + + png_colorp palette; + int palette_size; + + png_bytep trans; + int trans_num; + png_color_16p trans_values; + #ifdef PNG_SETJMP_SUPPORTED #ifdef USE_FAR_KEYWORD jmp_buf jmpbuf; @@ -864,10 +875,48 @@ static png_bytep *PNG_Read(const UINT8 *png, INT32 *w, INT32 *h, INT16 *topoffse if (bit_depth == 16) png_set_strip_16(png_ptr); + palette = NULL; + *use_palette = false; + if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); else if (color_type == PNG_COLOR_TYPE_PALETTE) - png_set_palette_to_rgb(png_ptr); + { + boolean usepal = false; + + // Lactozilla: Check if the PNG has a palette, and if its color count + // matches the color count of SRB2's palette: 256 colors. + if (png_get_PLTE(png_ptr, png_info_ptr, &palette, &palette_size)) + { + if (palette_size == 256) + usepal = true; + } + + // If any of the tRNS colors have an alpha lower than 0xFF, and that + // color is present on the image, the palette flag is disabled. + png_get_tRNS(png_ptr, png_info_ptr, &trans, &trans_num, &trans_values); + + if (trans_num == 256) + { + int i; + for (i = 0; i < trans_num; i++) + { + // libpng will transform this image into RGB even if + // the transparent index does not exist in the image, + // and there is no way around that. + if (trans[i] < 0xFF) + { + usepal = false; + break; + } + } + } + + if (usepal) + *use_palette = true; + else + png_set_palette_to_rgb(png_ptr); + } if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); @@ -908,6 +957,7 @@ static png_bytep *PNG_Read(const UINT8 *png, INT32 *w, INT32 *h, INT16 *topoffse *w = (INT32)width; *h = (INT32)height; + return row_pointers; } @@ -935,12 +985,17 @@ void *Picture_PNGConvert( INT32 outbpp; size_t flatsize; png_uint_32 x, y; - png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, insize); + png_bytep row; + boolean palette = false; + png_bytep *row_pointers = PNG_Read(png, w, h, topoffset, leftoffset, &palette, insize); png_uint_32 width = *w, height = *h; if (png == NULL) I_Error("Picture_PNGConvert: picture was NULL!"); + if (row_pointers == NULL) + I_Error("Picture_PNGConvert: row_pointers was NULL!"); + // Find the output format's bits per pixel amount outbpp = Picture_FormatBPP(outformat); @@ -973,43 +1028,119 @@ void *Picture_PNGConvert( InitColorLUT(&png_colorlookup, pMasterPalette, false); #endif - for (y = 0; y < height; y++) + if (outbpp == PICDEPTH_32BPP) { - png_bytep row = row_pointers[y]; - for (x = 0; x < width; x++) + RGBA_t out; + UINT32 *outflat = (UINT32 *)flat; + + if (palette) { - png_bytep px = &(row[x * 4]); - if ((UINT8)px[3]) + for (y = 0; y < height; y++) { - UINT8 red = (UINT8)px[0]; - UINT8 green = (UINT8)px[1]; - UINT8 blue = (UINT8)px[2]; - UINT8 alpha = (UINT8)px[3]; - if (outbpp == PICDEPTH_32BPP) + row = row_pointers[y]; + for (x = 0; x < width; x++) { - UINT32 *outflat = (UINT32 *)flat; - RGBA_t out; - out.s.red = red; - out.s.green = green; - out.s.blue = blue; - out.s.alpha = alpha; + out = V_GetColor(row[x]); outflat[((y * width) + x)] = out.rgba; } - else + } + } + else + { + for (y = 0; y < height; y++) + { + row = row_pointers[y]; + for (x = 0; x < width; x++) { -#ifdef PICTURE_PNG_USELOOKUP - UINT8 palidx = GetColorLUT(&png_colorlookup, red, green, blue); -#else - UINT8 palidx = NearestColor(red, green, blue); -#endif - if (outbpp == PICDEPTH_16BPP) + png_bytep px = &(row[x * 4]); + if ((UINT8)px[3]) { - UINT16 *outflat = (UINT16 *)flat; - outflat[((y * width) + x)] = (alpha << 8) | palidx; + out.s.red = (UINT8)px[0]; + out.s.green = (UINT8)px[1]; + out.s.blue = (UINT8)px[2]; + out.s.alpha = (UINT8)px[3]; + outflat[((y * width) + x)] = out.rgba; } - else // 8bpp + else + outflat[((y * width) + x)] = 0x00000000; + } + } + } + } + else if (outbpp == PICDEPTH_16BPP) + { + UINT16 *outflat = (UINT16 *)flat; + + if (palette) + { + for (y = 0; y < height; y++) + { + row = row_pointers[y]; + for (x = 0; x < width; x++) + outflat[((y * width) + x)] = (0xFF << 8) | row[x]; + } + } + else + { + for (y = 0; y < height; y++) + { + row = row_pointers[y]; + for (x = 0; x < width; x++) + { + png_bytep px = &(row[x * 4]); + UINT8 red = (UINT8)px[0]; + UINT8 green = (UINT8)px[1]; + UINT8 blue = (UINT8)px[2]; + UINT8 alpha = (UINT8)px[3]; + + if (alpha) { - UINT8 *outflat = (UINT8 *)flat; +#ifdef PICTURE_PNG_USELOOKUP + UINT8 palidx = GetColorLUT(&png_colorlookup, red, green, blue); +#else + UINT8 palidx = NearestColor(red, green, blue); +#endif + outflat[((y * width) + x)] = (0xFF << 8) | palidx; + } + else + outflat[((y * width) + x)] = 0x0000; + } + } + } + } + else // 8bpp + { + UINT8 *outflat = (UINT8 *)flat; + + if (palette) + { + for (y = 0; y < height; y++) + { + row = row_pointers[y]; + for (x = 0; x < width; x++) + outflat[((y * width) + x)] = row[x]; + } + } + else + { + for (y = 0; y < height; y++) + { + row = row_pointers[y]; + for (x = 0; x < width; x++) + { + png_bytep px = &(row[x * 4]); + UINT8 red = (UINT8)px[0]; + UINT8 green = (UINT8)px[1]; + UINT8 blue = (UINT8)px[2]; + UINT8 alpha = (UINT8)px[3]; + + if (alpha) + { +#ifdef PICTURE_PNG_USELOOKUP + UINT8 palidx = GetColorLUT(&png_colorlookup, red, green, blue); +#else + UINT8 palidx = NearestColor(red, green, blue); +#endif outflat[((y * width) + x)] = palidx; } } @@ -1018,6 +1149,8 @@ void *Picture_PNGConvert( } // Free the row pointers that we allocated for libpng. + for (y = 0; y < height; y++) + free(row_pointers[y]); free(row_pointers); // But wait, there's more!