/* $Id: loadpng.c,v 1.1 2004/04/26 20:24:47 mlefebvr Exp $ */ #include #include #include #include "ithelib.h" #include "media.h" /* IRE transparent colors */ #ifdef _WIN32 #define snprintf _snprintf // God I hate Microsoft #endif BITMAP *iload_png(char *filename) { IFILE *img_fp; #ifdef _WIN32 #define signature_size 8 #else const int signature_size = 8; /* Nb of magic bytes in a png signature */ #endif unsigned char magic[signature_size]; char *img_pixels = NULL; /* Pixels stream from the PNG image */ /* * png variables */ png_structp png_ptr = NULL; png_infop info_ptr = NULL; int color_type; /* Type of the png image */ int bit_depth; /* bits per _channel_ (red, green, blue, alpha) */ png_byte channels; /* nb of channels. channels * bit_depth = bpp */ png_uint_32 width, height; png_colorp img_palette; /* palette of the png file, if any */ int num_palette; /* number of colors in the png file palette, if any */ png_bytep trans; /* array of transparencies attached to the palette */ int num_trans; /* number of transparency entries */ png_color_16p trans_values; /* RGB samples for the transparent color (>8bpp) */ /* array of pointers to scanlines, needed to load the image * matches the lines of img_pixels */ png_bytep *row_pointers = NULL; png_uint_32 rowbytes; /* size of a row in bytes */ /* * Allegro variables */ BITMAP *bmp = NULL; /* Allegro bitmap */ PALETTE bmp_palette; /* Palette associated to the Allegro bitmap */ /* * Open png file and check signature */ img_fp = iopen(filename); /* handles errors */ memset (magic, '\0', sizeof magic); iread(magic, signature_size, img_fp); /* Check whether the signature matches a png file */ if (png_sig_cmp(magic, 0, signature_size) != 0) { iclose(img_fp); ithe_panic("PNG: Not a png file", filename); } /* * Create the main png variables */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) NULL, (png_error_ptr) NULL, (png_error_ptr) NULL); if (png_ptr == NULL) { /* error */ iclose(img_fp); ithe_panic("PNG: png_create_read_struct() error", filename); } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { /* error */ iclose(img_fp); png_destroy_read_struct(&png_ptr, NULL, NULL); ithe_panic("PNG: png_create_info_struct() error", filename); } /* libpng requires this */ if (setjmp(png_jmpbuf(png_ptr)) != 0) { /* error */ iclose(img_fp); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); ithe_panic("PNG: setjmp() failed", filename); } /* Set the png input stream */ png_init_io(png_ptr, img_fp->fp); /*Caveat to the IRE fs funcs.?*/ /* Tell libpng we have already read the signature of the png file */ png_set_sig_bytes(png_ptr, signature_size); /* Get info header from the png image */ png_read_info(png_ptr, info_ptr); /* * Transform image */ bit_depth = png_get_bit_depth(png_ptr, info_ptr); /* Strip 16 bits per channel to 8 bits */ if (bit_depth == 16) { png_set_strip_16(png_ptr); } if (bit_depth < 8) { png_set_packing(png_ptr); /* At least 1 byte per pixel */ } /* Get the image type: * grayscale, grayscale + alpha, paletted, RGB, RGBA */ color_type = png_get_color_type(png_ptr, info_ptr); /* I don't want to deal with grayscale images */ if ((color_type == PNG_COLOR_TYPE_GRAY) || (color_type == PNG_COLOR_TYPE_GRAY_ALPHA)) { png_set_gray_to_rgb(png_ptr); } /* Update the "info" structure of the png image */ png_read_update_info(png_ptr, info_ptr); /* Get updated informations about the image */ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); /* * Simple transparency (i.e without Alpha channel) */ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_get_tRNS(png_ptr, info_ptr, &trans /* array of transparent entries */, &num_trans /* nb of transparent entries */, &trans_values); } else { /* Sanity */ trans = NULL; num_trans = 0; trans_values = NULL; } /* Create the Allegro bitmap */ bmp = create_bitmap(width, height); if (bmp == NULL) { ithe_panic("PNG: failed to create bitmap", filename); } /* Get the number of channels in the image * _libpng_ says: * (valid values are 1 (GRAY, PALETTE), 2 (GRAY_ALPHA), * 3 (RGB), 4 (RGB_ALPHA or RGB + filler byte)) */ channels = png_get_channels(png_ptr, info_ptr); /* * Allocate a memory segment to store the image */ rowbytes = png_get_rowbytes(png_ptr, info_ptr); /* nb of bytes per row */ img_pixels = (unsigned char *) M_get(1, height * rowbytes); /* Allocate an array of row pointers, as required by libpng */ row_pointers = (png_bytep *) M_get(height, sizeof (png_bytep)); /* Match it to the data segment previously allocated */ { int row; for (row = 0; row < height; row++) { row_pointers[row] = (png_bytep) (img_pixels + (row * rowbytes)); } } /* Load the whole (transformed) png image in memory */ png_read_image(png_ptr, row_pointers); png_read_end(png_ptr, NULL); /* required, but useless */ iclose(img_fp); /* * Fill in the Allegro bitmap */ if (channels == 1) /* Paletted image */ { /* * Pixels are byte-length indexes to the palette. */ int i; int x, y; unsigned char pixel; int color; /* Allegro point of view */ /* * Get the palette */ png_get_PLTE(png_ptr, info_ptr, &img_palette, &num_palette); /* Transparent indexes first */ for (i = 0; i < num_trans; i++) { bmp_palette[i].r = ire_transparent_r; bmp_palette[i].g = ire_transparent_g; bmp_palette[i].b = ire_transparent_b; } /* go on with other indexes */ for (; i < num_palette; i++) { bmp_palette[i].r = (unsigned char) img_palette[i].red; bmp_palette[i].g = (unsigned char) img_palette[i].green; bmp_palette[i].b = (unsigned char) img_palette[i].blue; } /* * Fill in the pixels grid of the bitmap */ for (y = 0; y < height; y ++) { for (x = 0; x < width; x ++) { pixel = (unsigned char) row_pointers[y][x]; color = makecol(bmp_palette[pixel].r, bmp_palette[pixel].g, bmp_palette[pixel].b); putpixel(bmp, x, y, color); } } } else if (channels >= 3) /* RGB or RGBA image */ { /* * Pixels are a stream of RGB or RGBA samples, each byte-length. */ int x,y; unsigned char *pixel; /* RGB offsets relatively to a pixel starting position */ const int red_offset = 0, green_offset = 1, blue_offset = 2, alpha_offset = 3; /* RGB samples of a pixel color */ int red_sample, green_sample, blue_sample; int color; /* Allegro point of view */ for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { /* * The pixel info is at: * img_pixels + (y * rowbytes) + (x * channels) * also written: * row_pointers[y] + (x * channels) */ pixel = (unsigned char *) (row_pointers[y] + (x * channels)); /* * Translate transparent pixels to IRE transparency * if there is an alpha channel or a transparent color * in the image. */ /* Transparency through alpha channel * The alpha channel in a PNG image means a level * of opacity, ranging from 0 (transparency) to 255. * * From _this_ program point of view, * a pixel can be either transparent or opaque, * translucency is not supported. * We arbitrarily use the value "0" as the only * meaning of transparency; any other value will * be taken as _opaque_. */ if ((channels == 4) && (*(pixel + alpha_offset) == 0)) { putpixel(bmp, x, y, ire_transparent); continue; /* next pixel */ } red_sample = *(pixel + red_offset), green_sample = *(pixel + green_offset), blue_sample = *(pixel + blue_offset); /* Single transparent color * Note: * Maybe it should be an "else if" relative to the * alpha channel test, I don't know if both an alpha * channel _and_ a single transparent color are * possible in a png image */ if ((trans_values != NULL) && (trans_values->red == red_sample) && (trans_values->green == green_sample) && (trans_values->blue == blue_sample)) { putpixel(bmp, x, y, ire_transparent); continue; /* next pixel */ } /* * Set the pixel in the Allegro bitmap */ color = makecol(red_sample, green_sample, blue_sample); putpixel(bmp, x, y, color); } } } else /* a freaky png image */ { char msg[80]; #ifndef __DJGPP__ snprintf(msg, sizeof msg, "PNG: strange number of channels: %d in file", channels); #else sprintf(msg,"PNG: strange number of channels: %d in file", channels); #endif /* Free memory */ png_destroy_read_struct(&png_ptr, &info_ptr, NULL); M_free(row_pointers); M_free(img_pixels); ithe_panic(msg, filename); } /* if (channels...) */ /* Free memory */ png_destroy_read_struct(&png_ptr, &info_ptr, NULL); M_free(row_pointers); M_free(img_pixels); return bmp; }