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