From 33ca3cf8e0f707c87d0d3c18aeea19e32fbd8b0c Mon Sep 17 00:00:00 2001 From: David Capello Date: Thu, 19 Nov 2009 00:34:07 +0000 Subject: [PATCH] Added support to load png files through Allegro library (now you can save/load palettes in PNG files) --- makefile.gcc | 23 +- makefile.lst | 20 +- makefile.vc | 11 +- src/commands/cmd_palette_editor.cpp | 4 +- src/main.cpp | 4 + src/raster/palette.cpp | 6 +- third_party/loadpng/CHANGES.txt | 165 ++++++++++++ third_party/loadpng/README.txt | 53 ++++ third_party/loadpng/THANKS.txt | 16 ++ third_party/loadpng/loadpng.c | 400 ++++++++++++++++++++++++++++ third_party/loadpng/loadpng.h | 75 ++++++ third_party/loadpng/regpng.c | 68 +++++ third_party/loadpng/savepng.c | 306 +++++++++++++++++++++ 13 files changed, 1137 insertions(+), 14 deletions(-) create mode 100644 third_party/loadpng/CHANGES.txt create mode 100644 third_party/loadpng/README.txt create mode 100644 third_party/loadpng/THANKS.txt create mode 100644 third_party/loadpng/loadpng.c create mode 100644 third_party/loadpng/loadpng.h create mode 100644 third_party/loadpng/regpng.c create mode 100644 third_party/loadpng/savepng.c diff --git a/makefile.gcc b/makefile.gcc index 82456ae11..f3c086ac7 100644 --- a/makefile.gcc +++ b/makefile.gcc @@ -6,12 +6,13 @@ _default: default ###################################################################### # Setup CFLAGS and LFLAGS for GCC -CFLAGS += -Wall -I. -Isrc -Ithird_party \ - -I$(LIBFREETYPE_DIR)/include \ - -I$(LIBJPEG_DIR) \ - -I$(LIBPNG_DIR) \ - -I$(ZLIB_DIR) \ - -Wno-deprecated-declarations \ +CFLAGS += -Wall -I. -Isrc -Ithird_party \ + -I$(LIBFREETYPE_DIR)/include \ + -I$(LIBJPEG_DIR) \ + -I$(LIBPNG_DIR) \ + -I$(ZLIB_DIR) \ + -I$(LOADPNG_DIR) \ + -Wno-deprecated-declarations \ -DPNG_NO_MMX_CODE LFLAGS += $(THIRD_PARTY_LIBS) @@ -76,6 +77,10 @@ $(ZLIB_LIB): $(ZLIB_OBJS) -rm -f $@ ar rs $@ $^ +$(LOADPNG_LIB): $(LOADPNG_OBJS) + -rm -f $@ + ar rs $@ $^ + ###################################################################### # Rules to build objects and the application @@ -118,7 +123,8 @@ VPATH = src \ $(LIBGD_DIR) \ $(LIBJPEG_DIR) \ $(LIBPNG_DIR) \ - $(ZLIB_DIR) + $(ZLIB_DIR) \ + $(LOADPNG_DIR) ASE_DEPS = $(ASE_OBJS) $(THIRD_PARTY_LIBS) @@ -149,6 +155,9 @@ $(OBJ_DIR)/png.%$(OBJ): %.c $(OBJ_DIR)/zlib.%$(OBJ): %.c $(CC) $(CFLAGS) -o $@ -c $< +$(OBJ_DIR)/loadpng.%$(OBJ): %.c + $(CC) $(CFLAGS) -o $@ -c $< + ###################################################################### # Application diff --git a/makefile.lst b/makefile.lst index 64242e410..f7e4a29d8 100644 --- a/makefile.lst +++ b/makefile.lst @@ -466,6 +466,20 @@ ZLIB_OBJS = $(addprefix $(OBJ_DIR)/zlib., \ $(addsuffix $(OBJ), \ $(notdir $(basename $(ZLIB_SOURCES))))) +###################################################################### +# loadpng + +LOADPNG_LIB = $(OBJ_DIR)/libloadpng$(LIB_EXT) +LOADPNG_DIR = third_party/loadpng +LOADPNG_SOURCES = \ + $(LOADPNG_DIR)/loadpng.c \ + $(LOADPNG_DIR)/savepng.c \ + $(LOADPNG_DIR)/regpng.c + +LOADPNG_OBJS = $(addprefix $(OBJ_DIR)/loadpng., \ + $(addsuffix $(OBJ), \ + $(notdir $(basename $(LOADPNG_SOURCES))))) + ###################################################################### # All objects and libraries @@ -475,14 +489,16 @@ ALL_OBJS = \ $(LIBART_OBJS) \ $(LIBJPEG_OBJS) \ $(LIBPNG_OBJS) \ - $(ZLIB_OBJS) + $(ZLIB_OBJS) \ + $(LOADPNG_OBJS) THIRD_PARTY_LIBS = \ $(LIBART_LIB) \ $(LIBFREETYPE_LIB) \ $(LIBJPEG_LIB) \ $(LIBPNG_LIB) \ - $(ZLIB_LIB) + $(ZLIB_LIB) \ + $(LOADPNG_LIB) ###################################################################### # Tests diff --git a/makefile.vc b/makefile.vc index 4f11e17d4..ac9a11ad2 100644 --- a/makefile.vc +++ b/makefile.vc @@ -29,6 +29,7 @@ CFLAGS = -nologo \ -I$(LIBJPEG_DIR) \ -I$(LIBPNG_DIR) \ -I$(ZLIB_DIR) \ + -I$(LOADPNG_DIR) \ -DPNG_NO_MMX_CODE LFLAGS = -NOLOGO -SUBSYSTEM:WINDOWS -MACHINE:X86 @@ -111,6 +112,10 @@ $(ZLIB_LIB): $(ZLIB_OBJS) -rm -f $@ lib -NOLOGO -OUT:$@ $^ +$(LOADPNG_LIB): $(LOADPNG_OBJS) + -rm -f $@ + lib -NOLOGO -OUT:$@ $^ + ###################################################################### # Rules to build objects and the application @@ -153,7 +158,8 @@ VPATH = src \ $(LIBGD_DIR) \ $(LIBJPEG_DIR) \ $(LIBPNG_DIR) \ - $(ZLIB_DIR) + $(ZLIB_DIR) \ + $(LOADPNG_DIR) ASE_DEPS = $(ASE_OBJS) $(THIRD_PARTY_LIBS) @@ -181,6 +187,9 @@ $(OBJ_DIR)/png.%$(OBJ): %.c $(OBJ_DIR)/zlib.%$(OBJ): %.c $(CC) $(CFLAGS) -Fo$@ -c $< +$(OBJ_DIR)/loadpng.%$(OBJ): %.c + $(CC) $(CFLAGS) -Fo$@ -c $< + ###################################################################### # Application diff --git a/src/commands/cmd_palette_editor.cpp b/src/commands/cmd_palette_editor.cpp index bb39ff38b..d50d0f85e 100644 --- a/src/commands/cmd_palette_editor.cpp +++ b/src/commands/cmd_palette_editor.cpp @@ -305,7 +305,7 @@ static void select_all_command(JWidget widget) static void load_command(JWidget widget) { Palette *palette; - jstring filename = ase_file_selector(_("Load Palette"), "", "pcx,bmp,tga,lbm,col"); + jstring filename = ase_file_selector(_("Load Palette"), "", "png,pcx,bmp,tga,lbm,col"); if (!filename.empty()) { palette = palette_load(filename.c_str()); if (!palette) { @@ -324,7 +324,7 @@ static void save_command(JWidget widget) int ret; again: - filename = ase_file_selector(_("Save Palette"), "", "pcx,bmp,tga,col"); + filename = ase_file_selector(_("Save Palette"), "", "png,pcx,bmp,tga,col"); if (!filename.empty()) { if (exists(filename.c_str())) { ret = jalert("%s<<%s<<%s||%s", diff --git a/src/main.cpp b/src/main.cpp index 9e7ca54c7..4f4291562 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,7 @@ #include "ase_exception.h" #include "core/app.h" +#include "loadpng.h" ////////////////////////////////////////////////////////////////////// // Information for "ident". @@ -40,6 +41,9 @@ public: allegro_init(); set_uformat(U_ASCII); install_timer(); + + // Register PNG as a supported bitmap type + register_bitmap_file_type("png", load_png, save_png); } ~Allegro() { remove_timer(); diff --git a/src/raster/palette.cpp b/src/raster/palette.cpp index 8508e86f5..4d12c60c4 100644 --- a/src/raster/palette.cpp +++ b/src/raster/palette.cpp @@ -311,7 +311,8 @@ Palette* palette_load(const char *filename) ustrcpy(ext, get_extension(filename)); - if ((ustricmp(ext, "pcx") == 0) || + if ((ustricmp(ext, "png") == 0) || + (ustricmp(ext, "pcx") == 0) || (ustricmp(ext, "bmp") == 0) || (ustricmp(ext, "tga") == 0) || (ustricmp(ext, "lbm") == 0)) { @@ -340,7 +341,8 @@ bool palette_save(Palette* pal, const char *filename) ustrcpy(ext, get_extension(filename)); - if ((ustricmp(ext, "pcx") == 0) || + if ((ustricmp(ext, "png") == 0) || + (ustricmp(ext, "pcx") == 0) || (ustricmp(ext, "bmp") == 0) || (ustricmp(ext, "tga") == 0)) { PALETTE rgbpal; diff --git a/third_party/loadpng/CHANGES.txt b/third_party/loadpng/CHANGES.txt new file mode 100644 index 000000000..351bb6be2 --- /dev/null +++ b/third_party/loadpng/CHANGES.txt @@ -0,0 +1,165 @@ +------- April 1999 ------- + +?? Wrote load_png wrapper + +??+1 Cleaned up a bit, tried getting grayscale and + auto-MagicPink conversion working. + +9 Now can read gamma from environment variable. + Changed default gamma to 2.2. + +13 Added save_png, using pieces from allpng.c + +15 Fixed stupid bug I created in save_hicolour. + Added copyright notice. + Now uses packfile routines. + Added #ifdef __cplusplus hack. + Released: version 0.1. + +------- December 1999 ------- + +22 Updated to work with Allegro 3.9.xx. + Fixed alpha channel support. + Added alpha.c + test.c renamed to example.c + Released: version 0.2 + +------- January 1999 ------- + +3 Applied fix for < 8 bpp images (thanks to Dave Dribin) + Released: version 0.3 + +------- October 2000 ------- + +12 Updated const-correctness in accordance with Allegro. + Released: version 0.4 + +------- April 2001 ------- + +11 "Fixed" RGB/BGR problem with loading RGBA images. + Use GFX_AUTODETECT in examples (GFX_SAFE changed in Allegro). + Released: version 0.5 + +------- July 2001 ------- + +13 Reverted to 0.4 codebase, as 0.5 was really broken (oops). + Added "depth" argument to examples. + Commented out the BGR flipping code, which was incorrect I think + anyway. Someone with a BGR card should check it out. + Released: version 0.6 + +------- January 2002 ------- + +03 allegro/aintern.h -> allegro/internal/aintern.h (Allegro 4.0) + Released: version 0.7 + +------- February 2002 ------- + +25 Fixed a problem with passing NULL for pal to load_png. + Released: version 0.8 + +------- March 2002 ------- + +30 Tentative fixes for loading/saving RGBA images. + Thanks to Chris Graham for reporting the problem. + Minor changes to examples and makefile. + +------- May 2002 ------- + +06 Added shared library (unix) and install support into makefiles, + and cleaned them up. Thanks to Robbert Haarman for the start. + Minor changes for -W warnings. + +07 Dave Dribin fixed a problem that surfaced with some buggy versions + of libpng (1.0.13, 1.2.2). + Moved the thanks section into a separate document. + +08 Fixed a puny mistake in the makefile. + +12 Released: version 0.9 + +16 Tentative fixes for sometimes-problems loading RGB images. + +19 Released: version 0.10 + +------- September 2002 ------- + +03 Dave Dribin fixed two problems with images with alpha channels. + +04 Released: version 0.11 + +------- April 2003 ------- + +27 Mostly incorporated Ceniza's changes (load_memory_png and loading + from datafile support, plus an example). + +28 Reworked those changes a little. + +------- May 2003 ------- + +02 Miscellaneous changes; put examples into separate directory + (now that we have three!). + +04 Signal errors on read/write failure. + Released: version 1.0. + +20 Calling load_png with NULL for palette argument was not converting + paletted images to high/truecolour properly. Fixed. Thanks to + Lothar May for pointing it out. + Released: version 1.1. + +------- October 2003 ------- + +13 Changed the method of scaling of palette values from 6 bits + to 8 bits. Instead of just multiplying by 4, now we use + _rgb_scale_6[] so that e.g. 0 -> 0, but 63 -> 255. + Thanks Nicolas Lemal for the idea. + Released: version 1.2. + +16 MSVC doesn't like pointer arithmetic on void*'s, pointed out by + Chris Condrat. + Re-released: version 1.2 (I forgot to update version constants anyway). + +------- April 2004 ------- + +26 Jon Rafkind suggested to use _getpixel*/_putpixel* in loader to + speed up loading of large images. + Split up the code into three files. This prevents the linker pulling + in unused code, e.g. many people don't need save_png. + +28 It turns out that loadpng's origin (example.c from libpng docs) was in + the public domain, so now loadpng is in the public domain as well. + Released: version 1.3. + +------- October 2004 ------- + +09 Made save_png handle non-memory bitmaps as well. + +10 Fixed some bugs with load_png(). Now everything in PngSuite loads + properly, and with correct gamma correction, except for: + - bKGD chunks (solid background colours) are not respected. + I chose this in the past, but I wonder if I should change it? + - pHYs chunks (physical dimensions) are not respected. + (BTW, this was all tested with Valgrind - no errors) + + _png_screen_gamma can now be 0.0 to disable gamma correction, i.e. if + you don't know the screen gamma, it might be best not to guess. + + Got rid of inefficient BGR-handling hack and did things properly. + + New example: browse.c (for mass testing, e.g. PngSuite) + + Released: version 1.4pre1. + +------- August 2005 ------- + +18 Emmanuel Anne made _png_compression_level actually work. + + Released: version 1.4. + +------- April 2006 ------- + +9 Fix an endianness on big-endian machines, from Siarhei Siamashka. + I haven't tested it. + + Released: version 1.5. diff --git a/third_party/loadpng/README.txt b/third_party/loadpng/README.txt new file mode 100644 index 000000000..c545f6108 --- /dev/null +++ b/third_party/loadpng/README.txt @@ -0,0 +1,53 @@ + + loadpng: glue for Allegro and libpng + + +This wrapper is mostly a copy and paste job from example.c in the +libpng docs, stripping out the useless transformations and making it +use Allegro BITMAP and PALETTE structures. It is placed in the public +domain. + + +Requirements: + + Allegro http://alleg.sourceforge.net/ + libpng http://www.libpng.org/pub/png/ + zlib http://www.gzip.org/zlib/ + + +Usage: + +See loadpng.h for functions and their descriptions. There is a +simple example program called example.c, a program demonstrating +alpha translucency in exalpha.c, and a program demonstrating how to +load a PNG object from a datafile in exdata.c. + +To compile, just run "make" (or perhaps "mingw32-make"). To use +loadpng, you need to link with libpng, zlib in addition to loadpng +itself, e.g. + + gcc mygame.c -lldpng -lpng -lz -lalleg + +I recommend you copy loadpng's files into your own project's directory +and compile loadpng as part of your project. + + +Notes: + +- Grayscale images will be loaded in as 24 bit images, or 32 bit + images if they contain an alpha channel. These will then be + converted as usual, according to Allegro's conversion semantics. Be + aware of this if you have disabled automatic colour depth + conversion. + +- save_png() doesn't save any gamma chunk. I'm thinking of making it + write an sRGB chunk by default. If you want tight control of how + your images are saved, I recommend hacking save_png() to suit your + needs. There's simply too many options. + + +Enjoy! + +Peter Wang (tjaden@users.sf.net) +http://members.ozadsl.com.au/~tjaden/ +http://tjaden.strangesoft.net/ diff --git a/third_party/loadpng/THANKS.txt b/third_party/loadpng/THANKS.txt new file mode 100644 index 000000000..15f3c0728 --- /dev/null +++ b/third_party/loadpng/THANKS.txt @@ -0,0 +1,16 @@ +Peter Wang is me. + +Thanks to Martijn Versteegh for allpng.c, from which I took the actual +routine which saves 15 / 16 bpp images. + +The following people reported or fixed bugs or did something else +(assuming I didn't forget): + +- Dave Dribin +- Lennart Steinke +- Chris Graham +- Robbert Haarman +- Ceniza +- Nicolas Lemal +- Jon Rafkind +- Emmanuel Anne diff --git a/third_party/loadpng/loadpng.c b/third_party/loadpng/loadpng.c new file mode 100644 index 000000000..3905f9776 --- /dev/null +++ b/third_party/loadpng/loadpng.c @@ -0,0 +1,400 @@ +/* loadpng, Allegro wrapper routines for libpng + * by Peter Wang (tjaden@users.sf.net). + * + * This file is hereby placed in the public domain. + */ + + +#include +#include +#include +#include "loadpng.h" + +/* We need internals _color_load_depth and _fixup_loaded_bitmap. The + * first can be replaced by the new get_color_depth() function which + * is in Allegro 4.1 branch. But it's not worth it to break 4.0 + * compatibility. + */ + + + +double _png_screen_gamma = -1.0; +int _png_compression_level = Z_BEST_COMPRESSION; + + + +/* get_gamma: + * Get screen gamma value one of three ways. + */ +static double get_gamma(void) +{ + if (_png_screen_gamma == -1.0) { + /* Use the environment variable if available. + * 2.2 is a good guess for PC monitors. + * 1.1 is good for my laptop. + */ + AL_CONST char *gamma_str = getenv("SCREEN_GAMMA"); + return (gamma_str) ? atof(gamma_str) : 2.2; + } + + return _png_screen_gamma; +} + + + +/* read_data: + * Custom read function to use Allegro packfile routines, + * rather than C streams (so we can read from datafiles!) + */ +static void read_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_fread(data, length, f) != length) + png_error(png_ptr, "read error (loadpng calling pack_fread)"); +} + + + +/* check_if_png: + * Check if input file is really PNG format. + */ +#define PNG_BYTES_TO_CHECK 4 + +static int check_if_png(PACKFILE *fp) +{ + unsigned char buf[PNG_BYTES_TO_CHECK]; + + ASSERT(fp); + + if (pack_fread(buf, PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK) + return 0; + + return (png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK) == 0); +} + + + +/* really_load_png: + * Worker routine, used by load_png and load_memory_png. + */ +static BITMAP *really_load_png(png_structp png_ptr, png_infop info_ptr, RGB *pal) +{ + BITMAP *bmp; + PALETTE tmppal; + png_uint_32 width, height, rowbytes; + int bit_depth, color_type, interlace_type; + double image_gamma, screen_gamma; + int intent; + int bpp, dest_bpp; + int tRNS_to_alpha = FALSE; + int number_passes, pass; + + ASSERT(png_ptr && info_ptr && rgb); + + /* 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); + + png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, + &interlace_type, NULL, NULL); + + /* 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)) + png_set_expand(png_ptr); + + /* Adds a full alpha channel if there is transparency information + * in a tRNS chunk. */ + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { + png_set_tRNS_to_alpha(png_ptr); + tRNS_to_alpha = TRUE; + } + + /* Convert 16-bits per colour component to 8-bits per colour component. */ + if (bit_depth == 16) + png_set_strip_16(png_ptr); + + /* Convert grayscale to RGB triplets */ + if ((color_type == PNG_COLOR_TYPE_GRAY) || + (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) + png_set_gray_to_rgb(png_ptr); + + /* Optionally, tell libpng to handle the gamma correction for us. */ + if (_png_screen_gamma != 0.0) { + screen_gamma = get_gamma(); + + if (png_get_sRGB(png_ptr, info_ptr, &intent)) + png_set_gamma(png_ptr, screen_gamma, 0.45455); + else { + if (png_get_gAMA(png_ptr, info_ptr, &image_gamma)) + png_set_gamma(png_ptr, screen_gamma, image_gamma); + else + png_set_gamma(png_ptr, screen_gamma, 0.45455); + } + } + + /* Turn on interlace handling. */ + number_passes = png_set_interlace_handling(png_ptr); + + /* Call to gamma correct and add the background to the palette + * and update info structure. + */ + png_read_update_info(png_ptr, info_ptr); + + /* Even if the user doesn't supply space for a palette, we want + * one for the load process. + */ + if (!pal) + pal = tmppal; + + /* Palettes. */ + if (color_type & PNG_COLOR_MASK_PALETTE) { + int num_palette, i; + png_colorp palette; + + if (png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette)) { + /* We don't actually dither, we just copy the palette. */ + for (i = 0; ((i < num_palette) && (i < 256)); i++) { + pal[i].r = palette[i].red >> 2; /* 256 -> 64 */ + pal[i].g = palette[i].green >> 2; + pal[i].b = palette[i].blue >> 2; + } + + for (; i < 256; i++) + pal[i].r = pal[i].g = pal[i].b = 0; + } + } + else { + generate_332_palette(pal); + } + + rowbytes = png_get_rowbytes(png_ptr, info_ptr); + + /* Allocate the memory to hold the image using the fields of info_ptr. */ + bpp = rowbytes * 8 / width; + + /* Allegro cannot handle less than 8 bpp. */ + if (bpp < 8) + bpp = 8; + + dest_bpp = _color_load_depth(bpp, (bpp == 32)); + bmp = create_bitmap_ex(bpp, width, height); + + /* Maybe flip RGB to BGR. */ + if ((bpp == 24) || (bpp == 32)) { + int c = makecol_depth(bpp, 0, 0, 255); + unsigned char *pc = (unsigned char *)&c; + if (pc[0] == 255) + png_set_bgr(png_ptr); +#ifdef ALLEGRO_BIG_ENDIAN + png_set_swap_alpha(png_ptr); +#endif + } + + /* Read the image, one line at a line (easier to debug!) */ + for (pass = 0; pass < number_passes; pass++) { + png_uint_32 y; + for (y = 0; y < height; y++) + png_read_row(png_ptr, bmp->line[y], NULL); + } + + /* Let Allegro convert the image into the desired colour depth. */ + if (dest_bpp != bpp) + bmp = _fixup_loaded_bitmap(bmp, pal, dest_bpp); + + /* Read rest of file, and get additional chunks in info_ptr. */ + png_read_end(png_ptr, info_ptr); + + return bmp; +} + + + +/* load_png: + * Load a PNG file from disk, doing colour coversion if required. + */ +BITMAP *load_png(AL_CONST char *filename, RGB *pal) +{ + PACKFILE *fp; + BITMAP *bmp; + + ASSERT(filename); + + fp = pack_fopen(filename, "r"); + if (!fp) + return NULL; + + bmp = load_png_pf(fp, pal); + + pack_fclose(fp); + + return bmp; +} + + + +/* load_png_pf: + * Load a PNG file from disk, doing colour coversion if required. + */ +BITMAP *load_png_pf(PACKFILE *fp, RGB *pal) +{ + BITMAP *bmp; + png_structp png_ptr; + png_infop info_ptr; + + ASSERT(fp); + + if (!check_if_png(fp)) { + return NULL; + } + + /* 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. + */ + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + (void *)NULL, NULL, NULL); + if (!png_ptr) { + return NULL; + } + + /* Allocate/initialize the memory for image information. */ + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return NULL; + } + + /* Set error handling if you are using the setjmp/longjmp method (this is + * the normal method of doing things with libpng). REQUIRED unless you + * set up your own error handlers in the png_create_read_struct() earlier. + */ + if (setjmp(png_ptr->jmpbuf)) { + /* Free all of the memory associated with the png_ptr and info_ptr */ + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + /* If we get here, we had a problem reading the file */ + return NULL; + } + + /* Use Allegro packfile routines. */ + png_set_read_fn(png_ptr, fp, (png_rw_ptr)read_data); + + /* We have already read some of the signature. */ + png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); + + /* Really load the image now. */ + bmp = really_load_png(png_ptr, info_ptr, pal); + + /* Clean up after the read, and free any memory allocated. */ + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + + return bmp; +} + + + +/* read_data_memory: + * Custom reader function to read a PNG file from a memory buffer. + */ + +typedef struct { + AL_CONST unsigned char *buffer; + png_uint_32 bufsize; + png_uint_32 current_pos; +} MEMORY_READER_STATE; + +static void read_data_memory(png_structp png_ptr, png_bytep data, png_uint_32 length) +{ + MEMORY_READER_STATE *f = (MEMORY_READER_STATE *)png_get_io_ptr(png_ptr); + + if (length > (f->bufsize - f->current_pos)) + png_error(png_ptr, "read error in read_data_memory (loadpng)"); + + memcpy(data, f->buffer + f->current_pos, length); + f->current_pos += length; +} + + + +/* check_if_png_memory: + * Check if input buffer is really PNG format. + */ +static int check_if_png_memory(AL_CONST void *buffer) +{ + unsigned char *buf = (unsigned char *)buffer; + return (png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK) == 0); +} + + + +/* load_memory_png: + * Load a PNG file from memory, doing colour coversion if required. + */ +BITMAP *load_memory_png(AL_CONST void *buffer, int bufsize, RGB *pal) +{ + MEMORY_READER_STATE memory_reader_state; + BITMAP *bmp; + png_structp png_ptr; + png_infop info_ptr; + + if (!buffer || (bufsize <= 0)) + return NULL; + + if (!check_if_png_memory(buffer)) + return NULL; + + /* 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. + */ + png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + (void *)NULL, NULL, NULL); + if (!png_ptr) + return NULL; + + /* Allocate/initialize the memory for image information. */ + info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); + return NULL; + } + + /* Set error handling if you are using the setjmp/longjmp method (this is + * the normal method of doing things with libpng). REQUIRED unless you + * set up your own error handlers in the png_create_read_struct() earlier. + */ + if (setjmp(png_ptr->jmpbuf)) { + /* Free all of the memory associated with the png_ptr and info_ptr */ + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + /* If we get here, we had a problem reading the file */ + return NULL; + } + + /* Set up the reader state. */ + memory_reader_state.buffer = (unsigned char *)buffer; + memory_reader_state.bufsize = bufsize; + memory_reader_state.current_pos = PNG_BYTES_TO_CHECK; + + /* Tell libpng to use our custom reader. */ + png_set_read_fn(png_ptr, &memory_reader_state, (png_rw_ptr)read_data_memory); + + /* We have already read some of the signature. */ + png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); + + /* Really load the image now. */ + bmp = really_load_png(png_ptr, info_ptr, pal); + + /* Clean up after the read, and free any memory allocated. */ + png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); + + return bmp; +} diff --git a/third_party/loadpng/loadpng.h b/third_party/loadpng/loadpng.h new file mode 100644 index 000000000..9d584de15 --- /dev/null +++ b/third_party/loadpng/loadpng.h @@ -0,0 +1,75 @@ +/* loadpng.h */ +/* This file is hereby placed in the public domain. */ +#ifndef _included_loadpng_h_ +#define _included_loadpng_h_ + +#ifdef __cplusplus +extern "C" { +#endif + + + +/* Overkill :-) */ +#define LOADPNG_VERSION 1 +#define LOADPNG_SUBVERSION 5 +#define LOADPNG_VERSIONSTR "1.5" + + +/* _png_screen_gamma is slightly overloaded (sorry): + * + * A value of 0.0 means: Don't do any gamma correction in load_png() + * and load_memory_png(). This meaning was introduced in v1.4. + * + * A value of -1.0 means: Use the value from the environment variable + * SCREEN_GAMMA (if available), otherwise fallback to a value of 2.2 + * (a good guess for PC monitors, and the value for sRGB colourspace). + * This is the default. + * + * Otherwise, the value of _png_screen_gamma is taken as-is. + */ +extern double _png_screen_gamma; + + +/* Choose zlib compression level for saving file. + * Default is Z_BEST_COMPRESSION. + */ +extern int _png_compression_level; + + +/* Load a PNG from disk. */ +extern BITMAP *load_png(AL_CONST char *filename, RGB *pal); + +/* Load a PNG from some place. */ +extern BITMAP *load_png_pf(PACKFILE *fp, RGB *pal); + +/* Load a PNG from memory. */ +extern BITMAP *load_memory_png(AL_CONST void *buffer, int buffer_size, RGB *pal); + +/* Save a bitmap to disk in PNG format. */ +extern int save_png(AL_CONST char *filename, BITMAP *bmp, AL_CONST RGB *pal); + +/* Adds `PNG' to Allegro's internal file type table. + * You can then just use load_bitmap and save_bitmap as usual. + */ +extern void register_png_file_type(void); + +/* Register an datafile type ID with Allegro, so that when an object + * with that type ID is encountered while loading a datafile, that + * object will be loaded as a PNG file. + */ +extern void register_png_datafile_object(int id); + +/* This is supposed to resemble jpgalleg_init in JPGalleg 2.0, just in + * case you are lazier than lazy. It contains these 3 lines of code: + * register_png_datafile_object(DAT_ID('P','N','G',' ')); + * register_png_file_type(); + * return 0; + */ +extern int loadpng_init(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* _included_loadpng_h */ diff --git a/third_party/loadpng/regpng.c b/third_party/loadpng/regpng.c new file mode 100644 index 000000000..1681e4b8b --- /dev/null +++ b/third_party/loadpng/regpng.c @@ -0,0 +1,68 @@ +/* loadpng, Allegro wrapper routines for libpng + * by Peter Wang (tjaden@users.sf.net). + * + * This file is hereby placed in the public domain. + */ + + +#include +#include "loadpng.h" + + + +/* register_png_file_type: + */ +void register_png_file_type(void) +{ + register_bitmap_file_type("png", load_png, save_png); +} + + + +/* register_png_datafile_object: + */ + +static void *load_datafile_png(PACKFILE *f, long size) +{ + BITMAP *bmp; + void *buffer; + + buffer = malloc(size); + if (!buffer) + return NULL; + + if (pack_fread(buffer, size, f) != size) { + free(buffer); + return NULL; + } + + bmp = load_memory_png(buffer, size, NULL); + + free(buffer); + + return bmp; +} + +static void destroy_datafile_png(void *data) +{ + if (data) { + destroy_bitmap((BITMAP *)data); + } +} + +void register_png_datafile_object(int id) +{ + register_datafile_object(id, load_datafile_png, destroy_datafile_png); +} + + + +/* loadpng_init: + * This is supposed to resemble jpgalleg_init in JPGalleg 2.0. + */ +int loadpng_init(void) +{ + register_png_datafile_object(DAT_ID('P','N','G',' ')); + register_png_file_type(); + return 0; +} diff --git a/third_party/loadpng/savepng.c b/third_party/loadpng/savepng.c new file mode 100644 index 000000000..02a372531 --- /dev/null +++ b/third_party/loadpng/savepng.c @@ -0,0 +1,306 @@ +/* loadpng, Allegro wrapper routines for libpng + * by Peter Wang (tjaden@users.sf.net). + * + * This file is hereby placed in the public domain. + */ + + +#include +#include +#include "loadpng.h" + + + +/* 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; yh; 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; yh; y++) { + unsigned char *p = rowdata; + + for (x=0; xw; 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; yh; 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; yh; y++) { + unsigned char *p = rowdata; + + for (x=0; xw; 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) +{ + 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(png_ptr->jmpbuf)) { + /* If we get here, we had a problem reading the file. */ + goto Error; + } + + /* 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; +}