src/spriteloader/png.cpp
branchgamebalance
changeset 9913 e79cd19772dd
child 9631 8a2d1c2ceb88
equal deleted inserted replaced
9912:1ac8aac92385 9913:e79cd19772dd
       
     1 /* $Id$ */
       
     2 
       
     3 /** @file grf.cpp */
       
     4 
       
     5 #include "../stdafx.h"
       
     6 #include "../gfx.h"
       
     7 #include "../fileio.h"
       
     8 #include "../variables.h"
       
     9 #include "../debug.h"
       
    10 #include "png.hpp"
       
    11 #include <png.h>
       
    12 
       
    13 #define PNG_SLOT 62
       
    14 
       
    15 static void PNGAPI png_my_read(png_structp png_ptr, png_bytep data, png_size_t length)
       
    16 {
       
    17 	FioReadBlock(data, length);
       
    18 }
       
    19 
       
    20 static void PNGAPI png_my_error(png_structp png_ptr, png_const_charp message)
       
    21 {
       
    22 	DEBUG(sprite, 0, "ERROR (libpng): %s - %s", message, (char *)png_get_error_ptr(png_ptr));
       
    23 	longjmp(png_ptr->jmpbuf, 1);
       
    24 }
       
    25 
       
    26 static void PNGAPI png_my_warning(png_structp png_ptr, png_const_charp message)
       
    27 {
       
    28 	DEBUG(sprite, 0, "WARNING (libpng): %s - %s", message, (char *)png_get_error_ptr(png_ptr));
       
    29 }
       
    30 
       
    31 static bool OpenPNGFile(const char *filename, uint32 id, bool mask)
       
    32 {
       
    33 	char png_file[MAX_PATH];
       
    34 
       
    35 	snprintf(png_file, sizeof(png_file), "sprites" PATHSEP "%s" PATHSEP "%d%s.png", filename, id, mask ? "m" : "");
       
    36 	if (FioCheckFileExists(png_file)) {
       
    37 		FioOpenFile(PNG_SLOT, png_file);
       
    38 		return true;
       
    39 	}
       
    40 
       
    41 	/* TODO -- Add TAR support */
       
    42 	return false;
       
    43 }
       
    44 
       
    45 static bool LoadPNG(SpriteLoader::Sprite *sprite, const char *filename, uint32 id, bool mask)
       
    46 {
       
    47 	png_byte header[8];
       
    48 	png_structp png_ptr;
       
    49 	png_infop info_ptr, end_info;
       
    50 	uint bit_depth, color_type;
       
    51 	uint i, pixelsize;
       
    52 	png_bytep row_pointer;
       
    53 	SpriteLoader::CommonPixel *dst;
       
    54 
       
    55 	if (!OpenPNGFile(filename, id, mask)) return mask; // If mask is true, and file not found, continue true anyway, as it isn't a show-stopper
       
    56 
       
    57 	/* Check the header */
       
    58 	FioReadBlock(header, 8);
       
    59 	if (png_sig_cmp(header, 0, 8) != 0) return false;
       
    60 
       
    61 	/* Create the reader */
       
    62 	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, png_my_error, png_my_warning);
       
    63 	if (png_ptr == NULL) return false;
       
    64 
       
    65 	/* Create initial stuff */
       
    66 	info_ptr = png_create_info_struct(png_ptr);
       
    67 	if (info_ptr == NULL) {
       
    68 		png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
       
    69 		return false;
       
    70 	}
       
    71 	end_info = png_create_info_struct(png_ptr);
       
    72 	if (end_info == NULL) {
       
    73 		png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
       
    74 		return false;
       
    75 	}
       
    76 
       
    77 	/* Make sure that upon error, we can clean up graceful */
       
    78 	if (setjmp(png_jmpbuf(png_ptr))) {
       
    79 		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
       
    80 		return false;
       
    81 	}
       
    82 
       
    83 	/* Read the file */
       
    84 	png_set_read_fn(png_ptr, NULL, png_my_read);
       
    85 	png_set_sig_bytes(png_ptr, 8);
       
    86 
       
    87 	png_read_info(png_ptr, info_ptr);
       
    88 
       
    89 	if (!mask) {
       
    90 		/* Read the text chunks */
       
    91 		png_textp text_ptr;
       
    92 		int num_text = 0;
       
    93 		png_get_text(png_ptr, info_ptr, &text_ptr, &num_text);
       
    94 		if (num_text == 0) DEBUG(misc, 0, "Warning: PNG Sprite '%s/%d.png' doesn't have x_offs and y_offs; expect graphical problems", filename, id);
       
    95 		for (int i = 0; i < num_text; i++) {
       
    96 			/* x_offs and y_offs are in the text-chunk of PNG */
       
    97 			if (strcmp("x_offs", text_ptr[i].key) == 0) sprite->x_offs = strtol(text_ptr[i].text, NULL, 0);
       
    98 			if (strcmp("y_offs", text_ptr[i].key) == 0) sprite->y_offs = strtol(text_ptr[i].text, NULL, 0);
       
    99 		}
       
   100 
       
   101 		sprite->height = info_ptr->height;
       
   102 		sprite->width  = info_ptr->width;
       
   103 		sprite->data = CallocT<SpriteLoader::CommonPixel>(sprite->width * sprite->height);
       
   104 	}
       
   105 
       
   106 	bit_depth  = png_get_bit_depth(png_ptr, info_ptr);
       
   107 	color_type = png_get_color_type(png_ptr, info_ptr);
       
   108 
       
   109 	if (mask && (bit_depth != 8 || color_type != PNG_COLOR_TYPE_PALETTE)) {
       
   110 		DEBUG(misc, 0, "Ignoring mask for SpriteID %d as it isn't a 8 bit palette image", id);
       
   111 		return true;
       
   112 	}
       
   113 
       
   114 	if (!mask) {
       
   115 		if (bit_depth == 16) png_set_strip_16(png_ptr);
       
   116 
       
   117 		if (color_type == PNG_COLOR_TYPE_PALETTE) {
       
   118 			png_set_palette_to_rgb(png_ptr);
       
   119 			color_type = PNG_COLOR_TYPE_RGB;
       
   120 		}
       
   121 		if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) {
       
   122 			png_set_gray_to_rgb(png_ptr);
       
   123 			color_type = PNG_COLOR_TYPE_RGB;
       
   124 		}
       
   125 
       
   126 #ifdef TTD_LITTLE_ENDIAN
       
   127 		png_set_bgr(png_ptr);
       
   128 #else
       
   129 		if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) png_set_swap_alpha(png_ptr);
       
   130 #endif
       
   131 
       
   132 		if (color_type == PNG_COLOR_TYPE_RGB) {
       
   133 #ifdef TTD_LITTLE_ENDIAN
       
   134 			png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
       
   135 #else
       
   136 			png_set_filler(png_ptr, 0xff, PNG_FILLER_BEFORE);
       
   137 #endif
       
   138 		}
       
   139 
       
   140 		pixelsize = sizeof(uint32);
       
   141 	} else {
       
   142 		pixelsize = sizeof(uint8);
       
   143 	}
       
   144 
       
   145 	row_pointer = (png_byte *)malloc(info_ptr->width * pixelsize);
       
   146 	if (row_pointer == NULL) {
       
   147 		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
       
   148 		return false;
       
   149 	}
       
   150 
       
   151 	for (i = 0; i < info_ptr->height; i++) {
       
   152 		png_read_row(png_ptr, row_pointer, NULL);
       
   153 
       
   154 		dst = sprite->data + i * info_ptr->width;
       
   155 
       
   156 		for (uint x = 0; x < info_ptr->width; x++) {
       
   157 			if (mask) {
       
   158 				if (row_pointer[x * sizeof(uint8)] != 0) {
       
   159 					dst[x].b = 0;
       
   160 					dst[x].g = 0;
       
   161 					dst[x].r = 0;
       
   162 					/* Alpha channel is used from the original image (to allow transparency in remap colors) */
       
   163 					dst[x].m = row_pointer[x * sizeof(uint8)];
       
   164 				}
       
   165 			} else {
       
   166 				dst[x].b = row_pointer[x * sizeof(uint32) + 0];
       
   167 				dst[x].g = row_pointer[x * sizeof(uint32) + 1];
       
   168 				dst[x].r = row_pointer[x * sizeof(uint32) + 2];
       
   169 				dst[x].a = row_pointer[x * sizeof(uint32) + 3];
       
   170 				dst[x].m = 0;
       
   171 			}
       
   172 		}
       
   173 	}
       
   174 
       
   175 	free(row_pointer);
       
   176 	png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
       
   177 
       
   178 	return true;
       
   179 }
       
   180 
       
   181 bool SpriteLoaderPNG::LoadSprite(SpriteLoader::Sprite *sprite, const char *filename, uint32 file_pos)
       
   182 {
       
   183 	if (!LoadPNG(sprite, filename, file_pos, false)) return false;
       
   184 	if (!LoadPNG(sprite, filename, file_pos, true)) return false;
       
   185 	return true;
       
   186 }