From 0750d273a6004ce73e9ce11f4b01c6eb7799b74e Mon Sep 17 00:00:00 2001
From: Jaime Passos <lazymyuutsu@gmail.com>
Date: Thu, 10 Sep 2020 03:16:21 -0300
Subject: [PATCH] Attempt to use the PNG image's palette, if it is present

---
 src/r_picformats.c | 189 ++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 161 insertions(+), 28 deletions(-)

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++)
 				{
+					png_bytep px = &(row[x * 4]);
+					if ((UINT8)px[3])
+					{
+						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
+						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)
+					{
 #ifdef PICTURE_PNG_USELOOKUP
-					UINT8 palidx = GetColorLUT(&png_colorlookup, red, green, blue);
+						UINT8 palidx = GetColorLUT(&png_colorlookup, red, green, blue);
 #else
-					UINT8 palidx = NearestColor(red, green, blue);
+						UINT8 palidx = NearestColor(red, green, blue);
 #endif
-					if (outbpp == PICDEPTH_16BPP)
-					{
-						UINT16 *outflat = (UINT16 *)flat;
-						outflat[((y * width) + x)] = (alpha << 8) | palidx;
+						outflat[((y * width) + x)] = (0xFF << 8) | palidx;
 					}
-					else // 8bpp
+					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)
 					{
-						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)] = 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!
-- 
GitLab