• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
6 %         AAA   TTTTT  TTTTT  RRRR   IIIII  BBBB   U   U  TTTTT  EEEEE        %
7 %        A   A    T      T    R   R    I    B   B  U   U    T    E            %
8 %        AAAAA    T      T    RRRR     I    BBBB   U   U    T    EEE          %
9 %        A   A    T      T    R R      I    B   B  U   U    T    E            %
10 %        A   A    T      T    R  R   IIIII  BBBB    UUU     T    EEEEE        %
11 %                                                                             %
12 %                                                                             %
13 %                    MagickCore Get / Set Image Attributes                    %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                   Cristy                                    %
17 %                                October 2002                                 %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    https://imagemagick.org/script/license.php                               %
27 %                                                                             %
28 %  Unless required by applicable law or agreed to in writing, software        %
29 %  distributed under the License is distributed on an "AS IS" BASIS,          %
30 %  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31 %  See the License for the specific language governing permissions and        %
32 %  limitations under the License.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 %
38 */
39 
40 /*
41   Include declarations.
42 */
43 #include "MagickCore/studio.h"
44 #include "MagickCore/artifact.h"
45 #include "MagickCore/attribute.h"
46 #include "MagickCore/blob.h"
47 #include "MagickCore/blob-private.h"
48 #include "MagickCore/cache.h"
49 #include "MagickCore/cache-private.h"
50 #include "MagickCore/cache-view.h"
51 #include "MagickCore/channel.h"
52 #include "MagickCore/client.h"
53 #include "MagickCore/color.h"
54 #include "MagickCore/color-private.h"
55 #include "MagickCore/colormap.h"
56 #include "MagickCore/colormap-private.h"
57 #include "MagickCore/colorspace.h"
58 #include "MagickCore/colorspace-private.h"
59 #include "MagickCore/composite.h"
60 #include "MagickCore/composite-private.h"
61 #include "MagickCore/constitute.h"
62 #include "MagickCore/draw.h"
63 #include "MagickCore/draw-private.h"
64 #include "MagickCore/effect.h"
65 #include "MagickCore/enhance.h"
66 #include "MagickCore/exception.h"
67 #include "MagickCore/exception-private.h"
68 #include "MagickCore/geometry.h"
69 #include "MagickCore/histogram.h"
70 #include "MagickCore/identify.h"
71 #include "MagickCore/image.h"
72 #include "MagickCore/image-private.h"
73 #include "MagickCore/list.h"
74 #include "MagickCore/log.h"
75 #include "MagickCore/memory_.h"
76 #include "MagickCore/magick.h"
77 #include "MagickCore/monitor.h"
78 #include "MagickCore/monitor-private.h"
79 #include "MagickCore/option.h"
80 #include "MagickCore/paint.h"
81 #include "MagickCore/pixel.h"
82 #include "MagickCore/pixel-accessor.h"
83 #include "MagickCore/property.h"
84 #include "MagickCore/quantize.h"
85 #include "MagickCore/quantum-private.h"
86 #include "MagickCore/random_.h"
87 #include "MagickCore/resource_.h"
88 #include "MagickCore/semaphore.h"
89 #include "MagickCore/segment.h"
90 #include "MagickCore/splay-tree.h"
91 #include "MagickCore/string_.h"
92 #include "MagickCore/string-private.h"
93 #include "MagickCore/thread-private.h"
94 #include "MagickCore/threshold.h"
95 #include "MagickCore/transform.h"
96 #include "MagickCore/utility.h"
97 
98 /*
99 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
100 %                                                                             %
101 %                                                                             %
102 %                                                                             %
103 +   G e t I m a g e B o u n d i n g B o x                                     %
104 %                                                                             %
105 %                                                                             %
106 %                                                                             %
107 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
108 %
109 %  GetImageBoundingBox() returns the bounding box of an image canvas.
110 %
111 %  The format of the GetImageBoundingBox method is:
112 %
113 %      RectangleInfo GetImageBoundingBox(const Image *image,
114 %        ExceptionInfo *exception)
115 %
116 %  A description of each parameter follows:
117 %
118 %    o bounds: Method GetImageBoundingBox returns the bounding box of an
119 %      image canvas.
120 %
121 %    o image: the image.
122 %
123 %    o exception: return any errors or warnings in this structure.
124 %
125 */
126 
127 typedef struct _EdgeInfo
128 {
129   double
130     left,
131     right,
132     top,
133     bottom;
134 } EdgeInfo;
135 
GetEdgeBackgroundCensus(const Image * image,const CacheView * image_view,const GravityType gravity,const size_t width,const size_t height,const ssize_t x_offset,const ssize_t y_offset,ExceptionInfo * exception)136 static double GetEdgeBackgroundCensus(const Image *image,
137   const CacheView *image_view,const GravityType gravity,const size_t width,
138   const size_t height,const ssize_t x_offset,const ssize_t y_offset,
139   ExceptionInfo *exception)
140 {
141   CacheView
142     *edge_view;
143 
144   const char
145     *artifact;
146 
147   double
148     census;
149 
150   Image
151     *edge_image;
152 
153   PixelInfo
154     background,
155     pixel;
156 
157   RectangleInfo
158     edge_geometry;
159 
160   const Quantum
161     *p;
162 
163   ssize_t
164     y;
165 
166   /*
167     Determine the percent of image background for this edge.
168   */
169   switch (gravity)
170   {
171     case NorthWestGravity:
172     case NorthGravity:
173     default:
174     {
175       p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception);
176       break;
177     }
178     case NorthEastGravity:
179     case EastGravity:
180     {
181       p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,0,1,1,
182         exception);
183       break;
184     }
185     case SouthEastGravity:
186     case SouthGravity:
187     {
188       p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,
189         (ssize_t) image->rows-1,1,1,exception);
190       break;
191     }
192     case SouthWestGravity:
193     case WestGravity:
194     {
195       p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-1,1,1,
196         exception);
197       break;
198     }
199   }
200   GetPixelInfoPixel(image,p,&background);
201   artifact=GetImageArtifact(image,"background");
202   if (artifact != (const char *) NULL)
203     (void) QueryColorCompliance(artifact,AllCompliance,&background,exception);
204   artifact=GetImageArtifact(image,"trim:background-color");
205   if (artifact != (const char *) NULL)
206     (void) QueryColorCompliance(artifact,AllCompliance,&background,exception);
207   edge_geometry.width=width;
208   edge_geometry.height=height;
209   edge_geometry.x=x_offset;
210   edge_geometry.y=y_offset;
211   GravityAdjustGeometry(image->columns,image->rows,gravity,&edge_geometry);
212   edge_image=CropImage(image,&edge_geometry,exception);
213   if (edge_image == (Image *) NULL)
214     return(0.0);
215   census=0.0;
216   edge_view=AcquireVirtualCacheView(edge_image,exception);
217   for (y=0; y < (ssize_t) edge_image->rows; y++)
218   {
219     ssize_t
220       x;
221 
222     p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
223     if (p == (const Quantum *) NULL)
224       break;
225     for (x=0; x < (ssize_t) edge_image->columns; x++)
226     {
227       GetPixelInfoPixel(edge_image,p,&pixel);
228       if (IsFuzzyEquivalencePixelInfo(&pixel,&background) == MagickFalse)
229         census++;
230       p+=GetPixelChannels(edge_image);
231     }
232   }
233   census/=((double) edge_image->columns*edge_image->rows);
234   edge_view=DestroyCacheView(edge_view);
235   edge_image=DestroyImage(edge_image);
236   return(census);
237 }
238 
GetMinEdgeBackgroundCensus(const EdgeInfo * edge)239 static inline double GetMinEdgeBackgroundCensus(const EdgeInfo *edge)
240 {
241   double
242     census;
243 
244   census=MagickMin(MagickMin(MagickMin(edge->left,edge->right),edge->top),
245     edge->bottom);
246   return(census);
247 }
248 
GetEdgeBoundingBox(const Image * image,ExceptionInfo * exception)249 static RectangleInfo GetEdgeBoundingBox(const Image *image,
250   ExceptionInfo *exception)
251 {
252   CacheView
253     *edge_view;
254 
255   const char
256     *artifact;
257 
258   double
259     background_census,
260     percent_background;
261 
262   EdgeInfo
263     edge,
264     vertex;
265 
266   Image
267     *edge_image;
268 
269   RectangleInfo
270     bounds;
271 
272   /*
273     Get the image bounding box.
274   */
275   assert(image != (Image *) NULL);
276   assert(image->signature == MagickCoreSignature);
277   if (image->debug != MagickFalse)
278     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
279   SetGeometry(image,&bounds);
280   edge_image=CloneImage(image,0,0,MagickTrue,exception);
281   if (edge_image == (Image *) NULL)
282     return(bounds);
283   (void) ParseAbsoluteGeometry("0x0+0+0",&edge_image->page);
284   (void) memset(&vertex,0,sizeof(vertex));
285   edge_view=AcquireVirtualCacheView(edge_image,exception);
286   edge.left=GetEdgeBackgroundCensus(edge_image,edge_view,WestGravity,
287     1,0,0,0,exception);
288   edge.right=GetEdgeBackgroundCensus(edge_image,edge_view,EastGravity,
289     1,0,0,0,exception);
290   edge.top=GetEdgeBackgroundCensus(edge_image,edge_view,NorthGravity,
291     0,1,0,0,exception);
292   edge.bottom=GetEdgeBackgroundCensus(edge_image,edge_view,SouthGravity,
293     0,1,0,0,exception);
294   percent_background=1.0;
295   artifact=GetImageArtifact(edge_image,"trim:percent-background");
296   if (artifact != (const char *) NULL)
297     percent_background=StringToDouble(artifact,(char **) NULL)/100.0;
298   percent_background=MagickMin(MagickMax(1.0-percent_background,MagickEpsilon),
299     1.0);
300   background_census=GetMinEdgeBackgroundCensus(&edge);
301   for ( ; background_census < percent_background;
302           background_census=GetMinEdgeBackgroundCensus(&edge))
303   {
304     if ((bounds.width == 0) || (bounds.height == 0))
305       break;
306     if (fabs(edge.left-background_census) < MagickEpsilon)
307       {
308         /*
309           Trim left edge.
310         */
311         vertex.left++;
312         bounds.width--;
313         edge.left=GetEdgeBackgroundCensus(edge_image,edge_view,
314           NorthWestGravity,1,bounds.height,(ssize_t) vertex.left,(ssize_t)
315           vertex.top,exception);
316         edge.top=GetEdgeBackgroundCensus(edge_image,edge_view,
317           NorthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
318           vertex.top,exception);
319         edge.bottom=GetEdgeBackgroundCensus(edge_image,edge_view,
320           SouthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
321           vertex.bottom,exception);
322         continue;
323       }
324     if (fabs(edge.right-background_census) < MagickEpsilon)
325       {
326         /*
327           Trim right edge.
328         */
329         vertex.right++;
330         bounds.width--;
331         edge.right=GetEdgeBackgroundCensus(edge_image,edge_view,
332           NorthEastGravity,1,bounds.height,(ssize_t) vertex.right,(ssize_t)
333           vertex.top,exception);
334         edge.top=GetEdgeBackgroundCensus(edge_image,edge_view,
335           NorthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
336           vertex.top,exception);
337         edge.bottom=GetEdgeBackgroundCensus(edge_image,edge_view,
338           SouthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
339           vertex.bottom,exception);
340         continue;
341       }
342     if (fabs(edge.top-background_census) < MagickEpsilon)
343       {
344         /*
345           Trim top edge.
346         */
347         vertex.top++;
348         bounds.height--;
349         edge.left=GetEdgeBackgroundCensus(edge_image,edge_view,
350           NorthWestGravity,1,bounds.height,(ssize_t) vertex.left,(ssize_t)
351           vertex.top,exception);
352         edge.right=GetEdgeBackgroundCensus(edge_image,edge_view,
353           NorthEastGravity,1,bounds.height,(ssize_t) vertex.right,(ssize_t)
354           vertex.top,exception);
355         edge.top=GetEdgeBackgroundCensus(edge_image,edge_view,
356           NorthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
357           vertex.top,exception);
358         continue;
359       }
360     if (fabs(edge.bottom-background_census) < MagickEpsilon)
361       {
362         /*
363           Trim bottom edge.
364         */
365         vertex.bottom++;
366         bounds.height--;
367         edge.left=GetEdgeBackgroundCensus(edge_image,edge_view,
368           NorthWestGravity,1,bounds.height,(ssize_t) vertex.left,(ssize_t)
369           vertex.top,exception);
370         edge.right=GetEdgeBackgroundCensus(edge_image,edge_view,
371           NorthEastGravity,1,bounds.height,(ssize_t) vertex.right,(ssize_t)
372           vertex.top,exception);
373         edge.bottom=GetEdgeBackgroundCensus(edge_image,edge_view,
374           SouthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
375           vertex.bottom,exception);
376         continue;
377       }
378   }
379   edge_view=DestroyCacheView(edge_view);
380   edge_image=DestroyImage(edge_image);
381   bounds.x=(ssize_t) vertex.left;
382   bounds.y=(ssize_t) vertex.top;
383   if ((bounds.width == 0) || (bounds.height == 0))
384     (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
385       "GeometryDoesNotContainImage","`%s'",image->filename);
386   return(bounds);
387 }
388 
GetImageBoundingBox(const Image * image,ExceptionInfo * exception)389 MagickExport RectangleInfo GetImageBoundingBox(const Image *image,
390   ExceptionInfo *exception)
391 {
392   CacheView
393     *image_view;
394 
395   const char
396     *artifact;
397 
398   MagickBooleanType
399     status;
400 
401   PixelInfo
402     target[4],
403     zero;
404 
405   RectangleInfo
406     bounds;
407 
408   const Quantum
409     *p;
410 
411   ssize_t
412     y;
413 
414   assert(image != (Image *) NULL);
415   assert(image->signature == MagickCoreSignature);
416   if (image->debug != MagickFalse)
417     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
418   artifact=GetImageArtifact(image,"trim:percent-background");
419   if (artifact != (const char *) NULL)
420     return(GetEdgeBoundingBox(image,exception));
421   artifact=GetImageArtifact(image, "trim:edges");
422   if (artifact == (const char *) NULL)
423     {
424       bounds.width=0;
425       bounds.height=0;
426       bounds.x=(ssize_t) image->columns;
427       bounds.y=(ssize_t) image->rows;
428     }
429   else
430     {
431       char
432         *edges,
433         *p,
434         *q;
435 
436       bounds.width=(size_t) image->columns;
437       bounds.height=(size_t) image->rows;
438       bounds.x=0;
439       bounds.y=0;
440       edges=AcquireString(artifact);
441       q=edges;
442       while ((p=StringToken(",",&q)) != (char *) NULL)
443       {
444         if (LocaleCompare(p,"north") == 0)
445           bounds.y=(ssize_t) image->rows;
446         if (LocaleCompare(p,"east") == 0)
447           bounds.width=0;
448         if (LocaleCompare(p,"south") == 0)
449           bounds.height=0;
450         if (LocaleCompare(p,"west") == 0)
451           bounds.x=(ssize_t) image->columns;
452       }
453       edges=DestroyString(edges);
454     }
455   GetPixelInfo(image,&target[0]);
456   image_view=AcquireVirtualCacheView(image,exception);
457   p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception);
458   if (p == (const Quantum *) NULL)
459     {
460       image_view=DestroyCacheView(image_view);
461       return(bounds);
462     }
463   GetPixelInfoPixel(image,p,&target[0]);
464   GetPixelInfo(image,&target[1]);
465   p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,0,1,1,
466     exception);
467   if (p != (const Quantum *) NULL)
468     GetPixelInfoPixel(image,p,&target[1]);
469   GetPixelInfo(image,&target[2]);
470   p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-1,1,1,
471     exception);
472   if (p != (const Quantum *) NULL)
473     GetPixelInfoPixel(image,p,&target[2]);
474   p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,(ssize_t)
475     image->rows-1,1,1,exception);
476   if (p != (const Quantum *) NULL)
477     GetPixelInfoPixel(image,p,&target[3]);
478   status=MagickTrue;
479   GetPixelInfo(image,&zero);
480 #if defined(MAGICKCORE_OPENMP_SUPPORT)
481   #pragma omp parallel for schedule(static) shared(status) \
482     magick_number_threads(image,image,image->rows,1)
483 #endif
484   for (y=0; y < (ssize_t) image->rows; y++)
485   {
486     PixelInfo
487       pixel;
488 
489     RectangleInfo
490       bounding_box;
491 
492     const Quantum
493       *magick_restrict p;
494 
495     ssize_t
496       x;
497 
498     if (status == MagickFalse)
499       continue;
500 #if defined(MAGICKCORE_OPENMP_SUPPORT)
501 #  pragma omp critical (MagickCore_GetImageBoundingBox)
502 #endif
503     bounding_box=bounds;
504     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
505     if (p == (const Quantum *) NULL)
506       {
507         status=MagickFalse;
508         continue;
509       }
510     pixel=zero;
511     for (x=0; x < (ssize_t) image->columns; x++)
512     {
513       GetPixelInfoPixel(image,p,&pixel);
514       if ((x < bounding_box.x) &&
515           (IsFuzzyEquivalencePixelInfo(&pixel,&target[0]) == MagickFalse))
516         bounding_box.x=x;
517       if ((x > (ssize_t) bounding_box.width) &&
518           (IsFuzzyEquivalencePixelInfo(&pixel,&target[1]) == MagickFalse))
519         bounding_box.width=(size_t) x;
520       if ((y < bounding_box.y) &&
521           (IsFuzzyEquivalencePixelInfo(&pixel,&target[0]) == MagickFalse))
522         bounding_box.y=y;
523       if ((y > (ssize_t) bounding_box.height) &&
524           (IsFuzzyEquivalencePixelInfo(&pixel,&target[2]) == MagickFalse))
525         bounding_box.height=(size_t) y;
526       if ((x < (ssize_t) bounding_box.width) &&
527           (y > (ssize_t) bounding_box.height) &&
528           (IsFuzzyEquivalencePixelInfo(&pixel,&target[3]) == MagickFalse))
529         {
530           bounding_box.width=(size_t) x;
531           bounding_box.height=(size_t) y;
532         }
533       p+=GetPixelChannels(image);
534     }
535 #if defined(MAGICKCORE_OPENMP_SUPPORT)
536 #  pragma omp critical (MagickCore_GetImageBoundingBox)
537 #endif
538     {
539       if (bounding_box.x < bounds.x)
540         bounds.x=bounding_box.x;
541       if (bounding_box.y < bounds.y)
542         bounds.y=bounding_box.y;
543       if (bounding_box.width > bounds.width)
544         bounds.width=bounding_box.width;
545       if (bounding_box.height > bounds.height)
546         bounds.height=bounding_box.height;
547     }
548   }
549   image_view=DestroyCacheView(image_view);
550   if ((bounds.width == 0) || (bounds.height == 0))
551     (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
552       "GeometryDoesNotContainImage","`%s'",image->filename);
553   else
554     {
555       bounds.width-=(bounds.x-1);
556       bounds.height-=(bounds.y-1);
557     }
558   return(bounds);
559 }
560 
561 /*
562 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
563 %                                                                             %
564 %                                                                             %
565 %                                                                             %
566 %   G e t I m a g e C o n v e x H u l l                                       %
567 %                                                                             %
568 %                                                                             %
569 %                                                                             %
570 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
571 %
572 %  GetImageConvexHull() returns the convex hull points of an image canvas.
573 %
574 %  The format of the GetImageConvexHull method is:
575 %
576 %      PointInfo *GetImageConvexHull(const Image *image,
577 %        size_t number_vertices,ExceptionInfo *exception)
578 %
579 %  A description of each parameter follows:
580 %
581 %    o image: the image.
582 %
583 %    o number_vertices: the number of vertices in the convex hull.
584 %
585 %    o exception: return any errors or warnings in this structure.
586 %
587 */
588 
LexicographicalOrder(PointInfo * a,PointInfo * b,PointInfo * c)589 static double LexicographicalOrder(PointInfo *a,PointInfo *b,PointInfo *c)
590 {
591   /*
592     Order by x-coordinate, and in case of a tie, by y-coordinate.
593   */
594   return((b->x-a->x)*(c->y-a->y)-(b->y-a->y)*(c->x-a->x));
595 }
596 
GetEdgeBackgroundColor(const Image * image,const CacheView * image_view,ExceptionInfo * exception)597 static PixelInfo GetEdgeBackgroundColor(const Image *image,
598   const CacheView *image_view,ExceptionInfo *exception)
599 {
600   const char
601     *artifact;
602 
603   double
604     census[4],
605     edge_census;
606 
607   PixelInfo
608     background[4],
609     edge_background;
610 
611   ssize_t
612     i;
613 
614   /*
615     Most dominant color of edges/corners is the background color of the image.
616   */
617   artifact=GetImageArtifact(image,"convex-hull:background-color");
618   if (artifact == (const char *) NULL)
619     artifact=GetImageArtifact(image,"background");
620 #if defined(MAGICKCORE_OPENMP_SUPPORT)
621   #pragma omp parallel for schedule(static)
622 #endif
623   for (i=0; i < 4; i++)
624   {
625     CacheView
626       *edge_view;
627 
628     GravityType
629       gravity;
630 
631     Image
632       *edge_image;
633 
634     PixelInfo
635       pixel;
636 
637     RectangleInfo
638       edge_geometry;
639 
640     const Quantum
641       *p;
642 
643     ssize_t
644       y;
645 
646     census[i]=0.0;
647     (void) memset(&edge_geometry,0,sizeof(edge_geometry));
648     switch (i)
649     {
650       case 0:
651       default:
652       {
653         p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-1,1,1,
654           exception);
655         gravity=WestGravity;
656         edge_geometry.width=1;
657         edge_geometry.height=0;
658         break;
659       }
660       case 1:
661       {
662         p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,0,1,1,
663           exception);
664         gravity=EastGravity;
665         edge_geometry.width=1;
666         edge_geometry.height=0;
667         break;
668       }
669       case 2:
670       {
671         p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception);
672         gravity=NorthGravity;
673         edge_geometry.width=0;
674         edge_geometry.height=1;
675         break;
676       }
677       case 3:
678       {
679         p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,
680           (ssize_t) image->rows-1,1,1,exception);
681         gravity=SouthGravity;
682         edge_geometry.width=0;
683         edge_geometry.height=1;
684         break;
685       }
686     }
687     GetPixelInfoPixel(image,p,background+i);
688     if (artifact != (const char *) NULL)
689       (void) QueryColorCompliance(artifact,AllCompliance,background+i,
690         exception);
691     GravityAdjustGeometry(image->columns,image->rows,gravity,&edge_geometry);
692     edge_image=CropImage(image,&edge_geometry,exception);
693     if (edge_image == (Image *) NULL)
694       continue;
695     edge_view=AcquireVirtualCacheView(edge_image,exception);
696     for (y=0; y < (ssize_t) edge_image->rows; y++)
697     {
698       ssize_t
699         x;
700 
701       p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,
702         exception);
703       if (p == (const Quantum *) NULL)
704         break;
705       for (x=0; x < (ssize_t) edge_image->columns; x++)
706       {
707         GetPixelInfoPixel(edge_image,p,&pixel);
708         if (IsFuzzyEquivalencePixelInfo(&pixel,background+i) == MagickFalse)
709           census[i]++;
710         p+=GetPixelChannels(edge_image);
711       }
712     }
713     edge_view=DestroyCacheView(edge_view);
714     edge_image=DestroyImage(edge_image);
715   }
716   edge_census=(-1.0);
717   for (i=0; i < 4; i++)
718     if (census[i] > edge_census)
719       {
720         edge_background=background[i];
721         edge_census=census[i];
722       }
723   return(edge_background);
724 }
725 
TraceConvexHull(PointInfo * vertices,size_t number_vertices,PointInfo *** monotone_chain,size_t * chain_length)726 void TraceConvexHull(PointInfo *vertices,size_t number_vertices,
727   PointInfo ***monotone_chain,size_t *chain_length)
728 {
729   PointInfo
730     **chain;
731 
732   ssize_t
733     i;
734 
735   size_t
736     demark,
737     n;
738 
739   /*
740     Construct the upper and lower hulls: rightmost to leftmost counterclockwise.
741   */
742   chain=(*monotone_chain);
743   n=0;
744   for (i=0; i < (ssize_t) number_vertices; i++)
745   {
746     while ((n >= 2) &&
747            (LexicographicalOrder(chain[n-2],chain[n-1],&vertices[i]) <= 0.0))
748       n--;
749     chain[n++]=(&vertices[i]);
750   }
751   demark=n+1;
752   for (i=(ssize_t) number_vertices-2; i >= 0; i--)
753   {
754     while ((n >= demark) &&
755            (LexicographicalOrder(chain[n-2],chain[n-1],&vertices[i]) <= 0.0))
756       n--;
757     chain[n++]=(&vertices[i]);
758   }
759   *chain_length=n;
760 }
761 
GetImageConvexHull(const Image * image,size_t * number_vertices,ExceptionInfo * exception)762 MagickExport PointInfo *GetImageConvexHull(const Image *image,
763   size_t *number_vertices,ExceptionInfo *exception)
764 {
765   CacheView
766     *image_view;
767 
768   MagickBooleanType
769     status;
770 
771   MemoryInfo
772     *monotone_info,
773     *vertices_info;
774 
775   PixelInfo
776     background;
777 
778   PointInfo
779     *convex_hull,
780     **monotone_chain,
781     *vertices;
782 
783   size_t
784     n;
785 
786   ssize_t
787     y;
788 
789   /*
790     Identify convex hull vertices of image foreground object(s).
791   */
792   assert(image != (Image *) NULL);
793   assert(image->signature == MagickCoreSignature);
794   if (image->debug != MagickFalse)
795     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
796   *number_vertices=0;
797   vertices_info=AcquireVirtualMemory(image->columns,image->rows*
798     sizeof(*vertices));
799   monotone_info=AcquireVirtualMemory(2*image->columns,2*
800     image->rows*sizeof(*monotone_chain));
801   if ((vertices_info == (MemoryInfo *) NULL) ||
802       (monotone_info == (MemoryInfo *) NULL))
803     {
804       if (monotone_info != (MemoryInfo *) NULL)
805         monotone_info=(MemoryInfo *) RelinquishVirtualMemory(monotone_info);
806       if (vertices_info != (MemoryInfo *) NULL)
807         vertices_info=RelinquishVirtualMemory(vertices_info);
808       return((PointInfo *) NULL);
809     }
810   vertices=(PointInfo *) GetVirtualMemoryBlob(vertices_info);
811   monotone_chain=(PointInfo **) GetVirtualMemoryBlob(monotone_info);
812   image_view=AcquireVirtualCacheView(image,exception);
813   background=GetEdgeBackgroundColor(image,image_view,exception);
814   status=MagickTrue;
815   n=0;
816   for (y=0; y < (ssize_t) image->rows; y++)
817   {
818     const Quantum
819       *p;
820 
821     ssize_t
822       x;
823 
824     if (status == MagickFalse)
825       continue;
826     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
827     if (p == (const Quantum *) NULL)
828       {
829         status=MagickFalse;
830         continue;
831       }
832     for (x=0; x < (ssize_t) image->columns; x++)
833     {
834       PixelInfo
835         pixel;
836 
837       GetPixelInfoPixel(image,p,&pixel);
838       if (IsFuzzyEquivalencePixelInfo(&pixel,&background) == MagickFalse)
839         {
840           vertices[n].x=(double) x;
841           vertices[n].y=(double) y;
842           n++;
843         }
844       p+=GetPixelChannels(image);
845     }
846   }
847   image_view=DestroyCacheView(image_view);
848   /*
849     Return the convex hull of the image foreground object(s).
850   */
851   TraceConvexHull(vertices,n,&monotone_chain,number_vertices);
852   convex_hull=(PointInfo *) AcquireQuantumMemory(*number_vertices,
853     sizeof(*convex_hull));
854   if (convex_hull != (PointInfo *) NULL)
855     for (n=0; n < *number_vertices; n++)
856       convex_hull[n]=(*monotone_chain[n]);
857   monotone_info=RelinquishVirtualMemory(monotone_info);
858   vertices_info=RelinquishVirtualMemory(vertices_info);
859   return(convex_hull);
860 }
861 
862 /*
863 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
864 %                                                                             %
865 %                                                                             %
866 %                                                                             %
867 %   G e t I m a g e D e p t h                                                 %
868 %                                                                             %
869 %                                                                             %
870 %                                                                             %
871 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
872 %
873 %  GetImageDepth() returns the depth of a particular image channel.
874 %
875 %  The format of the GetImageDepth method is:
876 %
877 %      size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
878 %
879 %  A description of each parameter follows:
880 %
881 %    o image: the image.
882 %
883 %    o exception: return any errors or warnings in this structure.
884 %
885 */
GetImageDepth(const Image * image,ExceptionInfo * exception)886 MagickExport size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
887 {
888   CacheView
889     *image_view;
890 
891   MagickBooleanType
892     status;
893 
894   ssize_t
895     i;
896 
897   size_t
898     *current_depth,
899     depth,
900     number_threads;
901 
902   ssize_t
903     y;
904 
905   /*
906     Compute image depth.
907   */
908   assert(image != (Image *) NULL);
909   assert(image->signature == MagickCoreSignature);
910   if (image->debug != MagickFalse)
911     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
912   number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
913   current_depth=(size_t *) AcquireQuantumMemory(number_threads,
914     sizeof(*current_depth));
915   if (current_depth == (size_t *) NULL)
916     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
917   status=MagickTrue;
918   for (i=0; i < (ssize_t) number_threads; i++)
919     current_depth[i]=1;
920   if ((image->storage_class == PseudoClass) &&
921       (image->alpha_trait == UndefinedPixelTrait))
922     {
923       for (i=0; i < (ssize_t) image->colors; i++)
924       {
925         const int
926           id = GetOpenMPThreadId();
927 
928         while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
929         {
930           MagickBooleanType
931             atDepth;
932 
933           QuantumAny
934             range;
935 
936           atDepth=MagickTrue;
937           range=GetQuantumRange(current_depth[id]);
938           if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
939             if (IsPixelAtDepth(ClampToQuantum(image->colormap[i].red),range) == MagickFalse)
940               atDepth=MagickFalse;
941           if ((atDepth != MagickFalse) &&
942               (GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
943             if (IsPixelAtDepth(ClampToQuantum(image->colormap[i].green),range) == MagickFalse)
944               atDepth=MagickFalse;
945           if ((atDepth != MagickFalse) &&
946               (GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
947             if (IsPixelAtDepth(ClampToQuantum(image->colormap[i].blue),range) == MagickFalse)
948               atDepth=MagickFalse;
949           if ((atDepth != MagickFalse))
950             break;
951           current_depth[id]++;
952         }
953       }
954       depth=current_depth[0];
955       for (i=1; i < (ssize_t) number_threads; i++)
956         if (depth < current_depth[i])
957           depth=current_depth[i];
958       current_depth=(size_t *) RelinquishMagickMemory(current_depth);
959       return(depth);
960     }
961   image_view=AcquireVirtualCacheView(image,exception);
962 #if !defined(MAGICKCORE_HDRI_SUPPORT)
963   if ((1UL*QuantumRange) <= MaxMap)
964     {
965       size_t
966         *depth_map;
967 
968       /*
969         Scale pixels to desired (optimized with depth map).
970       */
971       depth_map=(size_t *) AcquireQuantumMemory(MaxMap+1,sizeof(*depth_map));
972       if (depth_map == (size_t *) NULL)
973         ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
974       for (i=0; i <= (ssize_t) MaxMap; i++)
975       {
976         unsigned int
977           depth;
978 
979         for (depth=1; depth < MAGICKCORE_QUANTUM_DEPTH; depth++)
980         {
981           Quantum
982             pixel;
983 
984           QuantumAny
985             range;
986 
987           range=GetQuantumRange(depth);
988           pixel=(Quantum) i;
989           if (pixel == ScaleAnyToQuantum(ScaleQuantumToAny(pixel,range),range))
990             break;
991         }
992         depth_map[i]=depth;
993       }
994 #if defined(MAGICKCORE_OPENMP_SUPPORT)
995       #pragma omp parallel for schedule(static) shared(status) \
996         magick_number_threads(image,image,image->rows,1)
997 #endif
998       for (y=0; y < (ssize_t) image->rows; y++)
999       {
1000         const int
1001           id = GetOpenMPThreadId();
1002 
1003         const Quantum
1004           *magick_restrict p;
1005 
1006         ssize_t
1007           x;
1008 
1009         if (status == MagickFalse)
1010           continue;
1011         p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1012         if (p == (const Quantum *) NULL)
1013           continue;
1014         for (x=0; x < (ssize_t) image->columns; x++)
1015         {
1016           ssize_t
1017             i;
1018 
1019           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1020           {
1021             PixelChannel channel = GetPixelChannelChannel(image,i);
1022             PixelTrait traits = GetPixelChannelTraits(image,channel);
1023             if ((traits & UpdatePixelTrait) == 0)
1024               continue;
1025             if (depth_map[ScaleQuantumToMap(p[i])] > current_depth[id])
1026               current_depth[id]=depth_map[ScaleQuantumToMap(p[i])];
1027           }
1028           p+=GetPixelChannels(image);
1029         }
1030         if (current_depth[id] == MAGICKCORE_QUANTUM_DEPTH)
1031           status=MagickFalse;
1032       }
1033       image_view=DestroyCacheView(image_view);
1034       depth=current_depth[0];
1035       for (i=1; i < (ssize_t) number_threads; i++)
1036         if (depth < current_depth[i])
1037           depth=current_depth[i];
1038       depth_map=(size_t *) RelinquishMagickMemory(depth_map);
1039       current_depth=(size_t *) RelinquishMagickMemory(current_depth);
1040       return(depth);
1041     }
1042 #endif
1043   /*
1044     Compute pixel depth.
1045   */
1046 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1047   #pragma omp parallel for schedule(static) shared(status) \
1048     magick_number_threads(image,image,image->rows,1)
1049 #endif
1050   for (y=0; y < (ssize_t) image->rows; y++)
1051   {
1052     const int
1053       id = GetOpenMPThreadId();
1054 
1055     const Quantum
1056       *magick_restrict p;
1057 
1058     ssize_t
1059       x;
1060 
1061     if (status == MagickFalse)
1062       continue;
1063     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1064     if (p == (const Quantum *) NULL)
1065       continue;
1066     for (x=0; x < (ssize_t) image->columns; x++)
1067     {
1068       ssize_t
1069         i;
1070 
1071       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1072       {
1073         PixelChannel
1074           channel;
1075 
1076         PixelTrait
1077           traits;
1078 
1079         channel=GetPixelChannelChannel(image,i);
1080         traits=GetPixelChannelTraits(image,channel);
1081         if ((traits & UpdatePixelTrait) == 0)
1082           continue;
1083         while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
1084         {
1085           QuantumAny
1086             range;
1087 
1088           range=GetQuantumRange(current_depth[id]);
1089           if (p[i] == ScaleAnyToQuantum(ScaleQuantumToAny(p[i],range),range))
1090             break;
1091           current_depth[id]++;
1092         }
1093       }
1094       p+=GetPixelChannels(image);
1095     }
1096     if (current_depth[id] == MAGICKCORE_QUANTUM_DEPTH)
1097       status=MagickFalse;
1098   }
1099   image_view=DestroyCacheView(image_view);
1100   depth=current_depth[0];
1101   for (i=1; i < (ssize_t) number_threads; i++)
1102     if (depth < current_depth[i])
1103       depth=current_depth[i];
1104   current_depth=(size_t *) RelinquishMagickMemory(current_depth);
1105   return(depth);
1106 }
1107 
1108 /*
1109 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1110 %                                                                             %
1111 %                                                                             %
1112 %                                                                             %
1113 %   G e t I m a g e M i n i m u m B o u n d i n g B o x                       %
1114 %                                                                             %
1115 %                                                                             %
1116 %                                                                             %
1117 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1118 %
1119 %  GetImageMinimumBoundingBox() returns the points that form the minimum
1120 %  bounding box around the image foreground objects with the "Rotating
1121 %  Calipers" algorithm.  The method also returns these properties:
1122 %  minimum-bounding-box:area, minimum-bounding-box:width,
1123 %  minimum-bounding-box:height, and minimum-bounding-box:angle.
1124 %
1125 %  The format of the GetImageMinimumBoundingBox method is:
1126 %
1127 %      PointInfo *GetImageMinimumBoundingBox(Image *image,
1128 %        size_t number_vertices,ExceptionInfo *exception)
1129 %
1130 %  A description of each parameter follows:
1131 %
1132 %    o image: the image.
1133 %
1134 %    o number_vertices: the number of vertices in the bounding box.
1135 %
1136 %    o exception: return any errors or warnings in this structure.
1137 %
1138 */
1139 
1140 typedef struct _CaliperInfo
1141 {
1142   double
1143     area,
1144     width,
1145     height,
1146     projection;
1147 
1148   ssize_t
1149     p,
1150     q,
1151     v;
1152 } CaliperInfo;
1153 
getAngle(PointInfo * p,PointInfo * q)1154 static inline double getAngle(PointInfo *p,PointInfo *q)
1155 {
1156   /*
1157     Get the angle between line (p,q) and horizontal axis, in degrees.
1158   */
1159   return(RadiansToDegrees(atan2(q->y-p->y,q->x-p->x)));
1160 }
1161 
getDistance(PointInfo * p,PointInfo * q)1162 static inline double getDistance(PointInfo *p,PointInfo *q)
1163 {
1164   double
1165     distance;
1166 
1167   distance=hypot(p->x-q->x,p->y-q->y);
1168   return(distance*distance);
1169 }
1170 
getProjection(PointInfo * p,PointInfo * q,PointInfo * v)1171 static inline double getProjection(PointInfo *p,PointInfo *q,PointInfo *v)
1172 {
1173   double
1174     distance;
1175 
1176   /*
1177     Projection of vector (x,y) - p into a line passing through p and q.
1178   */
1179   distance=getDistance(p,q);
1180   if (distance < MagickEpsilon)
1181     return(INFINITY);
1182   return((q->x-p->x)*(v->x-p->x)+(v->y-p->y)*(q->y-p->y))/sqrt(distance);
1183 }
1184 
getFeretDiameter(PointInfo * p,PointInfo * q,PointInfo * v)1185 static inline double getFeretDiameter(PointInfo *p,PointInfo *q,PointInfo *v)
1186 {
1187   double
1188     distance;
1189 
1190   /*
1191     Distance from a point (x,y) to a line passing through p and q.
1192   */
1193   distance=getDistance(p,q);
1194   if (distance < MagickEpsilon)
1195     return(INFINITY);
1196   return((q->x-p->x)*(v->y-p->y)-(v->x-p->x)*(q->y-p->y))/sqrt(distance);
1197 }
1198 
GetImageMinimumBoundingBox(Image * image,size_t * number_vertices,ExceptionInfo * exception)1199 MagickExport PointInfo *GetImageMinimumBoundingBox(Image *image,
1200   size_t *number_vertices,ExceptionInfo *exception)
1201 {
1202   CaliperInfo
1203     caliper_info;
1204 
1205   const char
1206     *artifact;
1207 
1208   double
1209     angle,
1210     diameter,
1211     distance;
1212 
1213   PointInfo
1214     *bounding_box,
1215     *vertices;
1216 
1217   ssize_t
1218     i;
1219 
1220   size_t
1221     number_hull_vertices;
1222 
1223   /*
1224     Generate the minimum bounding box with the "Rotating Calipers" algorithm.
1225   */
1226   assert(image != (Image *) NULL);
1227   assert(image->signature == MagickCoreSignature);
1228   if (image->debug != MagickFalse)
1229     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1230   *number_vertices=0;
1231   vertices=GetImageConvexHull(image,&number_hull_vertices,exception);
1232   if (vertices == (PointInfo *) NULL)
1233     return((PointInfo *) NULL);
1234   *number_vertices=4;
1235   bounding_box=(PointInfo *) AcquireQuantumMemory(*number_vertices,
1236     sizeof(*bounding_box));
1237   if (bounding_box == (PointInfo *) NULL)
1238     {
1239       vertices=(PointInfo *) RelinquishMagickMemory(vertices);
1240       return((PointInfo *) NULL);
1241     }
1242   caliper_info.area=2.0*image->columns*image->rows;
1243   caliper_info.width=(double) image->columns+image->rows;
1244   caliper_info.height=0.0;
1245   caliper_info.projection=0.0;
1246   caliper_info.p=(-1);
1247   caliper_info.q=(-1);
1248   caliper_info.v=(-1);
1249   for (i=0; i < (ssize_t) number_hull_vertices; i++)
1250   {
1251     double
1252       area = 0.0,
1253       max_projection = 0.0,
1254       min_diameter = -1.0,
1255       min_projection = 0.0;
1256 
1257     ssize_t
1258       j,
1259       k;
1260 
1261     ssize_t
1262       p = -1,
1263       q = -1,
1264       v = -1;
1265 
1266     for (j=0; j < (ssize_t) number_hull_vertices; j++)
1267     {
1268       double
1269         diameter;
1270 
1271       diameter=fabs(getFeretDiameter(&vertices[i],
1272         &vertices[(i+1) % number_hull_vertices],&vertices[j]));
1273       if (min_diameter < diameter)
1274         {
1275           min_diameter=diameter;
1276           p=i;
1277           q=(i+1) % number_hull_vertices;
1278           v=j;
1279         }
1280     }
1281     for (k=0; k < (ssize_t) number_hull_vertices; k++)
1282     {
1283       double
1284         projection;
1285 
1286       /*
1287         Rotating calipers.
1288       */
1289       projection=getProjection(&vertices[p],&vertices[q],&vertices[k]);
1290       min_projection=MagickMin(min_projection,projection);
1291       max_projection=MagickMax(max_projection,projection);
1292     }
1293     area=min_diameter*(max_projection-min_projection);
1294     if (caliper_info.area > area)
1295       {
1296         caliper_info.area=area;
1297         caliper_info.width=min_diameter;
1298         caliper_info.height=max_projection-min_projection;
1299         caliper_info.projection=max_projection;
1300         caliper_info.p=p;
1301         caliper_info.q=q;
1302         caliper_info.v=v;
1303       }
1304   }
1305   /*
1306     Initialize minimum bounding box.
1307   */
1308   diameter=getFeretDiameter(&vertices[caliper_info.p],
1309     &vertices[caliper_info.q],&vertices[caliper_info.v]);
1310   angle=atan2(vertices[caliper_info.q].y-vertices[caliper_info.p].y,
1311     vertices[caliper_info.q].x-vertices[caliper_info.p].x);
1312   bounding_box[0].x=vertices[caliper_info.p].x+cos(angle)*
1313     caliper_info.projection;
1314   bounding_box[0].y=vertices[caliper_info.p].y+sin(angle)*
1315     caliper_info.projection;
1316   bounding_box[1].x=floor(bounding_box[0].x+cos(angle+MagickPI/2.0)*diameter+
1317     0.5);
1318   bounding_box[1].y=floor(bounding_box[0].y+sin(angle+MagickPI/2.0)*diameter+
1319     0.5);
1320   bounding_box[2].x=floor(bounding_box[1].x+cos(angle)*(-caliper_info.height)+
1321     0.5);
1322   bounding_box[2].y=floor(bounding_box[1].y+sin(angle)*(-caliper_info.height)+
1323     0.5);
1324   bounding_box[3].x=floor(bounding_box[2].x+cos(angle+MagickPI/2.0)*(-diameter)+
1325     0.5);
1326   bounding_box[3].y=floor(bounding_box[2].y+sin(angle+MagickPI/2.0)*(-diameter)+
1327     0.5);
1328   /*
1329     Export minimum bounding box properties.
1330   */
1331   (void) FormatImageProperty(image,"minimum-bounding-box:area","%.*g",
1332     GetMagickPrecision(),caliper_info.area);
1333   (void) FormatImageProperty(image,"minimum-bounding-box:width","%.*g",
1334     GetMagickPrecision(),caliper_info.width);
1335   (void) FormatImageProperty(image,"minimum-bounding-box:height","%.*g",
1336     GetMagickPrecision(),caliper_info.height);
1337   (void) FormatImageProperty(image,"minimum-bounding-box:_p","%.*g,%.*g",
1338     GetMagickPrecision(),vertices[caliper_info.p].x,
1339     GetMagickPrecision(),vertices[caliper_info.p].y);
1340   (void) FormatImageProperty(image,"minimum-bounding-box:_q","%.*g,%.*g",
1341     GetMagickPrecision(),vertices[caliper_info.q].x,
1342     GetMagickPrecision(),vertices[caliper_info.q].y);
1343   (void) FormatImageProperty(image,"minimum-bounding-box:_v","%.*g,%.*g",
1344     GetMagickPrecision(),vertices[caliper_info.v].x,
1345     GetMagickPrecision(),vertices[caliper_info.v].y);
1346   /*
1347     Find smallest angle to origin.
1348   */
1349   distance=hypot(bounding_box[0].x,bounding_box[0].y);
1350   angle=getAngle(&bounding_box[0],&bounding_box[1]);
1351   for (i=1; i < 4; i++)
1352   {
1353     double d = hypot(bounding_box[i].x,bounding_box[i].y);
1354     if (d < distance)
1355       {
1356         distance=d;
1357         angle=getAngle(&bounding_box[i],&bounding_box[(i+1) % 4]);
1358       }
1359   }
1360   artifact=GetImageArtifact(image,"minimum-bounding-box:orientation");
1361   if (artifact != (const char *) NULL)
1362     {
1363       double
1364         length,
1365         q_length,
1366         p_length;
1367 
1368       PointInfo
1369         delta,
1370         point;
1371 
1372       /*
1373         Find smallest perpendicular distance from edge to origin.
1374       */
1375       point=bounding_box[0];
1376       for (i=1; i < 4; i++)
1377       {
1378         if (bounding_box[i].x < point.x)
1379           point.x=bounding_box[i].x;
1380         if (bounding_box[i].y < point.y)
1381           point.y=bounding_box[i].y;
1382       }
1383       for (i=0; i < 4; i++)
1384       {
1385         bounding_box[i].x-=point.x;
1386         bounding_box[i].y-=point.y;
1387       }
1388       for (i=0; i < 4; i++)
1389       {
1390         double
1391           d,
1392           intercept,
1393           slope;
1394 
1395         delta.x=bounding_box[(i+1) % 4].x-bounding_box[i].x;
1396         delta.y=bounding_box[(i+1) % 4].y-bounding_box[i].y;
1397         slope=delta.y*PerceptibleReciprocal(delta.x);
1398         intercept=bounding_box[(i+1) % 4].y-slope*bounding_box[i].x;
1399         d=fabs((slope*bounding_box[i].x-bounding_box[i].y+intercept)*
1400           PerceptibleReciprocal(sqrt(slope*slope+1.0)));
1401         if ((i == 0) || (d < distance))
1402           {
1403             distance=d;
1404             point=delta;
1405           }
1406       }
1407       angle=RadiansToDegrees(atan(point.y*PerceptibleReciprocal(point.x)));
1408       length=hypot(point.x,point.y);
1409       p_length=fabs((double) MagickMax(caliper_info.width,caliper_info.height)-
1410         length);
1411       q_length=fabs(length-(double) MagickMin(caliper_info.width,
1412         caliper_info.height));
1413       if (LocaleCompare(artifact,"landscape") == 0)
1414         {
1415           if (p_length > q_length)
1416             angle+=(angle < 0.0) ? 90.0 : -90.0;
1417         }
1418       else
1419         if (LocaleCompare(artifact,"portrait") == 0)
1420           {
1421             if (p_length < q_length)
1422               angle+=(angle >= 0.0) ? 90.0 : -90.0;
1423           }
1424     }
1425   (void) FormatImageProperty(image,"minimum-bounding-box:angle","%.*g",
1426     GetMagickPrecision(),angle);
1427   (void) FormatImageProperty(image,"minimum-bounding-box:unrotate","%.*g",
1428     GetMagickPrecision(),-angle);
1429   vertices=(PointInfo *) RelinquishMagickMemory(vertices);
1430   return(bounding_box);
1431 }
1432 
1433 /*
1434 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1435 %                                                                             %
1436 %                                                                             %
1437 %                                                                             %
1438 %   G e t I m a g e Q u a n t u m D e p t h                                   %
1439 %                                                                             %
1440 %                                                                             %
1441 %                                                                             %
1442 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1443 %
1444 %  GetImageQuantumDepth() returns the depth of the image rounded to a legal
1445 %  quantum depth: 8, 16, or 32.
1446 %
1447 %  The format of the GetImageQuantumDepth method is:
1448 %
1449 %      size_t GetImageQuantumDepth(const Image *image,
1450 %        const MagickBooleanType constrain)
1451 %
1452 %  A description of each parameter follows:
1453 %
1454 %    o image: the image.
1455 %
1456 %    o constrain: A value other than MagickFalse, constrains the depth to
1457 %      a maximum of MAGICKCORE_QUANTUM_DEPTH.
1458 %
1459 */
GetImageQuantumDepth(const Image * image,const MagickBooleanType constrain)1460 MagickExport size_t GetImageQuantumDepth(const Image *image,
1461   const MagickBooleanType constrain)
1462 {
1463   size_t
1464     depth;
1465 
1466   depth=image->depth;
1467   if (depth <= 8)
1468     depth=8;
1469   else
1470     if (depth <= 16)
1471       depth=16;
1472     else
1473       if (depth <= 32)
1474         depth=32;
1475       else
1476         if (depth <= 64)
1477           depth=64;
1478   if (constrain != MagickFalse)
1479     depth=(size_t) MagickMin((double) depth,(double) MAGICKCORE_QUANTUM_DEPTH);
1480   return(depth);
1481 }
1482 
1483 /*
1484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1485 %                                                                             %
1486 %                                                                             %
1487 %                                                                             %
1488 %   G e t I m a g e T y p e                                                   %
1489 %                                                                             %
1490 %                                                                             %
1491 %                                                                             %
1492 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1493 %
1494 %  GetImageType() returns the type of image:
1495 %
1496 %        Bilevel         Grayscale        GrayscaleMatte
1497 %        Palette         PaletteMatte     TrueColor
1498 %        TrueColorMatte  ColorSeparation  ColorSeparationMatte
1499 %
1500 %  The format of the GetImageType method is:
1501 %
1502 %      ImageType GetImageType(const Image *image)
1503 %
1504 %  A description of each parameter follows:
1505 %
1506 %    o image: the image.
1507 %
1508 */
GetImageType(const Image * image)1509 MagickExport ImageType GetImageType(const Image *image)
1510 {
1511   assert(image != (Image *) NULL);
1512   assert(image->signature == MagickCoreSignature);
1513   if (image->colorspace == CMYKColorspace)
1514     {
1515       if (image->alpha_trait == UndefinedPixelTrait)
1516         return(ColorSeparationType);
1517       return(ColorSeparationAlphaType);
1518     }
1519   if (IsImageMonochrome(image) != MagickFalse)
1520     return(BilevelType);
1521   if (IsImageGray(image) != MagickFalse)
1522     {
1523       if (image->alpha_trait != UndefinedPixelTrait)
1524         return(GrayscaleAlphaType);
1525       return(GrayscaleType);
1526     }
1527   if (IsPaletteImage(image) != MagickFalse)
1528     {
1529       if (image->alpha_trait != UndefinedPixelTrait)
1530         return(PaletteAlphaType);
1531       return(PaletteType);
1532     }
1533   if (image->alpha_trait != UndefinedPixelTrait)
1534     return(TrueColorAlphaType);
1535   return(TrueColorType);
1536 }
1537 
1538 /*
1539 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1540 %                                                                             %
1541 %                                                                             %
1542 %                                                                             %
1543 %     I d e n t i f y I m a g e G r a y                                       %
1544 %                                                                             %
1545 %                                                                             %
1546 %                                                                             %
1547 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1548 %
1549 %  IdentifyImageGray() returns grayscale if all the pixels in the image have
1550 %  the same red, green, and blue intensities, and bi-level is the intensity is
1551 %  either 0 or QuantumRange. Otherwise undefined is returned.
1552 %
1553 %  The format of the IdentifyImageGray method is:
1554 %
1555 %      ImageType IdentifyImageGray(const Image *image,ExceptionInfo *exception)
1556 %
1557 %  A description of each parameter follows:
1558 %
1559 %    o image: the image.
1560 %
1561 %    o exception: return any errors or warnings in this structure.
1562 %
1563 */
IdentifyImageGray(const Image * image,ExceptionInfo * exception)1564 MagickExport ImageType IdentifyImageGray(const Image *image,
1565   ExceptionInfo *exception)
1566 {
1567   CacheView
1568     *image_view;
1569 
1570   ImageType
1571     type;
1572 
1573   const Quantum
1574     *p;
1575 
1576   ssize_t
1577     x;
1578 
1579   ssize_t
1580     y;
1581 
1582   assert(image != (Image *) NULL);
1583   assert(image->signature == MagickCoreSignature);
1584   if (image->debug != MagickFalse)
1585     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1586   if ((image->type == BilevelType) || (image->type == GrayscaleType) ||
1587       (image->type == GrayscaleAlphaType))
1588     return(image->type);
1589   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1590     return(UndefinedType);
1591   type=BilevelType;
1592   image_view=AcquireVirtualCacheView(image,exception);
1593   for (y=0; y < (ssize_t) image->rows; y++)
1594   {
1595     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1596     if (p == (const Quantum *) NULL)
1597       break;
1598     for (x=0; x < (ssize_t) image->columns; x++)
1599     {
1600       if (IsPixelGray(image,p) == MagickFalse)
1601         {
1602           type=UndefinedType;
1603           break;
1604         }
1605       if ((type == BilevelType) &&
1606           (IsPixelMonochrome(image,p) == MagickFalse))
1607         type=GrayscaleType;
1608       p+=GetPixelChannels(image);
1609     }
1610     if (type == UndefinedType)
1611       break;
1612   }
1613   image_view=DestroyCacheView(image_view);
1614   if ((type == GrayscaleType) && (image->alpha_trait != UndefinedPixelTrait))
1615     type=GrayscaleAlphaType;
1616   return(type);
1617 }
1618 
1619 /*
1620 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1621 %                                                                             %
1622 %                                                                             %
1623 %                                                                             %
1624 %   I d e n t i f y I m a g e M o n o c h r o m e                             %
1625 %                                                                             %
1626 %                                                                             %
1627 %                                                                             %
1628 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1629 %
1630 %  IdentifyImageMonochrome() returns MagickTrue if all the pixels in the image
1631 %  have the same red, green, and blue intensities and the intensity is either
1632 %  0 or QuantumRange.
1633 %
1634 %  The format of the IdentifyImageMonochrome method is:
1635 %
1636 %      MagickBooleanType IdentifyImageMonochrome(const Image *image,
1637 %        ExceptionInfo *exception)
1638 %
1639 %  A description of each parameter follows:
1640 %
1641 %    o image: the image.
1642 %
1643 %    o exception: return any errors or warnings in this structure.
1644 %
1645 */
IdentifyImageMonochrome(const Image * image,ExceptionInfo * exception)1646 MagickExport MagickBooleanType IdentifyImageMonochrome(const Image *image,
1647   ExceptionInfo *exception)
1648 {
1649   CacheView
1650     *image_view;
1651 
1652   MagickBooleanType
1653     bilevel;
1654 
1655   ssize_t
1656     x;
1657 
1658   const Quantum
1659     *p;
1660 
1661   ssize_t
1662     y;
1663 
1664   assert(image != (Image *) NULL);
1665   assert(image->signature == MagickCoreSignature);
1666   if (image->debug != MagickFalse)
1667     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1668   if (image->type == BilevelType)
1669     return(MagickTrue);
1670   if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1671     return(MagickFalse);
1672   bilevel=MagickTrue;
1673   image_view=AcquireVirtualCacheView(image,exception);
1674   for (y=0; y < (ssize_t) image->rows; y++)
1675   {
1676     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1677     if (p == (const Quantum *) NULL)
1678       break;
1679     for (x=0; x < (ssize_t) image->columns; x++)
1680     {
1681       if (IsPixelMonochrome(image,p) == MagickFalse)
1682         {
1683           bilevel=MagickFalse;
1684           break;
1685         }
1686       p+=GetPixelChannels(image);
1687     }
1688     if (bilevel == MagickFalse)
1689       break;
1690   }
1691   image_view=DestroyCacheView(image_view);
1692   return(bilevel);
1693 }
1694 
1695 /*
1696 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1697 %                                                                             %
1698 %                                                                             %
1699 %                                                                             %
1700 %   I d e n t i f y I m a g e T y p e                                         %
1701 %                                                                             %
1702 %                                                                             %
1703 %                                                                             %
1704 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1705 %
1706 %  IdentifyImageType() returns the potential type of image:
1707 %
1708 %        Bilevel         Grayscale        GrayscaleMatte
1709 %        Palette         PaletteMatte     TrueColor
1710 %        TrueColorMatte  ColorSeparation  ColorSeparationMatte
1711 %
1712 %  To ensure the image type matches its potential, use SetImageType():
1713 %
1714 %    (void) SetImageType(image,IdentifyImageType(image,exception),exception);
1715 %
1716 %  The format of the IdentifyImageType method is:
1717 %
1718 %      ImageType IdentifyImageType(const Image *image,ExceptionInfo *exception)
1719 %
1720 %  A description of each parameter follows:
1721 %
1722 %    o image: the image.
1723 %
1724 %    o exception: return any errors or warnings in this structure.
1725 %
1726 */
IdentifyImageType(const Image * image,ExceptionInfo * exception)1727 MagickExport ImageType IdentifyImageType(const Image *image,
1728   ExceptionInfo *exception)
1729 {
1730   assert(image != (Image *) NULL);
1731   assert(image->signature == MagickCoreSignature);
1732   if (image->debug != MagickFalse)
1733     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1734   if (image->colorspace == CMYKColorspace)
1735     {
1736       if (image->alpha_trait == UndefinedPixelTrait)
1737         return(ColorSeparationType);
1738       return(ColorSeparationAlphaType);
1739     }
1740   if (IdentifyImageMonochrome(image,exception) != MagickFalse)
1741     return(BilevelType);
1742   if (IdentifyImageGray(image,exception) != UndefinedType)
1743     {
1744       if (image->alpha_trait != UndefinedPixelTrait)
1745         return(GrayscaleAlphaType);
1746       return(GrayscaleType);
1747     }
1748   if (IdentifyPaletteImage(image,exception) != MagickFalse)
1749     {
1750       if (image->alpha_trait != UndefinedPixelTrait)
1751         return(PaletteAlphaType);
1752       return(PaletteType);
1753     }
1754   if (image->alpha_trait != UndefinedPixelTrait)
1755     return(TrueColorAlphaType);
1756   return(TrueColorType);
1757 }
1758 
1759 /*
1760 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1761 %                                                                             %
1762 %                                                                             %
1763 %                                                                             %
1764 %     I s I m a g e G r a y                                                   %
1765 %                                                                             %
1766 %                                                                             %
1767 %                                                                             %
1768 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1769 %
1770 %  IsImageGray() returns MagickTrue if the type of the image is grayscale or
1771 %  bi-level.
1772 %
1773 %  The format of the IsImageGray method is:
1774 %
1775 %      MagickBooleanType IsImageGray(const Image *image)
1776 %
1777 %  A description of each parameter follows:
1778 %
1779 %    o image: the image.
1780 %
1781 */
IsImageGray(const Image * image)1782 MagickExport MagickBooleanType IsImageGray(const Image *image)
1783 {
1784   assert(image != (Image *) NULL);
1785   assert(image->signature == MagickCoreSignature);
1786   if ((image->type == BilevelType) || (image->type == GrayscaleType) ||
1787       (image->type == GrayscaleAlphaType))
1788     return(MagickTrue);
1789   return(MagickFalse);
1790 }
1791 
1792 /*
1793 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1794 %                                                                             %
1795 %                                                                             %
1796 %                                                                             %
1797 %   I s I m a g e M o n o c h r o m e                                         %
1798 %                                                                             %
1799 %                                                                             %
1800 %                                                                             %
1801 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1802 %
1803 %  IsImageMonochrome() returns MagickTrue if type of the image is bi-level.
1804 %
1805 %  The format of the IsImageMonochrome method is:
1806 %
1807 %      MagickBooleanType IsImageMonochrome(const Image *image)
1808 %
1809 %  A description of each parameter follows:
1810 %
1811 %    o image: the image.
1812 %
1813 */
IsImageMonochrome(const Image * image)1814 MagickExport MagickBooleanType IsImageMonochrome(const Image *image)
1815 {
1816   assert(image != (Image *) NULL);
1817   assert(image->signature == MagickCoreSignature);
1818   if (image->type == BilevelType)
1819     return(MagickTrue);
1820   return(MagickFalse);
1821 }
1822 
1823 /*
1824 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1825 %                                                                             %
1826 %                                                                             %
1827 %                                                                             %
1828 %     I s I m a g e O p a q u e                                               %
1829 %                                                                             %
1830 %                                                                             %
1831 %                                                                             %
1832 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1833 %
1834 %  IsImageOpaque() returns MagickTrue if none of the pixels in the image have
1835 %  an alpha value other than OpaqueAlpha (QuantumRange).
1836 %
1837 %  Will return true immediatally is alpha channel is not available.
1838 %
1839 %  The format of the IsImageOpaque method is:
1840 %
1841 %      MagickBooleanType IsImageOpaque(const Image *image,
1842 %        ExceptionInfo *exception)
1843 %
1844 %  A description of each parameter follows:
1845 %
1846 %    o image: the image.
1847 %
1848 %    o exception: return any errors or warnings in this structure.
1849 %
1850 */
IsImageOpaque(const Image * image,ExceptionInfo * exception)1851 MagickExport MagickBooleanType IsImageOpaque(const Image *image,
1852   ExceptionInfo *exception)
1853 {
1854   CacheView
1855     *image_view;
1856 
1857   const Quantum
1858     *p;
1859 
1860   ssize_t
1861     x;
1862 
1863   ssize_t
1864     y;
1865 
1866   /*
1867     Determine if image is opaque.
1868   */
1869   assert(image != (Image *) NULL);
1870   assert(image->signature == MagickCoreSignature);
1871   if (image->debug != MagickFalse)
1872     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1873   if (image->alpha_trait == UndefinedPixelTrait)
1874     return(MagickTrue);
1875   image_view=AcquireVirtualCacheView(image,exception);
1876   for (y=0; y < (ssize_t) image->rows; y++)
1877   {
1878     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1879     if (p == (const Quantum *) NULL)
1880       break;
1881     for (x=0; x < (ssize_t) image->columns; x++)
1882     {
1883       if (GetPixelAlpha(image,p) != OpaqueAlpha)
1884         break;
1885       p+=GetPixelChannels(image);
1886     }
1887     if (x < (ssize_t) image->columns)
1888       break;
1889   }
1890   image_view=DestroyCacheView(image_view);
1891   return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
1892 }
1893 
1894 /*
1895 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1896 %                                                                             %
1897 %                                                                             %
1898 %                                                                             %
1899 %   S e t I m a g e D e p t h                                                 %
1900 %                                                                             %
1901 %                                                                             %
1902 %                                                                             %
1903 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1904 %
1905 %  SetImageDepth() sets the depth of the image.
1906 %
1907 %  The format of the SetImageDepth method is:
1908 %
1909 %      MagickBooleanType SetImageDepth(Image *image,const size_t depth,
1910 %        ExceptionInfo *exception)
1911 %
1912 %  A description of each parameter follows:
1913 %
1914 %    o image: the image.
1915 %
1916 %    o channel: the channel.
1917 %
1918 %    o depth: the image depth.
1919 %
1920 %    o exception: return any errors or warnings in this structure.
1921 %
1922 */
SetImageDepth(Image * image,const size_t depth,ExceptionInfo * exception)1923 MagickExport MagickBooleanType SetImageDepth(Image *image,
1924   const size_t depth,ExceptionInfo *exception)
1925 {
1926   CacheView
1927     *image_view;
1928 
1929   MagickBooleanType
1930     status;
1931 
1932   QuantumAny
1933     range;
1934 
1935   ssize_t
1936     y;
1937 
1938   assert(image != (Image *) NULL);
1939   if (image->debug != MagickFalse)
1940     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1941   assert(image->signature == MagickCoreSignature);
1942   if (depth >= MAGICKCORE_QUANTUM_DEPTH)
1943     {
1944       image->depth=depth;
1945       return(MagickTrue);
1946     }
1947   range=GetQuantumRange(depth);
1948   if (image->storage_class == PseudoClass)
1949     {
1950       ssize_t
1951         i;
1952 
1953 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1954       #pragma omp parallel for schedule(static) shared(status) \
1955         magick_number_threads(image,image,image->colors,1)
1956 #endif
1957       for (i=0; i < (ssize_t) image->colors; i++)
1958       {
1959         if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1960           image->colormap[i].red=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
1961             ClampPixel(image->colormap[i].red),range),range);
1962         if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1963           image->colormap[i].green=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
1964             ClampPixel(image->colormap[i].green),range),range);
1965         if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1966           image->colormap[i].blue=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
1967             ClampPixel(image->colormap[i].blue),range),range);
1968         if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1969           image->colormap[i].alpha=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
1970             ClampPixel(image->colormap[i].alpha),range),range);
1971       }
1972     }
1973   status=MagickTrue;
1974   image_view=AcquireAuthenticCacheView(image,exception);
1975 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1976   if ((1UL*QuantumRange) <= MaxMap)
1977     {
1978       Quantum
1979         *depth_map;
1980 
1981       ssize_t
1982         i;
1983 
1984       /*
1985         Scale pixels to desired (optimized with depth map).
1986       */
1987       depth_map=(Quantum *) AcquireQuantumMemory(MaxMap+1,sizeof(*depth_map));
1988       if (depth_map == (Quantum *) NULL)
1989         ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1990       for (i=0; i <= (ssize_t) MaxMap; i++)
1991         depth_map[i]=ScaleAnyToQuantum(ScaleQuantumToAny((Quantum) i,range),
1992           range);
1993 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1994       #pragma omp parallel for schedule(static) shared(status) \
1995         magick_number_threads(image,image,image->rows,1)
1996 #endif
1997       for (y=0; y < (ssize_t) image->rows; y++)
1998       {
1999         ssize_t
2000           x;
2001 
2002         Quantum
2003           *magick_restrict q;
2004 
2005         if (status == MagickFalse)
2006           continue;
2007         q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2008           exception);
2009         if (q == (Quantum *) NULL)
2010           {
2011             status=MagickFalse;
2012             continue;
2013           }
2014         for (x=0; x < (ssize_t) image->columns; x++)
2015         {
2016           ssize_t
2017             i;
2018 
2019           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2020           {
2021             PixelChannel
2022               channel;
2023 
2024             PixelTrait
2025               traits;
2026 
2027             channel=GetPixelChannelChannel(image,i);
2028             traits=GetPixelChannelTraits(image,channel);
2029             if ((traits & UpdatePixelTrait) == 0)
2030               continue;
2031             q[i]=depth_map[ScaleQuantumToMap(q[i])];
2032           }
2033           q+=GetPixelChannels(image);
2034         }
2035         if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2036           {
2037             status=MagickFalse;
2038             continue;
2039           }
2040       }
2041       image_view=DestroyCacheView(image_view);
2042       depth_map=(Quantum *) RelinquishMagickMemory(depth_map);
2043       if (status != MagickFalse)
2044         image->depth=depth;
2045       return(status);
2046     }
2047 #endif
2048   /*
2049     Scale pixels to desired depth.
2050   */
2051 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2052   #pragma omp parallel for schedule(static) shared(status) \
2053     magick_number_threads(image,image,image->rows,1)
2054 #endif
2055   for (y=0; y < (ssize_t) image->rows; y++)
2056   {
2057     ssize_t
2058       x;
2059 
2060     Quantum
2061       *magick_restrict q;
2062 
2063     if (status == MagickFalse)
2064       continue;
2065     q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2066     if (q == (Quantum *) NULL)
2067       {
2068         status=MagickFalse;
2069         continue;
2070       }
2071     for (x=0; x < (ssize_t) image->columns; x++)
2072     {
2073       ssize_t
2074         i;
2075 
2076       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2077       {
2078         PixelChannel
2079           channel;
2080 
2081         PixelTrait
2082           traits;
2083 
2084         channel=GetPixelChannelChannel(image,i);
2085         traits=GetPixelChannelTraits(image,channel);
2086         if ((traits & UpdatePixelTrait) == 0)
2087           continue;
2088         q[i]=ScaleAnyToQuantum(ScaleQuantumToAny(ClampPixel((MagickRealType)
2089           q[i]),range),range);
2090       }
2091       q+=GetPixelChannels(image);
2092     }
2093     if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2094       {
2095         status=MagickFalse;
2096         continue;
2097       }
2098   }
2099   image_view=DestroyCacheView(image_view);
2100   if (status != MagickFalse)
2101     image->depth=depth;
2102   return(status);
2103 }
2104 
2105 /*
2106 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2107 %                                                                             %
2108 %                                                                             %
2109 %                                                                             %
2110 %   S e t I m a g e T y p e                                                   %
2111 %                                                                             %
2112 %                                                                             %
2113 %                                                                             %
2114 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2115 %
2116 %  SetImageType() sets the type of image.  Choose from these types:
2117 %
2118 %        Bilevel        Grayscale       GrayscaleMatte
2119 %        Palette        PaletteMatte    TrueColor
2120 %        TrueColorMatte ColorSeparation ColorSeparationMatte
2121 %        OptimizeType
2122 %
2123 %  The format of the SetImageType method is:
2124 %
2125 %      MagickBooleanType SetImageType(Image *image,const ImageType type,
2126 %        ExceptionInfo *exception)
2127 %
2128 %  A description of each parameter follows:
2129 %
2130 %    o image: the image.
2131 %
2132 %    o type: Image type.
2133 %
2134 %    o exception: return any errors or warnings in this structure.
2135 %
2136 */
SetImageType(Image * image,const ImageType type,ExceptionInfo * exception)2137 MagickExport MagickBooleanType SetImageType(Image *image,const ImageType type,
2138   ExceptionInfo *exception)
2139 {
2140   const char
2141     *artifact;
2142 
2143   ImageInfo
2144     *image_info;
2145 
2146   MagickBooleanType
2147     status;
2148 
2149   QuantizeInfo
2150     *quantize_info;
2151 
2152   assert(image != (Image *) NULL);
2153   if (image->debug != MagickFalse)
2154     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2155   assert(image->signature == MagickCoreSignature);
2156   status=MagickTrue;
2157   image_info=AcquireImageInfo();
2158   image_info->dither=image->dither;
2159   artifact=GetImageArtifact(image,"dither");
2160   if (artifact != (const char *) NULL)
2161     (void) SetImageOption(image_info,"dither",artifact);
2162   switch (type)
2163   {
2164     case BilevelType:
2165     {
2166       status=TransformImageColorspace(image,GRAYColorspace,exception);
2167       (void) NormalizeImage(image,exception);
2168       quantize_info=AcquireQuantizeInfo(image_info);
2169       quantize_info->number_colors=2;
2170       quantize_info->colorspace=GRAYColorspace;
2171       status=QuantizeImage(quantize_info,image,exception);
2172       quantize_info=DestroyQuantizeInfo(quantize_info);
2173       image->alpha_trait=UndefinedPixelTrait;
2174       break;
2175     }
2176     case GrayscaleType:
2177     {
2178       status=TransformImageColorspace(image,GRAYColorspace,exception);
2179       image->alpha_trait=UndefinedPixelTrait;
2180       break;
2181     }
2182     case GrayscaleAlphaType:
2183     {
2184       status=TransformImageColorspace(image,GRAYColorspace,exception);
2185       if (image->alpha_trait == UndefinedPixelTrait)
2186         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2187       break;
2188     }
2189     case PaletteType:
2190     {
2191       status=TransformImageColorspace(image,sRGBColorspace,exception);
2192       if ((image->storage_class == DirectClass) || (image->colors > 256))
2193         {
2194           quantize_info=AcquireQuantizeInfo(image_info);
2195           quantize_info->number_colors=256;
2196           status=QuantizeImage(quantize_info,image,exception);
2197           quantize_info=DestroyQuantizeInfo(quantize_info);
2198         }
2199       image->alpha_trait=UndefinedPixelTrait;
2200       break;
2201     }
2202     case PaletteBilevelAlphaType:
2203     {
2204       ChannelType
2205         channel_mask;
2206 
2207       status=TransformImageColorspace(image,sRGBColorspace,exception);
2208       if (image->alpha_trait == UndefinedPixelTrait)
2209         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2210       channel_mask=SetImageChannelMask(image,AlphaChannel);
2211       (void) BilevelImage(image,(double) QuantumRange/2.0,exception);
2212       (void) SetImageChannelMask(image,channel_mask);
2213       quantize_info=AcquireQuantizeInfo(image_info);
2214       status=QuantizeImage(quantize_info,image,exception);
2215       quantize_info=DestroyQuantizeInfo(quantize_info);
2216       break;
2217     }
2218     case PaletteAlphaType:
2219     {
2220       status=TransformImageColorspace(image,sRGBColorspace,exception);
2221       if (image->alpha_trait == UndefinedPixelTrait)
2222         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2223       quantize_info=AcquireQuantizeInfo(image_info);
2224       quantize_info->colorspace=TransparentColorspace;
2225       status=QuantizeImage(quantize_info,image,exception);
2226       quantize_info=DestroyQuantizeInfo(quantize_info);
2227       break;
2228     }
2229     case TrueColorType:
2230     {
2231       status=TransformImageColorspace(image,sRGBColorspace,exception);
2232       if (image->storage_class != DirectClass)
2233         status=SetImageStorageClass(image,DirectClass,exception);
2234       image->alpha_trait=UndefinedPixelTrait;
2235       break;
2236     }
2237     case TrueColorAlphaType:
2238     {
2239       status=TransformImageColorspace(image,sRGBColorspace,exception);
2240       if (image->storage_class != DirectClass)
2241         status=SetImageStorageClass(image,DirectClass,exception);
2242       if (image->alpha_trait == UndefinedPixelTrait)
2243         (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2244       break;
2245     }
2246     case ColorSeparationType:
2247     {
2248       status=TransformImageColorspace(image,CMYKColorspace,exception);
2249       if (image->storage_class != DirectClass)
2250         status=SetImageStorageClass(image,DirectClass,exception);
2251       image->alpha_trait=UndefinedPixelTrait;
2252       break;
2253     }
2254     case ColorSeparationAlphaType:
2255     {
2256       status=TransformImageColorspace(image,CMYKColorspace,exception);
2257       if (image->storage_class != DirectClass)
2258         status=SetImageStorageClass(image,DirectClass,exception);
2259       if (image->alpha_trait == UndefinedPixelTrait)
2260         status=SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2261       break;
2262     }
2263     case OptimizeType:
2264     case UndefinedType:
2265       break;
2266   }
2267   image_info=DestroyImageInfo(image_info);
2268   if (status == MagickFalse)
2269     return(status);
2270   image->type=type;
2271   return(MagickTrue);
2272 }
2273