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