FIBITMAP * CreateTemplete(FIBITMAP * hImage, unsigned ScreenWidth, unsigned ScreenHeight) // 创建空白PNG模版 { FIBITMAP * hPicTemplete; RGBQUAD * palMain ; RGBQUAD * palTemplete ; unsigned ImgPitchLocal ; BYTE * pBitLocal; unsigned x, y, n; hPicTemplete = FreeImage_Allocate(ScreenWidth, ScreenHeight, 8, 0, 0, 0); //创建目标图像 palMain = FreeImage_GetPalette(hImage); palTemplete = FreeImage_GetPalette(hPicTemplete); for (n = 0 ; n < 256 ; n++) { palTemplete[n].rgbRed = palMain[n].rgbRed ; palTemplete[n].rgbGreen = palMain[n].rgbGreen ; palTemplete[n].rgbBlue = palMain[n].rgbBlue ; } palTemplete[70].rgbRed = 255 ; palTemplete[70].rgbGreen = 255 ; palTemplete[70].rgbBlue = 255 ; // FreeImage_SetTransparent(hPicTemplete, false); // 所有像素颜色填充为 70 号索引 ImgPitchLocal = FreeImage_GetPitch(hPicTemplete) ; pBitLocal = FreeImage_GetBits(hPicTemplete); for (y = 0 ; y < ScreenHeight; y++) { for (x = 0; x < ScreenWidth ; x++) pBitLocal[x] = 70 ; pBitLocal += ImgPitchLocal ; // 下一行 } return hPicTemplete; }
bool readImage(SImage& image, MemChunk& data, int index) { // Get image info SImage::info_t info; FIBITMAP* bm = getFIInfo(data, info); // Check it created/read ok if (!bm) { Global::error = "Unable to read image data (unsupported format?)"; return false; } // Get image palette if it exists RGBQUAD* bm_pal = FreeImage_GetPalette(bm); Palette palette; if (bm_pal) { int a = 0; int b = FreeImage_GetColorsUsed(bm); if (b > 256) b = 256; for (; a < b; a++) palette.setColour(a, rgba_t(bm_pal[a].rgbRed, bm_pal[a].rgbGreen, bm_pal[a].rgbBlue, 255)); } // Create image if (info.has_palette) image.create(info, &palette); else image.create(info); uint8_t* img_data = imageData(image); // Convert to 32bpp & flip vertically FIBITMAP* rgba = FreeImage_ConvertTo32Bits(bm); if (!rgba) { LOG_MESSAGE(1, "FreeImage_ConvertTo32Bits failed for image data"); Global::error = "Error reading PNG data"; return false; } FreeImage_FlipVertical(rgba); // Load raw RGBA data uint8_t* bits_rgba = FreeImage_GetBits(rgba); int c = 0; for (int a = 0; a < info.width * info.height; a++) { img_data[c++] = bits_rgba[a * 4 + 2]; // Red img_data[c++] = bits_rgba[a * 4 + 1]; // Green img_data[c++] = bits_rgba[a * 4]; // Blue img_data[c++] = bits_rgba[a * 4 + 3]; // Alpha } // Free memory FreeImage_Unload(rgba); FreeImage_Unload(bm); return true; }
// 独立函数,读取gif图片,写白背景,转为PNG并写入缓存,返回缓存地址 FOX_DLL FIMEMORY * gif2png_bufopen(char *gifpath, BYTE ** buffpointeraddr, DWORD * bufflenaddr) { FIBITMAP * hImage ; RGBQUAD * pal ; FIMEMORY * hMemory = NULL ; BYTE *mem_buffer = NULL ; DWORD size_in_bytes = 0 ; hImage = FreeImage_Load(FIF_GIF, gifpath, 0); pal = FreeImage_GetPalette(hImage); pal[70].rgbRed = 255 ; pal[70].rgbGreen = 255 ; pal[70].rgbBlue = 255 ; FreeImage_SetTransparent(hImage, false); hMemory = FreeImage_OpenMemory() ; FreeImage_SaveToMemory(FIF_PNG, hImage, hMemory, PNG_DEFAULT) ; FreeImage_Unload(hImage) ; FreeImage_AcquireMemory(hMemory, &mem_buffer, &size_in_bytes); *buffpointeraddr = mem_buffer ; *bufflenaddr = size_in_bytes ; return hMemory ; // FreeImage_CloseMemory(hMemory) ; // 使用完缓存记得要释放 }
BOOL DLL_CALLCONV FreeImage_GetBackgroundColor(FIBITMAP *dib, RGBQUAD *bkcolor) { if(dib && bkcolor) { if(FreeImage_HasBackgroundColor(dib)) { // get the background color RGBQUAD *bkgnd_color = &((FREEIMAGEHEADER *)dib->data)->bkgnd_color; memcpy(bkcolor, bkgnd_color, sizeof(RGBQUAD)); // get the background index if(FreeImage_GetBPP(dib) == 8) { RGBQUAD *pal = FreeImage_GetPalette(dib); for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++) { if(bkgnd_color->rgbRed == pal[i].rgbRed) { if(bkgnd_color->rgbGreen == pal[i].rgbGreen) { if(bkgnd_color->rgbBlue == pal[i].rgbBlue) { bkcolor->rgbReserved = i; return TRUE; } } } } } bkcolor->rgbReserved = 0; return TRUE; } } return FALSE; }
// this function is only called for the 8bit case! int Vt2IsoImageFreeImage_c::getPaletteIndex (unsigned int aui_x, unsigned int aui_y) { if (mb_palettized) { // do this check here, because in case we only use 4bit bitmap, we don't have to care for the palette matching... if (mi_colourMismatch >= 0) { if (isOstream()) getOstream() << "*** COULDN'T LOAD BITMAP: WRONG PALETTE. See (first) mismatching colour #"<<mi_colourMismatch<<" below. Please use the ISO11783-Part 6 (VT)-Palette for bitmaps you have saved palettized and use in 8bit-mode. Use 'vt2iso -p' to generate an .act file and resample your bitmap to use this palette! ***" << std::endl; if (isOstream()) getOstream() << "HAS TO BE | you had" << std::hex << std::setfill('0'); for (int i=0; i<(16+216); i++) { if ((i % 8) == 0) { if (isOstream()) getOstream() << std::endl; } else { if (isOstream()) getOstream() << " "; } if (isOstream()) getOstream() << std::setw(2) << fiuint16_t(vtColourTable[i].bgrRed) << std::setw(2) << fiuint16_t(vtColourTable[i].bgrGreen) << std::setw(2) << fiuint16_t(vtColourTable[i].bgrBlue); if (i == mi_colourMismatch) { if (isOstream()) getOstream() << "*|*"; } else { if (isOstream()) getOstream() << " | "; } if (isOstream()) getOstream() << std::setw(2) << fiuint16_t(FreeImage_GetPalette (bitmap)[i].rgbRed) << std::setw(2) << fiuint16_t(FreeImage_GetPalette (bitmap)[i].rgbGreen) << std::setw(2) << fiuint16_t(FreeImage_GetPalette (bitmap)[i].rgbBlue); } if (isOstream()) getOstream() << std::endl; b_isInvalidPalette = true; return -1; } fiuint8_t idx; FreeImage_GetPixelIndex (bitmap, aui_x, (ui_height - 1) - aui_y, &idx); return idx; } else return -1; }
BOOL fipImage::setSize(FREE_IMAGE_TYPE image_type, WORD width, WORD height, WORD bpp, unsigned red_mask, unsigned green_mask, unsigned blue_mask) { if(_dib) { FreeImage_Unload(_dib); } if((_dib = FreeImage_AllocateT(image_type, width, height, bpp, red_mask, green_mask, blue_mask)) == NULL) return FALSE; if(image_type == FIT_BITMAP) { // Create palette if needed switch(bpp) { case 1: case 4: case 8: RGBQUAD *pal = FreeImage_GetPalette(_dib); for(unsigned i = 0; i < FreeImage_GetColorsUsed(_dib); i++) { pal[i].rgbRed = i; pal[i].rgbGreen = i; pal[i].rgbBlue = i; } break; } } _bHasChanged = TRUE; return TRUE; }
/** @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) { unsigned i, x, y, k; BYTE *bits; if (!src) return FALSE; int bpp = FreeImage_GetBPP(src); 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 < FreeImage_GetHeight(src); y++) { bits = FreeImage_GetScanLine(src, y); for (x = 0; x < FreeImage_GetLine(src); x++) { bits[x] = ~bits[x]; } } } break; } case 24 : case 32 : { unsigned bytespp = FreeImage_GetLine(src) / FreeImage_GetWidth(src); for(y = 0; y < FreeImage_GetHeight(src); y++) { bits = FreeImage_GetScanLine(src, y); for(x = 0; x < FreeImage_GetWidth(src); x++) { for(k = 0; k < bytespp; k++) { bits[k] = ~bits[k]; } bits += bytespp; } } break; } } return TRUE; }
void ofxGifEncoder::calculatePalette(FIBITMAP * bmp){ RGBQUAD *pal = FreeImage_GetPalette(bmp); for (int i = 0; i < 256; i++) { palette.push_back(ofColor(pal[i].rgbRed, pal[i].rgbGreen, pal[i].rgbBlue)); ofLog() << palette.at(i); } }
/** @brief Retrieves the red, green, blue or alpha channel of a 24- or 32-bit BGR[A] image. @param src Input image to be processed. @param channel Color channel to extract @return Returns the extracted channel if successful, returns NULL otherwise. */ FIBITMAP * DLL_CALLCONV FreeImage_GetChannel(FIBITMAP *src, FREE_IMAGE_COLOR_CHANNEL channel) { int c; if(!src) return NULL; unsigned bpp = FreeImage_GetBPP(src); if((bpp == 24) || (bpp == 32)) { // select the channel to extract switch(channel) { case FICC_BLUE: c = FI_RGBA_BLUE; break; case FICC_GREEN: c = FI_RGBA_GREEN; break; case FICC_RED: c = FI_RGBA_RED; break; case FICC_ALPHA: if(bpp != 32) return NULL; c = FI_RGBA_ALPHA; break; default: return NULL; } // allocate a 8-bit dib unsigned width = FreeImage_GetWidth(src); unsigned height = FreeImage_GetHeight(src); FIBITMAP *dst = FreeImage_Allocate(width, height, 8) ; if(!dst) return NULL; // build a greyscale palette RGBQUAD *pal = FreeImage_GetPalette(dst); for(int i = 0; i < 256; i++) { pal[i].rgbBlue = pal[i].rgbGreen = pal[i].rgbRed = i; } // perform extraction int bytespp = bpp / 8; // bytes / pixel for(unsigned y = 0; y < height; y++) { BYTE *src_bits = FreeImage_GetScanLine(src, y); BYTE *dst_bits = FreeImage_GetScanLine(dst, y); for(unsigned x = 0; x < width; x++) { dst_bits[x] = src_bits[c]; src_bits += bytespp; } } return dst; } return NULL; }
bool psdColourModeData::FillPalette(FIBITMAP *dib) { RGBQUAD *pal = FreeImage_GetPalette(dib); if(pal) { for (int i = 0; i < 256; i++) { pal[i].rgbRed = _plColourData[i + 0*256]; pal[i].rgbGreen = _plColourData[i + 1*256]; pal[i].rgbBlue = _plColourData[i + 2*256]; } return true; } return false; }
const QVector<QRgb> ImageLoaderFreeImage::colorTable() const { QVector<QRgb> result; const RGBQUAD* const palette = FreeImage_GetPalette(m_bitmap); if (palette) { const int count = FreeImage_GetColorsUsed(m_bitmap); result.resize(count); for (int i = 0; i < count; i++) result.replace(i, qRgb(palette[i].rgbRed, palette[i].rgbGreen, palette[i].rgbBlue)); } return result; }
FIBITMAP * gifcat(char gifpath[MAXGIFCOUNT][MAXPATHCHAR], unsigned gifPathCount) // 将多张图片连接为一张图片 { FIBITMAP * hImageAll ; unsigned ImgHeightAll = 0 ; FIBITMAP * hImage[MAXGIFCOUNT] ; unsigned ImgHeight[MAXGIFCOUNT] ; unsigned ImgWidth ; RGBQUAD * palSrc ; RGBQUAD * palAll ; unsigned n , NowYPos = 0 ; // --- for ( n=0; n < gifPathCount; ++n) { hImage[n] = FreeImage_Load(FIF_GIF, gifpath[n], 0); ImgHeight[n] = FreeImage_GetHeight(hImage[n]) ; ImgHeightAll += ImgHeight[n] ; } ImgWidth = FreeImage_GetWidth(hImage[0]) ; hImageAll = FreeImage_Allocate(ImgWidth, ImgHeightAll, 8, 0, 0, 0); //创建目标图像 palAll = FreeImage_GetPalette(hImageAll); // 复制调色板 palSrc = FreeImage_GetPalette(hImage[0]); for (n = 0; n < 256; ++n) { palAll[n].rgbRed = palSrc[n].rgbRed ; palAll[n].rgbGreen = palSrc[n].rgbGreen ; palAll[n].rgbBlue = palSrc[n].rgbBlue ; } palAll[70].rgbRed = 255 ; palAll[70].rgbGreen = 255 ; palAll[70].rgbBlue = 255 ; for ( n=0; n < gifPathCount; ++n) { // 粘贴图像 FreeImage_Paste(hImageAll, hImage[n], 0, NowYPos, 300) ; NowYPos += ImgHeight[n] ; FreeImage_Unload(hImage[n]) ; } return hImageAll ; }
HANDLE fipWinImage::copyToHandle() const { HANDLE hMem = NULL; if(_dib) { // Get equivalent DIB size long dib_size = sizeof(BITMAPINFOHEADER); dib_size += FreeImage_GetColorsUsed(_dib) * sizeof(RGBQUAD); dib_size += FreeImage_GetPitch(_dib) * FreeImage_GetHeight(_dib); // Allocate a DIB hMem = GlobalAlloc(GHND, dib_size); BYTE *dib = (BYTE*)GlobalLock(hMem); memset(dib, 0, dib_size); BYTE *p_dib = (BYTE*)dib; // Copy the BITMAPINFOHEADER BITMAPINFOHEADER *bih = FreeImage_GetInfoHeader(_dib); memcpy(p_dib, bih, sizeof(BITMAPINFOHEADER)); if(FreeImage_GetImageType(_dib) != FIT_BITMAP) { // this hack is used to store the bitmap type in the biCompression member of the BITMAPINFOHEADER SET_FREEIMAGE_MARKER((BITMAPINFOHEADER*)p_dib, _dib); } p_dib += sizeof(BITMAPINFOHEADER); // Copy the palette RGBQUAD *pal = FreeImage_GetPalette(_dib); memcpy(p_dib, pal, FreeImage_GetColorsUsed(_dib) * sizeof(RGBQUAD)); p_dib += FreeImage_GetColorsUsed(_dib) * sizeof(RGBQUAD); // Copy the bitmap BYTE *bits = FreeImage_GetBits(_dib); memcpy(p_dib, bits, FreeImage_GetPitch(_dib) * FreeImage_GetHeight(_dib)); GlobalUnlock(hMem); } return hMem; }
/** @brief Determines, whether a palletized image is visually greyscale or not. Unlike with FreeImage_GetColorType, which returns either FIC_MINISBLACK or FIC_MINISWHITE for a greyscale image with a linear ramp palette, the return value of this function does not depend on the palette's order, but only on the palette's individual colors. @param dib The image to be tested. @return Returns TRUE if the palette of the image specified contains only greyscales, FALSE otherwise. */ static BOOL IsVisualGreyscaleImage(FIBITMAP *dib) { switch (FreeImage_GetBPP(dib)) { case 1: case 4: case 8: { unsigned ncolors = FreeImage_GetColorsUsed(dib); RGBQUAD *rgb = FreeImage_GetPalette(dib); for (unsigned i = 0; i< ncolors; i++) { if ((rgb->rgbRed != rgb->rgbGreen) || (rgb->rgbRed != rgb->rgbBlue)) { return FALSE; } } return TRUE; } default: { return (FreeImage_GetColorType(dib) == FIC_MINISBLACK); } } }
/** Create a Zone Plate test pattern. The circular zone plate has zero horizontal and vertical frequencies at the center. Horizontal frequencies increase as you move horizontally, and vertical frequencies increase as you move vertically. These patterns are useful to: <ul> <li> evaluate image compression <li> evaluate filter properties <li> evaluate the quality of resampling algorithms <li> adjust gamma correction - at the proper gamma setting, the møires should be minimized </ul> Reference: [1] Ken Turkowski, Open Source Repository. [Online] http://www.worldserver.com/turk/opensource/ */ FIBITMAP* createZonePlateImage(unsigned width, unsigned height, int scale) { const double PI = 3.1415926535898; BYTE sinTab[256]; FIBITMAP *dst; int i, j, x, y; int cX, cY, d; // allocate a 8-bit dib dst = FreeImage_Allocate(width, height, 8); if(!dst) return NULL; // build a greyscale palette RGBQUAD *pal = FreeImage_GetPalette(dst); for(i = 0; i < 256; i++) { pal[i].rgbRed = i; pal[i].rgbGreen = i; pal[i].rgbBlue = i; } // build the sinus table for(i = 0; i < 256; i++) { sinTab[i] = (BYTE)(127.5 * sin(PI * (i - 127.5) / 127.5) + 127.5); } cX = width / 2; cY = height / 2; // create a zone plate for(i = height, y = -cY; i--; y++) { BYTE *dst_bits = FreeImage_GetScanLine(dst, i); for (j = width, x = -cX; j--; x++) { d = ((x * x + y * y) * scale) >> 8; dst_bits[j] = sinTab[d & 0xFF]; } } return dst; }
//---------------------------------------------------- FIBITMAP * getBmpFromPixels(ofPixels &pix){ FIBITMAP * bmp = NULL; int w = pix.getWidth(); int h = pix.getHeight(); unsigned char * pixels = pix.getPixels(); int bpp = pix.getBitsPerPixel(); int bytesPerPixel = pix.getBytesPerPixel(); bmp = FreeImage_ConvertFromRawBits(pixels, w,h, w*bytesPerPixel, bpp, 0,0,0, true); //this is for grayscale images they need to be paletted from: http://sourceforge.net/forum/message.php?msg_id=2856879 if( pix.getImageType() == OF_IMAGE_GRAYSCALE ){ RGBQUAD *pal = FreeImage_GetPalette(bmp); for(int i = 0; i < 256; i++) { pal[i].rgbRed = i; pal[i].rgbGreen = i; pal[i].rgbBlue = i; } } return bmp; }
ColorQuantize(PClip originClip, int paletteSize, bool useGlobalPalette, FREE_IMAGE_QUANTIZE algorithm, const char *globalPaletteOutputFile, IScriptEnvironment* env) : m_origin(originClip) , m_paletteSize(paletteSize) , m_useGlobalPalette(useGlobalPalette) , m_algorithm(algorithm) , m_targetVideoInfo(originClip->GetVideoInfo()) , m_globalPalette(0) { if (!originClip->GetVideoInfo().IsRGB24()) { m_originRgb = env->Invoke("ConvertToRgb24", originClip).AsClip(); m_targetVideoInfo.pixel_type = VideoInfo::CS_BGR24; } else { m_originRgb = originClip; } if (m_useGlobalPalette) { FIBITMAP *hugeImage = FreeImage_Allocate(m_targetVideoInfo.width, m_targetVideoInfo.height * m_targetVideoInfo.num_frames, 24); for (int frame = 0; frame < m_targetVideoInfo.num_frames; ++frame) { const PVideoFrame videoFrame = m_originRgb->GetFrame(frame, env); copyVideoFrameToImage(m_originRgb->GetFrame(frame, env),hugeImage, frame * m_targetVideoInfo.height); } FIBITMAP *quantizedImage = FreeImage_ColorQuantizeEx(hugeImage, algorithm, m_paletteSize); FreeImage_Unload(hugeImage); m_globalPalette = new RGBQUAD[m_paletteSize]; memcpy(m_globalPalette, FreeImage_GetPalette(quantizedImage), m_paletteSize * sizeof(RGBQUAD)); FreeImage_Unload(quantizedImage); if (globalPaletteOutputFile) savePaletteImage(globalPaletteOutputFile, m_globalPalette, m_paletteSize); } }
BOOL fipWinImage::copyFromHandle(HANDLE hMem) { BYTE *lpVoid = NULL; BITMAPINFOHEADER *pHead = NULL; RGBQUAD *pPalette = NULL; BYTE *bits = NULL; DWORD bitfields[3] = {0, 0, 0}; // Get a pointer to the bitmap lpVoid = (BYTE *)GlobalLock(hMem); // Get a pointer to the bitmap header pHead = (BITMAPINFOHEADER *)lpVoid; // Get a pointer to the palette if(pHead->biBitCount < 16) pPalette = (RGBQUAD *)(((BYTE *)pHead) + sizeof(BITMAPINFOHEADER)); // Get a pointer to the pixels bits = ((BYTE*)pHead + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * pHead->biClrUsed); if(pHead->biCompression == BI_BITFIELDS) { // Take into account the color masks that specify the red, green, and blue components (16- and 32-bit) unsigned mask_size = 3 * sizeof(DWORD); memcpy(&bitfields[0], bits, mask_size); bits += mask_size; } if(lpVoid) { // Allocate a new FIBITMAP FREE_IMAGE_TYPE image_type = FIT_BITMAP; // Use a hack to decide if the clipboard contains non standard bitmaps ... switch(GET_FREEIMAGE_MARKER(pHead)) { case FIT_UINT16: case FIT_INT16: case FIT_UINT32: case FIT_INT32: case FIT_FLOAT: case FIT_DOUBLE: case FIT_COMPLEX: case FIT_RGB16: case FIT_RGBA16: case FIT_RGBF: case FIT_RGBAF: image_type = GET_FREEIMAGE_MARKER(pHead); break; } if(!setSize(image_type, (WORD)pHead->biWidth, (WORD)pHead->biHeight, pHead->biBitCount, bitfields[2], bitfields[1], bitfields[0])) { GlobalUnlock(lpVoid); return FALSE; } // Copy the bitmap header memcpy(FreeImage_GetInfoHeader(_dib), pHead, sizeof(BITMAPINFOHEADER)); // Copy the palette memcpy(FreeImage_GetPalette(_dib), pPalette, pHead->biClrUsed * sizeof(RGBQUAD)); // Copy the bitmap memcpy(FreeImage_GetBits(_dib), bits, FreeImage_GetPitch(_dib) * FreeImage_GetHeight(_dib)); GlobalUnlock(lpVoid); return TRUE; } return FALSE; }
/** @brief Composite a foreground image against a background color or a background image. The equation for computing a composited sample value is:<br> output = alpha * foreground + (1-alpha) * background<br> where alpha and the input and output sample values are expressed as fractions in the range 0 to 1. For colour images, the computation is done separately for R, G, and B samples. @param fg Foreground image @param useFileBkg If TRUE and a file background is present, use it as the background color @param appBkColor If not equal to NULL, and useFileBkg is FALSE, use this color as the background color @param bg If not equal to NULL and useFileBkg is FALSE and appBkColor is NULL, use this as the background image @return Returns the composite image if successful, returns NULL otherwise @see FreeImage_IsTransparent, FreeImage_HasBackgroundColor */ FIBITMAP * DLL_CALLCONV FreeImage_Composite(FIBITMAP *fg, BOOL useFileBkg, RGBQUAD *appBkColor, FIBITMAP *bg) { if(!FreeImage_HasPixels(fg)) return NULL; int width = FreeImage_GetWidth(fg); int height = FreeImage_GetHeight(fg); int bpp = FreeImage_GetBPP(fg); if((bpp != 8) && (bpp != 32)) return NULL; if(bg) { int bg_width = FreeImage_GetWidth(bg); int bg_height = FreeImage_GetHeight(bg); int bg_bpp = FreeImage_GetBPP(bg); if((bg_width != width) || (bg_height != height) || (bg_bpp != 24)) return NULL; } int bytespp = (bpp == 8) ? 1 : 4; int x, y, c; BYTE alpha = 0, not_alpha; BYTE index; RGBQUAD fgc; // foreground color RGBQUAD bkc; // background color memset(&fgc, 0, sizeof(RGBQUAD)); memset(&bkc, 0, sizeof(RGBQUAD)); // allocate the composite image FIBITMAP *composite = FreeImage_Allocate(width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); if(!composite) return NULL; // get the palette RGBQUAD *pal = FreeImage_GetPalette(fg); // retrieve the alpha table from the foreground image BOOL bIsTransparent = FreeImage_IsTransparent(fg); BYTE *trns = FreeImage_GetTransparencyTable(fg); // retrieve the background color from the foreground image BOOL bHasBkColor = FALSE; if(useFileBkg && FreeImage_HasBackgroundColor(fg)) { FreeImage_GetBackgroundColor(fg, &bkc); bHasBkColor = TRUE; } else { // no file background color // use application background color ? if(appBkColor) { memcpy(&bkc, appBkColor, sizeof(RGBQUAD)); bHasBkColor = TRUE; } // use background image ? else if(bg) { bHasBkColor = FALSE; } } for(y = 0; y < height; y++) { // foreground BYTE *fg_bits = FreeImage_GetScanLine(fg, y); // background BYTE *bg_bits = FreeImage_GetScanLine(bg, y); // composite image BYTE *cp_bits = FreeImage_GetScanLine(composite, y); for(x = 0; x < width; x++) { // foreground color + alpha if(bpp == 8) { // get the foreground color index = fg_bits[0]; memcpy(&fgc, &pal[index], sizeof(RGBQUAD)); // get the alpha if(bIsTransparent) { alpha = trns[index]; } else { alpha = 255; } } else if(bpp == 32) { // get the foreground color fgc.rgbBlue = fg_bits[FI_RGBA_BLUE]; fgc.rgbGreen = fg_bits[FI_RGBA_GREEN]; fgc.rgbRed = fg_bits[FI_RGBA_RED]; // get the alpha alpha = fg_bits[FI_RGBA_ALPHA]; } // background color if(!bHasBkColor) { if(bg) { // get the background color from the background image bkc.rgbBlue = bg_bits[FI_RGBA_BLUE]; bkc.rgbGreen = bg_bits[FI_RGBA_GREEN]; bkc.rgbRed = bg_bits[FI_RGBA_RED]; } else { // use a checkerboard pattern c = (((y & 0x8) == 0) ^ ((x & 0x8) == 0)) * 192; c = c ? c : 255; bkc.rgbBlue = (BYTE)c; bkc.rgbGreen = (BYTE)c; bkc.rgbRed = (BYTE)c; } } // composition if(alpha == 0) { // output = background cp_bits[FI_RGBA_BLUE] = bkc.rgbBlue; cp_bits[FI_RGBA_GREEN] = bkc.rgbGreen; cp_bits[FI_RGBA_RED] = bkc.rgbRed; } else if(alpha == 255) { // output = foreground cp_bits[FI_RGBA_BLUE] = fgc.rgbBlue; cp_bits[FI_RGBA_GREEN] = fgc.rgbGreen; cp_bits[FI_RGBA_RED] = fgc.rgbRed; } else { // output = alpha * foreground + (1-alpha) * background not_alpha = (BYTE)~alpha; cp_bits[FI_RGBA_BLUE] = (BYTE)((alpha * (WORD)fgc.rgbBlue + not_alpha * (WORD)bkc.rgbBlue) >> 8); cp_bits[FI_RGBA_GREEN] = (BYTE)((alpha * (WORD)fgc.rgbGreen + not_alpha * (WORD)bkc.rgbGreen) >> 8); cp_bits[FI_RGBA_RED] = (BYTE)((alpha * (WORD)fgc.rgbRed + not_alpha * (WORD)bkc.rgbRed) >> 8); } fg_bits += bytespp; bg_bits += 3; cp_bits += 3; } } // copy metadata from src to dst FreeImage_CloneMetadata(composite, fg); return composite; }
static BOOL DLL_CALLCONV Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { png_structp png_ptr; png_infop info_ptr; png_colorp palette = NULL; png_uint_32 width, height; BOOL has_alpha_channel = FALSE; RGBQUAD *pal; // pointer to dib palette int bit_depth, pixel_depth; // pixel_depth = bit_depth * channels int palette_entries; int interlace_type; fi_ioStructure fio; fio.s_handle = handle; fio.s_io = io; if ((dib) && (handle)) { try { // create the chunk manage structure png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, error_handler, warning_handler); if (!png_ptr) { return FALSE; } // allocate/initialize the image information data. info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return FALSE; } // Set error handling. REQUIRED if you aren't supplying your own // error handling functions in the png_create_write_struct() call. if (setjmp(png_jmpbuf(png_ptr))) { // if we get here, we had a problem reading the file png_destroy_write_struct(&png_ptr, &info_ptr); return FALSE; } // init the IO png_set_write_fn(png_ptr, &fio, _WriteProc, _FlushProc); // set physical resolution png_uint_32 res_x = (png_uint_32)FreeImage_GetDotsPerMeterX(dib); png_uint_32 res_y = (png_uint_32)FreeImage_GetDotsPerMeterY(dib); if ((res_x > 0) && (res_y > 0)) { png_set_pHYs(png_ptr, info_ptr, res_x, res_y, PNG_RESOLUTION_METER); } // Set the image information here. Width and height are up to 2^31, // bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on // the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, // PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, // or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or // PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST // currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED width = FreeImage_GetWidth(dib); height = FreeImage_GetHeight(dib); pixel_depth = FreeImage_GetBPP(dib); BOOL bInterlaced = FALSE; if( (flags & PNG_INTERLACED) == PNG_INTERLACED) { interlace_type = PNG_INTERLACE_ADAM7; bInterlaced = TRUE; } else { interlace_type = PNG_INTERLACE_NONE; } // set the ZLIB compression level or default to PNG default compression level (ZLIB level = 6) int zlib_level = flags & 0x0F; if((zlib_level >= 1) && (zlib_level <= 9)) { png_set_compression_level(png_ptr, zlib_level); } else if((flags & PNG_Z_NO_COMPRESSION) == PNG_Z_NO_COMPRESSION) { png_set_compression_level(png_ptr, Z_NO_COMPRESSION); } // filtered strategy works better for high color images if(pixel_depth >= 16){ png_set_compression_strategy(png_ptr, Z_FILTERED); png_set_filter(png_ptr, 0, PNG_FILTER_NONE|PNG_FILTER_SUB|PNG_FILTER_PAETH); } else { png_set_compression_strategy(png_ptr, Z_DEFAULT_STRATEGY); } FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib); if(image_type == FIT_BITMAP) { // standard image type bit_depth = (pixel_depth > 8) ? 8 : pixel_depth; } else { // 16-bit greyscale or 16-bit RGB(A) bit_depth = 16; } switch (FreeImage_GetColorType(dib)) { case FIC_MINISWHITE: // Invert monochrome files to have 0 as black and 1 as white (no break here) png_set_invert_mono(png_ptr); case FIC_MINISBLACK: png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_GRAY, interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); break; case FIC_PALETTE: { png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_PALETTE, interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); // set the palette palette_entries = 1 << bit_depth; palette = (png_colorp)png_malloc(png_ptr, palette_entries * sizeof (png_color)); pal = FreeImage_GetPalette(dib); for (int i = 0; i < palette_entries; i++) { palette[i].red = pal[i].rgbRed; palette[i].green = pal[i].rgbGreen; palette[i].blue = pal[i].rgbBlue; } png_set_PLTE(png_ptr, info_ptr, palette, palette_entries); // You must not free palette here, because png_set_PLTE only makes a link to // the palette that you malloced. Wait until you are about to destroy // the png structure. break; } case FIC_RGBALPHA : has_alpha_channel = TRUE; png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_RGBA, interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // flip BGR pixels to RGB if(image_type == FIT_BITMAP) { png_set_bgr(png_ptr); } #endif break; case FIC_RGB: png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_RGB, interlace_type, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // flip BGR pixels to RGB if(image_type == FIT_BITMAP) { png_set_bgr(png_ptr); } #endif break; case FIC_CMYK: break; } // write possible ICC profile FIICCPROFILE *iccProfile = FreeImage_GetICCProfile(dib); if (iccProfile->size && iccProfile->data) { png_set_iCCP(png_ptr, info_ptr, "Embedded Profile", 0, (png_const_bytep)iccProfile->data, iccProfile->size); } // write metadata WriteMetadata(png_ptr, info_ptr, dib); // Optional gamma chunk is strongly suggested if you have any guess // as to the correct gamma of the image. // png_set_gAMA(png_ptr, info_ptr, gamma); // set the transparency table if (FreeImage_IsTransparent(dib) && (FreeImage_GetTransparencyCount(dib) > 0)) { png_set_tRNS(png_ptr, info_ptr, FreeImage_GetTransparencyTable(dib), FreeImage_GetTransparencyCount(dib), NULL); } // set the background color if(FreeImage_HasBackgroundColor(dib)) { png_color_16 image_background; RGBQUAD rgbBkColor; FreeImage_GetBackgroundColor(dib, &rgbBkColor); memset(&image_background, 0, sizeof(png_color_16)); image_background.blue = rgbBkColor.rgbBlue; image_background.green = rgbBkColor.rgbGreen; image_background.red = rgbBkColor.rgbRed; image_background.index = rgbBkColor.rgbReserved; png_set_bKGD(png_ptr, info_ptr, &image_background); } // Write the file header information. png_write_info(png_ptr, info_ptr); // write out the image data #ifndef FREEIMAGE_BIGENDIAN if (bit_depth == 16) { // turn on 16 bit byte swapping png_set_swap(png_ptr); } #endif int number_passes = 1; if (bInterlaced) { number_passes = png_set_interlace_handling(png_ptr); } if ((pixel_depth == 32) && (!has_alpha_channel)) { BYTE *buffer = (BYTE *)malloc(width * 3); // transparent conversion to 24-bit // the number of passes is either 1 for non-interlaced images, or 7 for interlaced images for (int pass = 0; pass < number_passes; pass++) { for (png_uint_32 k = 0; k < height; k++) { FreeImage_ConvertLine32To24(buffer, FreeImage_GetScanLine(dib, height - k - 1), width); png_write_row(png_ptr, buffer); } } free(buffer); } else { // the number of passes is either 1 for non-interlaced images, or 7 for interlaced images for (int pass = 0; pass < number_passes; pass++) { for (png_uint_32 k = 0; k < height; k++) { png_write_row(png_ptr, FreeImage_GetScanLine(dib, height - k - 1)); } } } // It is REQUIRED to call this to finish writing the rest of the file // Bug with png_flush png_write_end(png_ptr, info_ptr); // clean up after the write, and free any memory allocated if (palette) { png_free(png_ptr, palette); } png_destroy_write_struct(&png_ptr, &info_ptr); return TRUE; } catch (const char *text) { FreeImage_OutputMessageProc(s_format_id, text); } } return FALSE; }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { png_structp png_ptr = NULL; png_infop info_ptr; png_uint_32 width, height; png_colorp png_palette = NULL; int color_type, palette_entries = 0; int bit_depth, pixel_depth; // pixel_depth = bit_depth * channels FIBITMAP *dib = NULL; RGBQUAD *palette = NULL; // pointer to dib palette png_bytepp row_pointers = NULL; int i; fi_ioStructure fio; fio.s_handle = handle; fio.s_io = io; if (handle) { BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; try { // check to see if the file is in fact a PNG file BYTE png_check[PNG_BYTES_TO_CHECK]; io->read_proc(png_check, PNG_BYTES_TO_CHECK, 1, handle); if (png_sig_cmp(png_check, (png_size_t)0, PNG_BYTES_TO_CHECK) != 0) { return NULL; // Bad signature } // create the chunk manage structure png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, error_handler, warning_handler); if (!png_ptr) { return NULL; } // create the info structure info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return NULL; } // init the IO png_set_read_fn(png_ptr, &fio, _ReadProc); if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return NULL; } // because we have already read the signature... png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK); // read the IHDR chunk png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); pixel_depth = png_get_bit_depth(png_ptr, info_ptr) * png_get_channels(png_ptr, info_ptr); // get image data type (assume standard image type) FREE_IMAGE_TYPE image_type = FIT_BITMAP; if (bit_depth == 16) { if ((pixel_depth == 16) && (color_type == PNG_COLOR_TYPE_GRAY)) { image_type = FIT_UINT16; } else if ((pixel_depth == 48) && (color_type == PNG_COLOR_TYPE_RGB)) { image_type = FIT_RGB16; } else if ((pixel_depth == 64) && (color_type == PNG_COLOR_TYPE_RGB_ALPHA)) { image_type = FIT_RGBA16; } else { // tell libpng to strip 16 bit/color files down to 8 bits/color png_set_strip_16(png_ptr); bit_depth = 8; } } #ifndef FREEIMAGE_BIGENDIAN if((image_type == FIT_UINT16) || (image_type == FIT_RGB16) || (image_type == FIT_RGBA16)) { // turn on 16 bit byte swapping png_set_swap(png_ptr); } #endif // set some additional flags switch(color_type) { case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // flip the RGB pixels to BGR (or RGBA to BGRA) if(image_type == FIT_BITMAP) { png_set_bgr(png_ptr); } #endif break; case PNG_COLOR_TYPE_PALETTE: // expand palette images to the full 8 bits from 2 bits/pixel if (pixel_depth == 2) { png_set_packing(png_ptr); pixel_depth = 8; } break; case PNG_COLOR_TYPE_GRAY: // expand grayscale images to the full 8 bits from 2 bits/pixel // but *do not* expand fully transparent palette entries to a full alpha channel if (pixel_depth == 2) { png_set_expand_gray_1_2_4_to_8(png_ptr); pixel_depth = 8; } break; case PNG_COLOR_TYPE_GRAY_ALPHA: // expand 8-bit greyscale + 8-bit alpha to 32-bit png_set_gray_to_rgb(png_ptr); #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR // flip the RGBA pixels to BGRA png_set_bgr(png_ptr); #endif pixel_depth = 32; break; default: throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } // unlike the example in the libpng documentation, we have *no* idea where // this file may have come from--so if it doesn't have a file gamma, don't // do any correction ("do no harm") if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) { double gamma = 0; double screen_gamma = 2.2; if (png_get_gAMA(png_ptr, info_ptr, &gamma) && ( flags & PNG_IGNOREGAMMA ) != PNG_IGNOREGAMMA) { png_set_gamma(png_ptr, screen_gamma, gamma); } } // all transformations have been registered; now update info_ptr data png_read_update_info(png_ptr, info_ptr); // color type may have changed, due to our transformations color_type = png_get_color_type(png_ptr,info_ptr); // create a DIB and write the bitmap header // set up the DIB palette, if needed switch (color_type) { case PNG_COLOR_TYPE_RGB: png_set_invert_alpha(png_ptr); if(image_type == FIT_BITMAP) { dib = FreeImage_AllocateHeader(header_only, width, height, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } else { dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth); } break; case PNG_COLOR_TYPE_RGB_ALPHA: if(image_type == FIT_BITMAP) { dib = FreeImage_AllocateHeader(header_only, width, height, 32, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } else { dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth); } break; case PNG_COLOR_TYPE_PALETTE: dib = FreeImage_AllocateHeader(header_only, width, height, pixel_depth); png_get_PLTE(png_ptr,info_ptr, &png_palette, &palette_entries); palette_entries = MIN((unsigned)palette_entries, FreeImage_GetColorsUsed(dib)); palette = FreeImage_GetPalette(dib); // store the palette for (i = 0; i < palette_entries; i++) { palette[i].rgbRed = png_palette[i].red; palette[i].rgbGreen = png_palette[i].green; palette[i].rgbBlue = png_palette[i].blue; } break; case PNG_COLOR_TYPE_GRAY: dib = FreeImage_AllocateHeaderT(header_only, image_type, width, height, pixel_depth); if(pixel_depth <= 8) { palette = FreeImage_GetPalette(dib); palette_entries = 1 << pixel_depth; for (i = 0; i < palette_entries; i++) { palette[i].rgbRed = palette[i].rgbGreen = palette[i].rgbBlue = (BYTE)((i * 255) / (palette_entries - 1)); } } break; default: throw FI_MSG_ERROR_UNSUPPORTED_FORMAT; } // store the transparency table if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { // array of alpha (transparency) entries for palette png_bytep trans_alpha = NULL; // number of transparent entries int num_trans = 0; // graylevel or color sample values of the single transparent color for non-paletted images png_color_16p trans_color = NULL; png_get_tRNS(png_ptr, info_ptr, &trans_alpha, &num_trans, &trans_color); if((color_type == PNG_COLOR_TYPE_GRAY) && trans_color) { // single transparent color if (trans_color->gray < palette_entries) { BYTE table[256]; memset(table, 0xFF, palette_entries); table[trans_color->gray] = 0; FreeImage_SetTransparencyTable(dib, table, palette_entries); } } else if((color_type == PNG_COLOR_TYPE_PALETTE) && trans_alpha) { // transparency table FreeImage_SetTransparencyTable(dib, (BYTE *)trans_alpha, num_trans); } } // store the background color if (png_get_valid(png_ptr, info_ptr, PNG_INFO_bKGD)) { // Get the background color to draw transparent and alpha images over. // Note that even if the PNG file supplies a background, you are not required to // use it - you should use the (solid) application background if it has one. png_color_16p image_background = NULL; RGBQUAD rgbBkColor; if (png_get_bKGD(png_ptr, info_ptr, &image_background)) { rgbBkColor.rgbRed = (BYTE)image_background->red; rgbBkColor.rgbGreen = (BYTE)image_background->green; rgbBkColor.rgbBlue = (BYTE)image_background->blue; rgbBkColor.rgbReserved = 0; FreeImage_SetBackgroundColor(dib, &rgbBkColor); } } // get physical resolution if (png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) { png_uint_32 res_x, res_y; // we'll overload this var and use 0 to mean no phys data, // since if it's not in meters we can't use it anyway int res_unit_type = PNG_RESOLUTION_UNKNOWN; png_get_pHYs(png_ptr,info_ptr, &res_x, &res_y, &res_unit_type); if (res_unit_type == PNG_RESOLUTION_METER) { FreeImage_SetDotsPerMeterX(dib, res_x); FreeImage_SetDotsPerMeterY(dib, res_y); } } // get possible ICC profile if (png_get_valid(png_ptr, info_ptr, PNG_INFO_iCCP)) { png_charp profile_name = NULL; png_bytep profile_data = NULL; png_uint_32 profile_length = 0; int compression_type; png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &profile_length); // copy ICC profile data (must be done after FreeImage_AllocateHeader) FreeImage_CreateICCProfile(dib, profile_data, profile_length); } // --- header only mode => clean-up and return if (header_only) { // get possible metadata (it can be located both before and after the image data) ReadMetadata(png_ptr, info_ptr, dib); if (png_ptr) { // clean up after the read, and free any memory allocated - REQUIRED png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); } return dib; } // set the individual row_pointers to point at the correct offsets row_pointers = (png_bytepp)malloc(height * sizeof(png_bytep)); if (!row_pointers) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); FreeImage_Unload(dib); return NULL; } // read in the bitmap bits via the pointer table // allow loading of PNG with minor errors (such as images with several IDAT chunks) for (png_uint_32 k = 0; k < height; k++) { row_pointers[height - 1 - k] = FreeImage_GetScanLine(dib, k); } png_set_benign_errors(png_ptr, 1); png_read_image(png_ptr, row_pointers); // check if the bitmap contains transparency, if so enable it in the header if (FreeImage_GetBPP(dib) == 32) { if (FreeImage_GetColorType(dib) == FIC_RGBALPHA) { FreeImage_SetTransparent(dib, TRUE); } else { FreeImage_SetTransparent(dib, FALSE); } } // cleanup if (row_pointers) { free(row_pointers); row_pointers = NULL; } // read the rest of the file, getting any additional chunks in info_ptr png_read_end(png_ptr, info_ptr); // get possible metadata (it can be located both before and after the image data) ReadMetadata(png_ptr, info_ptr, dib); if (png_ptr) { // clean up after the read, and free any memory allocated - REQUIRED png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); } return dib; } catch (const char *text) { if (png_ptr) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); } if (row_pointers) { free(row_pointers); } if (dib) { FreeImage_Unload(dib); } FreeImage_OutputMessageProc(s_format_id, text); return NULL; } } return NULL; }
/** Image translation and rotation using B-Splines. @param dib Input 8-bit greyscale image @param angle Output image rotation in degree @param x_shift Output image horizontal shift @param y_shift Output image vertical shift @param x_origin Output origin of the x-axis @param y_origin Output origin of the y-axis @param spline_degree Output degree of the B-spline model @param use_mask Whether or not to mask the image @return Returns the translated & rotated dib if successful, returns NULL otherwise */ static FIBITMAP * Rotate8Bit(FIBITMAP *dib, double angle, double x_shift, double y_shift, double x_origin, double y_origin, long spline_degree, BOOL use_mask) { double *ImageRasterArray; double p; double a11, a12, a21, a22; double x0, y0, x1, y1; long x, y; long spline; bool bResult; int bpp = FreeImage_GetBPP(dib); if(bpp != 8) { return NULL; } int width = FreeImage_GetWidth(dib); int height = FreeImage_GetHeight(dib); switch(spline_degree) { case ROTATE_QUADRATIC: spline = 2L; // Use splines of degree 2 (quadratic interpolation) break; case ROTATE_CUBIC: spline = 3L; // Use splines of degree 3 (cubic interpolation) break; case ROTATE_QUARTIC: spline = 4L; // Use splines of degree 4 (quartic interpolation) break; case ROTATE_QUINTIC: spline = 5L; // Use splines of degree 5 (quintic interpolation) break; default: spline = 3L; } // allocate output image FIBITMAP *dst = FreeImage_Allocate(width, height, bpp); if(!dst) return NULL; // buid a grey scale palette RGBQUAD *pal = FreeImage_GetPalette(dst); for(int i = 0; i < 256; i++) { pal[i].rgbRed = pal[i].rgbGreen = pal[i].rgbBlue = (BYTE)i; } // allocate a temporary array ImageRasterArray = (double*)malloc(width * height * sizeof(double)); if(!ImageRasterArray) { FreeImage_Unload(dst); return NULL; } // copy data samples for(y = 0; y < height; y++) { double *pImage = &ImageRasterArray[y*width]; BYTE *src_bits = FreeImage_GetScanLine(dib, height-1-y); for(x = 0; x < width; x++) { pImage[x] = (double)src_bits[x]; } } // convert between a representation based on image samples // and a representation based on image B-spline coefficients bResult = SamplesToCoefficients(ImageRasterArray, width, height, spline); if(!bResult) { FreeImage_Unload(dst); free(ImageRasterArray); return NULL; } // prepare the geometry angle *= PI / 180.0; a11 = cos(angle); a12 = -sin(angle); a21 = sin(angle); a22 = cos(angle); x0 = a11 * (x_shift + x_origin) + a12 * (y_shift + y_origin); y0 = a21 * (x_shift + x_origin) + a22 * (y_shift + y_origin); x_shift = x_origin - x0; y_shift = y_origin - y0; // visit all pixels of the output image and assign their value for(y = 0; y < height; y++) { BYTE *dst_bits = FreeImage_GetScanLine(dst, height-1-y); x0 = a12 * (double)y + x_shift; y0 = a22 * (double)y + y_shift; for(x = 0; x < width; x++) { x1 = x0 + a11 * (double)x; y1 = y0 + a21 * (double)x; if(use_mask) { if((x1 <= -0.5) || (((double)width - 0.5) <= x1) || (y1 <= -0.5) || (((double)height - 0.5) <= y1)) { p = 0; } else { p = (double)InterpolatedValue(ImageRasterArray, width, height, x1, y1, spline); } } else { p = (double)InterpolatedValue(ImageRasterArray, width, height, x1, y1, spline); } // clamp and convert to BYTE dst_bits[x] = (BYTE)MIN(MAX((int)0, (int)(p + 0.5)), (int)255); } } // free working array and return free(ImageRasterArray); return dst; }
RGBQUAD* fipImage::getPalette() const { return FreeImage_GetPalette(_dib); }
static FIBITMAP * LoadOS21XBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset) { FIBITMAP *dib = NULL; try { BITMAPINFOOS2_1X_HEADER bios2_1x; io->read_proc(&bios2_1x, sizeof(BITMAPINFOOS2_1X_HEADER), 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapOS21XHeader(&bios2_1x); #endif // keep some general information about the bitmap int used_colors = 0; int width = bios2_1x.biWidth; int height = bios2_1x.biHeight; int bit_count = bios2_1x.biBitCount; int pitch = CalculatePitch(CalculateLine(width, bit_count)); switch (bit_count) { case 1 : case 4 : case 8 : { used_colors = CalculateUsedPaletteEntries(bit_count); // allocate enough memory to hold the bitmap (header, palette, pixels) and read the palette dib = FreeImage_Allocate(width, height, bit_count); if (dib == NULL) throw "DIB allocation failed"; BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib); pInfoHeader->biXPelsPerMeter = 0; pInfoHeader->biYPelsPerMeter = 0; // load the palette RGBQUAD *pal = FreeImage_GetPalette(dib); for (int count = 0; count < used_colors; count++) { FILE_BGR bgr; io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle); pal[count].rgbRed = bgr.r; pal[count].rgbGreen = bgr.g; pal[count].rgbBlue = bgr.b; } // Skip over the optional palette // A 24 or 32 bit DIB may contain a palette for faster color reduction io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); // read the pixel data if (height > 0) { io->read_proc((void *)FreeImage_GetBits(dib), height * pitch, 1, handle); } else { for (int c = 0; c < abs(height); ++c) { io->read_proc((void *)FreeImage_GetScanLine(dib, height - c - 1), pitch, 1, handle); } } return dib; } case 16 : { dib = FreeImage_Allocate(width, height, bit_count, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK); if (dib == NULL) throw "DIB allocation failed"; BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib); pInfoHeader->biXPelsPerMeter = 0; pInfoHeader->biYPelsPerMeter = 0; io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle); #ifdef FREEIMAGE_BIGENDIAN for(int y = 0; y < FreeImage_GetHeight(dib); y++) { WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y); for(int x = 0; x < FreeImage_GetWidth(dib); x++) { SwapShort(pixel); pixel++; } } #endif return dib; } case 24 : case 32 : { if( bit_count == 32 ) { dib = FreeImage_Allocate(width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } else { dib = FreeImage_Allocate(width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } if (dib == NULL) throw "DIB allocation failed"; BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib); pInfoHeader->biXPelsPerMeter = 0; pInfoHeader->biYPelsPerMeter = 0; // Skip over the optional palette // A 24 or 32 bit DIB may contain a palette for faster color reduction io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle); #ifdef FREEIMAGE_BIGENDIAN for(int y = 0; y < FreeImage_GetHeight(dib); y++) { BYTE *pixel = FreeImage_GetScanLine(dib, y); for(int x = 0; x < FreeImage_GetWidth(dib); x++) { INPLACESWAP(pixel[0], pixel[2]); pixel += (bit_count>>3); } } #endif // check if the bitmap contains transparency, if so enable it in the header FreeImage_SetTransparent(dib, (FreeImage_GetColorType(dib) == FIC_RGBALPHA)); return dib; } } } catch(const char *message) { if(dib) FreeImage_Unload(dib); FreeImage_OutputMessageProc(s_format_id, message); } return NULL; }
static FIBITMAP * LoadOS22XBMP(FreeImageIO *io, fi_handle handle, int flags, unsigned bitmap_bits_offset) { FIBITMAP *dib = NULL; try { // load the info header BITMAPINFOHEADER bih; io->read_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapInfoHeader(&bih); #endif // keep some general information about the bitmap int used_colors = bih.biClrUsed; int width = bih.biWidth; int height = bih.biHeight; int bit_count = bih.biBitCount; int compression = bih.biCompression; int pitch = CalculatePitch(CalculateLine(width, bit_count)); switch (bit_count) { case 1 : case 4 : case 8 : { if ((used_colors <= 0) || (used_colors > CalculateUsedPaletteEntries(bit_count))) used_colors = CalculateUsedPaletteEntries(bit_count); // allocate enough memory to hold the bitmap (header, palette, pixels) and read the palette dib = FreeImage_Allocate(width, height, bit_count); if (dib == NULL) throw "DIB allocation failed"; BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib); pInfoHeader->biXPelsPerMeter = bih.biXPelsPerMeter; pInfoHeader->biYPelsPerMeter = bih.biYPelsPerMeter; // load the palette io->seek_proc(handle, sizeof(BITMAPFILEHEADER) + bih.biSize, SEEK_SET); RGBQUAD *pal = FreeImage_GetPalette(dib); for (int count = 0; count < used_colors; count++) { FILE_BGR bgr; io->read_proc(&bgr, sizeof(FILE_BGR), 1, handle); pal[count].rgbRed = bgr.r; pal[count].rgbGreen = bgr.g; pal[count].rgbBlue = bgr.b; } // seek to the actual pixel data. // this is needed because sometimes the palette is larger than the entries it contains predicts if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); // read the pixel data switch (compression) { case BI_RGB : if (height > 0) { io->read_proc((void *)FreeImage_GetBits(dib), height * pitch, 1, handle); } else { for (int c = 0; c < abs(height); ++c) { io->read_proc((void *)FreeImage_GetScanLine(dib, height - c - 1), pitch, 1, handle); } } return dib; case BI_RLE4 : { BYTE status_byte = 0; BYTE second_byte = 0; int scanline = 0; int bits = 0; BOOL low_nibble = FALSE; for (;;) { io->read_proc(&status_byte, sizeof(BYTE), 1, handle); switch (status_byte) { case RLE_COMMAND : io->read_proc(&status_byte, sizeof(BYTE), 1, handle); switch (status_byte) { case RLE_ENDOFLINE : bits = 0; scanline++; low_nibble = FALSE; break; case RLE_ENDOFBITMAP : return (FIBITMAP *)dib; case RLE_DELTA : { // read the delta values BYTE delta_x; BYTE delta_y; io->read_proc(&delta_x, sizeof(BYTE), 1, handle); io->read_proc(&delta_y, sizeof(BYTE), 1, handle); // apply them bits += delta_x / 2; scanline += delta_y; break; } default : io->read_proc(&second_byte, sizeof(BYTE), 1, handle); BYTE *sline = FreeImage_GetScanLine(dib, scanline); for (int i = 0; i < status_byte; i++) { if (low_nibble) { *(sline + bits) |= LOWNIBBLE(second_byte); if (i != status_byte - 1) io->read_proc(&second_byte, sizeof(BYTE), 1, handle); bits++; } else { *(sline + bits) |= HINIBBLE(second_byte); } low_nibble = !low_nibble; } if (((status_byte / 2) & 1 ) == 1) io->read_proc(&second_byte, sizeof(BYTE), 1, handle); break; }; break; default : { BYTE *sline = FreeImage_GetScanLine(dib, scanline); io->read_proc(&second_byte, sizeof(BYTE), 1, handle); for (unsigned i = 0; i < status_byte; i++) { if (low_nibble) { *(sline + bits) |= LOWNIBBLE(second_byte); bits++; } else { *(sline + bits) |= HINIBBLE(second_byte); } low_nibble = !low_nibble; } } break; }; } break; } case BI_RLE8 : { BYTE status_byte = 0; BYTE second_byte = 0; int scanline = 0; int bits = 0; for (;;) { io->read_proc(&status_byte, sizeof(BYTE), 1, handle); switch (status_byte) { case RLE_COMMAND : io->read_proc(&status_byte, sizeof(BYTE), 1, handle); switch (status_byte) { case RLE_ENDOFLINE : bits = 0; scanline++; break; case RLE_ENDOFBITMAP : return (FIBITMAP *)dib; case RLE_DELTA : { // read the delta values BYTE delta_x; BYTE delta_y; io->read_proc(&delta_x, sizeof(BYTE), 1, handle); io->read_proc(&delta_y, sizeof(BYTE), 1, handle); // apply them bits += delta_x; scanline += delta_y; break; } default : io->read_proc((void *)(FreeImage_GetScanLine(dib, scanline) + bits), sizeof(BYTE) * status_byte, 1, handle); // align run length to even number of bytes if ((status_byte & 1) == 1) io->read_proc(&second_byte, sizeof(BYTE), 1, handle); bits += status_byte; break; }; break; default : BYTE *sline = FreeImage_GetScanLine(dib, scanline); io->read_proc(&second_byte, sizeof(BYTE), 1, handle); for (unsigned i = 0; i < status_byte; i++) { *(sline + bits) = second_byte; bits++; } break; }; } break; } default : throw "compression type not supported"; } break; } case 16 : { if (bih.biCompression == 3) { DWORD bitfields[3]; io->read_proc(bitfields, 3 * sizeof(DWORD), 1, handle); dib = FreeImage_Allocate(width, height, bit_count, bitfields[0], bitfields[1], bitfields[2]); } else { dib = FreeImage_Allocate(width, height, bit_count, FI16_555_RED_MASK, FI16_555_GREEN_MASK, FI16_555_BLUE_MASK); } if (dib == NULL) throw "DIB allocation failed"; BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib); pInfoHeader->biXPelsPerMeter = bih.biXPelsPerMeter; pInfoHeader->biYPelsPerMeter = bih.biYPelsPerMeter; if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle); #ifdef FREEIMAGE_BIGENDIAN for(int y = 0; y < FreeImage_GetHeight(dib); y++) { WORD *pixel = (WORD *)FreeImage_GetScanLine(dib, y); for(int x = 0; x < FreeImage_GetWidth(dib); x++) { SwapShort(pixel); pixel++; } } #endif return dib; } case 24 : case 32 : { if( bit_count == 32 ) { dib = FreeImage_Allocate(width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } else { dib = FreeImage_Allocate(width, height, bit_count, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK); } if (dib == NULL) throw "DIB allocation failed"; BITMAPINFOHEADER *pInfoHeader = FreeImage_GetInfoHeader(dib); pInfoHeader->biXPelsPerMeter = bih.biXPelsPerMeter; pInfoHeader->biYPelsPerMeter = bih.biYPelsPerMeter; // Skip over the optional palette // A 24 or 32 bit DIB may contain a palette for faster color reduction if (bitmap_bits_offset > (sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + (used_colors * 3))) io->seek_proc(handle, bitmap_bits_offset, SEEK_SET); // read in the bitmap bits io->read_proc(FreeImage_GetBits(dib), height * pitch, 1, handle); #ifdef FREEIMAGE_BIGENDIAN for(int y = 0; y < FreeImage_GetHeight(dib); y++) { BYTE *pixel = FreeImage_GetScanLine(dib, y); for(int x = 0; x < FreeImage_GetWidth(dib); x++) { INPLACESWAP(pixel[0], pixel[2]); pixel += (bit_count>>3); } } #endif // check if the bitmap contains transparency, if so enable it in the header FreeImage_SetTransparent(dib, (FreeImage_GetColorType(dib) == FIC_RGBALPHA)); return dib; } } } catch(const char *message) { if(dib) FreeImage_Unload(dib); FreeImage_OutputMessageProc(s_format_id, message); } return NULL; }
static BOOL DLL_CALLCONV Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) { if ((dib != NULL) && (handle != NULL)) { // write the file header BITMAPFILEHEADER bitmapfileheader; bitmapfileheader.bfType = 0x4D42; bitmapfileheader.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + FreeImage_GetHeight(dib) * FreeImage_GetPitch(dib); bitmapfileheader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + FreeImage_GetColorsUsed(dib) * sizeof(RGBQUAD); bitmapfileheader.bfReserved1 = 0; bitmapfileheader.bfReserved2 = 0; // take care of the bit fields data of any bool bit_fields = (FreeImage_GetBPP(dib) == 16); if (bit_fields) { bitmapfileheader.bfSize += 3 * sizeof(DWORD); bitmapfileheader.bfOffBits += 3 * sizeof(DWORD); } #ifdef FREEIMAGE_BIGENDIAN SwapFileHeader(&bitmapfileheader); #endif if (io->write_proc(&bitmapfileheader, sizeof(BITMAPFILEHEADER), 1, handle) != 1) return FALSE; // update the bitmap info header BITMAPINFOHEADER bih = *FreeImage_GetInfoHeader(dib); if (bit_fields) bih.biCompression = BI_BITFIELDS; else if ((bih.biBitCount == 8) && (flags & BMP_SAVE_RLE)) bih.biCompression = BI_RLE8; else bih.biCompression = BI_RGB; // write the bitmap info header #ifdef FREEIMAGE_BIGENDIAN SwapInfoHeader(&bih); #endif if (io->write_proc(&bih, sizeof(BITMAPINFOHEADER), 1, handle) != 1) return FALSE; // write the bit fields when we are dealing with a 16 bit BMP if (bit_fields) { DWORD d; d = FreeImage_GetRedMask(dib); if (io->write_proc(&d, sizeof(DWORD), 1, handle) != 1) return FALSE; d = FreeImage_GetGreenMask(dib); if (io->write_proc(&d, sizeof(DWORD), 1, handle) != 1) return FALSE; d = FreeImage_GetBlueMask(dib); if (io->write_proc(&d, sizeof(DWORD), 1, handle) != 1) return FALSE; } // write the palette if (FreeImage_GetPalette(dib) != NULL) { RGBQUAD *pal = FreeImage_GetPalette(dib); FILE_BGRA bgra; for(unsigned i = 0; i < FreeImage_GetColorsUsed(dib); i++ ) { bgra.b = pal[i].rgbBlue; bgra.g = pal[i].rgbGreen; bgra.r = pal[i].rgbRed; bgra.a = pal[i].rgbReserved; if (io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle) != 1) return FALSE; } } // write the bitmap data... if RLE compression is enable, use it unsigned bpp = FreeImage_GetBPP(dib); if ((bpp == 8) && (flags & BMP_SAVE_RLE)) { BYTE *buffer = new BYTE[FreeImage_GetPitch(dib) * 2]; for (DWORD i = 0; i < FreeImage_GetHeight(dib); ++i) { int size = RLEEncodeLine(buffer, FreeImage_GetScanLine(dib, i), FreeImage_GetLine(dib)); if (io->write_proc(buffer, size, 1, handle) != 1) { delete [] buffer; return FALSE; } } buffer[0] = RLE_COMMAND; buffer[1] = RLE_ENDOFBITMAP; if (io->write_proc(buffer, 2, 1, handle) != 1) { delete [] buffer; return FALSE; } delete [] buffer; #ifdef FREEIMAGE_BIGENDIAN } else if (bpp == 16) { WORD pixel; for(int y = 0; y < FreeImage_GetHeight(dib); y++) { BYTE *line = FreeImage_GetScanLine(dib, y); for(int x = 0; x < FreeImage_GetWidth(dib); x++) { pixel = ((WORD *)line)[x]; SwapShort(&pixel); if (io->write_proc(&pixel, sizeof(WORD), 1, handle) != 1) return FALSE; } } } else if (bpp == 24) { FILE_BGR bgr; for(int y = 0; y < FreeImage_GetHeight(dib); y++) { BYTE *line = FreeImage_GetScanLine(dib, y); for(int x = 0; x < FreeImage_GetWidth(dib); x++) { RGBTRIPLE *triple = ((RGBTRIPLE *)line)+x; bgr.b = triple->rgbtBlue; bgr.g = triple->rgbtGreen; bgr.r = triple->rgbtRed; if (io->write_proc(&bgr, sizeof(FILE_BGR), 1, handle) != 1) return FALSE; } } } else if (bpp == 32) { FILE_BGRA bgra; for(int y = 0; y < FreeImage_GetHeight(dib); y++) { BYTE *line = FreeImage_GetScanLine(dib, y); for(int x = 0; x < FreeImage_GetWidth(dib); x++) { RGBQUAD *quad = ((RGBQUAD *)line)+x; bgra.b = quad->rgbBlue; bgra.g = quad->rgbGreen; bgra.r = quad->rgbRed; bgra.a = quad->rgbReserved; if (io->write_proc(&bgra, sizeof(FILE_BGRA), 1, handle) != 1) return FALSE; } } #endif } else if (io->write_proc(FreeImage_GetBits(dib), FreeImage_GetHeight(dib) * FreeImage_GetPitch(dib), 1, handle) != 1) { return FALSE; } return TRUE; } else { return FALSE; } }
FREE_IMAGE_COLOR_TYPE DLL_CALLCONV FreeImage_GetColorType(FIBITMAP *dib) { RGBQUAD *rgb; 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* 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; }
template<class Tsrc> FIBITMAP* CONVERT_TO_BYTE<Tsrc>::convert(FIBITMAP *src, BOOL scale_linear) { FIBITMAP *dst = NULL; unsigned x, y; unsigned width = FreeImage_GetWidth(src); unsigned height = FreeImage_GetHeight(src); // allocate a 8-bit dib dst = FreeImage_AllocateT(FIT_BITMAP, width, height, 8, 0, 0, 0); if(!dst) return NULL; // build a greyscale palette RGBQUAD *pal = FreeImage_GetPalette(dst); for(int i = 0; i < 256; i++) { pal[i].rgbRed = (BYTE)i; pal[i].rgbGreen = (BYTE)i; pal[i].rgbBlue = (BYTE)i; } // convert the src image to dst // (FIBITMAP are stored upside down) if(scale_linear) { Tsrc max, min; double scale; // find the min and max value of the image Tsrc l_min, l_max; min = 255, max = 0; for(y = 0; y < height; y++) { Tsrc *bits = reinterpret_cast<Tsrc*>(FreeImage_GetScanLine(src, y)); MAXMIN(bits, width, l_max, l_min); if(l_max > max) max = l_max; if(l_min < min) min = l_min; } if(max == min) { max = 255; min = 0; } // compute the scaling factor scale = 255 / (double)(max - min); // scale to 8-bit for(y = 0; y < height; y++) { Tsrc *src_bits = reinterpret_cast<Tsrc*>(FreeImage_GetScanLine(src, y)); BYTE *dst_bits = FreeImage_GetScanLine(dst, y); for(x = 0; x < width; x++) { dst_bits[x] = (BYTE)( scale * (src_bits[x] - min) + 0.5); } } } else { for(y = 0; y < height; y++) { Tsrc *src_bits = reinterpret_cast<Tsrc*>(FreeImage_GetScanLine(src, y)); BYTE *dst_bits = FreeImage_GetScanLine(dst, y); for(x = 0; x < width; x++) { // rounding int q = int(src_bits[x] + 0.5); dst_bits[x] = (BYTE) MIN(255, MAX(0, q)); } } } return dst; }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { if( data == NULL ) { return NULL; } GIFinfo *info = (GIFinfo *)data; if( page == -1 ) { page = 0; } if( page < 0 || page >= (int)info->image_descriptor_offsets.size() ) { return NULL; } FIBITMAP *dib = NULL; try { bool have_transparent = false, no_local_palette = false, interlaced = false; int disposal_method = GIF_DISPOSAL_LEAVE, delay_time = 0, transparent_color = 0; WORD left, top, width, height; BYTE packed, b; WORD w; //playback pages to generate what the user would see for this frame if( (flags & GIF_PLAYBACK) == GIF_PLAYBACK ) { //Logical Screen Descriptor io->seek_proc(handle, 6, SEEK_SET); WORD logicalwidth, logicalheight; io->read_proc(&logicalwidth, 2, 1, handle); io->read_proc(&logicalheight, 2, 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapShort(&logicalwidth); SwapShort(&logicalheight); #endif //set the background color with 0 alpha RGBQUAD background; if( info->global_color_table_offset != 0 && info->background_color < info->global_color_table_size ) { io->seek_proc(handle, info->global_color_table_offset + (info->background_color * 3), SEEK_SET); io->read_proc(&background.rgbRed, 1, 1, handle); io->read_proc(&background.rgbGreen, 1, 1, handle); io->read_proc(&background.rgbBlue, 1, 1, handle); } else { background.rgbRed = 0; background.rgbGreen = 0; background.rgbBlue = 0; } background.rgbReserved = 0; //allocate entire logical area dib = FreeImage_Allocate(logicalwidth, logicalheight, 32); if( dib == NULL ) { throw "DIB allocated failed"; } //fill with background color to start int x, y; RGBQUAD *scanline; for( y = 0; y < logicalheight; y++ ) { scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, y); for( x = 0; x < logicalwidth; x++ ) { *scanline++ = background; } } //cache some info about each of the pages so we can avoid decoding as many of them as possible std::vector<PageInfo> pageinfo; int start = page, end = page; while( start >= 0 ) { //Graphic Control Extension io->seek_proc(handle, info->graphic_control_extension_offsets[start] + 1, SEEK_SET); io->read_proc(&packed, 1, 1, handle); have_transparent = (packed & GIF_PACKED_GCE_HAVETRANS) ? true : false; disposal_method = (packed & GIF_PACKED_GCE_DISPOSAL) >> 2; //Image Descriptor io->seek_proc(handle, info->image_descriptor_offsets[page], SEEK_SET); io->read_proc(&left, 2, 1, handle); io->read_proc(&top, 2, 1, handle); io->read_proc(&width, 2, 1, handle); io->read_proc(&height, 2, 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapShort(&left); SwapShort(&top); SwapShort(&width); SwapShort(&height); #endif pageinfo.push_back(PageInfo(disposal_method, left, top, width, height)); if( start != end ) { if( left == 0 && top == 0 && width == logicalwidth && height == logicalheight ) { if( disposal_method == GIF_DISPOSAL_BACKGROUND ) { pageinfo.pop_back(); start++; break; } else if( disposal_method != GIF_DISPOSAL_PREVIOUS ) { if( !have_transparent ) { break; } } } } start--; } if( start < 0 ) { start = 0; } //draw each page into the logical area for( page = start; page <= end; page++ ) { PageInfo &info = pageinfo[end - page]; //things we can skip having to decode if( page != end ) { if( info.disposal_method == GIF_DISPOSAL_PREVIOUS ) { continue; } if( info.disposal_method == GIF_DISPOSAL_BACKGROUND ) { for( y = 0; y < info.height; y++ ) { scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, logicalheight - (y + info.top) - 1) + info.left; for( x = 0; x < info.width; x++ ) { *scanline++ = background; } } } } //decode page FIBITMAP *pagedib = Load(io, handle, page, GIF_LOAD256, data); if( pagedib != NULL ) { RGBQUAD *pal = FreeImage_GetPalette(pagedib); have_transparent = false; if( FreeImage_IsTransparent(pagedib) ) { int count = FreeImage_GetTransparencyCount(pagedib); BYTE *table = FreeImage_GetTransparencyTable(pagedib); for( int i = 0; i < count; i++ ) { if( table[i] == 0 ) { have_transparent = true; transparent_color = i; break; } } } //copy page data into logical buffer, with full alpha opaqueness for( y = 0; y < info.height; y++ ) { scanline = (RGBQUAD *)FreeImage_GetScanLine(dib, logicalheight - (y + info.top) - 1) + info.left; BYTE *pageline = FreeImage_GetScanLine(pagedib, info.height - y - 1); for( x = 0; x < info.width; x++ ) { if( !have_transparent || *pageline != transparent_color ) { *scanline = pal[*pageline]; scanline->rgbReserved = 255; } scanline++; pageline++; } } FreeImage_Unload(pagedib); } } return dib; } //get the actual frame image data for a single frame //Image Descriptor io->seek_proc(handle, info->image_descriptor_offsets[page], SEEK_SET); io->read_proc(&left, 2, 1, handle); io->read_proc(&top, 2, 1, handle); io->read_proc(&width, 2, 1, handle); io->read_proc(&height, 2, 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapShort(&left); SwapShort(&top); SwapShort(&width); SwapShort(&height); #endif io->read_proc(&packed, 1, 1, handle); interlaced = (packed & GIF_PACKED_ID_INTERLACED) ? true : false; no_local_palette = (packed & GIF_PACKED_ID_HAVELCT) ? false : true; int bpp = 8; if( (flags & GIF_LOAD256) == 0 ) { if( !no_local_palette ) { int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE); if( size <= 2 ) bpp = 1; else if( size <= 16 ) bpp = 4; } else if( info->global_color_table_offset != 0 ) { if( info->global_color_table_size <= 2 ) bpp = 1; else if( info->global_color_table_size <= 16 ) bpp = 4; } } dib = FreeImage_Allocate(width, height, bpp); if( dib == NULL ) { throw "DIB allocated failed"; } FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameLeft", ANIMTAG_FRAMELEFT, FIDT_SHORT, 1, 2, &left); FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "FrameTop", ANIMTAG_FRAMETOP, FIDT_SHORT, 1, 2, &top); b = no_local_palette ? 1 : 0; FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "NoLocalPalette", ANIMTAG_NOLOCALPALETTE, FIDT_BYTE, 1, 1, &b); b = interlaced ? 1 : 0; FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "Interlaced", ANIMTAG_INTERLACED, FIDT_BYTE, 1, 1, &b); //Palette RGBQUAD *pal = FreeImage_GetPalette(dib); if( !no_local_palette ) { int size = 2 << (packed & GIF_PACKED_ID_LCTSIZE); int i = 0; while( i < size ) { io->read_proc(&pal[i].rgbRed, 1, 1, handle); io->read_proc(&pal[i].rgbGreen, 1, 1, handle); io->read_proc(&pal[i].rgbBlue, 1, 1, handle); i++; } } else if( info->global_color_table_offset != 0 ) { long pos = io->tell_proc(handle); io->seek_proc(handle, info->global_color_table_offset, SEEK_SET); int i = 0; while( i < info->global_color_table_size ) { io->read_proc(&pal[i].rgbRed, 1, 1, handle); io->read_proc(&pal[i].rgbGreen, 1, 1, handle); io->read_proc(&pal[i].rgbBlue, 1, 1, handle); i++; } io->seek_proc(handle, pos, SEEK_SET); } else { //its legal to have no palette, but we're going to generate *something* for( int i = 0; i < 256; i++ ) { pal[i].rgbRed = i; pal[i].rgbGreen = i; pal[i].rgbBlue = i; } } //LZW Minimum Code Size io->read_proc(&b, 1, 1, handle); StringTable stringtable; stringtable.Initialize(b); //Image Data Sub-blocks int x = 0, xpos = 0, y = 0, shift = 8 - bpp, mask = (1 << bpp) - 1, interlacepass = 0; BYTE *scanline = FreeImage_GetScanLine(dib, height - 1); BYTE buf[1024]; io->read_proc(&b, 1, 1, handle); while( b ) { io->read_proc(stringtable.FillInputBuffer(b), b, 1, handle); int size = sizeof(buf); while( stringtable.Decompress(buf, &size) ) { for( int i = 0; i < size; i++ ) { scanline[xpos] |= (buf[i] & mask) << shift; if( shift > 0 ) { shift -= bpp; } else { xpos++; shift = 8 - bpp; } if( ++x >= width ) { if( interlaced ) { y += g_GifInterlaceIncrement[interlacepass]; if( y >= height && interlacepass < GIF_INTERLACE_PASSES ) { y = g_GifInterlaceOffset[++interlacepass]; } } else { y++; } if( y >= height ) { stringtable.Done(); break; } x = xpos = 0; shift = 8 - bpp; scanline = FreeImage_GetScanLine(dib, height - y - 1); } } size = sizeof(buf); } io->read_proc(&b, 1, 1, handle); } if( page == 0 ) { size_t idx; //Logical Screen Descriptor io->seek_proc(handle, 6, SEEK_SET); WORD logicalwidth, logicalheight; io->read_proc(&logicalwidth, 2, 1, handle); io->read_proc(&logicalheight, 2, 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapShort(&logicalwidth); SwapShort(&logicalheight); #endif FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "LogicalWidth", ANIMTAG_LOGICALWIDTH, FIDT_SHORT, 1, 2, &logicalwidth); FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "LogicalHeight", ANIMTAG_LOGICALHEIGHT, FIDT_SHORT, 1, 2, &logicalheight); //Global Color Table if( info->global_color_table_offset != 0 ) { RGBQUAD globalpalette[256]; io->seek_proc(handle, info->global_color_table_offset, SEEK_SET); int i = 0; while( i < info->global_color_table_size ) { io->read_proc(&globalpalette[i].rgbRed, 1, 1, handle); io->read_proc(&globalpalette[i].rgbGreen, 1, 1, handle); io->read_proc(&globalpalette[i].rgbBlue, 1, 1, handle); globalpalette[i].rgbReserved = 0; i++; } FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "GlobalPalette", ANIMTAG_GLOBALPALETTE, FIDT_PALETTE, info->global_color_table_size, info->global_color_table_size * 4, globalpalette); //background color if( info->background_color < info->global_color_table_size ) { FreeImage_SetBackgroundColor(dib, &globalpalette[info->background_color]); } } //Application Extension LONG loop = 1; //If no AE with a loop count is found, the default must be 1 for( idx = 0; idx < info->application_extension_offsets.size(); idx++ ) { io->seek_proc(handle, info->application_extension_offsets[idx], SEEK_SET); io->read_proc(&b, 1, 1, handle); if( b == 11 ) { //All AEs start with an 11 byte sub-block to determine what type of AE it is char buf[11]; io->read_proc(buf, 11, 1, handle); if( !memcmp(buf, "NETSCAPE2.0", 11) || !memcmp(buf, "ANIMEXTS1.0", 11) ) { //Not everybody recognizes ANIMEXTS1.0 but it is valid io->read_proc(&b, 1, 1, handle); if( b == 3 ) { //we're supposed to have a 3 byte sub-block now io->read_proc(&b, 1, 1, handle); //this should be 0x01 but isn't really important io->read_proc(&w, 2, 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapShort(&w); #endif loop = w; if( loop > 0 ) loop++; break; } } } } FreeImage_SetMetadataEx(FIMD_ANIMATION, dib, "Loop", ANIMTAG_LOOP, FIDT_LONG, 1, 4, &loop); //Comment Extension for( idx = 0; idx < info->comment_extension_offsets.size(); idx++ ) { io->seek_proc(handle, info->comment_extension_offsets[idx], SEEK_SET); std::string comment; char buf[255]; io->read_proc(&b, 1, 1, handle); while( b ) { io->read_proc(buf, b, 1, handle); comment.append(buf, b); io->read_proc(&b, 1, 1, handle); } comment.append(1, '\0'); sprintf(buf, "Comment%d", idx); FreeImage_SetMetadataEx(FIMD_COMMENTS, dib, buf, 1, FIDT_ASCII, comment.size(), comment.size(), comment.c_str()); } } //Graphic Control Extension if( info->graphic_control_extension_offsets[page] != 0 ) { io->seek_proc(handle, info->graphic_control_extension_offsets[page] + 1, SEEK_SET); io->read_proc(&packed, 1, 1, handle); io->read_proc(&w, 2, 1, handle); #ifdef FREEIMAGE_BIGENDIAN SwapShort(&w); #endif io->read_proc(&b, 1, 1, handle); have_transparent = (packed & GIF_PACKED_GCE_HAVETRANS) ? true : false; disposal_method = (packed & GIF_PACKED_GCE_DISPOSAL) >> 2; delay_time = w * 10; //convert cs to ms transparent_color = b; if( have_transparent ) { int size = 1 << bpp; if( transparent_color <= size ) { BYTE table[256]; memset(table, 0xFF, size); table[transparent_color] = 0; FreeImage_SetTransparencyTable(dib, table, size); } } }