Root/src/imageio.cpp

Source at commit 00d3c3b57008bddf8770c51271db9d766acaf854 created 7 years 5 months ago.
By Maarten ter Huurne, Gave Layer class a protected constructor
1// Written by Maarten ter Huurne in 2011.
2// Based on the libpng example.c source, with some additional inspiration
3// from SDL_image and openMSX.
4// License: GPL version 2 or later.
5
6
7#include "imageio.h"
8
9#include "debug.h"
10
11#include <SDL.h>
12#include <png.h>
13#include <cassert>
14
15#ifdef HAVE_LIBOPK
16#include <opk.h>
17
18static void __readFromOpk(png_structp png_ptr, png_bytep ptr, png_size_t length)
19{
20    char **buf = (char **) png_get_io_ptr(png_ptr);
21
22    memcpy(ptr, *buf, length);
23    *buf += length;
24}
25#endif
26
27SDL_Surface *loadPNG(const std::string &path, bool loadAlpha) {
28    // Declare these with function scope and initialize them to NULL,
29    // so we can use a single cleanup block at the end of the function.
30    SDL_Surface *surface = NULL;
31    FILE *fp = NULL;
32    png_structp png = NULL;
33    png_infop info = NULL;
34#ifdef HAVE_LIBOPK
35    std::string::size_type pos;
36    struct OPK *opk = NULL;
37    void *buffer = NULL, *param;
38#endif
39
40    // Create and initialize the top-level libpng struct.
41    png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
42    if (!png) goto cleanup;
43    // Create and initialize the image information struct.
44    info = png_create_info_struct(png);
45    if (!info) goto cleanup;
46    // Setup error handling for errors detected by libpng.
47    if (setjmp(png_jmpbuf(png))) {
48        // Note: This gets executed when an error occurs.
49        if (surface) {
50            SDL_FreeSurface(surface);
51            surface = NULL;
52        }
53        goto cleanup;
54    }
55
56#ifdef HAVE_LIBOPK
57    pos = path.find('#');
58    if (pos != path.npos) {
59        int ret;
60        size_t length;
61
62        DEBUG("Registering specific callback for icon %s\n", path.c_str());
63
64        opk = opk_open(path.substr(0, pos).c_str());
65        if (!opk) {
66            ERROR("Unable to open OPK\n");
67            goto cleanup;
68        }
69
70        ret = opk_extract_file(opk, path.substr(pos + 1).c_str(),
71                    &buffer, &length);
72        if (ret < 0) {
73            ERROR("Unable to extract icon from OPK\n");
74            goto cleanup;
75        }
76
77        param = buffer;
78
79        png_set_read_fn(png, &param, __readFromOpk);
80    } else {
81#else
82    if (1) {
83#endif /* HAVE_LIBOPK */
84        fp = fopen(path.c_str(), "rb");
85        if (!fp) goto cleanup;
86
87        // Set up the input control if you are using standard C streams.
88        png_init_io(png, fp);
89    }
90
91    // The call to png_read_info() gives us all of the information from the
92    // PNG file before the first IDAT (image data chunk).
93    png_read_info(png, info);
94    png_uint_32 width, height;
95    int bitDepth, colorType;
96    png_get_IHDR(
97        png, info, &width, &height, &bitDepth, &colorType, NULL, NULL, NULL);
98
99    // Select ARGB pixel format:
100    // (ARGB is the native pixel format for the JZ47xx frame buffer in 24bpp)
101    // - strip 16 bit/color files down to 8 bits/color
102    png_set_strip_16(png);
103    // - convert 1/2/4 bpp to 8 bpp
104    png_set_packing(png);
105    // - expand paletted images to RGB
106    // - expand grayscale images of less than 8-bit depth to 8-bit depth
107    // - expand tRNS chunks to alpha channels
108    png_set_expand(png);
109    // - convert grayscale to RGB
110    png_set_gray_to_rgb(png);
111    // - add alpha channel
112    png_set_add_alpha(png, 0xFF, PNG_FILLER_AFTER);
113    // - convert RGBA to ARGB
114    if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
115        png_set_swap_alpha(png);
116    } else {
117        png_set_bgr(png); // BGRA in memory becomes ARGB in register
118    }
119
120    // Update the image info to the post-conversion state.
121    png_read_update_info(png, info);
122    png_get_IHDR(
123        png, info, &width, &height, &bitDepth, &colorType, NULL, NULL, NULL);
124    assert(bitDepth == 8);
125    assert(colorType == PNG_COLOR_TYPE_RGB_ALPHA);
126
127    // Refuse to load outrageously large images.
128    if (width > 65536) {
129        WARNING("Refusing to load image because it is too wide\n");
130        goto cleanup;
131    }
132    if (height > 2048) {
133        WARNING("Refusing to load image because it is too high\n");
134        goto cleanup;
135    }
136
137    // Allocate [A]RGB surface to hold the image.
138    surface = SDL_CreateRGBSurface(
139        SDL_SWSURFACE | SDL_SRCALPHA, width, height, 32,
140        0x00FF0000, 0x0000FF00, 0x000000FF, loadAlpha ? 0xFF000000 : 0x00000000
141        );
142    if (!surface) {
143        // Failed to create surface, probably out of memory.
144        goto cleanup;
145    }
146
147    // Note: GCC 4.9 doesn't want to jump over 'rowPointers' with goto
148    // if it is in the outer scope.
149    {
150        // Compute row pointers.
151        png_bytep rowPointers[height];
152        for (png_uint_32 y = 0; y < height; y++) {
153            rowPointers[y] =
154                static_cast<png_bytep>(surface->pixels) + y * surface->pitch;
155        }
156
157        // Read the entire image in one go.
158        png_read_image(png, rowPointers);
159    }
160
161    // Read rest of file, and get additional chunks in the info struct.
162    // Note: We got all we need, so skip this step.
163    //png_read_end(png, info);
164
165cleanup:
166    // Clean up.
167    png_destroy_read_struct(&png, &info, NULL);
168    if (fp) fclose(fp);
169#ifdef HAVE_LIBOPK
170    if (buffer)
171        free(buffer);
172    if (opk)
173        opk_close(opk);
174#endif
175
176    return surface;
177}
178

Archive Download this file



interactive