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; }
BOOL fipImage::setFileBkColor(RGBQUAD *bkcolor) { _bHasChanged = TRUE; return FreeImage_SetBackgroundColor(_dib, bkcolor); }
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; }
/** Load a FIBITMAP from a MNG or a JNG stream @param format_id ID of the caller @param io Stream i/o functions @param handle Stream handle @param Offset Start of the first chunk @param flags Loading flags @return Returns a dib if successful, returns NULL otherwise */ FIBITMAP* mng_ReadChunks(int format_id, FreeImageIO *io, fi_handle handle, long Offset, int flags = 0) { DWORD mLength = 0; BYTE mChunkName[5]; BYTE *mChunk = NULL; DWORD crc_file; long LastOffset; long mOrigPos; BYTE *PLTE_file_chunk = NULL; // whole PLTE chunk (lentgh, name, array, crc) DWORD PLTE_file_size = 0; // size of PLTE chunk BOOL m_HasGlobalPalette = FALSE; // may turn to TRUE in PLTE chunk unsigned m_TotalBytesOfChunks = 0; FIBITMAP *dib = NULL; FIBITMAP *dib_alpha = NULL; FIMEMORY *hJpegMemory = NULL; FIMEMORY *hPngMemory = NULL; FIMEMORY *hIDATMemory = NULL; // --- DWORD jng_width = 0; DWORD jng_height = 0; BYTE jng_color_type = 0; BYTE jng_image_sample_depth = 0; BYTE jng_image_compression_method = 0; BYTE jng_alpha_sample_depth = 0; BYTE jng_alpha_compression_method = 0; BYTE jng_alpha_filter_method = 0; BYTE jng_alpha_interlace_method = 0; DWORD mng_frame_width = 0; DWORD mng_frame_height = 0; DWORD mng_ticks_per_second = 0; DWORD mng_nominal_layer_count = 0; DWORD mng_nominal_frame_count = 0; DWORD mng_nominal_play_time = 0; DWORD mng_simplicity_profile = 0; DWORD res_x = 2835; // 72 dpi DWORD res_y = 2835; // 72 dpi RGBQUAD rgbBkColor = {0, 0, 0, 0}; WORD bk_red, bk_green, bk_blue; BOOL hasBkColor = FALSE; BOOL mHasIDAT = FALSE; tEXtMAP key_value_pair; // --- BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; // get the file size const long mLOF = mng_LOF(io, handle); // go to the first chunk io->seek_proc(handle, Offset, SEEK_SET); try { BOOL mEnd = FALSE; while(mEnd == FALSE) { // start of the chunk LastOffset = io->tell_proc(handle); // read length mLength = 0; io->read_proc(&mLength, 1, sizeof(mLength), handle); mng_SwapLong(&mLength); // read name io->read_proc(&mChunkName[0], 1, 4, handle); mChunkName[4] = '\0'; if(mLength > 0) { mChunk = (BYTE*)realloc(mChunk, mLength); if(!mChunk) { FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName); throw (const char*)NULL; } Offset = io->tell_proc(handle); if(Offset + (long)mLength > mLOF) { FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: unexpected end of file", mChunkName); throw (const char*)NULL; } // read chunk io->read_proc(mChunk, 1, mLength, handle); } // read crc io->read_proc(&crc_file, 1, sizeof(crc_file), handle); mng_SwapLong(&crc_file); // check crc DWORD crc_check = FreeImage_ZLibCRC32(0, &mChunkName[0], 4); crc_check = FreeImage_ZLibCRC32(crc_check, mChunk, mLength); if(crc_check != crc_file) { FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: bad CRC", mChunkName); throw (const char*)NULL; } switch( mng_GetChunckType(mChunkName) ) { case MHDR: // The MHDR chunk is always first in all MNG datastreams except for those // that consist of a single PNG or JNG datastream with a PNG or JNG signature. if(mLength == 28) { memcpy(&mng_frame_width, &mChunk[0], 4); memcpy(&mng_frame_height, &mChunk[4], 4); memcpy(&mng_ticks_per_second, &mChunk[8], 4); memcpy(&mng_nominal_layer_count, &mChunk[12], 4); memcpy(&mng_nominal_frame_count, &mChunk[16], 4); memcpy(&mng_nominal_play_time, &mChunk[20], 4); memcpy(&mng_simplicity_profile, &mChunk[24], 4); mng_SwapLong(&mng_frame_width); mng_SwapLong(&mng_frame_height); mng_SwapLong(&mng_ticks_per_second); mng_SwapLong(&mng_nominal_layer_count); mng_SwapLong(&mng_nominal_frame_count); mng_SwapLong(&mng_nominal_play_time); mng_SwapLong(&mng_simplicity_profile); } else { FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: size is %d instead of 28", mChunkName, mLength); } break; case MEND: mEnd = TRUE; break; case LOOP: case ENDL: break; case DEFI: break; case SAVE: case SEEK: case TERM: break; case BACK: break; // Global "PLTE" and "tRNS" (if any). PNG "PLTE" will be of 0 byte, as it uses global data. case PLTE: // Global m_HasGlobalPalette = TRUE; PLTE_file_size = mLength + 12; // (lentgh, name, array, crc) = (4, 4, mLength, 4) PLTE_file_chunk = (BYTE*)realloc(PLTE_file_chunk, PLTE_file_size); if(!PLTE_file_chunk) { FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName); throw (const char*)NULL; } else { mOrigPos = io->tell_proc(handle); // seek to the start of the chunk io->seek_proc(handle, LastOffset, SEEK_SET); // load the whole chunk io->read_proc(PLTE_file_chunk, 1, PLTE_file_size, handle); // go to the start of the next chunk io->seek_proc(handle, mOrigPos, SEEK_SET); } break; case tRNS: // Global break; case IHDR: Offset = LastOffset; // parse the PNG file and get its file size if(mng_CountPNGChunks(io, handle, Offset, &m_TotalBytesOfChunks) == FALSE) { // reach an unexpected end of file mEnd = TRUE; FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: unexpected end of PNG file", mChunkName); break; } // wrap the { IHDR, ..., IEND } chunks as a PNG stream if(hPngMemory == NULL) { hPngMemory = FreeImage_OpenMemory(); } mOrigPos = io->tell_proc(handle); // write PNG file signature FreeImage_SeekMemory(hPngMemory, 0, SEEK_SET); FreeImage_WriteMemory(g_png_signature, 1, 8, hPngMemory); mChunk = (BYTE*)realloc(mChunk, m_TotalBytesOfChunks); if(!mChunk) { FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName); throw (const char*)NULL; } // on calling CountPNGChunks earlier, we were in Offset pos, // go back there io->seek_proc(handle, Offset, SEEK_SET); io->read_proc(mChunk, 1, m_TotalBytesOfChunks, handle); // Put back to original pos io->seek_proc(handle, mOrigPos, SEEK_SET); // write the PNG chunks FreeImage_WriteMemory(mChunk, 1, m_TotalBytesOfChunks, hPngMemory); // plug in global PLTE if local PLTE exists if(m_HasGlobalPalette) { // ensure we remove some local chunks, so that global // "PLTE" can be inserted right before "IDAT". mng_RemoveChunk(hPngMemory, mng_PLTE); mng_RemoveChunk(hPngMemory, mng_tRNS); mng_RemoveChunk(hPngMemory, mng_bKGD); // insert global "PLTE" chunk in its entirety before "IDAT" mng_InsertChunk(hPngMemory, mng_IDAT, PLTE_file_chunk, PLTE_file_size); } if(dib) FreeImage_Unload(dib); dib = mng_LoadFromMemoryHandle(hPngMemory, flags); // stop after the first image mEnd = TRUE; break; case JHDR: if(mLength == 16) { memcpy(&jng_width, &mChunk[0], 4); memcpy(&jng_height, &mChunk[4], 4); mng_SwapLong(&jng_width); mng_SwapLong(&jng_height); jng_color_type = mChunk[8]; jng_image_sample_depth = mChunk[9]; jng_image_compression_method = mChunk[10]; //BYTE jng_image_interlace_method = mChunk[11]; // for debug only jng_alpha_sample_depth = mChunk[12]; jng_alpha_compression_method = mChunk[13]; jng_alpha_filter_method = mChunk[14]; jng_alpha_interlace_method = mChunk[15]; } else { FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: invalid chunk length", mChunkName); throw (const char*)NULL; } break; case JDAT: if(hJpegMemory == NULL) { hJpegMemory = FreeImage_OpenMemory(); } // as there may be several JDAT chunks, concatenate them FreeImage_WriteMemory(mChunk, 1, mLength, hJpegMemory); break; case IDAT: if(!header_only && (jng_alpha_compression_method == 0)) { // PNG grayscale IDAT format if(hIDATMemory == NULL) { hIDATMemory = FreeImage_OpenMemory(); mHasIDAT = TRUE; } // as there may be several IDAT chunks, concatenate them FreeImage_WriteMemory(mChunk, 1, mLength, hIDATMemory); } break; case IEND: if(!hJpegMemory) { mEnd = TRUE; break; } // load the JPEG if(dib) { FreeImage_Unload(dib); } dib = mng_LoadFromMemoryHandle(hJpegMemory, flags); // load the PNG alpha layer if(mHasIDAT) { BYTE *data = NULL; DWORD size_in_bytes = 0; // get a pointer to the IDAT buffer FreeImage_AcquireMemory(hIDATMemory, &data, &size_in_bytes); if(data && size_in_bytes) { // wrap the IDAT chunk as a PNG stream if(hPngMemory == NULL) { hPngMemory = FreeImage_OpenMemory(); } mng_WritePNGStream(jng_width, jng_height, jng_alpha_sample_depth, data, size_in_bytes, hPngMemory); // load the PNG if(dib_alpha) { FreeImage_Unload(dib_alpha); } dib_alpha = mng_LoadFromMemoryHandle(hPngMemory, flags); } } // stop the parsing mEnd = TRUE; break; case JDAA: break; case gAMA: break; case pHYs: // unit is pixels per meter memcpy(&res_x, &mChunk[0], 4); mng_SwapLong(&res_x); memcpy(&res_y, &mChunk[4], 4); mng_SwapLong(&res_y); break; case bKGD: memcpy(&bk_red, &mChunk[0], 2); mng_SwapShort(&bk_red); rgbBkColor.rgbRed = (BYTE)bk_red; memcpy(&bk_green, &mChunk[2], 2); mng_SwapShort(&bk_green); rgbBkColor.rgbGreen = (BYTE)bk_green; memcpy(&bk_blue, &mChunk[4], 2); mng_SwapShort(&bk_blue); rgbBkColor.rgbBlue = (BYTE)bk_blue; hasBkColor = TRUE; break; case tEXt: mng_SetMetadata_tEXt(key_value_pair, mChunk, mLength); break; case UNKNOWN_CHUNCK: default: break; } // switch( GetChunckType ) } // while(!mEnd) FreeImage_CloseMemory(hJpegMemory); FreeImage_CloseMemory(hPngMemory); FreeImage_CloseMemory(hIDATMemory); free(mChunk); free(PLTE_file_chunk); // convert to 32-bit if a transparent layer is available if(!header_only && dib_alpha) { FIBITMAP *dst = FreeImage_ConvertTo32Bits(dib); if((FreeImage_GetBPP(dib_alpha) == 8) && (FreeImage_GetImageType(dib_alpha) == FIT_BITMAP)) { FreeImage_SetChannel(dst, dib_alpha, FICC_ALPHA); } else { FIBITMAP *dst_alpha = FreeImage_ConvertTo8Bits(dib_alpha); FreeImage_SetChannel(dst, dst_alpha, FICC_ALPHA); FreeImage_Unload(dst_alpha); } FreeImage_Unload(dib); dib = dst; } FreeImage_Unload(dib_alpha); if(dib) { // set metadata FreeImage_SetDotsPerMeterX(dib, res_x); FreeImage_SetDotsPerMeterY(dib, res_y); if(hasBkColor) { FreeImage_SetBackgroundColor(dib, &rgbBkColor); } if(key_value_pair.size()) { for(tEXtMAP::iterator j = key_value_pair.begin(); j != key_value_pair.end(); j++) { std::string key = (*j).first; std::string value = (*j).second; mng_SetKeyValue(FIMD_COMMENTS, dib, key.c_str(), value.c_str()); } } } return dib; } catch(const char *text) { FreeImage_CloseMemory(hJpegMemory); FreeImage_CloseMemory(hPngMemory); FreeImage_CloseMemory(hIDATMemory); free(mChunk); free(PLTE_file_chunk); FreeImage_Unload(dib); FreeImage_Unload(dib_alpha); if(text) { FreeImage_OutputMessageProc(format_id, text); } return NULL; } }
/** * This method sets the background color value of the picture. * * @param colorValue the color value to set * @return true if the background color was set, else false */ bool fipImage::setFileBkColor( RGBQUAD * colorValue ){ return FreeImage_SetBackgroundColor( pImageData, colorValue ); }