Root/
Source at commit 73ceffa51d3450d2d20ce933e7fc6da187e09fc8 created 8 years 11 months ago. By Maarten ter Huurne, Fix bug in section directory creation method | |
---|---|
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 | |
18 | static 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 | |
27 | SDL_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, ¶m, __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 | |
165 | cleanup: |
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 |
Branches:
install_locations
master
opkrun
packages