diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 998f49231..cad1fc30c 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -1,3 +1,8 @@ +August 12, 2006 +- Added support for truecolor PNG textures. They still get resampled to the + global palette, but at least they are visible now. +- Optimized UnfilterRow() in m_png.cpp a little. + August 11, 2006 (Changes by Graf Zahl) - Fixed: P_CheckOnMobjZ returned the first thing an actor could stand on, not the highest possible. diff --git a/src/m_png.cpp b/src/m_png.cpp index b3eaf48a2..38e56ee5e 100644 --- a/src/m_png.cpp +++ b/src/m_png.cpp @@ -99,7 +99,7 @@ static inline void MakeChunk (void *where, DWORD type, size_t len); static inline void StuffPalette (const PalEntry *from, BYTE *to); static bool StuffBitmap (const DCanvas *canvas, FILE *file); static bool WriteIDAT (FILE *file, const BYTE *data, int len); -static void UnfilterRow (int width, BYTE *dest, BYTE *stream, BYTE *prev); +static void UnfilterRow (int width, BYTE *dest, BYTE *stream, BYTE *prev, int bpp); static void UnpackPixels (int width, int bytesPerRow, int bitdepth, BYTE *row); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- @@ -532,17 +532,30 @@ bool M_ReadIDAT (FileReader *file, BYTE *buffer, int width, int height, int pitc int y; bool lastIDAT; int bytesPerRowIn; + int bytesPerPixel; - inputLine = (Byte *)alloca (1+width*2); - prev = inputLine + 1 + width; - memset (prev, 0, width); + switch (colortype) + { + case 2: bytesPerPixel = 3; break; // RGB + case 4: bytesPerPixel = 2; break; // LA + case 6: bytesPerPixel = 4; break; // RGBA + default: bytesPerPixel = 1; break; + } + inputLine = (Byte *)alloca (1 + width*bytesPerPixel*2); + prev = inputLine + 1 + width*bytesPerPixel; + memset (prev, 0, width*bytesPerPixel); + + if (bytesPerPixel == 1 && bitdepth != 8) + { // This is an invalid combination for PNG files + return false; + } switch (bitdepth) { - case 8: bytesPerRowIn = width; break; - case 4: bytesPerRowIn = (width+1)/2; break; - case 2: bytesPerRowIn = (width+3)/4; break; - case 1: bytesPerRowIn = (width+7)/8; break; + case 8: bytesPerRowIn = width * bytesPerPixel; break; + case 4: bytesPerRowIn = (width+1)/2; break; + case 2: bytesPerRowIn = (width+3)/4; break; + case 1: bytesPerRowIn = (width+7)/8; break; default: return false; } @@ -581,7 +594,7 @@ bool M_ReadIDAT (FileReader *file, BYTE *buffer, int width, int height, int pitc if (stream.avail_out == 0) { - UnfilterRow (bytesPerRowIn, curr, inputLine, prev); + UnfilterRow (bytesPerRowIn, curr, inputLine, prev, bytesPerPixel); prev = curr; curr += pitch; y++; @@ -800,51 +813,77 @@ static bool WriteIDAT (FILE *file, const BYTE *data, int len) // UnfilterRow // // Unfilters the given row. Unknown filter types are silently ignored. +// bpp is bytes per pixel, not bits per pixel. +// width is in bytes, not pixels. // //========================================================================== -void UnfilterRow (int width, BYTE *dest, BYTE *row, BYTE *prev) +void UnfilterRow (int width, BYTE *dest, BYTE *row, BYTE *prev, int bpp) { int x; switch (*row++) { case 1: // Sub - dest[0] = row[0]; - for (x = 1; x < width; ++x) + x = bpp; + do { - dest[x] = row[x] + dest[x-1]; + *dest++ = *row++; + } + while (--x); + for (x = width - bpp; x > 0; --x) + { + *dest = *row++ + *(dest - bpp); + dest++; } break; case 2: // Up - for (x = 0; x < width; ++x) + x = width; + do { - dest[x] = row[x] + prev[x]; + *dest++ = *row++ + *prev++; } + while (--x); break; case 3: // Average - dest[0] = row[0] + prev[0]/2; - for (x = 1; x < width; ++x) + x = bpp; + do { - dest[x] = row[x] + (BYTE)(((unsigned)dest[x-1] + (unsigned)prev[x])/2); + *dest++ = *row++ + (*prev++)/2; + } + while (--x); + for (x = width - bpp; x > 0; --x) + { + *dest = *row++ + (BYTE)((unsigned(*(dest - bpp)) + unsigned(*prev++)) >> 1); + dest++; } break; case 4: // Paeth - dest[0] = row[0] + prev[0]; - for (x = 1; x < width; ++x) + x = bpp; + do + { + *dest++ = *row++ + *prev++; + } + while (--x); + for (x = width - bpp; x > 0; --x) { int a, b, c, pa, pb, pc; - a = dest[x-1]; - b = prev[x]; - c = prev[x-1]; - pa = abs (b - c); - pb = abs (a - c); - pc = abs (a + b - c - c); - dest[x] = row[x] + (BYTE)((pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c); + a = *(dest - bpp); + b = *(prev); + c = *(prev - bpp); + pa = b - c; + pb = a - c; + pc = abs (pa + pb); + pa = abs (pa); + pb = abs (pb); + *dest = *row + (BYTE)((pa <= pb && pa <= pc) ? a : (pb <= pc) ? b : c); + dest++; + row++; + prev++; } break; diff --git a/src/r_data.cpp b/src/r_data.cpp index ffb945b22..3fc4628c6 100644 --- a/src/r_data.cpp +++ b/src/r_data.cpp @@ -279,7 +279,11 @@ int FTextureManager::CreateTexture (int lumpnum, int usetype) { return -1; } - if (colortype != 0 && colortype != 3) + if (!((1 << colortype) & 0x5D)) + { + return -1; + } + if (!((1 << bitdepth) & 0x116)) { return -1; } @@ -1853,21 +1857,13 @@ FPNGTexture::FPNGTexture (int lumpnum, int width, int height, } StartOfIDAT = lump.Tell() - 8; - if (colortype == 3) - { - PaletteMap = new BYTE[PaletteSize]; - GPalette.MakeRemap (palette, PaletteMap, trans, PaletteSize); - for (i = 0; i < PaletteSize; ++i) - { - if (trans[i] == 0) - { - bMasked = true; - PaletteMap[i] = 0; - } - } - } - else if (colortype == 0) + switch (colortype) { + case 4: // Grayscale + Alpha + bMasked = true; + // intentional fall-through + + case 0: // Grayscale if (!bAlphaTexture) { if (GrayMap[0] == GrayMap[255]) @@ -1877,7 +1873,7 @@ FPNGTexture::FPNGTexture (int lumpnum, int width, int height, GrayMap[i] = ColorMatcher.Pick (i, i, i); } } - if (havetRNS && trans[0] != 0) + if (colortype == 0 && havetRNS && trans[0] != 0) { bMasked = true; PaletteSize = 256; @@ -1890,6 +1886,24 @@ FPNGTexture::FPNGTexture (int lumpnum, int width, int height, PaletteMap = GrayMap; } } + break; + + case 3: // Paletted + PaletteMap = new BYTE[PaletteSize]; + GPalette.MakeRemap (palette, PaletteMap, trans, PaletteSize); + for (i = 0; i < PaletteSize; ++i) + { + if (trans[i] == 0) + { + bMasked = true; + PaletteMap[i] = 0; + } + } + break; + + case 6: // RGB + Alpha + bMasked = true; + break; } } @@ -1964,33 +1978,111 @@ void FPNGTexture::MakeTexture () DWORD len, id; lump.Seek (StartOfIDAT, SEEK_SET); lump >> len >> id; - M_ReadIDAT (&lump, Pixels, Width, Height, Width, BitDepth, ColorType, Interlace, BigLong((unsigned int)len)); - if (Width == Height) + if (ColorType == 0 || ColorType == 3) /* Grayscale and paletted */ { - if (PaletteMap != NULL) + M_ReadIDAT (&lump, Pixels, Width, Height, Width, BitDepth, ColorType, Interlace, BigLong((unsigned int)len)); + + if (Width == Height) { - FlipSquareBlockRemap (Pixels, Width, Height, PaletteMap); + if (PaletteMap != NULL) + { + FlipSquareBlockRemap (Pixels, Width, Height, PaletteMap); + } + else + { + FlipSquareBlock (Pixels, Width, Height); + } } else { - FlipSquareBlock (Pixels, Width, Height); + BYTE *newpix = new BYTE[Width*Height]; + if (PaletteMap != NULL) + { + FlipNonSquareBlockRemap (newpix, Pixels, Width, Height, PaletteMap); + } + else + { + FlipNonSquareBlock (newpix, Pixels, Width, Height, Width); + } + BYTE *oldpix = Pixels; + Pixels = newpix; + delete[] oldpix; } } - else + else /* RGB and/or Alpha present */ { - BYTE *newpix = new BYTE[Width*Height]; - if (PaletteMap != NULL) + int bytesPerPixel = ColorType == 2 ? 3 : ColorType == 4 ? 2 : 4; + BYTE *tempix = new BYTE[Width * Height * bytesPerPixel]; + BYTE *in, *out; + int x, y, pitch, backstep; + + M_ReadIDAT (&lump, tempix, Width, Height, Width*bytesPerPixel, BitDepth, ColorType, Interlace, BigLong((unsigned int)len)); + in = tempix; + out = Pixels; + + // Convert from source format to paletted, column-major. + // Formats with alpha maps are reduced to only 1 bit of alpha. + switch (ColorType) { - FlipNonSquareBlockRemap (newpix, Pixels, Width, Height, PaletteMap); + case 2: // RGB + pitch = Width * 3; + backstep = Height * pitch - 3; + for (x = Width; x > 0; --x) + { + for (y = Height; y > 0; --y) + { + *out++ = RGB32k[in[0]>>3][in[1]>>3][in[2]>>3]; + in += pitch; + } + in -= backstep; + } + break; + + case 4: // Grayscale + Alpha + pitch = Width * 2; + backstep = Height * pitch - 2; + if (PaletteMap != NULL) + { + for (x = Width; x > 0; --x) + { + for (y = Height; y > 0; --y) + { + *out++ = in[1] < 128 ? 0 : PaletteMap[in[0]]; + in += pitch; + } + in -= backstep; + } + } + else + { + for (x = Width; x > 0; --x) + { + for (y = Height; y > 0; --y) + { + *out++ = in[1] < 128 ? 0 : in[0]; + in += pitch; + } + in -= backstep; + } + } + break; + + case 6: // RGB + Alpha + pitch = Width * 4; + backstep = Height * pitch - 4; + for (x = Width; x > 0; --x) + { + for (y = Height; y > 0; --y) + { + *out++ = in[3] < 128 ? 0 : RGB32k[in[0]>>3][in[1]>>3][in[2]>>3]; + in += pitch; + } + in -= backstep; + } + break; } - else - { - FlipNonSquareBlock (newpix, Pixels, Width, Height, Width); - } - BYTE *oldpix = Pixels; - Pixels = newpix; - delete[] oldpix; + delete[] tempix; } } if (Spans == NULL)