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

https://bitbucket.org/cabalistic/ogredeps/ · C++ · 974 lines · 597 code · 253 blank · 124 comment · 145 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. #ifdef _MSC_VER
  29. #pragma warning (disable : 4786) // identifier was truncated to 'number' characters
  30. #endif
  31. #include "CacheFile.h"
  32. #include "FreeImageIO.h"
  33. #include "Plugin.h"
  34. #include "Utilities.h"
  35. #include "FreeImage.h"
  36. // ----------------------------------------------------------
  37. enum BlockType { BLOCK_CONTINUEUS, BLOCK_REFERENCE };
  38. // ----------------------------------------------------------
  39. struct BlockTypeS {
  40. BlockType m_type;
  41. BlockTypeS(BlockType type) : m_type(type) {
  42. }
  43. virtual ~BlockTypeS() {}
  44. };
  45. struct BlockContinueus : public BlockTypeS {
  46. int m_start;
  47. int m_end;
  48. BlockContinueus(int s, int e) : BlockTypeS(BLOCK_CONTINUEUS),
  49. m_start(s),
  50. m_end(e) {
  51. }
  52. };
  53. struct BlockReference : public BlockTypeS {
  54. int m_reference;
  55. int m_size;
  56. BlockReference(int r, int size) : BlockTypeS(BLOCK_REFERENCE),
  57. m_reference(r),
  58. m_size(size) {
  59. }
  60. };
  61. // ----------------------------------------------------------
  62. typedef std::list<BlockTypeS *> BlockList;
  63. typedef std::list<BlockTypeS *>::iterator BlockListIterator;
  64. // ----------------------------------------------------------
  65. FI_STRUCT (MULTIBITMAPHEADER) {
  66. PluginNode *node;
  67. FREE_IMAGE_FORMAT fif;
  68. FreeImageIO *io;
  69. fi_handle handle;
  70. CacheFile *m_cachefile;
  71. std::map<FIBITMAP *, int> locked_pages;
  72. BOOL changed;
  73. int page_count;
  74. BlockList m_blocks;
  75. char *m_filename;
  76. BOOL read_only;
  77. FREE_IMAGE_FORMAT cache_fif;
  78. int load_flags;
  79. };
  80. // =====================================================================
  81. // Helper functions
  82. // =====================================================================
  83. inline void
  84. ReplaceExtension(std::string& dst_filename, const std::string& src_filename, const std::string& dst_extension) {
  85. size_t lastDot = src_filename.find_last_of('.');
  86. if (lastDot == std::string::npos) {
  87. dst_filename = src_filename;
  88. dst_filename += ".";
  89. dst_filename += dst_extension;
  90. }
  91. else {
  92. dst_filename = src_filename.substr(0, lastDot + 1);
  93. dst_filename += dst_extension;
  94. }
  95. }
  96. // =====================================================================
  97. // Internal Multipage functions
  98. // =====================================================================
  99. inline MULTIBITMAPHEADER *
  100. FreeImage_GetMultiBitmapHeader(FIMULTIBITMAP *bitmap) {
  101. return (MULTIBITMAPHEADER *)bitmap->data;
  102. }
  103. static BlockListIterator DLL_CALLCONV
  104. FreeImage_FindBlock(FIMULTIBITMAP *bitmap, int position) {
  105. assert(NULL != bitmap);
  106. MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
  107. // step 1: find the block that matches the given position
  108. int prev_count = 0;
  109. int count = 0;
  110. BlockListIterator i;
  111. BlockTypeS *current_block = NULL;
  112. for (i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
  113. prev_count = count;
  114. switch((*i)->m_type) {
  115. case BLOCK_CONTINUEUS :
  116. count += ((BlockContinueus *)(*i))->m_end - ((BlockContinueus *)(*i))->m_start + 1;
  117. break;
  118. case BLOCK_REFERENCE :
  119. count++;
  120. break;
  121. }
  122. current_block = *i;
  123. if (count > position)
  124. break;
  125. }
  126. // step 2: make sure we found the node. from here it gets a little complicated:
  127. // * if the block is there, just return it
  128. // * if the block is a series of blocks, split it in max 3 new blocks
  129. // and return the splitted block
  130. if ((current_block) && (count > position)) {
  131. switch(current_block->m_type) {
  132. case BLOCK_REFERENCE :
  133. return i;
  134. case BLOCK_CONTINUEUS :
  135. {
  136. BlockContinueus *block = (BlockContinueus *)current_block;
  137. if (block->m_start != block->m_end) {
  138. int item = block->m_start + (position - prev_count);
  139. // left part
  140. if (item != block->m_start) {
  141. BlockContinueus *block_a = new BlockContinueus(block->m_start, item - 1);
  142. header->m_blocks.insert(i, (BlockTypeS *)block_a);
  143. }
  144. // middle part
  145. BlockContinueus *block_b = new BlockContinueus(item, item);
  146. BlockListIterator block_target = header->m_blocks.insert(i, (BlockTypeS *)block_b);
  147. // right part
  148. if (item != block->m_end) {
  149. BlockContinueus *block_c = new BlockContinueus(item + 1, block->m_end);
  150. header->m_blocks.insert(i, (BlockTypeS *)block_c);
  151. }
  152. // remove the old block that was just splitted
  153. header->m_blocks.remove((BlockTypeS *)block);
  154. delete block;
  155. // return the splitted block
  156. return block_target;
  157. }
  158. return i;
  159. }
  160. }
  161. }
  162. // we should never go here ...
  163. assert(false);
  164. return header->m_blocks.end();
  165. }
  166. int DLL_CALLCONV
  167. FreeImage_InternalGetPageCount(FIMULTIBITMAP *bitmap) {
  168. if (bitmap) {
  169. if (((MULTIBITMAPHEADER *)bitmap->data)->handle) {
  170. MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
  171. header->io->seek_proc(header->handle, 0, SEEK_SET);
  172. void *data = FreeImage_Open(header->node, header->io, header->handle, TRUE);
  173. int page_count = (header->node->m_plugin->pagecount_proc != NULL) ? header->node->m_plugin->pagecount_proc(header->io, header->handle, data) : 1;
  174. FreeImage_Close(header->node, header->io, header->handle, data);
  175. return page_count;
  176. }
  177. }
  178. return 0;
  179. }
  180. // =====================================================================
  181. // Multipage functions
  182. // =====================================================================
  183. FIMULTIBITMAP * DLL_CALLCONV
  184. FreeImage_OpenMultiBitmap(FREE_IMAGE_FORMAT fif, const char *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory, int flags) {
  185. FILE *handle = NULL;
  186. try {
  187. // sanity check on the parameters
  188. if (create_new) {
  189. read_only = FALSE;
  190. }
  191. // retrieve the plugin list to find the node belonging to this plugin
  192. PluginList *list = FreeImage_GetPluginList();
  193. if (list) {
  194. PluginNode *node = list->FindNodeFromFIF(fif);
  195. if (node) {
  196. std::auto_ptr<FreeImageIO> io (new FreeImageIO);
  197. SetDefaultIO(io.get());
  198. if (!create_new) {
  199. handle = fopen(filename, "rb");
  200. if (handle == NULL) {
  201. return NULL;
  202. }
  203. }
  204. std::auto_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP);
  205. std::auto_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER);
  206. header->m_filename = new char[strlen(filename) + 1];
  207. strcpy(header->m_filename, filename);
  208. header->node = node;
  209. header->fif = fif;
  210. header->io = io.get ();
  211. header->handle = handle;
  212. header->changed = FALSE;
  213. header->read_only = read_only;
  214. header->m_cachefile = NULL;
  215. header->cache_fif = fif;
  216. header->load_flags = flags;
  217. // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
  218. bitmap->data = header.get();
  219. // cache the page count
  220. header->page_count = FreeImage_InternalGetPageCount(bitmap.get());
  221. // allocate a continueus block to describe the bitmap
  222. if (!create_new) {
  223. header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
  224. }
  225. // set up the cache
  226. if (!read_only) {
  227. std::string cache_name;
  228. ReplaceExtension(cache_name, filename, "ficache");
  229. std::auto_ptr<CacheFile> cache_file (new CacheFile(cache_name, keep_cache_in_memory));
  230. if (cache_file->open()) {
  231. // we can use release() as std::bad_alloc won't be thrown from here on
  232. header->m_cachefile = cache_file.release();
  233. } else {
  234. // an error occured ...
  235. fclose(handle);
  236. return NULL;
  237. }
  238. }
  239. // return the multibitmap
  240. // std::bad_alloc won't be thrown from here on
  241. header.release(); // now owned by bitmap
  242. io.release(); // now owned by bitmap
  243. return bitmap.release(); // now owned by caller
  244. }
  245. }
  246. } catch (std::bad_alloc &) {
  247. /** @todo report error */
  248. }
  249. if (handle)
  250. fclose(handle);
  251. return NULL;
  252. }
  253. FIMULTIBITMAP * DLL_CALLCONV
  254. FreeImage_OpenMultiBitmapFromHandle(FREE_IMAGE_FORMAT fif, FreeImageIO *io, fi_handle handle, int flags) {
  255. try {
  256. BOOL read_only = FALSE; // modifications (if any) will be stored into the memory cache
  257. if (io && handle) {
  258. // retrieve the plugin list to find the node belonging to this plugin
  259. PluginList *list = FreeImage_GetPluginList();
  260. if (list) {
  261. PluginNode *node = list->FindNodeFromFIF(fif);
  262. if (node) {
  263. std::auto_ptr<FIMULTIBITMAP> bitmap (new FIMULTIBITMAP);
  264. std::auto_ptr<MULTIBITMAPHEADER> header (new MULTIBITMAPHEADER);
  265. std::auto_ptr<FreeImageIO> tmp_io (new FreeImageIO (*io));
  266. header->io = tmp_io.get();
  267. header->m_filename = NULL;
  268. header->node = node;
  269. header->fif = fif;
  270. header->handle = handle;
  271. header->changed = FALSE;
  272. header->read_only = read_only;
  273. header->m_cachefile = NULL;
  274. header->cache_fif = fif;
  275. header->load_flags = flags;
  276. // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
  277. bitmap->data = header.get();
  278. // cache the page count
  279. header->page_count = FreeImage_InternalGetPageCount(bitmap.get());
  280. // allocate a continueus block to describe the bitmap
  281. header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
  282. if (!read_only) {
  283. // set up the cache
  284. std::auto_ptr<CacheFile> cache_file (new CacheFile("", TRUE));
  285. if (cache_file->open()) {
  286. header->m_cachefile = cache_file.release();
  287. }
  288. }
  289. tmp_io.release();
  290. header.release();
  291. return bitmap.release();
  292. }
  293. }
  294. }
  295. } catch (std::bad_alloc &) {
  296. /** @todo report error */
  297. }
  298. return NULL;
  299. }
  300. BOOL DLL_CALLCONV
  301. FreeImage_SaveMultiBitmapToHandle(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FreeImageIO *io, fi_handle handle, int flags) {
  302. if(!bitmap || !bitmap->data || !io || !handle) {
  303. return FALSE;
  304. }
  305. BOOL success = TRUE;
  306. // retrieve the plugin list to find the node belonging to this plugin
  307. PluginList *list = FreeImage_GetPluginList();
  308. if (list) {
  309. PluginNode *node = list->FindNodeFromFIF(fif);
  310. if(node) {
  311. MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
  312. // dst data
  313. void *data = FreeImage_Open(node, io, handle, FALSE);
  314. // src data
  315. void *data_read = NULL;
  316. if(header->handle) {
  317. // open src
  318. header->io->seek_proc(header->handle, 0, SEEK_SET);
  319. data_read = FreeImage_Open(header->node, header->io, header->handle, TRUE);
  320. }
  321. // write all the pages to the file using handle and io
  322. int count = 0;
  323. for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); i++) {
  324. if (success) {
  325. switch((*i)->m_type) {
  326. case BLOCK_CONTINUEUS:
  327. {
  328. BlockContinueus *block = (BlockContinueus *)(*i);
  329. for (int j = block->m_start; j <= block->m_end; j++) {
  330. // load the original source data
  331. FIBITMAP *dib = header->node->m_plugin->load_proc(header->io, header->handle, j, header->load_flags, data_read);
  332. // save the data
  333. success = node->m_plugin->save_proc(io, dib, handle, count, flags, data);
  334. count++;
  335. FreeImage_Unload(dib);
  336. }
  337. break;
  338. }
  339. case BLOCK_REFERENCE:
  340. {
  341. BlockReference *ref = (BlockReference *)(*i);
  342. // read the compressed data
  343. BYTE *compressed_data = (BYTE*)malloc(ref->m_size * sizeof(BYTE));
  344. header->m_cachefile->readFile((BYTE *)compressed_data, ref->m_reference, ref->m_size);
  345. // uncompress the data
  346. FIMEMORY *hmem = FreeImage_OpenMemory(compressed_data, ref->m_size);
  347. FIBITMAP *dib = FreeImage_LoadFromMemory(header->cache_fif, hmem, 0);
  348. FreeImage_CloseMemory(hmem);
  349. // get rid of the buffer
  350. free(compressed_data);
  351. // save the data
  352. success = node->m_plugin->save_proc(io, dib, handle, count, flags, data);
  353. count++;
  354. // unload the dib
  355. FreeImage_Unload(dib);
  356. break;
  357. }
  358. }
  359. } else {
  360. break;
  361. }
  362. }
  363. // close the files
  364. FreeImage_Close(header->node, header->io, header->handle, data_read);
  365. FreeImage_Close(node, io, handle, data);
  366. return success;
  367. }
  368. }
  369. return FALSE;
  370. }
  371. BOOL DLL_CALLCONV
  372. FreeImage_CloseMultiBitmap(FIMULTIBITMAP *bitmap, int flags) {
  373. if (bitmap) {
  374. BOOL success = TRUE;
  375. if (bitmap->data) {
  376. MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
  377. // saves changes only of images loaded directly from a file
  378. if (header->changed && header->m_filename) {
  379. try {
  380. // open a temp file
  381. std::string spool_name;
  382. ReplaceExtension(spool_name, header->m_filename, "fispool");
  383. // open the spool file and the source file
  384. FILE *f = fopen(spool_name.c_str(), "w+b");
  385. // saves changes
  386. if (f == NULL) {
  387. FreeImage_OutputMessageProc(header->fif, "Failed to open %s, %s", spool_name.c_str(), strerror(errno));
  388. success = FALSE;
  389. } else {
  390. success = FreeImage_SaveMultiBitmapToHandle(header->fif, bitmap, header->io, (fi_handle)f, flags);
  391. // close the files
  392. if (fclose(f) != 0) {
  393. success = FALSE;
  394. FreeImage_OutputMessageProc(header->fif, "Failed to close %s, %s", spool_name.c_str(), strerror(errno));
  395. }
  396. }
  397. if (header->handle) {
  398. fclose((FILE *)header->handle);
  399. }
  400. // applies changes to the destination file
  401. if (success) {
  402. remove(header->m_filename);
  403. success = (rename(spool_name.c_str(), header->m_filename) == 0) ? TRUE:FALSE;
  404. if(!success) {
  405. FreeImage_OutputMessageProc(header->fif, "Failed to rename %s to %s", spool_name.c_str(), header->m_filename);
  406. }
  407. } else {
  408. remove(spool_name.c_str());
  409. }
  410. } catch (std::bad_alloc &) {
  411. success = FALSE;
  412. }
  413. } else {
  414. if (header->handle && header->m_filename) {
  415. fclose((FILE *)header->handle);
  416. }
  417. }
  418. // clear the blocks list
  419. for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
  420. delete *i;
  421. }
  422. // flush and dispose the cache
  423. if (header->m_cachefile) {
  424. header->m_cachefile->close();
  425. delete header->m_cachefile;
  426. }
  427. // delete the last open bitmaps
  428. while (!header->locked_pages.empty()) {
  429. FreeImage_Unload(header->locked_pages.begin()->first);
  430. header->locked_pages.erase(header->locked_pages.begin()->first);
  431. }
  432. // get rid of the IO structure
  433. delete header->io;
  434. // delete the filename
  435. if(header->m_filename) {
  436. delete[] header->m_filename;
  437. }
  438. // delete the FIMULTIBITMAPHEADER
  439. delete header;
  440. }
  441. delete bitmap;
  442. return success;
  443. }
  444. return FALSE;
  445. }
  446. int DLL_CALLCONV
  447. FreeImage_GetPageCount(FIMULTIBITMAP *bitmap) {
  448. if (bitmap) {
  449. MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
  450. if (header->page_count == -1) {
  451. header->page_count = 0;
  452. for (BlockListIterator i = header->m_blocks.begin(); i != header->m_blocks.end(); ++i) {
  453. switch((*i)->m_type) {
  454. case BLOCK_CONTINUEUS :
  455. header->page_count += ((BlockContinueus *)(*i))->m_end - ((BlockContinueus *)(*i))->m_start + 1;
  456. break;
  457. case BLOCK_REFERENCE :
  458. header->page_count++;
  459. break;
  460. }
  461. }
  462. }
  463. return header->page_count;
  464. }
  465. return 0;
  466. }
  467. static BlockReference*
  468. FreeImage_SavePageToBlock(MULTIBITMAPHEADER *header, FIBITMAP *data) {
  469. if (header->read_only || !header->locked_pages.empty())
  470. return NULL;
  471. DWORD compressed_size = 0;
  472. BYTE *compressed_data = NULL;
  473. // compress the bitmap data
  474. // open a memory handle
  475. FIMEMORY *hmem = FreeImage_OpenMemory();
  476. if(hmem==NULL) return NULL;
  477. // save the file to memory
  478. if(!FreeImage_SaveToMemory(header->cache_fif, data, hmem, 0)) {
  479. FreeImage_CloseMemory(hmem);
  480. return NULL;
  481. }
  482. // get the buffer from the memory stream
  483. if(!FreeImage_AcquireMemory(hmem, &compressed_data, &compressed_size)) {
  484. FreeImage_CloseMemory(hmem);
  485. return NULL;
  486. }
  487. // write the compressed data to the cache
  488. int ref = header->m_cachefile->writeFile(compressed_data, compressed_size);
  489. // get rid of the compressed data
  490. FreeImage_CloseMemory(hmem);
  491. return new(std::nothrow) BlockReference(ref, compressed_size);
  492. }
  493. void DLL_CALLCONV
  494. FreeImage_AppendPage(FIMULTIBITMAP *bitmap, FIBITMAP *data) {
  495. if (!bitmap || !data)
  496. return;
  497. MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
  498. BlockReference *block = FreeImage_SavePageToBlock(header, data);
  499. if(block==NULL) return;
  500. // add the block
  501. header->m_blocks.push_back((BlockTypeS *)block);
  502. header->changed = TRUE;
  503. header->page_count = -1;
  504. }
  505. void DLL_CALLCONV
  506. FreeImage_InsertPage(FIMULTIBITMAP *bitmap, int page, FIBITMAP *data) {
  507. if (!bitmap || !data)
  508. return;
  509. if (page >= FreeImage_GetPageCount(bitmap))
  510. return;
  511. MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
  512. BlockReference *block = FreeImage_SavePageToBlock(header, data);
  513. if(block==NULL) return;
  514. // add a block
  515. if (page > 0) {
  516. BlockListIterator block_source = FreeImage_FindBlock(bitmap, page);
  517. header->m_blocks.insert(block_source, (BlockTypeS *)block);
  518. } else {
  519. header->m_blocks.push_front((BlockTypeS *)block);
  520. }
  521. header->changed = TRUE;
  522. header->page_count = -1;
  523. }
  524. void DLL_CALLCONV
  525. FreeImage_DeletePage(FIMULTIBITMAP *bitmap, int page) {
  526. if (bitmap) {
  527. MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
  528. if ((!header->read_only) && (header->locked_pages.empty())) {
  529. if (FreeImage_GetPageCount(bitmap) > 1) {
  530. BlockListIterator i = FreeImage_FindBlock(bitmap, page);
  531. if (i != header->m_blocks.end()) {
  532. switch((*i)->m_type) {
  533. case BLOCK_CONTINUEUS :
  534. delete *i;
  535. header->m_blocks.erase(i);
  536. break;
  537. case BLOCK_REFERENCE :
  538. header->m_cachefile->deleteFile(((BlockReference *)(*i))->m_reference);
  539. delete *i;
  540. header->m_blocks.erase(i);
  541. break;
  542. }
  543. header->changed = TRUE;
  544. header->page_count = -1;
  545. }
  546. }
  547. }
  548. }
  549. }
  550. FIBITMAP * DLL_CALLCONV
  551. FreeImage_LockPage(FIMULTIBITMAP *bitmap, int page) {
  552. if (bitmap) {
  553. MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
  554. // only lock if the page wasn't locked before...
  555. for (std::map<FIBITMAP *, int>::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) {
  556. if (i->second == page) {
  557. return NULL;
  558. }
  559. }
  560. // open the bitmap
  561. header->io->seek_proc(header->handle, 0, SEEK_SET);
  562. void *data = FreeImage_Open(header->node, header->io, header->handle, TRUE);
  563. // load the bitmap data
  564. if (data != NULL) {
  565. 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;
  566. // close the file
  567. FreeImage_Close(header->node, header->io, header->handle, data);
  568. // if there was still another bitmap open, get rid of it
  569. if (dib) {
  570. header->locked_pages[dib] = page;
  571. return dib;
  572. }
  573. return NULL;
  574. }
  575. }
  576. return NULL;
  577. }
  578. void DLL_CALLCONV
  579. FreeImage_UnlockPage(FIMULTIBITMAP *bitmap, FIBITMAP *page, BOOL changed) {
  580. if ((bitmap) && (page)) {
  581. MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
  582. // find out if the page we try to unlock is actually locked...
  583. if (header->locked_pages.find(page) != header->locked_pages.end()) {
  584. // store the bitmap compressed in the cache for later writing
  585. if (changed && !header->read_only) {
  586. header->changed = TRUE;
  587. // cut loose the block from the rest
  588. BlockListIterator i = FreeImage_FindBlock(bitmap, header->locked_pages[page]);
  589. // compress the data
  590. DWORD compressed_size = 0;
  591. BYTE *compressed_data = NULL;
  592. // open a memory handle
  593. FIMEMORY *hmem = FreeImage_OpenMemory();
  594. // save the page to memory
  595. FreeImage_SaveToMemory(header->cache_fif, page, hmem, 0);
  596. // get the buffer from the memory stream
  597. FreeImage_AcquireMemory(hmem, &compressed_data, &compressed_size);
  598. // write the data to the cache
  599. switch ((*i)->m_type) {
  600. case BLOCK_CONTINUEUS :
  601. {
  602. int iPage = header->m_cachefile->writeFile(compressed_data, compressed_size);
  603. delete (*i);
  604. *i = (BlockTypeS *)new BlockReference(iPage, compressed_size);
  605. break;
  606. }
  607. case BLOCK_REFERENCE :
  608. {
  609. BlockReference *reference = (BlockReference *)(*i);
  610. header->m_cachefile->deleteFile(reference->m_reference);
  611. delete (*i);
  612. int iPage = header->m_cachefile->writeFile(compressed_data, compressed_size);
  613. *i = (BlockTypeS *)new BlockReference(iPage, compressed_size);
  614. break;
  615. }
  616. }
  617. // get rid of the compressed data
  618. FreeImage_CloseMemory(hmem);
  619. }
  620. // reset the locked page so that another page can be locked
  621. FreeImage_Unload(page);
  622. header->locked_pages.erase(page);
  623. }
  624. }
  625. }
  626. BOOL DLL_CALLCONV
  627. FreeImage_MovePage(FIMULTIBITMAP *bitmap, int target, int source) {
  628. if (bitmap) {
  629. MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
  630. if ((!header->read_only) && (header->locked_pages.empty())) {
  631. if ((target != source) && ((target >= 0) && (target < FreeImage_GetPageCount(bitmap))) && ((source >= 0) && (source < FreeImage_GetPageCount(bitmap)))) {
  632. BlockListIterator block_source = FreeImage_FindBlock(bitmap, target);
  633. BlockListIterator block_target = FreeImage_FindBlock(bitmap, source);
  634. header->m_blocks.insert(block_target, *block_source);
  635. header->m_blocks.erase(block_source);
  636. header->changed = TRUE;
  637. return TRUE;
  638. }
  639. }
  640. }
  641. return FALSE;
  642. }
  643. BOOL DLL_CALLCONV
  644. FreeImage_GetLockedPageNumbers(FIMULTIBITMAP *bitmap, int *pages, int *count) {
  645. if ((bitmap) && (count)) {
  646. MULTIBITMAPHEADER *header = FreeImage_GetMultiBitmapHeader(bitmap);
  647. if ((pages == NULL) || (*count == 0)) {
  648. *count = (int)header->locked_pages.size();
  649. } else {
  650. int c = 0;
  651. for (std::map<FIBITMAP *, int>::iterator i = header->locked_pages.begin(); i != header->locked_pages.end(); ++i) {
  652. pages[c] = i->second;
  653. c++;
  654. if (c == *count)
  655. break;
  656. }
  657. }
  658. return TRUE;
  659. }
  660. return FALSE;
  661. }
  662. // =====================================================================
  663. // Memory IO Multipage functions
  664. // =====================================================================
  665. FIMULTIBITMAP * DLL_CALLCONV
  666. FreeImage_LoadMultiBitmapFromMemory(FREE_IMAGE_FORMAT fif, FIMEMORY *stream, int flags) {
  667. BOOL read_only = FALSE; // modifications (if any) will be stored into the memory cache
  668. // retrieve the plugin list to find the node belonging to this plugin
  669. PluginList *list = FreeImage_GetPluginList();
  670. if (list) {
  671. PluginNode *node = list->FindNodeFromFIF(fif);
  672. if (node) {
  673. FreeImageIO *io = new(std::nothrow) FreeImageIO;
  674. if (io) {
  675. SetMemoryIO(io);
  676. FIMULTIBITMAP *bitmap = new(std::nothrow) FIMULTIBITMAP;
  677. if (bitmap) {
  678. MULTIBITMAPHEADER *header = new(std::nothrow) MULTIBITMAPHEADER;
  679. if (header) {
  680. header->m_filename = NULL;
  681. header->node = node;
  682. header->fif = fif;
  683. header->io = io;
  684. header->handle = (fi_handle)stream;
  685. header->changed = FALSE;
  686. header->read_only = read_only;
  687. header->m_cachefile = NULL;
  688. header->cache_fif = fif;
  689. header->load_flags = flags;
  690. // store the MULTIBITMAPHEADER in the surrounding FIMULTIBITMAP structure
  691. bitmap->data = header;
  692. // cache the page count
  693. header->page_count = FreeImage_InternalGetPageCount(bitmap);
  694. // allocate a continueus block to describe the bitmap
  695. header->m_blocks.push_back((BlockTypeS *)new BlockContinueus(0, header->page_count - 1));
  696. if (!read_only) {
  697. // set up the cache
  698. CacheFile *cache_file = new(std::nothrow) CacheFile("", TRUE);
  699. if (cache_file && cache_file->open()) {
  700. header->m_cachefile = cache_file;
  701. }
  702. }
  703. return bitmap;
  704. }
  705. delete bitmap;
  706. }
  707. delete io;
  708. }
  709. }
  710. }
  711. return NULL;
  712. }
  713. BOOL DLL_CALLCONV
  714. FreeImage_SaveMultiBitmapToMemory(FREE_IMAGE_FORMAT fif, FIMULTIBITMAP *bitmap, FIMEMORY *stream, int flags) {
  715. if (stream && stream->data) {
  716. FreeImageIO io;
  717. SetMemoryIO(&io);
  718. return FreeImage_SaveMultiBitmapToHandle(fif, bitmap, &io, (fi_handle)stream, flags);
  719. }
  720. return FALSE;
  721. }