mirror of
git://projects.qi-hardware.com/gmenu2x.git
synced 2024-11-25 18:05:20 +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:
parent
2394a075d7
commit
7aeb7a4f0a
@ -15,17 +15,13 @@ SDL_VERSION=1.2.8
|
||||
AM_PATH_SDL($SDL_VERSION, :,
|
||||
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,
|
||||
[ --with-sdl-gfx-prefix=DIR specify where SDL_gfx library is installed],
|
||||
[SDL_GFX_PREFIX="$withval"])
|
||||
|
||||
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)
|
||||
|
141
src/imageio.cpp
141
src/imageio.cpp
@ -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 <SDL_image.h>
|
||||
#include "debug.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <png.h>
|
||||
#include <cassert>
|
||||
|
||||
|
||||
SDL_Surface *loadPNG(const std::string &path) {
|
||||
// Load PNG file into an SDL surface.
|
||||
SDL_Surface *surface = IMG_Load(path.c_str());
|
||||
if (!surface) {
|
||||
return NULL;
|
||||
// Declare these with function scope and initialize them to NULL,
|
||||
// so we can use a single cleanup block at the end of the function.
|
||||
SDL_Surface *surface = 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.
|
||||
if (surface->format->BytesPerPixel == 4) {
|
||||
SDL_SetAlpha(surface, SDL_SRCALPHA, 255);
|
||||
return surface;
|
||||
// Open input file.
|
||||
fp = fopen(path.c_str(), "rb");
|
||||
if (!fp) goto cleanup;
|
||||
// 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 {
|
||||
bool hasAlphaChannel = surface->format->Amask != 0;
|
||||
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;
|
||||
png_set_bgr(png); // BGRA in memory becomes ARGB in register
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user