/src/FreeImage/Source/FreeImage/MultiPage.cpp
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}