mirror of
https://github.com/DrBeef/Raze.git
synced 2024-11-16 01:11:28 +00:00
365 lines
9.2 KiB
C++
365 lines
9.2 KiB
C++
|
/*
|
||
|
** pngtexture.cpp
|
||
|
** Texture class for PNG images
|
||
|
**
|
||
|
**---------------------------------------------------------------------------
|
||
|
** Copyright 2004-2007 Randy Heit
|
||
|
** All rights reserved.
|
||
|
**
|
||
|
** Redistribution and use in source and binary forms, with or without
|
||
|
** modification, are permitted provided that the following conditions
|
||
|
** are met:
|
||
|
**
|
||
|
** 1. Redistributions of source code must retain the above copyright
|
||
|
** notice, this list of conditions and the following disclaimer.
|
||
|
** 2. Redistributions in binary form must reproduce the above copyright
|
||
|
** notice, this list of conditions and the following disclaimer in the
|
||
|
** documentation and/or other materials provided with the distribution.
|
||
|
** 3. The name of the author may not be used to endorse or promote products
|
||
|
** derived from this software without specific prior written permission.
|
||
|
**
|
||
|
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||
|
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||
|
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||
|
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||
|
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||
|
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||
|
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
**---------------------------------------------------------------------------
|
||
|
**
|
||
|
**
|
||
|
*/
|
||
|
|
||
|
#include "files.h"
|
||
|
#include "templates.h"
|
||
|
#include "m_png.h"
|
||
|
#include "bitmap.h"
|
||
|
#include "image.h"
|
||
|
#include "printf.h"
|
||
|
#include "cache1d.h"
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
// A PNG texture
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
class FPNGTexture : public FImageSource
|
||
|
{
|
||
|
public:
|
||
|
FPNGTexture (FileReader &lump, int width, int height, uint8_t bitdepth, uint8_t colortype, uint8_t interlace);
|
||
|
|
||
|
int CopyPixels(FBitmap *bmp, int conversion) override;
|
||
|
|
||
|
protected:
|
||
|
uint8_t BitDepth;
|
||
|
uint8_t ColorType;
|
||
|
uint8_t Interlace;
|
||
|
bool HaveTrans;
|
||
|
uint16_t NonPaletteTrans[3];
|
||
|
|
||
|
int PaletteSize = 0;
|
||
|
uint32_t StartOfIDAT = 0;
|
||
|
uint32_t StartOfPalette = 0;
|
||
|
};
|
||
|
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
FImageSource *PNGImage_TryCreate(FileReader & data)
|
||
|
{
|
||
|
union
|
||
|
{
|
||
|
uint32_t dw;
|
||
|
uint16_t w[2];
|
||
|
uint8_t b[4];
|
||
|
} first4bytes;
|
||
|
|
||
|
|
||
|
// This is most likely a PNG, but make sure. (Note that if the
|
||
|
// first 4 bytes match, but later bytes don't, we assume it's
|
||
|
// a corrupt PNG.)
|
||
|
|
||
|
data.Seek(0, FileReader::SeekSet);
|
||
|
if (data.Read (first4bytes.b, 4) != 4) return NULL;
|
||
|
if (first4bytes.dw != MAKE_ID(137,'P','N','G')) return NULL;
|
||
|
if (data.Read (first4bytes.b, 4) != 4) return NULL;
|
||
|
if (first4bytes.dw != MAKE_ID(13,10,26,10)) return NULL;
|
||
|
if (data.Read (first4bytes.b, 4) != 4) return NULL;
|
||
|
if (first4bytes.dw != MAKE_ID(0,0,0,13)) return NULL;
|
||
|
if (data.Read (first4bytes.b, 4) != 4) return NULL;
|
||
|
if (first4bytes.dw != MAKE_ID('I','H','D','R')) return NULL;
|
||
|
|
||
|
// The PNG looks valid so far. Check the IHDR to make sure it's a
|
||
|
// type of PNG we support.
|
||
|
int width = data.ReadInt32BE();
|
||
|
int height = data.ReadInt32BE();
|
||
|
uint8_t bitdepth = data.ReadUInt8();
|
||
|
uint8_t colortype = data.ReadUInt8();
|
||
|
uint8_t compression = data.ReadUInt8();
|
||
|
uint8_t filter = data.ReadUInt8();
|
||
|
uint8_t interlace = data.ReadUInt8();
|
||
|
|
||
|
if (compression != 0 || filter != 0 || interlace > 1)
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
if (!((1 << colortype) & 0x5D))
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
if (!((1 << bitdepth) & 0x116))
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// Just for completeness, make sure the PNG has something more than an IHDR.
|
||
|
data.Seek (4, FileReader::SeekSet);
|
||
|
data.Read (first4bytes.b, 4);
|
||
|
if (first4bytes.dw == 0)
|
||
|
{
|
||
|
data.Read (first4bytes.b, 4);
|
||
|
if (first4bytes.dw == MAKE_ID('I','E','N','D'))
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return new FPNGTexture (data, width, height, bitdepth, colortype, interlace);
|
||
|
}
|
||
|
|
||
|
//==========================================================================
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
//==========================================================================
|
||
|
|
||
|
FPNGTexture::FPNGTexture (FileReader &lump, int width, int height,
|
||
|
uint8_t depth, uint8_t colortype, uint8_t interlace)
|
||
|
: BitDepth(depth), ColorType(colortype), Interlace(interlace), HaveTrans(false)
|
||
|
{
|
||
|
uint8_t trans[256];
|
||
|
uint32_t len, id;
|
||
|
|
||
|
bMasked = false;
|
||
|
|
||
|
Width = width;
|
||
|
Height = height;
|
||
|
|
||
|
memset(trans, 255, 256);
|
||
|
|
||
|
// Parse pre-IDAT chunks. I skip the CRCs. Is that bad?
|
||
|
lump.Seek(33, FileReader::SeekSet);
|
||
|
|
||
|
lump.Read(&len, 4);
|
||
|
lump.Read(&id, 4);
|
||
|
while (id != MAKE_ID('I','D','A','T') && id != MAKE_ID('I','E','N','D'))
|
||
|
{
|
||
|
len = BigLong((unsigned int)len);
|
||
|
switch (id)
|
||
|
{
|
||
|
default:
|
||
|
lump.Seek (len, FileReader::SeekCur);
|
||
|
break;
|
||
|
|
||
|
case MAKE_ID('g','r','A','b'):
|
||
|
// This is like GRAB found in an ILBM, except coordinates use 4 bytes
|
||
|
{
|
||
|
uint32_t hotx, hoty;
|
||
|
int ihotx, ihoty;
|
||
|
|
||
|
lump.Read(&hotx, 4);
|
||
|
lump.Read(&hoty, 4);
|
||
|
ihotx = BigLong((int)hotx);
|
||
|
ihoty = BigLong((int)hoty);
|
||
|
if (ihotx < -32768 || ihotx > 32767)
|
||
|
{
|
||
|
Printf ("X-Offset for PNG texture %s is bad: %d (0x%08x)\n", Name.GetChars(), ihotx, ihotx);
|
||
|
ihotx = 0;
|
||
|
}
|
||
|
if (ihoty < -32768 || ihoty > 32767)
|
||
|
{
|
||
|
Printf ("Y-Offset for PNG texture %s is bad: %d (0x%08x)\n", Name.GetChars(), ihoty, ihoty);
|
||
|
ihoty = 0;
|
||
|
}
|
||
|
LeftOffset = ihotx;
|
||
|
TopOffset = ihoty;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MAKE_ID('P','L','T','E'):
|
||
|
PaletteSize = std::min<int> (len / 3, 256);
|
||
|
StartOfPalette = (uint32_t)lump.Tell();
|
||
|
break;
|
||
|
|
||
|
case MAKE_ID('t','R','N','S'):
|
||
|
lump.Read (trans, len);
|
||
|
HaveTrans = true;
|
||
|
// Save for colortype 2
|
||
|
NonPaletteTrans[0] = uint16_t(trans[0] * 256 + trans[1]);
|
||
|
NonPaletteTrans[1] = uint16_t(trans[2] * 256 + trans[3]);
|
||
|
NonPaletteTrans[2] = uint16_t(trans[4] * 256 + trans[5]);
|
||
|
break;
|
||
|
}
|
||
|
lump.Seek(4, FileReader::SeekCur); // Skip CRC
|
||
|
lump.Read(&len, 4);
|
||
|
id = MAKE_ID('I','E','N','D');
|
||
|
lump.Read(&id, 4);
|
||
|
}
|
||
|
StartOfIDAT = (uint32_t)lump.Tell() - 8;
|
||
|
|
||
|
switch (colortype)
|
||
|
{
|
||
|
case 4: // Grayscale + Alpha
|
||
|
bMasked = true;
|
||
|
break;
|
||
|
|
||
|
case 0: // Grayscale
|
||
|
if (colortype == 0 && HaveTrans && NonPaletteTrans[0] < 256)
|
||
|
{
|
||
|
bMasked = true;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 3: // Paletted
|
||
|
break;
|
||
|
|
||
|
case 6: // RGB + Alpha
|
||
|
bMasked = true;
|
||
|
break;
|
||
|
|
||
|
case 2: // RGB
|
||
|
bMasked = HaveTrans;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//===========================================================================
|
||
|
//
|
||
|
// FPNGTexture::CopyPixels
|
||
|
//
|
||
|
//===========================================================================
|
||
|
|
||
|
int FPNGTexture::CopyPixels(FBitmap *bmp, int conversion)
|
||
|
{
|
||
|
// Parse pre-IDAT chunks. I skip the CRCs. Is that bad?
|
||
|
PalEntry pe[256];
|
||
|
uint32_t len, id;
|
||
|
static char bpp[] = {1, 0, 3, 1, 2, 0, 4};
|
||
|
int pixwidth = Width * bpp[ColorType];
|
||
|
int transpal = false;
|
||
|
|
||
|
FileReader *lump;
|
||
|
FileReader lfr;
|
||
|
|
||
|
lfr = kopenFileReader(Name, 0);
|
||
|
if (!lfr.isOpen()) return -1; // Just leave the texture blank.
|
||
|
|
||
|
lump = 𝔩
|
||
|
|
||
|
lump->Seek(33, FileReader::SeekSet);
|
||
|
for(int i = 0; i < 256; i++) // default to a gray map
|
||
|
pe[i] = PalEntry(255,i,i,i);
|
||
|
|
||
|
lump->Read(&len, 4);
|
||
|
lump->Read(&id, 4);
|
||
|
while (id != MAKE_ID('I','D','A','T') && id != MAKE_ID('I','E','N','D'))
|
||
|
{
|
||
|
len = BigLong((unsigned int)len);
|
||
|
switch (id)
|
||
|
{
|
||
|
default:
|
||
|
lump->Seek (len, FileReader::SeekCur);
|
||
|
break;
|
||
|
|
||
|
case MAKE_ID('P','L','T','E'):
|
||
|
for(int i = 0; i < PaletteSize; i++)
|
||
|
{
|
||
|
pe[i].r = lump->ReadUInt8();
|
||
|
pe[i].g = lump->ReadUInt8();
|
||
|
pe[i].b = lump->ReadUInt8();
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case MAKE_ID('t','R','N','S'):
|
||
|
if (ColorType == 3)
|
||
|
{
|
||
|
for(uint32_t i = 0; i < len; i++)
|
||
|
{
|
||
|
pe[i].a = lump->ReadUInt8();
|
||
|
if (pe[i].a != 0 && pe[i].a != 255)
|
||
|
transpal = true;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lump->Seek(len, FileReader::SeekCur);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
lump->Seek(4, FileReader::SeekCur); // Skip CRC
|
||
|
lump->Read(&len, 4);
|
||
|
id = MAKE_ID('I','E','N','D');
|
||
|
lump->Read(&id, 4);
|
||
|
}
|
||
|
|
||
|
if (ColorType == 0 && HaveTrans && NonPaletteTrans[0] < 256)
|
||
|
{
|
||
|
pe[NonPaletteTrans[0]].a = 0;
|
||
|
transpal = true;
|
||
|
}
|
||
|
|
||
|
uint8_t * Pixels = new uint8_t[pixwidth * Height];
|
||
|
|
||
|
lump->Seek (StartOfIDAT, FileReader::SeekSet);
|
||
|
lump->Read(&len, 4);
|
||
|
lump->Read(&id, 4);
|
||
|
M_ReadIDAT (*lump, Pixels, Width, Height, pixwidth, BitDepth, ColorType, Interlace, BigLong((unsigned int)len));
|
||
|
|
||
|
switch (ColorType)
|
||
|
{
|
||
|
case 0:
|
||
|
case 3:
|
||
|
bmp->CopyPixelData(0, 0, Pixels, Width, Height, 1, Width, 0, pe);
|
||
|
break;
|
||
|
|
||
|
case 2:
|
||
|
if (!HaveTrans)
|
||
|
{
|
||
|
bmp->CopyPixelDataRGB(0, 0, Pixels, Width, Height, 3, pixwidth, 0, CF_RGB);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bmp->CopyPixelDataRGB(0, 0, Pixels, Width, Height, 3, pixwidth, 0, CF_RGBT,
|
||
|
NonPaletteTrans[0], NonPaletteTrans[1], NonPaletteTrans[2]);
|
||
|
transpal = true;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 4:
|
||
|
bmp->CopyPixelDataRGB(0, 0, Pixels, Width, Height, 2, pixwidth, 0, CF_IA);
|
||
|
transpal = -1;
|
||
|
break;
|
||
|
|
||
|
case 6:
|
||
|
bmp->CopyPixelDataRGB(0, 0, Pixels, Width, Height, 4, pixwidth, 0, CF_RGBA);
|
||
|
transpal = -1;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
delete[] Pixels;
|
||
|
return transpal;
|
||
|
}
|
||
|
|
||
|
|