/** @brief Fills a FIT_BITMAP image with the specified color. This function does the dirty work for FreeImage_FillBackground for FIT_BITMAP images. @param dib The image to be filled. @param color The color, the specified image should be filled with. @param options Options that affect the color search process for palletized images. @return Returns TRUE on success, FALSE otherwise. This function fails if any of the dib and color is NULL or the provided image is not a FIT_BITMAP image. */ static BOOL FillBackgroundBitmap(FIBITMAP *dib, const RGBQUAD *color, int options) { if ((!dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) { return FALSE;; } if (!color) { return FALSE; } const RGBQUAD *color_intl = color; unsigned bpp = FreeImage_GetBPP(dib); unsigned width = FreeImage_GetWidth(dib); unsigned height = FreeImage_GetHeight(dib); FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib); // get a pointer to the first scanline (bottom line) BYTE *src_bits = FreeImage_GetScanLine(dib, 0); BYTE *dst_bits = src_bits; BOOL supports_alpha = ((bpp >= 24) || ((bpp == 8) && (color_type != FIC_PALETTE))); // Check for RGBA case if bitmap supports alpha // blending (8-bit greyscale, 24- or 32-bit images) if (supports_alpha && (options & FI_COLOR_IS_RGBA_COLOR)) { if (color->rgbReserved == 0) { // the fill color is fully transparent; we are done return TRUE; } // Only if the fill color is NOT fully opaque, draw it with // the (much) slower FreeImage_DrawLine function and return. // Since we do not have the FreeImage_DrawLine function in this // release, just assume to have an unicolor background and fill // all with an 'alpha-blended' color. if (color->rgbReserved < 255) { // If we will draw on an unicolor background, it's // faster to draw opaque with an alpha blended color. // So, first get the color from the first pixel in the // image (bottom-left pixel). RGBQUAD bgcolor; if (bpp == 8) { bgcolor = FreeImage_GetPalette(dib)[*src_bits]; } else { bgcolor.rgbBlue = src_bits[FI_RGBA_BLUE]; bgcolor.rgbGreen = src_bits[FI_RGBA_GREEN]; bgcolor.rgbRed = src_bits[FI_RGBA_RED]; bgcolor.rgbReserved = 0xFF; } RGBQUAD blend; GetAlphaBlendedColor(&bgcolor, color_intl, &blend); color_intl = &blend; } } int index = (bpp <= 8) ? GetPaletteIndex(dib, color_intl, options, &color_type) : 0; if (index == -1) { // No palette index found for a palletized // image. This should never happen... return FALSE; } // first, build the first scanline (line 0) switch (bpp) { case 1: { unsigned bytes = (width / 8); memset(dst_bits, ((index == 1) ? 0xFF : 0x00), bytes); //int n = width % 8; int n = width & 7; if (n) { if (index == 1) { // set n leftmost bits dst_bits[bytes] |= (0xFF << (8 - n)); } else { // clear n leftmost bits dst_bits[bytes] &= (0xFF >> n); } } break; } case 4: { unsigned bytes = (width / 2); memset(dst_bits, (index | (index << 4)), bytes); //if (bytes % 2) { if (bytes & 1) { dst_bits[bytes] &= 0x0F; dst_bits[bytes] |= (index << 4); } break; } case 8: { memset(dst_bits, index, FreeImage_GetLine(dib)); break; } case 16: { WORD wcolor = RGBQUAD_TO_WORD(dib, color_intl); for (unsigned x = 0; x < width; x++) { ((WORD *)dst_bits)[x] = wcolor; } break; } case 24: { RGBTRIPLE rgbt = *((RGBTRIPLE *)color_intl); for (unsigned x = 0; x < width; x++) { ((RGBTRIPLE *)dst_bits)[x] = rgbt; } break; } case 32: { RGBQUAD rgbq; rgbq.rgbBlue = ((RGBTRIPLE *)color_intl)->rgbtBlue; rgbq.rgbGreen = ((RGBTRIPLE *)color_intl)->rgbtGreen; rgbq.rgbRed = ((RGBTRIPLE *)color_intl)->rgbtRed; rgbq.rgbReserved = 0xFF; for (unsigned x = 0; x < width; x++) { ((RGBQUAD *)dst_bits)[x] = rgbq; } break; } default: return FALSE; } // Then, copy the first scanline into all following scanlines. // 'src_bits' is a pointer to the first scanline and is already // set up correctly. if (src_bits) { unsigned pitch = FreeImage_GetPitch(dib); unsigned bytes = FreeImage_GetLine(dib); dst_bits = src_bits + pitch; for (unsigned y = 1; y < height; y++) { memcpy(dst_bits, src_bits, bytes); dst_bits += pitch; } } return TRUE; }
/** Tone mapping operator @param dib Input / Output RGBF image @param Y Input luminance image version of dib @param f Overall intensity in range [-8:8] : default to 0 @param m Contrast in range [0.3:1) : default to 0 @param a Adaptation in range [0:1] : default to 1 @param c Color correction in range [0:1] : default to 0 @return Returns TRUE if successful, returns FALSE otherwise @see LuminanceFromY */ static BOOL ToneMappingReinhard05(FIBITMAP *dib, FIBITMAP *Y, float f, float m, float a, float c) { float Cav[3]; // channel average float Lav = 0; // average luminance float Llav = 0; // log average luminance float minLum = 1; // min luminance float maxLum = 1; // max luminance float L; // pixel luminance float I_g, I_l; // global and local light adaptation float I_a; // interpolated pixel light adaptation float k; // key (low-key means overall dark image, high-key means overall light image) // check input parameters if((FreeImage_GetImageType(dib) != FIT_RGBF) || (FreeImage_GetImageType(Y) != FIT_FLOAT)) { return FALSE; } if(f < -8) f = -8; if(f > 8) f = 8; if(m < 0) m = 0; if(m > 1) m = 1; if(a < 0) a = 0; if(a > 1) a = 1; if(c < 0) c = 0; if(c > 1) c = 1; const unsigned width = FreeImage_GetWidth(dib); const unsigned height = FreeImage_GetHeight(dib); const unsigned dib_pitch = FreeImage_GetPitch(dib); const unsigned y_pitch = FreeImage_GetPitch(Y); int i; unsigned x, y; BYTE *bits = NULL, *Ybits = NULL; // get statistics about the data (but only if its really needed) f = exp(-f); if((m == 0) || (a != 1) && (c != 1)) { // avoid these calculations if its not needed after ... LuminanceFromY(Y, &maxLum, &minLum, &Lav, &Llav); k = (log(maxLum) - Llav) / (log(maxLum) - log(minLum)); if(k < 0) { // pow(k, 1.4F) is undefined ... // there's an ambiguity about the calculation of Llav between Reinhard papers and the various implementations ... // try another world adaptation luminance formula using instead 'worldLum = log(Llav)' k = (log(maxLum) - log(Llav)) / (log(maxLum) - log(minLum)); if(k < 0) m = 0.3F; } } m = (m > 0) ? m : (float)(0.3 + 0.7 * pow(k, 1.4F)); float max_color = -1e6F; float min_color = +1e6F; // tone map image bits = (BYTE*)FreeImage_GetBits(dib); Ybits = (BYTE*)FreeImage_GetBits(Y); if((a == 1) && (c == 0)) { // when using default values, use a fastest code for(y = 0; y < height; y++) { float *Y = (float*)Ybits; float *color = (float*)bits; for(x = 0; x < width; x++) { I_a = Y[x]; // luminance(x, y) for (i = 0; i < 3; i++) { *color /= ( *color + pow(f * I_a, m) ); max_color = (*color > max_color) ? *color : max_color; min_color = (*color < min_color) ? *color : min_color; color++; } } // next line bits += dib_pitch; Ybits += y_pitch; } } else { // complete algorithm // channel averages Cav[0] = Cav[1] = Cav[2] = 0; if((a != 1) && (c != 0)) { // channel averages are not needed when (a == 1) or (c == 0) bits = (BYTE*)FreeImage_GetBits(dib); for(y = 0; y < height; y++) { float *color = (float*)bits; for(x = 0; x < width; x++) { for(i = 0; i < 3; i++) { Cav[i] += *color; color++; } } // next line bits += dib_pitch; } const float image_size = (float)width * height; for(i = 0; i < 3; i++) { Cav[i] /= image_size; } } // perform tone mapping bits = (BYTE*)FreeImage_GetBits(dib); for(y = 0; y < height; y++) { const float *Y = (float*)Ybits; float *color = (float*)bits; for(x = 0; x < width; x++) { L = Y[x]; // luminance(x, y) for (i = 0; i < 3; i++) { I_l = c * *color + (1-c) * L; I_g = c * Cav[i] + (1-c) * Lav; I_a = a * I_l + (1-a) * I_g; *color /= ( *color + pow(f * I_a, m) ); max_color = (*color > max_color) ? *color : max_color; min_color = (*color < min_color) ? *color : min_color; color++; } } // next line bits += dib_pitch; Ybits += y_pitch; } } // normalize intensities if(max_color != min_color) { bits = (BYTE*)FreeImage_GetBits(dib); const float range = max_color - min_color; for(y = 0; y < height; y++) { float *color = (float*)bits; for(x = 0; x < width; x++) { for(i = 0; i < 3; i++) { *color = (*color - min_color) / range; color++; } } // next line bits += dib_pitch; } } return TRUE; }
bool StFreeImage::loadExtra(const StString& theFilePath, ImageType theImageType, uint8_t* theDataPtr, int theDataSize, bool theIsOnlyRGB) { (void )theIsOnlyRGB; if(!StFreeImage::init()) { setState("FreeImage library is not initialized"); return false; } // reset current data StImage::nullify(); setState(); close(); FREE_IMAGE_FORMAT aFIF = convertToFIF(theImageType); if(theDataPtr != NULL && theDataSize != 0 && aFIF != FIF_UNKNOWN) { FIMEMORY* aFIMemory = FreeImage_OpenMemory(theDataPtr, theDataSize); if(aFIMemory == NULL) { setState("FreeImage library, internal error"); return false; } myDIB = FreeImage_LoadFromMemory(aFIF, aFIMemory, 0); FreeImage_CloseMemory(aFIMemory); } else { // check the file signature and deduce its format #if defined(_WIN32) StStringUtfWide aFilePathWide = theFilePath.toUtfWide(); aFIF = FreeImage_GetFileType(aFilePathWide.toCString(), 0); #else aFIF = FreeImage_GetFileType(theFilePath.toCString(), 0); #endif if(aFIF == FIF_UNKNOWN) { // no signature? try to guess the file format from the file extension #if defined(_WIN32) aFIF = FreeImage_GetFIFFromFilename(aFilePathWide.toCString()); #else aFIF = FreeImage_GetFIFFromFilename(theFilePath.toCString()); #endif } if((aFIF == FIF_UNKNOWN) || !FreeImage_FIFSupportsReading(aFIF)) { setState("FreeImage library does not support image format"); return false; } int loadFlags = 0; if(aFIF == FIF_GIF) { // GIF_PLAYBACK - 'Play' the GIF to generate each frame (as 32bpp) instead of returning raw frame data when loading loadFlags = 2; } else if(aFIF == FIF_ICO) { // ICO_MAKEALPHA - convert to 32bpp and create an alpha channel from the AND-mask when loading loadFlags = 1; } #if defined(_WIN32) myDIB = FreeImage_Load(aFIF, aFilePathWide.toCString(), loadFlags); #else myDIB = FreeImage_Load(aFIF, theFilePath.toCString(), loadFlags); #endif } if(myDIB == NULL) { setState("FreeImage library, loading file failed"); return false; } StImagePlane::ImgFormat stImgFormat = convertFromFreeFormat(FreeImage_GetImageType(myDIB), FreeImage_GetColorType(myDIB), FreeImage_GetBPP(myDIB)); if(stImgFormat == StImagePlane::ImgUNKNOWN) { setState(StString("StFreeImage, image format ") + FreeImage_GetImageType(myDIB) + ", " + FreeImage_GetColorType(myDIB) + " doesn't supported by application"); close(); return false; } setColorModelPacked(stImgFormat); changePlane(0).initWrapper(stImgFormat, FreeImage_GetBits(myDIB), FreeImage_GetWidth(myDIB), FreeImage_GetHeight(myDIB), FreeImage_GetPitch(myDIB)); // FreeImage data always bottom-up... changePlane(0).setTopDown(false); // set debug information StString aDummy, aFileName; StFileNode::getFolderAndFile(theFilePath, aDummy, aFileName); setState(StString("FreeImage library, loaded image '") + aFileName + "' " + getDescription()); // we should not close the file because we create a wrapper over FreeImage native object return true; }
bool convert(const std::string &filename) { std::string file = removePath(filename); std::string outFile = getPrefix(filename, '.') + ".cfrt"; /* Retrieve file format */ FREE_IMAGE_FORMAT fif = FreeImage_GetFileType(filename.c_str(), 0); if (fif == FIF_UNKNOWN) fif = FreeImage_GetFIFFromFilename(filename.c_str()); if (fif == FIF_UNKNOWN) { std::cout << "Unknown file format " << file << std::endl; return false; } /* Load image */ FIBITMAP *dib = nullptr; if (FreeImage_FIFSupportsReading(fif)) dib = FreeImage_Load(fif, filename.c_str()); if (!dib) { std::cout << "Failed to load " << file << std::endl; return false; } /* Convert to standard format if necessary */ if (FreeImage_GetImageType(dib) != FIT_BITMAP) { FIBITMAP *tmp = dib; dib = FreeImage_ConvertToStandardType(dib); FreeImage_Unload(tmp); if (!dib) { std::cout << "Failed to convert " << file << " to standard type." << std::endl; return false; } } /* Convert bpp if needed */ unsigned int bpp = FreeImage_GetBPP(dib); if (bpp <= 8) { FIBITMAP *tmp = dib; dib = FreeImage_ConvertToGreyscale(dib); FreeImage_Unload(tmp); bpp = 8; if (!dib || FreeImage_GetBPP(dib) != 8) bpp = 0; } else if (bpp > 32) { FIBITMAP *tmp = dib; dib = FreeImage_ConvertTo32Bits(dib); FreeImage_Unload(tmp); bpp = 32; if (!dib || FreeImage_GetBPP(dib) != 32) bpp = 0; } /* Get image information */ unsigned int width = FreeImage_GetWidth(dib); unsigned int height = FreeImage_GetHeight(dib); unsigned int bytes = 1; unsigned int channels = 0; switch (bpp) { case 8: channels = 1; break; case 24: channels = 3; break; case 32: channels = 4; break; default: bpp = 0; } /* Check if image is valid */ if (!dib || bpp == 0 || width == 0 || height == 0 || !FreeImage_HasPixels(dib)) { if (dib) FreeImage_Unload(dib); std::cout << "Invalid image " << file << std::endl; return false; } std::cout << file << " Loaded. Converting.\n"; /* Create CFR texture */ CFR::Texture texture( width, height, 1, channels, bytes ); /* Set texture pixels */ for (unsigned int y = 0; y < height; y++) { BYTE* bits = FreeImage_GetScanLine(dib, height - y - 1); for (unsigned int x = 0; x < width; x++) { CFR::Pixel8 pixel(0, 0, 0, 0); BYTE *p = bits + (channels * x); if (channels >= 1) pixel.r = p[FI_RGBA_RED]; if (channels >= 2) pixel.g = p[FI_RGBA_GREEN]; if (channels >= 3) pixel.b = p[FI_RGBA_BLUE]; if (channels >= 4) pixel.a = p[FI_RGBA_ALPHA]; texture.setPixel8(pixel, x, y, 0); } } /* Unload image */ FreeImage_Unload(dib); /* Save texture */ try { texture.saveToFile(outFile); } catch (CFR::Exception &fail) { std::cout << "Failed to save " << removePath(outFile) << ": " << fail.what() << std::endl; } return true; }
/** Encode a FIBITMAP to a WebP image @param hmem Memory output stream, containing on return the encoded image @param dib The FIBITMAP to encode @param flags FreeImage save flags @return Returns TRUE if successfull, returns FALSE otherwise */ static BOOL EncodeImage(FIMEMORY *hmem, FIBITMAP *dib, int flags) { WebPPicture picture; // Input buffer WebPConfig config; // Coding parameters BOOL bIsFlipped = FALSE; try { const unsigned width = FreeImage_GetWidth(dib); const unsigned height = FreeImage_GetHeight(dib); const unsigned bpp = FreeImage_GetBPP(dib); const unsigned pitch = FreeImage_GetPitch(dib); // check image type FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); if( !((image_type == FIT_BITMAP) && ((bpp == 24) || (bpp == 32))) ) { throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } // check format limits if(MAX(width, height) > WEBP_MAX_DIMENSION) { FreeImage_OutputMessageProc(s_format_id, "Unsupported image size: width x height = %d x %d", width, height); return FALSE; } // Initialize output I/O if(WebPPictureInit(&picture) == 1) { picture.writer = WebP_MemoryWriter; picture.custom_ptr = hmem; picture.width = (int)width; picture.height = (int)height; } else { throw "Couldn't initialize WebPPicture"; } // --- Set encoding parameters --- // Initialize encoding parameters to default values WebPConfigInit(&config); // quality/speed trade-off (0=fast, 6=slower-better) config.method = 6; if((flags & WEBP_LOSSLESS) == WEBP_LOSSLESS) { // lossless encoding config.lossless = 1; picture.use_argb = 1; } else if((flags & 0x7F) > 0) { // lossy encoding config.lossless = 0; // quality is between 1 (smallest file) and 100 (biggest) - default to 75 config.quality = (float)(flags & 0x7F); if(config.quality > 100) { config.quality = 100; } } // validate encoding parameters if(WebPValidateConfig(&config) == 0) { throw "Failed to initialize encoder"; } // --- Perform encoding --- // Invert dib scanlines bIsFlipped = FreeImage_FlipVertical(dib); // convert dib buffer to output stream const BYTE *bits = FreeImage_GetBits(dib); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR switch(bpp) { case 24: WebPPictureImportBGR(&picture, bits, pitch); break; case 32: WebPPictureImportBGRA(&picture, bits, pitch); break; } #else switch(bpp) { case 24: WebPPictureImportRGB(&picture, bits, pitch); break; case 32: WebPPictureImportRGBA(&picture, bits, pitch); break; } #endif // FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR if(!WebPEncode(&config, &picture)) { throw "Failed to encode image"; } WebPPictureFree(&picture); if(bIsFlipped) { // invert dib scanlines FreeImage_FlipVertical(dib); } return TRUE; } catch (const char* text) { WebPPictureFree(&picture); if(bIsFlipped) { // invert dib scanlines FreeImage_FlipVertical(dib); } if(NULL != text) { FreeImage_OutputMessageProc(s_format_id, text); } } return FALSE; }
FREE_IMAGE_COLOR_TYPE DLL_CALLCONV FreeImage_GetColorType(FIBITMAP *dib) { RGBQUAD *rgb; const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); // special bitmap type if(image_type != FIT_BITMAP) { switch(image_type) { case FIT_RGB16: case FIT_RGBF: return FIC_RGB; case FIT_RGBA16: case FIT_RGBAF: return FIC_RGBALPHA; } return FIC_MINISBLACK; } // standard image type switch (FreeImage_GetBPP(dib)) { case 1: { rgb = FreeImage_GetPalette(dib); if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) && (rgb->rgbBlue == 0)) { rgb++; if ((rgb->rgbRed == 255) && (rgb->rgbGreen == 255) && (rgb->rgbBlue == 255)) return FIC_MINISBLACK; } if ((rgb->rgbRed == 255) && (rgb->rgbGreen == 255) && (rgb->rgbBlue == 255)) { rgb++; if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) && (rgb->rgbBlue == 0)) return FIC_MINISWHITE; } return FIC_PALETTE; } case 4: case 8: // Check if the DIB has a color or a greyscale palette { int ncolors = FreeImage_GetColorsUsed(dib); int minisblack = 1; rgb = FreeImage_GetPalette(dib); for (int i = 0; i < ncolors; i++) { if ((rgb->rgbRed != rgb->rgbGreen) || (rgb->rgbRed != rgb->rgbBlue)) return FIC_PALETTE; // The DIB has a color palette if the greyscale isn't a linear ramp // Take care of reversed grey images if (rgb->rgbRed != i) { if ((ncolors-i-1) != rgb->rgbRed) return FIC_PALETTE; else minisblack = 0; } rgb++; } return minisblack ? FIC_MINISBLACK : FIC_MINISWHITE; } case 16: case 24: return FIC_RGB; case 32: { if (FreeImage_GetICCProfile(dib)->flags & FIICC_COLOR_IS_CMYK) return FIC_CMYK; for (unsigned y = 0; y < FreeImage_GetHeight(dib); y++) { rgb = (RGBQUAD *)FreeImage_GetScanLine(dib, y); for (unsigned x = 0; x < FreeImage_GetWidth(dib); x++) if (rgb[x].rgbReserved != 0xFF) return FIC_RGBALPHA; } return FIC_RGB; } default : return FIC_MINISBLACK; } }
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); // 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 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, 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)); return new_dib; } return NULL; }
/** Write a FIBITMAP to a JNG stream @param format_id ID of the caller @param io Stream i/o functions @param dib Image to be saved @param handle Stream handle @param flags Saving flags @return Returns TRUE if successful, returns FALSE otherwise */ BOOL mng_WriteJNG(int format_id, FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int flags) { DWORD jng_width = 0; DWORD jng_height = 0; BYTE jng_color_type = 0; BYTE jng_image_sample_depth = 8; BYTE jng_image_compression_method = 8; // 8: ISO-10918-1 Huffman-coded baseline JPEG. BYTE jng_image_interlace_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; BYTE buffer[16]; FIMEMORY *hJngMemory = NULL; FIMEMORY *hJpegMemory = NULL; FIMEMORY *hPngMemory = NULL; FIBITMAP *dib_rgb = NULL; FIBITMAP *dib_alpha = NULL; if(!dib || (FreeImage_GetImageType(dib) != FIT_BITMAP)) { return FALSE; } unsigned bpp = FreeImage_GetBPP(dib); switch(bpp) { case 8: if(FreeImage_GetColorType(dib) == FIC_MINISBLACK) { dib_rgb = dib; jng_color_type = MNG_COLORTYPE_JPEGGRAY; } else { // JPEG plugin will convert other types (FIC_MINISWHITE, FIC_PALETTE) to 24-bit on the fly //dib_rgb = FreeImage_ConvertTo24Bits(dib); dib_rgb = dib; jng_color_type = MNG_COLORTYPE_JPEGCOLOR; } break; case 24: dib_rgb = dib; jng_color_type = MNG_COLORTYPE_JPEGCOLOR; break; case 32: dib_rgb = FreeImage_ConvertTo24Bits(dib); jng_color_type = MNG_COLORTYPE_JPEGCOLORA; jng_alpha_sample_depth = 8; break; default: return FALSE; } jng_width = (DWORD)FreeImage_GetWidth(dib); jng_height = (DWORD)FreeImage_GetHeight(dib); try { hJngMemory = FreeImage_OpenMemory(); // --- write JNG file signature --- FreeImage_WriteMemory(g_jng_signature, 1, 8, hJngMemory); // --- write a JHDR chunk --- SwapLong(&jng_width); SwapLong(&jng_height); memcpy(&buffer[0], &jng_width, 4); memcpy(&buffer[4], &jng_height, 4); SwapLong(&jng_width); SwapLong(&jng_height); buffer[8] = jng_color_type; buffer[9] = jng_image_sample_depth; buffer[10] = jng_image_compression_method; buffer[11] = jng_image_interlace_method; buffer[12] = jng_alpha_sample_depth; buffer[13] = jng_alpha_compression_method; buffer[14] = jng_alpha_filter_method; buffer[15] = jng_alpha_interlace_method; mng_WriteChunk(mng_JHDR, &buffer[0], 16, hJngMemory); // --- write a sequence of JDAT chunks --- hJpegMemory = FreeImage_OpenMemory(); flags |= JPEG_BASELINE; if(!FreeImage_SaveToMemory(FIF_JPEG, dib_rgb, hJpegMemory, flags)) { throw (const char*)NULL; } if(dib_rgb != dib) { FreeImage_Unload(dib_rgb); dib_rgb = NULL; } { BYTE *jpeg_data = NULL; DWORD size_in_bytes = 0; // get a pointer to the stream buffer FreeImage_AcquireMemory(hJpegMemory, &jpeg_data, &size_in_bytes); // write chunks for(DWORD k = 0; k < size_in_bytes;) { DWORD bytes_left = size_in_bytes - k; DWORD chunk_size = MIN(JPEG_CHUNK_SIZE, bytes_left); mng_WriteChunk(mng_JDAT, &jpeg_data[k], chunk_size, hJngMemory); k += chunk_size; } } FreeImage_CloseMemory(hJpegMemory); hJpegMemory = NULL; // --- write alpha layer as a sequence of IDAT chunk --- if((bpp == 32) && (jng_color_type == MNG_COLORTYPE_JPEGCOLORA)) { dib_alpha = FreeImage_GetChannel(dib, FICC_ALPHA); hPngMemory = FreeImage_OpenMemory(); if(!FreeImage_SaveToMemory(FIF_PNG, dib_alpha, hPngMemory, PNG_DEFAULT)) { throw (const char*)NULL; } FreeImage_Unload(dib_alpha); dib_alpha = NULL; // get the IDAT chunk { BOOL bResult = FALSE; DWORD start_pos = 0; DWORD next_pos = 0; long offset = 8; do { // find the next IDAT chunk from 'offset' position bResult = mng_FindChunk(hPngMemory, mng_IDAT, offset, &start_pos, &next_pos); if(!bResult) break; BYTE *png_data = NULL; DWORD size_in_bytes = 0; // get a pointer to the stream buffer FreeImage_AcquireMemory(hPngMemory, &png_data, &size_in_bytes); // write the IDAT chunk mng_WriteChunk(mng_IDAT, &png_data[start_pos+8], next_pos - start_pos - 12, hJngMemory); offset = next_pos; } while(bResult); } FreeImage_CloseMemory(hPngMemory); hPngMemory = NULL; } // --- write a IEND chunk --- mng_WriteChunk(mng_IEND, NULL, 0, hJngMemory); // write the JNG on output stream { BYTE *jng_data = NULL; DWORD size_in_bytes = 0; FreeImage_AcquireMemory(hJngMemory, &jng_data, &size_in_bytes); io->write_proc(jng_data, 1, size_in_bytes, handle); } FreeImage_CloseMemory(hJngMemory); FreeImage_CloseMemory(hJpegMemory); FreeImage_CloseMemory(hPngMemory); return TRUE; } catch(const char *text) { FreeImage_CloseMemory(hJngMemory); FreeImage_CloseMemory(hJpegMemory); FreeImage_CloseMemory(hPngMemory); if(dib_rgb && (dib_rgb != dib)) { FreeImage_Unload(dib_rgb); } FreeImage_Unload(dib_alpha); if(text) { FreeImage_OutputMessageProc(format_id, text); } return FALSE; } }
/** 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]; 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; } }
int ReadImageFile( const char *filename, uchar **imageData, int *imageWidth, int *imageHeight, int *numComponents, int flags ) { // Determine image format. FREE_IMAGE_FORMAT fif = FreeImage_GetFileType( filename, 0 ); if( fif == FIF_UNKNOWN ) fif = FreeImage_GetFIFFromFilename( filename ); if( fif == FIF_UNKNOWN ) { printf( "Error: Cannot determine image format of %s.\n", filename ); return 0; } // Read image data from file. FIBITMAP *dib = NULL; if( FreeImage_FIFSupportsReading( fif ) ) dib = FreeImage_Load( fif, filename, flags ); if( !dib ) { printf( "Error: Cannot read image file %s.\n", filename ); return 0; } // Check image type. FREE_IMAGE_TYPE fit = FreeImage_GetImageType( dib ); if ( fit != FIT_BITMAP ) { FreeImage_Unload( dib ); printf( "Error: Only 8-bits-per-component standard bitmap is supported.\n" ); return 0; } // Check bits per pixel. int bits_per_pixel = FreeImage_GetBPP( dib ); if ( bits_per_pixel != 8 && bits_per_pixel != 16 && bits_per_pixel != 24 && bits_per_pixel != 32 ) { FreeImage_Unload( dib ); printf( "Error: Only 8, 16, 24, 32 bits per pixel are supported.\n" ); return 0; } int _numComponents = bits_per_pixel / 8; int _imageWidth = FreeImage_GetWidth( dib ); int _imageHeight = FreeImage_GetHeight( dib ); uchar *_imageData = (uchar *) malloc( _imageWidth * _imageHeight * _numComponents ); if ( _imageData == NULL ) { FreeImage_Unload( dib ); printf( "Error: Not enough memory.\n" ); return 0; } // Copy image in FIBITMAP to user image data. int imageDataCount = 0; if ( _numComponents == 1 ) { for( int y = 0; y < _imageHeight; y++ ) { BYTE *dibData = FreeImage_GetScanLine( dib, y ); for( int x = 0; x < _imageWidth; x++) { _imageData[imageDataCount++] = dibData[x]; } } } else if ( _numComponents == 2 ) { for( int y = 0; y < _imageHeight; y++ ) { BYTE *dibData = FreeImage_GetScanLine( dib, y ); for( int x = 0; x < _imageWidth; x++) { _imageData[imageDataCount++] = dibData[0]; _imageData[imageDataCount++] = dibData[1]; dibData += _numComponents; } } } else if ( _numComponents == 3 ) { for( int y = 0; y < _imageHeight; y++ ) { BYTE *dibData = FreeImage_GetScanLine( dib, y ); for( int x = 0; x < _imageWidth; x++) { _imageData[imageDataCount++] = dibData[FI_RGBA_RED]; _imageData[imageDataCount++] = dibData[FI_RGBA_GREEN]; _imageData[imageDataCount++] = dibData[FI_RGBA_BLUE]; dibData += _numComponents; } } } else if ( _numComponents == 4 ) { for( int y = 0; y < _imageHeight; y++ ) { BYTE *dibData = FreeImage_GetScanLine( dib, y ); for( int x = 0; x < _imageWidth; x++) { _imageData[imageDataCount++] = dibData[FI_RGBA_RED]; _imageData[imageDataCount++] = dibData[FI_RGBA_GREEN]; _imageData[imageDataCount++] = dibData[FI_RGBA_BLUE]; _imageData[imageDataCount++] = dibData[FI_RGBA_ALPHA]; dibData += _numComponents; } } } FreeImage_Unload( dib ); (*numComponents) = _numComponents; (*imageWidth) = _imageWidth; (*imageHeight) = _imageHeight; (*imageData) = _imageData; return 1; }
static BOOL DLL_CALLCONV Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { const char *channel_name[4] = { "R", "G", "B", "A" }; BOOL bIsFlipped = FALSE; half *halfData = NULL; if(!dib || !handle) return FALSE; try { // check for EXR_LC compression and verify that the format is RGB if((flags & EXR_LC) == EXR_LC) { FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); if(((image_type != FIT_RGBF) && (image_type != FIT_RGBAF)) || ((flags & EXR_FLOAT) == EXR_FLOAT)) { THROW (Iex::IoExc, "EXR_LC compression is only available with RGB[A]F images"); } if((FreeImage_GetWidth(dib) % 2) || (FreeImage_GetHeight(dib) % 2)) { THROW (Iex::IoExc, "EXR_LC compression only works when the width and height are a multiple of 2"); } } // wrap the FreeImage IO stream C_OStream ostream(io, handle); // compression Imf::Compression compress; if((flags & EXR_NONE) == EXR_NONE) { // no compression compress = Imf::NO_COMPRESSION; } else if((flags & EXR_ZIP) == EXR_ZIP) { // zlib compression, in blocks of 16 scan lines compress = Imf::ZIP_COMPRESSION; } else if((flags & EXR_PIZ) == EXR_PIZ) { // piz-based wavelet compression compress = Imf::PIZ_COMPRESSION; } else if((flags & EXR_PXR24) == EXR_PXR24) { // lossy 24-bit float compression compress = Imf::PXR24_COMPRESSION; } else if((flags & EXR_B44) == EXR_B44) { // lossy 44% float compression compress = Imf::B44_COMPRESSION; } else { // default value compress = Imf::PIZ_COMPRESSION; } // create the header int width = FreeImage_GetWidth(dib); int height = FreeImage_GetHeight(dib); int dx = 0, dy = 0; Imath::Box2i dataWindow (Imath::V2i (0, 0), Imath::V2i (width - 1, height - 1)); Imath::Box2i displayWindow (Imath::V2i (-dx, -dy), Imath::V2i (width - dx - 1, height - dy - 1)); Imf::Header header = Imf::Header(displayWindow, dataWindow, 1, Imath::V2f(0,0), 1, Imf::INCREASING_Y, compress); // handle thumbnail SetPreviewImage(dib, header); // check for EXR_LC compression if((flags & EXR_LC) == EXR_LC) { return SaveAsEXR_LC(ostream, dib, header, width, height); } // output pixel type Imf::PixelType pixelType; if((flags & EXR_FLOAT) == EXR_FLOAT) { pixelType = Imf::FLOAT; // save as float data type } else { // default value pixelType = Imf::HALF; // save as half data type } // check the data type and number of channels int components = 0; FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); switch(image_type) { case FIT_FLOAT: components = 1; // insert luminance channel header.channels().insert ("Y", Imf::Channel(pixelType)); break; case FIT_RGBF: components = 3; for(int c = 0; c < components; c++) { // insert R, G and B channels header.channels().insert (channel_name[c], Imf::Channel(pixelType)); } break; case FIT_RGBAF: components = 4; for(int c = 0; c < components; c++) { // insert R, G, B and A channels header.channels().insert (channel_name[c], Imf::Channel(pixelType)); } break; default: THROW (Iex::ArgExc, "Cannot save: invalid data type.\nConvert the image to float before saving as OpenEXR."); } // build a frame buffer (i.e. what we have on input) Imf::FrameBuffer frameBuffer; BYTE *bits = NULL; // pointer to our pixel buffer size_t bytespp = 0; // size of our pixel in bytes size_t bytespc = 0; // size of our pixel component in bytes unsigned pitch = 0; // size of our yStride in bytes if(pixelType == Imf::HALF) { // convert from float to half halfData = new(std::nothrow) half[width * height * components]; if(!halfData) { THROW (Iex::NullExc, FI_MSG_ERROR_MEMORY); } for(int y = 0; y < height; y++) { float *src_bits = (float*)FreeImage_GetScanLine(dib, height - 1 - y); half *dst_bits = halfData + y * width * components; for(int x = 0; x < width; x++) { for(int c = 0; c < components; c++) { dst_bits[c] = src_bits[c]; } src_bits += components; dst_bits += components; } } bits = (BYTE*)halfData; bytespc = sizeof(half); bytespp = sizeof(half) * components; pitch = sizeof(half) * width * components; } else if(pixelType == Imf::FLOAT) { // invert dib scanlines bIsFlipped = FreeImage_FlipVertical(dib); bits = FreeImage_GetBits(dib); bytespc = sizeof(float); bytespp = sizeof(float) * components; pitch = FreeImage_GetPitch(dib); } if(image_type == FIT_FLOAT) { frameBuffer.insert ("Y", // name Imf::Slice (pixelType, // type (char*)(bits), // base bytespp, // xStride pitch)); // yStride } else if((image_type == FIT_RGBF) || (image_type == FIT_RGBAF)) { for(int c = 0; c < components; c++) { char *channel_base = (char*)(bits) + c*bytespc; frameBuffer.insert (channel_name[c],// name Imf::Slice (pixelType, // type channel_base, // base bytespp, // xStride pitch)); // yStride } } // write the data Imf::OutputFile file (ostream, header); file.setFrameBuffer (frameBuffer); file.writePixels (height); if(halfData != NULL) { delete[] halfData; } if(bIsFlipped) { // invert dib scanlines FreeImage_FlipVertical(dib); } return TRUE; } catch(Iex::BaseExc & e) { if(halfData != NULL) { delete[] halfData; } if(bIsFlipped) { // invert dib scanlines FreeImage_FlipVertical(dib); } FreeImage_OutputMessageProc(s_format_id, e.what()); return FALSE; } }
static inline void SET_FREEIMAGE_MARKER(BITMAPINFOHEADER *bmih, FIBITMAP *dib) { // Windows constants goes from 0L to 5L // Add 0xFF to avoid conflicts bmih->biCompression = 0xFF + FreeImage_GetImageType(dib); }
/** Convert a FIBITMAP to a OpenJPEG image @param format_id Plugin ID @param dib FreeImage image @param parameters Compression parameters @return Returns the converted image if successful, returns NULL otherwise */ opj_image_t* FIBITMAPToJ2KImage(int format_id, FIBITMAP *dib, const opj_cparameters_t *parameters) { int prec, numcomps, x, y, index; OPJ_COLOR_SPACE color_space; opj_image_cmptparm_t cmptparm[4]; // maximum of 4 components opj_image_t *image = NULL; // image to encode try { int w = FreeImage_GetWidth(dib); int h = FreeImage_GetHeight(dib); // get image characteristics FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); if(image_type == FIT_BITMAP) { // standard image ... prec = 8; switch(FreeImage_GetColorType(dib)) { case FIC_MINISBLACK: numcomps = 1; color_space = CLRSPC_GRAY; break; case FIC_RGB: if(FreeImage_GetBPP(dib) == 32) { // 32-bit image with a fully opaque layer numcomps = 4; color_space = CLRSPC_SRGB; } else { // 24-bit image numcomps = 3; color_space = CLRSPC_SRGB; } break; case FIC_RGBALPHA: numcomps = 4; color_space = CLRSPC_SRGB; break; default: return NULL; } } else { // HDR image ... prec = 16; switch(image_type) { case FIT_UINT16: numcomps = 1; color_space = CLRSPC_GRAY; break; case FIT_RGB16: numcomps = 3; color_space = CLRSPC_SRGB; break; case FIT_RGBA16: numcomps = 4; color_space = CLRSPC_SRGB; break; default: return NULL; } } // initialize image components memset(&cmptparm[0], 0, 4 * sizeof(opj_image_cmptparm_t)); for(int i = 0; i < numcomps; i++) { cmptparm[i].dx = parameters->subsampling_dx; cmptparm[i].dy = parameters->subsampling_dy; cmptparm[i].w = w; cmptparm[i].h = h; cmptparm[i].prec = prec; cmptparm[i].bpp = prec; cmptparm[i].sgnd = 0; } // create the image image = opj_image_create(numcomps, &cmptparm[0], color_space); if(!image) { throw FI_MSG_ERROR_DIB_MEMORY; } // set image offset and reference grid image->x0 = parameters->image_offset_x0; image->y0 = parameters->image_offset_y0; image->x1 = parameters->image_offset_x0 + (w - 1) * parameters->subsampling_dx + 1; image->y1 = parameters->image_offset_y0 + (h - 1) * parameters->subsampling_dy + 1; // set image data if(prec == 8) { switch(numcomps) { case 1: index = 0; for(y = 0; y < h; y++) { BYTE *bits = FreeImage_GetScanLine(dib, h - 1 - y); for(x = 0; x < w; x++) { image->comps[0].data[index] = bits[x]; index++; } } break; case 3: index = 0; for(y = 0; y < h; y++) { BYTE *bits = FreeImage_GetScanLine(dib, h - 1 - y); for(x = 0; x < w; x++) { image->comps[0].data[index] = bits[FI_RGBA_RED]; image->comps[1].data[index] = bits[FI_RGBA_GREEN]; image->comps[2].data[index] = bits[FI_RGBA_BLUE]; bits += 3; index++; } } break; case 4: index = 0; for(y = 0; y < h; y++) { BYTE *bits = FreeImage_GetScanLine(dib, h - 1 - y); for(x = 0; x < w; x++) { image->comps[0].data[index] = bits[FI_RGBA_RED]; image->comps[1].data[index] = bits[FI_RGBA_GREEN]; image->comps[2].data[index] = bits[FI_RGBA_BLUE]; image->comps[3].data[index] = bits[FI_RGBA_ALPHA]; bits += 4; index++; } } break; } } else if(prec == 16) { switch(numcomps) { case 1: index = 0; for(y = 0; y < h; y++) { WORD *bits = (WORD*)FreeImage_GetScanLine(dib, h - 1 - y); for(x = 0; x < w; x++) { image->comps[0].data[index] = bits[x]; index++; } } break; case 3: index = 0; for(y = 0; y < h; y++) { FIRGB16 *bits = (FIRGB16*)FreeImage_GetScanLine(dib, h - 1 - y); for(x = 0; x < w; x++) { image->comps[0].data[index] = bits[x].red; image->comps[1].data[index] = bits[x].green; image->comps[2].data[index] = bits[x].blue; index++; } } break; case 4: index = 0; for(y = 0; y < h; y++) { FIRGBA16 *bits = (FIRGBA16*)FreeImage_GetScanLine(dib, h - 1 - y); for(x = 0; x < w; x++) { image->comps[0].data[index] = bits[x].red; image->comps[1].data[index] = bits[x].green; image->comps[2].data[index] = bits[x].blue; image->comps[3].data[index] = bits[x].alpha; index++; } } break; } } return image; } catch (const char *text) { if(image) opj_image_destroy(image); FreeImage_OutputMessageProc(format_id, text); return NULL; } }
//!!!be sure to release memory before return null ResHandler* WIPResourceManager::alloc(const char* filename, WIPResourceType type) { ResHandler* res = new ResHandler; switch (type) { case TEXT: { } break; case TEXTURE: { TextureData *data = new TextureData; //初始化FreeImage FreeImage_Initialise(TRUE); FIBITMAP* bmpConverted = NULL; FREE_IMAGE_FORMAT fif = FIF_UNKNOWN; fif = FreeImage_GetFileType(filename); if (fif == FIF_UNKNOWN) fif = FreeImage_GetFIFFromFilename(filename); if ((fif != FIF_UNKNOWN) && FreeImage_FIFSupportsReading(fif)) { FIBITMAP *dib = FreeImage_Load(fif, filename); if (!dib) { printf("[fatal error]: \"%s\" load error!\n", filename); delete data; delete_handler(res); return NULL; } // we are top left, FIBITMAP is bottom left FreeImage_FlipVertical(dib); //get infomation FREE_IMAGE_TYPE imgType = FreeImage_GetImageType(dib); FREE_IMAGE_COLOR_TYPE colorType = FreeImage_GetColorType(dib); unsigned int bpp = FreeImage_GetBPP(dib); data->bpp = bpp; data->color_type = colorType; data->image_type = imgType; data->channel = 4; int x, y; RGBQUAD m_rgb; //获取图片长宽 int width = (int)FreeImage_GetWidth(dib); int height = (int)FreeImage_GetHeight(dib); unsigned char* imgBuf = new unsigned char[width*height * 4]; bool is_tr = FreeImage_IsTransparent(dib); bool is_little = FreeImage_IsLittleEndian(); //获取图片数据 //按RGBA格式保存到数组中 for (y = 0; y<height; y++) { for (x = 0; x<width; x++) { //获取像素值 FreeImage_GetPixelColor(dib, x, y, &m_rgb); if (is_little) { imgBuf[y*width * 4 + x * 4 + 0] = m_rgb.rgbRed; imgBuf[y*width * 4 + x * 4 + 1] = m_rgb.rgbGreen; imgBuf[y*width * 4 + x * 4 + 2] = m_rgb.rgbBlue; //判断是否透明图片 //如果是就取alpha值保存 if (is_tr) imgBuf[y*width * 4 + x * 4 + 3] = m_rgb.rgbReserved; else imgBuf[y*width * 4 + x * 4 + 3] = 255; } else { //大端警告! //Big Endian Warnning! printf("Note:This is a Big endian!\n"); } } } data->width = width; data->height = height; data->size = height*width * 4; FreeImage_Unload(dib); res->file_name = filename; res->extra = data; res->nref = 1; res->ptr = imgBuf; res->size = width*height * 4; res->type = TEXTURE; _map[filename] = res; return res; } } break; case SOUND: { } default: return res; break; } delete_handler(res); return NULL; }
FIBITMAP * DLL_CALLCONV FreeImage_ConvertToUINT16(FIBITMAP *dib) { FIBITMAP *src = NULL; FIBITMAP *dst = NULL; if(!FreeImage_HasPixels(dib)) return NULL; const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib); // check for allowed conversions switch(src_type) { case FIT_BITMAP: { // convert to greyscale if needed if((FreeImage_GetBPP(dib) == 8) && (FreeImage_GetColorType(dib) == FIC_MINISBLACK)) { src = dib; } else { src = FreeImage_ConvertToGreyscale(dib); if(!src) return NULL; } break; } case FIT_UINT16: // UINT16 type : clone the src return FreeImage_Clone(dib); break; case FIT_RGB16: // allow conversion from 48-bit RGB src = dib; break; case FIT_RGBA16: // allow conversion from 64-bit RGBA (ignore the alpha channel) src = dib; break; default: return NULL; } // allocate dst image const unsigned width = FreeImage_GetWidth(src); const unsigned height = FreeImage_GetHeight(src); dst = FreeImage_AllocateT(FIT_UINT16, width, height); if(!dst) return NULL; // copy metadata from src to dst FreeImage_CloneMetadata(dst, src); // convert from src type to UINT16 switch(src_type) { case FIT_BITMAP: { for(unsigned y = 0; y < height; y++) { const BYTE *src_bits = (BYTE*)FreeImage_GetScanLine(src, y); WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y); for(unsigned x = 0; x < width; x++) { dst_bits[x] = src_bits[x] << 8; } } } break; case FIT_RGB16: { for(unsigned y = 0; y < height; y++) { const FIRGB16 *src_bits = (FIRGB16*)FreeImage_GetScanLine(src, y); WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y); for(unsigned x = 0; x < width; x++) { // convert to grey dst_bits[x] = (WORD)LUMA_REC709(src_bits[x].red, src_bits[x].green, src_bits[x].blue); } } } break; case FIT_RGBA16: { for(unsigned y = 0; y < height; y++) { const FIRGBA16 *src_bits = (FIRGBA16*)FreeImage_GetScanLine(src, y); WORD *dst_bits = (WORD*)FreeImage_GetScanLine(dst, y); for(unsigned x = 0; x < width; x++) { // convert to grey dst_bits[x] = (WORD)LUMA_REC709(src_bits[x].red, src_bits[x].green, src_bits[x].blue); } } } break; default: break; } if(src != dib) { FreeImage_Unload(src); } return dst; }
/** @brief Perfoms an histogram transformation on a 8, 24 or 32-bit image according to the values of a lookup table (LUT). The transformation is done as follows.<br> Image 8-bit : if the image has a color palette, the LUT is applied to this palette, otherwise, it is applied to the grey values.<br> Image 24-bit & 32-bit : if channel == FICC_RGB, the same LUT is applied to each color plane (R,G, and B). Otherwise, the LUT is applied to the specified channel only. @param src Input image to be processed. @param LUT Lookup table. <b>The size of 'LUT' is assumed to be 256.</b> @param channel The color channel to be processed (only used with 24 & 32-bit DIB). @return Returns TRUE if successful, FALSE otherwise. @see FREE_IMAGE_COLOR_CHANNEL */ BOOL DLL_CALLCONV FreeImage_AdjustCurve(FIBITMAP *src, BYTE *LUT, FREE_IMAGE_COLOR_CHANNEL channel) { unsigned x, y; BYTE *bits = NULL; if(!FreeImage_HasPixels(src) || !LUT || (FreeImage_GetImageType(src) != FIT_BITMAP)) return FALSE; int bpp = FreeImage_GetBPP(src); if((bpp != 8) && (bpp != 24) && (bpp != 32)) return FALSE; // apply the LUT switch(bpp) { case 8 : { // if the dib has a colormap, apply the LUT to it // else, apply the LUT to pixel values if(FreeImage_GetColorType(src) == FIC_PALETTE) { RGBQUAD *rgb = FreeImage_GetPalette(src); for (unsigned pal = 0; pal < FreeImage_GetColorsUsed(src); pal++) { rgb->rgbRed = LUT[rgb->rgbRed]; rgb->rgbGreen = LUT[rgb->rgbGreen]; rgb->rgbBlue = LUT[rgb->rgbBlue]; rgb++; } } else { for(y = 0; y < FreeImage_GetHeight(src); y++) { bits = FreeImage_GetScanLine(src, y); for(x = 0; x < FreeImage_GetWidth(src); x++) { bits[x] = LUT[ bits[x] ]; } } } break; } case 24 : case 32 : { int bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src); switch(channel) { case FICC_RGB : for(y = 0; y < FreeImage_GetHeight(src); y++) { bits = FreeImage_GetScanLine(src, y); for(x = 0; x < FreeImage_GetWidth(src); x++) { bits[FI_RGBA_BLUE] = LUT[ bits[FI_RGBA_BLUE] ]; // B bits[FI_RGBA_GREEN] = LUT[ bits[FI_RGBA_GREEN] ]; // G bits[FI_RGBA_RED] = LUT[ bits[FI_RGBA_RED] ]; // R bits += bytespp; } } break; case FICC_BLUE : for(y = 0; y < FreeImage_GetHeight(src); y++) { bits = FreeImage_GetScanLine(src, y); for(x = 0; x < FreeImage_GetWidth(src); x++) { bits[FI_RGBA_BLUE] = LUT[ bits[FI_RGBA_BLUE] ]; // B bits += bytespp; } } break; case FICC_GREEN : for(y = 0; y < FreeImage_GetHeight(src); y++) { bits = FreeImage_GetScanLine(src, y); for(x = 0; x < FreeImage_GetWidth(src); x++) { bits[FI_RGBA_GREEN] = LUT[ bits[FI_RGBA_GREEN] ]; // G bits += bytespp; } } break; case FICC_RED : for(y = 0; y < FreeImage_GetHeight(src); y++) { bits = FreeImage_GetScanLine(src, y); for(x = 0; x < FreeImage_GetWidth(src); x++) { bits[FI_RGBA_RED] = LUT[ bits[FI_RGBA_RED] ]; // R bits += bytespp; } } break; case FICC_ALPHA : if(32 == bpp) { for(y = 0; y < FreeImage_GetHeight(src); y++) { bits = FreeImage_GetScanLine(src, y); for(x = 0; x < FreeImage_GetWidth(src); x++) { bits[FI_RGBA_ALPHA] = LUT[ bits[FI_RGBA_ALPHA] ]; // A bits += bytespp; } } } break; default: break; } break; } } return TRUE; }
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); // allocate a new dib FIBITMAP *new_dib = FreeImage_AllocateT(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 unsigned dib_size = FreeImage_GetImageSize(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; // 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 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; } } return new_dib; } return NULL; }
/** @brief Inverts each pixel data. @param src Input image to be processed. @return Returns TRUE if successful, FALSE otherwise. */ BOOL DLL_CALLCONV FreeImage_Invert(FIBITMAP *src) { if (!FreeImage_HasPixels(src)) return FALSE; unsigned i, x, y, k; const unsigned width = FreeImage_GetWidth(src); const unsigned height = FreeImage_GetHeight(src); const unsigned bpp = FreeImage_GetBPP(src); FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(src); if(image_type == FIT_BITMAP) { switch(bpp) { case 1 : case 4 : case 8 : { // if the dib has a colormap, just invert it // else, keep the linear grayscale if (FreeImage_GetColorType(src) == FIC_PALETTE) { RGBQUAD *pal = FreeImage_GetPalette(src); for(i = 0; i < FreeImage_GetColorsUsed(src); i++) { pal[i].rgbRed = 255 - pal[i].rgbRed; pal[i].rgbGreen = 255 - pal[i].rgbGreen; pal[i].rgbBlue = 255 - pal[i].rgbBlue; } } else { for(y = 0; y < height; y++) { BYTE *bits = FreeImage_GetScanLine(src, y); for (x = 0; x < FreeImage_GetLine(src); x++) { bits[x] = ~bits[x]; } } } break; } case 24 : case 32 : { // Calculate the number of bytes per pixel (3 for 24-bit or 4 for 32-bit) const unsigned bytespp = FreeImage_GetLine(src) / width; for(y = 0; y < height; y++) { BYTE *bits = FreeImage_GetScanLine(src, y); for(x = 0; x < width; x++) { for(k = 0; k < bytespp; k++) { bits[k] = ~bits[k]; } bits += bytespp; } } break; } default: return FALSE; } } else if((image_type == FIT_UINT16) || (image_type == FIT_RGB16) || (image_type == FIT_RGBA16)) { // Calculate the number of words per pixel (1 for 16-bit, 3 for 48-bit or 4 for 64-bit) const unsigned wordspp = (FreeImage_GetLine(src) / width) / sizeof(WORD); for(y = 0; y < height; y++) { WORD *bits = (WORD*)FreeImage_GetScanLine(src, y); for(x = 0; x < width; x++) { for(k = 0; k < wordspp; k++) { bits[k] = ~bits[k]; } bits += wordspp; } } } else { // anything else ... return FALSE; } return TRUE; }
FIBITMAP* psdParser::ReadImageData(FreeImageIO *io, fi_handle handle) { if(handle == NULL) return NULL; bool header_only = (_fi_flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; 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); if(dstCh < 3) { throw "Invalid number of channels"; } 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: throw "Unsupported color mode"; break; } if(!bitmap) { throw FI_MSG_ERROR_DIB_MEMORY; } // write thumbnail FreeImage_SetThumbnail(bitmap, _thumbnail.getDib()); // @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 && 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; }
/** @brief Applies color mapping for one or several colors on a 1-, 4- or 8-bit palletized or a 16-, 24- or 32-bit high color image. This function maps up to <i>count</i> colors specified in <i>srccolors</i> to these specified in <i>dstcolors</i>. Thereby, color <i>srccolors[N]</i>, if found in the image, will be replaced by color <i>dstcolors[N]</i>. If parameter <i>swap</i> is TRUE, additionally all colors specified in <i>dstcolors</i> are also mapped to these specified in <i>srccolors</i>. For high color images, the actual image data will be modified whereas, for palletized images only the palette will be changed.<br> The function returns the number of pixels changed or zero, if no pixels were changed. Both arrays <i>srccolors</i> and <i>dstcolors</i> are assumed not to hold less than <i>count</i> colors.<br> For 16-bit images, all colors specified are transparently converted to their proper 16-bit representation (either in RGB555 or RGB565 format, which is determined by the image's red- green- and blue-mask).<br> <b>Note, that this behaviour is different from what FreeImage_ApplyPaletteIndexMapping() does, which modifies the actual image data on palletized images.</b> @param dib Input/output image to be processed. @param srccolors Array of colors to be used as the mapping source. @param dstcolors Array of colors to be used as the mapping destination. @param count The number of colors to be mapped. This is the size of both <i>srccolors</i> and <i>dstcolors</i>. @param ignore_alpha If TRUE, 32-bit images and colors are treated as 24-bit. @param swap If TRUE, source and destination colors are swapped, that is, each destination color is also mapped to the corresponding source color. @return Returns the total number of pixels changed. */ unsigned DLL_CALLCONV FreeImage_ApplyColorMapping(FIBITMAP *dib, RGBQUAD *srccolors, RGBQUAD *dstcolors, unsigned count, BOOL ignore_alpha, BOOL swap) { unsigned result = 0; if (!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) { return 0; } // validate parameters if ((!srccolors) || (!dstcolors)|| (count < 1)) { return 0; } int bpp = FreeImage_GetBPP(dib); switch (bpp) { case 1: case 4: case 8: { unsigned size = FreeImage_GetColorsUsed(dib); RGBQUAD *pal = FreeImage_GetPalette(dib); RGBQUAD *a, *b; for (unsigned x = 0; x < size; x++) { for (unsigned j = 0; j < count; j++) { a = srccolors; b = dstcolors; for (int i = (swap ? 0 : 1); i < 2; i++) { if ((pal[x].rgbBlue == a[j].rgbBlue)&&(pal[x].rgbGreen == a[j].rgbGreen) &&(pal[x].rgbRed== a[j].rgbRed)) { pal[x].rgbBlue = b[j].rgbBlue; pal[x].rgbGreen = b[j].rgbGreen; pal[x].rgbRed = b[j].rgbRed; result++; j = count; break; } a = dstcolors; b = srccolors; } } } return result; } case 16: { WORD *src16 = (WORD *)malloc(sizeof(WORD) * count); if (NULL == src16) { return 0; } WORD *dst16 = (WORD *)malloc(sizeof(WORD) * count); if (NULL == dst16) { free(src16); return 0; } for (unsigned j = 0; j < count; j++) { src16[j] = RGBQUAD_TO_WORD(dib, (srccolors + j)); dst16[j] = RGBQUAD_TO_WORD(dib, (dstcolors + j)); } unsigned height = FreeImage_GetHeight(dib); unsigned width = FreeImage_GetWidth(dib); WORD *a, *b; for (unsigned y = 0; y < height; y++) { WORD *bits = (WORD *)FreeImage_GetScanLine(dib, y); for (unsigned x = 0; x < width; x++, bits++) { for (unsigned j = 0; j < count; j++) { a = src16; b = dst16; for (int i = (swap ? 0 : 1); i < 2; i++) { if (*bits == a[j]) { *bits = b[j]; result++; j = count; break; } a = dst16; b = src16; } } } } free(src16); free(dst16); return result; } case 24: { unsigned height = FreeImage_GetHeight(dib); unsigned width = FreeImage_GetWidth(dib); RGBQUAD *a, *b; for (unsigned y = 0; y < height; y++) { BYTE *bits = FreeImage_GetScanLine(dib, y); for (unsigned x = 0; x < width; x++, bits += 3) { for (unsigned j = 0; j < count; j++) { a = srccolors; b = dstcolors; for (int i = (swap ? 0 : 1); i < 2; i++) { if ((bits[FI_RGBA_BLUE] == a[j].rgbBlue) && (bits[FI_RGBA_GREEN] == a[j].rgbGreen) &&(bits[FI_RGBA_RED] == a[j].rgbRed)) { bits[FI_RGBA_BLUE] = b[j].rgbBlue; bits[FI_RGBA_GREEN] = b[j].rgbGreen; bits[FI_RGBA_RED] = b[j].rgbRed; result++; j = count; break; } a = dstcolors; b = srccolors; } } } } return result; } case 32: { unsigned height = FreeImage_GetHeight(dib); unsigned width = FreeImage_GetWidth(dib); RGBQUAD *a, *b; for (unsigned y = 0; y < height; y++) { BYTE *bits = FreeImage_GetScanLine(dib, y); for (unsigned x = 0; x < width; x++, bits += 4) { for (unsigned j = 0; j < count; j++) { a = srccolors; b = dstcolors; for (int i = (swap ? 0 : 1); i < 2; i++) { if ((bits[FI_RGBA_BLUE] == a[j].rgbBlue) &&(bits[FI_RGBA_GREEN] == a[j].rgbGreen) &&(bits[FI_RGBA_RED] == a[j].rgbRed) &&((ignore_alpha) || (bits[FI_RGBA_ALPHA] == a[j].rgbReserved))) { bits[FI_RGBA_BLUE] = b[j].rgbBlue; bits[FI_RGBA_GREEN] = b[j].rgbGreen; bits[FI_RGBA_RED] = b[j].rgbRed; if (!ignore_alpha) { bits[FI_RGBA_ALPHA] = b[j].rgbReserved; } result++; j = count; break; } a = dstcolors; b = srccolors; } } } } return result; } default: { return 0; } } }
FREE_IMAGE_COLOR_TYPE DLL_CALLCONV FreeImage_GetColorType(FIBITMAP *dib) { RGBQUAD *rgb; const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); // special bitmap type if(image_type != FIT_BITMAP) { switch(image_type) { case FIT_UINT16: { // 16-bit greyscale TIF can be either FIC_MINISBLACK (the most common case) or FIC_MINISWHITE // you can check this using EXIF_MAIN metadata FITAG *photometricTag = NULL; if(FreeImage_GetMetadata(FIMD_EXIF_MAIN, dib, "PhotometricInterpretation", &photometricTag)) { const short *value = (short*)FreeImage_GetTagValue(photometricTag); // PHOTOMETRIC_MINISWHITE = 0 => min value is white // PHOTOMETRIC_MINISBLACK = 1 => min value is black return (*value == 0) ? FIC_MINISWHITE : FIC_MINISBLACK; } return FIC_MINISBLACK; } break; case FIT_RGB16: case FIT_RGBF: return FIC_RGB; case FIT_RGBA16: case FIT_RGBAF: return FIC_RGBALPHA; } return FIC_MINISBLACK; } // standard image type switch (FreeImage_GetBPP(dib)) { case 1: { rgb = FreeImage_GetPalette(dib); if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) && (rgb->rgbBlue == 0)) { rgb++; if ((rgb->rgbRed == 255) && (rgb->rgbGreen == 255) && (rgb->rgbBlue == 255)) return FIC_MINISBLACK; } if ((rgb->rgbRed == 255) && (rgb->rgbGreen == 255) && (rgb->rgbBlue == 255)) { rgb++; if ((rgb->rgbRed == 0) && (rgb->rgbGreen == 0) && (rgb->rgbBlue == 0)) return FIC_MINISWHITE; } return FIC_PALETTE; } case 4: case 8: // Check if the DIB has a color or a greyscale palette { int ncolors = FreeImage_GetColorsUsed(dib); int minisblack = 1; rgb = FreeImage_GetPalette(dib); for (int i = 0; i < ncolors; i++) { if ((rgb->rgbRed != rgb->rgbGreen) || (rgb->rgbRed != rgb->rgbBlue)) return FIC_PALETTE; // The DIB has a color palette if the greyscale isn't a linear ramp // Take care of reversed grey images if (rgb->rgbRed != i) { if ((ncolors-i-1) != rgb->rgbRed) return FIC_PALETTE; else minisblack = 0; } rgb++; } return minisblack ? FIC_MINISBLACK : FIC_MINISWHITE; } case 16: case 24: return FIC_RGB; case 32: { if (FreeImage_GetICCProfile(dib)->flags & FIICC_COLOR_IS_CMYK) return FIC_CMYK; if( FreeImage_HasPixels(dib) ) { // check for fully opaque alpha layer for (unsigned y = 0; y < FreeImage_GetHeight(dib); y++) { rgb = (RGBQUAD *)FreeImage_GetScanLine(dib, y); for (unsigned x = 0; x < FreeImage_GetWidth(dib); x++) if (rgb[x].rgbReserved != 0xFF) return FIC_RGBALPHA; } return FIC_RGB; } return FIC_RGBALPHA; } default : return FIC_MINISBLACK; } }
/** @brief Applies palette index mapping for one or several indices on a 1-, 4- or 8-bit palletized image. This function maps up to <i>count</i> palette indices specified in <i>srcindices</i> to these specified in <i>dstindices</i>. Thereby, index <i>srcindices[N]</i>, if present in the image, will be replaced by index <i>dstindices[N]</i>. If parameter <i>swap</i> is TRUE, additionally all indices specified in <i>dstindices</i> are also mapped to these specified in <i>srcindices</i>.<br> The function returns the number of pixels changed or zero, if no pixels were changed. Both arrays <i>srcindices</i> and <i>dstindices</i> are assumed not to hold less than <i>count</i> indices.<br> <b>Note, that this behaviour is different from what FreeImage_ApplyColorMapping() does, which modifies the actual image data on palletized images.</b> @param dib Input/output image to be processed. @param srcindices Array of palette indices to be used as the mapping source. @param dstindices Array of palette indices to be used as the mapping destination. @param count The number of palette indices to be mapped. This is the size of both <i>srcindices</i> and <i>dstindices</i>. @param swap If TRUE, source and destination palette indices are swapped, that is, each destination index is also mapped to the corresponding source index. @return Returns the total number of pixels changed. */ unsigned DLL_CALLCONV FreeImage_ApplyPaletteIndexMapping(FIBITMAP *dib, BYTE *srcindices, BYTE *dstindices, unsigned count, BOOL swap) { unsigned result = 0; if (!FreeImage_HasPixels(dib) || (FreeImage_GetImageType(dib) != FIT_BITMAP)) { return 0; } // validate parameters if ((!srcindices) || (!dstindices)|| (count < 1)) { return 0; } unsigned height = FreeImage_GetHeight(dib); unsigned width = FreeImage_GetLine(dib); BYTE *a, *b; int bpp = FreeImage_GetBPP(dib); switch (bpp) { case 1: { return result; } case 4: { int skip_last = (FreeImage_GetWidth(dib) & 0x01); unsigned max_x = width - 1; for (unsigned y = 0; y < height; y++) { BYTE *bits = FreeImage_GetScanLine(dib, y); for (unsigned x = 0; x < width; x++) { int start = ((skip_last) && (x == max_x)) ? 1 : 0; for (int cn = start; cn < 2; cn++) { for (unsigned j = 0; j < count; j++) { a = srcindices; b = dstindices; for (int i = ((swap) ? 0 : 1); i < 2; i++) { if (GET_NIBBLE(cn, bits[x]) == (a[j] & 0x0F)) { SET_NIBBLE(cn, bits[x], b[j]); result++; j = count; break; } a = dstindices; b = srcindices; } } } } } return result; } case 8: { for (unsigned y = 0; y < height; y++) { BYTE *bits = FreeImage_GetScanLine(dib, y); for (unsigned x = 0; x < width; x++) { for (unsigned j = 0; j < count; j++) { a = srcindices; b = dstindices; for (int i = ((swap) ? 0 : 1); i < 2; i++) { if (bits[x] == a[j]) { bits[x] = b[j]; result++; j = count; break; } a = dstindices; b = srcindices; } } } } return result; } default: { return 0; } } }
Texture* loadTexture(const std::string& filename, bool useCompression) { std::string _loggerCat = "loadTexture"; #if defined( GHOUL_USE_DEVIL ) ilInit(); iluInit(); //ilOriginFunc(IL_ORIGIN_LOWER_LEFT); //ilEnable(IL_ORIGIN_SET); ILboolean loadSuccess = ilLoadImage(filename.c_str()); if (!loadSuccess) { ILenum error = ilGetError(); LERROR("Error while loading image '" << filename << "': " << iluErrorString(error)); return nullptr; } ILint imageFormat = ilGetInteger(IL_IMAGE_FORMAT); ILint imageType = ilGetInteger(IL_IMAGE_TYPE); ILint imageByte = ilGetInteger(IL_IMAGE_BYTES_PER_PIXEL); ILint width = ilGetInteger(IL_IMAGE_WIDTH); ILint height = ilGetInteger(IL_IMAGE_HEIGHT); ILint depth = ilGetInteger(IL_IMAGE_DEPTH); // Copy data from common data store to own address space ILubyte* data = new ILubyte[width * height * imageByte]; ilCopyPixels(0, 0, 0, width, height, 1, imageFormat, IL_UNSIGNED_BYTE, data); glm::size3_t size(width, height, depth); Texture::Format format; switch (imageFormat) { case IL_RGB: format = Texture::Format::RGB; break; case IL_RGBA: format = Texture::Format::RGBA; break; case IL_BGR: format = Texture::Format::BGR; break; case IL_BGRA: format = Texture::Format::BGRA; break; default: LERROR("Could not read image file '" << filename << "' of format: '" << imageFormat << "'"); return nullptr; } GLenum type; switch (imageType) { case IL_UNSIGNED_BYTE: type = GL_UNSIGNED_BYTE; break; case IL_BYTE: type = GL_BYTE; break; case IL_UNSIGNED_SHORT: type = GL_UNSIGNED_SHORT; break; case IL_SHORT: type = GL_SHORT; break; case IL_UNSIGNED_INT: type = GL_UNSIGNED_INT; break; case IL_INT: type = GL_INT; break; case IL_FLOAT: type = GL_FLOAT; break; default: LERROR("Could not read image file '" << filename << "' of data type: '" << imageType << "'"); return nullptr; } #elif defined( GHOUL_USE_FREEIMAGE) //image format FREE_IMAGE_FORMAT fif = FIF_UNKNOWN; //pointer to the image, once loaded FIBITMAP *dib(0); //pointer to the image data BYTE* bits(0); //image width and height unsigned int width(0), height(0); //OpenGL's image ID to map to GLuint gl_texID; //check the file signature and deduce its format fif = FreeImage_GetFileType(filename.c_str(), 0); //if still unknown, try to guess the file format from the file extension if (fif == FIF_UNKNOWN) fif = FreeImage_GetFIFFromFilename(filename.c_str()); //if still unkown, return failure if (fif == FIF_UNKNOWN) return nullptr; //check that the plugin has reading capabilities and load the file if (FreeImage_FIFSupportsReading(fif)) dib = FreeImage_Load(fif, filename.c_str()); //if the image failed to load, return failure if (!dib) return nullptr; //retrieve the image data bits = FreeImage_GetBits(dib); //get the image width and height width = FreeImage_GetWidth(dib); height = FreeImage_GetHeight(dib); glm::size3_t size(width, height, 1); //if this somehow one of these failed (they shouldn't), return failure if ((bits == 0) || (width == 0) || (height == 0)) return nullptr; FREE_IMAGE_TYPE imageType = FreeImage_GetImageType(dib); FREE_IMAGE_COLOR_TYPE colorType = FreeImage_GetColorType(dib); BITMAPINFOHEADER* infoheader = FreeImage_GetInfoHeader(dib); /* FIT_UNKNOWN = 0, // unknown type FIT_BITMAP = 1, // standard image : 1-, 4-, 8-, 16-, 24-, 32-bit FIT_UINT16 = 2, // array of unsigned short : unsigned 16-bit FIT_INT16 = 3, // array of short : signed 16-bit FIT_UINT32 = 4, // array of unsigned long : unsigned 32-bit FIT_INT32 = 5, // array of long : signed 32-bit FIT_FLOAT = 6, // array of float : 32-bit IEEE floating point FIT_DOUBLE = 7, // array of double : 64-bit IEEE floating point FIT_COMPLEX = 8, // array of FICOMPLEX : 2 x 64-bit IEEE floating point FIT_RGB16 = 9, // 48-bit RGB image : 3 x 16-bit FIT_RGBA16 = 10, // 64-bit RGBA image : 4 x 16-bit FIT_RGBF = 11, // 96-bit RGB float image : 3 x 32-bit IEEE floating point FIT_RGBAF = 12 // 128-bit RGBA float image : 4 x 32-bit IEEE floating point FI_ENUM(FREE_IMAGE_COLOR_TYPE) { FIC_MINISWHITE = 0, // min value is white FIC_MINISBLACK = 1, // min value is black FIC_RGB = 2, // RGB color model FIC_PALETTE = 3, // color map indexed FIC_RGBALPHA = 4, // RGB color model with alpha channel FIC_CMYK = 5 // CMYK color model }; */ if (imageType != FIT_BITMAP) { LERROR("Could not read image file '" << filename << "' of data type: '" << imageType << "'"); return nullptr; } GLenum type = GL_UNSIGNED_BYTE; Texture::Format format; switch (colorType) { case FIC_RGB: format = Texture::Format::RGB; break; case FIC_RGBALPHA: format = Texture::Format::RGBA; break; default: LERROR("Could not read image file '" << filename << "' of color type: '" << colorType << "'"); return nullptr; } int imageByte = FreeImage_GetBPP(dib); unsigned int pitch = FreeImage_GetPitch(dib); BYTE* data = new BYTE[width * height * imageByte/8]; FreeImage_ConvertToRawBits(data, dib, pitch, imageByte, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, TRUE); FreeImage_Unload(dib); // Swap red and blue channels, cannot use GL_BGR in OpenGL core profile for (size_t i = 0; i < width * height; ++i) { size_t index = i * imageByte / 8; std::swap(data[index], data[index + 2]); } #endif Texture* result; if (useCompression) { switch (format) { case Texture::Format::RGB: result = new Texture(data, size, format, GL_COMPRESSED_RGB_S3TC_DXT1_EXT, type); break; case Texture::Format::RGBA: result = new Texture(data, size, format, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, type); break; default: LERROR("Could not assign compressed format for: '" << GLint(format) << "'. Using no compression instead"); result = new Texture(data, size, format, static_cast<int>(format), type); break; } } else result = new Texture(data, size, format, static_cast<int>(format), type); return result; }
FIBITMAP * DLL_CALLCONV FreeImage_ConvertToRGB16(FIBITMAP *dib) { FIBITMAP *src = NULL; FIBITMAP *dst = NULL; if(!FreeImage_HasPixels(dib)) return NULL; const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib); // check for allowed conversions switch(src_type) { case FIT_BITMAP: { // convert to 24-bit if needed if((FreeImage_GetBPP(dib) == 24) || (FreeImage_GetBPP(dib) == 32)) { src = dib; } else { src = FreeImage_ConvertTo24Bits(dib); if(!src) return NULL; } break; } case FIT_UINT16: // allow conversion from unsigned 16-bit src = dib; break; case FIT_RGB16: // RGB16 type : clone the src return FreeImage_Clone(dib); break; case FIT_RGBA16: // allow conversion from 64-bit RGBA (ignore the alpha channel) src = dib; break; default: return NULL; } // allocate dst image const unsigned width = FreeImage_GetWidth(src); const unsigned height = FreeImage_GetHeight(src); dst = FreeImage_AllocateT(FIT_RGB16, width, height); if(!dst) { if(src != dib) { FreeImage_Unload(src); } return NULL; } // copy metadata from src to dst FreeImage_CloneMetadata(dst, src); // convert from src type to RGB16 switch(src_type) { case FIT_BITMAP: { // Calculate the number of bytes per pixel (1 for 8-bit, 3 for 24-bit or 4 for 32-bit) const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src); for(unsigned y = 0; y < height; y++) { const BYTE *src_bits = (BYTE*)FreeImage_GetScanLine(src, y); FIRGB16 *dst_bits = (FIRGB16*)FreeImage_GetScanLine(dst, y); for(unsigned x = 0; x < width; x++) { dst_bits[x].red = src_bits[FI_RGBA_RED] << 8; dst_bits[x].green = src_bits[FI_RGBA_GREEN] << 8; dst_bits[x].blue = src_bits[FI_RGBA_BLUE] << 8; src_bits += bytespp; } } } break; case FIT_UINT16: { for(unsigned y = 0; y < height; y++) { const WORD *src_bits = (WORD*)FreeImage_GetScanLine(src, y); FIRGB16 *dst_bits = (FIRGB16*)FreeImage_GetScanLine(dst, y); for(unsigned x = 0; x < width; x++) { // convert by copying greyscale channel to each R, G, B channels dst_bits[x].red = src_bits[x]; dst_bits[x].green = src_bits[x]; dst_bits[x].blue = src_bits[x]; } } } break; case FIT_RGBA16: { for(unsigned y = 0; y < height; y++) { const FIRGBA16 *src_bits = (FIRGBA16*)FreeImage_GetScanLine(src, y); FIRGB16 *dst_bits = (FIRGB16*)FreeImage_GetScanLine(dst, y); for(unsigned x = 0; x < width; x++) { // convert and skip alpha channel dst_bits[x].red = src_bits[x].red; dst_bits[x].green = src_bits[x].green; dst_bits[x].blue = src_bits[x].blue; } } } break; default: break; } if(src != dib) { FreeImage_Unload(src); } return dst; }
//--------------------------------------------------------------------- Codec::DecodeResult FreeImageCodec::decode(DataStreamPtr& input) const { // Buffer stream into memory (TODO: override IO functions instead?) MemoryDataStream memStream(input, true); FIMEMORY* fiMem = FreeImage_OpenMemory(memStream.getPtr(), static_cast<DWORD>(memStream.size())); FIBITMAP* fiBitmap = FreeImage_LoadFromMemory( (FREE_IMAGE_FORMAT)mFreeImageType, fiMem); if (!fiBitmap) { OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Error decoding image", "FreeImageCodec::decode"); } ImageData* imgData = new ImageData(); MemoryDataStreamPtr output; imgData->depth = 1; // only 2D formats handled by this codec imgData->width = FreeImage_GetWidth(fiBitmap); imgData->height = FreeImage_GetHeight(fiBitmap); imgData->num_mipmaps = 0; // no mipmaps in non-DDS imgData->flags = 0; // Must derive format first, this may perform conversions FREE_IMAGE_TYPE imageType = FreeImage_GetImageType(fiBitmap); FREE_IMAGE_COLOR_TYPE colourType = FreeImage_GetColorType(fiBitmap); unsigned bpp = FreeImage_GetBPP(fiBitmap); switch(imageType) { case FIT_UNKNOWN: case FIT_COMPLEX: case FIT_UINT32: case FIT_INT32: case FIT_DOUBLE: default: OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Unknown or unsupported image format", "FreeImageCodec::decode"); break; case FIT_BITMAP: // Standard image type // Perform any colour conversions for greyscale if (colourType == FIC_MINISWHITE || colourType == FIC_MINISBLACK) { FIBITMAP* newBitmap = FreeImage_ConvertToGreyscale(fiBitmap); // free old bitmap and replace FreeImage_Unload(fiBitmap); fiBitmap = newBitmap; // get new formats bpp = FreeImage_GetBPP(fiBitmap); colourType = FreeImage_GetColorType(fiBitmap); } // Perform any colour conversions for RGB else if (bpp < 8 || colourType == FIC_PALETTE || colourType == FIC_CMYK) { FIBITMAP* newBitmap = FreeImage_ConvertTo24Bits(fiBitmap); // free old bitmap and replace FreeImage_Unload(fiBitmap); fiBitmap = newBitmap; // get new formats bpp = FreeImage_GetBPP(fiBitmap); colourType = FreeImage_GetColorType(fiBitmap); } // by this stage, 8-bit is greyscale, 16/24/32 bit are RGB[A] switch(bpp) { case 8: imgData->format = PF_L8; break; case 16: // Determine 555 or 565 from green mask // cannot be 16-bit greyscale since that's FIT_UINT16 if(FreeImage_GetGreenMask(fiBitmap) == FI16_565_GREEN_MASK) { imgData->format = PF_R5G6B5; } else { // FreeImage doesn't support 4444 format so must be 1555 imgData->format = PF_A1R5G5B5; } break; case 24: // FreeImage differs per platform // PF_BYTE_BGR[A] for little endian (== PF_ARGB native) // PF_BYTE_RGB[A] for big endian (== PF_RGBA native) #if OGRE_ENDIAN == OGRE_ENDIAN_BIG imgData->format = PF_BYTE_RGB; #else imgData->format = PF_BYTE_BGR; #endif break; case 32: #if OGRE_ENDIAN == OGRE_ENDIAN_BIG imgData->format = PF_BYTE_RGBA; #else imgData->format = PF_BYTE_BGRA; #endif break; }; break; case FIT_UINT16: case FIT_INT16: // 16-bit greyscale imgData->format = PF_L16; break; case FIT_FLOAT: // Single-component floating point data imgData->format = PF_FLOAT32_R; break; case FIT_RGB16: imgData->format = PF_SHORT_RGB; break; case FIT_RGBA16: imgData->format = PF_SHORT_RGBA; break; case FIT_RGBF: imgData->format = PF_FLOAT32_RGB; break; case FIT_RGBAF: imgData->format = PF_FLOAT32_RGBA; break; }; unsigned char* srcData = FreeImage_GetBits(fiBitmap); unsigned srcPitch = FreeImage_GetPitch(fiBitmap); // Final data - invert image and trim pitch at the same time size_t dstPitch = imgData->width * PixelUtil::getNumElemBytes(imgData->format); imgData->size = dstPitch * imgData->height; // Bind output buffer output.bind(new MemoryDataStream(imgData->size)); uchar* pSrc; uchar* pDst = output->getPtr(); for (size_t y = 0; y < imgData->height; ++y) { pSrc = srcData + (imgData->height - y - 1) * srcPitch; memcpy(pDst, pSrc, dstPitch); pDst += dstPitch; } FreeImage_Unload(fiBitmap); FreeImage_CloseMemory(fiMem); DecodeResult ret; ret.first = output; ret.second = CodecDataPtr(imgData); return ret; }
FIBITMAP * DLL_CALLCONV FreeImage_ConvertToRGBF(FIBITMAP *dib) { FIBITMAP *src = NULL; FIBITMAP *dst = NULL; if(!FreeImage_HasPixels(dib)) return NULL; const FREE_IMAGE_TYPE src_type = FreeImage_GetImageType(dib); // check for allowed conversions switch(src_type) { case FIT_BITMAP: { // allow conversion from 24- and 32-bit const FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib); if((color_type != FIC_RGB) && (color_type != FIC_RGBALPHA)) { src = FreeImage_ConvertTo24Bits(dib); if(!src) return NULL; } else { src = dib; } break; } case FIT_UINT16: // allow conversion from 16-bit src = dib; break; case FIT_RGB16: // allow conversion from 48-bit RGB src = dib; break; case FIT_RGBA16: // allow conversion from 64-bit RGBA (ignore the alpha channel) src = dib; break; case FIT_FLOAT: // allow conversion from 32-bit float src = dib; break; case FIT_RGBAF: // allow conversion from 128-bit RGBAF src = dib; break; case FIT_RGBF: // RGBF type : clone the src return FreeImage_Clone(dib); break; default: return NULL; } // allocate dst image const unsigned width = FreeImage_GetWidth(src); const unsigned height = FreeImage_GetHeight(src); dst = FreeImage_AllocateT(FIT_RGBF, width, height); if(!dst) return NULL; // copy metadata from src to dst FreeImage_CloneMetadata(dst, src); // convert from src type to RGBF const unsigned src_pitch = FreeImage_GetPitch(src); const unsigned dst_pitch = FreeImage_GetPitch(dst); switch(src_type) { case FIT_BITMAP: { // calculate the number of bytes per pixel (3 for 24-bit or 4 for 32-bit) const unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src); const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); for(unsigned y = 0; y < height; y++) { const BYTE *src_pixel = (BYTE*)src_bits; FIRGBF *dst_pixel = (FIRGBF*)dst_bits; for(unsigned x = 0; x < width; x++) { // convert and scale to the range [0..1] dst_pixel->red = (float)(src_pixel[FI_RGBA_RED]) / 255.0F; dst_pixel->green = (float)(src_pixel[FI_RGBA_GREEN]) / 255.0F; dst_pixel->blue = (float)(src_pixel[FI_RGBA_BLUE]) / 255.0F; src_pixel += bytespp; dst_pixel ++; } src_bits += src_pitch; dst_bits += dst_pitch; } } break; case FIT_UINT16: { const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); for(unsigned y = 0; y < height; y++) { const WORD *src_pixel = (WORD*)src_bits; FIRGBF *dst_pixel = (FIRGBF*)dst_bits; for(unsigned x = 0; x < width; x++) { // convert and scale to the range [0..1] const float dst_value = (float)src_pixel[x] / 65535.0F; dst_pixel[x].red = dst_value; dst_pixel[x].green = dst_value; dst_pixel[x].blue = dst_value; } src_bits += src_pitch; dst_bits += dst_pitch; } } break; case FIT_RGB16: { const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); for(unsigned y = 0; y < height; y++) { const FIRGB16 *src_pixel = (FIRGB16*) src_bits; FIRGBF *dst_pixel = (FIRGBF*) dst_bits; for(unsigned x = 0; x < width; x++) { // convert and scale to the range [0..1] dst_pixel[x].red = (float)(src_pixel[x].red) / 65535.0F; dst_pixel[x].green = (float)(src_pixel[x].green) / 65535.0F; dst_pixel[x].blue = (float)(src_pixel[x].blue) / 65535.0F; } src_bits += src_pitch; dst_bits += dst_pitch; } } break; case FIT_RGBA16: { const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); for(unsigned y = 0; y < height; y++) { const FIRGBA16 *src_pixel = (FIRGBA16*) src_bits; FIRGBF *dst_pixel = (FIRGBF*) dst_bits; for(unsigned x = 0; x < width; x++) { // convert and scale to the range [0..1] dst_pixel[x].red = (float)(src_pixel[x].red) / 65535.0F; dst_pixel[x].green = (float)(src_pixel[x].green) / 65535.0F; dst_pixel[x].blue = (float)(src_pixel[x].blue) / 65535.0F; } src_bits += src_pitch; dst_bits += dst_pitch; } } break; case FIT_FLOAT: { const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); for(unsigned y = 0; y < height; y++) { const float *src_pixel = (float*) src_bits; FIRGBF *dst_pixel = (FIRGBF*) dst_bits; for(unsigned x = 0; x < width; x++) { // convert by copying greyscale channel to each R, G, B channels dst_pixel[x].red = src_pixel[x]; dst_pixel[x].green = src_pixel[x]; dst_pixel[x].blue = src_pixel[x]; } src_bits += src_pitch; dst_bits += dst_pitch; } } break; case FIT_RGBAF: { const BYTE *src_bits = (BYTE*)FreeImage_GetBits(src); BYTE *dst_bits = (BYTE*)FreeImage_GetBits(dst); for(unsigned y = 0; y < height; y++) { const FIRGBAF *src_pixel = (FIRGBAF*) src_bits; FIRGBF *dst_pixel = (FIRGBF*) dst_bits; for(unsigned x = 0; x < width; x++) { // convert and skip alpha channel dst_pixel[x].red = src_pixel[x].red; dst_pixel[x].green = src_pixel[x].green; dst_pixel[x].blue = src_pixel[x].blue; } src_bits += src_pitch; dst_bits += dst_pitch; } } break; } if(src != dib) { FreeImage_Unload(src); } return dst; }
bool M_loadImage(const char * filename, void * data, void * arg) { if(! filename) return false; if(! data) return false; // freeimage io FreeImageIO io; io.read_proc = (FI_ReadProc)readProc; io.write_proc = (FI_WriteProc)writeProc; io.tell_proc = (FI_TellProc)tellProc; io.seek_proc = (FI_SeekProc)seekProc; // read file buffer MFile * fp = M_fopen(filename, "rb"); if(! fp) { printf("Error : can't read file %s\n", filename); return false; } // read freeimage FREE_IMAGE_FORMAT format = FreeImage_GetFileTypeFromHandle(&io, (fi_handle)fp, 0); if(format == FIF_UNKNOWN) { printf("Error : unknow format %s\n", filename); M_fclose(fp); return false; } FIBITMAP * dib = FreeImage_LoadFromHandle(format, &io, (fi_handle)fp); if(! dib) { printf("Error : unknow dib %s\n", filename); M_fclose(fp); return false; } BYTE * bits = FreeImage_GetBits(dib); unsigned int width = FreeImage_GetWidth(dib); unsigned int height = FreeImage_GetHeight(dib); unsigned int bpp = FreeImage_GetBPP(dib); FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); if((bits == 0) || (width == 0) || (height == 0)) { FreeImage_Unload(dib); M_fclose(fp); return false; } // flip FreeImage_FlipVertical(dib); // create image MImage * image = (MImage *)data; switch(image_type) { case FIT_BITMAP: switch(bpp) { case 8: image->create(M_UBYTE, width, height, 1); for(unsigned int y=0; y<height; y++) { unsigned char * dest = (unsigned char *)image->getData() + width*y; bits = FreeImage_GetScanLine(dib, y); memcpy(dest, bits, width*sizeof(char)); } break; case 24: SwapRedBlue32(dib); image->create(M_UBYTE, width, height, 3); for(unsigned int y=0; y<height; y++) { unsigned char * dest = (unsigned char *)image->getData() + width*y*3; bits = FreeImage_GetScanLine(dib, y); memcpy(dest, bits, width*3*sizeof(char)); } break; case 32: SwapRedBlue32(dib); image->create(M_UBYTE, width, height, 4); for(unsigned int y=0; y<height; y++) { unsigned char * dest = (unsigned char *)image->getData() + width*y*4; bits = FreeImage_GetScanLine(dib, y); memcpy(dest, bits, width*4*sizeof(char)); } break; default: break; } break; case FIT_RGB16: image->create(M_USHORT, width, height, 3); for(unsigned int y=0; y<height; y++) { unsigned short * dest = (unsigned short *)image->getData() + width*y*3; bits = FreeImage_GetScanLine(dib, y); memcpy(dest, bits, width*3*sizeof(short)); } break; case FIT_RGBA16: image->create(M_USHORT, width, height, 4); for(unsigned int y=0; y<height; y++) { unsigned short * dest = (unsigned short *)image->getData() + width*y*4; bits = FreeImage_GetScanLine(dib, y); memcpy(dest, bits, width*4*sizeof(short)); } break; case FIT_FLOAT: image->create(M_FLOAT, width, height, 1); for(unsigned int y=0; y<height; y++) { float * dest = (float *)image->getData() + width*y; bits = FreeImage_GetScanLine(dib, y); memcpy(dest, bits, width*sizeof(float)); } break; case FIT_RGBF: image->create(M_FLOAT, width, height, 3); for(unsigned int y=0; y<height; y++) { float * dest = (float *)image->getData() + width*y*3; bits = FreeImage_GetScanLine(dib, y); memcpy(dest, bits, width*3*sizeof(float)); } break; case FIT_RGBAF: image->create(M_FLOAT, width, height, 4); for(unsigned int y=0; y<height; y++) { float * dest = (float *)image->getData() + width*y*4; bits = FreeImage_GetScanLine(dib, y); memcpy(dest, bits, width*4*sizeof(float)); } break; default: break; } // clean FreeImage_Unload(dib); M_fclose(fp); return true; }
FIABITMAP *DLL_CALLCONV FIA_SetBorder (FIBITMAP * src, int xborder, int yborder, BorderType type, double constant) { FIABITMAP *dst = NULL; if (!src) { return NULL; } FREE_IMAGE_TYPE src_type = FreeImage_GetImageType (src); switch (src_type) { case FIT_BITMAP: { // standard image: 1-, 4-, 8-, 16-, 24-, 32-bit if (FreeImage_GetBPP (src) == 8) { dst = borderUCharImage.SetBorder (src, xborder, yborder, type, (unsigned char) constant); break; } } case FIT_UINT16: { // array of unsigned short: unsigned 16-bit dst = borderUShortImage.SetBorder (src, xborder, yborder, type, (unsigned short) constant); break; } case FIT_INT16: { // array of short: signed 16-bit dst = borderShortImage.SetBorder (src, xborder, yborder, type, (short) constant); break; } case FIT_UINT32: { // array of unsigned long: unsigned 32-bit dst = borderULongImage.SetBorder (src, xborder, yborder, type, (unsigned long) constant); break; } case FIT_INT32: { // array of long: signed 32-bit dst = borderLongImage.SetBorder (src, xborder, yborder, type, (long) constant); break; } case FIT_FLOAT: { // array of float: 32-bit dst = borderFloatImage.SetBorder (src, xborder, yborder, type, (float) constant); break; } case FIT_DOUBLE: { // array of double: 64-bit dst = borderDoubleImage.SetBorder (src, xborder, yborder, type, constant); break; } default: { break; } } if (NULL == dst) { FreeImage_OutputMessageProc (FIF_UNKNOWN, "FREE_IMAGE_TYPE: Unable to set border for type %d.", type); } return dst; }
bool StFreeImage::save(const StString& theFilePath, ImageType theImageType, StFormat ) { if(!StFreeImage::init()) { setState("FreeImage library is not initialized"); return false; } FREE_IMAGE_FORMAT aFIF = convertToFIF(theImageType); if(aFIF == FIF_UNKNOWN) { setState("FreeImage library, not supported image file format"); return false; } StImage stSaveImage; if(getColorModel() != ImgColor_RGB && getColorModel() != ImgColor_RGBA && getColorModel() != ImgColor_GRAY) { // convert from YUV and so on if(!stSaveImage.initRGB(*this)) { setState("StFreeImage, only RGB image could be saved"); return false; } } else { stSaveImage.initWrapper(*this); } const StImagePlane& stImgPlane = stSaveImage.getPlane(); FREE_IMAGE_TYPE aSaveFormatFI = FIT_UNKNOWN; if(!convertToFreeFormat(stImgPlane.getFormat(), aSaveFormatFI)) { setState("StFreeImage, image format currently not supported"); return false; } // allocate FreeImage native structure FIBITMAP* aSaveDIB = FreeImage_AllocateT(aSaveFormatFI, (int )stImgPlane.getSizeX(), (int )stImgPlane.getSizeY(), (unsigned )stImgPlane.getSizePixelBytes() * 8, 0, 0, 0); if(aSaveDIB == NULL) { setState("FreeImage library, internal error"); FreeImage_Unload(aSaveDIB); return false; } // wrapper the created data StImagePlane stImgPlaneSave; StImagePlane::ImgFormat stImgFormatSave = convertFromFreeFormat(FreeImage_GetImageType(aSaveDIB), FreeImage_GetColorType(aSaveDIB), FreeImage_GetBPP(aSaveDIB)); stImgPlaneSave.initWrapper(stImgFormatSave, FreeImage_GetBits(aSaveDIB), FreeImage_GetWidth(aSaveDIB), FreeImage_GetHeight(aSaveDIB), FreeImage_GetPitch(aSaveDIB)); // FreeImage data should be bottom-up... stImgPlaneSave.setTopDown(false); // copy from local structure to the FreeImage structure size_t aRowInc = (( stImgPlaneSave.isTopDown() && stImgPlane.isTopDown()) || (!stImgPlaneSave.isTopDown() && !stImgPlane.isTopDown())) ? 1 : size_t(-1); size_t aRowTo = (aRowInc == 1) ? 0 : (stImgPlane.getSizeY() - 1); for(size_t aRowFrom = 0; aRowFrom < stImgPlane.getSizeY(); ++aRowFrom, aRowTo += aRowInc) { for(size_t aCol = 0; aCol < stImgPlane.getSizeX(); ++aCol) { stMemCpy(stImgPlaneSave.changeData(aRowTo, aCol), stImgPlane.getData(aRowFrom, aCol), stImgPlane.getSizePixelBytes()); } } // now save the image file! #if defined(_WIN32) if(!FreeImage_Save(aFIF, aSaveDIB, theFilePath.toUtfWide().toCString(), 0)) { #else if(!FreeImage_Save(aFIF, aSaveDIB, theFilePath.toCString(), 0)) { #endif setState("FreeImage library, image save failed"); FreeImage_Unload(aSaveDIB); return false; } // free resources FreeImage_Unload(aSaveDIB); // set debug information StString aDummy, aFileName; StFileNode::getFolderAndFile(theFilePath, aDummy, aFileName); setState(StString("FreeImage library, saved image '") + aFileName + "' " + getDescription()); return true; }
/** Scan input dib format and return the equivalent PKPixelFormatGUID format for saving @param dib Image to be saved @param guid_format (returned value) GUID format @param bHasAlpha (returned value) TRUE if an alpha layer is present @return Returns TRUE if successful, returns FALSE otherwise */ static ERR GetOutputPixelFormat(FIBITMAP *dib, PKPixelFormatGUID *guid_format, BOOL *bHasAlpha) { const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); const unsigned bpp = FreeImage_GetBPP(dib); const FREE_IMAGE_COLOR_TYPE color_type = FreeImage_GetColorType(dib); *guid_format = GUID_PKPixelFormatDontCare; *bHasAlpha = FALSE; switch(image_type) { case FIT_BITMAP: // standard image : 1-, 4-, 8-, 16-, 24-, 32-bit switch(bpp) { case 1: // assume FIC_MINISBLACK if(color_type == FIC_MINISBLACK) { *guid_format = GUID_PKPixelFormatBlackWhite; } break; case 8: // assume FIC_MINISBLACK if(color_type == FIC_MINISBLACK) { *guid_format = GUID_PKPixelFormat8bppGray; } break; case 16: if ((FreeImage_GetRedMask(dib) == FI16_565_RED_MASK) && (FreeImage_GetGreenMask(dib) == FI16_565_GREEN_MASK) && (FreeImage_GetBlueMask(dib) == FI16_565_BLUE_MASK)) { *guid_format = GUID_PKPixelFormat16bppRGB565; } else { // includes case where all the masks are 0 *guid_format = GUID_PKPixelFormat16bppRGB555; } break; #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR case 24: *guid_format = GUID_PKPixelFormat24bppBGR; break; case 32: *guid_format = GUID_PKPixelFormat32bppBGRA; *bHasAlpha = TRUE; break; #elif FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB case 24: *guid_format = GUID_PKPixelFormat24bppRGB; break; case 32: *guid_format = GUID_PKPixelFormat32bppRGBA; *bHasAlpha = TRUE; break; #endif case 4: default: // not supported break; } break; case FIT_UINT16: // array of unsigned short : unsigned 16-bit *guid_format = GUID_PKPixelFormat16bppGray; break; case FIT_FLOAT: // array of float : 32-bit IEEE floating point *guid_format = GUID_PKPixelFormat32bppGrayFloat; break; case FIT_RGB16: // 48-bit RGB image : 3 x 16-bit *guid_format = GUID_PKPixelFormat48bppRGB; break; case FIT_RGBA16: // 64-bit RGBA image : 4 x 16-bit *guid_format = GUID_PKPixelFormat64bppRGBA; *bHasAlpha = TRUE; break; case FIT_RGBF: // 96-bit RGB float image : 3 x 32-bit IEEE floating point *guid_format = GUID_PKPixelFormat96bppRGBFloat; break; case FIT_RGBAF: // 128-bit RGBA float image : 4 x 32-bit IEEE floating point *guid_format = GUID_PKPixelFormat128bppRGBAFloat; *bHasAlpha = TRUE; break; case FIT_INT16: // array of short : signed 16-bit case FIT_UINT32: // array of unsigned long : unsigned 32-bit case FIT_INT32: // array of long : signed 32-bit case FIT_DOUBLE: // array of double : 64-bit IEEE floating point case FIT_COMPLEX: // array of FICOMPLEX : 2 x 64-bit IEEE floating point default: // unsupported format break; } return (*guid_format != GUID_PKPixelFormatDontCare) ? WMP_errSuccess : WMP_errUnsupportedFormat; }