diff options
Diffstat (limited to 'plugins/AdvaImg/src/FreeImage/MNGHelper.cpp')
-rw-r--r-- | plugins/AdvaImg/src/FreeImage/MNGHelper.cpp | 1320 |
1 files changed, 0 insertions, 1320 deletions
diff --git a/plugins/AdvaImg/src/FreeImage/MNGHelper.cpp b/plugins/AdvaImg/src/FreeImage/MNGHelper.cpp deleted file mode 100644 index 2bc3e833a4..0000000000 --- a/plugins/AdvaImg/src/FreeImage/MNGHelper.cpp +++ /dev/null @@ -1,1320 +0,0 @@ -// ========================================================== -// MNG / JNG helpers -// -// Design and implementation by -// - Hervé Drolon (drolon@infonie.fr) -// -// This file is part of FreeImage 3 -// -// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY -// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES -// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE -// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED -// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT -// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY -// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL -// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER -// THIS DISCLAIMER. -// -// Use at your own risk! -// ========================================================== - -#include "FreeImage.h" -#include "Utilities.h" - -/** -References -http://www.libpng.org/pub/mng/spec/jng.html -http://www.w3.org/TR/PNG/ -http://libpng.org/pub/mng/spec/ -*/ - -// -------------------------------------------------------------------------- - -#define MNG_INCLUDE_JNG - -#ifdef MNG_INCLUDE_JNG -#define MNG_COLORTYPE_JPEGGRAY 8 /* JHDR */ -#define MNG_COLORTYPE_JPEGCOLOR 10 -#define MNG_COLORTYPE_JPEGGRAYA 12 -#define MNG_COLORTYPE_JPEGCOLORA 14 - -#define MNG_BITDEPTH_JPEG8 8 /* JHDR */ -#define MNG_BITDEPTH_JPEG12 12 -#define MNG_BITDEPTH_JPEG8AND12 20 - -#define MNG_COMPRESSION_BASELINEJPEG 8 /* JHDR */ - -#define MNG_INTERLACE_SEQUENTIAL 0 /* JHDR */ -#define MNG_INTERLACE_PROGRESSIVE 8 -#endif /* MNG_INCLUDE_JNG */ - -// -------------------------------------------------------------------------- - -#define JNG_SUPPORTED - -/** Size of a JDAT chunk on writing */ -const DWORD JPEG_CHUNK_SIZE = 8192; - -/** PNG signature */ -static const BYTE g_png_signature[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; -/** JNG signature */ -static const BYTE g_jng_signature[8] = { 139, 74, 78, 71, 13, 10, 26, 10 }; - -// -------------------------------------------------------------------------- - -/** Chunk type converted to enum */ -enum eChunckType { - UNKNOWN_CHUNCK, - MHDR, - BACK, - BASI, - CLIP, - CLON, - DEFI, - DHDR, - DISC, - ENDL, - FRAM, - IEND, - IHDR, - JHDR, - LOOP, - MAGN, - MEND, - MOVE, - PAST, - PLTE, - SAVE, - SEEK, - SHOW, - TERM, - bKGD, - cHRM, - gAMA, - iCCP, - nEED, - pHYg, - vpAg, - pHYs, - sBIT, - sRGB, - tRNS, - IDAT, - JDAT, - JDAA, - JdAA, - JSEP, - oFFs, - hIST, - iTXt, - sPLT, - sTER, - tEXt, - tIME, - zTXt -}; - -/** -Helper for map<key, value> where value is a pointer to a string. -Used to store tEXt metadata. -*/ -typedef std::map<std::string, std::string> tEXtMAP; - -// -------------------------------------------------------------------------- - -/* - Constant strings for known chunk types. If you need to add a chunk, - add a string holding the name here. To make the code more - portable, we use ASCII numbers like this, not characters. -*/ - -static BYTE mng_MHDR[5]={ 77, 72, 68, 82, (BYTE) '\0'}; -static BYTE mng_BACK[5]={ 66, 65, 67, 75, (BYTE) '\0'}; -static BYTE mng_BASI[5]={ 66, 65, 83, 73, (BYTE) '\0'}; -static BYTE mng_CLIP[5]={ 67, 76, 73, 80, (BYTE) '\0'}; -static BYTE mng_CLON[5]={ 67, 76, 79, 78, (BYTE) '\0'}; -static BYTE mng_DEFI[5]={ 68, 69, 70, 73, (BYTE) '\0'}; -static BYTE mng_DHDR[5]={ 68, 72, 68, 82, (BYTE) '\0'}; -static BYTE mng_DISC[5]={ 68, 73, 83, 67, (BYTE) '\0'}; -static BYTE mng_ENDL[5]={ 69, 78, 68, 76, (BYTE) '\0'}; -static BYTE mng_FRAM[5]={ 70, 82, 65, 77, (BYTE) '\0'}; -static BYTE mng_IEND[5]={ 73, 69, 78, 68, (BYTE) '\0'}; -static BYTE mng_IHDR[5]={ 73, 72, 68, 82, (BYTE) '\0'}; -static BYTE mng_JHDR[5]={ 74, 72, 68, 82, (BYTE) '\0'}; -static BYTE mng_LOOP[5]={ 76, 79, 79, 80, (BYTE) '\0'}; -static BYTE mng_MAGN[5]={ 77, 65, 71, 78, (BYTE) '\0'}; -static BYTE mng_MEND[5]={ 77, 69, 78, 68, (BYTE) '\0'}; -static BYTE mng_MOVE[5]={ 77, 79, 86, 69, (BYTE) '\0'}; -static BYTE mng_PAST[5]={ 80, 65, 83, 84, (BYTE) '\0'}; -static BYTE mng_PLTE[5]={ 80, 76, 84, 69, (BYTE) '\0'}; -static BYTE mng_SAVE[5]={ 83, 65, 86, 69, (BYTE) '\0'}; -static BYTE mng_SEEK[5]={ 83, 69, 69, 75, (BYTE) '\0'}; -static BYTE mng_SHOW[5]={ 83, 72, 79, 87, (BYTE) '\0'}; -static BYTE mng_TERM[5]={ 84, 69, 82, 77, (BYTE) '\0'}; -static BYTE mng_bKGD[5]={ 98, 75, 71, 68, (BYTE) '\0'}; -static BYTE mng_cHRM[5]={ 99, 72, 82, 77, (BYTE) '\0'}; -static BYTE mng_gAMA[5]={103, 65, 77, 65, (BYTE) '\0'}; -static BYTE mng_iCCP[5]={105, 67, 67, 80, (BYTE) '\0'}; -static BYTE mng_nEED[5]={110, 69, 69, 68, (BYTE) '\0'}; -static BYTE mng_pHYg[5]={112, 72, 89, 103, (BYTE) '\0'}; -static BYTE mng_vpAg[5]={118, 112, 65, 103, (BYTE) '\0'}; -static BYTE mng_pHYs[5]={112, 72, 89, 115, (BYTE) '\0'}; -static BYTE mng_sBIT[5]={115, 66, 73, 84, (BYTE) '\0'}; -static BYTE mng_sRGB[5]={115, 82, 71, 66, (BYTE) '\0'}; -static BYTE mng_tRNS[5]={116, 82, 78, 83, (BYTE) '\0'}; - -#if defined(JNG_SUPPORTED) -static BYTE mng_IDAT[5]={ 73, 68, 65, 84, (BYTE) '\0'}; -static BYTE mng_JDAT[5]={ 74, 68, 65, 84, (BYTE) '\0'}; -static BYTE mng_JDAA[5]={ 74, 68, 65, 65, (BYTE) '\0'}; -static BYTE mng_JdAA[5]={ 74, 100, 65, 65, (BYTE) '\0'}; -static BYTE mng_JSEP[5]={ 74, 83, 69, 80, (BYTE) '\0'}; -static BYTE mng_oFFs[5]={111, 70, 70, 115, (BYTE) '\0'}; -#endif - -static BYTE mng_hIST[5]={104, 73, 83, 84, (BYTE) '\0'}; -static BYTE mng_iTXt[5]={105, 84, 88, 116, (BYTE) '\0'}; -static BYTE mng_sPLT[5]={115, 80, 76, 84, (BYTE) '\0'}; -static BYTE mng_sTER[5]={115, 84, 69, 82, (BYTE) '\0'}; -static BYTE mng_tEXt[5]={116, 69, 88, 116, (BYTE) '\0'}; -static BYTE mng_tIME[5]={116, 73, 77, 69, (BYTE) '\0'}; -static BYTE mng_zTXt[5]={122, 84, 88, 116, (BYTE) '\0'}; - - -// -------------------------------------------------------------------------- - -/** -Convert a chunk name to a unique ID -*/ -static eChunckType -mng_GetChunckType(const BYTE *mChunkName) { - if(memcmp(mChunkName, mng_MHDR, 4) == 0) { - return MHDR; - } - if(memcmp(mChunkName, mng_LOOP, 4) == 0) { - return LOOP; - } - if(memcmp(mChunkName, mng_DEFI, 4) == 0) { - return DEFI; - } - if(memcmp(mChunkName, mng_PLTE, 4) == 0) { - return PLTE; - } - if(memcmp(mChunkName, mng_tRNS, 4) == 0) { - return tRNS; - } - if(memcmp(mChunkName, mng_IHDR, 4) == 0) { - return IHDR; - } - if(memcmp(mChunkName, mng_JHDR, 4) == 0) { - return JHDR; - } - if(memcmp(mChunkName, mng_MEND, 4) == 0) { - return MEND; - } - if(memcmp(mChunkName, mng_IEND, 4) == 0) { - return IEND; - } - if(memcmp(mChunkName, mng_JDAT, 4) == 0) { - return JDAT; - } - if(memcmp(mChunkName, mng_IDAT, 4) == 0) { - return IDAT; - } - if(memcmp(mChunkName, mng_JDAA, 4) == 0) { - return JDAA; - } - if(memcmp(mChunkName, mng_gAMA, 4) == 0) { - return gAMA; - } - if(memcmp(mChunkName, mng_pHYs, 4) == 0) { - return pHYs; - } - if(memcmp(mChunkName, mng_bKGD, 4) == 0) { - return bKGD; - } - if(memcmp(mChunkName, mng_tEXt, 4) == 0) { - return tEXt; - } - - return UNKNOWN_CHUNCK; -} - -inline void -mng_SwapShort(WORD *sp) { -#ifndef FREEIMAGE_BIGENDIAN - SwapShort(sp); -#endif -} - -inline void -mng_SwapLong(DWORD *lp) { -#ifndef FREEIMAGE_BIGENDIAN - SwapLong(lp); -#endif -} - -/** -Returns the size, in bytes, of a FreeImageIO stream, from the current position. -*/ -static long -mng_LOF(FreeImageIO *io, fi_handle handle) { - long start_pos = io->tell_proc(handle); - io->seek_proc(handle, 0, SEEK_END); - long file_length = io->tell_proc(handle); - io->seek_proc(handle, start_pos, SEEK_SET); - return file_length; -} - -/** -Count the number of bytes in a PNG stream, from IHDR to IEND. -If successful, the stream position, as given by io->tell_proc(handle), -should be the end of the PNG stream at the return of the function. -@param io -@param handle -@param inPos -@param m_TotalBytesOfChunks -@return Returns TRUE if successful, returns FALSE otherwise -*/ -static BOOL -mng_CountPNGChunks(FreeImageIO *io, fi_handle handle, long inPos, unsigned *m_TotalBytesOfChunks) { - long mLOF; - long mPos; - BOOL mEnd = FALSE; - DWORD mLength = 0; - BYTE mChunkName[5]; - - *m_TotalBytesOfChunks = 0; - - // get the length of the file - mLOF = mng_LOF(io, handle); - - // go to the start of the file - io->seek_proc(handle, inPos, SEEK_SET); - - try { - // parse chunks - while(mEnd == FALSE) { - // chunk length - mPos = io->tell_proc(handle); - if(mPos + 4 > mLOF) { - throw(1); - } - io->read_proc(&mLength, 1, 4, handle); - mng_SwapLong(&mLength); - // chunk name - mPos = io->tell_proc(handle); - if(mPos + 4 > mLOF) { - throw(1); - } - io->read_proc(&mChunkName[0], 1, 4, handle); - mChunkName[4] = '\0'; - - // go to next chunk - mPos = io->tell_proc(handle); - // 4 = size of the CRC - if(mPos + (long)mLength + 4 > mLOF) { - throw(1); - } - io->seek_proc(handle, mLength + 4, SEEK_CUR); - - switch( mng_GetChunckType(mChunkName) ) { - case IHDR: - if(mLength != 13) { - throw(1); - } - break; - - case IEND: - mEnd = TRUE; - // the length below includes 4 bytes CRC, but no bytes for Length - *m_TotalBytesOfChunks = io->tell_proc(handle) - inPos; - break; - - case UNKNOWN_CHUNCK: - default: - break; - } - - } // while(!mEnd) - - return TRUE; - - } catch(int) { - return FALSE; - } -} - -/** -Retrieve the position of a chunk in a PNG stream -@param hPngMemory PNG stream handle -@param chunk_name Name of the chunk to be found -@param offset Start of the search in the stream -@param start_pos [returned value] Start position of the chunk -@param next_pos [returned value] Start position of the next chunk -@return Returns TRUE if successful, returns FALSE otherwise -*/ -static BOOL -mng_FindChunk(FIMEMORY *hPngMemory, BYTE *chunk_name, long offset, DWORD *start_pos, DWORD *next_pos) { - DWORD mLength = 0; - - BYTE *data = NULL; - DWORD size_in_bytes = 0; - - *start_pos = 0; - *next_pos = 0; - - // get a pointer to the stream buffer - FreeImage_AcquireMemory(hPngMemory, &data, &size_in_bytes); - if(!(data && size_in_bytes) || (size_in_bytes < 20) || (size_in_bytes - offset < 20)) { - // not enough space to read a signature(8 bytes) + a chunk(at least 12 bytes) - return FALSE; - } - - try { - - // skip the signature and/or any following chunk(s) - DWORD chunk_pos = offset; - - while(1) { - // get chunk length - if(chunk_pos + 4 > size_in_bytes) { - break; - } - - memcpy(&mLength, &data[chunk_pos], 4); - mng_SwapLong(&mLength); - chunk_pos += 4; - - const DWORD next_chunk_pos = chunk_pos + 4 + mLength + 4; - if(next_chunk_pos > size_in_bytes) { - break; - } - - // get chunk name - if(memcmp(&data[chunk_pos], chunk_name, 4) == 0) { - chunk_pos -= 4; // found chunk - *start_pos = chunk_pos; - *next_pos = next_chunk_pos; - return TRUE; - } - - chunk_pos = next_chunk_pos; - } - - return FALSE; - - } catch(int) { - return FALSE; - } -} - -/** -Remove a chunk located at (start_pos, next_pos) in the PNG stream -@param hPngMemory PNG stream handle -@param start_pos Start position of the chunk -@param next_pos Start position of the next chunk -@return Returns TRUE if successfull, returns FALSE otherwise -*/ -static BOOL -mng_CopyRemoveChunks(FIMEMORY *hPngMemory, DWORD start_pos, DWORD next_pos) { - BYTE *data = NULL; - DWORD size_in_bytes = 0; - - // length of the chunk to remove - 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 = size_in_bytes + chunk_length; - - BYTE *buffer = (BYTE*)malloc(buffer_size * sizeof(BYTE)); - if(!buffer) { - return FALSE; - } - memcpy(&buffer[0], &data[0], start_pos); - memcpy(&buffer[start_pos], &data[next_pos], size_in_bytes - next_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; -} - -/** -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; -} - -static BOOL -mng_RemoveChunk(FIMEMORY *hPngMemory, BYTE *chunk_name) { - BOOL bResult = FALSE; - - DWORD start_pos = 0; - DWORD next_pos = 0; - - bResult = mng_FindChunk(hPngMemory, chunk_name, 8, &start_pos, &next_pos); - if(!bResult) { - return FALSE; - } - - bResult = mng_CopyRemoveChunks(hPngMemory, start_pos, next_pos); - if(!bResult) { - return FALSE; - } - - return TRUE; -} - -static BOOL -mng_InsertChunk(FIMEMORY *hPngMemory, BYTE *inNextChunkName, BYTE *inInsertChunk, unsigned chunk_length) { - BOOL bResult = FALSE; - - DWORD start_pos = 0; - DWORD next_pos = 0; - - bResult = mng_FindChunk(hPngMemory, inNextChunkName, 8, &start_pos, &next_pos); - if(!bResult) { - return FALSE; - } - - bResult = mng_CopyInsertChunks(hPngMemory, inNextChunkName, inInsertChunk, chunk_length, start_pos, next_pos); - if(!bResult) { - return FALSE; - } - - return TRUE; -} - -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; -} - -/** -Write a chunk in a PNG stream from the current position. -@param chunk_name Name of the chunk -@param chunk_data Chunk array -@param length Chunk length -@param hPngMemory PNG stream handle -*/ -static void -mng_WriteChunk(BYTE *chunk_name, BYTE *chunk_data, DWORD length, FIMEMORY *hPngMemory) { - DWORD crc_file = 0; - // write a PNG chunk ... - // - length - mng_SwapLong(&length); - FreeImage_WriteMemory(&length, 1, 4, hPngMemory); - mng_SwapLong(&length); - // - chunk name - FreeImage_WriteMemory(chunk_name, 1, 4, hPngMemory); - if(chunk_data && length) { - // - chunk data - FreeImage_WriteMemory(chunk_data, 1, length, hPngMemory); - // - crc - crc_file = FreeImage_ZLibCRC32(0, chunk_name, 4); - crc_file = FreeImage_ZLibCRC32(crc_file, chunk_data, length); - mng_SwapLong(&crc_file); - FreeImage_WriteMemory(&crc_file, 1, 4, hPngMemory); - } else { - // - crc - crc_file = FreeImage_ZLibCRC32(0, chunk_name, 4); - mng_SwapLong(&crc_file); - FreeImage_WriteMemory(&crc_file, 1, 4, hPngMemory); - } - -} - -/** -Wrap a IDAT chunk as a PNG stream. -The stream has the structure { g_png_signature, IHDR, IDAT, IEND } -The image is assumed to be a greyscale image. - -@param jng_width Image width -@param jng_height Image height -@param jng_alpha_sample_depth Bits per pixel -@param mChunk PNG grayscale IDAT format -@param mLength IDAT chunk length -@param hPngMemory Output memory stream -*/ -static void -mng_WritePNGStream(DWORD jng_width, DWORD jng_height, BYTE jng_alpha_sample_depth, BYTE *mChunk, DWORD mLength, FIMEMORY *hPngMemory) { - // PNG grayscale IDAT format - - BYTE data[14]; - - // wrap the IDAT chunk as a PNG stream - - // write PNG file signature - FreeImage_WriteMemory(g_png_signature, 1, 8, hPngMemory); - - // write a IHDR chunk ... - /* - The IHDR chunk must appear FIRST. It contains: - Width: 4 bytes - Height: 4 bytes - Bit depth: 1 byte - Color type: 1 byte - Compression method: 1 byte - Filter method: 1 byte - Interlace method: 1 byte - */ - // - chunk data - mng_SwapLong(&jng_width); - mng_SwapLong(&jng_height); - memcpy(&data[0], &jng_width, 4); - memcpy(&data[4], &jng_height, 4); - mng_SwapLong(&jng_width); - mng_SwapLong(&jng_height); - data[8] = jng_alpha_sample_depth; - data[9] = 0; // color_type gray (jng_color_type) - data[10] = 0; // compression method 0 (jng_alpha_compression_method) - data[11] = 0; // filter_method 0 (jng_alpha_filter_method) - data[12] = 0; // interlace_method 0 (jng_alpha_interlace_method) - - mng_WriteChunk(mng_IHDR, &data[0], 13, hPngMemory); - - // write a IDAT chunk ... - mng_WriteChunk(mng_IDAT, mChunk, mLength, hPngMemory); - - // write a IEND chunk ... - mng_WriteChunk(mng_IEND, NULL, 0, hPngMemory); - -} - -// -------------------------------------------------------------------------- - -/** -Build and set a FITAG whose type is FIDT_ASCII. -The tag must be destroyed by the caller using FreeImage_DeleteTag. -@param model Metadata model to be filled -@param dib Image to be filled -@param key Tag key -@param value Tag value -@return Returns TRUE if successful, returns FALSE otherwise -*/ -static BOOL -mng_SetKeyValue(FREE_IMAGE_MDMODEL model, FIBITMAP *dib, const char *key, const char *value) { - if(!dib || !key || !value) { - return FALSE; - } - // create a tag - FITAG *tag = FreeImage_CreateTag(); - if(tag) { - BOOL bSuccess = TRUE; - // fill the tag - DWORD tag_length = (DWORD)(strlen(value) + 1); - bSuccess &= FreeImage_SetTagKey(tag, key); - bSuccess &= FreeImage_SetTagLength(tag, tag_length); - bSuccess &= FreeImage_SetTagCount(tag, tag_length); - bSuccess &= FreeImage_SetTagType(tag, FIDT_ASCII); - bSuccess &= FreeImage_SetTagValue(tag, value); - if(bSuccess) { - // set the tag - FreeImage_SetMetadata(model, dib, FreeImage_GetTagKey(tag), tag); - } - FreeImage_DeleteTag(tag); - return bSuccess; - } - - return FALSE; -} - -/** -Read a tEXt chunk and extract the key/value pair. -@param key_value_pair [returned value] Array of key/value pairs -@param mChunk Chunk data -@param mLength Chunk length -@return Returns TRUE if successful, returns FALSE otherwise -*/ -static BOOL -mng_SetMetadata_tEXt(tEXtMAP &key_value_pair, const BYTE *mChunk, DWORD mLength) { - std::string key; - std::string value; - BYTE *buffer = (BYTE*)malloc(mLength * sizeof(BYTE)); - if(!buffer) { - return FALSE; - } - DWORD pos = 0; - - memset(buffer, 0, mLength * sizeof(BYTE)); - - for(DWORD i = 0; i < mLength; i++) { - buffer[pos++] = mChunk[i]; - if(mChunk[i] == '\0') { - if(key.size() == 0) { - key = (char*)buffer; - pos = 0; - memset(buffer, 0, mLength * sizeof(BYTE)); - } else { - break; - } - } - } - value = (char*)buffer; - free(buffer); - - key_value_pair[key] = value; - - return TRUE; -} - -// -------------------------------------------------------------------------- - -/** -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; - } -} - -// -------------------------------------------------------------------------- - -/** -Write a FIBITMAP to a JNG stream -@param format_id ID of the caller -@param io Stream i/o functions -@param dib Image to be saved -@param handle Stream handle -@param flags Saving flags -@return Returns TRUE if successful, returns FALSE otherwise -*/ -BOOL -mng_WriteJNG(int format_id, FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int flags) { - DWORD jng_width = 0; - DWORD jng_height = 0; - BYTE jng_color_type = 0; - BYTE jng_image_sample_depth = 8; - BYTE jng_image_compression_method = 8; // 8: ISO-10918-1 Huffman-coded baseline JPEG. - BYTE jng_image_interlace_method = 0; - - BYTE jng_alpha_sample_depth = 0; - BYTE jng_alpha_compression_method = 0; - BYTE jng_alpha_filter_method = 0; - BYTE jng_alpha_interlace_method = 0; - - BYTE buffer[16]; - - FIMEMORY *hJngMemory = NULL; - FIMEMORY *hJpegMemory = NULL; - FIMEMORY *hPngMemory = NULL; - - FIBITMAP *dib_rgb = NULL; - FIBITMAP *dib_alpha = NULL; - - if(!dib || (FreeImage_GetImageType(dib) != FIT_BITMAP)) { - return FALSE; - } - - unsigned bpp = FreeImage_GetBPP(dib); - - switch(bpp) { - case 8: - if(FreeImage_GetColorType(dib) == FIC_MINISBLACK) { - dib_rgb = dib; - jng_color_type = MNG_COLORTYPE_JPEGGRAY; - } else { - // JPEG plugin will convert other types (FIC_MINISWHITE, FIC_PALETTE) to 24-bit on the fly - //dib_rgb = FreeImage_ConvertTo24Bits(dib); - dib_rgb = dib; - jng_color_type = MNG_COLORTYPE_JPEGCOLOR; - - } - break; - case 24: - dib_rgb = dib; - jng_color_type = MNG_COLORTYPE_JPEGCOLOR; - break; - case 32: - dib_rgb = FreeImage_ConvertTo24Bits(dib); - jng_color_type = MNG_COLORTYPE_JPEGCOLORA; - jng_alpha_sample_depth = 8; - break; - default: - return FALSE; - } - - jng_width = (DWORD)FreeImage_GetWidth(dib); - jng_height = (DWORD)FreeImage_GetHeight(dib); - - try { - hJngMemory = FreeImage_OpenMemory(); - - // --- write JNG file signature --- - FreeImage_WriteMemory(g_jng_signature, 1, 8, hJngMemory); - - // --- write a JHDR chunk --- - SwapLong(&jng_width); - SwapLong(&jng_height); - memcpy(&buffer[0], &jng_width, 4); - memcpy(&buffer[4], &jng_height, 4); - SwapLong(&jng_width); - SwapLong(&jng_height); - buffer[8] = jng_color_type; - buffer[9] = jng_image_sample_depth; - buffer[10] = jng_image_compression_method; - buffer[11] = jng_image_interlace_method; - buffer[12] = jng_alpha_sample_depth; - buffer[13] = jng_alpha_compression_method; - buffer[14] = jng_alpha_filter_method; - buffer[15] = jng_alpha_interlace_method; - mng_WriteChunk(mng_JHDR, &buffer[0], 16, hJngMemory); - - // --- write a sequence of JDAT chunks --- - hJpegMemory = FreeImage_OpenMemory(); - flags |= JPEG_BASELINE; - if(!FreeImage_SaveToMemory(FIF_JPEG, dib_rgb, hJpegMemory, flags)) { - throw (const char*)NULL; - } - if(dib_rgb != dib) { - FreeImage_Unload(dib_rgb); - dib_rgb = NULL; - } - { - BYTE *jpeg_data = NULL; - DWORD size_in_bytes = 0; - - // get a pointer to the stream buffer - FreeImage_AcquireMemory(hJpegMemory, &jpeg_data, &size_in_bytes); - // write chunks - for(DWORD k = 0; k < size_in_bytes;) { - DWORD bytes_left = size_in_bytes - k; - DWORD chunk_size = MIN(JPEG_CHUNK_SIZE, bytes_left); - mng_WriteChunk(mng_JDAT, &jpeg_data[k], chunk_size, hJngMemory); - k += chunk_size; - } - } - FreeImage_CloseMemory(hJpegMemory); - hJpegMemory = NULL; - - // --- write alpha layer as a sequence of IDAT chunk --- - if((bpp == 32) && (jng_color_type == MNG_COLORTYPE_JPEGCOLORA)) { - dib_alpha = FreeImage_GetChannel(dib, FICC_ALPHA); - - hPngMemory = FreeImage_OpenMemory(); - if(!FreeImage_SaveToMemory(FIF_PNG, dib_alpha, hPngMemory, PNG_DEFAULT)) { - throw (const char*)NULL; - } - FreeImage_Unload(dib_alpha); - dib_alpha = NULL; - // get the IDAT chunk - { - BOOL bResult = FALSE; - DWORD start_pos = 0; - DWORD next_pos = 0; - long offset = 8; - - do { - // find the next IDAT chunk from 'offset' position - bResult = mng_FindChunk(hPngMemory, mng_IDAT, offset, &start_pos, &next_pos); - if(!bResult) break; - - BYTE *png_data = NULL; - DWORD size_in_bytes = 0; - - // get a pointer to the stream buffer - FreeImage_AcquireMemory(hPngMemory, &png_data, &size_in_bytes); - // write the IDAT chunk - mng_WriteChunk(mng_IDAT, &png_data[start_pos+8], next_pos - start_pos - 12, hJngMemory); - - offset = next_pos; - - } while(bResult); - } - - FreeImage_CloseMemory(hPngMemory); - hPngMemory = NULL; - } - - // --- write a IEND chunk --- - mng_WriteChunk(mng_IEND, NULL, 0, hJngMemory); - - // write the JNG on output stream - { - BYTE *jng_data = NULL; - DWORD size_in_bytes = 0; - FreeImage_AcquireMemory(hJngMemory, &jng_data, &size_in_bytes); - io->write_proc(jng_data, 1, size_in_bytes, handle); - } - - FreeImage_CloseMemory(hJngMemory); - FreeImage_CloseMemory(hJpegMemory); - FreeImage_CloseMemory(hPngMemory); - - return TRUE; - - } catch(const char *text) { - FreeImage_CloseMemory(hJngMemory); - FreeImage_CloseMemory(hJpegMemory); - FreeImage_CloseMemory(hPngMemory); - if(dib_rgb && (dib_rgb != dib)) { - FreeImage_Unload(dib_rgb); - } - FreeImage_Unload(dib_alpha); - if(text) { - FreeImage_OutputMessageProc(format_id, text); - } - return FALSE; - } -} |