MagickCore  7.1.0
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[4],
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=image->columns == 1 ? 1 : 0;
425  bounds.height=image->rows == 1 ? 1 : 0;
426  bounds.x=(ssize_t) image->columns;
427  bounds.y=(ssize_t) image->rows;
428  }
429  else
430  {
431  char
432  *edges,
433  *q,
434  *r;
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  r=edges;
442  while ((q=StringToken(",",&r)) != (char *) NULL)
443  {
444  if (LocaleCompare(q,"north") == 0)
445  bounds.y=(ssize_t) image->rows;
446  if (LocaleCompare(q,"east") == 0)
447  bounds.width=0;
448  if (LocaleCompare(q,"south") == 0)
449  bounds.height=0;
450  if (LocaleCompare(q,"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 
490  bounding_box;
491 
492  const Quantum
493  *magick_restrict q;
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  q=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
505  if (q == (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,q,&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  q+=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))
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 
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 
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  memset(&edge_background,0,sizeof(edge_background));
618  artifact=GetImageArtifact(image,"convex-hull:background-color");
619  if (artifact == (const char *) NULL)
620  artifact=GetImageArtifact(image,"background");
621 #if defined(MAGICKCORE_OPENMP_SUPPORT)
622  #pragma omp parallel for schedule(static)
623 #endif
624  for (i=0; i < 4; i++)
625  {
626  CacheView
627  *edge_view;
628 
630  gravity;
631 
632  Image
633  *edge_image;
634 
635  PixelInfo
636  pixel;
637 
639  edge_geometry;
640 
641  const Quantum
642  *p;
643 
644  ssize_t
645  y;
646 
647  census[i]=0.0;
648  (void) memset(&edge_geometry,0,sizeof(edge_geometry));
649  switch (i)
650  {
651  case 0:
652  default:
653  {
654  p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-1,1,1,
655  exception);
656  gravity=WestGravity;
657  edge_geometry.width=1;
658  edge_geometry.height=0;
659  break;
660  }
661  case 1:
662  {
663  p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,0,1,1,
664  exception);
665  gravity=EastGravity;
666  edge_geometry.width=1;
667  edge_geometry.height=0;
668  break;
669  }
670  case 2:
671  {
672  p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception);
673  gravity=NorthGravity;
674  edge_geometry.width=0;
675  edge_geometry.height=1;
676  break;
677  }
678  case 3:
679  {
680  p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,
681  (ssize_t) image->rows-1,1,1,exception);
682  gravity=SouthGravity;
683  edge_geometry.width=0;
684  edge_geometry.height=1;
685  break;
686  }
687  }
688  GetPixelInfoPixel(image,p,background+i);
689  if (artifact != (const char *) NULL)
690  (void) QueryColorCompliance(artifact,AllCompliance,background+i,
691  exception);
692  GravityAdjustGeometry(image->columns,image->rows,gravity,&edge_geometry);
693  edge_image=CropImage(image,&edge_geometry,exception);
694  if (edge_image == (Image *) NULL)
695  continue;
696  edge_view=AcquireVirtualCacheView(edge_image,exception);
697  for (y=0; y < (ssize_t) edge_image->rows; y++)
698  {
699  ssize_t
700  x;
701 
702  p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,
703  exception);
704  if (p == (const Quantum *) NULL)
705  break;
706  for (x=0; x < (ssize_t) edge_image->columns; x++)
707  {
708  GetPixelInfoPixel(edge_image,p,&pixel);
709  if (IsFuzzyEquivalencePixelInfo(&pixel,background+i) == MagickFalse)
710  census[i]++;
711  p+=GetPixelChannels(edge_image);
712  }
713  }
714  edge_view=DestroyCacheView(edge_view);
715  edge_image=DestroyImage(edge_image);
716  }
717  edge_census=(-1.0);
718  for (i=0; i < 4; i++)
719  if (census[i] > edge_census)
720  {
721  edge_background=background[i];
722  edge_census=census[i];
723  }
724  return(edge_background);
725 }
726 
727 void TraceConvexHull(PointInfo *vertices,size_t number_vertices,
728  PointInfo ***monotone_chain,size_t *chain_length)
729 {
730  PointInfo
731  **chain;
732 
733  ssize_t
734  i;
735 
736  size_t
737  demark,
738  n;
739 
740  /*
741  Construct the upper and lower hulls: rightmost to leftmost counterclockwise.
742  */
743  chain=(*monotone_chain);
744  n=0;
745  for (i=0; i < (ssize_t) number_vertices; i++)
746  {
747  while ((n >= 2) &&
748  (LexicographicalOrder(chain[n-2],chain[n-1],&vertices[i]) <= 0.0))
749  n--;
750  chain[n++]=(&vertices[i]);
751  }
752  demark=n+1;
753  for (i=(ssize_t) number_vertices-2; i >= 0; i--)
754  {
755  while ((n >= demark) &&
756  (LexicographicalOrder(chain[n-2],chain[n-1],&vertices[i]) <= 0.0))
757  n--;
758  chain[n++]=(&vertices[i]);
759  }
760  *chain_length=n;
761 }
762 
764  size_t *number_vertices,ExceptionInfo *exception)
765 {
766  CacheView
767  *image_view;
768 
770  status;
771 
772  MemoryInfo
773  *monotone_info,
774  *vertices_info;
775 
776  PixelInfo
777  background;
778 
779  PointInfo
780  *convex_hull,
781  **monotone_chain,
782  *vertices;
783 
784  size_t
785  n;
786 
787  ssize_t
788  y;
789 
790  /*
791  Identify convex hull vertices of image foreground object(s).
792  */
793  assert(image != (Image *) NULL);
794  assert(image->signature == MagickCoreSignature);
795  if (image->debug != MagickFalse)
796  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
797  *number_vertices=0;
798  vertices_info=AcquireVirtualMemory(image->columns,image->rows*
799  sizeof(*vertices));
800  monotone_info=AcquireVirtualMemory(2*image->columns,2*
801  image->rows*sizeof(*monotone_chain));
802  if ((vertices_info == (MemoryInfo *) NULL) ||
803  (monotone_info == (MemoryInfo *) NULL))
804  {
805  if (monotone_info != (MemoryInfo *) NULL)
806  monotone_info=(MemoryInfo *) RelinquishVirtualMemory(monotone_info);
807  if (vertices_info != (MemoryInfo *) NULL)
808  vertices_info=RelinquishVirtualMemory(vertices_info);
809  return((PointInfo *) NULL);
810  }
811  vertices=(PointInfo *) GetVirtualMemoryBlob(vertices_info);
812  monotone_chain=(PointInfo **) GetVirtualMemoryBlob(monotone_info);
813  image_view=AcquireVirtualCacheView(image,exception);
814  background=GetEdgeBackgroundColor(image,image_view,exception);
815  status=MagickTrue;
816  n=0;
817  for (y=0; y < (ssize_t) image->rows; y++)
818  {
819  const Quantum
820  *p;
821 
822  ssize_t
823  x;
824 
825  if (status == MagickFalse)
826  continue;
827  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
828  if (p == (const Quantum *) NULL)
829  {
830  status=MagickFalse;
831  continue;
832  }
833  for (x=0; x < (ssize_t) image->columns; x++)
834  {
835  PixelInfo
836  pixel;
837 
838  GetPixelInfoPixel(image,p,&pixel);
839  if (IsFuzzyEquivalencePixelInfo(&pixel,&background) == MagickFalse)
840  {
841  vertices[n].x=(double) x;
842  vertices[n].y=(double) y;
843  n++;
844  }
845  p+=GetPixelChannels(image);
846  }
847  }
848  image_view=DestroyCacheView(image_view);
849  /*
850  Return the convex hull of the image foreground object(s).
851  */
852  TraceConvexHull(vertices,n,&monotone_chain,number_vertices);
853  convex_hull=(PointInfo *) AcquireQuantumMemory(*number_vertices,
854  sizeof(*convex_hull));
855  if (convex_hull != (PointInfo *) NULL)
856  for (n=0; n < *number_vertices; n++)
857  convex_hull[n]=(*monotone_chain[n]);
858  monotone_info=RelinquishVirtualMemory(monotone_info);
859  vertices_info=RelinquishVirtualMemory(vertices_info);
860  return(convex_hull);
861 }
862 
863 /*
864 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
865 % %
866 % %
867 % %
868 % G e t I m a g e D e p t h %
869 % %
870 % %
871 % %
872 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
873 %
874 % GetImageDepth() returns the depth of a particular image channel.
875 %
876 % The format of the GetImageDepth method is:
877 %
878 % size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
879 %
880 % A description of each parameter follows:
881 %
882 % o image: the image.
883 %
884 % o exception: return any errors or warnings in this structure.
885 %
886 */
887 MagickExport size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
888 {
889  CacheView
890  *image_view;
891 
893  status;
894 
895  ssize_t
896  i;
897 
898  size_t
899  *current_depth,
900  depth,
901  number_threads;
902 
903  ssize_t
904  y;
905 
906  /*
907  Compute image depth.
908  */
909  assert(image != (Image *) NULL);
910  assert(image->signature == MagickCoreSignature);
911  if (image->debug != MagickFalse)
912  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
913  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
914  current_depth=(size_t *) AcquireQuantumMemory(number_threads,
915  sizeof(*current_depth));
916  if (current_depth == (size_t *) NULL)
917  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
918  status=MagickTrue;
919  for (i=0; i < (ssize_t) number_threads; i++)
920  current_depth[i]=1;
921  if ((image->storage_class == PseudoClass) &&
922  (image->alpha_trait == UndefinedPixelTrait))
923  {
924  for (i=0; i < (ssize_t) image->colors; i++)
925  {
926  const int
927  id = GetOpenMPThreadId();
928 
929  while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
930  {
932  atDepth;
933 
934  QuantumAny
935  range;
936 
937  atDepth=MagickTrue;
938  range=GetQuantumRange(current_depth[id]);
939  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
940  if (IsPixelAtDepth(ClampToQuantum(image->colormap[i].red),range) == MagickFalse)
941  atDepth=MagickFalse;
942  if ((atDepth != MagickFalse) &&
943  (GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
944  if (IsPixelAtDepth(ClampToQuantum(image->colormap[i].green),range) == MagickFalse)
945  atDepth=MagickFalse;
946  if ((atDepth != MagickFalse) &&
947  (GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
948  if (IsPixelAtDepth(ClampToQuantum(image->colormap[i].blue),range) == MagickFalse)
949  atDepth=MagickFalse;
950  if ((atDepth != MagickFalse))
951  break;
952  current_depth[id]++;
953  }
954  }
955  depth=current_depth[0];
956  for (i=1; i < (ssize_t) number_threads; i++)
957  if (depth < current_depth[i])
958  depth=current_depth[i];
959  current_depth=(size_t *) RelinquishMagickMemory(current_depth);
960  return(depth);
961  }
962  image_view=AcquireVirtualCacheView(image,exception);
963 #if !defined(MAGICKCORE_HDRI_SUPPORT)
964  DisableMSCWarning(4127)
965  if ((1UL*QuantumRange) <= MaxMap)
967  {
968  size_t
969  *depth_map;
970 
971  /*
972  Scale pixels to desired (optimized with depth map).
973  */
974  depth_map=(size_t *) AcquireQuantumMemory(MaxMap+1,sizeof(*depth_map));
975  if (depth_map == (size_t *) NULL)
976  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
977  for (i=0; i <= (ssize_t) MaxMap; i++)
978  {
979  for (depth=1; depth < (size_t) 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  j;
1018 
1019  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1020  {
1021  PixelChannel channel = GetPixelChannelChannel(image,j);
1022  PixelTrait traits = GetPixelChannelTraits(image,channel);
1023  if ((traits & UpdatePixelTrait) == 0)
1024  continue;
1025  if (depth_map[ScaleQuantumToMap(p[j])] > current_depth[id])
1026  current_depth[id]=depth_map[ScaleQuantumToMap(p[j])];
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  j;
1070 
1071  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
1072  {
1073  PixelChannel
1074  channel;
1075 
1076  PixelTrait
1077  traits;
1078 
1079  channel=GetPixelChannelChannel(image,j);
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[j] == ScaleAnyToQuantum(ScaleQuantumToAny(p[j],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
1144  width,
1145  height,
1146  projection;
1147 
1148  ssize_t
1149  p,
1150  q,
1151  v;
1152 } CaliperInfo;
1153 
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 
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 
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 
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 
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  diameter=fabs(getFeretDiameter(&vertices[i],
1269  &vertices[(i+1) % number_hull_vertices],&vertices[j]));
1270  if (min_diameter < diameter)
1271  {
1272  min_diameter=diameter;
1273  p=i;
1274  q=(i+1) % number_hull_vertices;
1275  v=j;
1276  }
1277  }
1278  for (k=0; k < (ssize_t) number_hull_vertices; k++)
1279  {
1280  double
1281  projection;
1282 
1283  /*
1284  Rotating calipers.
1285  */
1286  projection=getProjection(&vertices[p],&vertices[q],&vertices[k]);
1287  min_projection=MagickMin(min_projection,projection);
1288  max_projection=MagickMax(max_projection,projection);
1289  }
1290  area=min_diameter*(max_projection-min_projection);
1291  if (caliper_info.area > area)
1292  {
1293  caliper_info.area=area;
1294  caliper_info.width=min_diameter;
1295  caliper_info.height=max_projection-min_projection;
1296  caliper_info.projection=max_projection;
1297  caliper_info.p=p;
1298  caliper_info.q=q;
1299  caliper_info.v=v;
1300  }
1301  }
1302  /*
1303  Initialize minimum bounding box.
1304  */
1305  diameter=getFeretDiameter(&vertices[caliper_info.p],
1306  &vertices[caliper_info.q],&vertices[caliper_info.v]);
1307  angle=atan2(vertices[caliper_info.q].y-vertices[caliper_info.p].y,
1308  vertices[caliper_info.q].x-vertices[caliper_info.p].x);
1309  bounding_box[0].x=vertices[caliper_info.p].x+cos(angle)*
1310  caliper_info.projection;
1311  bounding_box[0].y=vertices[caliper_info.p].y+sin(angle)*
1312  caliper_info.projection;
1313  bounding_box[1].x=floor(bounding_box[0].x+cos(angle+MagickPI/2.0)*diameter+
1314  0.5);
1315  bounding_box[1].y=floor(bounding_box[0].y+sin(angle+MagickPI/2.0)*diameter+
1316  0.5);
1317  bounding_box[2].x=floor(bounding_box[1].x+cos(angle)*(-caliper_info.height)+
1318  0.5);
1319  bounding_box[2].y=floor(bounding_box[1].y+sin(angle)*(-caliper_info.height)+
1320  0.5);
1321  bounding_box[3].x=floor(bounding_box[2].x+cos(angle+MagickPI/2.0)*(-diameter)+
1322  0.5);
1323  bounding_box[3].y=floor(bounding_box[2].y+sin(angle+MagickPI/2.0)*(-diameter)+
1324  0.5);
1325  /*
1326  Export minimum bounding box properties.
1327  */
1328  (void) FormatImageProperty(image,"minimum-bounding-box:area","%.*g",
1329  GetMagickPrecision(),caliper_info.area);
1330  (void) FormatImageProperty(image,"minimum-bounding-box:width","%.*g",
1331  GetMagickPrecision(),caliper_info.width);
1332  (void) FormatImageProperty(image,"minimum-bounding-box:height","%.*g",
1333  GetMagickPrecision(),caliper_info.height);
1334  (void) FormatImageProperty(image,"minimum-bounding-box:_p","%.*g,%.*g",
1335  GetMagickPrecision(),vertices[caliper_info.p].x,
1336  GetMagickPrecision(),vertices[caliper_info.p].y);
1337  (void) FormatImageProperty(image,"minimum-bounding-box:_q","%.*g,%.*g",
1338  GetMagickPrecision(),vertices[caliper_info.q].x,
1339  GetMagickPrecision(),vertices[caliper_info.q].y);
1340  (void) FormatImageProperty(image,"minimum-bounding-box:_v","%.*g,%.*g",
1341  GetMagickPrecision(),vertices[caliper_info.v].x,
1342  GetMagickPrecision(),vertices[caliper_info.v].y);
1343  /*
1344  Find smallest angle to origin.
1345  */
1346  distance=hypot(bounding_box[0].x,bounding_box[0].y);
1347  angle=getAngle(&bounding_box[0],&bounding_box[1]);
1348  for (i=1; i < 4; i++)
1349  {
1350  double d = hypot(bounding_box[i].x,bounding_box[i].y);
1351  if (d < distance)
1352  {
1353  distance=d;
1354  angle=getAngle(&bounding_box[i],&bounding_box[(i+1) % 4]);
1355  }
1356  }
1357  artifact=GetImageArtifact(image,"minimum-bounding-box:orientation");
1358  if (artifact != (const char *) NULL)
1359  {
1360  double
1361  length,
1362  q_length,
1363  p_length;
1364 
1365  PointInfo
1366  delta,
1367  point;
1368 
1369  /*
1370  Find smallest perpendicular distance from edge to origin.
1371  */
1372  point=bounding_box[0];
1373  for (i=1; i < 4; i++)
1374  {
1375  if (bounding_box[i].x < point.x)
1376  point.x=bounding_box[i].x;
1377  if (bounding_box[i].y < point.y)
1378  point.y=bounding_box[i].y;
1379  }
1380  for (i=0; i < 4; i++)
1381  {
1382  bounding_box[i].x-=point.x;
1383  bounding_box[i].y-=point.y;
1384  }
1385  for (i=0; i < 4; i++)
1386  {
1387  double
1388  d,
1389  intercept,
1390  slope;
1391 
1392  delta.x=bounding_box[(i+1) % 4].x-bounding_box[i].x;
1393  delta.y=bounding_box[(i+1) % 4].y-bounding_box[i].y;
1394  slope=delta.y*PerceptibleReciprocal(delta.x);
1395  intercept=bounding_box[(i+1) % 4].y-slope*bounding_box[i].x;
1396  d=fabs((slope*bounding_box[i].x-bounding_box[i].y+intercept)*
1397  PerceptibleReciprocal(sqrt(slope*slope+1.0)));
1398  if ((i == 0) || (d < distance))
1399  {
1400  distance=d;
1401  point=delta;
1402  }
1403  }
1404  angle=RadiansToDegrees(atan(point.y*PerceptibleReciprocal(point.x)));
1405  length=hypot(point.x,point.y);
1406  p_length=fabs((double) MagickMax(caliper_info.width,caliper_info.height)-
1407  length);
1408  q_length=fabs(length-(double) MagickMin(caliper_info.width,
1409  caliper_info.height));
1410  if (LocaleCompare(artifact,"landscape") == 0)
1411  {
1412  if (p_length > q_length)
1413  angle+=(angle < 0.0) ? 90.0 : -90.0;
1414  }
1415  else
1416  if (LocaleCompare(artifact,"portrait") == 0)
1417  {
1418  if (p_length < q_length)
1419  angle+=(angle >= 0.0) ? 90.0 : -90.0;
1420  }
1421  }
1422  (void) FormatImageProperty(image,"minimum-bounding-box:angle","%.*g",
1423  GetMagickPrecision(),angle);
1424  (void) FormatImageProperty(image,"minimum-bounding-box:unrotate","%.*g",
1425  GetMagickPrecision(),-angle);
1426  vertices=(PointInfo *) RelinquishMagickMemory(vertices);
1427  return(bounding_box);
1428 }
1429 
1430 /*
1431 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1432 % %
1433 % %
1434 % %
1435 % G e t I m a g e Q u a n t u m D e p t h %
1436 % %
1437 % %
1438 % %
1439 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1440 %
1441 % GetImageQuantumDepth() returns the depth of the image rounded to a legal
1442 % quantum depth: 8, 16, or 32.
1443 %
1444 % The format of the GetImageQuantumDepth method is:
1445 %
1446 % size_t GetImageQuantumDepth(const Image *image,
1447 % const MagickBooleanType constrain)
1448 %
1449 % A description of each parameter follows:
1450 %
1451 % o image: the image.
1452 %
1453 % o constrain: A value other than MagickFalse, constrains the depth to
1454 % a maximum of MAGICKCORE_QUANTUM_DEPTH.
1455 %
1456 */
1458  const MagickBooleanType constrain)
1459 {
1460  size_t
1461  depth;
1462 
1463  depth=image->depth;
1464  if (depth <= 8)
1465  depth=8;
1466  else
1467  if (depth <= 16)
1468  depth=16;
1469  else
1470  if (depth <= 32)
1471  depth=32;
1472  else
1473  if (depth <= 64)
1474  depth=64;
1475  if (constrain != MagickFalse)
1476  depth=(size_t) MagickMin((double) depth,(double) MAGICKCORE_QUANTUM_DEPTH);
1477  return(depth);
1478 }
1479 
1480 /*
1481 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1482 % %
1483 % %
1484 % %
1485 % G e t I m a g e T y p e %
1486 % %
1487 % %
1488 % %
1489 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1490 %
1491 % GetImageType() returns the type of image:
1492 %
1493 % Bilevel Grayscale GrayscaleMatte
1494 % Palette PaletteMatte TrueColor
1495 % TrueColorMatte ColorSeparation ColorSeparationMatte
1496 %
1497 % The format of the GetImageType method is:
1498 %
1499 % ImageType GetImageType(const Image *image)
1500 %
1501 % A description of each parameter follows:
1502 %
1503 % o image: the image.
1504 %
1505 */
1507 {
1508  assert(image != (Image *) NULL);
1509  assert(image->signature == MagickCoreSignature);
1510  if (image->colorspace == CMYKColorspace)
1511  {
1512  if (image->alpha_trait == UndefinedPixelTrait)
1513  return(ColorSeparationType);
1514  return(ColorSeparationAlphaType);
1515  }
1516  if (IsImageMonochrome(image) != MagickFalse)
1517  return(BilevelType);
1518  if (IsImageGray(image) != MagickFalse)
1519  {
1520  if (image->alpha_trait != UndefinedPixelTrait)
1521  return(GrayscaleAlphaType);
1522  return(GrayscaleType);
1523  }
1524  if (IsPaletteImage(image) != MagickFalse)
1525  {
1526  if (image->alpha_trait != UndefinedPixelTrait)
1527  return(PaletteAlphaType);
1528  return(PaletteType);
1529  }
1530  if (image->alpha_trait != UndefinedPixelTrait)
1531  return(TrueColorAlphaType);
1532  return(TrueColorType);
1533 }
1534 
1535 /*
1536 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1537 % %
1538 % %
1539 % %
1540 % I d e n t i f y I m a g e G r a y %
1541 % %
1542 % %
1543 % %
1544 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1545 %
1546 % IdentifyImageGray() returns grayscale if all the pixels in the image have
1547 % the same red, green, and blue intensities, and bi-level is the intensity is
1548 % either 0 or QuantumRange. Otherwise undefined is returned.
1549 %
1550 % The format of the IdentifyImageGray method is:
1551 %
1552 % ImageType IdentifyImageGray(const Image *image,ExceptionInfo *exception)
1553 %
1554 % A description of each parameter follows:
1555 %
1556 % o image: the image.
1557 %
1558 % o exception: return any errors or warnings in this structure.
1559 %
1560 */
1562  ExceptionInfo *exception)
1563 {
1564  CacheView
1565  *image_view;
1566 
1567  ImageType
1568  type;
1569 
1570  const Quantum
1571  *p;
1572 
1573  ssize_t
1574  x;
1575 
1576  ssize_t
1577  y;
1578 
1579  assert(image != (Image *) NULL);
1580  assert(image->signature == MagickCoreSignature);
1581  if (image->debug != MagickFalse)
1582  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1583  if ((image->type == BilevelType) || (image->type == GrayscaleType) ||
1584  (image->type == GrayscaleAlphaType))
1585  return(image->type);
1587  return(UndefinedType);
1588  type=BilevelType;
1589  image_view=AcquireVirtualCacheView(image,exception);
1590  for (y=0; y < (ssize_t) image->rows; y++)
1591  {
1592  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1593  if (p == (const Quantum *) NULL)
1594  break;
1595  for (x=0; x < (ssize_t) image->columns; x++)
1596  {
1597  if (IsPixelGray(image,p) == MagickFalse)
1598  {
1599  type=UndefinedType;
1600  break;
1601  }
1602  if ((type == BilevelType) &&
1603  (IsPixelMonochrome(image,p) == MagickFalse))
1604  type=GrayscaleType;
1605  p+=GetPixelChannels(image);
1606  }
1607  if (type == UndefinedType)
1608  break;
1609  }
1610  image_view=DestroyCacheView(image_view);
1611  if ((type == GrayscaleType) && (image->alpha_trait != UndefinedPixelTrait))
1612  type=GrayscaleAlphaType;
1613  return(type);
1614 }
1615 
1616 /*
1617 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1618 % %
1619 % %
1620 % %
1621 % I d e n t i f y I m a g e M o n o c h r o m e %
1622 % %
1623 % %
1624 % %
1625 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1626 %
1627 % IdentifyImageMonochrome() returns MagickTrue if all the pixels in the image
1628 % have the same red, green, and blue intensities and the intensity is either
1629 % 0 or QuantumRange.
1630 %
1631 % The format of the IdentifyImageMonochrome method is:
1632 %
1633 % MagickBooleanType IdentifyImageMonochrome(const Image *image,
1634 % ExceptionInfo *exception)
1635 %
1636 % A description of each parameter follows:
1637 %
1638 % o image: the image.
1639 %
1640 % o exception: return any errors or warnings in this structure.
1641 %
1642 */
1644  ExceptionInfo *exception)
1645 {
1646  CacheView
1647  *image_view;
1648 
1650  bilevel;
1651 
1652  ssize_t
1653  x;
1654 
1655  const Quantum
1656  *p;
1657 
1658  ssize_t
1659  y;
1660 
1661  assert(image != (Image *) NULL);
1662  assert(image->signature == MagickCoreSignature);
1663  if (image->debug != MagickFalse)
1664  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1665  if (image->type == BilevelType)
1666  return(MagickTrue);
1668  return(MagickFalse);
1669  bilevel=MagickTrue;
1670  image_view=AcquireVirtualCacheView(image,exception);
1671  for (y=0; y < (ssize_t) image->rows; y++)
1672  {
1673  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1674  if (p == (const Quantum *) NULL)
1675  break;
1676  for (x=0; x < (ssize_t) image->columns; x++)
1677  {
1678  if (IsPixelMonochrome(image,p) == MagickFalse)
1679  {
1680  bilevel=MagickFalse;
1681  break;
1682  }
1683  p+=GetPixelChannels(image);
1684  }
1685  if (bilevel == MagickFalse)
1686  break;
1687  }
1688  image_view=DestroyCacheView(image_view);
1689  return(bilevel);
1690 }
1691 
1692 /*
1693 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1694 % %
1695 % %
1696 % %
1697 % I d e n t i f y I m a g e T y p e %
1698 % %
1699 % %
1700 % %
1701 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1702 %
1703 % IdentifyImageType() returns the potential type of image:
1704 %
1705 % Bilevel Grayscale GrayscaleMatte
1706 % Palette PaletteMatte TrueColor
1707 % TrueColorMatte ColorSeparation ColorSeparationMatte
1708 %
1709 % To ensure the image type matches its potential, use SetImageType():
1710 %
1711 % (void) SetImageType(image,IdentifyImageType(image,exception),exception);
1712 %
1713 % The format of the IdentifyImageType method is:
1714 %
1715 % ImageType IdentifyImageType(const Image *image,ExceptionInfo *exception)
1716 %
1717 % A description of each parameter follows:
1718 %
1719 % o image: the image.
1720 %
1721 % o exception: return any errors or warnings in this structure.
1722 %
1723 */
1725  ExceptionInfo *exception)
1726 {
1727  ImageType
1728  type;
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  type=IdentifyImageGray(image,exception);
1741  if ((type == BilevelType) || (type == GrayscaleType) ||
1742  (type == GrayscaleAlphaType))
1743  return(type);
1744  if (IdentifyPaletteImage(image,exception) != MagickFalse)
1745  {
1746  if (image->alpha_trait != UndefinedPixelTrait)
1747  return(PaletteAlphaType);
1748  return(PaletteType);
1749  }
1750  if (image->alpha_trait != UndefinedPixelTrait)
1751  return(TrueColorAlphaType);
1752  return(TrueColorType);
1753 }
1754 
1755 /*
1756 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1757 % %
1758 % %
1759 % %
1760 % I s I m a g e G r a y %
1761 % %
1762 % %
1763 % %
1764 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1765 %
1766 % IsImageGray() returns MagickTrue if the type of the image is grayscale or
1767 % bi-level.
1768 %
1769 % The format of the IsImageGray method is:
1770 %
1771 % MagickBooleanType IsImageGray(const Image *image)
1772 %
1773 % A description of each parameter follows:
1774 %
1775 % o image: the image.
1776 %
1777 */
1779 {
1780  assert(image != (Image *) NULL);
1781  assert(image->signature == MagickCoreSignature);
1782  if ((image->type == BilevelType) || (image->type == GrayscaleType) ||
1783  (image->type == GrayscaleAlphaType))
1784  return(MagickTrue);
1785  return(MagickFalse);
1786 }
1787 
1788 /*
1789 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1790 % %
1791 % %
1792 % %
1793 % I s I m a g e M o n o c h r o m e %
1794 % %
1795 % %
1796 % %
1797 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1798 %
1799 % IsImageMonochrome() returns MagickTrue if type of the image is bi-level.
1800 %
1801 % The format of the IsImageMonochrome method is:
1802 %
1803 % MagickBooleanType IsImageMonochrome(const Image *image)
1804 %
1805 % A description of each parameter follows:
1806 %
1807 % o image: the image.
1808 %
1809 */
1811 {
1812  assert(image != (Image *) NULL);
1813  assert(image->signature == MagickCoreSignature);
1814  if (image->type == BilevelType)
1815  return(MagickTrue);
1816  return(MagickFalse);
1817 }
1818 
1819 /*
1820 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1821 % %
1822 % %
1823 % %
1824 % I s I m a g e O p a q u e %
1825 % %
1826 % %
1827 % %
1828 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1829 %
1830 % IsImageOpaque() returns MagickTrue if none of the pixels in the image have
1831 % an alpha value other than OpaqueAlpha (QuantumRange).
1832 %
1833 % Will return true immediatally is alpha channel is not available.
1834 %
1835 % The format of the IsImageOpaque method is:
1836 %
1837 % MagickBooleanType IsImageOpaque(const Image *image,
1838 % ExceptionInfo *exception)
1839 %
1840 % A description of each parameter follows:
1841 %
1842 % o image: the image.
1843 %
1844 % o exception: return any errors or warnings in this structure.
1845 %
1846 */
1848  ExceptionInfo *exception)
1849 {
1850  CacheView
1851  *image_view;
1852 
1853  const Quantum
1854  *p;
1855 
1856  ssize_t
1857  x;
1858 
1859  ssize_t
1860  y;
1861 
1862  /*
1863  Determine if image is opaque.
1864  */
1865  assert(image != (Image *) NULL);
1866  assert(image->signature == MagickCoreSignature);
1867  if (image->debug != MagickFalse)
1868  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1869  if (image->alpha_trait == UndefinedPixelTrait)
1870  return(MagickTrue);
1871  image_view=AcquireVirtualCacheView(image,exception);
1872  for (y=0; y < (ssize_t) image->rows; y++)
1873  {
1874  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1875  if (p == (const Quantum *) NULL)
1876  break;
1877  for (x=0; x < (ssize_t) image->columns; x++)
1878  {
1879  if (GetPixelAlpha(image,p) != OpaqueAlpha)
1880  break;
1881  p+=GetPixelChannels(image);
1882  }
1883  if (x < (ssize_t) image->columns)
1884  break;
1885  }
1886  image_view=DestroyCacheView(image_view);
1887  return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
1888 }
1889 
1890 /*
1891 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1892 % %
1893 % %
1894 % %
1895 % S e t I m a g e D e p t h %
1896 % %
1897 % %
1898 % %
1899 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1900 %
1901 % SetImageDepth() sets the depth of the image.
1902 %
1903 % The format of the SetImageDepth method is:
1904 %
1905 % MagickBooleanType SetImageDepth(Image *image,const size_t depth,
1906 % ExceptionInfo *exception)
1907 %
1908 % A description of each parameter follows:
1909 %
1910 % o image: the image.
1911 %
1912 % o channel: the channel.
1913 %
1914 % o depth: the image depth.
1915 %
1916 % o exception: return any errors or warnings in this structure.
1917 %
1918 */
1920  const size_t depth,ExceptionInfo *exception)
1921 {
1922  CacheView
1923  *image_view;
1924 
1926  status;
1927 
1928  QuantumAny
1929  range;
1930 
1931  ssize_t
1932  y;
1933 
1934  assert(image != (Image *) NULL);
1935  if (image->debug != MagickFalse)
1936  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1937  assert(image->signature == MagickCoreSignature);
1938  if (depth >= MAGICKCORE_QUANTUM_DEPTH)
1939  {
1940  image->depth=depth;
1941  return(MagickTrue);
1942  }
1943  range=GetQuantumRange(depth);
1944  if (image->storage_class == PseudoClass)
1945  {
1946  ssize_t
1947  i;
1948 
1949 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1950  #pragma omp parallel for schedule(static) shared(status) \
1951  magick_number_threads(image,image,image->colors,1)
1952 #endif
1953  for (i=0; i < (ssize_t) image->colors; i++)
1954  {
1955  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1956  image->colormap[i].red=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
1957  ClampPixel(image->colormap[i].red),range),range);
1958  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1960  ClampPixel(image->colormap[i].green),range),range);
1961  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1962  image->colormap[i].blue=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
1963  ClampPixel(image->colormap[i].blue),range),range);
1964  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1966  ClampPixel(image->colormap[i].alpha),range),range);
1967  }
1968  }
1969  status=MagickTrue;
1970  image_view=AcquireAuthenticCacheView(image,exception);
1971 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1972  DisableMSCWarning(4127)
1973  if ((1UL*QuantumRange) <= MaxMap)
1975  {
1976  Quantum
1977  *depth_map;
1978 
1979  ssize_t
1980  i;
1981 
1982  /*
1983  Scale pixels to desired (optimized with depth map).
1984  */
1985  depth_map=(Quantum *) AcquireQuantumMemory(MaxMap+1,sizeof(*depth_map));
1986  if (depth_map == (Quantum *) NULL)
1987  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1988  for (i=0; i <= (ssize_t) MaxMap; i++)
1989  depth_map[i]=ScaleAnyToQuantum(ScaleQuantumToAny((Quantum) i,range),
1990  range);
1991 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1992  #pragma omp parallel for schedule(static) shared(status) \
1993  magick_number_threads(image,image,image->rows,1)
1994 #endif
1995  for (y=0; y < (ssize_t) image->rows; y++)
1996  {
1997  ssize_t
1998  x;
1999 
2000  Quantum
2001  *magick_restrict q;
2002 
2003  if (status == MagickFalse)
2004  continue;
2005  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2006  exception);
2007  if (q == (Quantum *) NULL)
2008  {
2009  status=MagickFalse;
2010  continue;
2011  }
2012  for (x=0; x < (ssize_t) image->columns; x++)
2013  {
2014  ssize_t
2015  j;
2016 
2017  for (j=0; j < (ssize_t) GetPixelChannels(image); j++)
2018  {
2019  PixelChannel
2020  channel;
2021 
2022  PixelTrait
2023  traits;
2024 
2025  channel=GetPixelChannelChannel(image,j);
2026  traits=GetPixelChannelTraits(image,channel);
2027  if ((traits & UpdatePixelTrait) == 0)
2028  continue;
2029  q[j]=depth_map[ScaleQuantumToMap(q[j])];
2030  }
2031  q+=GetPixelChannels(image);
2032  }
2033  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2034  {
2035  status=MagickFalse;
2036  continue;
2037  }
2038  }
2039  image_view=DestroyCacheView(image_view);
2040  depth_map=(Quantum *) RelinquishMagickMemory(depth_map);
2041  if (status != MagickFalse)
2042  image->depth=depth;
2043  return(status);
2044  }
2045 #endif
2046  /*
2047  Scale pixels to desired depth.
2048  */
2049 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2050  #pragma omp parallel for schedule(static) shared(status) \
2051  magick_number_threads(image,image,image->rows,1)
2052 #endif
2053  for (y=0; y < (ssize_t) image->rows; y++)
2054  {
2055  ssize_t
2056  x;
2057 
2058  Quantum
2059  *magick_restrict q;
2060 
2061  if (status == MagickFalse)
2062  continue;
2063  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2064  if (q == (Quantum *) NULL)
2065  {
2066  status=MagickFalse;
2067  continue;
2068  }
2069  for (x=0; x < (ssize_t) image->columns; x++)
2070  {
2071  ssize_t
2072  i;
2073 
2074  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2075  {
2076  PixelChannel
2077  channel;
2078 
2079  PixelTrait
2080  traits;
2081 
2082  channel=GetPixelChannelChannel(image,i);
2083  traits=GetPixelChannelTraits(image,channel);
2084  if ((traits & UpdatePixelTrait) == 0)
2085  continue;
2087  q[i]),range),range);
2088  }
2089  q+=GetPixelChannels(image);
2090  }
2091  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2092  {
2093  status=MagickFalse;
2094  continue;
2095  }
2096  }
2097  image_view=DestroyCacheView(image_view);
2098  if (status != MagickFalse)
2099  image->depth=depth;
2100  return(status);
2101 }
2102 
2103 /*
2104 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2105 % %
2106 % %
2107 % %
2108 % S e t I m a g e T y p e %
2109 % %
2110 % %
2111 % %
2112 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2113 %
2114 % SetImageType() sets the type of image. Choose from these types:
2115 %
2116 % Bilevel Grayscale GrayscaleMatte
2117 % Palette PaletteMatte TrueColor
2118 % TrueColorMatte ColorSeparation ColorSeparationMatte
2119 % OptimizeType
2120 %
2121 % The format of the SetImageType method is:
2122 %
2123 % MagickBooleanType SetImageType(Image *image,const ImageType type,
2124 % ExceptionInfo *exception)
2125 %
2126 % A description of each parameter follows:
2127 %
2128 % o image: the image.
2129 %
2130 % o type: Image type.
2131 %
2132 % o exception: return any errors or warnings in this structure.
2133 %
2134 */
2136  ExceptionInfo *exception)
2137 {
2138  const char
2139  *artifact;
2140 
2141  ImageInfo
2142  *image_info;
2143 
2145  status;
2146 
2147  QuantizeInfo
2148  *quantize_info;
2149 
2150  assert(image != (Image *) NULL);
2151  if (image->debug != MagickFalse)
2152  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2153  assert(image->signature == MagickCoreSignature);
2154  status=MagickTrue;
2155  image_info=AcquireImageInfo();
2156  image_info->dither=image->dither;
2157  artifact=GetImageArtifact(image,"dither");
2158  if (artifact != (const char *) NULL)
2159  (void) SetImageOption(image_info,"dither",artifact);
2160  switch (type)
2161  {
2162  case BilevelType:
2163  {
2164  status=TransformImageColorspace(image,GRAYColorspace,exception);
2165  (void) NormalizeImage(image,exception);
2166  quantize_info=AcquireQuantizeInfo(image_info);
2167  quantize_info->number_colors=2;
2168  quantize_info->colorspace=GRAYColorspace;
2169  status=QuantizeImage(quantize_info,image,exception);
2170  quantize_info=DestroyQuantizeInfo(quantize_info);
2172  break;
2173  }
2174  case GrayscaleType:
2175  {
2176  status=TransformImageColorspace(image,GRAYColorspace,exception);
2178  break;
2179  }
2180  case GrayscaleAlphaType:
2181  {
2182  status=TransformImageColorspace(image,GRAYColorspace,exception);
2183  if (image->alpha_trait == UndefinedPixelTrait)
2184  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2185  break;
2186  }
2187  case PaletteType:
2188  {
2189  status=TransformImageColorspace(image,sRGBColorspace,exception);
2190  if ((image->storage_class == DirectClass) || (image->colors > 256))
2191  {
2192  quantize_info=AcquireQuantizeInfo(image_info);
2193  quantize_info->number_colors=256;
2194  status=QuantizeImage(quantize_info,image,exception);
2195  quantize_info=DestroyQuantizeInfo(quantize_info);
2196  }
2198  break;
2199  }
2201  {
2202  ChannelType
2203  channel_mask;
2204 
2205  status=TransformImageColorspace(image,sRGBColorspace,exception);
2206  if (image->alpha_trait == UndefinedPixelTrait)
2207  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2208  channel_mask=SetImageChannelMask(image,AlphaChannel);
2209  (void) BilevelImage(image,(double) QuantumRange/2.0,exception);
2210  (void) SetImageChannelMask(image,channel_mask);
2211  quantize_info=AcquireQuantizeInfo(image_info);
2212  status=QuantizeImage(quantize_info,image,exception);
2213  quantize_info=DestroyQuantizeInfo(quantize_info);
2214  break;
2215  }
2216  case PaletteAlphaType:
2217  {
2218  status=TransformImageColorspace(image,sRGBColorspace,exception);
2219  if (image->alpha_trait == UndefinedPixelTrait)
2220  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2221  quantize_info=AcquireQuantizeInfo(image_info);
2222  quantize_info->colorspace=TransparentColorspace;
2223  status=QuantizeImage(quantize_info,image,exception);
2224  quantize_info=DestroyQuantizeInfo(quantize_info);
2225  break;
2226  }
2227  case TrueColorType:
2228  {
2229  status=TransformImageColorspace(image,sRGBColorspace,exception);
2230  if (image->storage_class != DirectClass)
2231  status=SetImageStorageClass(image,DirectClass,exception);
2233  break;
2234  }
2235  case TrueColorAlphaType:
2236  {
2237  status=TransformImageColorspace(image,sRGBColorspace,exception);
2238  if (image->storage_class != DirectClass)
2239  status=SetImageStorageClass(image,DirectClass,exception);
2240  if (image->alpha_trait == UndefinedPixelTrait)
2241  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2242  break;
2243  }
2244  case ColorSeparationType:
2245  {
2246  status=TransformImageColorspace(image,CMYKColorspace,exception);
2247  if (image->storage_class != DirectClass)
2248  status=SetImageStorageClass(image,DirectClass,exception);
2250  break;
2251  }
2253  {
2254  status=TransformImageColorspace(image,CMYKColorspace,exception);
2255  if (image->storage_class != DirectClass)
2256  status=SetImageStorageClass(image,DirectClass,exception);
2257  if (image->alpha_trait == UndefinedPixelTrait)
2258  status=SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2259  break;
2260  }
2261  case OptimizeType:
2262  case UndefinedType:
2263  break;
2264  }
2265  image_info=DestroyImageInfo(image_info);
2266  if (status == MagickFalse)
2267  return(status);
2268  image->type=type;
2269  return(MagickTrue);
2270 }
size_t rows
Definition: image.h:172
#define magick_restrict
Definition: MagickCore.h:41
MagickDoubleType MagickRealType
Definition: magick-type.h:124
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:1609
#define DisableMSCWarning(nr)
Definition: studio.h:358
static PixelTrait GetPixelAlphaTraits(const Image *magick_restrict image)
ColorspaceType colorspace
Definition: quantize.h:44
static double getAngle(PointInfo *p, PointInfo *q)
Definition: attribute.c:1154
MagickExport MagickBooleanType SetImageDepth(Image *image, const size_t depth, ExceptionInfo *exception)
Definition: attribute.c:1919
#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:727
MagickExport ImageType IdentifyImageGray(const Image *image, ExceptionInfo *exception)
Definition: attribute.c:1561
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:1386
MagickExport PointInfo * GetImageMinimumBoundingBox(Image *image, size_t *number_vertices, ExceptionInfo *exception)
Definition: attribute.c:1199
MagickExport const char * GetImageArtifact(const Image *image, const char *artifact)
Definition: artifact.c:273
MagickRealType red
Definition: pixel.h:193
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:1643
struct _CaliperInfo CaliperInfo
#define MagickEpsilon
Definition: magick-type.h:114
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:3325
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:2135
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:1143
MagickExport MagickBooleanType SetImageAlphaChannel(Image *image, const AlphaChannelOption alpha_type, ExceptionInfo *exception)
Definition: channel.c:974
MagickBooleanType
Definition: magick-type.h:165
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:1162
static double getFeretDiameter(PointInfo *p, PointInfo *q, PointInfo *v)
Definition: attribute.c:1185
static MagickBooleanType IssRGBCompatibleColorspace(const ColorspaceType colorspace)
MagickExport MagickBooleanType NormalizeImage(Image *image, ExceptionInfo *exception)
Definition: enhance.c:4114
MagickExport void * AcquireQuantumMemory(const size_t count, const size_t quantum)
Definition: memory.c:665
ssize_t p
Definition: attribute.c:1149
MagickExport ImageType GetImageType(const Image *image)
Definition: attribute.c:1506
double y
Definition: geometry.h:124
static int GetOpenMPThreadId(void)
static MagickBooleanType IsPixelAtDepth(const Quantum pixel, const QuantumAny range)
#define RestoreMSCWarning
Definition: studio.h:359
MagickExport PointInfo * GetImageConvexHull(const Image *image, size_t *number_vertices, ExceptionInfo *exception)
Definition: attribute.c:763
size_t number_colors
Definition: quantize.h:38
RectangleInfo page
Definition: image.h:212
ssize_t v
Definition: attribute.c:1149
static void GetPixelInfoPixel(const Image *magick_restrict image, const Quantum *magick_restrict pixel, PixelInfo *magick_restrict pixel_info)
#define INFINITY
Definition: magick-type.h:191
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:2498
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:3088
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:1457
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:1724
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:2614
double projection
Definition: attribute.c:1143
PixelChannel
Definition: pixel.h:70
#define MaxMap
Definition: magick-type.h:79
#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:597
static size_t GetPixelChannels(const Image *magick_restrict image)
MagickExport int LocaleCompare(const char *p, const char *q)
Definition: locale.c:1399
MagickExport MagickBooleanType IsPaletteImage(const Image *image)
Definition: histogram.c:867
MagickExport QuantizeInfo * AcquireQuantizeInfo(const ImageInfo *image_info)
Definition: quantize.c:378
static double LexicographicalOrder(PointInfo *a, PointInfo *b, PointInfo *c)
Definition: attribute.c:589
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:1248
MagickExport char * StringToken(const char *delimiters, char **string)
Definition: string.c:2235
MagickExport MagickBooleanType IsImageGray(const Image *image)
Definition: attribute.c:1778
MagickExport size_t GetImageDepth(const Image *image, ExceptionInfo *exception)
Definition: attribute.c:887
unsigned short Quantum
Definition: magick-type.h:86
MagickExport MagickBooleanType IsImageMonochrome(const Image *image)
Definition: attribute.c:1810
MagickExport RectangleInfo GetImageBoundingBox(const Image *image, ExceptionInfo *exception)
Definition: attribute.c:389
MagickExport char * DestroyString(char *string)
Definition: string.c:788
double area
Definition: attribute.c:1143
MagickExport MagickBooleanType IsFuzzyEquivalencePixelInfo(const PixelInfo *p, const PixelInfo *q)
Definition: pixel.c:6054
MagickBooleanType dither
Definition: image.h:432
#define MagickMin(x, y)
Definition: image-private.h:37
double width
Definition: attribute.c:1143
MagickExport void SetGeometry(const Image *image, RectangleInfo *geometry)
Definition: geometry.c:1696
static double StringToDouble(const char *magick_restrict string, char *magick_restrict *sentinal)
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:1149
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:152
MagickExport Image * DestroyImage(Image *image)
Definition: image.c:1177
MagickExport Image * CloneImage(const Image *image, const size_t columns, const size_t rows, const MagickBooleanType detach, ExceptionInfo *exception)
Definition: image.c:788
static double getProjection(PointInfo *p, PointInfo *q, PointInfo *v)
Definition: attribute.c:1171
ColorspaceType colorspace
Definition: image.h:157
MagickExport MagickBooleanType IsImageOpaque(const Image *image, ExceptionInfo *exception)
Definition: attribute.c:1847
#define QuantumRange
Definition: magick-type.h:87
MagickBooleanType debug
Definition: image.h:334
static PixelTrait GetPixelBlueTraits(const Image *magick_restrict image)
size_t depth
Definition: image.h:172