blob: c557e02e7268dfe04fc6be3e4efcd460c9175305 [file] [log] [blame]
// ==========================================================
// 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::unique_ptr<FreeImageIO> io (new FreeImageIO);
SetDefaultIO(io.get());
if (!create_new) {
handle = fopen(filename, "rb");
if (handle == NULL) {
return NULL;
}
}
std::unique_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP);
std::unique_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::unique_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_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::unique_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP);
std::unique_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER);
std::unique_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::unique_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;
}