FIBITMAP* TargaThumbnail::toFIBITMAP() { if(isNull() || _depth == 0) { return NULL; } const unsigned line_size = _depth * _w / 8; FIBITMAP* dib = FreeImage_Allocate(_w, _h, _depth); if(!dib) { return NULL; } const BYTE* line = _data; const BYTE height = _h; for (BYTE h = 0; h < height; ++h, line += line_size) { BYTE* dst_line = FreeImage_GetScanLine(dib, height - 1 - h); memcpy(dst_line, line, line_size); } #ifdef FREEIMAGE_BIGENDIAN swapShortPixels(dib); #endif #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB SwapRedBlue32(dib); #endif return dib; }
byte* LoadImage(const char* filename, uint* width, uint* height, uint* bits, bool flipY) { FREE_IMAGE_FORMAT fif = FIF_UNKNOWN; FIBITMAP* dib = nullptr; fif = FreeImage_GetFileType(filename, 0); if (fif == FIF_UNKNOWN) fif = FreeImage_GetFIFFromFilename(filename); if (fif == FIF_UNKNOWN) return nullptr; if (FreeImage_FIFSupportsReading(fif)) dib = FreeImage_Load(fif, filename); SP_ASSERT(dib, "Could not load image '", filename, "'!"); FIBITMAP* bitmap = FreeImage_ConvertTo32Bits(dib); FreeImage_Unload(dib); byte* pixels = FreeImage_GetBits(bitmap); uint w = FreeImage_GetWidth(bitmap); uint h = FreeImage_GetHeight(bitmap); uint b = FreeImage_GetBPP(bitmap); if (flipY) FreeImage_FlipVertical(bitmap); if (FreeImage_GetRedMask(bitmap) == 0xff0000) SwapRedBlue32(bitmap); if (width) *width = w; if (height) *height = h; if (bits) *bits = b; int32 size = w * h * (b / 8); byte* result = spnew byte[size]; memcpy(result, pixels, size); FreeImage_Unload(bitmap); return result; }
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; }
int psdThumbnail::Read(FreeImageIO *io, fi_handle handle, int iResourceSize, bool isBGR) { BYTE ShortValue[2], IntValue[4]; int nBytes=0, n; // remove the header size (28 bytes) from the total data size int iTotalData = iResourceSize - 28; const long block_end = io->tell_proc(handle) + iTotalData; n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle); nBytes += n * sizeof(IntValue); _Format = psdGetValue(IntValue, sizeof(_Format) ); n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle); nBytes += n * sizeof(IntValue); _Width = psdGetValue(IntValue, sizeof(_Width) ); n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle); nBytes += n * sizeof(IntValue); _Height = psdGetValue(IntValue, sizeof(_Height) ); n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle); nBytes += n * sizeof(IntValue); _WidthBytes = psdGetValue(IntValue, sizeof(_WidthBytes) ); n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle); nBytes += n * sizeof(IntValue); _Size = psdGetValue(IntValue, sizeof(_Size) ); n = (int)io->read_proc(&IntValue, sizeof(IntValue), 1, handle); nBytes += n * sizeof(IntValue); _CompressedSize = psdGetValue(IntValue, sizeof(_CompressedSize) ); n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); nBytes += n * sizeof(ShortValue); _BitPerPixel = (short)psdGetValue(ShortValue, sizeof(_BitPerPixel) ); n = (int)io->read_proc(&ShortValue, sizeof(ShortValue), 1, handle); nBytes += n * sizeof(ShortValue); _Planes = (short)psdGetValue(ShortValue, sizeof(_Planes) ); const long JFIF_startpos = io->tell_proc(handle); if(_dib) { FreeImage_Unload(_dib); } if(_Format == 1) { // kJpegRGB thumbnail image _dib = FreeImage_LoadFromHandle(FIF_JPEG, io, handle); if(isBGR) { SwapRedBlue32(_dib); } // HACK: manually go to end of thumbnail, because (for some reason) LoadFromHandle consumes more bytes then available! io->seek_proc(handle, block_end, SEEK_SET); } else { // kRawRGB thumbnail image // ### Unimplemented (should be trivial) // skip the thumbnail part io->seek_proc(handle, iTotalData, SEEK_CUR); return iResourceSize; } nBytes += (block_end - JFIF_startpos); return nBytes; }
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; }
bool M_saveImage(const char * filename, void * data) { if(! filename) return false; if(! data) return false; // format FREE_IMAGE_FORMAT format = FreeImage_GetFIFFromFilename(filename); if(format == FIF_UNKNOWN) { printf("Error freeImage: unknow image format %s\n", filename); return false; } // image MImage * image = (MImage *)data; if(! image->getData()) return false; unsigned int width = image->getWidth(); unsigned int height = image->getHeight(); unsigned int components = image->getComponents(); unsigned int yStep = width*components; FIBITMAP * dib = NULL; if(image->getDataType() == M_UBYTE) { dib = FreeImage_AllocateT(FIT_BITMAP, width, height, components*8); if(dib) { unsigned char * srcBits = (unsigned char *)image->getData(); for(unsigned int y=0; y<height; y++) { BYTE * bits = FreeImage_GetScanLine(dib, y); memcpy(bits, srcBits, yStep*sizeof(char)); srcBits += yStep; } if(components == 3 || components == 4) SwapRedBlue32(dib); } } else if(image->getDataType() == M_FLOAT) { switch(image->getComponents()) { case 1: dib = FreeImage_AllocateT(FIT_FLOAT, image->getWidth(), image->getHeight(), 32); break; case 3: dib = FreeImage_AllocateT(FIT_RGBF, image->getWidth(), image->getHeight(), 3*32); break; case 4: dib = FreeImage_AllocateT(FIT_RGBAF, image->getWidth(), image->getHeight(), 4*32); break; } if(dib) { float * srcBits = (float *)image->getData(); for(unsigned int y=0; y<height; y++) { BYTE * bits = FreeImage_GetScanLine(dib, y); memcpy(bits, srcBits, yStep*sizeof(float)); srcBits += yStep; } } } if(dib) { // flip FreeImage_FlipVertical(dib); // save if(FreeImage_Save(format, dib, filename, PNG_Z_BEST_SPEED)) { FreeImage_Unload(dib); return true; } } return false; }
/** Copy or convert & copy decoded pixels into the dib @param pDecoder Decoder handle @param out_guid_format Target guid format @param dib Output dib @param width Image width @param height Image height @return Returns 0 if successful, returns ERR otherwise */ static ERR CopyPixels(PKImageDecode *pDecoder, PKPixelFormatGUID out_guid_format, FIBITMAP *dib, int width, int height) { PKFormatConverter *pConverter = NULL; // pixel format converter ERR error_code = 0; // error code as returned by the interface BYTE *pb = NULL; // local buffer used for pixel format conversion // image dimensions const PKRect rect = {0, 0, width, height}; try { // get input file pixel format ... PKPixelFormatGUID in_guid_format; error_code = pDecoder->GetPixelFormat(pDecoder, &in_guid_format); JXR_CHECK(error_code); // is a format conversion needed ? if(IsEqualGUID(out_guid_format, in_guid_format)) { // no conversion, load bytes "as is" ... // get a pointer to dst pixel data BYTE *dib_bits = FreeImage_GetBits(dib); // get dst pitch (count of BYTE for stride) const unsigned cbStride = FreeImage_GetPitch(dib); // decode and copy bits to dst array error_code = pDecoder->Copy(pDecoder, &rect, dib_bits, cbStride); JXR_CHECK(error_code); } else { // we need to use the conversion API ... // allocate the pixel format converter error_code = PKCodecFactory_CreateFormatConverter(&pConverter); JXR_CHECK(error_code); // set the conversion function error_code = pConverter->Initialize(pConverter, pDecoder, NULL, out_guid_format); JXR_CHECK(error_code); // get the maximum stride unsigned cbStride = 0; { PKPixelInfo pPIFrom; PKPixelInfo pPITo; pPIFrom.pGUIDPixFmt = &in_guid_format; error_code = PixelFormatLookup(&pPIFrom, LOOKUP_FORWARD); JXR_CHECK(error_code); pPITo.pGUIDPixFmt = &out_guid_format; error_code = PixelFormatLookup(&pPITo, LOOKUP_FORWARD); JXR_CHECK(error_code); unsigned cbStrideFrom = ((pPIFrom.cbitUnit + 7) >> 3) * width; unsigned cbStrideTo = ((pPITo.cbitUnit + 7) >> 3) * width; cbStride = MAX(cbStrideFrom, cbStrideTo); } // allocate a local decoder / encoder buffer error_code = PKAllocAligned((void **) &pb, cbStride * height, 128); JXR_CHECK(error_code); // copy / convert pixels error_code = pConverter->Copy(pConverter, &rect, pb, cbStride); JXR_CHECK(error_code); // now copy pixels into the dib const size_t line_size = FreeImage_GetLine(dib); for(int y = 0; y < height; y++) { BYTE *src_bits = (BYTE*)(pb + y * cbStride); BYTE *dst_bits = (BYTE*)FreeImage_GetScanLine(dib, y); memcpy(dst_bits, src_bits, line_size); } // free the local buffer PKFreeAligned((void **) &pb); // free the pixel format converter PKFormatConverter_Release(&pConverter); } // FreeImage DIB are upside-down relative to usual graphic conventions FreeImage_FlipVertical(dib); // post-processing ... // ------------------- // swap RGB as needed #if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR if(IsEqualGUID(out_guid_format, GUID_PKPixelFormat24bppRGB) || IsEqualGUID(out_guid_format, GUID_PKPixelFormat32bppRGB)) { SwapRedBlue32(dib); } #elif FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_RGB if(IsEqualGUID(out_guid_format, GUID_PKPixelFormat24bppBGR) || IsEqualGUID(out_guid_format, GUID_PKPixelFormat32bppBGR)) { SwapRedBlue32(dib); } #endif return WMP_errSuccess; } catch(...) { // free the local buffer PKFreeAligned((void **) &pb); // free the pixel format converter PKFormatConverter_Release(&pConverter); return error_code; } }