PageRenderTime 59ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/MagickCore/layer.c

https://gitlab.com/ImageMagick/ImageMagick
C | 2055 lines | 1234 code | 69 blank | 752 comment | 364 complexity | 884ed27635fe46c51fb0f1fda7b9ab69 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception
  1. /*
  2. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  3. % %
  4. % %
  5. % L AAA Y Y EEEEE RRRR %
  6. % L A A Y Y E R R %
  7. % L AAAAA Y EEE RRRR %
  8. % L A A Y E R R %
  9. % LLLLL A A Y EEEEE R R %
  10. % %
  11. % MagickCore Image Layering Methods %
  12. % %
  13. % Software Design %
  14. % Cristy %
  15. % Anthony Thyssen %
  16. % January 2006 %
  17. % %
  18. % %
  19. % Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization %
  20. % dedicated to making software imaging solutions freely available. %
  21. % %
  22. % You may not use this file except in compliance with the License. You may %
  23. % obtain a copy of the License at %
  24. % %
  25. % https://imagemagick.org/script/license.php %
  26. % %
  27. % Unless required by applicable law or agreed to in writing, software %
  28. % distributed under the License is distributed on an "AS IS" BASIS, %
  29. % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
  30. % See the License for the specific language governing permissions and %
  31. % limitations under the License. %
  32. % %
  33. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  34. %
  35. */
  36. /*
  37. Include declarations.
  38. */
  39. #include "MagickCore/studio.h"
  40. #include "MagickCore/artifact.h"
  41. #include "MagickCore/cache.h"
  42. #include "MagickCore/channel.h"
  43. #include "MagickCore/color.h"
  44. #include "MagickCore/color-private.h"
  45. #include "MagickCore/composite.h"
  46. #include "MagickCore/effect.h"
  47. #include "MagickCore/exception.h"
  48. #include "MagickCore/exception-private.h"
  49. #include "MagickCore/geometry.h"
  50. #include "MagickCore/image.h"
  51. #include "MagickCore/layer.h"
  52. #include "MagickCore/list.h"
  53. #include "MagickCore/memory_.h"
  54. #include "MagickCore/monitor.h"
  55. #include "MagickCore/monitor-private.h"
  56. #include "MagickCore/option.h"
  57. #include "MagickCore/pixel-accessor.h"
  58. #include "MagickCore/property.h"
  59. #include "MagickCore/profile.h"
  60. #include "MagickCore/resource_.h"
  61. #include "MagickCore/resize.h"
  62. #include "MagickCore/statistic.h"
  63. #include "MagickCore/string_.h"
  64. #include "MagickCore/transform.h"
  65. /*
  66. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  67. % %
  68. % %
  69. % %
  70. + C l e a r B o u n d s %
  71. % %
  72. % %
  73. % %
  74. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  75. %
  76. % ClearBounds() Clear the area specified by the bounds in an image to
  77. % transparency. This typically used to handle Background Disposal for the
  78. % previous frame in an animation sequence.
  79. %
  80. % Warning: no bounds checks are performed, except for the null or missed
  81. % image, for images that don't change. in all other cases bound must fall
  82. % within the image.
  83. %
  84. % The format is:
  85. %
  86. % void ClearBounds(Image *image,RectangleInfo *bounds,
  87. % ExceptionInfo *exception)
  88. %
  89. % A description of each parameter follows:
  90. %
  91. % o image: the image to had the area cleared in
  92. %
  93. % o bounds: the area to be clear within the imag image
  94. %
  95. % o exception: return any errors or warnings in this structure.
  96. %
  97. */
  98. static void ClearBounds(Image *image,RectangleInfo *bounds,
  99. ExceptionInfo *exception)
  100. {
  101. ssize_t
  102. y;
  103. if (bounds->x < 0)
  104. return;
  105. if (image->alpha_trait == UndefinedPixelTrait)
  106. (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
  107. for (y=0; y < (ssize_t) bounds->height; y++)
  108. {
  109. register ssize_t
  110. x;
  111. register Quantum
  112. *magick_restrict q;
  113. q=GetAuthenticPixels(image,bounds->x,bounds->y+y,bounds->width,1,exception);
  114. if (q == (Quantum *) NULL)
  115. break;
  116. for (x=0; x < (ssize_t) bounds->width; x++)
  117. {
  118. SetPixelAlpha(image,TransparentAlpha,q);
  119. q+=GetPixelChannels(image);
  120. }
  121. if (SyncAuthenticPixels(image,exception) == MagickFalse)
  122. break;
  123. }
  124. }
  125. /*
  126. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  127. % %
  128. % %
  129. % %
  130. + I s B o u n d s C l e a r e d %
  131. % %
  132. % %
  133. % %
  134. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  135. %
  136. % IsBoundsCleared() tests whether any pixel in the bounds given, gets cleared
  137. % when going from the first image to the second image. This typically used
  138. % to check if a proposed disposal method will work successfully to generate
  139. % the second frame image from the first disposed form of the previous frame.
  140. %
  141. % Warning: no bounds checks are performed, except for the null or missed
  142. % image, for images that don't change. in all other cases bound must fall
  143. % within the image.
  144. %
  145. % The format is:
  146. %
  147. % MagickBooleanType IsBoundsCleared(const Image *image1,
  148. % const Image *image2,RectangleInfo bounds,ExceptionInfo *exception)
  149. %
  150. % A description of each parameter follows:
  151. %
  152. % o image1, image 2: the images to check for cleared pixels
  153. %
  154. % o bounds: the area to be clear within the imag image
  155. %
  156. % o exception: return any errors or warnings in this structure.
  157. %
  158. */
  159. static MagickBooleanType IsBoundsCleared(const Image *image1,
  160. const Image *image2,RectangleInfo *bounds,ExceptionInfo *exception)
  161. {
  162. register const Quantum
  163. *p,
  164. *q;
  165. register ssize_t
  166. x;
  167. ssize_t
  168. y;
  169. if (bounds->x < 0)
  170. return(MagickFalse);
  171. for (y=0; y < (ssize_t) bounds->height; y++)
  172. {
  173. p=GetVirtualPixels(image1,bounds->x,bounds->y+y,bounds->width,1,exception);
  174. q=GetVirtualPixels(image2,bounds->x,bounds->y+y,bounds->width,1,exception);
  175. if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
  176. break;
  177. for (x=0; x < (ssize_t) bounds->width; x++)
  178. {
  179. if ((GetPixelAlpha(image1,p) >= (Quantum) (QuantumRange/2)) &&
  180. (GetPixelAlpha(image2,q) < (Quantum) (QuantumRange/2)))
  181. break;
  182. p+=GetPixelChannels(image1);
  183. q+=GetPixelChannels(image2);
  184. }
  185. if (x < (ssize_t) bounds->width)
  186. break;
  187. }
  188. return(y < (ssize_t) bounds->height ? MagickTrue : MagickFalse);
  189. }
  190. /*
  191. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  192. % %
  193. % %
  194. % %
  195. % C o a l e s c e I m a g e s %
  196. % %
  197. % %
  198. % %
  199. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  200. %
  201. % CoalesceImages() composites a set of images while respecting any page
  202. % offsets and disposal methods. GIF, MIFF, and MNG animation sequences
  203. % typically start with an image background and each subsequent image
  204. % varies in size and offset. A new image sequence is returned with all
  205. % images the same size as the first images virtual canvas and composited
  206. % with the next image in the sequence.
  207. %
  208. % The format of the CoalesceImages method is:
  209. %
  210. % Image *CoalesceImages(Image *image,ExceptionInfo *exception)
  211. %
  212. % A description of each parameter follows:
  213. %
  214. % o image: the image sequence.
  215. %
  216. % o exception: return any errors or warnings in this structure.
  217. %
  218. */
  219. MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
  220. {
  221. Image
  222. *coalesce_image,
  223. *dispose_image,
  224. *previous;
  225. register Image
  226. *next;
  227. RectangleInfo
  228. bounds;
  229. /*
  230. Coalesce the image sequence.
  231. */
  232. assert(image != (Image *) NULL);
  233. assert(image->signature == MagickCoreSignature);
  234. if (image->debug != MagickFalse)
  235. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  236. assert(exception != (ExceptionInfo *) NULL);
  237. assert(exception->signature == MagickCoreSignature);
  238. next=GetFirstImageInList(image);
  239. bounds=next->page;
  240. if (bounds.width == 0)
  241. {
  242. bounds.width=next->columns;
  243. if (bounds.x > 0)
  244. bounds.width+=bounds.x;
  245. }
  246. if (bounds.height == 0)
  247. {
  248. bounds.height=next->rows;
  249. if (bounds.y > 0)
  250. bounds.height+=bounds.y;
  251. }
  252. bounds.x=0;
  253. bounds.y=0;
  254. coalesce_image=CloneImage(next,bounds.width,bounds.height,MagickTrue,
  255. exception);
  256. if (coalesce_image == (Image *) NULL)
  257. return((Image *) NULL);
  258. coalesce_image->background_color.alpha=(MagickRealType) TransparentAlpha;
  259. (void) SetImageBackgroundColor(coalesce_image,exception);
  260. coalesce_image->alpha_trait=next->alpha_trait;
  261. coalesce_image->page=bounds;
  262. coalesce_image->dispose=NoneDispose;
  263. /*
  264. Coalesce rest of the images.
  265. */
  266. dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
  267. (void) CompositeImage(coalesce_image,next,CopyCompositeOp,MagickTrue,
  268. next->page.x,next->page.y,exception);
  269. next=GetNextImageInList(next);
  270. for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
  271. {
  272. /*
  273. Determine the bounds that was overlaid in the previous image.
  274. */
  275. previous=GetPreviousImageInList(next);
  276. bounds=previous->page;
  277. bounds.width=previous->columns;
  278. bounds.height=previous->rows;
  279. if (bounds.x < 0)
  280. {
  281. bounds.width+=bounds.x;
  282. bounds.x=0;
  283. }
  284. if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) coalesce_image->columns)
  285. bounds.width=coalesce_image->columns-bounds.x;
  286. if (bounds.y < 0)
  287. {
  288. bounds.height+=bounds.y;
  289. bounds.y=0;
  290. }
  291. if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) coalesce_image->rows)
  292. bounds.height=coalesce_image->rows-bounds.y;
  293. /*
  294. Replace the dispose image with the new coalesced image.
  295. */
  296. if (GetPreviousImageInList(next)->dispose != PreviousDispose)
  297. {
  298. dispose_image=DestroyImage(dispose_image);
  299. dispose_image=CloneImage(coalesce_image,0,0,MagickTrue,exception);
  300. if (dispose_image == (Image *) NULL)
  301. {
  302. coalesce_image=DestroyImageList(coalesce_image);
  303. return((Image *) NULL);
  304. }
  305. }
  306. /*
  307. Clear the overlaid area of the coalesced bounds for background disposal
  308. */
  309. if (next->previous->dispose == BackgroundDispose)
  310. ClearBounds(dispose_image,&bounds,exception);
  311. /*
  312. Next image is the dispose image, overlaid with next frame in sequence.
  313. */
  314. coalesce_image->next=CloneImage(dispose_image,0,0,MagickTrue,exception);
  315. coalesce_image->next->previous=coalesce_image;
  316. previous=coalesce_image;
  317. coalesce_image=GetNextImageInList(coalesce_image);
  318. (void) CompositeImage(coalesce_image,next,
  319. next->alpha_trait != UndefinedPixelTrait ? OverCompositeOp : CopyCompositeOp,
  320. MagickTrue,next->page.x,next->page.y,exception);
  321. (void) CloneImageProfiles(coalesce_image,next);
  322. (void) CloneImageProperties(coalesce_image,next);
  323. (void) CloneImageArtifacts(coalesce_image,next);
  324. coalesce_image->page=previous->page;
  325. /*
  326. If a pixel goes opaque to transparent, use background dispose.
  327. */
  328. if (IsBoundsCleared(previous,coalesce_image,&bounds,exception) != MagickFalse)
  329. coalesce_image->dispose=BackgroundDispose;
  330. else
  331. coalesce_image->dispose=NoneDispose;
  332. previous->dispose=coalesce_image->dispose;
  333. }
  334. dispose_image=DestroyImage(dispose_image);
  335. return(GetFirstImageInList(coalesce_image));
  336. }
  337. /*
  338. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  339. % %
  340. % %
  341. % %
  342. % D i s p o s e I m a g e s %
  343. % %
  344. % %
  345. % %
  346. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  347. %
  348. % DisposeImages() returns the coalesced frames of a GIF animation as it would
  349. % appear after the GIF dispose method of that frame has been applied. That is
  350. % it returned the appearance of each frame before the next is overlaid.
  351. %
  352. % The format of the DisposeImages method is:
  353. %
  354. % Image *DisposeImages(Image *image,ExceptionInfo *exception)
  355. %
  356. % A description of each parameter follows:
  357. %
  358. % o images: the image sequence.
  359. %
  360. % o exception: return any errors or warnings in this structure.
  361. %
  362. */
  363. MagickExport Image *DisposeImages(const Image *images,ExceptionInfo *exception)
  364. {
  365. Image
  366. *dispose_image,
  367. *dispose_images;
  368. RectangleInfo
  369. bounds;
  370. register Image
  371. *image,
  372. *next;
  373. /*
  374. Run the image through the animation sequence
  375. */
  376. assert(images != (Image *) NULL);
  377. assert(images->signature == MagickCoreSignature);
  378. if (images->debug != MagickFalse)
  379. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
  380. assert(exception != (ExceptionInfo *) NULL);
  381. assert(exception->signature == MagickCoreSignature);
  382. image=GetFirstImageInList(images);
  383. dispose_image=CloneImage(image,image->page.width,image->page.height,
  384. MagickTrue,exception);
  385. if (dispose_image == (Image *) NULL)
  386. return((Image *) NULL);
  387. dispose_image->page=image->page;
  388. dispose_image->page.x=0;
  389. dispose_image->page.y=0;
  390. dispose_image->dispose=NoneDispose;
  391. dispose_image->background_color.alpha=(MagickRealType) TransparentAlpha;
  392. (void) SetImageBackgroundColor(dispose_image,exception);
  393. dispose_images=NewImageList();
  394. for (next=image; image != (Image *) NULL; image=GetNextImageInList(image))
  395. {
  396. Image
  397. *current_image;
  398. /*
  399. Overlay this frame's image over the previous disposal image.
  400. */
  401. current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
  402. if (current_image == (Image *) NULL)
  403. {
  404. dispose_images=DestroyImageList(dispose_images);
  405. dispose_image=DestroyImage(dispose_image);
  406. return((Image *) NULL);
  407. }
  408. (void) CompositeImage(current_image,next,
  409. next->alpha_trait != UndefinedPixelTrait ? OverCompositeOp : CopyCompositeOp,
  410. MagickTrue,next->page.x,next->page.y,exception);
  411. /*
  412. Handle Background dispose: image is displayed for the delay period.
  413. */
  414. if (next->dispose == BackgroundDispose)
  415. {
  416. bounds=next->page;
  417. bounds.width=next->columns;
  418. bounds.height=next->rows;
  419. if (bounds.x < 0)
  420. {
  421. bounds.width+=bounds.x;
  422. bounds.x=0;
  423. }
  424. if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns)
  425. bounds.width=current_image->columns-bounds.x;
  426. if (bounds.y < 0)
  427. {
  428. bounds.height+=bounds.y;
  429. bounds.y=0;
  430. }
  431. if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows)
  432. bounds.height=current_image->rows-bounds.y;
  433. ClearBounds(current_image,&bounds,exception);
  434. }
  435. /*
  436. Select the appropriate previous/disposed image.
  437. */
  438. if (next->dispose == PreviousDispose)
  439. current_image=DestroyImage(current_image);
  440. else
  441. {
  442. dispose_image=DestroyImage(dispose_image);
  443. dispose_image=current_image;
  444. current_image=(Image *) NULL;
  445. }
  446. /*
  447. Save the dispose image just calculated for return.
  448. */
  449. {
  450. Image
  451. *dispose;
  452. dispose=CloneImage(dispose_image,0,0,MagickTrue,exception);
  453. if (dispose == (Image *) NULL)
  454. {
  455. dispose_images=DestroyImageList(dispose_images);
  456. dispose_image=DestroyImage(dispose_image);
  457. return((Image *) NULL);
  458. }
  459. (void) CloneImageProfiles(dispose,next);
  460. (void) CloneImageProperties(dispose,next);
  461. (void) CloneImageArtifacts(dispose,next);
  462. dispose->page.x=0;
  463. dispose->page.y=0;
  464. dispose->dispose=next->dispose;
  465. AppendImageToList(&dispose_images,dispose);
  466. }
  467. }
  468. dispose_image=DestroyImage(dispose_image);
  469. return(GetFirstImageInList(dispose_images));
  470. }
  471. /*
  472. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  473. % %
  474. % %
  475. % %
  476. + C o m p a r e P i x e l s %
  477. % %
  478. % %
  479. % %
  480. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  481. %
  482. % ComparePixels() Compare the two pixels and return true if the pixels
  483. % differ according to the given LayerType comparision method.
  484. %
  485. % This currently only used internally by CompareImagesBounds(). It is
  486. % doubtful that this sub-routine will be useful outside this module.
  487. %
  488. % The format of the ComparePixels method is:
  489. %
  490. % MagickBooleanType *ComparePixels(const LayerMethod method,
  491. % const PixelInfo *p,const PixelInfo *q)
  492. %
  493. % A description of each parameter follows:
  494. %
  495. % o method: What differences to look for. Must be one of
  496. % CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
  497. %
  498. % o p, q: the pixels to test for appropriate differences.
  499. %
  500. */
  501. static MagickBooleanType ComparePixels(const LayerMethod method,
  502. const PixelInfo *p,const PixelInfo *q)
  503. {
  504. double
  505. o1,
  506. o2;
  507. /*
  508. Any change in pixel values
  509. */
  510. if (method == CompareAnyLayer)
  511. return((MagickBooleanType)(IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse));
  512. o1 = (p->alpha_trait != UndefinedPixelTrait) ? p->alpha : OpaqueAlpha;
  513. o2 = (q->alpha_trait != UndefinedPixelTrait) ? q->alpha : OpaqueAlpha;
  514. /*
  515. Pixel goes from opaque to transprency.
  516. */
  517. if (method == CompareClearLayer)
  518. return((MagickBooleanType) ( (o1 >= ((double) QuantumRange/2.0)) &&
  519. (o2 < ((double) QuantumRange/2.0)) ) );
  520. /*
  521. Overlay would change first pixel by second.
  522. */
  523. if (method == CompareOverlayLayer)
  524. {
  525. if (o2 < ((double) QuantumRange/2.0))
  526. return MagickFalse;
  527. return((MagickBooleanType) (IsFuzzyEquivalencePixelInfo(p,q) == MagickFalse));
  528. }
  529. return(MagickFalse);
  530. }
  531. /*
  532. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  533. % %
  534. % %
  535. % %
  536. + C o m p a r e I m a g e B o u n d s %
  537. % %
  538. % %
  539. % %
  540. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  541. %
  542. % CompareImagesBounds() Given two images return the smallest rectangular area
  543. % by which the two images differ, accourding to the given 'Compare...'
  544. % layer method.
  545. %
  546. % This currently only used internally in this module, but may eventually
  547. % be used by other modules.
  548. %
  549. % The format of the CompareImagesBounds method is:
  550. %
  551. % RectangleInfo *CompareImagesBounds(const LayerMethod method,
  552. % const Image *image1, const Image *image2, ExceptionInfo *exception)
  553. %
  554. % A description of each parameter follows:
  555. %
  556. % o method: What differences to look for. Must be one of CompareAnyLayer,
  557. % CompareClearLayer, CompareOverlayLayer.
  558. %
  559. % o image1, image2: the two images to compare.
  560. %
  561. % o exception: return any errors or warnings in this structure.
  562. %
  563. */
  564. static RectangleInfo CompareImagesBounds(const Image *image1,
  565. const Image *image2,const LayerMethod method,ExceptionInfo *exception)
  566. {
  567. RectangleInfo
  568. bounds;
  569. PixelInfo
  570. pixel1,
  571. pixel2;
  572. register const Quantum
  573. *p,
  574. *q;
  575. register ssize_t
  576. x;
  577. ssize_t
  578. y;
  579. /*
  580. Set bounding box of the differences between images.
  581. */
  582. GetPixelInfo(image1,&pixel1);
  583. GetPixelInfo(image2,&pixel2);
  584. for (x=0; x < (ssize_t) image1->columns; x++)
  585. {
  586. p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
  587. q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
  588. if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
  589. break;
  590. for (y=0; y < (ssize_t) image1->rows; y++)
  591. {
  592. GetPixelInfoPixel(image1,p,&pixel1);
  593. GetPixelInfoPixel(image2,q,&pixel2);
  594. if (ComparePixels(method,&pixel1,&pixel2))
  595. break;
  596. p+=GetPixelChannels(image1);
  597. q+=GetPixelChannels(image2);
  598. }
  599. if (y < (ssize_t) image1->rows)
  600. break;
  601. }
  602. if (x >= (ssize_t) image1->columns)
  603. {
  604. /*
  605. Images are identical, return a null image.
  606. */
  607. bounds.x=-1;
  608. bounds.y=-1;
  609. bounds.width=1;
  610. bounds.height=1;
  611. return(bounds);
  612. }
  613. bounds.x=x;
  614. for (x=(ssize_t) image1->columns-1; x >= 0; x--)
  615. {
  616. p=GetVirtualPixels(image1,x,0,1,image1->rows,exception);
  617. q=GetVirtualPixels(image2,x,0,1,image2->rows,exception);
  618. if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
  619. break;
  620. for (y=0; y < (ssize_t) image1->rows; y++)
  621. {
  622. GetPixelInfoPixel(image1,p,&pixel1);
  623. GetPixelInfoPixel(image2,q,&pixel2);
  624. if (ComparePixels(method,&pixel1,&pixel2))
  625. break;
  626. p+=GetPixelChannels(image1);
  627. q+=GetPixelChannels(image2);
  628. }
  629. if (y < (ssize_t) image1->rows)
  630. break;
  631. }
  632. bounds.width=(size_t) (x-bounds.x+1);
  633. for (y=0; y < (ssize_t) image1->rows; y++)
  634. {
  635. p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
  636. q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
  637. if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
  638. break;
  639. for (x=0; x < (ssize_t) image1->columns; x++)
  640. {
  641. GetPixelInfoPixel(image1,p,&pixel1);
  642. GetPixelInfoPixel(image2,q,&pixel2);
  643. if (ComparePixels(method,&pixel1,&pixel2))
  644. break;
  645. p+=GetPixelChannels(image1);
  646. q+=GetPixelChannels(image2);
  647. }
  648. if (x < (ssize_t) image1->columns)
  649. break;
  650. }
  651. bounds.y=y;
  652. for (y=(ssize_t) image1->rows-1; y >= 0; y--)
  653. {
  654. p=GetVirtualPixels(image1,0,y,image1->columns,1,exception);
  655. q=GetVirtualPixels(image2,0,y,image2->columns,1,exception);
  656. if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
  657. break;
  658. for (x=0; x < (ssize_t) image1->columns; x++)
  659. {
  660. GetPixelInfoPixel(image1,p,&pixel1);
  661. GetPixelInfoPixel(image2,q,&pixel2);
  662. if (ComparePixels(method,&pixel1,&pixel2))
  663. break;
  664. p+=GetPixelChannels(image1);
  665. q+=GetPixelChannels(image2);
  666. }
  667. if (x < (ssize_t) image1->columns)
  668. break;
  669. }
  670. bounds.height=(size_t) (y-bounds.y+1);
  671. return(bounds);
  672. }
  673. /*
  674. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  675. % %
  676. % %
  677. % %
  678. % C o m p a r e I m a g e L a y e r s %
  679. % %
  680. % %
  681. % %
  682. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  683. %
  684. % CompareImagesLayers() compares each image with the next in a sequence and
  685. % returns the minimum bounding region of all the pixel differences (of the
  686. % LayerMethod specified) it discovers.
  687. %
  688. % Images do NOT have to be the same size, though it is best that all the
  689. % images are 'coalesced' (images are all the same size, on a flattened
  690. % canvas, so as to represent exactly how an specific frame should look).
  691. %
  692. % No GIF dispose methods are applied, so GIF animations must be coalesced
  693. % before applying this image operator to find differences to them.
  694. %
  695. % The format of the CompareImagesLayers method is:
  696. %
  697. % Image *CompareImagesLayers(const Image *images,
  698. % const LayerMethod method,ExceptionInfo *exception)
  699. %
  700. % A description of each parameter follows:
  701. %
  702. % o image: the image.
  703. %
  704. % o method: the layers type to compare images with. Must be one of...
  705. % CompareAnyLayer, CompareClearLayer, CompareOverlayLayer.
  706. %
  707. % o exception: return any errors or warnings in this structure.
  708. %
  709. */
  710. MagickExport Image *CompareImagesLayers(const Image *image,
  711. const LayerMethod method, ExceptionInfo *exception)
  712. {
  713. Image
  714. *image_a,
  715. *image_b,
  716. *layers;
  717. RectangleInfo
  718. *bounds;
  719. register const Image
  720. *next;
  721. register ssize_t
  722. i;
  723. assert(image != (const Image *) NULL);
  724. assert(image->signature == MagickCoreSignature);
  725. if (image->debug != MagickFalse)
  726. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  727. assert(exception != (ExceptionInfo *) NULL);
  728. assert(exception->signature == MagickCoreSignature);
  729. assert((method == CompareAnyLayer) ||
  730. (method == CompareClearLayer) ||
  731. (method == CompareOverlayLayer));
  732. /*
  733. Allocate bounds memory.
  734. */
  735. next=GetFirstImageInList(image);
  736. bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
  737. GetImageListLength(next),sizeof(*bounds));
  738. if (bounds == (RectangleInfo *) NULL)
  739. ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
  740. /*
  741. Set up first comparision images.
  742. */
  743. image_a=CloneImage(next,next->page.width,next->page.height,
  744. MagickTrue,exception);
  745. if (image_a == (Image *) NULL)
  746. {
  747. bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
  748. return((Image *) NULL);
  749. }
  750. image_a->background_color.alpha=(MagickRealType) TransparentAlpha;
  751. (void) SetImageBackgroundColor(image_a,exception);
  752. image_a->page=next->page;
  753. image_a->page.x=0;
  754. image_a->page.y=0;
  755. (void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x,
  756. next->page.y,exception);
  757. /*
  758. Compute the bounding box of changes for the later images
  759. */
  760. i=0;
  761. next=GetNextImageInList(next);
  762. for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
  763. {
  764. image_b=CloneImage(image_a,0,0,MagickTrue,exception);
  765. if (image_b == (Image *) NULL)
  766. {
  767. image_a=DestroyImage(image_a);
  768. bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
  769. return((Image *) NULL);
  770. }
  771. (void) CompositeImage(image_a,next,CopyCompositeOp,MagickTrue,next->page.x,
  772. next->page.y,exception);
  773. bounds[i]=CompareImagesBounds(image_b,image_a,method,exception);
  774. image_b=DestroyImage(image_b);
  775. i++;
  776. }
  777. image_a=DestroyImage(image_a);
  778. /*
  779. Clone first image in sequence.
  780. */
  781. next=GetFirstImageInList(image);
  782. layers=CloneImage(next,0,0,MagickTrue,exception);
  783. if (layers == (Image *) NULL)
  784. {
  785. bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
  786. return((Image *) NULL);
  787. }
  788. /*
  789. Deconstruct the image sequence.
  790. */
  791. i=0;
  792. next=GetNextImageInList(next);
  793. for ( ; next != (const Image *) NULL; next=GetNextImageInList(next))
  794. {
  795. if ((bounds[i].x == -1) && (bounds[i].y == -1) &&
  796. (bounds[i].width == 1) && (bounds[i].height == 1))
  797. {
  798. /*
  799. An empty frame is returned from CompareImageBounds(), which means the
  800. current frame is identical to the previous frame.
  801. */
  802. i++;
  803. continue;
  804. }
  805. image_a=CloneImage(next,0,0,MagickTrue,exception);
  806. if (image_a == (Image *) NULL)
  807. break;
  808. image_b=CropImage(image_a,&bounds[i],exception);
  809. image_a=DestroyImage(image_a);
  810. if (image_b == (Image *) NULL)
  811. break;
  812. AppendImageToList(&layers,image_b);
  813. i++;
  814. }
  815. bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
  816. if (next != (Image *) NULL)
  817. {
  818. layers=DestroyImageList(layers);
  819. return((Image *) NULL);
  820. }
  821. return(GetFirstImageInList(layers));
  822. }
  823. /*
  824. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  825. % %
  826. % %
  827. % %
  828. + O p t i m i z e L a y e r F r a m e s %
  829. % %
  830. % %
  831. % %
  832. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  833. %
  834. % OptimizeLayerFrames() takes a coalesced GIF animation, and compares each
  835. % frame against the three different 'disposal' forms of the previous frame.
  836. % From this it then attempts to select the smallest cropped image and
  837. % disposal method needed to reproduce the resulting image.
  838. %
  839. % Note that this not easy, and may require the expansion of the bounds
  840. % of previous frame, simply clear pixels for the next animation frame to
  841. % transparency according to the selected dispose method.
  842. %
  843. % The format of the OptimizeLayerFrames method is:
  844. %
  845. % Image *OptimizeLayerFrames(const Image *image,
  846. % const LayerMethod method, ExceptionInfo *exception)
  847. %
  848. % A description of each parameter follows:
  849. %
  850. % o image: the image.
  851. %
  852. % o method: the layers technique to optimize with. Must be one of...
  853. % OptimizeImageLayer, or OptimizePlusLayer. The Plus form allows
  854. % the addition of extra 'zero delay' frames to clear pixels from
  855. % the previous frame, and the removal of frames that done change,
  856. % merging the delay times together.
  857. %
  858. % o exception: return any errors or warnings in this structure.
  859. %
  860. */
  861. /*
  862. Define a 'fake' dispose method where the frame is duplicated, (for
  863. OptimizePlusLayer) with a extra zero time delay frame which does a
  864. BackgroundDisposal to clear the pixels that need to be cleared.
  865. */
  866. #define DupDispose ((DisposeType)9)
  867. /*
  868. Another 'fake' dispose method used to removed frames that don't change.
  869. */
  870. #define DelDispose ((DisposeType)8)
  871. #define DEBUG_OPT_FRAME 0
  872. static Image *OptimizeLayerFrames(const Image *image,
  873. const LayerMethod method, ExceptionInfo *exception)
  874. {
  875. ExceptionInfo
  876. *sans_exception;
  877. Image
  878. *prev_image,
  879. *dup_image,
  880. *bgnd_image,
  881. *optimized_image;
  882. RectangleInfo
  883. try_bounds,
  884. bgnd_bounds,
  885. dup_bounds,
  886. *bounds;
  887. MagickBooleanType
  888. add_frames,
  889. try_cleared,
  890. cleared;
  891. DisposeType
  892. *disposals;
  893. register const Image
  894. *curr;
  895. register ssize_t
  896. i;
  897. assert(image != (const Image *) NULL);
  898. assert(image->signature == MagickCoreSignature);
  899. if (image->debug != MagickFalse)
  900. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  901. assert(exception != (ExceptionInfo *) NULL);
  902. assert(exception->signature == MagickCoreSignature);
  903. assert(method == OptimizeLayer ||
  904. method == OptimizeImageLayer ||
  905. method == OptimizePlusLayer);
  906. /*
  907. Are we allowed to add/remove frames from animation?
  908. */
  909. add_frames=method == OptimizePlusLayer ? MagickTrue : MagickFalse;
  910. /*
  911. Ensure all the images are the same size.
  912. */
  913. curr=GetFirstImageInList(image);
  914. for (; curr != (Image *) NULL; curr=GetNextImageInList(curr))
  915. {
  916. if ((curr->columns != image->columns) || (curr->rows != image->rows))
  917. ThrowImageException(OptionError,"ImagesAreNotTheSameSize");
  918. if ((curr->page.x != 0) || (curr->page.y != 0) ||
  919. (curr->page.width != image->page.width) ||
  920. (curr->page.height != image->page.height))
  921. ThrowImageException(OptionError,"ImagePagesAreNotCoalesced");
  922. }
  923. /*
  924. Allocate memory (times 2 if we allow the use of frame duplications)
  925. */
  926. curr=GetFirstImageInList(image);
  927. bounds=(RectangleInfo *) AcquireQuantumMemory((size_t)
  928. GetImageListLength(curr),(add_frames != MagickFalse ? 2UL : 1UL)*
  929. sizeof(*bounds));
  930. if (bounds == (RectangleInfo *) NULL)
  931. ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
  932. disposals=(DisposeType *) AcquireQuantumMemory((size_t)
  933. GetImageListLength(image),(add_frames != MagickFalse ? 2UL : 1UL)*
  934. sizeof(*disposals));
  935. if (disposals == (DisposeType *) NULL)
  936. {
  937. bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
  938. ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
  939. }
  940. /*
  941. Initialise Previous Image as fully transparent
  942. */
  943. prev_image=CloneImage(curr,curr->columns,curr->rows,MagickTrue,exception);
  944. if (prev_image == (Image *) NULL)
  945. {
  946. bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
  947. disposals=(DisposeType *) RelinquishMagickMemory(disposals);
  948. return((Image *) NULL);
  949. }
  950. prev_image->page=curr->page; /* ERROR: <-- should not be need, but is! */
  951. prev_image->page.x=0;
  952. prev_image->page.y=0;
  953. prev_image->dispose=NoneDispose;
  954. prev_image->background_color.alpha_trait=BlendPixelTrait;
  955. prev_image->background_color.alpha=(MagickRealType) TransparentAlpha;
  956. (void) SetImageBackgroundColor(prev_image,exception);
  957. /*
  958. Figure out the area of overlay of the first frame
  959. No pixel could be cleared as all pixels are already cleared.
  960. */
  961. #if DEBUG_OPT_FRAME
  962. i=0;
  963. (void) FormatLocaleFile(stderr, "frame %.20g :-\n", (double) i);
  964. #endif
  965. disposals[0]=NoneDispose;
  966. bounds[0]=CompareImagesBounds(prev_image,curr,CompareAnyLayer,exception);
  967. #if DEBUG_OPT_FRAME
  968. (void) FormatLocaleFile(stderr, "overlay: %.20gx%.20g%+.20g%+.20g\n\n",
  969. (double) bounds[i].width,(double) bounds[i].height,
  970. (double) bounds[i].x,(double) bounds[i].y );
  971. #endif
  972. /*
  973. Compute the bounding box of changes for each pair of images.
  974. */
  975. i=1;
  976. bgnd_image=(Image *) NULL;
  977. dup_image=(Image *) NULL;
  978. dup_bounds.width=0;
  979. dup_bounds.height=0;
  980. dup_bounds.x=0;
  981. dup_bounds.y=0;
  982. curr=GetNextImageInList(curr);
  983. for ( ; curr != (const Image *) NULL; curr=GetNextImageInList(curr))
  984. {
  985. #if DEBUG_OPT_FRAME
  986. (void) FormatLocaleFile(stderr, "frame %.20g :-\n", (double) i);
  987. #endif
  988. /*
  989. Assume none disposal is the best
  990. */
  991. bounds[i]=CompareImagesBounds(curr->previous,curr,CompareAnyLayer,exception);
  992. cleared=IsBoundsCleared(curr->previous,curr,&bounds[i],exception);
  993. disposals[i-1]=NoneDispose;
  994. #if DEBUG_OPT_FRAME
  995. (void) FormatLocaleFile(stderr, "overlay: %.20gx%.20g%+.20g%+.20g%s%s\n",
  996. (double) bounds[i].width,(double) bounds[i].height,
  997. (double) bounds[i].x,(double) bounds[i].y,
  998. bounds[i].x < 0?" (unchanged)":"",
  999. cleared?" (pixels cleared)":"");
  1000. #endif
  1001. if ( bounds[i].x < 0 ) {
  1002. /*
  1003. Image frame is exactly the same as the previous frame!
  1004. If not adding frames leave it to be cropped down to a null image.
  1005. Otherwise mark previous image for deleted, transfering its crop bounds
  1006. to the current image.
  1007. */
  1008. if ( add_frames && i>=2 ) {
  1009. disposals[i-1]=DelDispose;
  1010. disposals[i]=NoneDispose;
  1011. bounds[i]=bounds[i-1];
  1012. i++;
  1013. continue;
  1014. }
  1015. }
  1016. else
  1017. {
  1018. /*
  1019. Compare a none disposal against a previous disposal
  1020. */
  1021. try_bounds=CompareImagesBounds(prev_image,curr,CompareAnyLayer,exception);
  1022. try_cleared=IsBoundsCleared(prev_image,curr,&try_bounds,exception);
  1023. #if DEBUG_OPT_FRAME
  1024. (void) FormatLocaleFile(stderr, "test_prev: %.20gx%.20g%+.20g%+.20g%s\n",
  1025. (double) try_bounds.width,(double) try_bounds.height,
  1026. (double) try_bounds.x,(double) try_bounds.y,
  1027. try_cleared?" (pixels were cleared)":"");
  1028. #endif
  1029. if ( (!try_cleared && cleared ) ||
  1030. try_bounds.width * try_bounds.height
  1031. < bounds[i].width * bounds[i].height )
  1032. {
  1033. cleared=try_cleared;
  1034. bounds[i]=try_bounds;
  1035. disposals[i-1]=PreviousDispose;
  1036. #if DEBUG_OPT_FRAME
  1037. (void) FormatLocaleFile(stderr, "previous: accepted\n");
  1038. } else {
  1039. (void) FormatLocaleFile(stderr, "previous: rejected\n");
  1040. #endif
  1041. }
  1042. /*
  1043. If we are allowed lets try a complex frame duplication.
  1044. It is useless if the previous image already clears pixels correctly.
  1045. This method will always clear all the pixels that need to be cleared.
  1046. */
  1047. dup_bounds.width=dup_bounds.height=0; /* no dup, no pixel added */
  1048. if ( add_frames )
  1049. {
  1050. dup_image=CloneImage(curr->previous,0,0,MagickTrue,exception);
  1051. if (dup_image == (Image *) NULL)
  1052. {
  1053. bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
  1054. disposals=(DisposeType *) RelinquishMagickMemory(disposals);
  1055. prev_image=DestroyImage(prev_image);
  1056. return((Image *) NULL);
  1057. }
  1058. dup_bounds=CompareImagesBounds(dup_image,curr,CompareClearLayer,exception);
  1059. ClearBounds(dup_image,&dup_bounds,exception);
  1060. try_bounds=CompareImagesBounds(dup_image,curr,CompareAnyLayer,exception);
  1061. if ( cleared ||
  1062. dup_bounds.width*dup_bounds.height
  1063. +try_bounds.width*try_bounds.height
  1064. < bounds[i].width * bounds[i].height )
  1065. {
  1066. cleared=MagickFalse;
  1067. bounds[i]=try_bounds;
  1068. disposals[i-1]=DupDispose;
  1069. /* to be finalised later, if found to be optimial */
  1070. }
  1071. else
  1072. dup_bounds.width=dup_bounds.height=0;
  1073. }
  1074. /*
  1075. Now compare against a simple background disposal
  1076. */
  1077. bgnd_image=CloneImage(curr->previous,0,0,MagickTrue,exception);
  1078. if (bgnd_image == (Image *) NULL)
  1079. {
  1080. bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
  1081. disposals=(DisposeType *) RelinquishMagickMemory(disposals);
  1082. prev_image=DestroyImage(prev_image);
  1083. if ( dup_image != (Image *) NULL)
  1084. dup_image=DestroyImage(dup_image);
  1085. return((Image *) NULL);
  1086. }
  1087. bgnd_bounds=bounds[i-1]; /* interum bounds of the previous image */
  1088. ClearBounds(bgnd_image,&bgnd_bounds,exception);
  1089. try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception);
  1090. try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
  1091. #if DEBUG_OPT_FRAME
  1092. (void) FormatLocaleFile(stderr, "background: %s\n",
  1093. try_cleared?"(pixels cleared)":"");
  1094. #endif
  1095. if ( try_cleared )
  1096. {
  1097. /*
  1098. Straight background disposal failed to clear pixels needed!
  1099. Lets try expanding the disposal area of the previous frame, to
  1100. include the pixels that are cleared. This guaranteed
  1101. to work, though may not be the most optimized solution.
  1102. */
  1103. try_bounds=CompareImagesBounds(curr->previous,curr,CompareClearLayer,exception);
  1104. #if DEBUG_OPT_FRAME
  1105. (void) FormatLocaleFile(stderr, "expand_clear: %.20gx%.20g%+.20g%+.20g%s\n",
  1106. (double) try_bounds.width,(double) try_bounds.height,
  1107. (double) try_bounds.x,(double) try_bounds.y,
  1108. try_bounds.x<0?" (no expand nessary)":"");
  1109. #endif
  1110. if ( bgnd_bounds.x < 0 )
  1111. bgnd_bounds = try_bounds;
  1112. else
  1113. {
  1114. #if DEBUG_OPT_FRAME
  1115. (void) FormatLocaleFile(stderr, "expand_bgnd: %.20gx%.20g%+.20g%+.20g\n",
  1116. (double) bgnd_bounds.width,(double) bgnd_bounds.height,
  1117. (double) bgnd_bounds.x,(double) bgnd_bounds.y );
  1118. #endif
  1119. if ( try_bounds.x < bgnd_bounds.x )
  1120. {
  1121. bgnd_bounds.width+= bgnd_bounds.x-try_bounds.x;
  1122. if ( bgnd_bounds.width < try_bounds.width )
  1123. bgnd_bounds.width = try_bounds.width;
  1124. bgnd_bounds.x = try_bounds.x;
  1125. }
  1126. else
  1127. {
  1128. try_bounds.width += try_bounds.x - bgnd_bounds.x;
  1129. if ( bgnd_bounds.width < try_bounds.width )
  1130. bgnd_bounds.width = try_bounds.width;
  1131. }
  1132. if ( try_bounds.y < bgnd_bounds.y )
  1133. {
  1134. bgnd_bounds.height += bgnd_bounds.y - try_bounds.y;
  1135. if ( bgnd_bounds.height < try_bounds.height )
  1136. bgnd_bounds.height = try_bounds.height;
  1137. bgnd_bounds.y = try_bounds.y;
  1138. }
  1139. else
  1140. {
  1141. try_bounds.height += try_bounds.y - bgnd_bounds.y;
  1142. if ( bgnd_bounds.height < try_bounds.height )
  1143. bgnd_bounds.height = try_bounds.height;
  1144. }
  1145. #if DEBUG_OPT_FRAME
  1146. (void) FormatLocaleFile(stderr, " to : %.20gx%.20g%+.20g%+.20g\n",
  1147. (double) bgnd_bounds.width,(double) bgnd_bounds.height,
  1148. (double) bgnd_bounds.x,(double) bgnd_bounds.y );
  1149. #endif
  1150. }
  1151. ClearBounds(bgnd_image,&bgnd_bounds,exception);
  1152. #if DEBUG_OPT_FRAME
  1153. /* Something strange is happening with a specific animation
  1154. * CompareAnyLayers (normal method) and CompareClearLayers returns the whole
  1155. * image, which is not posibly correct! As verified by previous tests.
  1156. * Something changed beyond the bgnd_bounds clearing. But without being able
  1157. * to see, or writet he image at this point it is hard to tell what is wrong!
  1158. * Only CompareOverlay seemed to return something sensible.
  1159. */
  1160. try_bounds=CompareImagesBounds(bgnd_image,curr,CompareClearLayer,exception);
  1161. (void) FormatLocaleFile(stderr, "expand_ctst: %.20gx%.20g%+.20g%+.20g\n",
  1162. (double) try_bounds.width,(double) try_bounds.height,
  1163. (double) try_bounds.x,(double) try_bounds.y );
  1164. try_bounds=CompareImagesBounds(bgnd_image,curr,CompareAnyLayer,exception);
  1165. try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
  1166. (void) FormatLocaleFile(stderr, "expand_any : %.20gx%.20g%+.20g%+.20g%s\n",
  1167. (double) try_bounds.width,(double) try_bounds.height,
  1168. (double) try_bounds.x,(double) try_bounds.y,
  1169. try_cleared?" (pixels cleared)":"");
  1170. #endif
  1171. try_bounds=CompareImagesBounds(bgnd_image,curr,CompareOverlayLayer,exception);
  1172. #if DEBUG_OPT_FRAME
  1173. try_cleared=IsBoundsCleared(bgnd_image,curr,&try_bounds,exception);
  1174. (void) FormatLocaleFile(stderr, "expand_test: %.20gx%.20g%+.20g%+.20g%s\n",
  1175. (double) try_bounds.width,(double) try_bounds.height,
  1176. (double) try_bounds.x,(double) try_bounds.y,
  1177. try_cleared?" (pixels cleared)":"");
  1178. #endif
  1179. }
  1180. /*
  1181. Test if this background dispose is smaller than any of the
  1182. other methods we tryed before this (including duplicated frame)
  1183. */
  1184. if ( cleared ||
  1185. bgnd_bounds.width*bgnd_bounds.height
  1186. +try_bounds.width*try_bounds.height
  1187. < bounds[i-1].width*bounds[i-1].height
  1188. +dup_bounds.width*dup_bounds.height
  1189. +bounds[i].width*bounds[i].height )
  1190. {
  1191. cleared=MagickFalse;
  1192. bounds[i-1]=bgnd_bounds;
  1193. bounds[i]=try_bounds;
  1194. if ( disposals[i-1] == DupDispose )
  1195. dup_image=DestroyImage(dup_image);
  1196. disposals[i-1]=BackgroundDispose;
  1197. #if DEBUG_OPT_FRAME
  1198. (void) FormatLocaleFile(stderr, "expand_bgnd: accepted\n");
  1199. } else {
  1200. (void) FormatLocaleFile(stderr, "expand_bgnd: reject\n");
  1201. #endif
  1202. }
  1203. }
  1204. /*
  1205. Finalise choice of dispose, set new prev_image,
  1206. and junk any extra images as appropriate,
  1207. */
  1208. if ( disposals[i-1] == DupDispose )
  1209. {
  1210. if (bgnd_image != (Image *) NULL)
  1211. bgnd_image=DestroyImage(bgnd_image);
  1212. prev_image=DestroyImage(prev_image);
  1213. prev_image=dup_image, dup_image=(Image *) NULL;
  1214. bounds[i+1]=bounds[i];
  1215. bounds[i]=dup_bounds;
  1216. disposals[i-1]=DupDispose;
  1217. disposals[i]=BackgroundDispose;
  1218. i++;
  1219. }
  1220. else
  1221. {
  1222. if ( dup_image != (Image *) NULL)
  1223. dup_image=DestroyImage(dup_image);
  1224. if ( disposals[i-1] != PreviousDispose )
  1225. prev_image=DestroyImage(prev_image);
  1226. if ( disposals[i-1] == BackgroundDispose )
  1227. prev_image=bgnd_image, bgnd_image=(Image *) NULL;
  1228. if (bgnd_image != (Image *) NULL)
  1229. bgnd_image=DestroyImage(bgnd_image);
  1230. if ( disposals[i-1] == NoneDispose )
  1231. {
  1232. prev_image=ReferenceImage(curr->previous);
  1233. if (prev_image == (Image *) NULL)
  1234. {
  1235. bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
  1236. disposals=(DisposeType *) RelinquishMagickMemory(disposals);
  1237. return((Image *) NULL);
  1238. }
  1239. }
  1240. }
  1241. assert(prev_image != (Image *) NULL);
  1242. disposals[i]=disposals[i-1];
  1243. #if DEBUG_OPT_FRAME
  1244. (void) FormatLocaleFile(stderr, "final %.20g : %s %.20gx%.20g%+.20g%+.20g\n",
  1245. (double) i-1,
  1246. CommandOptionToMnemonic(MagickDisposeOptions, disposals[i-1]),
  1247. (double) bounds[i-1].width, (double) bounds[i-1].height,
  1248. (double) bounds[i-1].x, (double) bounds[i-1].y );
  1249. #endif
  1250. #if DEBUG_OPT_FRAME
  1251. (void) FormatLocaleFile(stderr, "interum %.20g : %s %.20gx%.20g%+.20g%+.20g\n",
  1252. (double) i,
  1253. CommandOptionToMnemonic(MagickDisposeOptions, disposals[i]),
  1254. (double) bounds[i].width, (double) bounds[i].height,
  1255. (double) bounds[i].x, (double) bounds[i].y );
  1256. (void) FormatLocaleFile(stderr, "\n");
  1257. #endif
  1258. i++;
  1259. }
  1260. prev_image=DestroyImage(prev_image);
  1261. /*
  1262. Optimize all images in sequence.
  1263. */
  1264. sans_exception=AcquireExceptionInfo();
  1265. i=0;
  1266. curr=GetFirstImageInList(image);
  1267. optimized_image=NewImageList();
  1268. while ( curr != (const Image *) NULL )
  1269. {
  1270. prev_image=CloneImage(curr,0,0,MagickTrue,exception);
  1271. if (prev_image == (Image *) NULL)
  1272. break;
  1273. if (prev_image->alpha_trait == UndefinedPixelTrait)
  1274. (void) SetImageAlphaChannel(prev_image,OpaqueAlphaChannel,exception);
  1275. if ( disposals[i] == DelDispose ) {
  1276. size_t time = 0;
  1277. while ( disposals[i] == DelDispose ) {
  1278. time += curr->delay*1000/curr->ticks_per_second;
  1279. curr=GetNextImageInList(curr);
  1280. i++;
  1281. }
  1282. time += curr->delay*1000/curr->ticks_per_second;
  1283. prev_image->ticks_per_second = 100L;
  1284. prev_image->delay = time*prev_image->ticks_per_second/1000;
  1285. }
  1286. bgnd_image=CropImage(prev_image,&bounds[i],sans_exception);
  1287. prev_image=DestroyImage(prev_image);
  1288. if (bgnd_image == (Image *) NULL)
  1289. break;
  1290. bgnd_image->dispose=disposals[i];
  1291. if ( disposals[i] == DupDispose ) {
  1292. bgnd_image->delay=0;
  1293. bgnd_image->dispose=NoneDispose;
  1294. }
  1295. else
  1296. curr=GetNextImageInList(curr);
  1297. AppendImageToList(&optimized_image,bgnd_image);
  1298. i++;
  1299. }
  1300. sans_exception=DestroyExceptionInfo(sans_exception);
  1301. bounds=(RectangleInfo *) RelinquishMagickMemory(bounds);
  1302. disposals=(DisposeType *) RelinquishMagickMemory(disposals);
  1303. if (curr != (Image *) NULL)
  1304. {
  1305. optimized_image=DestroyImageList(optimized_image);
  1306. return((Image *) NULL);
  1307. }
  1308. return(GetFirstImageInList(optimized_image));
  1309. }
  1310. /*
  1311. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1312. % %
  1313. % %
  1314. % %
  1315. % O p t i m i z e I m a g e L a y e r s %
  1316. % %
  1317. % %
  1318. % %
  1319. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1320. %
  1321. % OptimizeImageLayers() compares each image the GIF disposed forms of the
  1322. % previous image in the sequence. From this it attempts to select the
  1323. % smallest cropped image to replace each frame, while preserving the results
  1324. % of the GIF animation.
  1325. %
  1326. % The format of the OptimizeImageLayers method is:
  1327. %
  1328. % Image *OptimizeImageLayers(const Image *image,
  1329. % ExceptionInfo *exception)
  1330. %
  1331. % A description of each parameter follows:
  1332. %
  1333. % o image: the image.
  1334. %
  1335. % o exception: return any errors or warnings in this structure.
  1336. %
  1337. */
  1338. MagickExport Image *OptimizeImageLayers(const Image *image,
  1339. ExceptionInfo *exception)
  1340. {
  1341. return(OptimizeLayerFrames(image,OptimizeImageLayer,exception));
  1342. }
  1343. /*
  1344. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1345. % %
  1346. % %
  1347. % %
  1348. % O p t i m i z e P l u s I m a g e L a y e r s %
  1349. % %
  1350. % %
  1351. % %
  1352. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1353. %
  1354. % OptimizeImagePlusLayers() is exactly as OptimizeImageLayers(), but may
  1355. % also add or even remove extra frames in the animation, if it improves
  1356. % the total number of pixels in the resulting GIF animation.
  1357. %
  1358. % The format of the OptimizePlusImageLayers method is:
  1359. %
  1360. % Image *OptimizePlusImageLayers(const Image *image,
  1361. % ExceptionInfo *exception)
  1362. %
  1363. % A description of each parameter follows:
  1364. %
  1365. % o image: the image.
  1366. %
  1367. % o exception: return any errors or warnings in this structure.
  1368. %
  1369. */
  1370. MagickExport Image *OptimizePlusImageLayers(const Image *image,
  1371. ExceptionInfo *exception)
  1372. {
  1373. return OptimizeLayerFrames(image, OptimizePlusLayer, exception);
  1374. }
  1375. /*
  1376. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1377. % %
  1378. % %
  1379. % %
  1380. % O p t i m i z e I m a g e T r a n s p a r e n c y %
  1381. % %
  1382. % %
  1383. % %
  1384. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1385. %
  1386. % OptimizeImageTransparency() takes a frame optimized GIF animation, and
  1387. % compares the overlayed pixels against the disposal image resulting from all
  1388. % the previous frames in the animation. Any pixel that does not change the
  1389. % disposal image (and thus does not effect the outcome of an overlay) is made
  1390. % transparent.
  1391. %
  1392. % WARNING: This modifies the current images directly, rather than generate
  1393. % a new image sequence.
  1394. %
  1395. % The format of the OptimizeImageTransperency method is:
  1396. %
  1397. % void OptimizeImageTransperency(Image *image,ExceptionInfo *exception)
  1398. %
  1399. % A description of each parameter follows:
  1400. %
  1401. % o image: the image sequence
  1402. %
  1403. % o exception: return any errors or warnings in this structure.
  1404. %
  1405. */
  1406. MagickExport void OptimizeImageTransparency(const Image *image,
  1407. ExceptionInfo *exception)
  1408. {
  1409. Image
  1410. *dispose_image;
  1411. register Image
  1412. *next;
  1413. /*
  1414. Run the image through the animation sequence
  1415. */
  1416. assert(image != (Image *) NULL);
  1417. assert(image->signature == MagickCoreSignature);
  1418. if (image->debug != MagickFalse)
  1419. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  1420. assert(exception != (ExceptionInfo *) NULL);
  1421. assert(exception->signature == MagickCoreSignature);
  1422. next=GetFirstImageInList(image);
  1423. dispose_image=CloneImage(next,next->page.width,next->page.height,
  1424. MagickTrue,exception);
  1425. if (dispose_image == (Image *) NULL)
  1426. return;
  1427. dispose_image->page=next->page;
  1428. dispose_image->page.x=0;
  1429. dispose_image->page.y=0;
  1430. dispose_image->dispose=NoneDispose;
  1431. dispose_image->background_color.alpha_trait=BlendPixelTrait;
  1432. dispose_image->background_color.alpha=(MagickRealType) TransparentAlpha;
  1433. (void) SetImageBackgroundColor(dispose_image,exception);
  1434. while ( next != (Image *) NULL )
  1435. {
  1436. Image
  1437. *current_image;
  1438. /*
  1439. Overlay this frame's image over the previous disposal image
  1440. */
  1441. current_image=CloneImage(dispose_image,0,0,MagickTrue,exception);
  1442. if (current_image == (Image *) NULL)
  1443. {
  1444. dispose_image=DestroyImage(dispose_image);
  1445. return;
  1446. }
  1447. (void) CompositeImage(current_image,next,next->alpha_trait != UndefinedPixelTrait ?
  1448. OverCompositeOp : CopyCompositeOp,MagickTrue,next->page.x,next->page.y,
  1449. exception);
  1450. /*
  1451. At this point the image would be displayed, for the delay period
  1452. **
  1453. Work out the disposal of the previous image
  1454. */
  1455. if (next->dispose == BackgroundDispose)
  1456. {
  1457. RectangleInfo
  1458. bounds=next->page;
  1459. bounds.width=next->columns;
  1460. bounds.height=next->rows;
  1461. if (bounds.x < 0)
  1462. {
  1463. bounds.width+=bounds.x;
  1464. bounds.x=0;
  1465. }
  1466. if ((ssize_t) (bounds.x+bounds.width) > (ssize_t) current_image->columns)
  1467. bounds.width=current_image->columns-bounds.x;
  1468. if (bounds.y < 0)
  1469. {
  1470. bounds.height+=bounds.y;
  1471. bounds.y=0;
  1472. }
  1473. if ((ssize_t) (bounds.y+bounds.height) > (ssize_t) current_image->rows)
  1474. bounds.height=current_image->rows-bounds.y;
  1475. ClearBounds(current_image, &bounds,exception);
  1476. }
  1477. if (next->dispose != PreviousDispose)
  1478. {
  1479. dispose_image=DestroyImage(dispose_image);
  1480. dispose_image=current_image;
  1481. }
  1482. else
  1483. current_image=DestroyImage(current_image);
  1484. /*
  1485. Optimize Transparency of the next frame (if present)
  1486. */
  1487. next=GetNextImageInList(next);
  1488. if (next != (Image *) NULL) {
  1489. (void) CompositeImage(next,dispose_image,ChangeMaskCompositeOp,
  1490. MagickTrue,-(next->page.x),-(next->page.y),exception);
  1491. }
  1492. }
  1493. dispose_image=DestroyImage(dispose_image);
  1494. return;
  1495. }
  1496. /*
  1497. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1498. % %
  1499. % %
  1500. % %
  1501. % R e m o v e D u p l i c a t e L a y e r s %
  1502. % %
  1503. % %
  1504. % %
  1505. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1506. %
  1507. % RemoveDuplicateLayers() removes any image that is exactly the same as the
  1508. % next image in the given image list. Image size and virtual canvas offset
  1509. % must also match, though not the virtual canvas size itself.
  1510. %
  1511. % No check is made with regards to image disposal setting, though it is the
  1512. % dispose setting of later image that is kept. Also any time delays are also
  1513. % added together. As such coalesced image animations should still produce the
  1514. % same result, though with duplicte frames merged into a single frame.
  1515. %
  1516. % The format of the RemoveDuplicateLayers method is:
  1517. %
  1518. % void RemoveDuplicateLayers(Image **image, ExceptionInfo *exception)
  1519. %
  1520. % A description of each parameter follows:
  1521. %
  1522. % o images: the image list
  1523. %
  1524. % o exception: return any errors or warnings in this structure.
  1525. %
  1526. */
  1527. MagickExport void RemoveDuplicateLayers(Image **images,
  1528. ExceptionInfo *exception)
  1529. {
  1530. register Image
  1531. *curr,
  1532. *next;
  1533. RectangleInfo
  1534. bounds;
  1535. assert((*images) != (const Image *) NULL);
  1536. assert((*images)->signature == MagickCoreSignature);
  1537. if ((*images)->debug != MagickFalse)
  1538. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
  1539. assert(exception != (ExceptionInfo *) NULL);
  1540. assert(exception->signature == MagickCoreSignature);
  1541. curr=GetFirstImageInList(*images);
  1542. for (; (next=GetNextImageInList(curr)) != (Image *) NULL; curr=next)
  1543. {
  1544. if ( curr->columns != next->columns || curr->rows != next->rows
  1545. || curr->page.x != next->page.x || curr->page.y != next->page.y )
  1546. continue;
  1547. bounds=CompareImagesBounds(curr,next,CompareAnyLayer,exception);
  1548. if ( bounds.x < 0 ) {
  1549. /*
  1550. the two images are the same, merge time delays and delete one.
  1551. */
  1552. size_t time;
  1553. time = curr->delay*1000/curr->ticks_per_second;
  1554. time += next->delay*1000/next->ticks_per_second;
  1555. next->ticks_per_second = 100L;
  1556. next->delay = time*curr->ticks_per_second/1000;
  1557. next->iterations = curr->iterations;
  1558. *images = curr;
  1559. (void) DeleteImageFromList(images);
  1560. }
  1561. }
  1562. *images = GetFirstImageInList(*images);
  1563. }
  1564. /*
  1565. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1566. % %
  1567. % %
  1568. % %
  1569. % R e m o v e Z e r o D e l a y L a y e r s %
  1570. % %
  1571. % %
  1572. % %
  1573. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1574. %
  1575. % RemoveZeroDelayLayers() removes any image that as a zero delay time. Such
  1576. % images generally represent intermediate or partial updates in GIF
  1577. % animations used for file optimization. They are not ment to be displayed
  1578. % to users of the animation. Viewable images in an animation should have a
  1579. % time delay of 3 or more centi-seconds (hundredths of a second).
  1580. %
  1581. % However if all the frames have a zero time delay, then either the animation
  1582. % is as yet incomplete, or it is not a GIF animation. This a non-sensible
  1583. % situation, so no image will be removed and a 'Zero Time Animation' warning
  1584. % (exception) given.
  1585. %
  1586. % No warning will be given if no image was removed because all images had an
  1587. % appropriate non-zero time delay set.
  1588. %
  1589. % Due to the special requirements of GIF disposal handling, GIF animations
  1590. % should be coalesced first, before calling this function, though that is not
  1591. % a requirement.
  1592. %
  1593. % The format of the RemoveZeroDelayLayers method is:
  1594. %
  1595. % void RemoveZeroDelayLayers(Image **image, ExceptionInfo *exception)
  1596. %
  1597. % A description of each parameter follows:
  1598. %
  1599. % o images: the image list
  1600. %
  1601. % o exception: return any errors or warnings in this structure.
  1602. %
  1603. */
  1604. MagickExport void RemoveZeroDelayLayers(Image **images,
  1605. ExceptionInfo *exception)
  1606. {
  1607. Image
  1608. *i;
  1609. assert((*images) != (const Image *) NULL);
  1610. assert((*images)->signature == MagickCoreSignature);
  1611. if ((*images)->debug != MagickFalse)
  1612. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*images)->filename);
  1613. assert(exception != (ExceptionInfo *) NULL);
  1614. assert(exception->signature == MagickCoreSignature);
  1615. i=GetFirstImageInList(*images);
  1616. for ( ; i != (Image *) NULL; i=GetNextImageInList(i))
  1617. if ( i->delay != 0L ) break;
  1618. if ( i == (Image *) NULL ) {
  1619. (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
  1620. "ZeroTimeAnimation","`%s'",GetFirstImageInList(*images)->filename);
  1621. return;
  1622. }
  1623. i=GetFirstImageInList(*images);
  1624. while ( i != (Image *) NULL )
  1625. {
  1626. if ( i->delay == 0L ) {
  1627. (void) DeleteImageFromList(&i);
  1628. *images=i;
  1629. }
  1630. else
  1631. i=GetNextImageInList(i);
  1632. }
  1633. *images=GetFirstImageInList(*images);
  1634. }
  1635. /*
  1636. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1637. % %
  1638. % %
  1639. % %
  1640. % C o m p o s i t e L a y e r s %
  1641. % %
  1642. % %
  1643. % %
  1644. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1645. %
  1646. % CompositeLayers() compose the source image sequence over the destination
  1647. % image sequence, starting with the current image in both lists.
  1648. %
  1649. % Each layer from the two image lists are composted together until the end of
  1650. % one of the image lists is reached. The offset of each composition is also
  1651. % adjusted to match the virtual canvas offsets of each layer. As such the
  1652. % given offset is relative to the virtual canvas, and not the actual image.
  1653. %
  1654. % Composition uses given x and y offsets, as the 'origin' location of the
  1655. % source images virtual canvas (not the real image) allowing you to compose a
  1656. % list of 'layer images' into the destiantioni images. This makes it well
  1657. % sutiable for directly composing 'Clears Frame Animations' or 'Coaleased
  1658. % Animations' onto a static or other 'Coaleased Animation' destination image
  1659. % list. GIF disposal handling is not looked at.
  1660. %
  1661. % Special case:- If one of the image sequences is the last image (just a
  1662. % single image remaining), that image is repeatally composed with all the
  1663. % images in the other image list. Either the source or destination lists may
  1664. % be the single image, for this situation.
  1665. %
  1666. % In the case of a single destination image (or last image given), that image
  1667. % will ve cloned to match the number of images remaining in the source image
  1668. % list.
  1669. %
  1670. % This is equivelent to the "-layer Composite" Shell API operator.
  1671. %
  1672. %
  1673. % The format of the CompositeLayers method is:
  1674. %
  1675. % void CompositeLayers(Image *destination, const CompositeOperator
  1676. % compose, Image *source, const ssize_t x_offset, const ssize_t y_offset,
  1677. % ExceptionInfo *exception);
  1678. %
  1679. % A description of each parameter follows:
  1680. %
  1681. % o destination: the destination images and results
  1682. %
  1683. % o source: source image(s) for the layer composition
  1684. %
  1685. % o compose, x_offset, y_offset: arguments passed on to CompositeImages()
  1686. %
  1687. % o exception: return any errors or warnings in this structure.
  1688. %
  1689. */
  1690. static inline void CompositeCanvas(Image *destination,
  1691. const CompositeOperator compose,Image *source,ssize_t x_offset,
  1692. ssize_t y_offset,ExceptionInfo *exception)
  1693. {
  1694. const char
  1695. *value;
  1696. x_offset+=source->page.x-destination->page.x;
  1697. y_offset+=source->page.y-destination->page.y;
  1698. value=GetImageArtifact(source,"compose:outside-overlay");
  1699. (void) CompositeImage(destination,source,compose,
  1700. (value != (const char *) NULL) && (IsStringTrue(value) != MagickFalse) ?
  1701. MagickFalse : MagickTrue,x_offset,y_offset,exception);
  1702. }
  1703. MagickExport void CompositeLayers(Image *destination,
  1704. const CompositeOperator compose, Image *source,const ssize_t x_offset,
  1705. const ssize_t y_offset,ExceptionInfo *exception)
  1706. {
  1707. assert(destination != (Image *) NULL);
  1708. assert(destination->signature == MagickCoreSignature);
  1709. assert(source != (Image *) NULL);
  1710. assert(source->signature == MagickCoreSignature);
  1711. assert(exception != (ExceptionInfo *) NULL);
  1712. assert(exception->signature == MagickCoreSignature);
  1713. if (source->debug != MagickFalse || destination->debug != MagickFalse)
  1714. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s - %s",
  1715. source->filename, destination->filename);
  1716. /*
  1717. Overlay single source image over destation image/list
  1718. */
  1719. if ( source->next == (Image *) NULL )
  1720. while ( destination != (Image *) NULL )
  1721. {
  1722. CompositeCanvas(destination, compose, source, x_offset, y_offset,
  1723. exception);
  1724. destination=GetNextImageInList(destination);
  1725. }
  1726. /*
  1727. Overlay source image list over single destination.
  1728. Multiple clones of destination image are created to match source list.
  1729. Original Destination image becomes first image of generated list.
  1730. As such the image list pointer does not require any change in caller.
  1731. Some animation attributes however also needs coping in this case.
  1732. */
  1733. else if ( destination->next == (Image *) NULL )
  1734. {
  1735. Image *dest = CloneImage(destination,0,0,MagickTrue,exception);
  1736. CompositeCanvas(destination, compose, source, x_offset, y_offset,
  1737. exception);
  1738. /* copy source image attributes ? */
  1739. if ( source->next != (Image *) NULL )
  1740. {
  1741. destination->delay = source->delay;
  1742. destination->iterations = source->iterations;
  1743. }
  1744. source=GetNextImageInList(source);
  1745. while ( source != (Image *) NULL )
  1746. {
  1747. AppendImageToList(&destination,
  1748. CloneImage(dest,0,0,MagickTrue,exception));
  1749. destination=GetLastImageInList(destination);
  1750. CompositeCanvas(destination, compose, source, x_offset, y_offset,
  1751. exception);
  1752. destination->delay = source->delay;
  1753. destination->iterations = source->iterations;
  1754. source=GetNextImageInList(source);
  1755. }
  1756. dest=DestroyImage(dest);
  1757. }
  1758. /*
  1759. Overlay a source image list over a destination image list
  1760. until either list runs out of images. (Does not repeat)
  1761. */
  1762. else
  1763. while ( source != (Image *) NULL && destination != (Image *) NULL )
  1764. {
  1765. CompositeCanvas(destination, compose, source, x_offset, y_offset,
  1766. exception);
  1767. source=GetNextImageInList(source);
  1768. destination=GetNextImageInList(destination);
  1769. }
  1770. }
  1771. /*
  1772. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1773. % %
  1774. % %
  1775. % %
  1776. % M e r g e I m a g e L a y e r s %
  1777. % %
  1778. % %
  1779. % %
  1780. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1781. %
  1782. % MergeImageLayers() composes all the image layers from the current given
  1783. % image onward to produce a single image of the merged layers.
  1784. %
  1785. % The inital canvas's size depends on the given LayerMethod, and is
  1786. % initialized using the first images background color. The images
  1787. % are then compositied onto that image in sequence using the given
  1788. % composition that has been assigned to each individual image.
  1789. %
  1790. % The format of the MergeImageLayers is:
  1791. %
  1792. % Image *MergeImageLayers(Image *image,const LayerMethod method,
  1793. % ExceptionInfo *exception)
  1794. %
  1795. % A description of each parameter follows:
  1796. %
  1797. % o image: the image list to be composited together
  1798. %
  1799. % o method: the method of selecting the size of the initial canvas.
  1800. %
  1801. % MergeLayer: Merge all layers onto a canvas just large enough
  1802. % to hold all the actual images. The virtual canvas of the
  1803. % first image is preserved but otherwise ignored.
  1804. %
  1805. % FlattenLayer: Use the virtual canvas size of first image.
  1806. % Images which fall outside this canvas is clipped.
  1807. % This can be used to 'fill out' a given virtual canvas.
  1808. %
  1809. % MosaicLayer: Start with the virtual canvas of the first image,
  1810. % enlarging left and right edges to contain all images.
  1811. % Images with negative offsets will be clipped.
  1812. %
  1813. % TrimBoundsLayer: Determine the overall bounds of all the image
  1814. % layers just as in "MergeLayer", then adjust the the canvas
  1815. % and offsets to be relative to those bounds, without overlaying
  1816. % the images.
  1817. %
  1818. % WARNING: a new image is not returned, the original image
  1819. % sequence page data is modified instead.
  1820. %
  1821. % o exception: return any errors or warnings in this structure.
  1822. %
  1823. */
  1824. MagickExport Image *MergeImageLayers(Image *image,const LayerMethod method,
  1825. ExceptionInfo *exception)
  1826. {
  1827. #define MergeLayersTag "Merge/Layers"
  1828. Image
  1829. *canvas;
  1830. MagickBooleanType
  1831. proceed;
  1832. RectangleInfo
  1833. page;
  1834. register const Image
  1835. *next;
  1836. size_t
  1837. number_images,
  1838. height,
  1839. width;
  1840. ssize_t
  1841. scene;
  1842. assert(image != (Image *) NULL);
  1843. assert(image->signature == MagickCoreSignature);
  1844. if (image->debug != MagickFalse)
  1845. (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
  1846. assert(exception != (ExceptionInfo *) NULL);
  1847. assert(exception->signature == MagickCoreSignature);
  1848. /*
  1849. Determine canvas image size, and its virtual canvas size and offset
  1850. */
  1851. page=image->page;
  1852. width=image->columns;
  1853. height=image->rows;
  1854. switch (method)
  1855. {
  1856. case TrimBoundsLayer:
  1857. case MergeLayer:
  1858. default:
  1859. {
  1860. next=GetNextImageInList(image);
  1861. for ( ; next != (Image *) NULL; next=GetNextImageInList(next))
  1862. {
  1863. if (page.x > next->page.x)
  1864. {
  1865. width+=page.x-next->page.x;
  1866. page.x=next->page.x;
  1867. }
  1868. if (page.y > next->page.y)
  1869. {
  1870. height+=page.y-next->page.y;
  1871. page.y=next->page.y;
  1872. }
  1873. if ((ssize_t) width < (next->page.x+(ssize_t) next->columns-page.x))
  1874. width=(size_t) next->page.x+(ssize_t) next->columns-page.x;
  1875. if ((ssize_t) height < (next->page.y+(ssize_t) next->rows-page.y))
  1876. height=(size_t) next->page.y+(ssize_t) next->rows-page.y;
  1877. }
  1878. break;
  1879. }
  1880. case FlattenLayer:
  1881. {
  1882. if (page.width > 0)
  1883. width=page.width;
  1884. if (page.height > 0)
  1885. height=page.height;
  1886. page.x=0;
  1887. page.y=0;
  1888. break;
  1889. }
  1890. case MosaicLayer:
  1891. {
  1892. if (page.width > 0)
  1893. width=page.width;
  1894. if (page.height > 0)
  1895. height=page.height;
  1896. for (next=image; next != (Image *) NULL; next=GetNextImageInList(next))
  1897. {
  1898. if (method == MosaicLayer)
  1899. {
  1900. page.x=next->page.x;
  1901. page.y=next->page.y;
  1902. if ((ssize_t) width < (next->page.x+(ssize_t) next->columns))
  1903. width=(size_t) next->page.x+next->columns;
  1904. if ((ssize_t) height < (next->page.y+(ssize_t) next->rows))
  1905. height=(size_t) next->page.y+next->rows;
  1906. }
  1907. }
  1908. page.width=width;
  1909. page.height=height;
  1910. page.x=0;
  1911. page.y=0;
  1912. }
  1913. break;
  1914. }
  1915. /*
  1916. Set virtual canvas size if not defined.
  1917. */
  1918. if (page.width == 0)
  1919. page.width=page.x < 0 ? width : width+page.x;
  1920. if (page.height == 0)
  1921. page.height=page.y < 0 ? height : height+page.y;
  1922. /*
  1923. Handle "TrimBoundsLayer" method separately to normal 'layer merge'.
  1924. */
  1925. if (method == TrimBoundsLayer)
  1926. {
  1927. number_images=GetImageListLength(image);
  1928. for (scene=0; scene < (ssize_t) number_images; scene++)
  1929. {
  1930. image->page.x-=page.x;
  1931. image->page.y-=page.y;
  1932. image->page.width=width;
  1933. image->page.height=height;
  1934. proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
  1935. number_images);
  1936. if (proceed == MagickFalse)
  1937. break;
  1938. image=GetNextImageInList(image);
  1939. if (image == (Image *) NULL)
  1940. break;
  1941. }
  1942. return((Image *) NULL);
  1943. }
  1944. /*
  1945. Create canvas size of width and height, and background color.
  1946. */
  1947. canvas=CloneImage(image,width,height,MagickTrue,exception);
  1948. if (canvas == (Image *) NULL)
  1949. return((Image *) NULL);
  1950. (void) SetImageBackgroundColor(canvas,exception);
  1951. canvas->page=page;
  1952. canvas->dispose=UndefinedDispose;
  1953. /*
  1954. Compose images onto canvas, with progress monitor
  1955. */
  1956. number_images=GetImageListLength(image);
  1957. for (scene=0; scene < (ssize_t) number_images; scene++)
  1958. {
  1959. (void) CompositeImage(canvas,image,image->compose,MagickTrue,image->page.x-
  1960. canvas->page.x,image->page.y-canvas->page.y,exception);
  1961. proceed=SetImageProgress(image,MergeLayersTag,(MagickOffsetType) scene,
  1962. number_images);
  1963. if (proceed == MagickFalse)
  1964. break;
  1965. image=GetNextImageInList(image);
  1966. if (image == (Image *) NULL)
  1967. break;
  1968. }
  1969. return(canvas);
  1970. }