1
0
mirror of git://projects.qi-hardware.com/gmenu2x.git synced 2024-11-29 12:43:09 +02:00

PNG: Use libpng directly instead of via SDL_image.

This eliminates the build-time dependency on SDL_image and the run-time
dependencies on all libraries used by SDL_image except SDL and libpng.

In addition we now let libpng convert to ARGB format while decoding the
image, rather than letting SDL convert the surface afterwards.
This commit is contained in:
Maarten ter Huurne 2011-06-05 05:58:47 +02:00
parent 2394a075d7
commit 7aeb7a4f0a
2 changed files with 115 additions and 34 deletions

View File

@ -15,17 +15,13 @@ SDL_VERSION=1.2.8
AM_PATH_SDL($SDL_VERSION, :, AM_PATH_SDL($SDL_VERSION, :,
AC_MSG_ERROR([*** SDL version $SDL_VERSION not found!])) AC_MSG_ERROR([*** SDL version $SDL_VERSION not found!]))
AC_ARG_WITH(sdl-image-prefix,
[ --with-sdl-image-prefix=DIR specify where SDL_image library is installed],
[SDL_IMAGE_PREFIX="$withval"])
AC_CHECK_LIB(SDL_image, IMG_LoadPNG_RW,,check_sdl_image="no")
AC_ARG_WITH(sdl-gfx-prefix, AC_ARG_WITH(sdl-gfx-prefix,
[ --with-sdl-gfx-prefix=DIR specify where SDL_gfx library is installed], [ --with-sdl-gfx-prefix=DIR specify where SDL_gfx library is installed],
[SDL_GFX_PREFIX="$withval"]) [SDL_GFX_PREFIX="$withval"])
AC_CHECK_LIB(SDL_gfx, rotozoomSurfaceXY,,check_sdl_gfx="no") AC_CHECK_LIB(SDL_gfx, rotozoomSurfaceXY,,check_sdl_gfx="no")
# Check for libpng
AC_CHECK_LIB(png, png_read_image,,check_png="no")
AC_OUTPUT(Makefile src/Makefile) AC_OUTPUT(Makefile src/Makefile)

View File

@ -1,37 +1,122 @@
// Written by Maarten ter Huurne in 2011.
// Based on the libpng example.c source, with some additional inspiration
// from SDL_image and openMSX.
// License: GPL version 2 or later.
#include "imageio.h" #include "imageio.h"
#include <SDL_image.h> #include "debug.h"
#include <SDL.h>
#include <png.h>
#include <cassert>
SDL_Surface *loadPNG(const std::string &path) { SDL_Surface *loadPNG(const std::string &path) {
// Load PNG file into an SDL surface. // Declare these with function scope and initialize them to NULL,
SDL_Surface *surface = IMG_Load(path.c_str()); // so we can use a single cleanup block at the end of the function.
if (!surface) { SDL_Surface *surface = NULL;
return NULL; FILE *fp = NULL;
png_structp png = NULL;
png_infop info = NULL;
// Create and initialize the top-level libpng struct.
png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png) goto cleanup;
// Create and initialize the image information struct.
info = png_create_info_struct(png);
if (!info) goto cleanup;
// Setup error handling for errors detected by libpng.
if (setjmp(png_jmpbuf(png))) {
// Note: This gets executed when an error occurs.
if (surface) {
SDL_FreeSurface(surface);
surface = NULL;
}
goto cleanup;
} }
// Make sure we have a surface that can be blitted using alpha blending. // Open input file.
if (surface->format->BytesPerPixel == 4) { fp = fopen(path.c_str(), "rb");
SDL_SetAlpha(surface, SDL_SRCALPHA, 255); if (!fp) goto cleanup;
return surface; // Set up the input control if you are using standard C streams.
png_init_io(png, fp);
// 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, info);
png_uint_32 width, height;
int bitDepth, colorType;
png_get_IHDR(
png, info, &width, &height, &bitDepth, &colorType, NULL, NULL, NULL);
// Select ARGB pixel format:
// (ARGB is the native pixel format for the JZ47xx frame buffer in 24bpp)
// - strip 16 bit/color files down to 8 bits/color
png_set_strip_16(png);
// - convert 1/2/4 bpp to 8 bpp
png_set_packing(png);
// - expand paletted images to RGB
// - expand grayscale images of less than 8-bit depth to 8-bit depth
// - expand tRNS chunks to alpha channels
png_set_expand(png);
// - convert grayscale to RGB
png_set_gray_to_rgb(png);
// - add alpha channel
png_set_add_alpha(png, 0xFF, PNG_FILLER_AFTER);
// - convert RGBA to ARGB
if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
png_set_swap_alpha(png);
} else { } else {
bool hasAlphaChannel = surface->format->Amask != 0; png_set_bgr(png); // BGRA in memory becomes ARGB in register
SDL_PixelFormat format32;
memset(&format32, 0, sizeof(format32));
format32.BitsPerPixel = 32;
format32.BytesPerPixel = 4;
format32.Rmask =
SDL_BYTEORDER == SDL_BIG_ENDIAN ? 0x00FF0000 : 0x000000FF;
format32.Gmask = 0x0000FF00;
format32.Bmask =
SDL_BYTEORDER == SDL_BIG_ENDIAN ? 0x000000FF : 0x00FF0000;
format32.Amask = hasAlphaChannel ? 0xFF000000 : 0;
format32.Rshift = SDL_BYTEORDER == SDL_BIG_ENDIAN ? 16 : 0;
format32.Gshift = 8;
format32.Bshift = SDL_BYTEORDER == SDL_BIG_ENDIAN ? 0 : 16;
format32.Ashift = hasAlphaChannel ? 24 : 0;
SDL_Surface *surface32 = SDL_ConvertSurface(
surface, &format32, hasAlphaChannel ? SDL_SRCALPHA : 0);
SDL_FreeSurface(surface);
return surface32;
} }
// Update the image info to the post-conversion state.
png_read_update_info(png, info);
png_get_IHDR(
png, info, &width, &height, &bitDepth, &colorType, NULL, NULL, NULL);
assert(bitDepth == 8);
assert(colorType == PNG_COLOR_TYPE_RGB_ALPHA);
// Refuse to load outrageously large images.
if (width > 65536) {
WARNING("Refusing to load image because it is too wide\n");
goto cleanup;
}
if (height > 2048) {
WARNING("Refusing to load image because it is too high\n");
goto cleanup;
}
// Allocate ARGB surface to hold the image.
surface = SDL_CreateRGBSurface(
SDL_SWSURFACE | SDL_SRCALPHA, width, height, 32,
0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000
);
if (!surface) {
// Failed to create surface, probably out of memory.
goto cleanup;
}
// Compute row pointers.
png_bytep rowPointers[height];
for (png_uint_32 y = 0; y < height; y++) {
rowPointers[y] =
static_cast<png_bytep>(surface->pixels) + y * surface->pitch;
}
// Read the entire image in one go.
png_read_image(png, rowPointers);
// Read rest of file, and get additional chunks in the info struct.
// Note: We got all we need, so skip this step.
//png_read_end(png, info);
cleanup:
// Clean up.
png_destroy_read_struct(&png, &info, NULL);
if (fp) fclose(fp);
return surface;
} }