2015-02-12 23:16:25 +08:00
|
|
|
// Aseprite
|
2017-09-23 02:57:38 +08:00
|
|
|
// Copyright (C) 2001-2017 David Capello
|
2015-02-12 23:16:25 +08:00
|
|
|
//
|
2016-08-27 04:02:58 +08:00
|
|
|
// This program is distributed under the terms of
|
|
|
|
// the End-User License Agreement for Aseprite.
|
2007-09-20 08:32:35 +08:00
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
#ifdef HAVE_CONFIG_H
|
2007-09-20 08:32:35 +08:00
|
|
|
#include "config.h"
|
2013-08-06 08:20:19 +08:00
|
|
|
#endif
|
2007-09-20 08:32:35 +08:00
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
#include "app/app.h"
|
|
|
|
#include "app/document.h"
|
|
|
|
#include "app/file/file.h"
|
|
|
|
#include "app/file/file_format.h"
|
|
|
|
#include "app/file/format_options.h"
|
|
|
|
#include "app/ini_file.h"
|
2013-10-15 06:58:11 +08:00
|
|
|
#include "base/file_handle.h"
|
2014-10-21 09:21:31 +08:00
|
|
|
#include "doc/doc.h"
|
2007-12-06 11:00:10 +08:00
|
|
|
|
2011-03-23 08:11:25 +08:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2007-09-20 08:32:35 +08:00
|
|
|
#include "png.h"
|
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
namespace app {
|
|
|
|
|
2013-10-15 06:58:11 +08:00
|
|
|
using namespace base;
|
|
|
|
|
2013-08-06 08:20:19 +08:00
|
|
|
class PngFormat : public FileFormat {
|
2015-10-01 03:34:43 +08:00
|
|
|
const char* onGetName() const override { return "png"; }
|
|
|
|
const char* onGetExtensions() const override { return "png"; }
|
2017-09-23 02:57:38 +08:00
|
|
|
dio::FileFormat onGetDioFormat() const override { return dio::FileFormat::PNG_IMAGE; }
|
2015-10-01 03:34:43 +08:00
|
|
|
int onGetFlags() const override {
|
2011-01-17 04:27:18 +08:00
|
|
|
return
|
|
|
|
FILE_SUPPORT_LOAD |
|
|
|
|
FILE_SUPPORT_SAVE |
|
|
|
|
FILE_SUPPORT_RGB |
|
|
|
|
FILE_SUPPORT_RGBA |
|
|
|
|
FILE_SUPPORT_GRAY |
|
|
|
|
FILE_SUPPORT_GRAYA |
|
|
|
|
FILE_SUPPORT_INDEXED |
|
2015-07-01 05:08:14 +08:00
|
|
|
FILE_SUPPORT_SEQUENCES |
|
|
|
|
FILE_SUPPORT_PALETTE_WITH_ALPHA;
|
2011-01-17 04:27:18 +08:00
|
|
|
}
|
|
|
|
|
2014-08-15 10:07:47 +08:00
|
|
|
bool onLoad(FileOp* fop) override;
|
2014-04-10 11:33:28 +08:00
|
|
|
#ifdef ENABLE_SAVE
|
2014-08-15 10:07:47 +08:00
|
|
|
bool onSave(FileOp* fop) override;
|
2014-04-10 11:33:28 +08:00
|
|
|
#endif
|
2007-09-20 08:32:35 +08:00
|
|
|
};
|
|
|
|
|
2011-01-17 04:27:18 +08:00
|
|
|
FileFormat* CreatePngFormat()
|
|
|
|
{
|
|
|
|
return new PngFormat;
|
|
|
|
}
|
|
|
|
|
2007-09-20 08:32:35 +08:00
|
|
|
static void report_png_error(png_structp png_ptr, png_const_charp error)
|
|
|
|
{
|
2015-09-29 22:27:00 +08:00
|
|
|
((FileOp*)png_get_error_ptr(png_ptr))->setError("libpng: %s\n", error);
|
2007-09-20 08:32:35 +08:00
|
|
|
}
|
|
|
|
|
2011-01-17 04:27:18 +08:00
|
|
|
bool PngFormat::onLoad(FileOp* fop)
|
2007-09-20 08:32:35 +08:00
|
|
|
{
|
|
|
|
png_uint_32 width, height, y;
|
|
|
|
unsigned int sig_read = 0;
|
|
|
|
png_structp png_ptr;
|
|
|
|
png_infop info_ptr;
|
|
|
|
int bit_depth, color_type, interlace_type;
|
2007-11-25 09:26:08 +08:00
|
|
|
int pass, number_passes;
|
|
|
|
int num_palette;
|
|
|
|
png_colorp palette;
|
2016-02-17 05:18:29 +08:00
|
|
|
png_bytepp rows_pointer;
|
2012-02-13 10:21:06 +08:00
|
|
|
PixelFormat pixelFormat;
|
2007-09-20 08:32:35 +08:00
|
|
|
|
2015-09-29 22:27:00 +08:00
|
|
|
FileHandle handle(open_file_with_exception(fop->filename(), "rb"));
|
2015-04-03 07:42:43 +08:00
|
|
|
FILE* fp = handle.get();
|
2007-09-20 08:32:35 +08:00
|
|
|
|
|
|
|
/* Create and initialize the png_struct with the desired error handler
|
|
|
|
* functions. If you want to use the default stderr and longjump method,
|
|
|
|
* you can supply NULL for the last three parameters. We also supply the
|
|
|
|
* the compiler header file version, so that we know if the application
|
|
|
|
* was compiled with a compatible version of the library
|
|
|
|
*/
|
2008-02-04 10:37:26 +08:00
|
|
|
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)fop,
|
2012-01-06 06:45:03 +08:00
|
|
|
report_png_error, report_png_error);
|
2007-09-20 08:32:35 +08:00
|
|
|
if (png_ptr == NULL) {
|
2015-09-29 22:27:00 +08:00
|
|
|
fop->setError("png_create_read_struct\n");
|
2010-01-31 00:43:13 +08:00
|
|
|
return false;
|
2007-09-20 08:32:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate/initialize the memory for image information. */
|
|
|
|
info_ptr = png_create_info_struct(png_ptr);
|
|
|
|
if (info_ptr == NULL) {
|
2015-09-29 22:27:00 +08:00
|
|
|
fop->setError("png_create_info_struct\n");
|
2012-01-08 08:11:19 +08:00
|
|
|
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
2010-01-31 00:43:13 +08:00
|
|
|
return false;
|
2007-09-20 08:32:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Set error handling if you are using the setjmp/longjmp method (this is
|
|
|
|
* the normal method of doing things with libpng).
|
|
|
|
*/
|
|
|
|
if (setjmp(png_jmpbuf(png_ptr))) {
|
2015-09-29 22:27:00 +08:00
|
|
|
fop->setError("Error reading PNG file\n");
|
2012-01-08 08:11:19 +08:00
|
|
|
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
2010-01-31 00:43:13 +08:00
|
|
|
return false;
|
2007-09-20 08:32:35 +08:00
|
|
|
}
|
|
|
|
|
2016-08-30 05:24:03 +08:00
|
|
|
// Do not check sRGB profile
|
2016-08-30 23:26:30 +08:00
|
|
|
#ifdef PNG_SKIP_sRGB_CHECK_PROFILE
|
2016-08-30 05:24:03 +08:00
|
|
|
png_set_option(png_ptr, PNG_SKIP_sRGB_CHECK_PROFILE, 1);
|
2016-08-30 23:26:30 +08:00
|
|
|
#endif
|
2016-08-30 05:24:03 +08:00
|
|
|
|
2007-09-20 08:32:35 +08:00
|
|
|
/* Set up the input control if you are using standard C streams */
|
|
|
|
png_init_io(png_ptr, fp);
|
|
|
|
|
|
|
|
/* If we have already read some of the signature */
|
|
|
|
png_set_sig_bytes(png_ptr, sig_read);
|
|
|
|
|
|
|
|
/* The call to png_read_info() gives us all of the information from the
|
|
|
|
* PNG file before the first IDAT (image data chunk).
|
|
|
|
*/
|
|
|
|
png_read_info(png_ptr, info_ptr);
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2007-09-20 08:32:35 +08:00
|
|
|
png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
|
2012-01-08 08:11:19 +08:00
|
|
|
&interlace_type, NULL, NULL);
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2007-09-20 08:32:35 +08:00
|
|
|
|
|
|
|
/* Set up the data transformations you want. Note that these are all
|
|
|
|
* optional. Only call them if you want/need them. Many of the
|
|
|
|
* transformations only work on specific types of images, and many
|
|
|
|
* are mutually exclusive.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* tell libpng to strip 16 bit/color files down to 8 bits/color */
|
|
|
|
png_set_strip_16(png_ptr);
|
|
|
|
|
|
|
|
/* Extract multiple pixels with bit depths of 1, 2, and 4 from a single
|
|
|
|
* byte into separate bytes (useful for paletted and grayscale images).
|
|
|
|
*/
|
|
|
|
png_set_packing(png_ptr);
|
|
|
|
|
|
|
|
/* Expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixel */
|
|
|
|
if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
|
2012-01-08 08:11:19 +08:00
|
|
|
png_set_expand_gray_1_2_4_to_8(png_ptr);
|
2007-09-20 08:32:35 +08:00
|
|
|
|
|
|
|
/* Turn on interlace handling. REQUIRED if you are not using
|
|
|
|
* png_read_image(). To see how to handle interlacing passes,
|
|
|
|
* see the png_read_row() method below:
|
|
|
|
*/
|
2007-11-25 09:26:08 +08:00
|
|
|
number_passes = png_set_interlace_handling(png_ptr);
|
2007-09-20 08:32:35 +08:00
|
|
|
|
|
|
|
/* Optional call to gamma correct and add the background to the palette
|
|
|
|
* and update info structure.
|
|
|
|
*/
|
|
|
|
png_read_update_info(png_ptr, info_ptr);
|
|
|
|
|
|
|
|
/* create the output image */
|
2012-03-23 06:00:20 +08:00
|
|
|
switch (png_get_color_type(png_ptr, info_ptr)) {
|
2008-03-29 11:43:19 +08:00
|
|
|
|
2007-12-06 11:00:10 +08:00
|
|
|
case PNG_COLOR_TYPE_RGB_ALPHA:
|
2015-09-29 22:27:00 +08:00
|
|
|
fop->sequenceSetHasAlpha(true);
|
2012-01-06 06:45:03 +08:00
|
|
|
case PNG_COLOR_TYPE_RGB:
|
2012-02-13 10:21:06 +08:00
|
|
|
pixelFormat = IMAGE_RGB;
|
2007-12-06 11:00:10 +08:00
|
|
|
break;
|
2008-03-29 11:43:19 +08:00
|
|
|
|
2007-09-20 08:32:35 +08:00
|
|
|
case PNG_COLOR_TYPE_GRAY_ALPHA:
|
2015-09-29 22:27:00 +08:00
|
|
|
fop->sequenceSetHasAlpha(true);
|
2008-03-29 11:43:19 +08:00
|
|
|
case PNG_COLOR_TYPE_GRAY:
|
2012-02-13 10:21:06 +08:00
|
|
|
pixelFormat = IMAGE_GRAYSCALE;
|
2007-11-25 09:26:08 +08:00
|
|
|
break;
|
2008-03-29 11:43:19 +08:00
|
|
|
|
2007-09-20 08:32:35 +08:00
|
|
|
case PNG_COLOR_TYPE_PALETTE:
|
2012-02-13 10:21:06 +08:00
|
|
|
pixelFormat = IMAGE_INDEXED;
|
2007-11-25 09:26:08 +08:00
|
|
|
break;
|
2008-03-29 11:43:19 +08:00
|
|
|
|
2007-09-20 08:32:35 +08:00
|
|
|
default:
|
2015-09-29 22:27:00 +08:00
|
|
|
fop->setError("Color type not supported\n)");
|
2012-01-08 08:11:19 +08:00
|
|
|
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
2010-01-31 00:43:13 +08:00
|
|
|
return false;
|
2007-09-20 08:32:35 +08:00
|
|
|
}
|
2012-01-06 06:45:03 +08:00
|
|
|
|
2012-03-23 06:00:20 +08:00
|
|
|
int imageWidth = png_get_image_width(png_ptr, info_ptr);
|
|
|
|
int imageHeight = png_get_image_height(png_ptr, info_ptr);
|
2015-09-29 22:27:00 +08:00
|
|
|
Image* image = fop->sequenceImage(pixelFormat, imageWidth, imageHeight);
|
2007-09-20 08:32:35 +08:00
|
|
|
if (!image) {
|
2015-09-29 22:27:00 +08:00
|
|
|
fop->setError("file_sequence_image %dx%d\n", imageWidth, imageHeight);
|
2012-01-08 08:11:19 +08:00
|
|
|
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
2010-01-31 00:43:13 +08:00
|
|
|
return false;
|
2007-09-20 08:32:35 +08:00
|
|
|
}
|
2007-11-25 09:26:08 +08:00
|
|
|
|
2015-08-14 21:37:26 +08:00
|
|
|
// Transparent color
|
|
|
|
png_color_16p png_trans_color = NULL;
|
|
|
|
|
2011-01-05 11:16:39 +08:00
|
|
|
// Read the palette
|
2012-03-23 06:00:20 +08:00
|
|
|
if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE &&
|
2007-11-25 09:26:08 +08:00
|
|
|
png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)) {
|
2015-09-29 22:27:00 +08:00
|
|
|
fop->sequenceSetNColors(num_palette);
|
2007-11-25 09:26:08 +08:00
|
|
|
|
2015-07-01 05:16:10 +08:00
|
|
|
for (int c=0; c<num_palette; ++c) {
|
2015-09-29 22:27:00 +08:00
|
|
|
fop->sequenceSetColor(c,
|
|
|
|
palette[c].red,
|
|
|
|
palette[c].green,
|
|
|
|
palette[c].blue);
|
2007-11-25 09:26:08 +08:00
|
|
|
}
|
2011-01-05 11:16:39 +08:00
|
|
|
|
2011-08-09 11:01:57 +08:00
|
|
|
// Read alpha values for palette entries
|
2012-01-06 06:45:03 +08:00
|
|
|
png_bytep trans = NULL; // Transparent palette entries
|
2011-01-05 11:16:39 +08:00
|
|
|
int num_trans = 0;
|
2015-07-01 05:08:14 +08:00
|
|
|
int mask_entry = -1;
|
2011-01-05 11:16:39 +08:00
|
|
|
|
2015-08-14 21:37:26 +08:00
|
|
|
png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, nullptr);
|
2011-01-05 11:16:39 +08:00
|
|
|
|
|
|
|
for (int i = 0; i < num_trans; ++i) {
|
2015-09-29 22:27:00 +08:00
|
|
|
fop->sequenceSetAlpha(i, trans[i]);
|
2011-08-09 11:01:57 +08:00
|
|
|
|
2015-07-01 05:08:14 +08:00
|
|
|
if (trans[i] < 255) {
|
2015-09-29 22:27:00 +08:00
|
|
|
fop->sequenceSetHasAlpha(true); // Is a transparent sprite
|
2015-07-01 05:08:14 +08:00
|
|
|
if (trans[i] == 0) {
|
|
|
|
if (mask_entry < 0)
|
|
|
|
mask_entry = i;
|
|
|
|
}
|
2011-01-05 11:16:39 +08:00
|
|
|
}
|
|
|
|
}
|
2011-08-09 11:01:57 +08:00
|
|
|
|
|
|
|
if (mask_entry >= 0)
|
2015-09-29 22:27:00 +08:00
|
|
|
fop->document()->sprite()->setTransparentColor(mask_entry);
|
2007-11-25 09:26:08 +08:00
|
|
|
}
|
2015-08-14 21:37:26 +08:00
|
|
|
else {
|
|
|
|
png_get_tRNS(png_ptr, info_ptr, nullptr, nullptr, &png_trans_color);
|
|
|
|
}
|
2007-11-25 09:26:08 +08:00
|
|
|
|
2015-08-14 21:37:26 +08:00
|
|
|
// Allocate the memory to hold the image using the fields of info_ptr.
|
2016-02-17 05:18:29 +08:00
|
|
|
rows_pointer = (png_bytepp)png_malloc(png_ptr, sizeof(png_bytep) * height);
|
|
|
|
for (y = 0; y < height; y++)
|
|
|
|
rows_pointer[y] = (png_bytep)png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
|
|
|
|
|
2007-11-25 09:26:08 +08:00
|
|
|
for (pass = 0; pass < number_passes; pass++) {
|
2007-09-20 08:32:35 +08:00
|
|
|
for (y = 0; y < height; y++) {
|
2016-02-17 05:18:29 +08:00
|
|
|
png_read_rows(png_ptr, rows_pointer+y, nullptr, 1);
|
2007-11-25 09:26:08 +08:00
|
|
|
|
2015-09-29 22:27:00 +08:00
|
|
|
fop->setProgress(
|
|
|
|
(double)((double)pass + (double)(y+1) / (double)(height))
|
|
|
|
/ (double)number_passes);
|
2008-02-11 03:06:03 +08:00
|
|
|
|
2015-09-29 22:27:00 +08:00
|
|
|
if (fop->isStop())
|
2012-01-06 06:45:03 +08:00
|
|
|
break;
|
2007-09-20 08:32:35 +08:00
|
|
|
}
|
2007-11-25 09:26:08 +08:00
|
|
|
}
|
2016-02-17 05:18:29 +08:00
|
|
|
|
|
|
|
// Convert rows_pointer into the doc::Image
|
|
|
|
for (y = 0; y < height; y++) {
|
|
|
|
// RGB_ALPHA
|
|
|
|
if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB_ALPHA) {
|
|
|
|
uint8_t* src_address = rows_pointer[y];
|
|
|
|
uint32_t* dst_address = (uint32_t*)image->getPixelAddress(0, y);
|
|
|
|
unsigned int x, r, g, b, a;
|
|
|
|
|
|
|
|
for (x=0; x<width; x++) {
|
|
|
|
r = *(src_address++);
|
|
|
|
g = *(src_address++);
|
|
|
|
b = *(src_address++);
|
|
|
|
a = *(src_address++);
|
|
|
|
*(dst_address++) = rgba(r, g, b, a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// RGB
|
|
|
|
else if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB) {
|
|
|
|
uint8_t* src_address = rows_pointer[y];
|
|
|
|
uint32_t* dst_address = (uint32_t*)image->getPixelAddress(0, y);
|
|
|
|
unsigned int x, r, g, b, a;
|
|
|
|
|
|
|
|
for (x=0; x<width; x++) {
|
|
|
|
r = *(src_address++);
|
|
|
|
g = *(src_address++);
|
|
|
|
b = *(src_address++);
|
|
|
|
|
|
|
|
// Transparent color
|
|
|
|
if (png_trans_color &&
|
|
|
|
r == png_trans_color->red &&
|
|
|
|
g == png_trans_color->green &&
|
|
|
|
b == png_trans_color->blue) {
|
|
|
|
a = 0;
|
|
|
|
if (!fop->sequenceGetHasAlpha())
|
|
|
|
fop->sequenceSetHasAlpha(true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
a = 255;
|
|
|
|
|
|
|
|
*(dst_address++) = rgba(r, g, b, a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// GRAY_ALPHA
|
|
|
|
else if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY_ALPHA) {
|
|
|
|
uint8_t* src_address = rows_pointer[y];
|
|
|
|
uint16_t* dst_address = (uint16_t*)image->getPixelAddress(0, y);
|
|
|
|
unsigned int x, k, a;
|
|
|
|
|
|
|
|
for (x=0; x<width; x++) {
|
|
|
|
k = *(src_address++);
|
|
|
|
a = *(src_address++);
|
|
|
|
*(dst_address++) = graya(k, a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// GRAY
|
|
|
|
else if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY) {
|
|
|
|
uint8_t* src_address = rows_pointer[y];
|
|
|
|
uint16_t* dst_address = (uint16_t*)image->getPixelAddress(0, y);
|
|
|
|
unsigned int x, k, a;
|
|
|
|
|
|
|
|
for (x=0; x<width; x++) {
|
|
|
|
k = *(src_address++);
|
|
|
|
|
|
|
|
// Transparent color
|
|
|
|
if (png_trans_color &&
|
|
|
|
k == png_trans_color->gray) {
|
|
|
|
a = 0;
|
|
|
|
if (!fop->sequenceGetHasAlpha())
|
|
|
|
fop->sequenceSetHasAlpha(true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
a = 255;
|
|
|
|
|
|
|
|
*(dst_address++) = graya(k, a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// PALETTE
|
|
|
|
else if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE) {
|
|
|
|
uint8_t* src_address = rows_pointer[y];
|
|
|
|
uint8_t* dst_address = (uint8_t*)image->getPixelAddress(0, y);
|
|
|
|
unsigned int x;
|
|
|
|
|
|
|
|
for (x=0; x<width; x++)
|
|
|
|
*(dst_address++) = *(src_address++);
|
|
|
|
}
|
|
|
|
png_free(png_ptr, rows_pointer[y]);
|
|
|
|
}
|
|
|
|
png_free(png_ptr, rows_pointer);
|
2007-09-20 08:32:35 +08:00
|
|
|
|
2015-08-14 21:37:26 +08:00
|
|
|
// Clean up after the read, and free any memory allocated
|
2012-01-08 08:11:19 +08:00
|
|
|
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
|
2010-01-31 00:43:13 +08:00
|
|
|
return true;
|
2007-09-20 08:32:35 +08:00
|
|
|
}
|
|
|
|
|
2014-04-10 11:33:28 +08:00
|
|
|
#ifdef ENABLE_SAVE
|
2011-01-17 04:27:18 +08:00
|
|
|
bool PngFormat::onSave(FileOp* fop)
|
2007-09-20 08:32:35 +08:00
|
|
|
{
|
2015-09-29 22:27:00 +08:00
|
|
|
const Image* image = fop->sequenceImage();
|
2007-12-06 11:00:10 +08:00
|
|
|
png_uint_32 width, height, y;
|
|
|
|
png_structp png_ptr;
|
|
|
|
png_infop info_ptr;
|
2008-03-15 09:54:45 +08:00
|
|
|
png_colorp palette = NULL;
|
2007-12-06 11:00:10 +08:00
|
|
|
png_bytep row_pointer;
|
2008-09-30 07:58:07 +08:00
|
|
|
int color_type = 0;
|
2007-12-06 11:00:10 +08:00
|
|
|
int pass, number_passes;
|
|
|
|
|
|
|
|
/* open the file */
|
2015-09-29 22:27:00 +08:00
|
|
|
FileHandle handle(open_file_with_exception(fop->filename(), "wb"));
|
2015-04-03 07:42:43 +08:00
|
|
|
FILE* fp = handle.get();
|
2007-09-20 08:32:35 +08:00
|
|
|
|
2007-12-06 11:00:10 +08:00
|
|
|
/* Create and initialize the png_struct with the desired error handler
|
|
|
|
* functions. If you want to use the default stderr and longjump method,
|
|
|
|
* you can supply NULL for the last three parameters. We also check that
|
|
|
|
* the library version is compatible with the one used at compile time,
|
|
|
|
* in case we are using dynamically linked libraries. REQUIRED.
|
|
|
|
*/
|
2008-02-04 10:37:26 +08:00
|
|
|
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)fop,
|
2012-01-06 06:45:03 +08:00
|
|
|
report_png_error, report_png_error);
|
2007-12-06 11:00:10 +08:00
|
|
|
if (png_ptr == NULL) {
|
2010-01-31 00:43:13 +08:00
|
|
|
return false;
|
2007-12-06 11:00:10 +08:00
|
|
|
}
|
2007-09-20 08:32:35 +08:00
|
|
|
|
2007-12-06 11:00:10 +08:00
|
|
|
/* Allocate/initialize the image information data. REQUIRED */
|
|
|
|
info_ptr = png_create_info_struct(png_ptr);
|
|
|
|
if (info_ptr == NULL) {
|
2012-01-08 08:11:19 +08:00
|
|
|
png_destroy_write_struct(&png_ptr, NULL);
|
2010-01-31 00:43:13 +08:00
|
|
|
return false;
|
2007-12-06 11:00:10 +08:00
|
|
|
}
|
2007-09-20 08:32:35 +08:00
|
|
|
|
2007-12-06 11:00:10 +08:00
|
|
|
/* Set error handling. REQUIRED if you aren't supplying your own
|
|
|
|
* error handling functions in the png_create_write_struct() call.
|
|
|
|
*/
|
|
|
|
if (setjmp(png_jmpbuf(png_ptr))) {
|
|
|
|
/* If we get here, we had a problem reading the file */
|
|
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
2010-01-31 00:43:13 +08:00
|
|
|
return false;
|
2007-12-06 11:00:10 +08:00
|
|
|
}
|
2007-09-20 08:32:35 +08:00
|
|
|
|
2007-12-06 11:00:10 +08:00
|
|
|
/* set up the output control if you are using standard C streams */
|
|
|
|
png_init_io(png_ptr, fp);
|
2007-09-20 08:32:35 +08:00
|
|
|
|
2007-12-06 11:00:10 +08:00
|
|
|
/* Set the image information here. Width and height are up to 2^31,
|
|
|
|
* bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on
|
|
|
|
* the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY,
|
|
|
|
* PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB,
|
|
|
|
* or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or
|
|
|
|
* PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST
|
|
|
|
* currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED
|
|
|
|
*/
|
2014-07-30 12:28:15 +08:00
|
|
|
width = image->width();
|
|
|
|
height = image->height();
|
2007-12-06 11:00:10 +08:00
|
|
|
|
2014-07-30 12:28:15 +08:00
|
|
|
switch (image->pixelFormat()) {
|
2007-12-06 11:00:10 +08:00
|
|
|
case IMAGE_RGB:
|
2015-09-29 22:27:00 +08:00
|
|
|
color_type = fop->document()->sprite()->needAlpha() ?
|
2012-01-06 06:45:03 +08:00
|
|
|
PNG_COLOR_TYPE_RGB_ALPHA:
|
|
|
|
PNG_COLOR_TYPE_RGB;
|
2007-12-06 11:00:10 +08:00
|
|
|
break;
|
|
|
|
case IMAGE_GRAYSCALE:
|
2015-09-29 22:27:00 +08:00
|
|
|
color_type = fop->document()->sprite()->needAlpha() ?
|
2012-01-06 06:45:03 +08:00
|
|
|
PNG_COLOR_TYPE_GRAY_ALPHA:
|
|
|
|
PNG_COLOR_TYPE_GRAY;
|
2007-12-06 11:00:10 +08:00
|
|
|
break;
|
|
|
|
case IMAGE_INDEXED:
|
|
|
|
color_type = PNG_COLOR_TYPE_PALETTE;
|
|
|
|
break;
|
2007-09-20 08:32:35 +08:00
|
|
|
}
|
|
|
|
|
2007-12-06 11:00:10 +08:00
|
|
|
png_set_IHDR(png_ptr, info_ptr, width, height, 8, color_type,
|
2012-01-06 06:45:03 +08:00
|
|
|
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
|
2007-12-06 11:00:10 +08:00
|
|
|
|
2014-07-30 12:28:15 +08:00
|
|
|
if (image->pixelFormat() == IMAGE_INDEXED) {
|
2007-12-06 11:00:10 +08:00
|
|
|
int c, r, g, b;
|
2015-09-29 22:27:00 +08:00
|
|
|
int pal_size = fop->sequenceGetNColors();
|
2015-07-01 05:16:10 +08:00
|
|
|
pal_size = MID(1, pal_size, PNG_MAX_PALETTE_LENGTH);
|
2007-12-06 11:00:10 +08:00
|
|
|
|
|
|
|
#if PNG_MAX_PALETTE_LENGTH != 256
|
|
|
|
#error PNG_MAX_PALETTE_LENGTH should be 256
|
|
|
|
#endif
|
2007-09-20 08:32:35 +08:00
|
|
|
|
2011-08-09 11:01:57 +08:00
|
|
|
// Save the color palette.
|
2015-07-01 05:16:10 +08:00
|
|
|
palette = (png_colorp)png_malloc(png_ptr, pal_size * sizeof(png_color));
|
|
|
|
for (c = 0; c < pal_size; c++) {
|
2015-09-29 22:27:00 +08:00
|
|
|
fop->sequenceGetColor(c, &r, &g, &b);
|
2008-03-23 02:43:56 +08:00
|
|
|
palette[c].red = r;
|
|
|
|
palette[c].green = g;
|
|
|
|
palette[c].blue = b;
|
2007-09-20 08:32:35 +08:00
|
|
|
}
|
2007-12-06 11:00:10 +08:00
|
|
|
|
2015-07-01 05:16:10 +08:00
|
|
|
png_set_PLTE(png_ptr, info_ptr, palette, pal_size);
|
2011-01-05 11:16:39 +08:00
|
|
|
|
2015-04-21 21:01:28 +08:00
|
|
|
// If the sprite does not have a (visible) background layer, we
|
2015-07-01 05:08:14 +08:00
|
|
|
// put alpha=0 to the transparent color.
|
|
|
|
int mask_entry = -1;
|
2015-09-29 22:27:00 +08:00
|
|
|
if (fop->document()->sprite()->backgroundLayer() == NULL ||
|
|
|
|
!fop->document()->sprite()->backgroundLayer()->isVisible()) {
|
|
|
|
mask_entry = fop->document()->sprite()->transparentColor();
|
2015-07-01 05:08:14 +08:00
|
|
|
}
|
2011-08-09 11:01:57 +08:00
|
|
|
|
2015-07-01 05:16:10 +08:00
|
|
|
int num_trans = pal_size;
|
2015-07-01 05:08:14 +08:00
|
|
|
png_bytep trans = (png_bytep)png_malloc(png_ptr, num_trans);
|
2011-01-05 11:16:39 +08:00
|
|
|
|
2015-07-01 05:08:14 +08:00
|
|
|
for (c=0; c<num_trans; ++c) {
|
|
|
|
int alpha = 255;
|
2015-09-29 22:27:00 +08:00
|
|
|
fop->sequenceGetAlpha(c, &alpha);
|
2015-07-01 05:08:14 +08:00
|
|
|
trans[c] = (c == mask_entry ? 0: alpha);
|
2011-01-05 11:16:39 +08:00
|
|
|
}
|
2015-07-01 05:08:14 +08:00
|
|
|
|
|
|
|
png_set_tRNS(png_ptr, info_ptr, trans, num_trans, NULL);
|
|
|
|
png_free(png_ptr, trans);
|
2007-09-20 08:32:35 +08:00
|
|
|
}
|
|
|
|
|
2007-12-06 11:00:10 +08:00
|
|
|
/* Write the file header information. */
|
|
|
|
png_write_info(png_ptr, info_ptr);
|
|
|
|
|
|
|
|
/* pack pixels into bytes */
|
|
|
|
png_set_packing(png_ptr);
|
|
|
|
|
|
|
|
/* non-interlaced */
|
|
|
|
number_passes = 1;
|
|
|
|
|
2008-10-01 05:01:54 +08:00
|
|
|
row_pointer = (png_bytep)png_malloc(png_ptr, png_get_rowbytes(png_ptr, info_ptr));
|
2007-12-06 11:00:10 +08:00
|
|
|
|
|
|
|
/* The number of passes is either 1 for non-interlaced images,
|
|
|
|
* or 7 for interlaced images.
|
|
|
|
*/
|
|
|
|
for (pass = 0; pass < number_passes; pass++) {
|
|
|
|
/* If you are only writing one row at a time, this works */
|
|
|
|
for (y = 0; y < height; y++) {
|
|
|
|
/* RGB_ALPHA */
|
2012-03-23 06:00:20 +08:00
|
|
|
if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB_ALPHA) {
|
2014-08-15 10:26:52 +08:00
|
|
|
uint32_t* src_address = (uint32_t*)image->getPixelAddress(0, y);
|
|
|
|
uint8_t* dst_address = row_pointer;
|
|
|
|
unsigned int x, c;
|
2012-01-06 06:45:03 +08:00
|
|
|
|
|
|
|
for (x=0; x<width; x++) {
|
|
|
|
c = *(src_address++);
|
2013-11-10 06:59:05 +08:00
|
|
|
*(dst_address++) = rgba_getr(c);
|
|
|
|
*(dst_address++) = rgba_getg(c);
|
|
|
|
*(dst_address++) = rgba_getb(c);
|
|
|
|
*(dst_address++) = rgba_geta(c);
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
2007-09-20 08:32:35 +08:00
|
|
|
}
|
2007-12-06 11:00:10 +08:00
|
|
|
/* RGB */
|
2012-03-23 06:00:20 +08:00
|
|
|
else if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB) {
|
2014-08-15 10:26:52 +08:00
|
|
|
uint32_t* src_address = (uint32_t*)image->getPixelAddress(0, y);
|
|
|
|
uint8_t* dst_address = row_pointer;
|
|
|
|
unsigned int x, c;
|
2012-01-06 06:45:03 +08:00
|
|
|
|
|
|
|
for (x=0; x<width; x++) {
|
|
|
|
c = *(src_address++);
|
2013-11-10 06:59:05 +08:00
|
|
|
*(dst_address++) = rgba_getr(c);
|
|
|
|
*(dst_address++) = rgba_getg(c);
|
|
|
|
*(dst_address++) = rgba_getb(c);
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
2007-12-06 11:00:10 +08:00
|
|
|
}
|
|
|
|
/* GRAY_ALPHA */
|
2012-03-23 06:00:20 +08:00
|
|
|
else if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY_ALPHA) {
|
2014-08-15 10:26:52 +08:00
|
|
|
uint16_t* src_address = (uint16_t*)image->getPixelAddress(0, y);
|
|
|
|
uint8_t* dst_address = row_pointer;
|
|
|
|
unsigned int x, c;
|
2012-01-06 06:45:03 +08:00
|
|
|
|
|
|
|
for (x=0; x<width; x++) {
|
|
|
|
c = *(src_address++);
|
2013-11-10 06:59:05 +08:00
|
|
|
*(dst_address++) = graya_getv(c);
|
|
|
|
*(dst_address++) = graya_geta(c);
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
2007-12-06 11:00:10 +08:00
|
|
|
}
|
|
|
|
/* GRAY */
|
2012-03-23 06:00:20 +08:00
|
|
|
else if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY) {
|
2014-08-15 10:26:52 +08:00
|
|
|
uint16_t* src_address = (uint16_t*)image->getPixelAddress(0, y);
|
|
|
|
uint8_t* dst_address = row_pointer;
|
|
|
|
unsigned int x, c;
|
2012-01-06 06:45:03 +08:00
|
|
|
|
|
|
|
for (x=0; x<width; x++) {
|
|
|
|
c = *(src_address++);
|
2013-11-10 06:59:05 +08:00
|
|
|
*(dst_address++) = graya_getv(c);
|
2012-01-06 06:45:03 +08:00
|
|
|
}
|
2007-12-06 11:00:10 +08:00
|
|
|
}
|
|
|
|
/* PALETTE */
|
2012-03-23 06:00:20 +08:00
|
|
|
else if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE) {
|
2014-08-15 10:26:52 +08:00
|
|
|
uint8_t* src_address = (uint8_t*)image->getPixelAddress(0, y);
|
|
|
|
uint8_t* dst_address = row_pointer;
|
|
|
|
unsigned int x;
|
2007-12-06 11:00:10 +08:00
|
|
|
|
2012-01-06 06:45:03 +08:00
|
|
|
for (x=0; x<width; x++)
|
|
|
|
*(dst_address++) = *(src_address++);
|
2007-09-20 08:32:35 +08:00
|
|
|
}
|
2007-12-06 11:00:10 +08:00
|
|
|
|
|
|
|
/* write the line */
|
|
|
|
png_write_rows(png_ptr, &row_pointer, 1);
|
2008-02-04 10:37:26 +08:00
|
|
|
|
2015-09-29 22:27:00 +08:00
|
|
|
fop->setProgress(
|
|
|
|
(double)((double)pass + (double)(y+1) / (double)(height))
|
|
|
|
/ (double)number_passes);
|
2007-09-20 08:32:35 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-12-06 11:00:10 +08:00
|
|
|
png_free(png_ptr, row_pointer);
|
2007-09-20 08:32:35 +08:00
|
|
|
|
2007-12-06 11:00:10 +08:00
|
|
|
/* It is REQUIRED to call this to finish writing the rest of the file */
|
|
|
|
png_write_end(png_ptr, info_ptr);
|
2007-09-20 08:32:35 +08:00
|
|
|
|
2007-12-06 11:00:10 +08:00
|
|
|
/* If you png_malloced a palette, free it here (don't free info_ptr->palette,
|
|
|
|
as recommended in versions 1.0.5m and earlier of this example; if
|
|
|
|
libpng mallocs info_ptr->palette, libpng will free it). If you
|
|
|
|
allocated it with malloc() instead of png_malloc(), use free() instead
|
|
|
|
of png_free(). */
|
2014-07-30 12:28:15 +08:00
|
|
|
if (image->pixelFormat() == IMAGE_INDEXED) {
|
2007-12-09 09:52:58 +08:00
|
|
|
png_free(png_ptr, palette);
|
|
|
|
palette = NULL;
|
|
|
|
}
|
2007-09-20 08:32:35 +08:00
|
|
|
|
2007-12-06 11:00:10 +08:00
|
|
|
/* clean up after the write, and free any memory allocated */
|
|
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
2007-09-20 08:32:35 +08:00
|
|
|
|
2007-12-06 11:00:10 +08:00
|
|
|
/* all right */
|
2010-01-31 00:43:13 +08:00
|
|
|
return true;
|
2007-09-20 08:32:35 +08:00
|
|
|
}
|
2014-04-10 11:33:28 +08:00
|
|
|
#endif
|
2013-08-06 08:20:19 +08:00
|
|
|
|
|
|
|
} // namespace app
|