PageRenderTime 111ms CodeModel.GetById 48ms app.highlight 56ms RepoModel.GetById 1ms app.codeStats 0ms

/src/FreeImage/Source/FreeImage/MultiPage.cpp

https://bitbucket.org/cabalistic/ogredeps/
C++ | 974 lines | 597 code | 253 blank | 124 comment | 140 complexity | fece4a731154b7b8c0f3ca12f79e39f6 MD5 | raw file
  1// ==========================================================
  2// Multi-Page functions
  3//
  4// Design and implementation by
  5// - Floris van den Berg (flvdberg@wxs.nl)
  6// - Laurent Rocher (rocherl@club-internet.fr)
  7// - Steve Johnson (steve@parisgroup.net)
  8// - Petr Pytelka (pyta@lightcomp.com)
  9// - Hervé Drolon (drolon@infonie.fr)
 10// - Vadim Alexandrov (vadimalexandrov@users.sourceforge.net
 11// - Martin Dyring-Andersen (mda@spamfighter.com)
 12// - Volodymyr Goncharov (volodymyr.goncharov@gmail.com)
 13//
 14// This file is part of FreeImage 3
 15//
 16// COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
 17// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
 18// THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
 19// OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
 20// CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
 21// THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
 22// SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
 23// PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
 24// THIS DISCLAIMER.
 25//
 26// Use at your own risk!
 27// ==========================================================
 28
 29#ifdef _MSC_VER 
 30#pragma warning (disable : 4786) // identifier was truncated to 'number' characters
 31#endif
 32
 33#include "CacheFile.h"
 34#include "FreeImageIO.h"
 35#include "Plugin.h"
 36#include "Utilities.h"
 37#include "FreeImage.h"
 38
 39// ----------------------------------------------------------
 40
 41enum BlockType { BLOCK_CONTINUEUS, BLOCK_REFERENCE };
 42
 43// ----------------------------------------------------------
 44
 45struct BlockTypeS {
 46	BlockType m_type;
 47
 48	BlockTypeS(BlockType type) : m_type(type) {
 49	}
 50	virtual ~BlockTypeS() {}
 51};
 52
 53struct BlockContinueus : public BlockTypeS {
 54	int       m_start;
 55	int       m_end;
 56
 57	BlockContinueus(int s, int e) : BlockTypeS(BLOCK_CONTINUEUS),
 58	m_start(s),
 59	m_end(e) {
 60	}	
 61};
 62
 63struct BlockReference : public BlockTypeS {
 64	int       m_reference;
 65	int       m_size;
 66
 67	BlockReference(int r, int size) : BlockTypeS(BLOCK_REFERENCE),
 68	m_reference(r),
 69	m_size(size) {
 70	}
 71};
 72
 73// ----------------------------------------------------------
 74
 75typedef std::list<BlockTypeS *> BlockList;
 76typedef std::list<BlockTypeS *>::iterator BlockListIterator;
 77
 78// ----------------------------------------------------------
 79
 80FI_STRUCT (MULTIBITMAPHEADER) {
 81	PluginNode *node;
 82	FREE_IMAGE_FORMAT fif;
 83	FreeImageIO *io;
 84	fi_handle handle;
 85	CacheFile *m_cachefile;
 86	std::map<FIBITMAP *, int> locked_pages;
 87	BOOL changed;
 88	int page_count;
 89	BlockList m_blocks;
 90	char *m_filename;
 91	BOOL read_only;
 92	FREE_IMAGE_FORMAT cache_fif;
 93	int load_flags;
 94};
 95
 96// =====================================================================
 97// Helper functions
 98// =====================================================================
 99
100inline void
101ReplaceExtension(std::string& dst_filename, const std::string& src_filename, const std::string& dst_extension) {
102	size_t lastDot = src_filename.find_last_of('.');
103	if (lastDot == std::string::npos) {
104		dst_filename = src_filename;
105		dst_filename += ".";
106		dst_filename += dst_extension;
107	}
108	else {
109		dst_filename = src_filename.substr(0, lastDot + 1);
110		dst_filename += dst_extension;
111	}
112}
113
114// =====================================================================
115// Internal Multipage functions
116// =====================================================================
117
118inline MULTIBITMAPHEADER *
119FreeImage_GetMultiBitmapHeader(FIMULTIBITMAP *bitmap) {
120	return (MULTIBITMAPHEADER *)bitmap->data;
121}
122
123static BlockListIterator DLL_CALLCONV
124FreeImage_FindBlock(FIMULTIBITMAP *bitmap, int position) {
125	assert(NULL != bitmap);
126
127	MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
128
129	// step 1: find the block that matches the given position
130
131	int prev_count = 0;
132	int count = 0;
133	BlockListIterator i;
134	BlockTypeS *current_block = NULL;
135
136	for (i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
137		prev_count = count;
138
139		switch((*i)->m_type) {
140			case BLOCK_CONTINUEUS :
141				count += ((BlockContinueus *)(*i))->m_end - ((BlockContinueus *)(*i))->m_start + 1;
142				break;
143
144			case BLOCK_REFERENCE :
145				count++;
146				break;
147		}
148
149		current_block = *i;
150
151		if (count > position)
152			break;
153	}
154
155	// step 2: make sure we found the node. from here it gets a little complicated:
156	// * if the block is there, just return it
157	// * if the block is a series of blocks, split it in max 3 new blocks
158	//   and return the splitted block
159
160	if ((current_block) && (count > position)) {
161		switch(current_block->m_type) {
162			case BLOCK_REFERENCE :
163				return i;
164
165			case BLOCK_CONTINUEUS :
166			{
167				BlockContinueus *block = (BlockContinueus *)current_block;
168
169				if (block->m_start != block->m_end) {
170					int item = block->m_start + (position - prev_count);
171
172					// left part
173
174					if (item != block->m_start) {
175						BlockContinueus *block_a = new BlockContinueus(block->m_start, item - 1);
176						header->m_blocks.insert(i, (BlockTypeS *)block_a);
177					}
178
179					// middle part
180
181					BlockContinueus *block_b = new BlockContinueus(item, item);
182					BlockListIterator block_target = header->m_blocks.insert(i, (BlockTypeS *)block_b);
183
184					// right part
185
186					if (item != block->m_end) {
187						BlockContinueus *block_c = new BlockContinueus(item + 1, block->m_end);
188						header->m_blocks.insert(i, (BlockTypeS *)block_c);
189					}
190
191					// remove the old block that was just splitted
192
193					header->m_blocks.remove((BlockTypeS *)block);
194					delete block;
195
196					// return the splitted block
197					
198					return block_target;
199				}
200
201				return i;
202			}
203		}
204	}
205	// we should never go here ...
206	assert(false);
207	return header->m_blocks.end();
208}
209
210int DLL_CALLCONV
211FreeImage_InternalGetPageCount(FIMULTIBITMAP *bitmap) {	
212	if (bitmap) {
213		if (((MULTIBITMAPHEADER *)bitmap->data)->handle) {
214			MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
215
216			header->io->seek_proc(header->handle, 0, SEEK_SET);
217
218   			void *data = FreeImage_Open(header->node, header->io, header->handle, TRUE);
219
220			int page_count = (header->node->m_plugin->pagecount_proc != NULL) ? header->node->m_plugin->pagecount_proc(header->io, header->handle, data) : 1;
221
222			FreeImage_Close(header->node, header->io, header->handle, data);
223
224			return page_count;
225		}
226	}
227
228	return 0;
229}
230
231// =====================================================================
232// Multipage functions
233// =====================================================================
234
235FIMULTIBITMAP * DLL_CALLCONV
236FreeImage_OpenMultiBitmap(FREE_IMAGE_FORMAT fif, const char *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory, int flags) {
237
238	FILE *handle = NULL;
239	try {
240		// sanity check on the parameters
241
242		if (create_new) {
243			read_only = FALSE;
244		}
245
246		// retrieve the plugin list to find the node belonging to this plugin
247
248		PluginList *list = FreeImage_GetPluginList();
249
250		if (list) {
251			PluginNode *node = list->FindNodeFromFIF(fif);
252
253			if (node) {
254				std::auto_ptr<FreeImageIO> io (new FreeImageIO);
255
256				SetDefaultIO(io.get());
257
258				if (!create_new) {
259					handle = fopen(filename, "rb");
260					if (handle == NULL) {
261						return NULL;
262					}
263				}
264
265				std::auto_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP);
266				std::auto_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER);
267				header->m_filename = new char[strlen(filename) + 1];
268				strcpy(header->m_filename, filename);
269				header->node = node;
270				header->fif = fif;
271				header->io = io.get ();
272				header->handle = handle;						
273				header->changed = FALSE;						
274				header->read_only = read_only;
275				header->m_cachefile = NULL;
276				header->cache_fif = fif;
277				header->load_flags = flags;
278
279				// store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
280
281				bitmap->data = header.get();
282
283				// cache the page count
284
285				header->page_count = FreeImage_InternalGetPageCount(bitmap.get());
286
287				// allocate a continueus block to describe the bitmap
288
289				if (!create_new) {
290					header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
291				}
292
293				// set up the cache
294
295				if (!read_only) {
296					std::string cache_name;
297					ReplaceExtension(cache_name, filename, "ficache");
298
299					std::auto_ptr<CacheFile> cache_file (new CacheFile(cache_name, keep_cache_in_memory));
300
301					if (cache_file->open()) {
302						// we can use release() as std::bad_alloc won't be thrown from here on
303						header->m_cachefile = cache_file.release();
304					} else {
305						// an error occured ...
306						fclose(handle);
307						return NULL;
308					}
309				}
310				// return the multibitmap
311				// std::bad_alloc won't be thrown from here on
312				header.release(); // now owned by bitmap
313				io.release();	  // now owned by bitmap
314				return bitmap.release(); // now owned by caller
315			}
316		}
317	} catch (std::bad_alloc &) {
318		/** @todo report error */
319	}
320	if (handle)
321		fclose(handle);
322	return NULL;
323}
324
325FIMULTIBITMAP * DLL_CALLCONV
326FreeImage_OpenMultiBitmapFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags) {
327	try {
328		BOOL read_only = FALSE;	// modifications (if any) will be stored into the memory cache
329
330		if (io && handle) {
331		
332			// retrieve the plugin list to find the node belonging to this plugin
333			PluginList *list = FreeImage_GetPluginList();
334		
335			if (list) {
336				PluginNode *node = list->FindNodeFromFIF(fif);
337			
338				if (node) {
339					std::auto_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP);
340					std::auto_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER);
341					std::auto_ptr<FreeImageIO> tmp_io (new FreeImageIO (*io));
342					header->io = tmp_io.get();
343					header->m_filename = NULL;
344					header->node = node;
345					header->fif = fif;
346					header->handle = handle;						
347					header->changed = FALSE;						
348					header->read_only = read_only;	
349					header->m_cachefile = NULL;
350					header->cache_fif = fif;
351					header->load_flags = flags;
352							
353					// store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
354
355					bitmap->data = header.get();
356
357					// cache the page count
358
359					header->page_count = FreeImage_InternalGetPageCount(bitmap.get());
360
361					// allocate a continueus block to describe the bitmap
362
363					header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
364
365					if (!read_only) {
366						// set up the cache
367						std::auto_ptr<CacheFile> cache_file (new CacheFile("", TRUE));
368						
369						if (cache_file->open()) {
370							header->m_cachefile = cache_file.release();
371						}
372					}
373					tmp_io.release();
374					header.release();
375					return bitmap.release();
376				}
377			}
378		}
379	} catch (std::bad_alloc &) {
380		/** @todo report error */
381	}
382	return NULL;
383}
384
385BOOL DLL_CALLCONV
386FreeImage_SaveMultiBitmapToHandle(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FreeImageIO *io, fi_handle handle, int flags) {
387	if(!bitmap || !bitmap->data || !io || !handle) {
388		return FALSE;
389	}
390
391	BOOL success = TRUE;
392
393	// retrieve the plugin list to find the node belonging to this plugin
394	PluginList *list = FreeImage_GetPluginList();
395	
396	if (list) {
397		PluginNode *node = list->FindNodeFromFIF(fif);
398
399		if(node) {
400			MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
401			
402			// dst data
403			void *data = FreeImage_Open(node, io, handle, FALSE);
404			// src data
405			void *data_read = NULL;
406			
407			if(header->handle) {
408				// open src
409				header->io->seek_proc(header->handle, 0, SEEK_SET);
410				data_read = FreeImage_Open(header->node, header->io, header->handle, TRUE);
411			}
412			
413			// write all the pages to the file using handle and io
414			
415			int count = 0;
416			
417			for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); i++) {
418				if (success) {
419					switch((*i)->m_type) {
420						case BLOCK_CONTINUEUS:
421						{
422							BlockContinueus *block = (BlockContinueus *)(*i);
423							
424							for (int j = block->m_start; j <= block->m_end; j++) {
425
426								// load the original source data
427								FIBITMAP *dib = header->node->m_plugin->load_proc(header->io, header->handle, j, header->load_flags, data_read);
428								
429								// save the data
430								success = node->m_plugin->save_proc(io, dib, handle, count, flags, data);
431								count++;
432								
433								FreeImage_Unload(dib);
434							}
435							
436							break;
437						}
438						
439						case BLOCK_REFERENCE:
440						{
441							BlockReference *ref = (BlockReference *)(*i);
442							
443							// read the compressed data
444							
445							BYTE *compressed_data = (BYTE*)malloc(ref->m_size * sizeof(BYTE));
446							
447							header->m_cachefile->readFile((BYTE *)compressed_data, ref->m_reference, ref->m_size);
448							
449							// uncompress the data
450							
451							FIMEMORY *hmem = FreeImage_OpenMemory(compressed_data, ref->m_size);
452							FIBITMAP *dib = FreeImage_LoadFromMemory(header->cache_fif, hmem, 0);
453							FreeImage_CloseMemory(hmem);
454							
455							// get rid of the buffer
456							free(compressed_data);
457							
458							// save the data
459							
460							success = node->m_plugin->save_proc(io, dib, handle, count, flags, data);
461							count++;
462							
463							// unload the dib
464
465							FreeImage_Unload(dib);
466
467							break;
468						}
469					}
470				} else {
471					break;
472				}
473			}
474			
475			// close the files
476			
477			FreeImage_Close(header->node, header->io, header->handle, data_read);
478
479			FreeImage_Close(node, io, handle, data); 
480			
481			return success;
482		}
483	}
484
485	return FALSE;
486}
487
488
489BOOL DLL_CALLCONV
490FreeImage_CloseMultiBitmap(FIMULTIBITMAP *bitmap, int flags) {
491	if (bitmap) {
492		BOOL success = TRUE;
493		
494		if (bitmap->data) {
495			MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);			
496			
497			// saves changes only of images loaded directly from a file
498			if (header->changed && header->m_filename) {
499				try {
500					// open a temp file
501
502					std::string spool_name;
503
504					ReplaceExtension(spool_name, header->m_filename, "fispool");
505
506					// open the spool file and the source file
507        
508					FILE *f = fopen(spool_name.c_str(), "w+b");
509				
510					// saves changes
511					if (f == NULL) {
512						FreeImage_OutputMessageProc(header->fif, "Failed to open %s, %s", spool_name.c_str(), strerror(errno));
513						success = FALSE;
514					} else {
515						success = FreeImage_SaveMultiBitmapToHandle(header->fif, bitmap, header->io, (fi_handle)f, flags);
516
517						// close the files
518
519						if (fclose(f) != 0) {
520							success = FALSE;
521							FreeImage_OutputMessageProc(header->fif, "Failed to close %s, %s", spool_name.c_str(), strerror(errno));
522						}
523					}
524					if (header->handle) {
525						fclose((FILE *)header->handle);
526					}
527				
528					// applies changes to the destination file
529
530					if (success) {
531						remove(header->m_filename);
532						success = (rename(spool_name.c_str(), header->m_filename) == 0) ? TRUE:FALSE;
533						if(!success) {
534							FreeImage_OutputMessageProc(header->fif, "Failed to rename %s to %s", spool_name.c_str(), header->m_filename);
535						}
536					} else {
537						remove(spool_name.c_str());
538					}
539				} catch (std::bad_alloc &) {
540					success = FALSE;
541				}
542
543			} else {
544				if (header->handle && header->m_filename) {
545					fclose((FILE *)header->handle);
546				}
547			}
548
549			// clear the blocks list
550
551			for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
552				delete *i;
553			}
554
555			// flush and dispose the cache
556
557			if (header->m_cachefile) {
558				header->m_cachefile->close();
559				delete header->m_cachefile;
560			}
561
562			// delete the last open bitmaps
563
564			while (!header->locked_pages.empty()) {
565				FreeImage_Unload(header->locked_pages.begin()->first);
566
567				header->locked_pages.erase(header->locked_pages.begin()->first);
568			}
569
570			// get rid of the IO structure
571
572			delete header->io;
573
574			// delete the filename
575
576			if(header->m_filename) {
577				delete[] header->m_filename;
578			}
579
580			// delete the FIMULTIBITMAPHEADER
581
582			delete header;
583		}
584
585		delete bitmap;
586
587		return success;
588	}
589
590	return FALSE;
591}
592
593int DLL_CALLCONV
594FreeImage_GetPageCount(FIMULTIBITMAP *bitmap) {
595	if (bitmap) {
596		MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
597
598		if (header->page_count == -1) {
599			header->page_count = 0;
600
601			for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
602				switch((*i)->m_type) {
603					case BLOCK_CONTINUEUS :
604						header->page_count += ((BlockContinueus *)(*i))->m_end - ((BlockContinueus *)(*i))->m_start + 1;
605						break;
606
607					case BLOCK_REFERENCE :
608						header->page_count++;
609						break;
610				}
611			}
612		}
613
614		return header->page_count;
615	}
616
617	return 0;
618}
619
620static BlockReference* 
621FreeImage_SavePageToBlock(MULTIBITMAPHEADER *header, FIBITMAP *data) {
622	if (header->read_only || !header->locked_pages.empty())
623		return NULL;
624
625	DWORD compressed_size = 0;
626	BYTE *compressed_data = NULL;
627
628	// compress the bitmap data
629
630	// open a memory handle
631	FIMEMORY *hmem = FreeImage_OpenMemory();
632	if(hmem==NULL) return NULL;
633	// save the file to memory
634	if(!FreeImage_SaveToMemory(header->cache_fif, data, hmem, 0)) {
635		FreeImage_CloseMemory(hmem);
636		return NULL;
637	}
638	// get the buffer from the memory stream
639	if(!FreeImage_AcquireMemory(hmem, &compressed_data, &compressed_size)) {
640		FreeImage_CloseMemory(hmem);
641		return NULL;
642	}
643
644	// write the compressed data to the cache
645	int ref = header->m_cachefile->writeFile(compressed_data, compressed_size);
646	// get rid of the compressed data
647	FreeImage_CloseMemory(hmem);
648
649	return new(std::nothrow) BlockReference(ref, compressed_size);
650}
651
652void DLL_CALLCONV
653FreeImage_AppendPage(FIMULTIBITMAP *bitmap, FIBITMAP *data) {
654	if (!bitmap || !data) 
655		return;
656
657	MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
658
659	BlockReference *block = FreeImage_SavePageToBlock(header, data);
660	if(block==NULL) return;
661
662	// add the block
663	header->m_blocks.push_back((BlockTypeS *)block);
664	header->changed = TRUE;
665	header->page_count = -1;
666}
667
668void DLL_CALLCONV
669FreeImage_InsertPage(FIMULTIBITMAP *bitmap, int page, FIBITMAP *data) {
670	if (!bitmap || !data) 
671		return;
672
673	if (page >= FreeImage_GetPageCount(bitmap)) 
674		return;
675			
676	MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
677
678	BlockReference *block = FreeImage_SavePageToBlock(header, data);
679	if(block==NULL) return;
680
681	// add a block
682	if (page > 0) {
683		BlockListIterator block_source = FreeImage_FindBlock(bitmap, page);		
684
685		header->m_blocks.insert(block_source, (BlockTypeS *)block);
686	} else {
687		header->m_blocks.push_front((BlockTypeS *)block);
688	}
689
690	header->changed = TRUE;
691	header->page_count = -1;
692}
693
694void DLL_CALLCONV
695FreeImage_DeletePage(FIMULTIBITMAP *bitmap, int page) {
696	if (bitmap) {
697		MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
698
699		if ((!header->read_only) && (header->locked_pages.empty())) {
700			if (FreeImage_GetPageCount(bitmap) > 1) {
701				BlockListIterator i = FreeImage_FindBlock(bitmap, page);
702
703				if (i != header->m_blocks.end()) {
704					switch((*i)->m_type) {
705						case BLOCK_CONTINUEUS :
706							delete *i;
707							header->m_blocks.erase(i);
708							break;
709
710						case BLOCK_REFERENCE :
711							header->m_cachefile->deleteFile(((BlockReference *)(*i))->m_reference);
712							delete *i;
713							header->m_blocks.erase(i);
714							break;
715					}
716
717					header->changed = TRUE;
718					header->page_count = -1;
719				}
720			}
721		}
722	}
723}
724
725
726FIBITMAP * DLL_CALLCONV
727FreeImage_LockPage(FIMULTIBITMAP *bitmap, int page) {
728	if (bitmap) {
729		MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
730
731		// only lock if the page wasn't locked before...
732
733		for (std::map<FIBITMAP *, int>::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) {
734			if (i->second == page) {
735				return NULL;
736			}
737		}
738
739		// open the bitmap
740
741		header->io->seek_proc(header->handle, 0, SEEK_SET);
742
743   		void *data = FreeImage_Open(header->node, header->io, header->handle, TRUE);
744
745		// load the bitmap data
746
747		if (data != NULL) {
748			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;
749
750			// close the file
751
752			FreeImage_Close(header->node, header->io, header->handle, data);
753
754			// if there was still another bitmap open, get rid of it
755
756			if (dib) {
757				header->locked_pages[dib] = page;
758
759				return dib;
760			}	
761
762			return NULL;
763		}
764	}
765
766	return NULL;
767}
768
769void DLL_CALLCONV
770FreeImage_UnlockPage(FIMULTIBITMAP *bitmap, FIBITMAP *page, BOOL changed) {
771	if ((bitmap) && (page)) {
772		MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
773
774		// find out if the page we try to unlock is actually locked...
775
776		if (header->locked_pages.find(page) != header->locked_pages.end()) {
777			// store the bitmap compressed in the cache for later writing
778
779			if (changed && !header->read_only) {
780				header->changed = TRUE;
781
782				// cut loose the block from the rest
783
784				BlockListIterator i = FreeImage_FindBlock(bitmap, header->locked_pages[page]);
785
786				// compress the data
787
788				DWORD compressed_size = 0;
789				BYTE *compressed_data = NULL;
790
791				// open a memory handle
792				FIMEMORY *hmem = FreeImage_OpenMemory();
793				// save the page to memory
794				FreeImage_SaveToMemory(header->cache_fif, page, hmem, 0);
795				// get the buffer from the memory stream
796				FreeImage_AcquireMemory(hmem, &compressed_data, &compressed_size);
797
798				// write the data to the cache
799
800				switch ((*i)->m_type) {
801					case BLOCK_CONTINUEUS :
802					{
803						int iPage = header->m_cachefile->writeFile(compressed_data, compressed_size);
804
805						delete (*i);
806
807						*i = (BlockTypeS *)new BlockReference(iPage, compressed_size);
808
809						break;
810					}
811
812					case BLOCK_REFERENCE :
813					{
814						BlockReference *reference = (BlockReference *)(*i);
815
816						header->m_cachefile->deleteFile(reference->m_reference);
817
818						delete (*i);
819
820						int iPage = header->m_cachefile->writeFile(compressed_data, compressed_size);
821
822						*i = (BlockTypeS *)new BlockReference(iPage, compressed_size);
823
824						break;
825					}
826				}
827
828				// get rid of the compressed data
829
830				FreeImage_CloseMemory(hmem);
831			}
832
833			// reset the locked page so that another page can be locked
834
835			FreeImage_Unload(page);
836
837			header->locked_pages.erase(page);
838		}
839	}
840}
841
842BOOL DLL_CALLCONV
843FreeImage_MovePage(FIMULTIBITMAP *bitmap, int target, int source) {
844	if (bitmap) {
845		MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
846
847		if ((!header->read_only) && (header->locked_pages.empty())) {
848			if ((target != source) && ((target >= 0) && (target < FreeImage_GetPageCount(bitmap))) && ((source >= 0) && (source < FreeImage_GetPageCount(bitmap)))) {
849				BlockListIterator block_source = FreeImage_FindBlock(bitmap, target);
850				BlockListIterator block_target = FreeImage_FindBlock(bitmap, source);
851
852				header->m_blocks.insert(block_target, *block_source);			
853				header->m_blocks.erase(block_source);
854
855				header->changed = TRUE;
856
857				return TRUE;
858			}
859		}
860	}
861
862	return FALSE;
863}
864
865BOOL DLL_CALLCONV
866FreeImage_GetLockedPageNumbers(FIMULTIBITMAP *bitmap, int *pages, int *count) {
867	if ((bitmap) && (count)) {
868		MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
869
870		if ((pages == NULL) || (*count == 0)) {
871			*count = (int)header->locked_pages.size();
872		} else {
873			int c = 0;
874
875			for (std::map<FIBITMAP *, int>::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) {
876				pages[c] = i->second;
877
878				c++;
879
880				if (c == *count)
881					break;
882			}
883		}
884
885		return TRUE;
886	}
887
888	return FALSE;
889}
890
891// =====================================================================
892// Memory IO Multipage functions
893// =====================================================================
894
895FIMULTIBITMAP * DLL_CALLCONV
896FreeImage_LoadMultiBitmapFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags) {
897	BOOL read_only = FALSE;	// modifications (if any) will be stored into the memory cache
898
899	// retrieve the plugin list to find the node belonging to this plugin
900
901	PluginList *list = FreeImage_GetPluginList();
902
903	if (list) {
904		PluginNode *node = list->FindNodeFromFIF(fif);
905
906		if (node) {
907			FreeImageIO *io = new(std::nothrow) FreeImageIO;
908
909			if (io) {
910				SetMemoryIO(io);
911
912				FIMULTIBITMAP *bitmap = new(std::nothrow) FIMULTIBITMAP;
913
914				if (bitmap) {
915					MULTIBITMAPHEADER *header = new(std::nothrow) MULTIBITMAPHEADER;
916
917					if (header) {
918						header->m_filename = NULL;
919						header->node = node;
920						header->fif = fif;
921						header->io = io;
922						header->handle = (fi_handle)stream;						
923						header->changed = FALSE;						
924						header->read_only = read_only;
925						header->m_cachefile = NULL;
926						header->cache_fif = fif;
927						header->load_flags = flags;
928
929						// store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
930
931						bitmap->data = header;
932
933						// cache the page count
934
935						header->page_count = FreeImage_InternalGetPageCount(bitmap);
936
937						// allocate a continueus block to describe the bitmap
938
939						header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
940						
941						if (!read_only) {
942							// set up the cache
943							CacheFile *cache_file = new(std::nothrow) CacheFile("", TRUE);
944							
945							if (cache_file && cache_file->open()) {
946								header->m_cachefile = cache_file;
947							}
948						}
949
950						return bitmap;
951					}
952					
953					delete bitmap;
954				}
955				
956				delete io;
957			}
958		}
959	}
960
961	return NULL;
962}
963
964BOOL DLL_CALLCONV
965FreeImage_SaveMultiBitmapToMemory(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FIMEMORY *stream, int flags) {
966	if (stream && stream->data) {
967		FreeImageIO io;
968		SetMemoryIO(&io);
969
970		return FreeImage_SaveMultiBitmapToHandle(fif, bitmap, &io, (fi_handle)stream, flags);
971	}
972
973	return FALSE;
974}