PageRenderTime 50ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/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

Large files files are truncated, but you can click here to view the full file

  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_imag

Large files files are truncated, but you can click here to view the full file