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