forked from Mirrors/openclonk
307 lines
8.3 KiB
C++
307 lines
8.3 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 2002 Sven Eberhardt
|
|
* Copyright (c) 2005, 2011 Günther Brammer
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
|
|
*
|
|
* Portions might be copyrighted by other authors who have contributed
|
|
* to OpenClonk.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
* See isc_license.txt for full license and disclaimer.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender.
|
|
* See clonk_trademark_license.txt for full license.
|
|
*/
|
|
// png file reading functionality
|
|
|
|
#include "C4Include.h"
|
|
#include <StdPNG.h>
|
|
|
|
#include <StdColors.h>
|
|
|
|
CPNGFile *pCurrPng=NULL; // global crap for file-reading callback
|
|
|
|
// png reading proc
|
|
void PNGAPI CPNGReadFn(png_structp png_ptr, png_bytep data, size_t length)
|
|
{
|
|
// read from current pnt
|
|
if (!pCurrPng) return;
|
|
pCurrPng->Read(data, length);
|
|
}
|
|
|
|
void CPNGFile::Read(unsigned char *pData, int iLength)
|
|
{
|
|
// fixme: overflow check schould be done here
|
|
// simply copy into buffer
|
|
memcpy(pData, pFilePtr, iLength);
|
|
// advance file ptr
|
|
pFilePtr+=iLength;
|
|
}
|
|
|
|
bool CPNGFile::DoLoad()
|
|
{
|
|
// set current png ptr
|
|
pCurrPng=this;
|
|
// reset file ptr
|
|
pFilePtr=pFile;
|
|
// check file
|
|
if (png_sig_cmp((unsigned char *) pFilePtr, 0, 8)) return false;
|
|
// setup png for reading
|
|
fWriteMode=false;
|
|
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
if (!png_ptr) return false;
|
|
info_ptr = png_create_info_struct(png_ptr);
|
|
if (!info_ptr) return false;
|
|
end_info = png_create_info_struct(png_ptr);
|
|
if (!end_info) return false;
|
|
// error handling
|
|
if (setjmp(png_jmpbuf(png_ptr))) return false;
|
|
// set file-reading proc
|
|
png_set_read_fn(png_ptr, png_get_io_ptr(png_ptr), &CPNGReadFn);
|
|
// read info
|
|
png_read_info(png_ptr, info_ptr);
|
|
// assign local vars
|
|
png_uint_32 uWdt = iWdt, uHgt = iHgt;
|
|
png_get_IHDR(png_ptr, info_ptr, &uWdt, &uHgt, &iBPC, &iClrType, &iIntrlcType, &iCmprType, &iFltrType);
|
|
// convert to bgra
|
|
if (iClrType == PNG_COLOR_TYPE_PALETTE && iBPC <= 8) png_set_expand(png_ptr);
|
|
if (iClrType == PNG_COLOR_TYPE_GRAY && iBPC < 8) png_set_expand(png_ptr);
|
|
if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr);
|
|
if (iBPC == 16) png_set_strip_16(png_ptr);
|
|
if (iBPC < 8) png_set_packing(png_ptr);
|
|
if (iClrType == PNG_COLOR_TYPE_GRAY || iClrType == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr);
|
|
/*if (iClrType == PNG_COLOR_TYPE_RGB || iClrType == PNG_COLOR_TYPE_RGB_ALPHA)*/ png_set_bgr(png_ptr);
|
|
// update info
|
|
png_read_update_info(png_ptr, info_ptr);
|
|
png_get_IHDR(png_ptr, info_ptr, &uWdt, &uHgt, &iBPC, &iClrType, &iIntrlcType, &iCmprType, &iFltrType);
|
|
iWdt = uWdt; iHgt = uHgt;
|
|
// get bit depth/check format
|
|
switch (iClrType)
|
|
{
|
|
case PNG_COLOR_TYPE_RGB: iPixSize=3; break;
|
|
case PNG_COLOR_TYPE_RGB_ALPHA: iPixSize=4; break;
|
|
default: return false; // unrecognized image
|
|
}
|
|
// allocate mem for the whole image
|
|
iRowSize=png_get_rowbytes(png_ptr, info_ptr);
|
|
pImageData = new unsigned char[iRowSize*iHgt];
|
|
// create row ptr buffer
|
|
unsigned char **ppRowBuf = new unsigned char *[iHgt];
|
|
unsigned char **ppRows=ppRowBuf; unsigned char *pRow=pImageData;
|
|
for (unsigned int i=0; i<iHgt; ++i,pRow+=iRowSize) *ppRows++=pRow;
|
|
// read image!
|
|
png_read_image(png_ptr, ppRowBuf);
|
|
// free row buffer
|
|
delete [] ppRowBuf;
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
CPNGFile::CPNGFile()
|
|
{
|
|
Default();
|
|
}
|
|
|
|
void CPNGFile::Default()
|
|
{
|
|
// zero fields
|
|
pFile=NULL;
|
|
fpFileOwned=false;
|
|
pFilePtr=NULL;
|
|
png_ptr=NULL;
|
|
info_ptr=end_info=NULL;
|
|
pImageData=NULL;
|
|
iRowSize=0;
|
|
iPixSize=0;
|
|
fp=NULL;
|
|
}
|
|
|
|
CPNGFile::~CPNGFile()
|
|
{
|
|
// clear
|
|
Clear();
|
|
}
|
|
|
|
void CPNGFile::ClearPngStructs()
|
|
{
|
|
// clear internal png ptrs
|
|
if (png_ptr || info_ptr || end_info)
|
|
{
|
|
if (fWriteMode)
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
else
|
|
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
|
|
}
|
|
png_ptr=NULL;
|
|
info_ptr=end_info=NULL;
|
|
fWriteMode=false;
|
|
}
|
|
|
|
void CPNGFile::Clear()
|
|
{
|
|
// free image data
|
|
if (pImageData) { delete [] pImageData; pImageData=NULL; }
|
|
// clear internal png ptrs
|
|
ClearPngStructs();
|
|
// free file ptr if owned
|
|
if (pFile && fpFileOwned) delete [] pFile; pFile=NULL;
|
|
// reset fields
|
|
fpFileOwned=false;
|
|
pFilePtr=NULL;
|
|
iRowSize=0;
|
|
iPixSize=0;
|
|
// close file if open
|
|
if (fp) { fclose(fp); fp=NULL; }
|
|
}
|
|
|
|
bool CPNGFile::Load(unsigned char *pFile, int iSize)
|
|
{
|
|
// clear any previously loaded file
|
|
Clear();
|
|
// store file ptr as not owned
|
|
this->pFile = pFile;
|
|
iFileSize=iSize;
|
|
fpFileOwned=false;
|
|
// perform the loading
|
|
if (!DoLoad())
|
|
{
|
|
Clear();
|
|
return false;
|
|
}
|
|
// reset file-field
|
|
this->pFile = NULL; iFileSize=0;
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
DWORD CPNGFile::GetPix(int iX, int iY)
|
|
{
|
|
// image loaded?
|
|
if (!pImageData) return 0;
|
|
// return pixel value
|
|
unsigned char *pPix=pImageData+iY*iRowSize+iX*iPixSize;
|
|
switch (iClrType)
|
|
{
|
|
case PNG_COLOR_TYPE_RGB:
|
|
return C4RGB(pPix[2], pPix[1], pPix[0]);
|
|
case PNG_COLOR_TYPE_RGB_ALPHA:
|
|
return RGBA(pPix[2], pPix[1], pPix[0], pPix[3]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool CPNGFile::Create(int iWdt, int iHgt, bool fAlpha)
|
|
{
|
|
// catch invalid size
|
|
if (iWdt<=0 || iHgt<=0) return false;
|
|
// clear current image in mem
|
|
Clear();
|
|
// set dimensions
|
|
this->iWdt=iWdt; this->iHgt=iHgt;
|
|
// set color type
|
|
iBPC = 8;
|
|
iClrType = fAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB;
|
|
iPixSize = fAlpha ? 4 : 3;
|
|
// set interlacing, filters and stuff
|
|
iIntrlcType = PNG_INTERLACE_NONE;
|
|
iCmprType = PNG_COMPRESSION_TYPE_DEFAULT;
|
|
iFltrType = PNG_FILTER_TYPE_DEFAULT;
|
|
// calculate rowbytes
|
|
int iBPP = (fAlpha ? 4 : 3) * iBPC;
|
|
iRowSize = (iBPP*iWdt+7)>>3;
|
|
// create image data
|
|
pImageData = new unsigned char[iHgt * iRowSize];
|
|
// success
|
|
return true;
|
|
}
|
|
|
|
bool CPNGFile::SetPix(int iX, int iY, DWORD dwValue)
|
|
{
|
|
// image created?
|
|
if (!pImageData) return false;
|
|
// set pixel value
|
|
unsigned char *pPix=pImageData+iY*iRowSize+iX*iPixSize;
|
|
switch (iClrType)
|
|
{
|
|
case PNG_COLOR_TYPE_RGB: // RGB: set r, g and b values
|
|
pPix[0] = GetBlueValue(dwValue);
|
|
pPix[1] = GetGreenValue(dwValue);
|
|
pPix[2] = GetRedValue(dwValue);
|
|
return true;
|
|
case PNG_COLOR_TYPE_RGB_ALPHA: // RGBA: simply set in mem
|
|
*(unsigned long *) pPix = dwValue;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CPNGFile::Save(const char *szFilename)
|
|
{
|
|
// regular file saving - first, there has to be a buffer
|
|
if (!pImageData) return false;
|
|
// open the file
|
|
fp = fopen(szFilename, "wb"); if (!fp) return false;
|
|
// clear any previously initialized png-structs (e.g. by reading)
|
|
ClearPngStructs();
|
|
// reinit them for writing
|
|
fWriteMode=true;
|
|
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
|
if (!png_ptr) { Clear(); return false; }
|
|
info_ptr = png_create_info_struct(png_ptr);
|
|
if (!info_ptr) { Clear(); return false; }
|
|
// error handling
|
|
if (setjmp(png_jmpbuf(png_ptr))) { Clear(); return false; }
|
|
// io initialization
|
|
png_init_io(png_ptr, fp);
|
|
// compression stuff
|
|
png_set_filter(png_ptr, 0, PNG_FILTER_NONE | PNG_FILTER_SUB | PNG_FILTER_PAETH);
|
|
png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
|
|
png_set_compression_mem_level(png_ptr, 8);
|
|
png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY);
|
|
png_set_compression_window_bits(png_ptr, 15);
|
|
png_set_compression_method(png_ptr, 8);
|
|
// set header
|
|
png_set_IHDR(png_ptr, info_ptr, iWdt, iHgt, iBPC, iClrType, iIntrlcType, iCmprType, iFltrType);
|
|
// double-check our calculated row size
|
|
int iRealRowSize=png_get_rowbytes(png_ptr, info_ptr);
|
|
if (iRealRowSize != iRowSize)
|
|
{
|
|
// this won't go well, so better abort
|
|
Clear(); return false;
|
|
}
|
|
// write png header
|
|
png_write_info(png_ptr, info_ptr);
|
|
// image data is given as bgr...
|
|
png_set_bgr(png_ptr);
|
|
// create row array
|
|
unsigned char **ppRowBuf = new unsigned char *[iHgt];
|
|
unsigned char **ppRows=ppRowBuf; unsigned char *pRow=pImageData;
|
|
for (unsigned int i=0; i<iHgt; ++i,pRow+=iRowSize) *ppRows++=pRow;
|
|
// write image
|
|
png_write_image(png_ptr, ppRowBuf);
|
|
// free row buffer
|
|
delete [] ppRowBuf;
|
|
// write end struct
|
|
png_write_end(png_ptr, info_ptr);
|
|
// finally, close the file
|
|
fclose(fp); fp = NULL;
|
|
// clear png structs
|
|
ClearPngStructs();
|
|
// success!
|
|
return true;
|
|
}
|
|
|
|
int CPNGFile::GetBitsPerPixel()
|
|
{
|
|
switch (iClrType)
|
|
{
|
|
case PNG_COLOR_TYPE_RGB: return 24;
|
|
case PNG_COLOR_TYPE_RGB_ALPHA: return 32;
|
|
}
|
|
return 0;
|
|
}
|