diff options
Diffstat (limited to 'plugins/AdvaImg/src/FreeImage/MultiPage.cpp')
-rw-r--r-- | plugins/AdvaImg/src/FreeImage/MultiPage.cpp | 1980 |
1 files changed, 990 insertions, 990 deletions
diff --git a/plugins/AdvaImg/src/FreeImage/MultiPage.cpp b/plugins/AdvaImg/src/FreeImage/MultiPage.cpp index 60ce85eb10..d48e11be94 100644 --- a/plugins/AdvaImg/src/FreeImage/MultiPage.cpp +++ b/plugins/AdvaImg/src/FreeImage/MultiPage.cpp @@ -1,990 +1,990 @@ -// ==========================================================
-// Multi-Page functions
-//
-// Design and implementation by
-// - Floris van den Berg (flvdberg@wxs.nl)
-// - Laurent Rocher (rocherl@club-internet.fr)
-// - Steve Johnson (steve@parisgroup.net)
-// - Petr Pytelka (pyta@lightcomp.com)
-// - Hervé Drolon (drolon@infonie.fr)
-// - Vadim Alexandrov (vadimalexandrov@users.sourceforge.net
-// - Martin Dyring-Andersen (mda@spamfighter.com)
-// - Volodymyr Goncharov (volodymyr.goncharov@gmail.com)
-//
-// 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!
-// ==========================================================
-
-#ifdef _MSC_VER
-#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
-#endif
-
-#include "CacheFile.h"
-#include "FreeImageIO.h"
-#include "Plugin.h"
-#include "Utilities.h"
-#include "FreeImage.h"
-
-// ----------------------------------------------------------
-
-enum BlockType { BLOCK_CONTINUEUS, BLOCK_REFERENCE };
-
-// ----------------------------------------------------------
-
-struct BlockTypeS {
- BlockType m_type;
-
- BlockTypeS(BlockType type) : m_type(type) {
- }
- virtual ~BlockTypeS() {}
-};
-
-struct BlockContinueus : public BlockTypeS {
- int m_start;
- int m_end;
-
- BlockContinueus(int s, int e) : BlockTypeS(BLOCK_CONTINUEUS),
- m_start(s),
- m_end(e) {
- }
-};
-
-struct BlockReference : public BlockTypeS {
- int m_reference;
- int m_size;
-
- BlockReference(int r, int size) : BlockTypeS(BLOCK_REFERENCE),
- m_reference(r),
- m_size(size) {
- }
-};
-
-// ----------------------------------------------------------
-
-typedef std::list<BlockTypeS *> BlockList;
-typedef std::list<BlockTypeS *>::iterator BlockListIterator;
-
-// ----------------------------------------------------------
-
-FI_STRUCT (MULTIBITMAPHEADER) {
- PluginNode *node;
- FREE_IMAGE_FORMAT fif;
- FreeImageIO *io;
- fi_handle handle;
- CacheFile *m_cachefile;
- std::map<FIBITMAP *, int> locked_pages;
- BOOL changed;
- int page_count;
- BlockList m_blocks;
- char *m_filename;
- BOOL read_only;
- FREE_IMAGE_FORMAT cache_fif;
- int load_flags;
-};
-
-// =====================================================================
-// Helper functions
-// =====================================================================
-
-inline void
-ReplaceExtension(std::string& dst_filename, const std::string& src_filename, const std::string& dst_extension) {
- size_t lastDot = src_filename.find_last_of('.');
- if (lastDot == std::string::npos) {
- dst_filename = src_filename;
- dst_filename += ".";
- dst_filename += dst_extension;
- }
- else {
- dst_filename = src_filename.substr(0, lastDot + 1);
- dst_filename += dst_extension;
- }
-}
-
-// =====================================================================
-// Internal Multipage functions
-// =====================================================================
-
-inline MULTIBITMAPHEADER *
-FreeImage_GetMultiBitmapHeader(FIMULTIBITMAP *bitmap) {
- return (MULTIBITMAPHEADER *)bitmap->data;
-}
-
-static BlockListIterator DLL_CALLCONV
-FreeImage_FindBlock(FIMULTIBITMAP *bitmap, int position) {
- assert(NULL != bitmap);
-
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- // step 1: find the block that matches the given position
-
- int prev_count = 0;
- int count = 0;
- BlockListIterator i;
- BlockTypeS *current_block = NULL;
-
- for (i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
- prev_count = count;
-
- switch((*i)->m_type) {
- case BLOCK_CONTINUEUS :
- count += ((BlockContinueus *)(*i))->m_end - ((BlockContinueus *)(*i))->m_start + 1;
- break;
-
- case BLOCK_REFERENCE :
- count++;
- break;
- }
-
- current_block = *i;
-
- if (count > position)
- break;
- }
-
- // step 2: make sure we found the node. from here it gets a little complicated:
- // * if the block is there, just return it
- // * if the block is a series of blocks, split it in max 3 new blocks
- // and return the splitted block
-
- if ((current_block) && (count > position)) {
- switch(current_block->m_type) {
- case BLOCK_REFERENCE :
- return i;
-
- case BLOCK_CONTINUEUS :
- {
- BlockContinueus *block = (BlockContinueus *)current_block;
-
- if (block->m_start != block->m_end) {
- int item = block->m_start + (position - prev_count);
-
- // left part
-
- if (item != block->m_start) {
- BlockContinueus *block_a = new BlockContinueus(block->m_start, item - 1);
- header->m_blocks.insert(i, (BlockTypeS *)block_a);
- }
-
- // middle part
-
- BlockContinueus *block_b = new BlockContinueus(item, item);
- BlockListIterator block_target = header->m_blocks.insert(i, (BlockTypeS *)block_b);
-
- // right part
-
- if (item != block->m_end) {
- BlockContinueus *block_c = new BlockContinueus(item + 1, block->m_end);
- header->m_blocks.insert(i, (BlockTypeS *)block_c);
- }
-
- // remove the old block that was just splitted
-
- header->m_blocks.remove((BlockTypeS *)block);
- delete block;
-
- // return the splitted block
-
- return block_target;
- }
-
- return i;
- }
- }
- }
- // we should never go here ...
- assert(false);
- return header->m_blocks.end();
-}
-
-int DLL_CALLCONV
-FreeImage_InternalGetPageCount(FIMULTIBITMAP *bitmap) {
- if (bitmap) {
- if (((MULTIBITMAPHEADER *)bitmap->data)->handle) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- header->io->seek_proc(header->handle, 0, SEEK_SET);
-
- void *data = FreeImage_Open(header->node, header->io, header->handle, TRUE);
-
- int page_count = (header->node->m_plugin->pagecount_proc != NULL) ? header->node->m_plugin->pagecount_proc(header->io, header->handle, data) : 1;
-
- FreeImage_Close(header->node, header->io, header->handle, data);
-
- return page_count;
- }
- }
-
- return 0;
-}
-
-// =====================================================================
-// Multipage functions
-// =====================================================================
-
-FIMULTIBITMAP * DLL_CALLCONV
-FreeImage_OpenMultiBitmap(FREE_IMAGE_FORMAT fif, const char *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory, int flags) {
-
- FILE *handle = NULL;
- try {
- // sanity check on the parameters
-
- if (create_new) {
- read_only = FALSE;
- }
-
- // retrieve the plugin list to find the node belonging to this plugin
-
- PluginList *list = FreeImage_GetPluginList();
-
- if (list) {
- PluginNode *node = list->FindNodeFromFIF(fif);
-
- if (node) {
- std::auto_ptr<FreeImageIO> io (new FreeImageIO);
-
- SetDefaultIO(io.get());
-
- if (!create_new) {
- handle = fopen(filename, "rb");
- if (handle == NULL) {
- return NULL;
- }
- }
-
- std::auto_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP);
- std::auto_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER);
- header->m_filename = new char[strlen(filename) + 1];
- strcpy(header->m_filename, filename);
- header->node = node;
- header->fif = fif;
- header->io = io.get ();
- header->handle = handle;
- header->changed = FALSE;
- header->read_only = read_only;
- header->m_cachefile = NULL;
- header->cache_fif = fif;
- header->load_flags = flags;
-
- // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
-
- bitmap->data = header.get();
-
- // cache the page count
-
- header->page_count = FreeImage_InternalGetPageCount(bitmap.get());
-
- // allocate a continueus block to describe the bitmap
-
- if (!create_new) {
- header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
- }
-
- // set up the cache
-
- if (!read_only) {
- std::string cache_name;
- ReplaceExtension(cache_name, filename, "ficache");
-
- std::auto_ptr<CacheFile> cache_file (new CacheFile(cache_name, keep_cache_in_memory));
-
- if (cache_file->open()) {
- // we can use release() as std::bad_alloc won't be thrown from here on
- header->m_cachefile = cache_file.release();
- } else {
- // an error occured ...
- fclose(handle);
- return NULL;
- }
- }
- // return the multibitmap
- // std::bad_alloc won't be thrown from here on
- header.release(); // now owned by bitmap
- io.release(); // now owned by bitmap
- return bitmap.release(); // now owned by caller
- }
- }
- } catch (std::bad_alloc &) {
- /** @todo report error */
- }
- if (handle)
- fclose(handle);
- return NULL;
-}
-
-FIMULTIBITMAP * DLL_CALLCONV
-FreeImage_OpenMultiBitmapU(FREE_IMAGE_FORMAT fif, const wchar_t *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory, int flags) {
-
- // convert to single character - no national chars in extensions
- char *extension = (char *)malloc(wcslen(filename)+1);
- unsigned int i=0;
- for (; i < wcslen(filename); i++) // convert 16-bit to 8-bit
- extension[i] = (char)(filename[i] & 0x00FF);
- // set terminating 0
- extension[i]=0;
- FIMULTIBITMAP *fRet = FreeImage_OpenMultiBitmap(fif, extension, create_new, read_only, keep_cache_in_memory, flags);
- free(extension);
-
- return fRet;
-}
-
-FIMULTIBITMAP * DLL_CALLCONV
-FreeImage_OpenMultiBitmapFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags) {
- try {
- BOOL read_only = FALSE; // modifications (if any) will be stored into the memory cache
-
- if (io && handle) {
-
- // retrieve the plugin list to find the node belonging to this plugin
- PluginList *list = FreeImage_GetPluginList();
-
- if (list) {
- PluginNode *node = list->FindNodeFromFIF(fif);
-
- if (node) {
- std::auto_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP);
- std::auto_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER);
- std::auto_ptr<FreeImageIO> tmp_io (new FreeImageIO (*io));
- header->io = tmp_io.get();
- header->m_filename = NULL;
- header->node = node;
- header->fif = fif;
- header->handle = handle;
- header->changed = FALSE;
- header->read_only = read_only;
- header->m_cachefile = NULL;
- header->cache_fif = fif;
- header->load_flags = flags;
-
- // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
-
- bitmap->data = header.get();
-
- // cache the page count
-
- header->page_count = FreeImage_InternalGetPageCount(bitmap.get());
-
- // allocate a continueus block to describe the bitmap
-
- header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
-
- if (!read_only) {
- // set up the cache
- std::auto_ptr<CacheFile> cache_file (new CacheFile("", TRUE));
-
- if (cache_file->open()) {
- header->m_cachefile = cache_file.release();
- }
- }
- tmp_io.release();
- header.release();
- return bitmap.release();
- }
- }
- }
- } catch (std::bad_alloc &) {
- /** @todo report error */
- }
- return NULL;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_SaveMultiBitmapToHandle(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FreeImageIO *io, fi_handle handle, int flags) {
- if(!bitmap || !bitmap->data || !io || !handle) {
- return FALSE;
- }
-
- BOOL success = TRUE;
-
- // retrieve the plugin list to find the node belonging to this plugin
- PluginList *list = FreeImage_GetPluginList();
-
- if (list) {
- PluginNode *node = list->FindNodeFromFIF(fif);
-
- if(node) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- // dst data
- void *data = FreeImage_Open(node, io, handle, FALSE);
- // src data
- void *data_read = NULL;
-
- if(header->handle) {
- // open src
- header->io->seek_proc(header->handle, 0, SEEK_SET);
- data_read = FreeImage_Open(header->node, header->io, header->handle, TRUE);
- }
-
- // write all the pages to the file using handle and io
-
- int count = 0;
-
- for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); i++) {
- if (success) {
- switch((*i)->m_type) {
- case BLOCK_CONTINUEUS:
- {
- BlockContinueus *block = (BlockContinueus *)(*i);
-
- for (int j = block->m_start; j <= block->m_end; j++) {
-
- // load the original source data
- FIBITMAP *dib = header->node->m_plugin->load_proc(header->io, header->handle, j, header->load_flags, data_read);
-
- // save the data
- success = node->m_plugin->save_proc(io, dib, handle, count, flags, data);
- count++;
-
- FreeImage_Unload(dib);
- }
-
- break;
- }
-
- case BLOCK_REFERENCE:
- {
- BlockReference *ref = (BlockReference *)(*i);
-
- // read the compressed data
-
- BYTE *compressed_data = (BYTE*)malloc(ref->m_size * sizeof(BYTE));
-
- header->m_cachefile->readFile((BYTE *)compressed_data, ref->m_reference, ref->m_size);
-
- // uncompress the data
-
- FIMEMORY *hmem = FreeImage_OpenMemory(compressed_data, ref->m_size);
- FIBITMAP *dib = FreeImage_LoadFromMemory(header->cache_fif, hmem, 0);
- FreeImage_CloseMemory(hmem);
-
- // get rid of the buffer
- free(compressed_data);
-
- // save the data
-
- success = node->m_plugin->save_proc(io, dib, handle, count, flags, data);
- count++;
-
- // unload the dib
-
- FreeImage_Unload(dib);
-
- break;
- }
- }
- } else {
- break;
- }
- }
-
- // close the files
-
- FreeImage_Close(header->node, header->io, header->handle, data_read);
-
- FreeImage_Close(node, io, handle, data);
-
- return success;
- }
- }
-
- return FALSE;
-}
-
-
-BOOL DLL_CALLCONV
-FreeImage_CloseMultiBitmap(FIMULTIBITMAP *bitmap, int flags) {
- if (bitmap) {
- BOOL success = TRUE;
-
- if (bitmap->data) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- // saves changes only of images loaded directly from a file
- if (header->changed && header->m_filename) {
- try {
- // open a temp file
-
- std::string spool_name;
-
- ReplaceExtension(spool_name, header->m_filename, "fispool");
-
- // open the spool file and the source file
-
- FILE *f = fopen(spool_name.c_str(), "w+b");
-
- // saves changes
- if (f == NULL) {
- FreeImage_OutputMessageProc(header->fif, "Failed to open %s, %s", spool_name.c_str(), strerror(errno));
- success = FALSE;
- } else {
- success = FreeImage_SaveMultiBitmapToHandle(header->fif, bitmap, header->io, (fi_handle)f, flags);
-
- // close the files
-
- if (fclose(f) != 0) {
- success = FALSE;
- FreeImage_OutputMessageProc(header->fif, "Failed to close %s, %s", spool_name.c_str(), strerror(errno));
- }
- }
- if (header->handle) {
- fclose((FILE *)header->handle);
- }
-
- // applies changes to the destination file
-
- if (success) {
- remove(header->m_filename);
- success = (rename(spool_name.c_str(), header->m_filename) == 0) ? TRUE:FALSE;
- if(!success) {
- FreeImage_OutputMessageProc(header->fif, "Failed to rename %s to %s", spool_name.c_str(), header->m_filename);
- }
- } else {
- remove(spool_name.c_str());
- }
- } catch (std::bad_alloc &) {
- success = FALSE;
- }
-
- } else {
- if (header->handle && header->m_filename) {
- fclose((FILE *)header->handle);
- }
- }
-
- // clear the blocks list
-
- for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
- delete *i;
- }
-
- // flush and dispose the cache
-
- if (header->m_cachefile) {
- header->m_cachefile->close();
- delete header->m_cachefile;
- }
-
- // delete the last open bitmaps
-
- while (!header->locked_pages.empty()) {
- FreeImage_Unload(header->locked_pages.begin()->first);
-
- header->locked_pages.erase(header->locked_pages.begin()->first);
- }
-
- // get rid of the IO structure
-
- delete header->io;
-
- // delete the filename
-
- if(header->m_filename) {
- delete[] header->m_filename;
- }
-
- // delete the FIMULTIBITMAPHEADER
-
- delete header;
- }
-
- delete bitmap;
-
- return success;
- }
-
- return FALSE;
-}
-
-int DLL_CALLCONV
-FreeImage_GetPageCount(FIMULTIBITMAP *bitmap) {
- if (bitmap) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- if (header->page_count == -1) {
- header->page_count = 0;
-
- for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
- switch((*i)->m_type) {
- case BLOCK_CONTINUEUS :
- header->page_count += ((BlockContinueus *)(*i))->m_end - ((BlockContinueus *)(*i))->m_start + 1;
- break;
-
- case BLOCK_REFERENCE :
- header->page_count++;
- break;
- }
- }
- }
-
- return header->page_count;
- }
-
- return 0;
-}
-
-static BlockReference*
-FreeImage_SavePageToBlock(MULTIBITMAPHEADER *header, FIBITMAP *data) {
- if (header->read_only || !header->locked_pages.empty())
- return NULL;
-
- DWORD compressed_size = 0;
- BYTE *compressed_data = NULL;
-
- // compress the bitmap data
-
- // open a memory handle
- FIMEMORY *hmem = FreeImage_OpenMemory();
- if(hmem==NULL) return NULL;
- // save the file to memory
- if(!FreeImage_SaveToMemory(header->cache_fif, data, hmem, 0)) {
- FreeImage_CloseMemory(hmem);
- return NULL;
- }
- // get the buffer from the memory stream
- if(!FreeImage_AcquireMemory(hmem, &compressed_data, &compressed_size)) {
- FreeImage_CloseMemory(hmem);
- return NULL;
- }
-
- // write the compressed data to the cache
- int ref = header->m_cachefile->writeFile(compressed_data, compressed_size);
- // get rid of the compressed data
- FreeImage_CloseMemory(hmem);
-
- return new(std::nothrow) BlockReference(ref, compressed_size);
-}
-
-void DLL_CALLCONV
-FreeImage_AppendPage(FIMULTIBITMAP *bitmap, FIBITMAP *data) {
- if (!bitmap || !data)
- return;
-
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- BlockReference *block = FreeImage_SavePageToBlock(header, data);
- if(block==NULL) return;
-
- // add the block
- header->m_blocks.push_back((BlockTypeS *)block);
- header->changed = TRUE;
- header->page_count = -1;
-}
-
-void DLL_CALLCONV
-FreeImage_InsertPage(FIMULTIBITMAP *bitmap, int page, FIBITMAP *data) {
- if (!bitmap || !data)
- return;
-
- if (page >= FreeImage_GetPageCount(bitmap))
- return;
-
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- BlockReference *block = FreeImage_SavePageToBlock(header, data);
- if(block==NULL) return;
-
- // add a block
- if (page > 0) {
- BlockListIterator block_source = FreeImage_FindBlock(bitmap, page);
-
- header->m_blocks.insert(block_source, (BlockTypeS *)block);
- } else {
- header->m_blocks.push_front((BlockTypeS *)block);
- }
-
- header->changed = TRUE;
- header->page_count = -1;
-}
-
-void DLL_CALLCONV
-FreeImage_DeletePage(FIMULTIBITMAP *bitmap, int page) {
- if (bitmap) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- if ((!header->read_only) && (header->locked_pages.empty())) {
- if (FreeImage_GetPageCount(bitmap) > 1) {
- BlockListIterator i = FreeImage_FindBlock(bitmap, page);
-
- if (i != header->m_blocks.end()) {
- switch((*i)->m_type) {
- case BLOCK_CONTINUEUS :
- delete *i;
- header->m_blocks.erase(i);
- break;
-
- case BLOCK_REFERENCE :
- header->m_cachefile->deleteFile(((BlockReference *)(*i))->m_reference);
- delete *i;
- header->m_blocks.erase(i);
- break;
- }
-
- header->changed = TRUE;
- header->page_count = -1;
- }
- }
- }
- }
-}
-
-
-FIBITMAP * DLL_CALLCONV
-FreeImage_LockPage(FIMULTIBITMAP *bitmap, int page) {
- if (bitmap) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- // only lock if the page wasn't locked before...
-
- for (std::map<FIBITMAP *, int>::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) {
- if (i->second == page) {
- return NULL;
- }
- }
-
- // open the bitmap
-
- header->io->seek_proc(header->handle, 0, SEEK_SET);
-
- void *data = FreeImage_Open(header->node, header->io, header->handle, TRUE);
-
- // load the bitmap data
-
- if (data != NULL) {
- FIBITMAP *dib = (header->node->m_plugin->load_proc != NULL) ? header->node->m_plugin->load_proc(header->io, header->handle, page, header->load_flags, data) : NULL;
-
- // close the file
-
- FreeImage_Close(header->node, header->io, header->handle, data);
-
- // if there was still another bitmap open, get rid of it
-
- if (dib) {
- header->locked_pages[dib] = page;
-
- return dib;
- }
-
- return NULL;
- }
- }
-
- return NULL;
-}
-
-void DLL_CALLCONV
-FreeImage_UnlockPage(FIMULTIBITMAP *bitmap, FIBITMAP *page, BOOL changed) {
- if ((bitmap) && (page)) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- // find out if the page we try to unlock is actually locked...
-
- if (header->locked_pages.find(page) != header->locked_pages.end()) {
- // store the bitmap compressed in the cache for later writing
-
- if (changed && !header->read_only) {
- header->changed = TRUE;
-
- // cut loose the block from the rest
-
- BlockListIterator i = FreeImage_FindBlock(bitmap, header->locked_pages[page]);
-
- // compress the data
-
- DWORD compressed_size = 0;
- BYTE *compressed_data = NULL;
-
- // open a memory handle
- FIMEMORY *hmem = FreeImage_OpenMemory();
- // save the page to memory
- FreeImage_SaveToMemory(header->cache_fif, page, hmem, 0);
- // get the buffer from the memory stream
- FreeImage_AcquireMemory(hmem, &compressed_data, &compressed_size);
-
- // write the data to the cache
-
- switch ((*i)->m_type) {
- case BLOCK_CONTINUEUS :
- {
- int iPage = header->m_cachefile->writeFile(compressed_data, compressed_size);
-
- delete (*i);
-
- *i = (BlockTypeS *)new BlockReference(iPage, compressed_size);
-
- break;
- }
-
- case BLOCK_REFERENCE :
- {
- BlockReference *reference = (BlockReference *)(*i);
-
- header->m_cachefile->deleteFile(reference->m_reference);
-
- delete (*i);
-
- int iPage = header->m_cachefile->writeFile(compressed_data, compressed_size);
-
- *i = (BlockTypeS *)new BlockReference(iPage, compressed_size);
-
- break;
- }
- }
-
- // get rid of the compressed data
-
- FreeImage_CloseMemory(hmem);
- }
-
- // reset the locked page so that another page can be locked
-
- FreeImage_Unload(page);
-
- header->locked_pages.erase(page);
- }
- }
-}
-
-BOOL DLL_CALLCONV
-FreeImage_MovePage(FIMULTIBITMAP *bitmap, int target, int source) {
- if (bitmap) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- if ((!header->read_only) && (header->locked_pages.empty())) {
- if ((target != source) && ((target >= 0) && (target < FreeImage_GetPageCount(bitmap))) && ((source >= 0) && (source < FreeImage_GetPageCount(bitmap)))) {
- BlockListIterator block_source = FreeImage_FindBlock(bitmap, target);
- BlockListIterator block_target = FreeImage_FindBlock(bitmap, source);
-
- header->m_blocks.insert(block_target, *block_source);
- header->m_blocks.erase(block_source);
-
- header->changed = TRUE;
-
- return TRUE;
- }
- }
- }
-
- return FALSE;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_GetLockedPageNumbers(FIMULTIBITMAP *bitmap, int *pages, int *count) {
- if ((bitmap) && (count)) {
- MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
-
- if ((pages == NULL) || (*count == 0)) {
- *count = (int)header->locked_pages.size();
- } else {
- int c = 0;
-
- for (std::map<FIBITMAP *, int>::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) {
- pages[c] = i->second;
-
- c++;
-
- if (c == *count)
- break;
- }
- }
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-// =====================================================================
-// Memory IO Multipage functions
-// =====================================================================
-
-FIMULTIBITMAP * DLL_CALLCONV
-FreeImage_LoadMultiBitmapFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags) {
- BOOL read_only = FALSE; // modifications (if any) will be stored into the memory cache
-
- // retrieve the plugin list to find the node belonging to this plugin
-
- PluginList *list = FreeImage_GetPluginList();
-
- if (list) {
- PluginNode *node = list->FindNodeFromFIF(fif);
-
- if (node) {
- FreeImageIO *io = new(std::nothrow) FreeImageIO;
-
- if (io) {
- SetMemoryIO(io);
-
- FIMULTIBITMAP *bitmap = new(std::nothrow) FIMULTIBITMAP;
-
- if (bitmap) {
- MULTIBITMAPHEADER *header = new(std::nothrow) MULTIBITMAPHEADER;
-
- if (header) {
- header->m_filename = NULL;
- header->node = node;
- header->fif = fif;
- header->io = io;
- header->handle = (fi_handle)stream;
- header->changed = FALSE;
- header->read_only = read_only;
- header->m_cachefile = NULL;
- header->cache_fif = fif;
- header->load_flags = flags;
-
- // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
-
- bitmap->data = header;
-
- // cache the page count
-
- header->page_count = FreeImage_InternalGetPageCount(bitmap);
-
- // allocate a continueus block to describe the bitmap
-
- header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
-
- if (!read_only) {
- // set up the cache
- CacheFile *cache_file = new(std::nothrow) CacheFile("", TRUE);
-
- if (cache_file && cache_file->open()) {
- header->m_cachefile = cache_file;
- }
- }
-
- return bitmap;
- }
-
- delete bitmap;
- }
-
- delete io;
- }
- }
- }
-
- return NULL;
-}
-
-BOOL DLL_CALLCONV
-FreeImage_SaveMultiBitmapToMemory(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FIMEMORY *stream, int flags) {
- if (stream && stream->data) {
- FreeImageIO io;
- SetMemoryIO(&io);
-
- return FreeImage_SaveMultiBitmapToHandle(fif, bitmap, &io, (fi_handle)stream, flags);
- }
-
- return FALSE;
-}
+// ========================================================== +// Multi-Page functions +// +// Design and implementation by +// - Floris van den Berg (flvdberg@wxs.nl) +// - Laurent Rocher (rocherl@club-internet.fr) +// - Steve Johnson (steve@parisgroup.net) +// - Petr Pytelka (pyta@lightcomp.com) +// - Hervé Drolon (drolon@infonie.fr) +// - Vadim Alexandrov (vadimalexandrov@users.sourceforge.net +// - Martin Dyring-Andersen (mda@spamfighter.com) +// - Volodymyr Goncharov (volodymyr.goncharov@gmail.com) +// +// 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! +// ========================================================== + +#ifdef _MSC_VER +#pragma warning (disable : 4786) // identifier was truncated to 'number' characters +#endif + +#include "CacheFile.h" +#include "FreeImageIO.h" +#include "Plugin.h" +#include "Utilities.h" +#include "FreeImage.h" + +// ---------------------------------------------------------- + +enum BlockType { BLOCK_CONTINUEUS, BLOCK_REFERENCE }; + +// ---------------------------------------------------------- + +struct BlockTypeS { + BlockType m_type; + + BlockTypeS(BlockType type) : m_type(type) { + } + virtual ~BlockTypeS() {} +}; + +struct BlockContinueus : public BlockTypeS { + int m_start; + int m_end; + + BlockContinueus(int s, int e) : BlockTypeS(BLOCK_CONTINUEUS), + m_start(s), + m_end(e) { + } +}; + +struct BlockReference : public BlockTypeS { + int m_reference; + int m_size; + + BlockReference(int r, int size) : BlockTypeS(BLOCK_REFERENCE), + m_reference(r), + m_size(size) { + } +}; + +// ---------------------------------------------------------- + +typedef std::list<BlockTypeS *> BlockList; +typedef std::list<BlockTypeS *>::iterator BlockListIterator; + +// ---------------------------------------------------------- + +FI_STRUCT (MULTIBITMAPHEADER) { + PluginNode *node; + FREE_IMAGE_FORMAT fif; + FreeImageIO *io; + fi_handle handle; + CacheFile *m_cachefile; + std::map<FIBITMAP *, int> locked_pages; + BOOL changed; + int page_count; + BlockList m_blocks; + char *m_filename; + BOOL read_only; + FREE_IMAGE_FORMAT cache_fif; + int load_flags; +}; + +// ===================================================================== +// Helper functions +// ===================================================================== + +inline void +ReplaceExtension(std::string& dst_filename, const std::string& src_filename, const std::string& dst_extension) { + size_t lastDot = src_filename.find_last_of('.'); + if (lastDot == std::string::npos) { + dst_filename = src_filename; + dst_filename += "."; + dst_filename += dst_extension; + } + else { + dst_filename = src_filename.substr(0, lastDot + 1); + dst_filename += dst_extension; + } +} + +// ===================================================================== +// Internal Multipage functions +// ===================================================================== + +inline MULTIBITMAPHEADER * +FreeImage_GetMultiBitmapHeader(FIMULTIBITMAP *bitmap) { + return (MULTIBITMAPHEADER *)bitmap->data; +} + +static BlockListIterator DLL_CALLCONV +FreeImage_FindBlock(FIMULTIBITMAP *bitmap, int position) { + assert(NULL != bitmap); + + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + // step 1: find the block that matches the given position + + int prev_count = 0; + int count = 0; + BlockListIterator i; + BlockTypeS *current_block = NULL; + + for (i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) { + prev_count = count; + + switch((*i)->m_type) { + case BLOCK_CONTINUEUS : + count += ((BlockContinueus *)(*i))->m_end - ((BlockContinueus *)(*i))->m_start + 1; + break; + + case BLOCK_REFERENCE : + count++; + break; + } + + current_block = *i; + + if (count > position) + break; + } + + // step 2: make sure we found the node. from here it gets a little complicated: + // * if the block is there, just return it + // * if the block is a series of blocks, split it in max 3 new blocks + // and return the splitted block + + if ((current_block) && (count > position)) { + switch(current_block->m_type) { + case BLOCK_REFERENCE : + return i; + + case BLOCK_CONTINUEUS : + { + BlockContinueus *block = (BlockContinueus *)current_block; + + if (block->m_start != block->m_end) { + int item = block->m_start + (position - prev_count); + + // left part + + if (item != block->m_start) { + BlockContinueus *block_a = new BlockContinueus(block->m_start, item - 1); + header->m_blocks.insert(i, (BlockTypeS *)block_a); + } + + // middle part + + BlockContinueus *block_b = new BlockContinueus(item, item); + BlockListIterator block_target = header->m_blocks.insert(i, (BlockTypeS *)block_b); + + // right part + + if (item != block->m_end) { + BlockContinueus *block_c = new BlockContinueus(item + 1, block->m_end); + header->m_blocks.insert(i, (BlockTypeS *)block_c); + } + + // remove the old block that was just splitted + + header->m_blocks.remove((BlockTypeS *)block); + delete block; + + // return the splitted block + + return block_target; + } + + return i; + } + } + } + // we should never go here ... + assert(false); + return header->m_blocks.end(); +} + +int DLL_CALLCONV +FreeImage_InternalGetPageCount(FIMULTIBITMAP *bitmap) { + if (bitmap) { + if (((MULTIBITMAPHEADER *)bitmap->data)->handle) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + header->io->seek_proc(header->handle, 0, SEEK_SET); + + void *data = FreeImage_Open(header->node, header->io, header->handle, TRUE); + + int page_count = (header->node->m_plugin->pagecount_proc != NULL) ? header->node->m_plugin->pagecount_proc(header->io, header->handle, data) : 1; + + FreeImage_Close(header->node, header->io, header->handle, data); + + return page_count; + } + } + + return 0; +} + +// ===================================================================== +// Multipage functions +// ===================================================================== + +FIMULTIBITMAP * DLL_CALLCONV +FreeImage_OpenMultiBitmap(FREE_IMAGE_FORMAT fif, const char *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory, int flags) { + + FILE *handle = NULL; + try { + // sanity check on the parameters + + if (create_new) { + read_only = FALSE; + } + + // retrieve the plugin list to find the node belonging to this plugin + + PluginList *list = FreeImage_GetPluginList(); + + if (list) { + PluginNode *node = list->FindNodeFromFIF(fif); + + if (node) { + std::auto_ptr<FreeImageIO> io (new FreeImageIO); + + SetDefaultIO(io.get()); + + if (!create_new) { + handle = fopen(filename, "rb"); + if (handle == NULL) { + return NULL; + } + } + + std::auto_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP); + std::auto_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER); + header->m_filename = new char[strlen(filename) + 1]; + strcpy(header->m_filename, filename); + header->node = node; + header->fif = fif; + header->io = io.get (); + header->handle = handle; + header->changed = FALSE; + header->read_only = read_only; + header->m_cachefile = NULL; + header->cache_fif = fif; + header->load_flags = flags; + + // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure + + bitmap->data = header.get(); + + // cache the page count + + header->page_count = FreeImage_InternalGetPageCount(bitmap.get()); + + // allocate a continueus block to describe the bitmap + + if (!create_new) { + header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1)); + } + + // set up the cache + + if (!read_only) { + std::string cache_name; + ReplaceExtension(cache_name, filename, "ficache"); + + std::auto_ptr<CacheFile> cache_file (new CacheFile(cache_name, keep_cache_in_memory)); + + if (cache_file->open()) { + // we can use release() as std::bad_alloc won't be thrown from here on + header->m_cachefile = cache_file.release(); + } else { + // an error occured ... + fclose(handle); + return NULL; + } + } + // return the multibitmap + // std::bad_alloc won't be thrown from here on + header.release(); // now owned by bitmap + io.release(); // now owned by bitmap + return bitmap.release(); // now owned by caller + } + } + } catch (std::bad_alloc &) { + /** @todo report error */ + } + if (handle) + fclose(handle); + return NULL; +} + +FIMULTIBITMAP * DLL_CALLCONV +FreeImage_OpenMultiBitmapU(FREE_IMAGE_FORMAT fif, const wchar_t *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory, int flags) { + + // convert to single character - no national chars in extensions + char *extension = (char *)malloc(wcslen(filename)+1); + unsigned int i=0; + for (; i < wcslen(filename); i++) // convert 16-bit to 8-bit + extension[i] = (char)(filename[i] & 0x00FF); + // set terminating 0 + extension[i]=0; + FIMULTIBITMAP *fRet = FreeImage_OpenMultiBitmap(fif, extension, create_new, read_only, keep_cache_in_memory, flags); + free(extension); + + return fRet; +} + +FIMULTIBITMAP * DLL_CALLCONV +FreeImage_OpenMultiBitmapFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags) { + try { + BOOL read_only = FALSE; // modifications (if any) will be stored into the memory cache + + if (io && handle) { + + // retrieve the plugin list to find the node belonging to this plugin + PluginList *list = FreeImage_GetPluginList(); + + if (list) { + PluginNode *node = list->FindNodeFromFIF(fif); + + if (node) { + std::auto_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP); + std::auto_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER); + std::auto_ptr<FreeImageIO> tmp_io (new FreeImageIO (*io)); + header->io = tmp_io.get(); + header->m_filename = NULL; + header->node = node; + header->fif = fif; + header->handle = handle; + header->changed = FALSE; + header->read_only = read_only; + header->m_cachefile = NULL; + header->cache_fif = fif; + header->load_flags = flags; + + // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure + + bitmap->data = header.get(); + + // cache the page count + + header->page_count = FreeImage_InternalGetPageCount(bitmap.get()); + + // allocate a continueus block to describe the bitmap + + header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1)); + + if (!read_only) { + // set up the cache + std::auto_ptr<CacheFile> cache_file (new CacheFile("", TRUE)); + + if (cache_file->open()) { + header->m_cachefile = cache_file.release(); + } + } + tmp_io.release(); + header.release(); + return bitmap.release(); + } + } + } + } catch (std::bad_alloc &) { + /** @todo report error */ + } + return NULL; +} + +BOOL DLL_CALLCONV +FreeImage_SaveMultiBitmapToHandle(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FreeImageIO *io, fi_handle handle, int flags) { + if(!bitmap || !bitmap->data || !io || !handle) { + return FALSE; + } + + BOOL success = TRUE; + + // retrieve the plugin list to find the node belonging to this plugin + PluginList *list = FreeImage_GetPluginList(); + + if (list) { + PluginNode *node = list->FindNodeFromFIF(fif); + + if(node) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + // dst data + void *data = FreeImage_Open(node, io, handle, FALSE); + // src data + void *data_read = NULL; + + if(header->handle) { + // open src + header->io->seek_proc(header->handle, 0, SEEK_SET); + data_read = FreeImage_Open(header->node, header->io, header->handle, TRUE); + } + + // write all the pages to the file using handle and io + + int count = 0; + + for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); i++) { + if (success) { + switch((*i)->m_type) { + case BLOCK_CONTINUEUS: + { + BlockContinueus *block = (BlockContinueus *)(*i); + + for (int j = block->m_start; j <= block->m_end; j++) { + + // load the original source data + FIBITMAP *dib = header->node->m_plugin->load_proc(header->io, header->handle, j, header->load_flags, data_read); + + // save the data + success = node->m_plugin->save_proc(io, dib, handle, count, flags, data); + count++; + + FreeImage_Unload(dib); + } + + break; + } + + case BLOCK_REFERENCE: + { + BlockReference *ref = (BlockReference *)(*i); + + // read the compressed data + + BYTE *compressed_data = (BYTE*)malloc(ref->m_size * sizeof(BYTE)); + + header->m_cachefile->readFile((BYTE *)compressed_data, ref->m_reference, ref->m_size); + + // uncompress the data + + FIMEMORY *hmem = FreeImage_OpenMemory(compressed_data, ref->m_size); + FIBITMAP *dib = FreeImage_LoadFromMemory(header->cache_fif, hmem, 0); + FreeImage_CloseMemory(hmem); + + // get rid of the buffer + free(compressed_data); + + // save the data + + success = node->m_plugin->save_proc(io, dib, handle, count, flags, data); + count++; + + // unload the dib + + FreeImage_Unload(dib); + + break; + } + } + } else { + break; + } + } + + // close the files + + FreeImage_Close(header->node, header->io, header->handle, data_read); + + FreeImage_Close(node, io, handle, data); + + return success; + } + } + + return FALSE; +} + + +BOOL DLL_CALLCONV +FreeImage_CloseMultiBitmap(FIMULTIBITMAP *bitmap, int flags) { + if (bitmap) { + BOOL success = TRUE; + + if (bitmap->data) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + // saves changes only of images loaded directly from a file + if (header->changed && header->m_filename) { + try { + // open a temp file + + std::string spool_name; + + ReplaceExtension(spool_name, header->m_filename, "fispool"); + + // open the spool file and the source file + + FILE *f = fopen(spool_name.c_str(), "w+b"); + + // saves changes + if (f == NULL) { + FreeImage_OutputMessageProc(header->fif, "Failed to open %s, %s", spool_name.c_str(), strerror(errno)); + success = FALSE; + } else { + success = FreeImage_SaveMultiBitmapToHandle(header->fif, bitmap, header->io, (fi_handle)f, flags); + + // close the files + + if (fclose(f) != 0) { + success = FALSE; + FreeImage_OutputMessageProc(header->fif, "Failed to close %s, %s", spool_name.c_str(), strerror(errno)); + } + } + if (header->handle) { + fclose((FILE *)header->handle); + } + + // applies changes to the destination file + + if (success) { + remove(header->m_filename); + success = (rename(spool_name.c_str(), header->m_filename) == 0) ? TRUE:FALSE; + if(!success) { + FreeImage_OutputMessageProc(header->fif, "Failed to rename %s to %s", spool_name.c_str(), header->m_filename); + } + } else { + remove(spool_name.c_str()); + } + } catch (std::bad_alloc &) { + success = FALSE; + } + + } else { + if (header->handle && header->m_filename) { + fclose((FILE *)header->handle); + } + } + + // clear the blocks list + + for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) { + delete *i; + } + + // flush and dispose the cache + + if (header->m_cachefile) { + header->m_cachefile->close(); + delete header->m_cachefile; + } + + // delete the last open bitmaps + + while (!header->locked_pages.empty()) { + FreeImage_Unload(header->locked_pages.begin()->first); + + header->locked_pages.erase(header->locked_pages.begin()->first); + } + + // get rid of the IO structure + + delete header->io; + + // delete the filename + + if(header->m_filename) { + delete[] header->m_filename; + } + + // delete the FIMULTIBITMAPHEADER + + delete header; + } + + delete bitmap; + + return success; + } + + return FALSE; +} + +int DLL_CALLCONV +FreeImage_GetPageCount(FIMULTIBITMAP *bitmap) { + if (bitmap) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + if (header->page_count == -1) { + header->page_count = 0; + + for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) { + switch((*i)->m_type) { + case BLOCK_CONTINUEUS : + header->page_count += ((BlockContinueus *)(*i))->m_end - ((BlockContinueus *)(*i))->m_start + 1; + break; + + case BLOCK_REFERENCE : + header->page_count++; + break; + } + } + } + + return header->page_count; + } + + return 0; +} + +static BlockReference* +FreeImage_SavePageToBlock(MULTIBITMAPHEADER *header, FIBITMAP *data) { + if (header->read_only || !header->locked_pages.empty()) + return NULL; + + DWORD compressed_size = 0; + BYTE *compressed_data = NULL; + + // compress the bitmap data + + // open a memory handle + FIMEMORY *hmem = FreeImage_OpenMemory(); + if(hmem==NULL) return NULL; + // save the file to memory + if(!FreeImage_SaveToMemory(header->cache_fif, data, hmem, 0)) { + FreeImage_CloseMemory(hmem); + return NULL; + } + // get the buffer from the memory stream + if(!FreeImage_AcquireMemory(hmem, &compressed_data, &compressed_size)) { + FreeImage_CloseMemory(hmem); + return NULL; + } + + // write the compressed data to the cache + int ref = header->m_cachefile->writeFile(compressed_data, compressed_size); + // get rid of the compressed data + FreeImage_CloseMemory(hmem); + + return new(std::nothrow) BlockReference(ref, compressed_size); +} + +void DLL_CALLCONV +FreeImage_AppendPage(FIMULTIBITMAP *bitmap, FIBITMAP *data) { + if (!bitmap || !data) + return; + + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + BlockReference *block = FreeImage_SavePageToBlock(header, data); + if(block==NULL) return; + + // add the block + header->m_blocks.push_back((BlockTypeS *)block); + header->changed = TRUE; + header->page_count = -1; +} + +void DLL_CALLCONV +FreeImage_InsertPage(FIMULTIBITMAP *bitmap, int page, FIBITMAP *data) { + if (!bitmap || !data) + return; + + if (page >= FreeImage_GetPageCount(bitmap)) + return; + + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + BlockReference *block = FreeImage_SavePageToBlock(header, data); + if(block==NULL) return; + + // add a block + if (page > 0) { + BlockListIterator block_source = FreeImage_FindBlock(bitmap, page); + + header->m_blocks.insert(block_source, (BlockTypeS *)block); + } else { + header->m_blocks.push_front((BlockTypeS *)block); + } + + header->changed = TRUE; + header->page_count = -1; +} + +void DLL_CALLCONV +FreeImage_DeletePage(FIMULTIBITMAP *bitmap, int page) { + if (bitmap) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + if ((!header->read_only) && (header->locked_pages.empty())) { + if (FreeImage_GetPageCount(bitmap) > 1) { + BlockListIterator i = FreeImage_FindBlock(bitmap, page); + + if (i != header->m_blocks.end()) { + switch((*i)->m_type) { + case BLOCK_CONTINUEUS : + delete *i; + header->m_blocks.erase(i); + break; + + case BLOCK_REFERENCE : + header->m_cachefile->deleteFile(((BlockReference *)(*i))->m_reference); + delete *i; + header->m_blocks.erase(i); + break; + } + + header->changed = TRUE; + header->page_count = -1; + } + } + } + } +} + + +FIBITMAP * DLL_CALLCONV +FreeImage_LockPage(FIMULTIBITMAP *bitmap, int page) { + if (bitmap) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + // only lock if the page wasn't locked before... + + for (std::map<FIBITMAP *, int>::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) { + if (i->second == page) { + return NULL; + } + } + + // open the bitmap + + header->io->seek_proc(header->handle, 0, SEEK_SET); + + void *data = FreeImage_Open(header->node, header->io, header->handle, TRUE); + + // load the bitmap data + + if (data != NULL) { + FIBITMAP *dib = (header->node->m_plugin->load_proc != NULL) ? header->node->m_plugin->load_proc(header->io, header->handle, page, header->load_flags, data) : NULL; + + // close the file + + FreeImage_Close(header->node, header->io, header->handle, data); + + // if there was still another bitmap open, get rid of it + + if (dib) { + header->locked_pages[dib] = page; + + return dib; + } + + return NULL; + } + } + + return NULL; +} + +void DLL_CALLCONV +FreeImage_UnlockPage(FIMULTIBITMAP *bitmap, FIBITMAP *page, BOOL changed) { + if ((bitmap) && (page)) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + // find out if the page we try to unlock is actually locked... + + if (header->locked_pages.find(page) != header->locked_pages.end()) { + // store the bitmap compressed in the cache for later writing + + if (changed && !header->read_only) { + header->changed = TRUE; + + // cut loose the block from the rest + + BlockListIterator i = FreeImage_FindBlock(bitmap, header->locked_pages[page]); + + // compress the data + + DWORD compressed_size = 0; + BYTE *compressed_data = NULL; + + // open a memory handle + FIMEMORY *hmem = FreeImage_OpenMemory(); + // save the page to memory + FreeImage_SaveToMemory(header->cache_fif, page, hmem, 0); + // get the buffer from the memory stream + FreeImage_AcquireMemory(hmem, &compressed_data, &compressed_size); + + // write the data to the cache + + switch ((*i)->m_type) { + case BLOCK_CONTINUEUS : + { + int iPage = header->m_cachefile->writeFile(compressed_data, compressed_size); + + delete (*i); + + *i = (BlockTypeS *)new BlockReference(iPage, compressed_size); + + break; + } + + case BLOCK_REFERENCE : + { + BlockReference *reference = (BlockReference *)(*i); + + header->m_cachefile->deleteFile(reference->m_reference); + + delete (*i); + + int iPage = header->m_cachefile->writeFile(compressed_data, compressed_size); + + *i = (BlockTypeS *)new BlockReference(iPage, compressed_size); + + break; + } + } + + // get rid of the compressed data + + FreeImage_CloseMemory(hmem); + } + + // reset the locked page so that another page can be locked + + FreeImage_Unload(page); + + header->locked_pages.erase(page); + } + } +} + +BOOL DLL_CALLCONV +FreeImage_MovePage(FIMULTIBITMAP *bitmap, int target, int source) { + if (bitmap) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + if ((!header->read_only) && (header->locked_pages.empty())) { + if ((target != source) && ((target >= 0) && (target < FreeImage_GetPageCount(bitmap))) && ((source >= 0) && (source < FreeImage_GetPageCount(bitmap)))) { + BlockListIterator block_source = FreeImage_FindBlock(bitmap, target); + BlockListIterator block_target = FreeImage_FindBlock(bitmap, source); + + header->m_blocks.insert(block_target, *block_source); + header->m_blocks.erase(block_source); + + header->changed = TRUE; + + return TRUE; + } + } + } + + return FALSE; +} + +BOOL DLL_CALLCONV +FreeImage_GetLockedPageNumbers(FIMULTIBITMAP *bitmap, int *pages, int *count) { + if ((bitmap) && (count)) { + MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap); + + if ((pages == NULL) || (*count == 0)) { + *count = (int)header->locked_pages.size(); + } else { + int c = 0; + + for (std::map<FIBITMAP *, int>::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) { + pages[c] = i->second; + + c++; + + if (c == *count) + break; + } + } + + return TRUE; + } + + return FALSE; +} + +// ===================================================================== +// Memory IO Multipage functions +// ===================================================================== + +FIMULTIBITMAP * DLL_CALLCONV +FreeImage_LoadMultiBitmapFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags) { + BOOL read_only = FALSE; // modifications (if any) will be stored into the memory cache + + // retrieve the plugin list to find the node belonging to this plugin + + PluginList *list = FreeImage_GetPluginList(); + + if (list) { + PluginNode *node = list->FindNodeFromFIF(fif); + + if (node) { + FreeImageIO *io = new(std::nothrow) FreeImageIO; + + if (io) { + SetMemoryIO(io); + + FIMULTIBITMAP *bitmap = new(std::nothrow) FIMULTIBITMAP; + + if (bitmap) { + MULTIBITMAPHEADER *header = new(std::nothrow) MULTIBITMAPHEADER; + + if (header) { + header->m_filename = NULL; + header->node = node; + header->fif = fif; + header->io = io; + header->handle = (fi_handle)stream; + header->changed = FALSE; + header->read_only = read_only; + header->m_cachefile = NULL; + header->cache_fif = fif; + header->load_flags = flags; + + // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure + + bitmap->data = header; + + // cache the page count + + header->page_count = FreeImage_InternalGetPageCount(bitmap); + + // allocate a continueus block to describe the bitmap + + header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1)); + + if (!read_only) { + // set up the cache + CacheFile *cache_file = new(std::nothrow) CacheFile("", TRUE); + + if (cache_file && cache_file->open()) { + header->m_cachefile = cache_file; + } + } + + return bitmap; + } + + delete bitmap; + } + + delete io; + } + } + } + + return NULL; +} + +BOOL DLL_CALLCONV +FreeImage_SaveMultiBitmapToMemory(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FIMEMORY *stream, int flags) { + if (stream && stream->data) { + FreeImageIO io; + SetMemoryIO(&io); + + return FreeImage_SaveMultiBitmapToHandle(fif, bitmap, &io, (fi_handle)stream, flags); + } + + return FALSE; +} |