static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { bool bUseRgbaInterface = false; FIBITMAP *dib = NULL; if(!handle) { return NULL; } try { BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; // save the stream starting point const long stream_start = io->tell_proc(handle); // wrap the FreeImage IO stream C_IStream istream(io, handle); // open the file Imf::InputFile file(istream); // get file info const Imath::Box2i &dataWindow = file.header().dataWindow(); int width = dataWindow.max.x - dataWindow.min.x + 1; int height = dataWindow.max.y - dataWindow.min.y + 1; //const Imf::Compression &compression = file.header().compression(); const Imf::ChannelList &channels = file.header().channels(); // check the number of components and check for a coherent format std::string exr_color_model; Imf::PixelType pixel_type = Imf::HALF; FREE_IMAGE_TYPE image_type = FIT_UNKNOWN; int components = 0; bool bMixedComponents = false; for (Imf::ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { components++; if(components == 1) { exr_color_model += i.name(); pixel_type = i.channel().type; } else { exr_color_model += "/"; exr_color_model += i.name(); if (i.channel().type != pixel_type) { bMixedComponents = true; } } } if(bMixedComponents) { bool bHandled = false; // we may have a RGBZ or RGBAZ image ... if(components > 4) { if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B") && channels.findChannel("A")) { std::string msg = "Warning: converting color model " + exr_color_model + " to RGBA color model"; FreeImage_OutputMessageProc(s_format_id, msg.c_str()); bHandled = true; } } else if(components > 3) { if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) { std::string msg = "Warning: converting color model " + exr_color_model + " to RGB color model"; FreeImage_OutputMessageProc(s_format_id, msg.c_str()); bHandled = true; } } if(!bHandled) { THROW (Iex::InputExc, "Unable to handle mixed component types (color model = " << exr_color_model << ")"); } } switch(pixel_type) { case Imf::UINT: THROW (Iex::InputExc, "Unsupported format: UINT"); break; case Imf::HALF: case Imf::FLOAT: default: break; } // check for supported image color models // -------------------------------------------------------------- if((components == 1) || (components == 2)) { // if the image is gray-alpha (YA), ignore the alpha channel if((components == 1) && channels.findChannel("Y")) { image_type = FIT_FLOAT; components = 1; } else { std::string msg = "Warning: loading color model " + exr_color_model + " as Y color model"; FreeImage_OutputMessageProc(s_format_id, msg.c_str()); image_type = FIT_FLOAT; // ignore the other channel components = 1; } } else if(components == 3) { if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) { image_type = FIT_RGBF; } else if(channels.findChannel("BY") && channels.findChannel("RY") && channels.findChannel("Y")) { image_type = FIT_RGBF; bUseRgbaInterface = true; } } else if(components >= 4) { if(channels.findChannel("R") && channels.findChannel("G") && channels.findChannel("B")) { if(channels.findChannel("A")) { if(components > 4) { std::string msg = "Warning: converting color model " + exr_color_model + " to RGBA color model"; FreeImage_OutputMessageProc(s_format_id, msg.c_str()); } image_type = FIT_RGBAF; // ignore other layers if there is more than one alpha layer components = 4; } else { std::string msg = "Warning: converting color model " + exr_color_model + " to RGB color model"; FreeImage_OutputMessageProc(s_format_id, msg.c_str()); image_type = FIT_RGBF; // ignore other channels components = 3; } } } if(image_type == FIT_UNKNOWN) { THROW (Iex::InputExc, "Unsupported color model: " << exr_color_model); } // allocate a new dib dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, 0); if(!dib) THROW (Iex::NullExc, FI_MSG_ERROR_MEMORY); // try to load the preview image // -------------------------------------------------------------- if(file.header().hasPreviewImage()) { const Imf::PreviewImage& preview = file.header().previewImage(); const unsigned thWidth = preview.width(); const unsigned thHeight = preview.height(); FIBITMAP* thumbnail = FreeImage_Allocate(thWidth, thHeight, 32); if(thumbnail) { const Imf::PreviewRgba *src_line = preview.pixels(); BYTE *dst_line = FreeImage_GetScanLine(thumbnail, thHeight - 1); const unsigned dstPitch = FreeImage_GetPitch(thumbnail); for (unsigned y = 0; y < thHeight; ++y) { const Imf::PreviewRgba *src_pixel = src_line; RGBQUAD* dst_pixel = (RGBQUAD*)dst_line; for(unsigned x = 0; x < thWidth; ++x) { dst_pixel->rgbRed = src_pixel->r; dst_pixel->rgbGreen = src_pixel->g; dst_pixel->rgbBlue = src_pixel->b; dst_pixel->rgbReserved = src_pixel->a; src_pixel++; dst_pixel++; } src_line += thWidth; dst_line -= dstPitch; } FreeImage_SetThumbnail(dib, thumbnail); FreeImage_Unload(thumbnail); } } if(header_only) { // header only mode return dib; } // load pixels // -------------------------------------------------------------- const BYTE *bits = FreeImage_GetBits(dib); // pointer to our pixel buffer const size_t bytespp = sizeof(float) * components; // size of our pixel in bytes const unsigned pitch = FreeImage_GetPitch(dib); // size of our yStride in bytes Imf::PixelType pixelType = Imf::FLOAT; // load as float data type; if(bUseRgbaInterface) { // use the RGBA interface (used when loading RY BY Y images ) const int chunk_size = 16; BYTE *scanline = (BYTE*)bits; // re-open using the RGBA interface io->seek_proc(handle, stream_start, SEEK_SET); Imf::RgbaInputFile rgbaFile(istream); // read the file in chunks Imath::Box2i dw = dataWindow; Imf::Array2D<Imf::Rgba> chunk(chunk_size, width); while (dw.min.y <= dw.max.y) { // read a chunk rgbaFile.setFrameBuffer (&chunk[0][0] - dw.min.x - dw.min.y * width, 1, width); rgbaFile.readPixels (dw.min.y, MIN(dw.min.y + chunk_size - 1, dw.max.y)); // fill the dib const int y_max = ((dw.max.y - dw.min.y) <= chunk_size) ? (dw.max.y - dw.min.y) : chunk_size; for(int y = 0; y < y_max; y++) { FIRGBF *pixel = (FIRGBF*)scanline; const Imf::Rgba *half_rgba = chunk[y]; for(int x = 0; x < width; x++) { // convert from half to float pixel[x].red = half_rgba[x].r; pixel[x].green = half_rgba[x].g; pixel[x].blue = half_rgba[x].b; } // next line scanline += pitch; } // next chunk dw.min.y += chunk_size; } } else { // use the low level interface // build a frame buffer (i.e. what we want on output) Imf::FrameBuffer frameBuffer; // allow dataWindow with minimal bounds different form zero size_t offset = - dataWindow.min.x * bytespp - dataWindow.min.y * pitch; if(components == 1) { frameBuffer.insert ("Y", // name Imf::Slice (pixelType, // type (char*)(bits + offset), // base bytespp, // xStride pitch, // yStride 1, 1, // x/y sampling 0.0)); // fillValue } else if((components == 3) || (components == 4)) { const char *channel_name[4] = { "R", "G", "B", "A" }; for(int c = 0; c < components; c++) { frameBuffer.insert ( channel_name[c], // name Imf::Slice (pixelType, // type (char*)(bits + c * sizeof(float) + offset), // base bytespp, // xStride pitch, // yStride 1, 1, // x/y sampling 0.0)); // fillValue } } // read the file file.setFrameBuffer(frameBuffer); file.readPixels(dataWindow.min.y, dataWindow.max.y); } // lastly, flip dib lines FreeImage_FlipVertical(dib); } catch(Iex::BaseExc & e) { if(dib != NULL) { FreeImage_Unload(dib); } FreeImage_OutputMessageProc(s_format_id, e.what()); return NULL; } return dib; }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { char line_buffer[PFM_MAXLINE]; char id_one = 0, id_two = 0; FIBITMAP *dib = NULL; float *lineBuffer = NULL; if (!handle) { return NULL; } BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; try { FREE_IMAGE_TYPE image_type = FIT_UNKNOWN; // Read the first two bytes of the file to determine the file format // "PF" = color image // "Pf" = greyscale image io->read_proc(&id_one, 1, 1, handle); io->read_proc(&id_two, 1, 1, handle); if(id_one == 'P') { if(id_two == 'F') { image_type = FIT_RGBF; } else if(id_two == 'f') { image_type = FIT_FLOAT; } } if(image_type == FIT_UNKNOWN) { // signature error throw FI_MSG_ERROR_MAGIC_NUMBER; } // Read the header information: width, height and the scale value unsigned width = (unsigned) pfm_get_int(io, handle); unsigned height = (unsigned) pfm_get_int(io, handle); float scalefactor = 1; BOOL bResult = pfm_get_line(io, handle, line_buffer, PFM_MAXLINE); if(bResult) { bResult = (sscanf(line_buffer, "%f", &scalefactor) == 1) ? TRUE : FALSE; } if(!bResult) { throw "Read error: invalid PFM header"; } // Create a new DIB dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height); if (dib == NULL) { throw FI_MSG_ERROR_DIB_MEMORY; } if(header_only) { // header only mode return dib; } // Read the image... if(image_type == FIT_RGBF) { const unsigned lineWidth = 3 * width; lineBuffer = (float*)malloc(lineWidth * sizeof(float)); if(!lineBuffer) { throw FI_MSG_ERROR_MEMORY; } for (unsigned y = 0; y < height; y++) { FIRGBF *bits = (FIRGBF*)FreeImage_GetScanLine(dib, height - 1 - y); if(io->read_proc(lineBuffer, sizeof(float), lineWidth, handle) != lineWidth) { throw "Read error"; } float *channel = lineBuffer; if(scalefactor > 0) { // MSB for (unsigned x = 0; x < width; x++) { REVERSEBYTES(channel++, &bits[x].red); REVERSEBYTES(channel++, &bits[x].green); REVERSEBYTES(channel++, &bits[x].blue); } } else { // LSB for (unsigned x = 0; x < width; x++) { bits[x].red = *channel++; bits[x].green = *channel++; bits[x].blue = *channel++; } } } free(lineBuffer); lineBuffer = NULL; } else if(image_type == FIT_FLOAT) { const unsigned lineWidth = width; lineBuffer = (float*)malloc(lineWidth * sizeof(float)); if(!lineBuffer) { throw FI_MSG_ERROR_MEMORY; } for (unsigned y = 0; y < height; y++) { float *bits = (float*)FreeImage_GetScanLine(dib, height - 1 - y); if(io->read_proc(lineBuffer, sizeof(float), lineWidth, handle) != lineWidth) { throw "Read error"; } float *channel = lineBuffer; if(scalefactor > 0) { // MSB - File is Big endian for (unsigned x = 0; x < width; x++) { REVERSEBYTES(channel++, &bits[x]); } } else { // LSB - File is Little Endian for (unsigned x = 0; x < width; x++) { bits[x] = *channel++; } } } free(lineBuffer); lineBuffer = NULL; } return dib; } catch (const char *text) { if(lineBuffer) free(lineBuffer); if(dib) FreeImage_Unload(dib); if(NULL != text) { FreeImage_OutputMessageProc(s_format_id, text); } return NULL; } }
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; }
FIBITMAP * DLL_CALLCONV FreeImage_Clone(FIBITMAP *dib) { if(!dib) return NULL; unsigned width = FreeImage_GetWidth(dib); unsigned height = FreeImage_GetHeight(dib); unsigned bpp = FreeImage_GetBPP(dib); // check for pixel availability ... BOOL header_only = FreeImage_HasPixels(dib) ? FALSE : TRUE; // allocate a new dib FIBITMAP *new_dib = FreeImage_AllocateHeaderT(header_only, FreeImage_GetImageType(dib), width, height, bpp, FreeImage_GetRedMask(dib), FreeImage_GetGreenMask(dib), FreeImage_GetBlueMask(dib)); if (new_dib) { // save ICC profile links FIICCPROFILE *src_iccProfile = FreeImage_GetICCProfile(dib); FIICCPROFILE *dst_iccProfile = FreeImage_GetICCProfile(new_dib); // save metadata links METADATAMAP *src_metadata = ((FREEIMAGEHEADER *)dib->data)->metadata; METADATAMAP *dst_metadata = ((FREEIMAGEHEADER *)new_dib->data)->metadata; // calculate the size of a FreeImage image // align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary // palette is aligned on a 16 bytes boundary // pixels are aligned on a 16 bytes boundary size_t dib_size = FreeImage_GetImageSizeHeader(header_only, width, height, bpp); // copy the bitmap + internal pointers (remember to restore new_dib internal pointers later) memcpy(new_dib->data, dib->data, dib_size); // reset ICC profile link for new_dib memset(dst_iccProfile, 0, sizeof(FIICCPROFILE)); // restore metadata link for new_dib ((FREEIMAGEHEADER *)new_dib->data)->metadata = dst_metadata; // reset thumbnail link for new_dib ((FREEIMAGEHEADER *)new_dib->data)->thumbnail = NULL; // copy possible ICC profile FreeImage_CreateICCProfile(new_dib, src_iccProfile->data, src_iccProfile->size); dst_iccProfile->flags = src_iccProfile->flags; // copy metadata models for(METADATAMAP::iterator i = (*src_metadata).begin(); i != (*src_metadata).end(); i++) { int model = (*i).first; TAGMAP *src_tagmap = (*i).second; if(src_tagmap) { // 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; } } } // copy the thumbnail FreeImage_SetThumbnail(new_dib, FreeImage_GetThumbnail(dib)); return new_dib; } return NULL; }
FIBITMAP* psdParser::ReadImageData(FreeImageIO *io, fi_handle handle) { if(handle == NULL) return NULL; bool header_only = (_fi_flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; assert(sizeof(WORD) == 2); WORD nCompression = 0; io->read_proc(&nCompression, sizeof(nCompression), 1, handle); #ifndef FREEIMAGE_BIGENDIAN SwapShort(&nCompression); #endif if((nCompression != PSDP_COMPRESSION_NONE && nCompression != PSDP_COMPRESSION_RLE)) { FreeImage_OutputMessageProc(_fi_format_id, "Unsupported compression %d", nCompression); return NULL; } const unsigned nWidth = _headerInfo._Width; const unsigned nHeight = _headerInfo._Height; const unsigned nChannels = _headerInfo._Channels; const unsigned depth = _headerInfo._BitsPerChannel; const unsigned bytes = (depth == 1) ? 1 : depth / 8; // channel(plane) line (BYTE aligned) const unsigned lineSize = (_headerInfo._BitsPerChannel == 1) ? (nWidth + 7) / 8 : nWidth * bytes; if(nCompression == PSDP_COMPRESSION_RLE && depth > 16) { FreeImage_OutputMessageProc(_fi_format_id, "Unsupported RLE with depth %d", depth); return NULL; } // build output buffer FIBITMAP* bitmap = NULL; unsigned dstCh = 0; short mode = _headerInfo._ColourMode; if(mode == PSDP_MULTICHANNEL && nChannels < 3) { // CM mode = PSDP_GRAYSCALE; // C as gray, M as extra channel } bool needPalette = false; switch (mode) { case PSDP_BITMAP: case PSDP_DUOTONE: case PSDP_INDEXED: case PSDP_GRAYSCALE: dstCh = 1; switch(depth) { case 16: bitmap = FreeImage_AllocateHeaderT(header_only, FIT_UINT16, nWidth, nHeight, depth*dstCh); break; case 32: bitmap = FreeImage_AllocateHeaderT(header_only, FIT_FLOAT, nWidth, nHeight, depth*dstCh); break; default: // 1-, 8- needPalette = true; bitmap = FreeImage_AllocateHeader(header_only, nWidth, nHeight, depth*dstCh); break; } break; case PSDP_RGB: case PSDP_LAB: case PSDP_CMYK : case PSDP_MULTICHANNEL : // force PSDP_MULTICHANNEL CMY as CMYK dstCh = (mode == PSDP_MULTICHANNEL && !header_only) ? 4 : MIN<unsigned>(nChannels, 4); assert(dstCh >= 3); switch(depth) { case 16: bitmap = FreeImage_AllocateHeaderT(header_only, dstCh < 4 ? FIT_RGB16 : FIT_RGBA16, nWidth, nHeight, depth*dstCh); break; case 32: bitmap = FreeImage_AllocateHeaderT(header_only, dstCh < 4 ? FIT_RGBF : FIT_RGBAF, nWidth, nHeight, depth*dstCh); break; default: bitmap = FreeImage_AllocateHeader(header_only, nWidth, nHeight, depth*dstCh); break; } break; default: assert(false); break; } if(!bitmap) { throw FI_MSG_ERROR_DIB_MEMORY; } // @todo Add some metadata model if(header_only) { return bitmap; } // Load pixels data const unsigned dstChannels = dstCh; const unsigned dstBpp = (depth == 1) ? 1 : FreeImage_GetBPP(bitmap)/8; const unsigned dstLineSize = FreeImage_GetPitch(bitmap); BYTE* const dst_first_line = FreeImage_GetScanLine(bitmap, nHeight - 1);//<*** flipped BYTE* line_start = new BYTE[lineSize]; //< fileline cache switch ( nCompression ) { case PSDP_COMPRESSION_NONE: // raw data { for(unsigned c = 0; c < nChannels; c++) { if(c >= dstChannels) { // @todo write extra channels break; } const unsigned channelOffset = c * bytes; BYTE* dst_line_start = dst_first_line; for(unsigned h = 0; h < nHeight; ++h, dst_line_start -= dstLineSize) {//<*** flipped io->read_proc(line_start, lineSize, 1, handle); for (BYTE *line = line_start, *dst_line = dst_line_start; line < line_start + lineSize; line += bytes, dst_line += dstBpp) { #ifdef FREEIMAGE_BIGENDIAN memcpy(dst_line + channelOffset, line, bytes); #else // reverse copy bytes for (unsigned b = 0; b < bytes; ++b) { dst_line[channelOffset + b] = line[(bytes-1) - b]; } #endif // FREEIMAGE_BIGENDIAN } } //< h }//< ch SAFE_DELETE_ARRAY(line_start); } break; case PSDP_COMPRESSION_RLE: // RLE compression { // The RLE-compressed data is preceeded by a 2-byte line size for each row in the data, // store an array of these // later use this array as WORD rleLineSizeList[nChannels][nHeight]; WORD *rleLineSizeList = new (std::nothrow) WORD[nChannels*nHeight]; if(!rleLineSizeList) { FreeImage_Unload(bitmap); SAFE_DELETE_ARRAY(line_start); throw std::bad_alloc(); } io->read_proc(rleLineSizeList, 2, nChannels * nHeight, handle); WORD largestRLELine = 0; for(unsigned ch = 0; ch < nChannels; ++ch) { for(unsigned h = 0; h < nHeight; ++h) { const unsigned index = ch * nHeight + h; #ifndef FREEIMAGE_BIGENDIAN SwapShort(&rleLineSizeList[index]); #endif if(largestRLELine < rleLineSizeList[index]) { largestRLELine = rleLineSizeList[index]; } } } BYTE* rle_line_start = new (std::nothrow) BYTE[largestRLELine]; if(!rle_line_start) { FreeImage_Unload(bitmap); SAFE_DELETE_ARRAY(line_start); SAFE_DELETE_ARRAY(rleLineSizeList); throw std::bad_alloc(); } // Read the RLE data (assume 8-bit) const BYTE* const line_end = line_start + lineSize; for (unsigned ch = 0; ch < nChannels; ch++) { const unsigned channelOffset = ch * bytes; BYTE* dst_line_start = dst_first_line; for(unsigned h = 0; h < nHeight; ++h, dst_line_start -= dstLineSize) {//<*** flipped const unsigned index = ch * nHeight + h; // - read and uncompress line - const WORD rleLineSize = rleLineSizeList[index]; io->read_proc(rle_line_start, rleLineSize, 1, handle); for (BYTE* rle_line = rle_line_start, *line = line_start; rle_line < rle_line_start + rleLineSize, line < line_end;) { int len = *rle_line++; // NOTE len is signed byte in PackBits RLE if ( len < 128 ) { //<- MSB is not set // uncompressed packet // (len + 1) bytes of data are copied ++len; // assert we don't write beyound eol memcpy(line, rle_line, line + len > line_end ? line_end - line : len); line += len; rle_line += len; } else if ( len > 128 ) { //< MSB is set // RLE compressed packet // One byte of data is repeated (–len + 1) times len ^= 0xFF; // same as (-len + 1) & 0xFF len += 2; // // assert we don't write beyound eol memset(line, *rle_line++, line + len > line_end ? line_end - line : len); line += len; } else if ( 128 == len ) { // Do nothing } }//< rle_line // - write line to destination - if(ch >= dstChannels) { // @todo write to extra channels break; } // byte by byte copy a single channel to pixel for (BYTE *line = line_start, *dst_line = dst_line_start; line < line_start + lineSize; line += bytes, dst_line += dstBpp) { #ifdef FREEIMAGE_BIGENDIAN memcpy(dst_line + channelOffset, line, bytes); #else // reverse copy bytes for (unsigned b = 0; b < bytes; ++b) { dst_line[channelOffset + b] = line[(bytes-1) - b]; } #endif // FREEIMAGE_BIGENDIAN } }//< h }//< ch SAFE_DELETE_ARRAY(line_start); SAFE_DELETE_ARRAY(rleLineSizeList); SAFE_DELETE_ARRAY(rle_line_start); } break; case 2: // ZIP without prediction, no specification break; case 3: // ZIP with prediction, no specification break; default: // Unknown format break; } // --- Further process the bitmap --- if((mode == PSDP_CMYK || mode == PSDP_MULTICHANNEL)) { // CMYK values are "inverted", invert them back if(mode == PSDP_MULTICHANNEL) { invertColor(bitmap); } else { FreeImage_Invert(bitmap); } if((_fi_flags & PSD_CMYK) == PSD_CMYK) { // keep as CMYK if(mode == PSDP_MULTICHANNEL) { //### we force CMY to be CMYK, but CMY has no ICC. // Create empty profile and add the flag. FreeImage_CreateICCProfile(bitmap, NULL, 0); FreeImage_GetICCProfile(bitmap)->flags |= FIICC_COLOR_IS_CMYK; } } else { // convert to RGB ConvertCMYKtoRGBA(bitmap); // The ICC Profile is no longer valid _iccProfile.clear(); // remove the pending A if not present in source if(nChannels == 4 || nChannels == 3 ) { FIBITMAP* t = RemoveAlphaChannel(bitmap); if(t) { FreeImage_Unload(bitmap); bitmap = t; } // else: silently fail } } } else if ( mode == PSDP_LAB && !((_fi_flags & PSD_LAB) == PSD_LAB)) { ConvertLABtoRGB(bitmap); } else { if (needPalette) { assert(FreeImage_GetPalette(bitmap)); if(mode == PSDP_BITMAP) { CREATE_GREYSCALE_PALETTE_REVERSE(FreeImage_GetPalette(bitmap), 2); } else if(mode == PSDP_INDEXED) { if(!_colourModeData._plColourData || _colourModeData._Length != 768 || _ColourCount < 0) { FreeImage_OutputMessageProc(_fi_format_id, "Indexed image has no palette. Using the default grayscale one."); } else { _colourModeData.FillPalette(bitmap); } } // GRAYSCALE, DUOTONE - use default grayscale palette } #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR if(FreeImage_GetImageType(bitmap) == FIT_BITMAP) { SwapRedBlue32(bitmap); } #endif } return bitmap; }
FIBITMAP * DLL_CALLCONV FreeImage_Allocate(int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { return FreeImage_AllocateHeaderT(FALSE, FIT_BITMAP, width, height, bpp, red_mask, green_mask, blue_mask); }
FIBITMAP * DLL_CALLCONV FreeImage_AllocateT(FREE_IMAGE_TYPE type, int width, int height, int bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { return FreeImage_AllocateHeaderT(FALSE, type, width, height, bpp, red_mask, green_mask, blue_mask); }
FIBITMAP * DLL_CALLCONV FreeImage_Clone(FIBITMAP *dib) { if(!dib) { return NULL; } FREE_IMAGE_TYPE type = FreeImage_GetImageType(dib); unsigned width = FreeImage_GetWidth(dib); unsigned height = FreeImage_GetHeight(dib); unsigned bpp = FreeImage_GetBPP(dib); const BYTE *ext_bits = ((FREEIMAGEHEADER *)dib->data)->external_bits; // check for pixel availability ... BOOL header_only = FreeImage_HasPixels(dib) ? FALSE : TRUE; // check whether this image has masks defined ... BOOL need_masks = (bpp == 16 && type == FIT_BITMAP) ? TRUE : FALSE; // allocate a new dib FIBITMAP *new_dib = FreeImage_AllocateHeaderT(header_only, type, width, height, bpp, FreeImage_GetRedMask(dib), FreeImage_GetGreenMask(dib), FreeImage_GetBlueMask(dib)); if (new_dib) { // save ICC profile links FIICCPROFILE *src_iccProfile = FreeImage_GetICCProfile(dib); FIICCPROFILE *dst_iccProfile = FreeImage_GetICCProfile(new_dib); // save metadata links METADATAMAP *src_metadata = ((FREEIMAGEHEADER *)dib->data)->metadata; METADATAMAP *dst_metadata = ((FREEIMAGEHEADER *)new_dib->data)->metadata; // calculate the size of the src image // align the palette and the pixels on a FIBITMAP_ALIGNMENT bytes alignment boundary // palette is aligned on a 16 bytes boundary // pixels are aligned on a 16 bytes boundary // when using a user provided pixel buffer, force a 'header only' calculation size_t dib_size = FreeImage_GetInternalImageSize(header_only || ext_bits, width, height, bpp, need_masks); // copy the bitmap + internal pointers (remember to restore new_dib internal pointers later) memcpy(new_dib->data, dib->data, dib_size); // reset ICC profile link for new_dib memset(dst_iccProfile, 0, sizeof(FIICCPROFILE)); // restore metadata link for new_dib ((FREEIMAGEHEADER *)new_dib->data)->metadata = dst_metadata; // reset thumbnail link for new_dib ((FREEIMAGEHEADER *)new_dib->data)->thumbnail = NULL; // copy possible ICC profile FreeImage_CreateICCProfile(new_dib, src_iccProfile->data, src_iccProfile->size); dst_iccProfile->flags = src_iccProfile->flags; // copy metadata models for(METADATAMAP::iterator i = (*src_metadata).begin(); i != (*src_metadata).end(); i++) { int model = (*i).first; TAGMAP *src_tagmap = (*i).second; if(src_tagmap) { // 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; } } } // copy the thumbnail FreeImage_SetThumbnail(new_dib, FreeImage_GetThumbnail(dib)); // copy user provided pixel buffer (if any) if(ext_bits) { const unsigned pitch = FreeImage_GetPitch(dib); const unsigned linesize = FreeImage_GetLine(dib); for(unsigned y = 0; y < height; y++) { memcpy(FreeImage_GetScanLine(new_dib, y), ext_bits, linesize); ext_bits += pitch; } } return new_dib; } return NULL; }
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; }
/** Convert a OpenJPEG image to a FIBITMAP @param format_id Plugin ID @param image OpenJPEG image @param header_only If TRUE, allocate a 'header only' FIBITMAP, otherwise allocate a full FIBITMAP @return Returns the converted image if successful, returns NULL otherwise */ FIBITMAP* J2KImageToFIBITMAP(int format_id, const opj_image_t *image, BOOL header_only) { FIBITMAP *dib = NULL; try { // compute image width and height //int w = int_ceildiv(image->x1 - image->x0, image->comps[0].dx); int wr = image->comps[0].w; int wrr = int_ceildivpow2(image->comps[0].w, image->comps[0].factor); //int h = int_ceildiv(image->y1 - image->y0, image->comps[0].dy); //int hr = image->comps[0].h; int hrr = int_ceildivpow2(image->comps[0].h, image->comps[0].factor); // check the number of components int numcomps = image->numcomps; BOOL bIsValid = TRUE; for(int c = 0; c < numcomps - 1; c++) { if( (image->comps[c].dx == image->comps[c+1].dx) && (image->comps[c].dy == image->comps[c+1].dy) && (image->comps[c].prec == image->comps[c+1].prec) ) { continue; } else { bIsValid = FALSE; break; } } bIsValid &= ((numcomps == 1) || (numcomps == 3) || (numcomps == 4)); if(!bIsValid) { if(numcomps) { FreeImage_OutputMessageProc(format_id, "Warning: image contains %d greyscale components. Only the first will be loaded.\n", numcomps); numcomps = 1; } else { // unknown type throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } } // create a new DIB if(image->comps[0].prec <= 8) { switch(numcomps) { case 1: dib = FreeImage_AllocateHeader(header_only, wrr, hrr, 8); break; case 3: dib = FreeImage_AllocateHeader(header_only, wrr, hrr, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); break; case 4: dib = FreeImage_AllocateHeader(header_only, wrr, hrr, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); break; } } else if(image->comps[0].prec <= 16) { switch(numcomps) { case 1: dib = FreeImage_AllocateHeaderT(header_only, FIT_UINT16, wrr, hrr); break; case 3: dib = FreeImage_AllocateHeaderT(header_only, FIT_RGB16, wrr, hrr); break; case 4: dib = FreeImage_AllocateHeaderT(header_only, FIT_RGBA16, wrr, hrr); break; } } else { throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } if(!dib) { throw FI_MSG_ERROR_DIB_MEMORY; } // "header only" FIBITMAP ? if(header_only) { return dib; } if(image->comps[0].prec <= 8) { if(numcomps == 1) { // 8-bit greyscale // ---------------------------------------------------------- // build a greyscale palette RGBQUAD *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; } // load pixel data unsigned pixel_count = 0; for(int y = 0; y < hrr; y++) { BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y); for(int x = 0; x < wrr; x++) { const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; int index = image->comps[0].data[pixel_pos]; index += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); bits[x] = (BYTE)index; pixel_count++; } } } else if(numcomps == 3) { // 24-bit RGB // ---------------------------------------------------------- // load pixel data unsigned pixel_count = 0; for(int y = 0; y < hrr; y++) { BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y); for(int x = 0; x < wrr; x++) { const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; int r = image->comps[0].data[pixel_pos]; r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); int g = image->comps[1].data[pixel_pos]; g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); int b = image->comps[2].data[pixel_pos]; b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); bits[FI_RGBA_RED] = (BYTE)r; bits[FI_RGBA_GREEN] = (BYTE)g; bits[FI_RGBA_BLUE] = (BYTE)b; bits += 3; pixel_count++; } } } else if(numcomps == 4) { // 32-bit RGBA // ---------------------------------------------------------- // load pixel data unsigned pixel_count = 0; for(int y = 0; y < hrr; y++) { BYTE *bits = FreeImage_GetScanLine(dib, hrr - 1 - y); for(int x = 0; x < wrr; x++) { const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; int r = image->comps[0].data[pixel_pos]; r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); int g = image->comps[1].data[pixel_pos]; g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); int b = image->comps[2].data[pixel_pos]; b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); int a = image->comps[3].data[pixel_pos]; a += (image->comps[3].sgnd ? 1 << (image->comps[3].prec - 1) : 0); bits[FI_RGBA_RED] = (BYTE)r; bits[FI_RGBA_GREEN] = (BYTE)g; bits[FI_RGBA_BLUE] = (BYTE)b; bits[FI_RGBA_ALPHA] = (BYTE)a; bits += 4; pixel_count++; } } } } else if(image->comps[0].prec <= 16) { if(numcomps == 1) { // 16-bit greyscale // ---------------------------------------------------------- // load pixel data unsigned pixel_count = 0; for(int y = 0; y < hrr; y++) { WORD *bits = (WORD*)FreeImage_GetScanLine(dib, hrr - 1 - y); for(int x = 0; x < wrr; x++) { const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; int index = image->comps[0].data[pixel_pos]; index += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); bits[x] = (WORD)index; pixel_count++; } } } else if(numcomps == 3) { // 48-bit RGB // ---------------------------------------------------------- // load pixel data unsigned pixel_count = 0; for(int y = 0; y < hrr; y++) { FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, hrr - 1 - y); for(int x = 0; x < wrr; x++) { const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; int r = image->comps[0].data[pixel_pos]; r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); int g = image->comps[1].data[pixel_pos]; g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); int b = image->comps[2].data[pixel_pos]; b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); bits[x].red = (WORD)r; bits[x].green = (WORD)g; bits[x].blue = (WORD)b; pixel_count++; } } } else if(numcomps == 4) { // 64-bit RGBA // ---------------------------------------------------------- // load pixel data unsigned pixel_count = 0; for(int y = 0; y < hrr; y++) { FIRGBA16 *bits = (FIRGBA16*)FreeImage_GetScanLine(dib, hrr - 1 - y); for(int x = 0; x < wrr; x++) { const unsigned pixel_pos = pixel_count / wrr * wr + pixel_count % wrr; int r = image->comps[0].data[pixel_pos]; r += (image->comps[0].sgnd ? 1 << (image->comps[0].prec - 1) : 0); int g = image->comps[1].data[pixel_pos]; g += (image->comps[1].sgnd ? 1 << (image->comps[1].prec - 1) : 0); int b = image->comps[2].data[pixel_pos]; b += (image->comps[2].sgnd ? 1 << (image->comps[2].prec - 1) : 0); int a = image->comps[3].data[pixel_pos]; a += (image->comps[3].sgnd ? 1 << (image->comps[3].prec - 1) : 0); bits[x].red = (WORD)r; bits[x].green = (WORD)g; bits[x].blue = (WORD)b; bits[x].alpha = (WORD)a; pixel_count++; } } } } return dib; } catch(const char *text) { if(dib) FreeImage_Unload(dib); FreeImage_OutputMessageProc(format_id, text); return NULL; } }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { FIBITMAP *dib = NULL; LibRaw RawProcessor; BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; try { // wrap the input datastream LibRaw_freeimage_datastream datastream(io, handle); // set decoding parameters // the following parameters affect data reading // -------------------------------------------- // (-s [0..N-1]) Select one raw image from input file RawProcessor.imgdata.params.shot_select = 0; // (-w) Use camera white balance, if possible (otherwise, fallback to auto_wb) RawProcessor.imgdata.params.use_camera_wb = 1; // (-h) outputs the image in 50% size RawProcessor.imgdata.params.half_size = ((flags & RAW_HALFSIZE) == RAW_HALFSIZE) ? 1 : 0; // open the datastream if(RawProcessor.open_datastream(&datastream) != LIBRAW_SUCCESS) { throw "LibRaw : failed to open input stream (unknown format)"; } if(header_only) { // header only mode dib = FreeImage_AllocateHeaderT(header_only, FIT_RGB16, RawProcessor.imgdata.sizes.width, RawProcessor.imgdata.sizes.height); } else if((flags & RAW_PREVIEW) == RAW_PREVIEW) { // try to get the embedded JPEG dib = libraw_LoadEmbeddedPreview(RawProcessor, 0); if(!dib) { // no JPEG preview: try to load as 8-bit/sample (i.e. RGB 24-bit) dib = libraw_LoadRawData(RawProcessor, 8); } } else if((flags & RAW_DISPLAY) == RAW_DISPLAY) { // load raw data as 8-bit/sample (i.e. RGB 24-bit) dib = libraw_LoadRawData(RawProcessor, 8); } else { // default: load raw data as linear 16-bit/sample (i.e. RGB 48-bit) dib = libraw_LoadRawData(RawProcessor, 16); } // save ICC profile if present if(dib && (NULL != RawProcessor.imgdata.color.profile)) { FreeImage_CreateICCProfile(dib, RawProcessor.imgdata.color.profile, RawProcessor.imgdata.color.profile_length); } // try to get JPEG embedded Exif metadata if(dib && !((flags & RAW_PREVIEW) == RAW_PREVIEW)) { FIBITMAP *metadata_dib = libraw_LoadEmbeddedPreview(RawProcessor, FIF_LOAD_NOPIXELS); if(metadata_dib) { FreeImage_CloneMetadata(dib, metadata_dib); FreeImage_Unload(metadata_dib); } } // clean-up internal memory allocations RawProcessor.recycle(); return dib; } catch(const char *text) { if(dib) { FreeImage_Unload(dib); } RawProcessor.recycle(); FreeImage_OutputMessageProc(s_format_id, text); } return NULL; }
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; }