mirror of
https://github.com/aseprite/aseprite.git
synced 2024-12-26 09:19:27 +00:00
316 lines
7.6 KiB
C
316 lines
7.6 KiB
C
/* loadpng, Allegro wrapper routines for libpng
|
|
* by Peter Wang (tjaden@users.sf.net).
|
|
*/
|
|
|
|
|
|
#include <png.h>
|
|
#include <allegro.h>
|
|
#include "loadpng.h"
|
|
|
|
|
|
|
|
static void user_error_fn(png_structp png_ptr, png_const_charp message)
|
|
{
|
|
jmp_buf *jmpbuf = (jmp_buf *)png_get_error_ptr(png_ptr);
|
|
(void)message;
|
|
longjmp(*jmpbuf, 1);
|
|
}
|
|
|
|
|
|
|
|
/* write_data:
|
|
* Custom write function to use Allegro packfile routines,
|
|
* rather than C streams.
|
|
*/
|
|
static void write_data(png_structp png_ptr, png_bytep data, png_uint_32 length)
|
|
{
|
|
PACKFILE *f = (PACKFILE *)png_get_io_ptr(png_ptr);
|
|
if ((png_uint_32)pack_fwrite(data, length, f) != length)
|
|
png_error(png_ptr, "write error (loadpng calling pack_fwrite)");
|
|
}
|
|
|
|
/* Don't think Allegro has any problem with buffering
|
|
* (rather, Allegro provides no way to flush packfiles).
|
|
*/
|
|
static void flush_data(png_structp png_ptr) { (void)png_ptr; }
|
|
|
|
|
|
|
|
/* save_indexed:
|
|
* Core save routine for 8 bpp images.
|
|
* */
|
|
static int save_indexed(png_structp png_ptr, BITMAP *bmp)
|
|
{
|
|
ASSERT(bitmap_color_depth(bmp) == 8);
|
|
|
|
if (is_memory_bitmap(bmp)) { /* fast path */
|
|
int y;
|
|
|
|
for (y=0; y<bmp->h; y++) {
|
|
png_write_row(png_ptr, bmp->line[y]);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
else { /* generic case */
|
|
unsigned char *rowdata;
|
|
int x, y;
|
|
|
|
rowdata = (unsigned char *)malloc(bmp->w * 3);
|
|
if (!rowdata)
|
|
return 0;
|
|
|
|
for (y=0; y<bmp->h; y++) {
|
|
unsigned char *p = rowdata;
|
|
|
|
for (x=0; x<bmp->w; x++) {
|
|
*p++ = getpixel(bmp, x, y);
|
|
}
|
|
|
|
png_write_row(png_ptr, rowdata);
|
|
}
|
|
|
|
free(rowdata);
|
|
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* save_rgb:
|
|
* Core save routine for 15/16/24 bpp images (original by Martijn Versteegh).
|
|
*/
|
|
static int save_rgb(png_structp png_ptr, BITMAP *bmp)
|
|
{
|
|
AL_CONST int depth = bitmap_color_depth(bmp);
|
|
unsigned char *rowdata;
|
|
int y, x;
|
|
|
|
ASSERT(depth == 15 || depth == 16 || depth == 24);
|
|
|
|
rowdata = (unsigned char *)malloc(bmp->w * 3);
|
|
if (!rowdata)
|
|
return 0;
|
|
|
|
for (y=0; y<bmp->h; y++) {
|
|
unsigned char *p = rowdata;
|
|
|
|
if (depth == 15) {
|
|
for (x = 0; x < bmp->w; x++) {
|
|
int c = getpixel(bmp, x, y);
|
|
*p++ = getr15(c);
|
|
*p++ = getg15(c);
|
|
*p++ = getb15(c);
|
|
}
|
|
}
|
|
else if (depth == 16) {
|
|
for (x = 0; x < bmp->w; x++) {
|
|
int c = getpixel(bmp, x, y);
|
|
*p++ = getr16(c);
|
|
*p++ = getg16(c);
|
|
*p++ = getb16(c);
|
|
}
|
|
}
|
|
else { /* depth == 24 */
|
|
for (x = 0; x < bmp->w; x++) {
|
|
int c = getpixel(bmp, x, y);
|
|
*p++ = getr24(c);
|
|
*p++ = getg24(c);
|
|
*p++ = getb24(c);
|
|
}
|
|
}
|
|
|
|
png_write_row(png_ptr, rowdata);
|
|
}
|
|
|
|
free(rowdata);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
/* save_rgba:
|
|
* Core save routine for 32 bpp images.
|
|
*/
|
|
static int save_rgba(png_structp png_ptr, BITMAP *bmp)
|
|
{
|
|
unsigned char *rowdata;
|
|
int x, y;
|
|
|
|
ASSERT(bitmap_color_depth(bmp) == 32);
|
|
|
|
rowdata = (unsigned char *)malloc(bmp->w * 4);
|
|
if (!rowdata)
|
|
return 0;
|
|
|
|
for (y=0; y<bmp->h; y++) {
|
|
unsigned char *p = rowdata;
|
|
|
|
for (x=0; x<bmp->w; x++) {
|
|
int c = getpixel(bmp, x, y);
|
|
*p++ = getr32(c);
|
|
*p++ = getg32(c);
|
|
*p++ = getb32(c);
|
|
*p++ = geta32(c);
|
|
}
|
|
|
|
png_write_row(png_ptr, rowdata);
|
|
}
|
|
|
|
free(rowdata);
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
|
|
/* save_png:
|
|
* Writes a non-interlaced, no-frills PNG, taking the usual save_xyz
|
|
* parameters. Returns non-zero on error.
|
|
*/
|
|
static int really_save_png(PACKFILE *fp, BITMAP *bmp, AL_CONST RGB *pal)
|
|
{
|
|
jmp_buf jmpbuf;
|
|
png_structp png_ptr = NULL;
|
|
png_infop info_ptr = NULL;
|
|
int depth;
|
|
int colour_type;
|
|
|
|
depth = bitmap_color_depth(bmp);
|
|
if (depth == 8 && !pal)
|
|
return -1;
|
|
|
|
/* Create and initialize the png_struct with the
|
|
* desired error handler functions.
|
|
*/
|
|
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
|
(void *)NULL, NULL, NULL);
|
|
if (!png_ptr)
|
|
goto Error;
|
|
|
|
/* Allocate/initialize the image information data. */
|
|
info_ptr = png_create_info_struct(png_ptr);
|
|
if (!info_ptr)
|
|
goto Error;
|
|
|
|
/* Set error handling. */
|
|
if (setjmp(jmpbuf)) {
|
|
/* If we get here, we had a problem reading the file. */
|
|
goto Error;
|
|
}
|
|
png_set_error_fn(png_ptr, jmpbuf, user_error_fn, NULL);
|
|
|
|
/* Use packfile routines. */
|
|
png_set_write_fn(png_ptr, fp, (png_rw_ptr)write_data, flush_data);
|
|
|
|
/* 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.
|
|
*/
|
|
if (depth == 8)
|
|
colour_type = PNG_COLOR_TYPE_PALETTE;
|
|
else if (depth == 32)
|
|
colour_type = PNG_COLOR_TYPE_RGB_ALPHA;
|
|
else
|
|
colour_type = PNG_COLOR_TYPE_RGB;
|
|
|
|
/* Set compression level. */
|
|
png_set_compression_level(png_ptr, _png_compression_level);
|
|
|
|
png_set_IHDR(png_ptr, info_ptr, bmp->w, bmp->h, 8, colour_type,
|
|
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
|
|
PNG_FILTER_TYPE_BASE);
|
|
|
|
/* Set the palette if there is one. Required for indexed-color images. */
|
|
if (colour_type == PNG_COLOR_TYPE_PALETTE) {
|
|
png_color palette[256];
|
|
int i;
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
palette[i].red = _rgb_scale_6[pal[i].r]; /* 64 -> 256 */
|
|
palette[i].green = _rgb_scale_6[pal[i].g];
|
|
palette[i].blue = _rgb_scale_6[pal[i].b];
|
|
}
|
|
|
|
/* Set palette colors. */
|
|
png_set_PLTE(png_ptr, info_ptr, palette, 256);
|
|
}
|
|
|
|
/* Optionally write comments into the image ... Nah. */
|
|
|
|
/* Write the file header information. */
|
|
png_write_info(png_ptr, info_ptr);
|
|
|
|
/* Once we write out the header, the compression type on the text
|
|
* chunks gets changed to PNG_TEXT_COMPRESSION_NONE_WR or
|
|
* PNG_TEXT_COMPRESSION_zTXt_WR, so it doesn't get written out again
|
|
* at the end.
|
|
*/
|
|
|
|
/* Save the data. */
|
|
switch (depth) {
|
|
case 8:
|
|
if (!save_indexed(png_ptr, bmp))
|
|
goto Error;
|
|
break;
|
|
case 15:
|
|
case 16:
|
|
case 24:
|
|
if (!save_rgb(png_ptr, bmp))
|
|
goto Error;
|
|
break;
|
|
case 32:
|
|
if (!save_rgba(png_ptr, bmp))
|
|
goto Error;
|
|
break;
|
|
default:
|
|
ASSERT(FALSE);
|
|
goto Error;
|
|
}
|
|
|
|
png_write_end(png_ptr, info_ptr);
|
|
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
|
|
return 0;
|
|
|
|
Error:
|
|
|
|
if (png_ptr) {
|
|
if (info_ptr)
|
|
png_destroy_write_struct(&png_ptr, &info_ptr);
|
|
else
|
|
png_destroy_write_struct(&png_ptr, NULL);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
int save_png(AL_CONST char *filename, BITMAP *bmp, AL_CONST RGB *pal)
|
|
{
|
|
PACKFILE *fp;
|
|
int result;
|
|
|
|
ASSERT(filename);
|
|
ASSERT(bmp);
|
|
|
|
fp = pack_fopen(filename, "w");
|
|
if (!fp)
|
|
return -1;
|
|
|
|
acquire_bitmap(bmp);
|
|
result = really_save_png(fp, bmp, pal);
|
|
release_bitmap(bmp);
|
|
|
|
pack_fclose(fp);
|
|
|
|
return result;
|
|
}
|