mirror of
https://github.com/minetest/irrlicht.git
synced 2024-11-16 15:30:25 +01:00
8bf9cf5471
A bit annoying that it kinda has the opposite meaning for png and jpg compression for same parameter (png compression goes up, jpg goes down). Also unfortunate we chose u32 instead of int here or we could at least use the usual zlib value range for png. But I think it still won't mess up in many cases. Defaults (value 0) stay the same as before. And setting a jpg range <= 10 is rarely done and even if so it just changes png sizes a bit now if people use writer for both. People just have to be careful now when they override defaults for png and then also write jpg - but can't help it. And it's too useful to not have this - this can massively change the write-speed for png's (like up to 3 times faster with no compression on my system). git-svn-id: svn://svn.code.sf.net/p/irrlicht/code/trunk@6570 dfc29bdd-3216-0410-991c-e03cc46cb475
228 lines
6.4 KiB
C++
228 lines
6.4 KiB
C++
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
|
// This file is part of the "Irrlicht Engine".
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
|
|
#include "CImageWriterPNG.h"
|
|
|
|
#ifdef _IRR_COMPILE_WITH_PNG_WRITER_
|
|
|
|
#include "IImage.h"
|
|
#include "CColorConverter.h"
|
|
#include "IWriteFile.h"
|
|
#include "os.h" // for logging
|
|
|
|
#ifdef _IRR_COMPILE_WITH_LIBPNG_
|
|
#ifndef _IRR_USE_NON_SYSTEM_LIB_PNG_
|
|
#include <png.h> // use system lib png
|
|
#else // _IRR_USE_NON_SYSTEM_LIB_PNG_
|
|
#include "libpng/png.h" // use irrlicht included lib png
|
|
#endif // _IRR_USE_NON_SYSTEM_LIB_PNG_
|
|
#endif // _IRR_COMPILE_WITH_LIBPNG_
|
|
|
|
namespace irr
|
|
{
|
|
namespace video
|
|
{
|
|
|
|
IImageWriter* createImageWriterPNG()
|
|
{
|
|
return new CImageWriterPNG;
|
|
}
|
|
|
|
#ifdef _IRR_COMPILE_WITH_LIBPNG_
|
|
// PNG function for error handling
|
|
static void png_cpexcept_error(png_structp png_ptr, png_const_charp msg)
|
|
{
|
|
os::Printer::log("PNG fatal error", msg, ELL_ERROR);
|
|
longjmp(png_jmpbuf(png_ptr), 1);
|
|
}
|
|
|
|
// PNG function for warning handling
|
|
static void png_cpexcept_warning(png_structp png_ptr, png_const_charp msg)
|
|
{
|
|
os::Printer::log("PNG warning", msg, ELL_WARNING);
|
|
}
|
|
|
|
// PNG function for file writing
|
|
void PNGAPI user_write_data_fcn(png_structp png_ptr, png_bytep data, png_size_t length)
|
|
{
|
|
png_size_t check;
|
|
|
|
io::IWriteFile* file=(io::IWriteFile*)png_get_io_ptr(png_ptr);
|
|
check=(png_size_t) file->write((const void*)data,(u32)length);
|
|
|
|
if (check != length)
|
|
png_error(png_ptr, "Write Error");
|
|
}
|
|
#endif // _IRR_COMPILE_WITH_LIBPNG_
|
|
|
|
CImageWriterPNG::CImageWriterPNG()
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("CImageWriterPNG");
|
|
#endif
|
|
}
|
|
|
|
bool CImageWriterPNG::isAWriteableFileExtension(const io::path& filename) const
|
|
{
|
|
#ifdef _IRR_COMPILE_WITH_LIBPNG_
|
|
return core::hasFileExtension ( filename, "png" );
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
bool CImageWriterPNG::writeImage(io::IWriteFile* file, IImage* image,u32 param) const
|
|
{
|
|
#ifdef _IRR_COMPILE_WITH_LIBPNG_
|
|
if (!file || !image)
|
|
return false;
|
|
|
|
// Allocate the png write struct
|
|
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
|
NULL, (png_error_ptr)png_cpexcept_error, (png_error_ptr)png_cpexcept_warning);
|
|
if (!png_ptr)
|
|
{
|
|
os::Printer::log("PNGWriter: Internal PNG create write struct failure", file->getFileName(), ELL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
// Set compression level
|
|
// Sadly Irrlicht used param=0 as default and an u32 type
|
|
// So to avoid breaking downward compatibility we keep 0 as default (which is -1 in zlib) and subtract 1 from param to everything into zlib range
|
|
if (param <= 10) // Z_BEST_COMPRESSION is 9 - values above have so far no meaning
|
|
png_set_compression_level(png_ptr, (int)param-1);
|
|
|
|
// Allocate the png info struct
|
|
png_infop info_ptr = png_create_info_struct(png_ptr);
|
|
if (!info_ptr)
|
|
{
|
|
os::Printer::log("PNGWriter: Internal PNG create info struct failure", file->getFileName(), ELL_ERROR);
|
|
png_destroy_write_struct(&png_ptr, NULL);
|
|
return false;
|
|
}
|
|
|
|
// for proper error handling
|
|
if (setjmp(png_jmpbuf(png_ptr)))
|
|
{
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
return false;
|
|
}
|
|
|
|
png_set_write_fn(png_ptr, file, user_write_data_fcn, NULL);
|
|
|
|
// Set info
|
|
switch(image->getColorFormat())
|
|
{
|
|
case ECF_A8R8G8B8:
|
|
case ECF_A1R5G5B5:
|
|
png_set_IHDR(png_ptr, info_ptr,
|
|
image->getDimension().Width, image->getDimension().Height,
|
|
8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
|
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
|
break;
|
|
default:
|
|
png_set_IHDR(png_ptr, info_ptr,
|
|
image->getDimension().Width, image->getDimension().Height,
|
|
8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
|
|
PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
|
|
}
|
|
|
|
s32 lineWidth = image->getDimension().Width;
|
|
switch(image->getColorFormat())
|
|
{
|
|
case ECF_R8G8B8:
|
|
case ECF_R5G6B5:
|
|
lineWidth*=3;
|
|
break;
|
|
case ECF_A8R8G8B8:
|
|
case ECF_A1R5G5B5:
|
|
lineWidth*=4;
|
|
break;
|
|
// TODO: Error handling in case of unsupported color format
|
|
default:
|
|
break;
|
|
}
|
|
u8* tmpImage = new u8[image->getDimension().Height*lineWidth];
|
|
if (!tmpImage)
|
|
{
|
|
os::Printer::log("PNGWriter: Internal PNG create image failure", file->getFileName(), ELL_ERROR);
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
return false;
|
|
}
|
|
|
|
u8* data = (u8*)image->getData();
|
|
switch(image->getColorFormat())
|
|
{
|
|
case ECF_R8G8B8:
|
|
CColorConverter::convert_R8G8B8toR8G8B8(data,image->getDimension().Height*image->getDimension().Width,tmpImage);
|
|
break;
|
|
case ECF_A8R8G8B8:
|
|
CColorConverter::convert_A8R8G8B8toA8R8G8B8(data,image->getDimension().Height*image->getDimension().Width,tmpImage);
|
|
break;
|
|
case ECF_R5G6B5:
|
|
CColorConverter::convert_R5G6B5toR8G8B8(data,image->getDimension().Height*image->getDimension().Width,tmpImage);
|
|
break;
|
|
case ECF_A1R5G5B5:
|
|
CColorConverter::convert_A1R5G5B5toA8R8G8B8(data,image->getDimension().Height*image->getDimension().Width,tmpImage);
|
|
break;
|
|
// TODO: Error handling in case of unsupported color format
|
|
default:
|
|
os::Printer::log("CImageWriterPNG does not support image format", ColorFormatNames[image->getColorFormat()], ELL_WARNING);
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
delete [] tmpImage;
|
|
return false;
|
|
}
|
|
|
|
// Create array of pointers to rows in image data
|
|
|
|
//Used to point to image rows
|
|
u8** RowPointers = new png_bytep[image->getDimension().Height];
|
|
if (!RowPointers)
|
|
{
|
|
os::Printer::log("PNGWriter: Internal PNG create row pointers failure", file->getFileName(), ELL_ERROR);
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
delete [] tmpImage;
|
|
return false;
|
|
}
|
|
|
|
data=tmpImage;
|
|
// Fill array of pointers to rows in image data
|
|
for (u32 i=0; i<image->getDimension().Height; ++i)
|
|
{
|
|
RowPointers[i]=data;
|
|
data += lineWidth;
|
|
}
|
|
// for proper error handling
|
|
if (setjmp(png_jmpbuf(png_ptr)))
|
|
{
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
delete [] RowPointers;
|
|
delete [] tmpImage;
|
|
return false;
|
|
}
|
|
|
|
png_set_rows(png_ptr, info_ptr, RowPointers);
|
|
|
|
if (image->getColorFormat()==ECF_A8R8G8B8 || image->getColorFormat()==ECF_A1R5G5B5)
|
|
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_BGR, NULL);
|
|
else
|
|
{
|
|
png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
|
|
}
|
|
|
|
delete [] RowPointers;
|
|
delete [] tmpImage;
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
} // namespace video
|
|
} // namespace irr
|
|
|
|
#endif
|
|
|