// Takes an image from the frame buffer and compresses it int writeFrame(FIMEMORY *fiBuffer, FIBITMAP *fiImage, unsigned char imageType) { /* char msg[1024]; sprintf_s(msg, 1024, "imageType: %i", imageType); MessageBox(NULL,msg,"ADES DEBUG", MB_OK); */ imageType = 2; int errStatus = 1; u_short imageWidth, imageHeight; unsigned width, height, pitch, line; BYTE *bits; // Package image using correct compression switch(imageType) { case 0: // Send a raw frame // Get image characteristics width = FreeImage_GetWidth(fiImage); height = FreeImage_GetHeight(fiImage); pitch = FreeImage_GetPitch(fiImage); line = FreeImage_GetLine(fiImage); // Write out width and height errStatus = FreeImage_SeekMemory(fiBuffer, 0, SEEK_SET); if (errStatus != 1) break; imageWidth = htons(width); errStatus = FreeImage_WriteMemory( &imageWidth, 2, 1, fiBuffer ); if (errStatus != 1) break; imageHeight = htons(height); errStatus = FreeImage_WriteMemory( &imageHeight, 2, 1, fiBuffer ); if (errStatus != 1) break; // Write out image (convert the bitmap to raw bits, top-left pixel first) bits = (BYTE*)malloc(height * pitch); FreeImage_ConvertToRawBits(bits, fiImage, pitch, 24, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK, TRUE); errStatus = FreeImage_WriteMemory( bits, height*pitch*sizeof(BYTE), 1, fiBuffer ); free(bits); if (errStatus != 1) break; break; default: // Send a jpg frame errStatus = FreeImage_SeekMemory(fiBuffer, 0, SEEK_SET); if (errStatus != 1) break; errStatus = FreeImage_SaveToMemory(FIF_JPEG, fiImage, fiBuffer, convertToJpegFlag(imageType)); if (errStatus != 1) break; break; } // Clean up and exit return errStatus; }
static FIBITMAP* mng_LoadFromMemoryHandle(FIMEMORY *hmem, int flags = 0) { long offset = 0; FIBITMAP *dib = NULL; if(hmem) { // seek to the start of the stream FreeImage_SeekMemory(hmem, offset, SEEK_SET); // check the file signature and deduce its format // (the second argument is currently not used by FreeImage) FREE_IMAGE_FORMAT fif = FreeImage_GetFileTypeFromMemory(hmem, 0); if(fif != FIF_UNKNOWN) { dib = FreeImage_LoadFromMemory(fif, hmem, flags); } } return dib; }
/** Insert a chunk just before the inNextChunkName chunk @param hPngMemory PNG stream handle @param start_pos Start position of the inNextChunkName chunk @param next_pos Start position of the next chunk @return Returns TRUE if successfull, returns FALSE otherwise */ static BOOL mng_CopyInsertChunks(FIMEMORY *hPngMemory, BYTE *inNextChunkName, BYTE *inInsertChunk, DWORD inChunkLength, DWORD start_pos, DWORD next_pos) { BYTE *data = NULL; DWORD size_in_bytes = 0; // length of the chunk to check DWORD chunk_length = next_pos - start_pos; if(chunk_length == 0) { return TRUE; } // get a pointer to the stream buffer FreeImage_AcquireMemory(hPngMemory, &data, &size_in_bytes); if(!(data && size_in_bytes) || (size_in_bytes < 20) || (chunk_length >= size_in_bytes)) { // not enough space to read a signature(8 bytes) + a chunk(at least 12 bytes) return FALSE; } // new file length unsigned buffer_size = inChunkLength + size_in_bytes; BYTE *buffer = (BYTE*)malloc(buffer_size * sizeof(BYTE)); if(!buffer) { return FALSE; } unsigned p = 0; memcpy(&buffer[p], &data[0], start_pos); p += start_pos; memcpy(&buffer[p], inInsertChunk, inChunkLength); p += inChunkLength; memcpy(&buffer[p], &data[start_pos], size_in_bytes - start_pos); // seek to the start of the stream FreeImage_SeekMemory(hPngMemory, 0, SEEK_SET); // re-write the stream FreeImage_WriteMemory(buffer, 1, buffer_size, hPngMemory); free(buffer); return TRUE; }
void testSaveMemIO(const char *lpszPathName) { FIMEMORY *hmem = NULL; // load a regular file FREE_IMAGE_FORMAT fif = FreeImage_GetFileType(lpszPathName); FIBITMAP *dib = FreeImage_Load(fif, lpszPathName, 0); // open a memory handle hmem = FreeImage_OpenMemory(); // save the file to memory FreeImage_SaveToMemory(fif, dib, hmem, 0); // at this point, hmem contains the entire PNG data in memory. // the amount of space used by the memory is equal to file_size long file_size = FreeImage_TellMemory(hmem); printf("File size : %ld\n", file_size); // its easy load an image from memory as well // seek to the start of the memory stream FreeImage_SeekMemory(hmem, 0L, SEEK_SET); // get the file type FREE_IMAGE_FORMAT mem_fif = FreeImage_GetFileTypeFromMemory(hmem, 0); // load an image from the memory handle FIBITMAP *check = FreeImage_LoadFromMemory(mem_fif, hmem, 0); // save as a regular file FreeImage_Save(FIF_PNG, check, "dump.png", PNG_DEFAULT); // make sure to free the data since FreeImage_SaveToMemory // will cause it to be malloc'd FreeImage_CloseMemory(hmem); FreeImage_Unload(check); FreeImage_Unload(dib); }
BOOL fipMemoryIO::seek(long offset, int origin) { return FreeImage_SeekMemory(_hmem, offset, origin); }
/** Load a FIBITMAP from a MNG or a JNG stream @param format_id ID of the caller @param io Stream i/o functions @param handle Stream handle @param Offset Start of the first chunk @param flags Loading flags @return Returns a dib if successful, returns NULL otherwise */ FIBITMAP* mng_ReadChunks(int format_id, FreeImageIO *io, fi_handle handle, long Offset, int flags = 0) { DWORD mLength = 0; BYTE mChunkName[5]; BYTE *mChunk = NULL; DWORD crc_file; long LastOffset; long mOrigPos; BYTE *PLTE_file_chunk = NULL; // whole PLTE chunk (lentgh, name, array, crc) DWORD PLTE_file_size = 0; // size of PLTE chunk BOOL m_HasGlobalPalette = FALSE; // may turn to TRUE in PLTE chunk unsigned m_TotalBytesOfChunks = 0; FIBITMAP *dib = NULL; FIBITMAP *dib_alpha = NULL; FIMEMORY *hJpegMemory = NULL; FIMEMORY *hPngMemory = NULL; FIMEMORY *hIDATMemory = NULL; // --- DWORD jng_width = 0; DWORD jng_height = 0; BYTE jng_color_type = 0; BYTE jng_image_sample_depth = 0; BYTE jng_image_compression_method = 0; BYTE jng_alpha_sample_depth = 0; BYTE jng_alpha_compression_method = 0; BYTE jng_alpha_filter_method = 0; BYTE jng_alpha_interlace_method = 0; DWORD mng_frame_width = 0; DWORD mng_frame_height = 0; DWORD mng_ticks_per_second = 0; DWORD mng_nominal_layer_count = 0; DWORD mng_nominal_frame_count = 0; DWORD mng_nominal_play_time = 0; DWORD mng_simplicity_profile = 0; DWORD res_x = 2835; // 72 dpi DWORD res_y = 2835; // 72 dpi RGBQUAD rgbBkColor = {0, 0, 0, 0}; WORD bk_red, bk_green, bk_blue; BOOL hasBkColor = FALSE; BOOL mHasIDAT = FALSE; tEXtMAP key_value_pair; // --- BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS; // get the file size const long mLOF = mng_LOF(io, handle); // go to the first chunk io->seek_proc(handle, Offset, SEEK_SET); try { BOOL mEnd = FALSE; while(mEnd == FALSE) { // start of the chunk LastOffset = io->tell_proc(handle); // read length mLength = 0; io->read_proc(&mLength, 1, sizeof(mLength), handle); mng_SwapLong(&mLength); // read name io->read_proc(&mChunkName[0], 1, 4, handle); mChunkName[4] = '\0'; if(mLength > 0) { mChunk = (BYTE*)realloc(mChunk, mLength); if(!mChunk) { FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName); throw (const char*)NULL; } Offset = io->tell_proc(handle); if(Offset + (long)mLength > mLOF) { FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: unexpected end of file", mChunkName); throw (const char*)NULL; } // read chunk io->read_proc(mChunk, 1, mLength, handle); } // read crc io->read_proc(&crc_file, 1, sizeof(crc_file), handle); mng_SwapLong(&crc_file); // check crc DWORD crc_check = FreeImage_ZLibCRC32(0, &mChunkName[0], 4); crc_check = FreeImage_ZLibCRC32(crc_check, mChunk, mLength); if(crc_check != crc_file) { FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: bad CRC", mChunkName); throw (const char*)NULL; } switch( mng_GetChunckType(mChunkName) ) { case MHDR: // The MHDR chunk is always first in all MNG datastreams except for those // that consist of a single PNG or JNG datastream with a PNG or JNG signature. if(mLength == 28) { memcpy(&mng_frame_width, &mChunk[0], 4); memcpy(&mng_frame_height, &mChunk[4], 4); memcpy(&mng_ticks_per_second, &mChunk[8], 4); memcpy(&mng_nominal_layer_count, &mChunk[12], 4); memcpy(&mng_nominal_frame_count, &mChunk[16], 4); memcpy(&mng_nominal_play_time, &mChunk[20], 4); memcpy(&mng_simplicity_profile, &mChunk[24], 4); mng_SwapLong(&mng_frame_width); mng_SwapLong(&mng_frame_height); mng_SwapLong(&mng_ticks_per_second); mng_SwapLong(&mng_nominal_layer_count); mng_SwapLong(&mng_nominal_frame_count); mng_SwapLong(&mng_nominal_play_time); mng_SwapLong(&mng_simplicity_profile); } else { FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: size is %d instead of 28", mChunkName, mLength); } break; case MEND: mEnd = TRUE; break; case LOOP: case ENDL: break; case DEFI: break; case SAVE: case SEEK: case TERM: break; case BACK: break; // Global "PLTE" and "tRNS" (if any). PNG "PLTE" will be of 0 byte, as it uses global data. case PLTE: // Global m_HasGlobalPalette = TRUE; PLTE_file_size = mLength + 12; // (lentgh, name, array, crc) = (4, 4, mLength, 4) PLTE_file_chunk = (BYTE*)realloc(PLTE_file_chunk, PLTE_file_size); if(!PLTE_file_chunk) { FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName); throw (const char*)NULL; } else { mOrigPos = io->tell_proc(handle); // seek to the start of the chunk io->seek_proc(handle, LastOffset, SEEK_SET); // load the whole chunk io->read_proc(PLTE_file_chunk, 1, PLTE_file_size, handle); // go to the start of the next chunk io->seek_proc(handle, mOrigPos, SEEK_SET); } break; case tRNS: // Global break; case IHDR: Offset = LastOffset; // parse the PNG file and get its file size if(mng_CountPNGChunks(io, handle, Offset, &m_TotalBytesOfChunks) == FALSE) { // reach an unexpected end of file mEnd = TRUE; FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: unexpected end of PNG file", mChunkName); break; } // wrap the { IHDR, ..., IEND } chunks as a PNG stream if(hPngMemory == NULL) { hPngMemory = FreeImage_OpenMemory(); } mOrigPos = io->tell_proc(handle); // write PNG file signature FreeImage_SeekMemory(hPngMemory, 0, SEEK_SET); FreeImage_WriteMemory(g_png_signature, 1, 8, hPngMemory); mChunk = (BYTE*)realloc(mChunk, m_TotalBytesOfChunks); if(!mChunk) { FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: out of memory", mChunkName); throw (const char*)NULL; } // on calling CountPNGChunks earlier, we were in Offset pos, // go back there io->seek_proc(handle, Offset, SEEK_SET); io->read_proc(mChunk, 1, m_TotalBytesOfChunks, handle); // Put back to original pos io->seek_proc(handle, mOrigPos, SEEK_SET); // write the PNG chunks FreeImage_WriteMemory(mChunk, 1, m_TotalBytesOfChunks, hPngMemory); // plug in global PLTE if local PLTE exists if(m_HasGlobalPalette) { // ensure we remove some local chunks, so that global // "PLTE" can be inserted right before "IDAT". mng_RemoveChunk(hPngMemory, mng_PLTE); mng_RemoveChunk(hPngMemory, mng_tRNS); mng_RemoveChunk(hPngMemory, mng_bKGD); // insert global "PLTE" chunk in its entirety before "IDAT" mng_InsertChunk(hPngMemory, mng_IDAT, PLTE_file_chunk, PLTE_file_size); } if(dib) FreeImage_Unload(dib); dib = mng_LoadFromMemoryHandle(hPngMemory, flags); // stop after the first image mEnd = TRUE; break; case JHDR: if(mLength == 16) { memcpy(&jng_width, &mChunk[0], 4); memcpy(&jng_height, &mChunk[4], 4); mng_SwapLong(&jng_width); mng_SwapLong(&jng_height); jng_color_type = mChunk[8]; jng_image_sample_depth = mChunk[9]; jng_image_compression_method = mChunk[10]; //BYTE jng_image_interlace_method = mChunk[11]; // for debug only jng_alpha_sample_depth = mChunk[12]; jng_alpha_compression_method = mChunk[13]; jng_alpha_filter_method = mChunk[14]; jng_alpha_interlace_method = mChunk[15]; } else { FreeImage_OutputMessageProc(format_id, "Error while parsing %s chunk: invalid chunk length", mChunkName); throw (const char*)NULL; } break; case JDAT: if(hJpegMemory == NULL) { hJpegMemory = FreeImage_OpenMemory(); } // as there may be several JDAT chunks, concatenate them FreeImage_WriteMemory(mChunk, 1, mLength, hJpegMemory); break; case IDAT: if(!header_only && (jng_alpha_compression_method == 0)) { // PNG grayscale IDAT format if(hIDATMemory == NULL) { hIDATMemory = FreeImage_OpenMemory(); mHasIDAT = TRUE; } // as there may be several IDAT chunks, concatenate them FreeImage_WriteMemory(mChunk, 1, mLength, hIDATMemory); } break; case IEND: if(!hJpegMemory) { mEnd = TRUE; break; } // load the JPEG if(dib) { FreeImage_Unload(dib); } dib = mng_LoadFromMemoryHandle(hJpegMemory, flags); // load the PNG alpha layer if(mHasIDAT) { BYTE *data = NULL; DWORD size_in_bytes = 0; // get a pointer to the IDAT buffer FreeImage_AcquireMemory(hIDATMemory, &data, &size_in_bytes); if(data && size_in_bytes) { // wrap the IDAT chunk as a PNG stream if(hPngMemory == NULL) { hPngMemory = FreeImage_OpenMemory(); } mng_WritePNGStream(jng_width, jng_height, jng_alpha_sample_depth, data, size_in_bytes, hPngMemory); // load the PNG if(dib_alpha) { FreeImage_Unload(dib_alpha); } dib_alpha = mng_LoadFromMemoryHandle(hPngMemory, flags); } } // stop the parsing mEnd = TRUE; break; case JDAA: break; case gAMA: break; case pHYs: // unit is pixels per meter memcpy(&res_x, &mChunk[0], 4); mng_SwapLong(&res_x); memcpy(&res_y, &mChunk[4], 4); mng_SwapLong(&res_y); break; case bKGD: memcpy(&bk_red, &mChunk[0], 2); mng_SwapShort(&bk_red); rgbBkColor.rgbRed = (BYTE)bk_red; memcpy(&bk_green, &mChunk[2], 2); mng_SwapShort(&bk_green); rgbBkColor.rgbGreen = (BYTE)bk_green; memcpy(&bk_blue, &mChunk[4], 2); mng_SwapShort(&bk_blue); rgbBkColor.rgbBlue = (BYTE)bk_blue; hasBkColor = TRUE; break; case tEXt: mng_SetMetadata_tEXt(key_value_pair, mChunk, mLength); break; case UNKNOWN_CHUNCK: default: break; } // switch( GetChunckType ) } // while(!mEnd) FreeImage_CloseMemory(hJpegMemory); FreeImage_CloseMemory(hPngMemory); FreeImage_CloseMemory(hIDATMemory); free(mChunk); free(PLTE_file_chunk); // convert to 32-bit if a transparent layer is available if(!header_only && dib_alpha) { FIBITMAP *dst = FreeImage_ConvertTo32Bits(dib); if((FreeImage_GetBPP(dib_alpha) == 8) && (FreeImage_GetImageType(dib_alpha) == FIT_BITMAP)) { FreeImage_SetChannel(dst, dib_alpha, FICC_ALPHA); } else { FIBITMAP *dst_alpha = FreeImage_ConvertTo8Bits(dib_alpha); FreeImage_SetChannel(dst, dst_alpha, FICC_ALPHA); FreeImage_Unload(dst_alpha); } FreeImage_Unload(dib); dib = dst; } FreeImage_Unload(dib_alpha); if(dib) { // set metadata FreeImage_SetDotsPerMeterX(dib, res_x); FreeImage_SetDotsPerMeterY(dib, res_y); if(hasBkColor) { FreeImage_SetBackgroundColor(dib, &rgbBkColor); } if(key_value_pair.size()) { for(tEXtMAP::iterator j = key_value_pair.begin(); j != key_value_pair.end(); j++) { std::string key = (*j).first; std::string value = (*j).second; mng_SetKeyValue(FIMD_COMMENTS, dib, key.c_str(), value.c_str()); } } } return dib; } catch(const char *text) { FreeImage_CloseMemory(hJpegMemory); FreeImage_CloseMemory(hPngMemory); FreeImage_CloseMemory(hIDATMemory); free(mChunk); free(PLTE_file_chunk); FreeImage_Unload(dib); FreeImage_Unload(dib_alpha); if(text) { FreeImage_OutputMessageProc(format_id, text); } return NULL; } }
bool IPLFileIO::loadMemory(void* mem, IPLImage*& image) { FIMEMORY* hmem = (FIMEMORY*) mem; FREE_IMAGE_FORMAT fif = FIF_UNKNOWN; FreeImage_SeekMemory(hmem, 0L, SEEK_SET); fif = FreeImage_GetFileTypeFromMemory(hmem, 0); if(fif == FIF_UNKNOWN) { // always close the memory stream FreeImage_CloseMemory(hmem); return false; } FIBITMAP *dib = FreeImage_LoadFromMemory(fif, hmem); int width = FreeImage_GetWidth(dib); int height = FreeImage_GetHeight(dib); // all files need to be flipped FreeImage_FlipVertical(dib); if(FreeImage_GetBPP(dib) == 8) { // grayscale images // convert to 32 bit FIBITMAP *dib2 = FreeImage_ConvertToGreyscale(dib); // clear old image delete image; // create new instance with the right dimensions image = new IPLImage(IPLData::IMAGE_GRAYSCALE, width, height); for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { BYTE value; FreeImage_GetPixelIndex(dib2, x, y, &value); image->plane(0)->p(x, y) = (value * FACTOR_TO_FLOAT); } } FreeImage_Unload(dib2); } else { // color images // convert to 32 bit FIBITMAP *dib2 = FreeImage_ConvertTo32Bits(dib); // clear old image delete image; // create new instance with the right dimensions image = new IPLImage(IPLData::IMAGE_COLOR, width, height); for(int y = 0; y < height; y++) { for(int x = 0; x < width; x++) { RGBQUAD rgb; FreeImage_GetPixelColor(dib2, x, y, &rgb); image->plane(0)->p(x, y) = (rgb.rgbRed * FACTOR_TO_FLOAT); // R image->plane(1)->p(x, y) = (rgb.rgbGreen * FACTOR_TO_FLOAT); // G image->plane(2)->p(x, y) = (rgb.rgbBlue * FACTOR_TO_FLOAT); // B } } FreeImage_Unload(dib2); } // always close the memory stream FreeImage_CloseMemory(hmem); // free temporary memory FreeImage_Unload(dib); return true; }
/** Write a metadata model as a TIF IFD to a FIMEMORY handle. The entries in the TIF IFD are sorted in ascending order by tag id. The last entry is written as 0 (4 bytes) which means no more IFD to follow. Supported metadata models are <ul> <li>FIMD_EXIF_MAIN <li>FIMD_EXIF_EXIF <li>FIMD_EXIF_GPS <li>FIMD_EXIF_INTEROP </ul> The end of the buffer is filled with 4 bytes equal to 0 (end of IFD offset) @param dib Input FIBITMAP @param md_model Metadata model to write @param hmem Memory handle @return Returns TRUE if successful, FALSE otherwise @see tiff_get_ifd_profile */ static BOOL tiff_write_ifd(FIBITMAP *dib, FREE_IMAGE_MDMODEL md_model, FIMEMORY *hmem) { FITAG *tag = NULL; FIMETADATA *mdhandle = NULL; std::vector<FITAG*> vTagList; TagLib::MDMODEL internal_md_model; DWORD ifd_offset = 0; // WORD-aligned IFD value offset const BYTE empty_byte = 0; // start of the file const long start_of_file = FreeImage_TellMemory(hmem); // get the metadata count unsigned metadata_count = FreeImage_GetMetadataCount(md_model, dib); if(metadata_count == 0) { return FALSE; } TagLib& s = TagLib::instance(); // check for supported metadata models switch(md_model) { case FIMD_EXIF_MAIN: internal_md_model = TagLib::EXIF_MAIN; break; case FIMD_EXIF_EXIF: internal_md_model = TagLib::EXIF_EXIF; break; case FIMD_EXIF_GPS: internal_md_model = TagLib::EXIF_GPS; break; case FIMD_EXIF_INTEROP: internal_md_model = TagLib::EXIF_INTEROP; break; default: return FALSE; } try { // 1) according to the TIFF specifications, // the entries in a TIF IFD must be sorted in ascending order by tag id // store the tags into a vector vTagList.reserve(metadata_count); mdhandle = FreeImage_FindFirstMetadata(md_model, dib, &tag); if(mdhandle) { // parse the tags and store them inside vTagList do { // rewrite the tag id using FreeImage internal database // (in case the tag id is wrong or missing) const char *key = FreeImage_GetTagKey(tag); int tag_id = s.getTagID(internal_md_model, key); if(tag_id != -1) { // this is a known tag, set the tag ID FreeImage_SetTagID(tag, (WORD)tag_id); // record the tag vTagList.push_back(tag); } // else ignore this tag } while(FreeImage_FindNextMetadata(mdhandle, &tag)); FreeImage_FindCloseMetadata(mdhandle); // sort the vector by tag id std::sort(vTagList.begin(), vTagList.end(), PredicateTagIDCompare()); // update the metadata_count metadata_count = (unsigned)vTagList.size(); } else { throw(1); } // 2) prepare the place for each IFD entries. /* An Image File Directory (IFD) consists of a 2-byte count of the number of directory entries (i.e., the number of fields), followed by a sequence of 12-byte field entries, followed by a 4-byte offset of the next IFD (or 0 if none). Do not forget to write the 4 bytes of 0 after the last IFD. */ { // prepare place for 2 bytes for number of entries + 12 bytes for each entry unsigned ifd_size = 2 + 12 * metadata_count; FreeImage_WriteMemory(&empty_byte, 1, ifd_size, hmem); // record the offset used to write values > 4-bytes ifd_offset = FreeImage_TellMemory(hmem); // rewind FreeImage_SeekMemory(hmem, start_of_file, SEEK_SET); } // 3) write each IFD entry in tag id ascending order // number of directory entries WORD nde = (WORD)metadata_count; FreeImage_WriteMemory(&nde, 1, 2, hmem); // for each entry ... for(unsigned i = 0; i < metadata_count; i++) { FITAG *tag = vTagList[i]; // tag id WORD tag_id = FreeImage_GetTagID(tag); FreeImage_WriteMemory(&tag_id, 1, 2, hmem); // tag type (compliant with TIFF specification) WORD tag_type = (WORD)FreeImage_GetTagType(tag); FreeImage_WriteMemory(&tag_type, 1, 2, hmem); // tag count DWORD tag_count = FreeImage_GetTagCount(tag); FreeImage_WriteMemory(&tag_count, 1, 4, hmem); // tag value or offset (results are in BYTE's units) unsigned tag_length = FreeImage_GetTagLength(tag); if(tag_length <= 4) { // 4 bytes or less, write the value (left justified) const BYTE *tag_value = (BYTE*)FreeImage_GetTagValue(tag); FreeImage_WriteMemory(tag_value, 1, tag_length, hmem); for(unsigned k = tag_length; k < 4; k++) { FreeImage_WriteMemory(&empty_byte, 1, 1, hmem); } } else { // write an offset FreeImage_WriteMemory(&ifd_offset, 1, 4, hmem); // write the value long current_position = FreeImage_TellMemory(hmem); FreeImage_SeekMemory(hmem, ifd_offset, SEEK_SET); FreeImage_WriteMemory(FreeImage_GetTagValue(tag), 1, tag_length, hmem); if(tag_length & 1) { // align to the next WORD boundary FreeImage_WriteMemory(&empty_byte, 1, 1, hmem); } // next offset to use ifd_offset = FreeImage_TellMemory(hmem); // rewind FreeImage_SeekMemory(hmem, current_position, SEEK_SET); } } // end-of-IFD or next IFD (0 == none) FreeImage_SeekMemory(hmem, ifd_offset, SEEK_SET); FreeImage_WriteMemory(&empty_byte, 1, 4, hmem); return TRUE; } catch(int) { return FALSE; } }
static FIBITMAP * DLL_CALLCONV Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) { TIFF *faxTIFF = NULL; FIBITMAP *dib = NULL; FIMEMORY *memory = NULL; //int verbose = 0; int stretch = 0; int rows; float resX = 204.0; float resY = 196.0; uint32 xsize = G3_DEFAULT_WIDTH; int compression_in = COMPRESSION_CCITTFAX3; int fillorder_in = FILLORDER_LSB2MSB; uint32 group3options_in = 0; // 1d-encoded uint32 group4options_in = 0; // compressed int photometric_in = PHOTOMETRIC_MINISWHITE; if(handle==NULL) return NULL; try { // set default load options compression_in = COMPRESSION_CCITTFAX3; // input is g3-encoded group3options_in &= ~GROUP3OPT_2DENCODING; // input is 1d-encoded (g3 only) fillorder_in = FILLORDER_MSB2LSB; // input has msb-to-lsb fillorder /* Original input-related fax2tiff options while ((c = getopt(argc, argv, "R:X:o:1234ABLMPUW5678abcflmprsuvwz?")) != -1) { switch (c) { // input-related options case '3': // input is g3-encoded compression_in = COMPRESSION_CCITTFAX3; break; case '4': // input is g4-encoded compression_in = COMPRESSION_CCITTFAX4; break; case 'U': // input is uncompressed (g3 and g4) group3options_in |= GROUP3OPT_UNCOMPRESSED; group4options_in |= GROUP4OPT_UNCOMPRESSED; break; case '1': // input is 1d-encoded (g3 only) group3options_in &= ~GROUP3OPT_2DENCODING; break; case '2': // input is 2d-encoded (g3 only) group3options_in |= GROUP3OPT_2DENCODING; break; case 'P': // input has not-aligned EOL (g3 only) group3options_in &= ~GROUP3OPT_FILLBITS; break; case 'A': // input has aligned EOL (g3 only) group3options_in |= GROUP3OPT_FILLBITS; break; case 'W': // input has 0 mean white photometric_in = PHOTOMETRIC_MINISWHITE; break; case 'B': // input has 0 mean black photometric_in = PHOTOMETRIC_MINISBLACK; break; case 'L': // input has lsb-to-msb fillorder fillorder_in = FILLORDER_LSB2MSB; break; case 'M': // input has msb-to-lsb fillorder fillorder_in = FILLORDER_MSB2LSB; break; case 'R': // input resolution resY = (float) atof(optarg); break; case 'X': // input width xsize = (uint32) atoi(optarg); break; // output-related options case 's': // stretch image by dup'ng scanlines stretch = 1; break; case 'v': // -v for info verbose++; break; } } */ // open a temporary memory buffer to save decoded scanlines memory = FreeImage_OpenMemory(); if(!memory) throw FI_MSG_ERROR_MEMORY; // wrap the raw fax file faxTIFF = TIFFClientOpen("(FakeInput)", "w", // TIFFClientOpen() fails if we don't set existing value here NULL, _g3ReadProc, _g3WriteProc, _g3SeekProc, _g3CloseProc, _g3SizeProc, _g3MapProc, _g3UnmapProc); if (faxTIFF == NULL) { throw "Can not create fake input file"; } TIFFSetMode(faxTIFF, O_RDONLY); TIFFSetField(faxTIFF, TIFFTAG_IMAGEWIDTH, xsize); TIFFSetField(faxTIFF, TIFFTAG_SAMPLESPERPIXEL, 1); TIFFSetField(faxTIFF, TIFFTAG_BITSPERSAMPLE, 1); TIFFSetField(faxTIFF, TIFFTAG_FILLORDER, fillorder_in); TIFFSetField(faxTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); TIFFSetField(faxTIFF, TIFFTAG_PHOTOMETRIC, photometric_in); TIFFSetField(faxTIFF, TIFFTAG_YRESOLUTION, resY); TIFFSetField(faxTIFF, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH); // NB: this must be done after directory info is setup TIFFSetField(faxTIFF, TIFFTAG_COMPRESSION, compression_in); if (compression_in == COMPRESSION_CCITTFAX3) TIFFSetField(faxTIFF, TIFFTAG_GROUP3OPTIONS, group3options_in); else if (compression_in == COMPRESSION_CCITTFAX4) TIFFSetField(faxTIFF, TIFFTAG_GROUP4OPTIONS, group4options_in); resX = 204; if (!stretch) { TIFFGetField(faxTIFF, TIFFTAG_YRESOLUTION, &resY); } else { resY = 196; } // decode the raw fax data rows = copyFaxFile(io, handle, faxTIFF, xsize, stretch, memory); if(rows <= 0) throw "Error when decoding raw fax file : check the decoder options"; // allocate the output dib dib = FreeImage_Allocate(xsize, rows, 1); unsigned pitch = FreeImage_GetPitch(dib); uint32 linesize = TIFFhowmany8(xsize); // fill the bitmap structure ... // ... palette RGBQUAD *pal = FreeImage_GetPalette(dib); if(photometric_in == PHOTOMETRIC_MINISWHITE) { pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 255; pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 0; } else { pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0; pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255; } // ... resolution FreeImage_SetDotsPerMeterX(dib, (unsigned)(resX/0.0254000 + 0.5)); FreeImage_SetDotsPerMeterY(dib, (unsigned)(resY/0.0254000 + 0.5)); // read the decoded scanline and fill the bitmap data FreeImage_SeekMemory(memory, 0, SEEK_SET); BYTE *bits = FreeImage_GetScanLine(dib, rows - 1); for(int k = 0; k < rows; k++) { FreeImage_ReadMemory(bits, linesize, 1, memory); bits -= pitch; } // free the TIFF wrapper TIFFClose(faxTIFF); // free the memory buffer FreeImage_CloseMemory(memory); } catch(const char *message) { if(memory) FreeImage_CloseMemory(memory); if(faxTIFF) TIFFClose(faxTIFF); if(dib) FreeImage_Unload(dib); FreeImage_OutputMessageProc(s_format_id, message); return NULL; } return dib; }