bool ImageLoaderFreeImage::loadInputImage(const QString &imageFileName, QString &errorMessage) { bool result = false; FreeImageErrorMessage.clear(); const FREE_IMAGE_FORMAT fileType = FreeImage_GetFileType(imageFileName.toAscii(), 0); FIBITMAP* newImage = FreeImage_Load(fileType, imageFileName.toAscii(), TIFF_CMYK|JPEG_CMYK); // Filter out images which FreeImage can load but not convert to Rgb24 // And images which we simply don't handle if (newImage) { const FREE_IMAGE_TYPE type = FreeImage_GetImageType(newImage); if (type != FIT_BITMAP // 1pbb Monochrome, 1-8bpp Palette, 8bpp Greyscale, // 24bpp Rgb, 32bpp Argb, 32bpp Cmyk && type != FIT_RGB16 // 16bpp Greyscale, 48bpp Rgb ) { FreeImage_Unload(newImage); newImage = NULL; } } if (newImage) { result = true; disposeImage(); m_bitmap = newImage; m_widthPixels = FreeImage_GetWidth(m_bitmap); m_heightPixels = FreeImage_GetHeight(m_bitmap); m_horizontalDotsPerMeter = FreeImage_GetDotsPerMeterX(m_bitmap); m_verticalDotsPerMeter = FreeImage_GetDotsPerMeterY(m_bitmap); if (m_horizontalDotsPerMeter == 0) m_horizontalDotsPerMeter = 2835; // 72 dpi if (m_verticalDotsPerMeter == 0) m_verticalDotsPerMeter = 2835; m_imageFileName = imageFileName; if (colorDataType() == Types::ColorTypeRGB && bitsPerPixel() == 32) { // Sometimes, there are strange .PSD images like this (FreeImage bug?) RGBQUAD white = { 255, 255, 255, 0 }; FIBITMAP *Image24Bit = FreeImage_Composite(m_bitmap, FALSE, &white); FreeImage_Unload(m_bitmap); m_bitmap = Image24Bit; } } errorMessage = FreeImageErrorMessage; return result; }
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 BOOL DLL_CALLCONV Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { png_structp png_ptr; png_infop info_ptr; png_colorp palette = NULL; png_uint_32 width, height; BOOL has_alpha_channel = FALSE; RGBQUAD *pal; // pointer to dib palette int bit_depth, pixel_depth; // pixel_depth = bit_depth * channels int palette_entries; int interlace_type; fi_ioStructure fio; fio.s_handle = handle; fio.s_io = io; if ((dib) && (handle)) { try { // create the chunk manage structure png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, error_handler, warning_handler); if (!png_ptr) { return FALSE; } // allocate/initialize the image information data. info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return FALSE; } // Set error handling. REQUIRED if you aren't supplying your own // error handling functions in the png_create_write_struct() call. if (setjmp(png_jmpbuf(png_ptr))) { // if we get here, we had a problem reading the file png_destroy_write_struct(&png_ptr, &info_ptr); return FALSE; } // init the IO png_set_write_fn(png_ptr, &fio, _WriteProc, _FlushProc); // set physical resolution png_uint_32 res_x = (png_uint_32)FreeImage_GetDotsPerMeterX(dib); png_uint_32 res_y = (png_uint_32)FreeImage_GetDotsPerMeterY(dib); if ((res_x > 0) && (res_y > 0)) { png_set_pHYs(png_ptr, info_ptr, res_x, res_y, PNG_RESOLUTION_METER); } // Set the image information here. Width and height are up to 2^31, // bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on // the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, // PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, // or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or // PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST // currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED width = FreeImage_GetWidth(dib); height = FreeImage_GetHeight(dib); pixel_depth = FreeImage_GetBPP(dib); BOOL bInterlaced = FALSE; if( (flags & PNG_INTERLACED) == PNG_INTERLACED) { interlace_type = PNG_INTERLACE_ADAM7; bInterlaced = TRUE; } else { interlace_type = PNG_INTERLACE_NONE; } // set the ZLIB compression level or default to PNG default compression level (ZLIB level = 6) int zlib_level = flags & 0x0F; if((zlib_level >= 1) && (zlib_level <= 9)) { png_set_compression_level(png_ptr, zlib_level); } else if((flags & PNG_Z_NO_COMPRESSION) == PNG_Z_NO_COMPRESSION) { png_set_compression_level(png_ptr, Z_NO_COMPRESSION); } // filtered strategy works better for high color images if(pixel_depth >= 16){ png_set_compression_strategy(png_ptr, Z_FILTERED); png_set_filter(png_ptr, 0, PNG_FILTER_NONE|PNG_FILTER_SUB|PNG_FILTER_PAETH); } else { png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); } FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); if(image_type == FIT_BITMAP) { // standard image type bit_depth = (pixel_depth > 8) ? 8 : pixel_depth; } else { // 16-bit greyscale or 16-bit RGB(A) bit_depth = 16; } switch (FreeImage_GetColorType(dib)) { case FIC_MINISWHITE: // Invert monochrome files to have 0 as black and 1 as white (no break here) png_set_invert_mono(png_ptr); case FIC_MINISBLACK: png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_GRAY, interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); break; case FIC_PALETTE: { png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_PALETTE, interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // set the palette palette_entries = 1 << bit_depth; palette = (png_colorp)png_malloc(png_ptr, palette_entries * sizeof (png_color)); pal = FreeImage_GetPalette(dib); for (int i = 0; i < palette_entries; i++) { palette[i].red = pal[i].rgbRed; palette[i].green = pal[i].rgbGreen; palette[i].blue = pal[i].rgbBlue; } png_set_PLTE(png_ptr, info_ptr, palette, palette_entries); // You must not free palette here, because png_set_PLTE only makes a link to // the palette that you malloced. Wait until you are about to destroy // the png structure. break; } case FIC_RGBALPHA : has_alpha_channel = TRUE; png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_RGBA, interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // flip BGR pixels to RGB if(image_type == FIT_BITMAP) { png_set_bgr(png_ptr); } #endif break; case FIC_RGB: png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_RGB, interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // flip BGR pixels to RGB if(image_type == FIT_BITMAP) { png_set_bgr(png_ptr); } #endif break; case FIC_CMYK: break; } // write possible ICC profile FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib); if (iccProfile->size && iccProfile->data) { png_set_iCCP(png_ptr, info_ptr, "Embedded Profile", 0, (png_const_bytep)iccProfile->data, iccProfile->size); } // write metadata WriteMetadata(png_ptr, info_ptr, dib); // Optional gamma chunk is strongly suggested if you have any guess // as to the correct gamma of the image. // png_set_gAMA(png_ptr, info_ptr, gamma); // set the transparency table if (FreeImage_IsTransparent(dib) && (FreeImage_GetTransparencyCount(dib) > 0)) { png_set_tRNS(png_ptr, info_ptr, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib), NULL); } // set the background color if(FreeImage_HasBackgroundColor(dib)) { png_color_16 image_background; RGBQUAD rgbBkColor; FreeImage_GetBackgroundColor(dib, &rgbBkColor); memset(&image_background, 0, sizeof(png_color_16)); image_background.blue = rgbBkColor.rgbBlue; image_background.green = rgbBkColor.rgbGreen; image_background.red = rgbBkColor.rgbRed; image_background.index = rgbBkColor.rgbReserved; png_set_bKGD(png_ptr, info_ptr, &image_background); } // Write the file header information. png_write_info(png_ptr, info_ptr); // write out the image data #ifndef FREEIMAGE_BIGENDIAN if (bit_depth == 16) { // turn on 16 bit byte swapping png_set_swap(png_ptr); } #endif int number_passes = 1; if (bInterlaced) { number_passes = png_set_interlace_handling(png_ptr); } if ((pixel_depth == 32) && (!has_alpha_channel)) { BYTE *buffer = (BYTE *)malloc(width * 3); // transparent conversion to 24-bit // the number of passes is either 1 for non-interlaced images, or 7 for interlaced images for (int pass = 0; pass < number_passes; pass++) { for (png_uint_32 k = 0; k < height; k++) { FreeImage_ConvertLine32To24(buffer, FreeImage_GetScanLine(dib, height - k - 1), width); png_write_row(png_ptr, buffer); } } free(buffer); } else { // the number of passes is either 1 for non-interlaced images, or 7 for interlaced images for (int pass = 0; pass < number_passes; pass++) { for (png_uint_32 k = 0; k < height; k++) { png_write_row(png_ptr, FreeImage_GetScanLine(dib, height - k - 1)); } } } // It is REQUIRED to call this to finish writing the rest of the file // Bug with png_flush png_write_end(png_ptr, info_ptr); // clean up after the write, and free any memory allocated if (palette) { png_free(png_ptr, palette); } png_destroy_write_struct(&png_ptr, &info_ptr); return TRUE; } catch (const char *text) { FreeImage_OutputMessageProc(s_format_id, text); } } return FALSE; }
double fipImage::getHorizontalResolution() const { return (FreeImage_GetDotsPerMeterX(_dib) / (double)100); }
static BOOL DLL_CALLCONV Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { if ((dib) && (handle)) { try { // Check dib format const char *sError = "only 24-bit highcolor or 8-bit greyscale/palette bitmaps can be saved as JPEG"; FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib); WORD bpp = (WORD)FreeImage_GetBPP(dib); if ((bpp != 24) && (bpp != 8)) throw sError; if(bpp == 8) { // allow grey, reverse grey and palette if ((color_type != FIC_MINISBLACK) && (color_type != FIC_MINISWHITE) && (color_type != FIC_PALETTE)) throw sError; } struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; // Step 1: allocate and initialize JPEG compression object cinfo.err = jpeg_std_error(&jerr); jerr.error_exit = jpeg_error_exit; jerr.output_message = jpeg_output_message; // Now we can initialize the JPEG compression object jpeg_create_compress(&cinfo); // Step 2: specify data destination (eg, a file) jpeg_freeimage_dst(&cinfo, handle, io); // Step 3: set parameters for compression cinfo.image_width = FreeImage_GetWidth(dib); cinfo.image_height = FreeImage_GetHeight(dib); switch(color_type) { case FIC_MINISBLACK : case FIC_MINISWHITE : cinfo.in_color_space = JCS_GRAYSCALE; cinfo.input_components = 1; break; default : cinfo.in_color_space = JCS_RGB; cinfo.input_components = 3; break; } jpeg_set_defaults(&cinfo); // progressive-JPEG support if((flags & JPEG_PROGRESSIVE) == JPEG_PROGRESSIVE) { jpeg_simple_progression(&cinfo); } // Set JFIF density parameters from the DIB data cinfo.X_density = (UINT16) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterX(dib)); cinfo.Y_density = (UINT16) (0.5 + 0.0254 * FreeImage_GetDotsPerMeterY(dib)); cinfo.density_unit = 1; // dots / inch // set subsampling options if required if(cinfo.in_color_space == JCS_RGB) { if((flags & JPEG_SUBSAMPLING_411) == JPEG_SUBSAMPLING_411) { // 4:1:1 (4x1 1x1 1x1) - CrH 25% - CbH 25% - CrV 100% - CbV 100% // the horizontal color resolution is quartered cinfo.comp_info[0].h_samp_factor = 4; // Y cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; // Cb cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; // Cr cinfo.comp_info[2].v_samp_factor = 1; } else if((flags & JPEG_SUBSAMPLING_420) == JPEG_SUBSAMPLING_420) { // 4:2:0 (2x2 1x1 1x1) - CrH 50% - CbH 50% - CrV 50% - CbV 50% // the chrominance resolution in both the horizontal and vertical directions is cut in half cinfo.comp_info[0].h_samp_factor = 2; // Y cinfo.comp_info[0].v_samp_factor = 2; cinfo.comp_info[1].h_samp_factor = 1; // Cb cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; // Cr cinfo.comp_info[2].v_samp_factor = 1; } else if((flags & JPEG_SUBSAMPLING_422) == JPEG_SUBSAMPLING_422){ //2x1 (low) // 4:2:2 (2x1 1x1 1x1) - CrH 50% - CbH 50% - CrV 100% - CbV 100% // half of the horizontal resolution in the chrominance is dropped (Cb & Cr), // while the full resolution is retained in the vertical direction, with respect to the luminance cinfo.comp_info[0].h_samp_factor = 2; // Y cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; // Cb cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; // Cr cinfo.comp_info[2].v_samp_factor = 1; } else if((flags & JPEG_SUBSAMPLING_444) == JPEG_SUBSAMPLING_444){ //1x1 (no subsampling) // 4:4:4 (1x1 1x1 1x1) - CrH 100% - CbH 100% - CrV 100% - CbV 100% // the resolution of chrominance information (Cb & Cr) is preserved // at the same rate as the luminance (Y) information cinfo.comp_info[0].h_samp_factor = 1; // Y cinfo.comp_info[0].v_samp_factor = 1; cinfo.comp_info[1].h_samp_factor = 1; // Cb cinfo.comp_info[1].v_samp_factor = 1; cinfo.comp_info[2].h_samp_factor = 1; // Cr cinfo.comp_info[2].v_samp_factor = 1; } } // Step 4: set quality // the first 7 bits are reserved for low level quality settings // the other bits are high level (i.e. enum-ish) int quality; if ((flags & JPEG_QUALITYBAD) == JPEG_QUALITYBAD) { quality = 10; } else if ((flags & JPEG_QUALITYAVERAGE) == JPEG_QUALITYAVERAGE) { quality = 25; } else if ((flags & JPEG_QUALITYNORMAL) == JPEG_QUALITYNORMAL) { quality = 50; } else if ((flags & JPEG_QUALITYGOOD) == JPEG_QUALITYGOOD) { quality = 75; } else if ((flags & JPEG_QUALITYSUPERB) == JPEG_QUALITYSUPERB) { quality = 100; } else { if ((flags & 0x7F) == 0) { quality = 75; } else { quality = flags & 0x7F; } } jpeg_set_quality(&cinfo, quality, TRUE); /* limit to baseline-JPEG values */ // Step 5: Start compressor jpeg_start_compress(&cinfo, TRUE); // Step 6: Write special markers write_markers(&cinfo, dib); // Step 7: while (scan lines remain to be written) if(color_type == FIC_RGB) { // 24-bit RGB image : need to swap red and blue channels unsigned pitch = FreeImage_GetPitch(dib); BYTE *target = (BYTE*)malloc(pitch * sizeof(BYTE)); if (target == NULL) { throw FI_MSG_ERROR_MEMORY; } while (cinfo.next_scanline < cinfo.image_height) { // get a copy of the scanline memcpy(target, FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1), pitch); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // swap R and B channels BYTE *target_p = target; for(unsigned x = 0; x < cinfo.image_width; x++) { INPLACESWAP(target_p[0], target_p[2]); target_p += 3; } #endif // write the scanline jpeg_write_scanlines(&cinfo, &target, 1); } free(target); } else if(color_type == FIC_MINISBLACK) { // 8-bit standard greyscale images while (cinfo.next_scanline < cinfo.image_height) { JSAMPROW b = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1); jpeg_write_scanlines(&cinfo, &b, 1); } } else if(color_type == FIC_PALETTE) { // 8-bit palettized images are converted to 24-bit images RGBQUAD *palette = FreeImage_GetPalette(dib); BYTE *target = (BYTE*)malloc(cinfo.image_width * 3); if (target == NULL) { throw FI_MSG_ERROR_MEMORY; } while (cinfo.next_scanline < cinfo.image_height) { BYTE *source = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1); FreeImage_ConvertLine8To24(target, source, cinfo.image_width, palette); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // swap R and B channels BYTE *target_p = target; for(unsigned x = 0; x < cinfo.image_width; x++) { INPLACESWAP(target_p[0], target_p[2]); target_p += 3; } #endif jpeg_write_scanlines(&cinfo, &target, 1); } free(target); } else if(color_type == FIC_MINISWHITE) { // reverse 8-bit greyscale image, so reverse grey value on the fly unsigned i; BYTE reverse[256]; BYTE *target = (BYTE *)malloc(cinfo.image_width); if (target == NULL) { throw FI_MSG_ERROR_MEMORY; } for(i = 0; i < 256; i++) { reverse[i] = (BYTE)(255 - i); } while(cinfo.next_scanline < cinfo.image_height) { BYTE *source = FreeImage_GetScanLine(dib, FreeImage_GetHeight(dib) - cinfo.next_scanline - 1); for(i = 0; i < cinfo.image_width; i++) { target[i] = reverse[ source[i] ]; } jpeg_write_scanlines(&cinfo, &target, 1); } free(target); } // Step 8: Finish compression jpeg_finish_compress(&cinfo); // Step 9: release JPEG compression object jpeg_destroy_compress(&cinfo); return TRUE; } catch (const char *text) { FreeImage_OutputMessageProc(s_format_id, text); return FALSE; } catch (FREE_IMAGE_FORMAT) { return FALSE; } } return FALSE; }
static BOOL DLL_CALLCONV Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { BOOL bIsFlipped = FALSE; // FreeImage DIB are upside-down relative to usual graphic conventions PKPixelFormatGUID guid_format; // image format PKPixelInfo pixelInfo; // image specifications BOOL bHasAlpha = FALSE; // is alpha layer present ? PKImageEncode *pEncoder = NULL; // encoder interface ERR error_code = 0; // error code as returned by the interface // get the I/O stream wrapper WMPStream *pEncodeStream = (WMPStream*)data; if(!dib || !handle || !pEncodeStream) { return FALSE; } try { // get image dimensions unsigned width = FreeImage_GetWidth(dib); unsigned height = FreeImage_GetHeight(dib); // check JPEG-XR limits if((width < MB_WIDTH_PIXEL) || (height < MB_HEIGHT_PIXEL)) { FreeImage_OutputMessageProc(s_format_id, "Unsupported image size: width x height = %d x %d", width, height); throw (const char*)NULL; } // get output pixel format error_code = GetOutputPixelFormat(dib, &guid_format, &bHasAlpha); JXR_CHECK(error_code); pixelInfo.pGUIDPixFmt = &guid_format; error_code = PixelFormatLookup(&pixelInfo, LOOKUP_FORWARD); JXR_CHECK(error_code); // create a JXR encoder interface and initialize function pointers with *_WMP functions error_code = PKImageEncode_Create_WMP(&pEncoder); JXR_CHECK(error_code); // attach the stream to the encoder and set all encoder parameters to zero ... error_code = pEncoder->Initialize(pEncoder, pEncodeStream, &pEncoder->WMP.wmiSCP, sizeof(CWMIStrCodecParam)); JXR_CHECK(error_code); // ... then configure the encoder SetEncoderParameters(&pEncoder->WMP.wmiSCP, &pixelInfo, flags, bHasAlpha); // set pixel format pEncoder->SetPixelFormat(pEncoder, guid_format); // set image size pEncoder->SetSize(pEncoder, width, height); // set resolution (convert from universal units to English units) float resX = (float)(unsigned)(0.5F + 0.0254F * FreeImage_GetDotsPerMeterX(dib)); float resY = (float)(unsigned)(0.5F + 0.0254F * FreeImage_GetDotsPerMeterY(dib)); pEncoder->SetResolution(pEncoder, resX, resY); // set metadata WriteMetadata(pEncoder, dib); // write metadata & pixels // ----------------------- // dib coordinates are upside-down relative to usual conventions bIsFlipped = FreeImage_FlipVertical(dib); // get a pointer to dst pixel data BYTE *dib_bits = FreeImage_GetBits(dib); // get dst pitch (count of BYTE for stride) const unsigned cbStride = FreeImage_GetPitch(dib); // write metadata + pixels on output error_code = pEncoder->WritePixels(pEncoder, height, dib_bits, cbStride); JXR_CHECK(error_code); // recover dib coordinates FreeImage_FlipVertical(dib); // free the encoder pEncoder->Release(&pEncoder); assert(pEncoder == NULL); return TRUE; } catch (const char *message) { if(bIsFlipped) { // recover dib coordinates FreeImage_FlipVertical(dib); } if(pEncoder) { // free the encoder pEncoder->Release(&pEncoder); assert(pEncoder == NULL); } if(NULL != message) { FreeImage_OutputMessageProc(s_format_id, message); } } return FALSE; }