Root/
| 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
