/** @brief Sets the index of the palette entry to be used as transparent color for the image specified. Does nothing on high color images. This method sets the index of the palette entry to be used as single transparent color for the image specified. This works on palletised images only and does nothing for high color images. Although it is possible for palletised images to have more than one transparent color, this method sets the palette entry specified as the single transparent color for the image. All other colors will be set to be non-transparent by this method. As with FreeImage_SetTransparencyTable(), this method also sets the image's transparency property to TRUE (as it is set and obtained by FreeImage_SetTransparent() and FreeImage_IsTransparent() respectively) for palletised images. @param dib Input image, whose transparent color is to be set. @param index The index of the palette entry to be set as transparent color. */ void DLL_CALLCONV FreeImage_SetTransparentIndex(FIBITMAP *dib, int index) { if (dib) { int count = FreeImage_GetColorsUsed(dib); if (count) { BYTE *new_tt = (BYTE *)malloc(count * sizeof(BYTE)); memset(new_tt, 0xFF, count); if ((index >= 0) && (index < count)) { new_tt[index] = 0x00; } FreeImage_SetTransparencyTable(dib, new_tt, count); free(new_tt); } } }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { png_structp png_ptr = NULL; png_infop info_ptr; png_uint_32 width, height; png_colorp png_palette = NULL; int color_type, palette_entries = 0; int bit_depth, pixel_depth; // pixel_depth = bit_depth * channels FIBITMAP *dib = NULL; RGBQUAD *palette = NULL; // pointer to dib palette png_bytepp row_pointers = NULL; int i; fi_ioStructure fio; fio.s_handle = handle; fio.s_io = io; if (handle) { BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; try { // check to see if the file is in fact a PNG file BYTE png_check[PNG_BYTES_TO_CHECK]; io->read_proc(png_check, PNG_BYTES_TO_CHECK, 1, handle); if (png_sig_cmp(png_check, (png_size_t)0, PNG_BYTES_TO_CHECK) != 0) { return NULL; // Bad signature } // create the chunk manage structure png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, error_handler, warning_handler); if (!png_ptr) { return NULL; } // create the info structure info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return NULL; } // init the IO png_set_read_fn(png_ptr, &fio, _ReadProc); if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return NULL; } // because we have already read the signature... png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); // read the IHDR chunk png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); pixel_depth = png_get_bit_depth(png_ptr, info_ptr) * png_get_channels(png_ptr, info_ptr); // get image data type (assume standard image type) FREE_IMAGE_TYPE image_type = FIT_BITMAP; if (bit_depth == 16) { if ((pixel_depth == 16) && (color_type == PNG_COLOR_TYPE_GRAY)) { image_type = FIT_UINT16; } else if ((pixel_depth == 48) && (color_type == PNG_COLOR_TYPE_RGB)) { image_type = FIT_RGB16; } else if ((pixel_depth == 64) && (color_type == PNG_COLOR_TYPE_RGB_ALPHA)) { image_type = FIT_RGBA16; } else { // tell libpng to strip 16 bit/color files down to 8 bits/color png_set_strip_16(png_ptr); bit_depth = 8; } } #ifndef FREEIMAGE_BIGENDIAN if((image_type == FIT_UINT16) || (image_type == FIT_RGB16) || (image_type == FIT_RGBA16)) { // turn on 16 bit byte swapping png_set_swap(png_ptr); } #endif // set some additional flags switch(color_type) { case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // flip the RGB pixels to BGR (or RGBA to BGRA) if(image_type == FIT_BITMAP) { png_set_bgr(png_ptr); } #endif break; case PNG_COLOR_TYPE_PALETTE: // expand palette images to the full 8 bits from 2 bits/pixel if (pixel_depth == 2) { png_set_packing(png_ptr); pixel_depth = 8; } break; case PNG_COLOR_TYPE_GRAY: // expand grayscale images to the full 8 bits from 2 bits/pixel // but *do not* expand fully transparent palette entries to a full alpha channel if (pixel_depth == 2) { png_set_expand_gray_1_2_4_to_8(png_ptr); pixel_depth = 8; } break; case PNG_COLOR_TYPE_GRAY_ALPHA: // expand 8-bit greyscale + 8-bit alpha to 32-bit png_set_gray_to_rgb(png_ptr); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // flip the RGBA pixels to BGRA png_set_bgr(png_ptr); #endif pixel_depth = 32; break; default: throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } // unlike the example in the libpng documentation, we have *no* idea where // this file may have come from--so if it doesn't have a file gamma, don't // do any correction ("do no harm") if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) { double gamma = 0; double screen_gamma = 2.2; if (png_get_gAMA(png_ptr, info_ptr, &gamma) && ( flags & PNG_IGNOREGAMMA ) != PNG_IGNOREGAMMA) { png_set_gamma(png_ptr, screen_gamma, gamma); } } // all transformations have been registered; now update info_ptr data png_read_update_info(png_ptr, info_ptr); // color type may have changed, due to our transformations color_type = png_get_color_type(png_ptr,info_ptr); // create a DIB and write the bitmap header // set up the DIB palette, if needed switch (color_type) { case PNG_COLOR_TYPE_RGB: png_set_invert_alpha(png_ptr); if(image_type == FIT_BITMAP) { dib = FreeImage_AllocateHeader(header_only, width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } else { dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth); } break; case PNG_COLOR_TYPE_RGB_ALPHA: if(image_type == FIT_BITMAP) { dib = FreeImage_AllocateHeader(header_only, width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } else { dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth); } break; case PNG_COLOR_TYPE_PALETTE: dib = FreeImage_AllocateHeader(header_only, width, height, pixel_depth); png_get_PLTE(png_ptr,info_ptr, &png_palette, &palette_entries); palette_entries = MIN((unsigned)palette_entries, FreeImage_GetColorsUsed(dib)); palette = FreeImage_GetPalette(dib); // store the palette for (i = 0; i < palette_entries; i++) { palette[i].rgbRed = png_palette[i].red; palette[i].rgbGreen = png_palette[i].green; palette[i].rgbBlue = png_palette[i].blue; } break; case PNG_COLOR_TYPE_GRAY: dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth); if(pixel_depth <= 8) { palette = FreeImage_GetPalette(dib); palette_entries = 1 << pixel_depth; for (i = 0; i < palette_entries; i++) { palette[i].rgbRed = palette[i].rgbGreen = palette[i].rgbBlue = (BYTE)((i * 255) / (palette_entries - 1)); } } break; default: throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } // store the transparency table if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { // array of alpha (transparency) entries for palette png_bytep trans_alpha = NULL; // number of transparent entries int num_trans = 0; // graylevel or color sample values of the single transparent color for non-paletted images png_color_16p trans_color = NULL; png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color); if((color_type == PNG_COLOR_TYPE_GRAY) && trans_color) { // single transparent color if (trans_color->gray < palette_entries) { BYTE table[256]; memset(table, 0xFF, palette_entries); table[trans_color->gray] = 0; FreeImage_SetTransparencyTable(dib, table, palette_entries); } } else if((color_type == PNG_COLOR_TYPE_PALETTE) && trans_alpha) { // transparency table FreeImage_SetTransparencyTable(dib, (BYTE *)trans_alpha, num_trans); } } // store the background color if (png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) { // Get the background color to draw transparent and alpha images over. // Note that even if the PNG file supplies a background, you are not required to // use it - you should use the (solid) application background if it has one. png_color_16p image_background = NULL; RGBQUAD rgbBkColor; if (png_get_bKGD(png_ptr, info_ptr, &image_background)) { rgbBkColor.rgbRed = (BYTE)image_background->red; rgbBkColor.rgbGreen = (BYTE)image_background->green; rgbBkColor.rgbBlue = (BYTE)image_background->blue; rgbBkColor.rgbReserved = 0; FreeImage_SetBackgroundColor(dib, &rgbBkColor); } } // get physical resolution if (png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) { png_uint_32 res_x, res_y; // we'll overload this var and use 0 to mean no phys data, // since if it's not in meters we can't use it anyway int res_unit_type = PNG_RESOLUTION_UNKNOWN; png_get_pHYs(png_ptr,info_ptr, &res_x, &res_y, &res_unit_type); if (res_unit_type == PNG_RESOLUTION_METER) { FreeImage_SetDotsPerMeterX(dib, res_x); FreeImage_SetDotsPerMeterY(dib, res_y); } } // get possible ICC profile if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) { png_charp profile_name = NULL; png_bytep profile_data = NULL; png_uint_32 profile_length = 0; int compression_type; png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &profile_length); // copy ICC profile data (must be done after FreeImage_AllocateHeader) FreeImage_CreateICCProfile(dib, profile_data, profile_length); } // --- header only mode => clean-up and return if (header_only) { // get possible metadata (it can be located both before and after the image data) ReadMetadata(png_ptr, info_ptr, dib); if (png_ptr) { // clean up after the read, and free any memory allocated - REQUIRED png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); } return dib; } // set the individual row_pointers to point at the correct offsets row_pointers = (png_bytepp)malloc(height * sizeof(png_bytep)); if (!row_pointers) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); FreeImage_Unload(dib); return NULL; } // read in the bitmap bits via the pointer table // allow loading of PNG with minor errors (such as images with several IDAT chunks) for (png_uint_32 k = 0; k < height; k++) { row_pointers[height - 1 - k] = FreeImage_GetScanLine(dib, k); } png_set_benign_errors(png_ptr, 1); png_read_image(png_ptr, row_pointers); // check if the bitmap contains transparency, if so enable it in the header if (FreeImage_GetBPP(dib) == 32) { if (FreeImage_GetColorType(dib) == FIC_RGBALPHA) { FreeImage_SetTransparent(dib, TRUE); } else { FreeImage_SetTransparent(dib, FALSE); } } // cleanup if (row_pointers) { free(row_pointers); row_pointers = NULL; } // read the rest of the file, getting any additional chunks in info_ptr png_read_end(png_ptr, info_ptr); // get possible metadata (it can be located both before and after the image data) ReadMetadata(png_ptr, info_ptr, dib); if (png_ptr) { // clean up after the read, and free any memory allocated - REQUIRED png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); } return dib; } catch (const char *text) { if (png_ptr) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); } if (row_pointers) { free(row_pointers); } if (dib) { FreeImage_Unload(dib); } FreeImage_OutputMessageProc(s_format_id, text); return NULL; } } return NULL; }
void fipImage::setTransparencyTable(BYTE *table, int count) { FreeImage_SetTransparencyTable(_dib, table, count); _bHasChanged = TRUE; }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { if( data == NULL ) { return NULL; } GIFinfo *info = (GIFinfo *)data; if( page == -1 ) { page = 0; } if( page < 0 || page >= (int)info->image_descriptor_offsets.size() ) { return NULL; } FIBITMAP *dib = NULL; try { bool have_transparent = false, no_local_palette = false, interlaced = false; int disposal_method = GIF_DISPOSAL_LEAVE, delay_time = 0, transparent_color = 0; WORD left, top, width, height; BYTE packed, b; WORD w; //playback pages to generate what the user would see for this frame if( (flags & GIF_PLAYBACK) == GIF_PLAYBACK ) { //Logical Screen Descriptor io->seek_proc(handle, 6, SEEK_SET); WORD logicalwidth, logicalheight; io->read_proc(&logicalwidth, 2, 1, handle); io->read_proc(&logicalheight, 2, 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapShort(&logicalwidth); SwapShort(&logicalheight); #endif //set the background color with 0 alpha RGBQUAD background; if( info->global_color_table_offset != 0 && info->background_color < info->global_color_table_size ) { io->seek_proc(handle, info->global_color_table_offset + (info->background_color * 3), SEEK_SET); io->read_proc(&background.rgbRed, 1, 1, handle); io->read_proc(&background.rgbGreen, 1, 1, handle); io->read_proc(&background.rgbBlue, 1, 1, handle); } else { background.rgbRed = 0; background.rgbGreen = 0; background.rgbBlue = 0; } background.rgbReserved = 0; //allocate entire logical area dib = FreeImage_Allocate(logicalwidth, logicalheight, 32); if( dib == NULL ) { throw "DIB allocated failed"; } //fill with background color to start int x, y; RGBQUAD *scanline; for( y = 0; y < logicalheight; y++ ) { scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, y); for( x = 0; x < logicalwidth; x++ ) { *scanline++ = background; } } //cache some info about each of the pages so we can avoid decoding as many of them as possible std::vector<PageInfo> pageinfo; int start = page, end = page; while( start >= 0 ) { //Graphic Control Extension io->seek_proc(handle, info->graphic_control_extension_offsets[start] + 1, SEEK_SET); io->read_proc(&packed, 1, 1, handle); have_transparent = (packed & GIF_PACKED_GCE_HAVETRANS) ? true : false; disposal_method = (packed & GIF_PACKED_GCE_DISPOSAL) >> 2; //Image Descriptor io->seek_proc(handle, info->image_descriptor_offsets[page], SEEK_SET); io->read_proc(&left, 2, 1, handle); io->read_proc(&top, 2, 1, handle); io->read_proc(&width, 2, 1, handle); io->read_proc(&height, 2, 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapShort(&left); SwapShort(&top); SwapShort(&width); SwapShort(&height); #endif pageinfo.push_back(PageInfo(disposal_method, left, top, width, height)); if( start != end ) { if( left == 0 && top == 0 && width == logicalwidth && height == logicalheight ) { if( disposal_method == GIF_DISPOSAL_BACKGROUND ) { pageinfo.pop_back(); start++; break; } else if( disposal_method != GIF_DISPOSAL_PREVIOUS ) { if( !have_transparent ) { break; } } } } start--; } if( start < 0 ) { start = 0; } //draw each page into the logical area for( page = start; page <= end; page++ ) { PageInfo &info = pageinfo[end - page]; //things we can skip having to decode if( page != end ) { if( info.disposal_method == GIF_DISPOSAL_PREVIOUS ) { continue; } if( info.disposal_method == GIF_DISPOSAL_BACKGROUND ) { for( y = 0; y < info.height; y++ ) { scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, logicalheight - (y + info.top) - 1) + info.left; for( x = 0; x < info.width; x++ ) { *scanline++ = background; } } } } //decode page FIBITMAP *pagedib = Load(io, handle, page, GIF_LOAD256, data); if( pagedib != NULL ) { RGBQUAD *pal = FreeImage_GetPalette(pagedib); have_transparent = false; if( FreeImage_IsTransparent(pagedib) ) { int count = FreeImage_GetTransparencyCount(pagedib); BYTE *table = FreeImage_GetTransparencyTable(pagedib); for( int i = 0; i < count; i++ ) { if( table[i] == 0 ) { have_transparent = true; transparent_color = i; break; } } } //copy page data into logical buffer, with full alpha opaqueness for( y = 0; y < info.height; y++ ) { scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, logicalheight - (y + info.top) - 1) + info.left; BYTE *pageline = FreeImage_GetScanLine(pagedib, info.height - y - 1); for( x = 0; x < info.width; x++ ) { if( !have_transparent || *pageline != transparent_color ) { *scanline = pal[*pageline]; scanline->rgbReserved = 255; } scanline++; pageline++; } } FreeImage_Unload(pagedib); } } return dib; } //get the actual frame image data for a single frame //Image Descriptor io->seek_proc(handle, info->image_descriptor_offsets[page], SEEK_SET); io->read_proc(&left, 2, 1, handle); io->read_proc(&top, 2, 1, handle); io->read_proc(&width, 2, 1, handle); io->read_proc(&height, 2, 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapShort(&left); SwapShort(&top); SwapShort(&width); SwapShort(&height); #endif io->read_proc(&packed, 1, 1, handle); interlaced = (packed & GIF_PACKED_ID_INTERLACED) ? true : false; no_local_palette = (packed & GIF_PACKED_ID_HAVELCT) ? false : true; int bpp = 8; if( (flags & GIF_LOAD256) == 0 ) { if( !no_local_palette ) { int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE); if( size <= 2 ) bpp = 1; else if( size <= 16 ) bpp = 4; } else if( info->global_color_table_offset != 0 ) { if( info->global_color_table_size <= 2 ) bpp = 1; else if( info->global_color_table_size <= 16 ) bpp = 4; } } dib = FreeImage_Allocate(width, height, bpp); if( dib == NULL ) { throw "DIB allocated failed"; } FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameLeft", ANIMTAG_FRAMELEFT, FIDT_SHORT, 1, 2, &left); FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTop", ANIMTAG_FRAMETOP, FIDT_SHORT, 1, 2, &top); b = no_local_palette ? 1 : 0; FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "NoLocalPalette", ANIMTAG_NOLOCALPALETTE, FIDT_BYTE, 1, 1, &b); b = interlaced ? 1 : 0; FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "Interlaced", ANIMTAG_INTERLACED, FIDT_BYTE, 1, 1, &b); //Palette RGBQUAD *pal = FreeImage_GetPalette(dib); if( !no_local_palette ) { int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE); int i = 0; while( i < size ) { io->read_proc(&pal[i].rgbRed, 1, 1, handle); io->read_proc(&pal[i].rgbGreen, 1, 1, handle); io->read_proc(&pal[i].rgbBlue, 1, 1, handle); i++; } } else if( info->global_color_table_offset != 0 ) { long pos = io->tell_proc(handle); io->seek_proc(handle, info->global_color_table_offset, SEEK_SET); int i = 0; while( i < info->global_color_table_size ) { io->read_proc(&pal[i].rgbRed, 1, 1, handle); io->read_proc(&pal[i].rgbGreen, 1, 1, handle); io->read_proc(&pal[i].rgbBlue, 1, 1, handle); i++; } io->seek_proc(handle, pos, SEEK_SET); } else { //its legal to have no palette, but we're going to generate *something* for( int i = 0; i < 256; i++ ) { pal[i].rgbRed = i; pal[i].rgbGreen = i; pal[i].rgbBlue = i; } } //LZW Minimum Code Size io->read_proc(&b, 1, 1, handle); StringTable stringtable; stringtable.Initialize(b); //Image Data Sub-blocks int x = 0, xpos = 0, y = 0, shift = 8 - bpp, mask = (1 << bpp) - 1, interlacepass = 0; BYTE *scanline = FreeImage_GetScanLine(dib, height - 1); BYTE buf[1024]; io->read_proc(&b, 1, 1, handle); while( b ) { io->read_proc(stringtable.FillInputBuffer(b), b, 1, handle); int size = sizeof(buf); while( stringtable.Decompress(buf, &size) ) { for( int i = 0; i < size; i++ ) { scanline[xpos] |= (buf[i] & mask) << shift; if( shift > 0 ) { shift -= bpp; } else { xpos++; shift = 8 - bpp; } if( ++x >= width ) { if( interlaced ) { y += g_GifInterlaceIncrement[interlacepass]; if( y >= height && interlacepass < GIF_INTERLACE_PASSES ) { y = g_GifInterlaceOffset[++interlacepass]; } } else { y++; } if( y >= height ) { stringtable.Done(); break; } x = xpos = 0; shift = 8 - bpp; scanline = FreeImage_GetScanLine(dib, height - y - 1); } } size = sizeof(buf); } io->read_proc(&b, 1, 1, handle); } if( page == 0 ) { size_t idx; //Logical Screen Descriptor io->seek_proc(handle, 6, SEEK_SET); WORD logicalwidth, logicalheight; io->read_proc(&logicalwidth, 2, 1, handle); io->read_proc(&logicalheight, 2, 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapShort(&logicalwidth); SwapShort(&logicalheight); #endif FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "LogicalWidth", ANIMTAG_LOGICALWIDTH, FIDT_SHORT, 1, 2, &logicalwidth); FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "LogicalHeight", ANIMTAG_LOGICALHEIGHT, FIDT_SHORT, 1, 2, &logicalheight); //Global Color Table if( info->global_color_table_offset != 0 ) { RGBQUAD globalpalette[256]; io->seek_proc(handle, info->global_color_table_offset, SEEK_SET); int i = 0; while( i < info->global_color_table_size ) { io->read_proc(&globalpalette[i].rgbRed, 1, 1, handle); io->read_proc(&globalpalette[i].rgbGreen, 1, 1, handle); io->read_proc(&globalpalette[i].rgbBlue, 1, 1, handle); globalpalette[i].rgbReserved = 0; i++; } FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "GlobalPalette", ANIMTAG_GLOBALPALETTE, FIDT_PALETTE, info->global_color_table_size, info->global_color_table_size * 4, globalpalette); //background color if( info->background_color < info->global_color_table_size ) { FreeImage_SetBackgroundColor(dib, &globalpalette[info->background_color]); } } //Application Extension LONG loop = 1; //If no AE with a loop count is found, the default must be 1 for( idx = 0; idx < info->application_extension_offsets.size(); idx++ ) { io->seek_proc(handle, info->application_extension_offsets[idx], SEEK_SET); io->read_proc(&b, 1, 1, handle); if( b == 11 ) { //All AEs start with an 11 byte sub-block to determine what type of AE it is char buf[11]; io->read_proc(buf, 11, 1, handle); if( !memcmp(buf, "NETSCAPE2.0", 11) || !memcmp(buf, "ANIMEXTS1.0", 11) ) { //Not everybody recognizes ANIMEXTS1.0 but it is valid io->read_proc(&b, 1, 1, handle); if( b == 3 ) { //we're supposed to have a 3 byte sub-block now io->read_proc(&b, 1, 1, handle); //this should be 0x01 but isn't really important io->read_proc(&w, 2, 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapShort(&w); #endif loop = w; if( loop > 0 ) loop++; break; } } } } FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "Loop", ANIMTAG_LOOP, FIDT_LONG, 1, 4, &loop); //Comment Extension for( idx = 0; idx < info->comment_extension_offsets.size(); idx++ ) { io->seek_proc(handle, info->comment_extension_offsets[idx], SEEK_SET); std::string comment; char buf[255]; io->read_proc(&b, 1, 1, handle); while( b ) { io->read_proc(buf, b, 1, handle); comment.append(buf, b); io->read_proc(&b, 1, 1, handle); } comment.append(1, '\0'); sprintf(buf, "Comment%d", idx); FreeImage_SetMetadataEx(FIMD_COMMENTS, dib, buf, 1, FIDT_ASCII, comment.size(), comment.size(), comment.c_str()); } } //Graphic Control Extension if( info->graphic_control_extension_offsets[page] != 0 ) { io->seek_proc(handle, info->graphic_control_extension_offsets[page] + 1, SEEK_SET); io->read_proc(&packed, 1, 1, handle); io->read_proc(&w, 2, 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapShort(&w); #endif io->read_proc(&b, 1, 1, handle); have_transparent = (packed & GIF_PACKED_GCE_HAVETRANS) ? true : false; disposal_method = (packed & GIF_PACKED_GCE_DISPOSAL) >> 2; delay_time = w * 10; //convert cs to ms transparent_color = b; if( have_transparent ) { int size = 1 << bpp; if( transparent_color <= size ) { BYTE table[256]; memset(table, 0xFF, size); table[transparent_color] = 0; FreeImage_SetTransparencyTable(dib, table, size); } } }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_uint_32 width, height; int color_type; int bit_depth; int pixel_depth = 0; // pixel_depth = bit_depth * channels FIBITMAP *dib = NULL; png_bytepp row_pointers = NULL; fi_ioStructure fio; fio.s_handle = handle; fio.s_io = io; if (handle) { BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; try { // check to see if the file is in fact a PNG file BYTE png_check[PNG_BYTES_TO_CHECK]; io->read_proc(png_check, PNG_BYTES_TO_CHECK, 1, handle); if (png_sig_cmp(png_check, (png_size_t)0, PNG_BYTES_TO_CHECK) != 0) { return NULL; // Bad signature } // create the chunk manage structure png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, error_handler, warning_handler); if (!png_ptr) { return NULL; } // create the info structure info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return NULL; } // init the IO png_set_read_fn(png_ptr, &fio, _ReadProc); if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return NULL; } // because we have already read the signature... png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); // read the IHDR chunk png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); // configure the decoder FREE_IMAGE_TYPE image_type = FIT_BITMAP; if(!ConfigureDecoder(png_ptr, info_ptr, flags, &image_type)) { throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } // update image info color_type = png_get_color_type(png_ptr, info_ptr); bit_depth = png_get_bit_depth(png_ptr, info_ptr); pixel_depth = bit_depth * png_get_channels(png_ptr, info_ptr); // create a dib and write the bitmap header // set up the dib palette, if needed switch (color_type) { case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); break; case PNG_COLOR_TYPE_PALETTE: dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); if(dib) { png_colorp png_palette = NULL; int palette_entries = 0; png_get_PLTE(png_ptr,info_ptr, &png_palette, &palette_entries); palette_entries = MIN((unsigned)palette_entries, FreeImage_GetColorsUsed(dib)); // store the palette RGBQUAD *palette = FreeImage_GetPalette(dib); for(int i = 0; i < palette_entries; i++) { palette[i].rgbRed = png_palette[i].red; palette[i].rgbGreen = png_palette[i].green; palette[i].rgbBlue = png_palette[i].blue; } } break; case PNG_COLOR_TYPE_GRAY: dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); if(dib && (pixel_depth <= 8)) { RGBQUAD *palette = FreeImage_GetPalette(dib); const int palette_entries = 1 << pixel_depth; for(int i = 0; i < palette_entries; i++) { palette[i].rgbRed = palette[i].rgbGreen = palette[i].rgbBlue = (BYTE)((i * 255) / (palette_entries - 1)); } } break; default: throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } if(!dib) { throw FI_MSG_ERROR_DIB_MEMORY; } // store the transparency table if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { // array of alpha (transparency) entries for palette png_bytep trans_alpha = NULL; // number of transparent entries int num_trans = 0; // graylevel or color sample values of the single transparent color for non-paletted images png_color_16p trans_color = NULL; png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color); if((color_type == PNG_COLOR_TYPE_GRAY) && trans_color) { // single transparent color if (trans_color->gray < 256) { BYTE table[256]; memset(table, 0xFF, 256); table[trans_color->gray] = 0; FreeImage_SetTransparencyTable(dib, table, 256); } // check for a full transparency table, too else if ((trans_alpha) && (pixel_depth <= 8)) { FreeImage_SetTransparencyTable(dib, (BYTE *)trans_alpha, num_trans); } } else if((color_type == PNG_COLOR_TYPE_PALETTE) && trans_alpha) { // transparency table FreeImage_SetTransparencyTable(dib, (BYTE *)trans_alpha, num_trans); } } // store the background color (only supported for FIT_BITMAP types) if ((image_type == FIT_BITMAP) && png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) { // Get the background color to draw transparent and alpha images over. // Note that even if the PNG file supplies a background, you are not required to // use it - you should use the (solid) application background if it has one. png_color_16p image_background = NULL; RGBQUAD rgbBkColor; if (png_get_bKGD(png_ptr, info_ptr, &image_background)) { rgbBkColor.rgbRed = (BYTE)image_background->red; rgbBkColor.rgbGreen = (BYTE)image_background->green; rgbBkColor.rgbBlue = (BYTE)image_background->blue; rgbBkColor.rgbReserved = 0; FreeImage_SetBackgroundColor(dib, &rgbBkColor); } } // get physical resolution if (png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) { png_uint_32 res_x, res_y; // we'll overload this var and use 0 to mean no phys data, // since if it's not in meters we can't use it anyway int res_unit_type = PNG_RESOLUTION_UNKNOWN; png_get_pHYs(png_ptr,info_ptr, &res_x, &res_y, &res_unit_type); if (res_unit_type == PNG_RESOLUTION_METER) { FreeImage_SetDotsPerMeterX(dib, res_x); FreeImage_SetDotsPerMeterY(dib, res_y); } } // get possible ICC profile if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) { png_charp profile_name = NULL; png_bytep profile_data = NULL; png_uint_32 profile_length = 0; int compression_type; png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &profile_length); // copy ICC profile data (must be done after FreeImage_AllocateHeader) FreeImage_CreateICCProfile(dib, profile_data, profile_length); } // --- header only mode => clean-up and return if (header_only) { // get possible metadata (it can be located both before and after the image data) ReadMetadata(png_ptr, info_ptr, dib); if (png_ptr) { // clean up after the read, and free any memory allocated - REQUIRED png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); } return dib; } // set the individual row_pointers to point at the correct offsets row_pointers = (png_bytepp)malloc(height * sizeof(png_bytep)); if (!row_pointers) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); FreeImage_Unload(dib); return NULL; } // read in the bitmap bits via the pointer table // allow loading of PNG with minor errors (such as images with several IDAT chunks) for (png_uint_32 k = 0; k < height; k++) { row_pointers[height - 1 - k] = FreeImage_GetScanLine(dib, k); } png_set_benign_errors(png_ptr, 1); png_read_image(png_ptr, row_pointers); // check if the bitmap contains transparency, if so enable it in the header if (FreeImage_GetBPP(dib) == 32) { if (FreeImage_GetColorType(dib) == FIC_RGBALPHA) { FreeImage_SetTransparent(dib, TRUE); } else { FreeImage_SetTransparent(dib, FALSE); } } // cleanup if (row_pointers) { free(row_pointers); row_pointers = NULL; } // read the rest of the file, getting any additional chunks in info_ptr png_read_end(png_ptr, info_ptr); // get possible metadata (it can be located both before and after the image data) ReadMetadata(png_ptr, info_ptr, dib); if (png_ptr) { // clean up after the read, and free any memory allocated - REQUIRED png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); } return dib; } catch (const char *text) { if (png_ptr) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); } if (row_pointers) { free(row_pointers); } if (dib) { FreeImage_Unload(dib); } FreeImage_OutputMessageProc(s_format_id, text); return NULL; } } return NULL; }