FIBITMAP* psdParser::Load(FreeImageIO *io, fi_handle handle, int s_format_id, int flags) { FIBITMAP *Bitmap = NULL; _fi_flags = flags; _fi_format_id = s_format_id; try { if (NULL == handle) { throw("Cannot open file"); } if (!_headerInfo.Read(io, handle)) { throw("Error in header"); } if (!_colourModeData.Read(io, handle)) { throw("Error in ColourMode Data"); } if (!ReadImageResource(io, handle)) { throw("Error in Image Resource"); } if (!ReadLayerAndMaskInfoSection(io, handle)) { throw("Error in Mask Info"); } Bitmap = ReadImageData(io, handle); if (NULL == Bitmap) { throw("Error in Image Data"); } // set resolution info if(NULL != Bitmap) { unsigned res_x = 2835; // 72 dpi unsigned res_y = 2835; // 72 dpi if (_bResolutionInfoFilled) { _resolutionInfo.GetResolutionInfo(res_x, res_y); } FreeImage_SetDotsPerMeterX(Bitmap, res_x); FreeImage_SetDotsPerMeterY(Bitmap, res_y); } // set ICC profile if(NULL != _iccProfile._ProfileData) { FreeImage_CreateICCProfile(Bitmap, _iccProfile._ProfileData, _iccProfile._ProfileSize); if ((flags & PSD_CMYK) == PSD_CMYK) { FreeImage_GetICCProfile(Bitmap)->flags |= FIICC_COLOR_IS_CMYK; } } } catch(const char *text) { FreeImage_OutputMessageProc(s_format_id, text); } catch(const std::exception& e) { FreeImage_OutputMessageProc(s_format_id, "%s", e.what()); } return Bitmap; }
BOOL DLL_CALLCONV FreeImage_CloneMetadata(FIBITMAP *dst, FIBITMAP *src) { if(!src || !dst) { return FALSE; } // get metadata links METADATAMAP *src_metadata = ((FREEIMAGEHEADER *)src->data)->metadata; METADATAMAP *dst_metadata = ((FREEIMAGEHEADER *)dst->data)->metadata; // copy metadata models, *except* the FIMD_ANIMATION model for(METADATAMAP::iterator i = (*src_metadata).begin(); i != (*src_metadata).end(); i++) { int model = (*i).first; if(model == (int)FIMD_ANIMATION) { continue; } TAGMAP *src_tagmap = (*i).second; if(src_tagmap) { if( dst_metadata->find(model) != dst_metadata->end() ) { // destroy dst model FreeImage_SetMetadata((FREE_IMAGE_MDMODEL)model, dst, NULL, NULL); } // create a metadata model TAGMAP *dst_tagmap = new(std::nothrow) TAGMAP(); if(dst_tagmap) { // fill the model for(TAGMAP::iterator j = src_tagmap->begin(); j != src_tagmap->end(); j++) { std::string dst_key = (*j).first; FITAG *dst_tag = FreeImage_CloneTag( (*j).second ); // assign key and tag value (*dst_tagmap)[dst_key] = dst_tag; } // assign model and tagmap (*dst_metadata)[model] = dst_tagmap; } } } // clone resolution FreeImage_SetDotsPerMeterX(dst, FreeImage_GetDotsPerMeterX(src)); FreeImage_SetDotsPerMeterY(dst, FreeImage_GetDotsPerMeterY(src)); return TRUE; }
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::setHorizontalResolution(double value) { FreeImage_SetDotsPerMeterX(_dib, (unsigned)(value * 100 + 0.5)); }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { FIBITMAP *dib = NULL; BYTE *bits; // Pointer to dib data RGBQUAD *pal; // Pointer to dib palette BYTE *line = NULL; // PCX raster line BYTE *ReadBuf = NULL; // buffer; WORD linelength; // Length of raster line in bytes WORD pitch; // Length of DIB line in bytes BOOL rle; // True if the file is run-length encoded if (handle) { try { // process the header PCXHEADER header; io->read_proc(&header, sizeof(PCXHEADER), 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapHeader(&header); #endif // check PCX identifier if ((header.manufacturer != 0x0A) || (header.version > 5)) throw "Invalid PCX file"; // allocate a new DIB WORD width = header.window[2] - header.window[0] + 1; WORD height = header.window[3] - header.window[1] + 1; WORD bitcount = header.bpp * header.planes; if (bitcount == 24) dib = FreeImage_Allocate(width, height, bitcount, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); else dib = FreeImage_Allocate(width, height, bitcount); // if the dib couldn't be allocated, throw an error if (!dib) throw "DIB allocation failed"; // metrics handling code FreeImage_SetDotsPerMeterX(dib, (unsigned) (((float)header.hdpi) / 0.0254000 + 0.5)); FreeImage_SetDotsPerMeterY(dib, (unsigned) (((float)header.vdpi) / 0.0254000 + 0.5)); // Set up the palette if needed // ---------------------------- switch(bitcount) { case 1: { pal = FreeImage_GetPalette(dib); pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0; pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255; break; } case 4: { pal = FreeImage_GetPalette(dib); BYTE *pColormap = &header.color_map[0]; for (int i = 0; i < 16; i++) { pal[i].rgbRed = pColormap[0]; pal[i].rgbGreen = pColormap[1]; pal[i].rgbBlue = pColormap[2]; pColormap += 3; } break; } case 8: { BYTE palette_id; io->seek_proc(handle, -769L, SEEK_END); io->read_proc(&palette_id, 1, 1, handle); if (palette_id == 0x0C) { BYTE *cmap = (BYTE*)malloc(768 * sizeof(BYTE)); io->read_proc(cmap, 768, 1, handle); pal = FreeImage_GetPalette(dib); BYTE *pColormap = &cmap[0]; for(int i = 0; i < 256; i++) { pal[i].rgbRed = pColormap[0]; pal[i].rgbGreen = pColormap[1]; pal[i].rgbBlue = pColormap[2]; pColormap += 3; } free(cmap); } // wrong palette ID, perhaps a gray scale is needed ? else if (header.palette_info == 2) { pal = FreeImage_GetPalette(dib); for(int i = 0; i < 256; i++) { pal[i].rgbRed = (BYTE)i; pal[i].rgbGreen = (BYTE)i; pal[i].rgbBlue = (BYTE)i; } } io->seek_proc(handle, (long)sizeof(PCXHEADER), SEEK_SET); } break; } // calculate the line length for the PCX and the DIB linelength = header.bytes_per_line * header.planes; pitch = (WORD)FreeImage_GetPitch(dib); // run-length encoding ? rle = (header.encoding == 1) ? TRUE : FALSE; // load image data // --------------- line = new BYTE[linelength]; bits = FreeImage_GetScanLine(dib, height - 1); ReadBuf = new BYTE[IO_BUF_SIZE]; int ReadPos = IO_BUF_SIZE; if ((header.planes == 1) && ((header.bpp == 1) || (header.bpp == 8))) { BYTE skip; WORD written; for (WORD y = 0; y < height; y++) { written = readline(*io, handle, bits, linelength, rle, ReadBuf, &ReadPos); // skip trailing garbage at the end of the scanline for (int count = written; count < linelength; count++) { if (ReadPos < IO_BUF_SIZE) { ReadPos++; } else { io->read_proc(&skip, sizeof(BYTE), 1, handle); } } bits -= pitch; } } else if ((header.planes == 4) && (header.bpp == 1)) { BYTE bit, mask, skip; WORD index; BYTE *buffer; WORD x, y, written; buffer = new BYTE[width]; for (y = 0; y < height; y++) { written = readline(*io, handle, line, linelength, rle, ReadBuf, &ReadPos); // build a nibble using the 4 planes memset(buffer, 0, width * sizeof(BYTE)); for(int plane = 0; plane < 4; plane++) { bit = (BYTE)(1 << plane); for (x = 0; x < width; x++) { index = (WORD)((x / 8) + plane * header.bytes_per_line); mask = (BYTE)(0x80 >> (x & 0x07)); buffer[x] |= (line[index] & mask) ? bit : 0; } } // then write the DIB row for (x = 0; x < width / 2; x++) bits[x] = (buffer[2*x] << 4) | buffer[2*x+1]; // skip trailing garbage at the end of the scanline for (int count = written; count < linelength; count++) { if (ReadPos < IO_BUF_SIZE) { ReadPos++; } else { io->read_proc(&skip, sizeof(BYTE), 1, handle); } } bits -= pitch; } delete [] buffer; } else if((header.planes == 3) && (header.bpp == 8)) { BYTE *pline; for (WORD y = 0; y < height; y++) { readline(*io, handle, line, linelength, rle, ReadBuf, &ReadPos); // convert the plane stream to BGR (RRRRGGGGBBBB -> BGRBGRBGRBGR) // well, now with the FI_RGBA_x macros, on BIGENDIAN we convert to RGB pline = line; WORD x; for (x = 0; x < width; x++) bits[x * 3 + FI_RGBA_RED] = pline[x]; pline += header.bytes_per_line; for (x = 0; x < width; x++) bits[x * 3 + FI_RGBA_GREEN] = pline[x]; pline += header.bytes_per_line; for (x = 0; x < width; x++) bits[x * 3 + FI_RGBA_BLUE] = pline[x]; pline += header.bytes_per_line; bits -= pitch; } } else { throw "Unable to read this file"; } delete [] line; delete [] ReadBuf; return dib; } catch (const char *text) {
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { FIBITMAP *dib = NULL; BYTE *bits; // Pointer to dib data RGBQUAD *pal; // Pointer to dib palette BYTE *line = NULL; // PCX raster line BYTE *ReadBuf = NULL; // buffer; BOOL bIsRLE; // True if the file is run-length encoded if(!handle) { return NULL; } BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; try { // check PCX identifier long start_pos = io->tell_proc(handle); BOOL validated = pcx_validate(io, handle); io->seek_proc(handle, start_pos, SEEK_SET); if(!validated) { throw FI_MSG_ERROR_MAGIC_NUMBER; } // process the header PCXHEADER header; if(io->read_proc(&header, sizeof(PCXHEADER), 1, handle) != 1) { throw FI_MSG_ERROR_PARSING; } #ifdef FREEIMAGE_BIGENDIAN SwapHeader(&header); #endif // allocate a new DIB unsigned width = header.window[2] - header.window[0] + 1; unsigned height = header.window[3] - header.window[1] + 1; unsigned bitcount = header.bpp * header.planes; if (bitcount == 24) { dib = FreeImage_AllocateHeader(header_only, width, height, bitcount, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } else { dib = FreeImage_AllocateHeader(header_only, width, height, bitcount); } // if the dib couldn't be allocated, throw an error if (!dib) { throw FI_MSG_ERROR_DIB_MEMORY; } // metrics handling code FreeImage_SetDotsPerMeterX(dib, (unsigned) (((float)header.hdpi) / 0.0254000 + 0.5)); FreeImage_SetDotsPerMeterY(dib, (unsigned) (((float)header.vdpi) / 0.0254000 + 0.5)); // Set up the palette if needed // ---------------------------- switch(bitcount) { case 1: { pal = FreeImage_GetPalette(dib); pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0; pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255; break; } case 4: { pal = FreeImage_GetPalette(dib); BYTE *pColormap = &header.color_map[0]; for (int i = 0; i < 16; i++) { pal[i].rgbRed = pColormap[0]; pal[i].rgbGreen = pColormap[1]; pal[i].rgbBlue = pColormap[2]; pColormap += 3; } break; } case 8: { BYTE palette_id; io->seek_proc(handle, -769L, SEEK_END); io->read_proc(&palette_id, 1, 1, handle); if (palette_id == 0x0C) { BYTE *cmap = (BYTE*)malloc(768 * sizeof(BYTE)); io->read_proc(cmap, 768, 1, handle); pal = FreeImage_GetPalette(dib); BYTE *pColormap = &cmap[0]; for(int i = 0; i < 256; i++) { pal[i].rgbRed = pColormap[0]; pal[i].rgbGreen = pColormap[1]; pal[i].rgbBlue = pColormap[2]; pColormap += 3; } free(cmap); } // wrong palette ID, perhaps a gray scale is needed ? else if (header.palette_info == 2) { pal = FreeImage_GetPalette(dib); for(int i = 0; i < 256; i++) { pal[i].rgbRed = (BYTE)i; pal[i].rgbGreen = (BYTE)i; pal[i].rgbBlue = (BYTE)i; } } io->seek_proc(handle, (long)sizeof(PCXHEADER), SEEK_SET); } break; } if(header_only) { // header only mode return dib; } // calculate the line length for the PCX and the DIB // length of raster line in bytes unsigned linelength = header.bytes_per_line * header.planes; // length of DIB line (rounded to DWORD) in bytes unsigned pitch = FreeImage_GetPitch(dib); // run-length encoding ? bIsRLE = (header.encoding == 1) ? TRUE : FALSE; // load image data // --------------- line = (BYTE*)malloc(linelength * sizeof(BYTE)); if(!line) throw FI_MSG_ERROR_MEMORY; ReadBuf = (BYTE*)malloc(IO_BUF_SIZE * sizeof(BYTE)); if(!ReadBuf) throw FI_MSG_ERROR_MEMORY; bits = FreeImage_GetScanLine(dib, height - 1); int ReadPos = IO_BUF_SIZE; if ((header.planes == 1) && ((header.bpp == 1) || (header.bpp == 8))) { BYTE skip; unsigned written; for (unsigned y = 0; y < height; y++) { written = readline(*io, handle, bits, linelength, bIsRLE, ReadBuf, &ReadPos); // skip trailing garbage at the end of the scanline for (unsigned count = written; count < linelength; count++) { if (ReadPos < IO_BUF_SIZE) { ReadPos++; } else { io->read_proc(&skip, sizeof(BYTE), 1, handle); } } bits -= pitch; } } else if ((header.planes == 4) && (header.bpp == 1)) { BYTE bit, mask, skip; unsigned index; BYTE *buffer; unsigned x, y, written; buffer = (BYTE*)malloc(width * sizeof(BYTE)); if(!buffer) throw FI_MSG_ERROR_MEMORY; for (y = 0; y < height; y++) { written = readline(*io, handle, line, linelength, bIsRLE, ReadBuf, &ReadPos); // build a nibble using the 4 planes memset(buffer, 0, width * sizeof(BYTE)); for(int plane = 0; plane < 4; plane++) { bit = (BYTE)(1 << plane); for (x = 0; x < width; x++) { index = (unsigned)((x / 8) + plane * header.bytes_per_line); mask = (BYTE)(0x80 >> (x & 0x07)); buffer[x] |= (line[index] & mask) ? bit : 0; } } // then write the DIB row for (x = 0; x < width / 2; x++) { bits[x] = (buffer[2*x] << 4) | buffer[2*x+1]; } // skip trailing garbage at the end of the scanline for (unsigned count = written; count < linelength; count++) { if (ReadPos < IO_BUF_SIZE) { ReadPos++; } else { io->read_proc(&skip, sizeof(BYTE), 1, handle); } } bits -= pitch; } free(buffer); } else if((header.planes == 3) && (header.bpp == 8)) { BYTE *pline; for (unsigned y = 0; y < height; y++) { readline(*io, handle, line, linelength, bIsRLE, ReadBuf, &ReadPos); // convert the plane stream to BGR (RRRRGGGGBBBB -> BGRBGRBGRBGR) // well, now with the FI_RGBA_x macros, on BIGENDIAN we convert to RGB pline = line; unsigned x; for (x = 0; x < width; x++) { bits[x * 3 + FI_RGBA_RED] = pline[x]; } pline += header.bytes_per_line; for (x = 0; x < width; x++) { bits[x * 3 + FI_RGBA_GREEN] = pline[x]; } pline += header.bytes_per_line; for (x = 0; x < width; x++) { bits[x * 3 + FI_RGBA_BLUE] = pline[x]; } pline += header.bytes_per_line; bits -= pitch; } } else { throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } free(line); free(ReadBuf); return dib; } catch (const char *text) {
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; } }
void save_stream_to_image(Stream* str, GfxImageColorMap* color_map, int width, int height, GBool mono, char* file_name, GBool mask) { // read raw data from memory FIBITMAP* fib; if (mono) { int size = height * ((width + 7) / 8); unsigned char* buf = (unsigned char*)malloc(size); if (!buf) return; unsigned char* p = buf; str->reset(); for (int i = 0; i < size; i++) *p++ = str->getChar(); str->close(); fib = FreeImage_ConvertFromRawBits(buf, width, height, (width + 7) / 8, 1, 0, 0, 0, 1); RGBQUAD* pal = FreeImage_GetPalette(fib); pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = (mask ? 0 : 255); pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = (mask ? 255 : 0); free(buf); } else { int row_size = width * 3; if (row_size % 4) row_size += (4 - row_size % 4); unsigned char* buf = (unsigned char*)malloc(height * row_size); if (!buf) return; ImageStream* img_str = new ImageStream(str, width, color_map->getNumPixelComps(), color_map->getBits()); img_str->reset(); Guchar* p; unsigned char* pbuf; GfxRGB rgb; for (int y = 0; y < height; ++y) { p = img_str->getLine(); pbuf = buf + row_size * y; for (int x = 0; x < width; ++x) { color_map->getRGB(p, &rgb); pbuf[0] = colToByte(rgb.b); pbuf[1] = colToByte(rgb.g); pbuf[2] = colToByte(rgb.r); p += color_map->getNumPixelComps(); pbuf += 3; } } fib = FreeImage_ConvertFromRawBits(buf, width, height, row_size, 24, 0, 0, 0, 1); delete img_str; free(buf); } // adjust color depth if (fif == FIF_GIF && !mono) { FIBITMAP* fib_temp = fib; fib = FreeImage_ColorQuantize(fib_temp, FIQ_WUQUANT); FreeImage_Unload(fib_temp); } else if (fif == FIF_JPEG && mono) { FIBITMAP* fib_temp = fib; fib = FreeImage_ConvertTo8Bits(fib_temp); FreeImage_Unload(fib_temp); } // save FreeImage_SetDotsPerMeterX(fib, 72 * 10000 / 254); FreeImage_SetDotsPerMeterY(fib, 72 * 10000 / 254); int flag = 0; if (fif == FIF_JPEG) flag = jpg_quality; else if (fif == FIF_TIFF && tiff_jpeg && !mono) flag = TIFF_JPEG; if (!FreeImage_Save(fif, fib, file_name, flag)) error(-1, "Couldn't create file '%s'", file_name); FreeImage_Unload(fib); }
void save_stream_to_alpha_image(Stream* str, GfxImageColorMap* colorMap, int width, int height, Stream* maskStr, GfxImageColorMap* maskColorMap, int maskWidth, int maskHeight, char* file_name) { if (fif != FIF_PNG || width == 0 || height == 0 || maskWidth == 0 || maskHeight == 0) { save_stream_to_image(str, colorMap, width, height, colorMap->getNumPixelComps() == 1 && colorMap->getBits() == 1, file_name, gFalse); return; } // allocate buffer int row_size = width * 4; unsigned char* buf = (unsigned char*)malloc(height * row_size); if (!buf) return; // read mask data ImageStream* mask_str = new ImageStream(maskStr, maskWidth, maskColorMap->getNumPixelComps(), maskColorMap->getBits()); mask_str->reset(); Guchar* mask_line = mask_str->getLine(); int old_mask_row = 0; for (int y = 0; y < height; ++y) { unsigned char* pbuf = buf + row_size * y; int new_mask_row = y * maskHeight / height; for (int i = old_mask_row; i < new_mask_row; ++i) { mask_line = mask_str->getLine(); } old_mask_row = new_mask_row; int old_mask_column = 0; for (int x = 0; x < width; ++x) { int new_mask_column = x * maskWidth / width; mask_line += maskColorMap->getNumPixelComps() * (new_mask_column - old_mask_column); old_mask_column = new_mask_column; GfxGray gray; maskColorMap->getGray(mask_line, &gray); pbuf[3] = colToByte(gray); pbuf += 4; } } delete mask_str; // read image data ImageStream* img_str = new ImageStream(str, width, colorMap->getNumPixelComps(), colorMap->getBits()); img_str->reset(); for (int y = 0; y < height; ++y) { unsigned char* pbuf = buf + row_size * y; Guchar* line = img_str->getLine(); for (int x = 0; x < width; ++x) { GfxRGB rgb; colorMap->getRGB(line, &rgb); // int alpha = pbuf[3]; // if (alpha < 255 && alpha > 0) // { // pbuf[0] = (colToByte(rgb.b) + alpha - 255) * 255 / alpha; // pbuf[1] = (colToByte(rgb.g) + alpha - 255) * 255 / alpha; // pbuf[2] = (colToByte(rgb.r) + alpha - 255) * 255 / alpha; // } // else { pbuf[0] = colToByte(rgb.b); pbuf[1] = colToByte(rgb.g); pbuf[2] = colToByte(rgb.r); } line += colorMap->getNumPixelComps(); pbuf += 4; } } delete img_str; // create image FIBITMAP* fib = FreeImage_ConvertFromRawBits(buf, width, height, row_size, 32, 0, 0, 0, 1); free(buf); // save FreeImage_SetDotsPerMeterX(fib, 72 * 10000 / 254); FreeImage_SetDotsPerMeterY(fib, 72 * 10000 / 254); int flag = 0; if (fif == FIF_JPEG) flag = jpg_quality; else if (fif == FIF_TIFF && tiff_jpeg) flag = TIFF_JPEG; if (!FreeImage_Save(fif, fib, file_name, flag)) error(-1, "Couldn't create file '%s'", file_name); FreeImage_Unload(fib); }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { if (handle) { FIBITMAP *dib = NULL; try { // set up the jpeglib structures struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; // step 1: allocate and initialize JPEG decompression object cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = jpeg_error_exit; jerr.output_message = jpeg_output_message; jpeg_create_decompress(&cinfo); // step 2a: specify data source (eg, a handle) jpeg_freeimage_src(&cinfo, handle, io); // step 2b: save special markers for later reading jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF); for(int m = 0; m < 16; m++) { jpeg_save_markers(&cinfo, JPEG_APP0 + m, 0xFFFF); } // step 3: read handle parameters with jpeg_read_header() jpeg_read_header(&cinfo, TRUE); // step 4: set parameters for decompression unsigned int scale_denom = 1; // fraction by which to scale image int requested_size = flags >> 16; // requested user size in pixels if(requested_size > 0) { // the JPEG codec can perform x2, x4 or x8 scaling on loading // try to find the more appropriate scaling according to user's need double scale = MAX((double)cinfo.image_width, (double)cinfo.image_height) / (double)requested_size; if(scale >= 8) { scale_denom = 8; } else if(scale >= 4) { scale_denom = 4; } else if(scale >= 2) { scale_denom = 2; } } cinfo.scale_num = 1; cinfo.scale_denom = scale_denom; if ((flags & JPEG_ACCURATE) != JPEG_ACCURATE) { cinfo.dct_method = JDCT_IFAST; cinfo.do_fancy_upsampling = FALSE; } // step 5a: start decompressor and calculate output width and height jpeg_start_decompress(&cinfo); // step 5b: allocate dib and init header if((cinfo.num_components == 4) && (cinfo.out_color_space == JCS_CMYK)) { // CMYK image if((flags & JPEG_CMYK) == JPEG_CMYK) { // load as CMYK dib = FreeImage_Allocate(cinfo.output_width, cinfo.output_height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); if(!dib) return NULL; FreeImage_GetICCProfile(dib)->flags |= FIICC_COLOR_IS_CMYK; } else { // load as CMYK and convert to RGB dib = FreeImage_Allocate(cinfo.output_width, cinfo.output_height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); if(!dib) return NULL; } } else { // RGB or greyscale image dib = FreeImage_Allocate(cinfo.output_width, cinfo.output_height, 8 * cinfo.num_components, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); if(!dib) return NULL; if (cinfo.num_components == 1) { // build a greyscale palette RGBQUAD *colors = FreeImage_GetPalette(dib); for (int i = 0; i < 256; i++) { colors[i].rgbRed = (BYTE)i; colors[i].rgbGreen = (BYTE)i; colors[i].rgbBlue = (BYTE)i; } } } if(scale_denom != 1) { // store original size info if a scaling was requested store_size_info(dib, cinfo.image_width, cinfo.image_height); } // step 5c: handle metrices if (cinfo.density_unit == 1) { // dots/inch FreeImage_SetDotsPerMeterX(dib, (unsigned) (((float)cinfo.X_density) / 0.0254000 + 0.5)); FreeImage_SetDotsPerMeterY(dib, (unsigned) (((float)cinfo.Y_density) / 0.0254000 + 0.5)); } else if (cinfo.density_unit == 2) { // dots/cm FreeImage_SetDotsPerMeterX(dib, (unsigned) (cinfo.X_density * 100)); FreeImage_SetDotsPerMeterY(dib, (unsigned) (cinfo.Y_density * 100)); } // step 6a: while (scan lines remain to be read) jpeg_read_scanlines(...); if((cinfo.out_color_space == JCS_CMYK) && ((flags & JPEG_CMYK) != JPEG_CMYK)) { // convert from CMYK to RGB JSAMPARRAY buffer; // output row buffer unsigned row_stride; // physical row width in output buffer // JSAMPLEs per row in output buffer row_stride = cinfo.output_width * cinfo.output_components; // make a one-row-high sample array that will go away when done with image buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); while (cinfo.output_scanline < cinfo.output_height) { JSAMPROW src = buffer[0]; JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1); jpeg_read_scanlines(&cinfo, buffer, 1); for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) { WORD K = (WORD)src[3]; dst[FI_RGBA_RED] = (BYTE)((K * src[0]) / 255); dst[FI_RGBA_GREEN] = (BYTE)((K * src[1]) / 255); dst[FI_RGBA_BLUE] = (BYTE)((K * src[2]) / 255); src += 4; dst += 3; } } } else { // normal case (RGB or greyscale image) while (cinfo.output_scanline < cinfo.output_height) { JSAMPROW dst = FreeImage_GetScanLine(dib, cinfo.output_height - cinfo.output_scanline - 1); jpeg_read_scanlines(&cinfo, &dst, 1); } // step 6b: swap red and blue components (see LibJPEG/jmorecfg.h: #define RGB_RED, ...) // The default behavior of the JPEG library is kept "as is" because LibTIFF uses // LibJPEG "as is". #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR if(cinfo.num_components == 3) { for(unsigned y = 0; y < FreeImage_GetHeight(dib); y++) { BYTE *target = FreeImage_GetScanLine(dib, y); for(unsigned x = 0; x < FreeImage_GetWidth(dib); x++) { INPLACESWAP(target[0], target[2]); target += 3; } } } #endif } // step 7: read special markers read_markers(&cinfo, dib); // step 8: finish decompression jpeg_finish_decompress(&cinfo); // step 9: release JPEG decompression object jpeg_destroy_decompress(&cinfo); // check for automatic Exif rotation if((flags & JPEG_EXIFROTATE) == JPEG_EXIFROTATE) { rotate_exif(&dib); } // everything went well. return the loaded dib return (FIBITMAP *)dib; } catch (...) { if(NULL != dib) { FreeImage_Unload(dib); } } } return NULL; }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { TIFF *faxTIFF = NULL; FIBITMAP *dib = NULL; FIMEMORY *memory = NULL; //int verbose = 0; int stretch = 0; int rows; float resX = 204.0; float resY = 196.0; uint32 xsize = G3_DEFAULT_WIDTH; int compression_in = COMPRESSION_CCITTFAX3; int fillorder_in = FILLORDER_LSB2MSB; uint32 group3options_in = 0; // 1d-encoded uint32 group4options_in = 0; // compressed int photometric_in = PHOTOMETRIC_MINISWHITE; if(handle==NULL) return NULL; try { // set default load options compression_in = COMPRESSION_CCITTFAX3; // input is g3-encoded group3options_in &= ~GROUP3OPT_2DENCODING; // input is 1d-encoded (g3 only) fillorder_in = FILLORDER_MSB2LSB; // input has msb-to-lsb fillorder /* Original input-related fax2tiff options while ((c = getopt(argc, argv, "R:X:o:1234ABLMPUW5678abcflmprsuvwz?")) != -1) { switch (c) { // input-related options case '3': // input is g3-encoded compression_in = COMPRESSION_CCITTFAX3; break; case '4': // input is g4-encoded compression_in = COMPRESSION_CCITTFAX4; break; case 'U': // input is uncompressed (g3 and g4) group3options_in |= GROUP3OPT_UNCOMPRESSED; group4options_in |= GROUP4OPT_UNCOMPRESSED; break; case '1': // input is 1d-encoded (g3 only) group3options_in &= ~GROUP3OPT_2DENCODING; break; case '2': // input is 2d-encoded (g3 only) group3options_in |= GROUP3OPT_2DENCODING; break; case 'P': // input has not-aligned EOL (g3 only) group3options_in &= ~GROUP3OPT_FILLBITS; break; case 'A': // input has aligned EOL (g3 only) group3options_in |= GROUP3OPT_FILLBITS; break; case 'W': // input has 0 mean white photometric_in = PHOTOMETRIC_MINISWHITE; break; case 'B': // input has 0 mean black photometric_in = PHOTOMETRIC_MINISBLACK; break; case 'L': // input has lsb-to-msb fillorder fillorder_in = FILLORDER_LSB2MSB; break; case 'M': // input has msb-to-lsb fillorder fillorder_in = FILLORDER_MSB2LSB; break; case 'R': // input resolution resY = (float) atof(optarg); break; case 'X': // input width xsize = (uint32) atoi(optarg); break; // output-related options case 's': // stretch image by dup'ng scanlines stretch = 1; break; case 'v': // -v for info verbose++; break; } } */ // open a temporary memory buffer to save decoded scanlines memory = FreeImage_OpenMemory(); if(!memory) throw FI_MSG_ERROR_MEMORY; // wrap the raw fax file faxTIFF = TIFFClientOpen("(FakeInput)", "w", // TIFFClientOpen() fails if we don't set existing value here NULL, _g3ReadProc, _g3WriteProc, _g3SeekProc, _g3CloseProc, _g3SizeProc, _g3MapProc, _g3UnmapProc); if (faxTIFF == NULL) { throw "Can not create fake input file"; } TIFFSetMode(faxTIFF, O_RDONLY); TIFFSetField(faxTIFF, TIFFTAG_IMAGEWIDTH, xsize); TIFFSetField(faxTIFF, TIFFTAG_SAMPLESPERPIXEL, 1); TIFFSetField(faxTIFF, TIFFTAG_BITSPERSAMPLE, 1); TIFFSetField(faxTIFF, TIFFTAG_FILLORDER, fillorder_in); TIFFSetField(faxTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(faxTIFF, TIFFTAG_PHOTOMETRIC, photometric_in); TIFFSetField(faxTIFF, TIFFTAG_YRESOLUTION, resY); TIFFSetField(faxTIFF, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); // NB: this must be done after directory info is setup TIFFSetField(faxTIFF, TIFFTAG_COMPRESSION, compression_in); if (compression_in == COMPRESSION_CCITTFAX3) TIFFSetField(faxTIFF, TIFFTAG_GROUP3OPTIONS, group3options_in); else if (compression_in == COMPRESSION_CCITTFAX4) TIFFSetField(faxTIFF, TIFFTAG_GROUP4OPTIONS, group4options_in); resX = 204; if (!stretch) { TIFFGetField(faxTIFF, TIFFTAG_YRESOLUTION, &resY); } else { resY = 196; } // decode the raw fax data rows = copyFaxFile(io, handle, faxTIFF, xsize, stretch, memory); if(rows <= 0) throw "Error when decoding raw fax file : check the decoder options"; // allocate the output dib dib = FreeImage_Allocate(xsize, rows, 1); unsigned pitch = FreeImage_GetPitch(dib); uint32 linesize = TIFFhowmany8(xsize); // fill the bitmap structure ... // ... palette RGBQUAD *pal = FreeImage_GetPalette(dib); if(photometric_in == PHOTOMETRIC_MINISWHITE) { pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 255; pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 0; } else { pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0; pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255; } // ... resolution FreeImage_SetDotsPerMeterX(dib, (unsigned)(resX/0.0254000 + 0.5)); FreeImage_SetDotsPerMeterY(dib, (unsigned)(resY/0.0254000 + 0.5)); // read the decoded scanline and fill the bitmap data FreeImage_SeekMemory(memory, 0, SEEK_SET); BYTE *bits = FreeImage_GetScanLine(dib, rows - 1); for(int k = 0; k < rows; k++) { FreeImage_ReadMemory(bits, linesize, 1, memory); bits -= pitch; } // free the TIFF wrapper TIFFClose(faxTIFF); // free the memory buffer FreeImage_CloseMemory(memory); } catch(const char *message) { if(memory) FreeImage_CloseMemory(memory); if(faxTIFF) TIFFClose(faxTIFF); if(dib) FreeImage_Unload(dib); FreeImage_OutputMessageProc(s_format_id, message); return NULL; } return dib; }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { PKImageDecode *pDecoder = NULL; // decoder interface ERR error_code = 0; // error code as returned by the interface PKPixelFormatGUID guid_format; // loaded pixel format (== input file pixel format if no conversion needed) FREE_IMAGE_TYPE image_type = FIT_UNKNOWN; // input image type unsigned bpp = 0; // input image bit depth FIBITMAP *dib = NULL; // get the I/O stream wrapper WMPStream *pDecodeStream = (WMPStream*)data; if(!handle || !pDecodeStream) { return NULL; } BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; try { int width, height; // image dimensions (in pixels) // create a JXR decoder interface and initialize function pointers with *_WMP functions error_code = PKImageDecode_Create_WMP(&pDecoder); JXR_CHECK(error_code); // attach the stream to the decoder ... // ... then read the image container and the metadata error_code = pDecoder->Initialize(pDecoder, pDecodeStream); JXR_CHECK(error_code); // set decoder parameters SetDecoderParameters(pDecoder, flags); // get dst image format specifications unsigned red_mask = 0, green_mask = 0, blue_mask = 0; error_code = GetInputPixelFormat(pDecoder, &guid_format, &image_type, &bpp, &red_mask, &green_mask, &blue_mask); JXR_CHECK(error_code); // get image dimensions pDecoder->GetSize(pDecoder, &width, &height); // allocate dst image { dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, bpp, red_mask, green_mask, blue_mask); if(!dib) { throw FI_MSG_ERROR_DIB_MEMORY; } if(FreeImage_GetBPP(dib) == 1) { // BD_1 - build a FIC_MINISBLACK palette RGBQUAD *pal = FreeImage_GetPalette(dib); pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0; pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255; } } // get image resolution { float resX, resY; // image resolution (in dots per inch) // convert from English units, i.e. dots per inch to universal units, i.e. dots per meter pDecoder->GetResolution(pDecoder, &resX, &resY); FreeImage_SetDotsPerMeterX(dib, (unsigned)(resX / 0.0254F + 0.5F)); FreeImage_SetDotsPerMeterY(dib, (unsigned)(resY / 0.0254F + 0.5F)); } // get metadata & ICC profile error_code = ReadMetadata(pDecoder, dib); JXR_CHECK(error_code); if(header_only) { // header only mode ... // free the decoder pDecoder->Release(&pDecoder); assert(pDecoder == NULL); return dib; } // copy pixels into the dib, perform pixel conversion if needed error_code = CopyPixels(pDecoder, guid_format, dib, width, height); JXR_CHECK(error_code); // free the decoder pDecoder->Release(&pDecoder); assert(pDecoder == NULL); return dib; } catch (const char *message) { // unload the dib FreeImage_Unload(dib); // free the decoder pDecoder->Release(&pDecoder); if(NULL != message) { FreeImage_OutputMessageProc(s_format_id, message); } } return NULL; }