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