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  *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  ImageType
1731  type;
1732 
1733  assert(image != (Image *) NULL);
1734  assert(image->signature == MagickCoreSignature);
1735  if (image->debug != MagickFalse)
1736  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1737  if (image->colorspace == CMYKColorspace)
1738  {
1739  if (image->alpha_trait == UndefinedPixelTrait)
1740  return(ColorSeparationType);
1741  return(ColorSeparationAlphaType);
1742  }
1743  type=IdentifyImageGray(image,exception);
1744  if ((type == BilevelType) || (type == GrayscaleType) ||
1745  (type == GrayscaleAlphaType))
1746  return(type);
1747  if (IdentifyPaletteImage(image,exception) != MagickFalse)
1748  {
1749  if (image->alpha_trait != UndefinedPixelTrait)
1750  return(PaletteAlphaType);
1751  return(PaletteType);
1752  }
1753  if (image->alpha_trait != UndefinedPixelTrait)
1754  return(TrueColorAlphaType);
1755  return(TrueColorType);
1756 }
1757 
1758 /*
1759 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1760 % %
1761 % %
1762 % %
1763 % I s I m a g e G r a y %
1764 % %
1765 % %
1766 % %
1767 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1768 %
1769 % IsImageGray() returns MagickTrue if the type of the image is grayscale or
1770 % bi-level.
1771 %
1772 % The format of the IsImageGray method is:
1773 %
1774 % MagickBooleanType IsImageGray(const Image *image)
1775 %
1776 % A description of each parameter follows:
1777 %
1778 % o image: the image.
1779 %
1780 */
1782 {
1783  assert(image != (Image *) NULL);
1784  assert(image->signature == MagickCoreSignature);
1785  if ((image->type == BilevelType) || (image->type == GrayscaleType) ||
1786  (image->type == GrayscaleAlphaType))
1787  return(MagickTrue);
1788  return(MagickFalse);
1789 }
1790 
1791 /*
1792 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1793 % %
1794 % %
1795 % %
1796 % I s I m a g e M o n o c h r o m e %
1797 % %
1798 % %
1799 % %
1800 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1801 %
1802 % IsImageMonochrome() returns MagickTrue if type of the image is bi-level.
1803 %
1804 % The format of the IsImageMonochrome method is:
1805 %
1806 % MagickBooleanType IsImageMonochrome(const Image *image)
1807 %
1808 % A description of each parameter follows:
1809 %
1810 % o image: the image.
1811 %
1812 */
1814 {
1815  assert(image != (Image *) NULL);
1816  assert(image->signature == MagickCoreSignature);
1817  if (image->type == BilevelType)
1818  return(MagickTrue);
1819  return(MagickFalse);
1820 }
1821 
1822 /*
1823 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1824 % %
1825 % %
1826 % %
1827 % I s I m a g e O p a q u e %
1828 % %
1829 % %
1830 % %
1831 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1832 %
1833 % IsImageOpaque() returns MagickTrue if none of the pixels in the image have
1834 % an alpha value other than OpaqueAlpha (QuantumRange).
1835 %
1836 % Will return true immediatally is alpha channel is not available.
1837 %
1838 % The format of the IsImageOpaque method is:
1839 %
1840 % MagickBooleanType IsImageOpaque(const Image *image,
1841 % ExceptionInfo *exception)
1842 %
1843 % A description of each parameter follows:
1844 %
1845 % o image: the image.
1846 %
1847 % o exception: return any errors or warnings in this structure.
1848 %
1849 */
1851  ExceptionInfo *exception)
1852 {
1853  CacheView
1854  *image_view;
1855 
1856  const Quantum
1857  *p;
1858 
1859  ssize_t
1860  x;
1861 
1862  ssize_t
1863  y;
1864 
1865  /*
1866  Determine if image is opaque.
1867  */
1868  assert(image != (Image *) NULL);
1869  assert(image->signature == MagickCoreSignature);
1870  if (image->debug != MagickFalse)
1871  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1872  if (image->alpha_trait == UndefinedPixelTrait)
1873  return(MagickTrue);
1874  image_view=AcquireVirtualCacheView(image,exception);
1875  for (y=0; y < (ssize_t) image->rows; y++)
1876  {
1877  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1878  if (p == (const Quantum *) NULL)
1879  break;
1880  for (x=0; x < (ssize_t) image->columns; x++)
1881  {
1882  if (GetPixelAlpha(image,p) != OpaqueAlpha)
1883  break;
1884  p+=GetPixelChannels(image);
1885  }
1886  if (x < (ssize_t) image->columns)
1887  break;
1888  }
1889  image_view=DestroyCacheView(image_view);
1890  return(y < (ssize_t) image->rows ? MagickFalse : MagickTrue);
1891 }
1892 
1893 /*
1894 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1895 % %
1896 % %
1897 % %
1898 % S e t I m a g e D e p t h %
1899 % %
1900 % %
1901 % %
1902 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1903 %
1904 % SetImageDepth() sets the depth of the image.
1905 %
1906 % The format of the SetImageDepth method is:
1907 %
1908 % MagickBooleanType SetImageDepth(Image *image,const size_t depth,
1909 % ExceptionInfo *exception)
1910 %
1911 % A description of each parameter follows:
1912 %
1913 % o image: the image.
1914 %
1915 % o channel: the channel.
1916 %
1917 % o depth: the image depth.
1918 %
1919 % o exception: return any errors or warnings in this structure.
1920 %
1921 */
1923  const size_t depth,ExceptionInfo *exception)
1924 {
1925  CacheView
1926  *image_view;
1927 
1929  status;
1930 
1931  QuantumAny
1932  range;
1933 
1934  ssize_t
1935  y;
1936 
1937  assert(image != (Image *) NULL);
1938  if (image->debug != MagickFalse)
1939  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1940  assert(image->signature == MagickCoreSignature);
1941  if (depth >= MAGICKCORE_QUANTUM_DEPTH)
1942  {
1943  image->depth=depth;
1944  return(MagickTrue);
1945  }
1946  range=GetQuantumRange(depth);
1947  if (image->storage_class == PseudoClass)
1948  {
1949  ssize_t
1950  i;
1951 
1952 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1953  #pragma omp parallel for schedule(static) shared(status) \
1954  magick_number_threads(image,image,image->colors,1)
1955 #endif
1956  for (i=0; i < (ssize_t) image->colors; i++)
1957  {
1958  if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
1959  image->colormap[i].red=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
1960  ClampPixel(image->colormap[i].red),range),range);
1961  if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
1963  ClampPixel(image->colormap[i].green),range),range);
1964  if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
1965  image->colormap[i].blue=(double) ScaleAnyToQuantum(ScaleQuantumToAny(
1966  ClampPixel(image->colormap[i].blue),range),range);
1967  if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
1969  ClampPixel(image->colormap[i].alpha),range),range);
1970  }
1971  }
1972  status=MagickTrue;
1973  image_view=AcquireAuthenticCacheView(image,exception);
1974 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1975  if ((1UL*QuantumRange) <= MaxMap)
1976  {
1977  Quantum
1978  *depth_map;
1979 
1980  ssize_t
1981  i;
1982 
1983  /*
1984  Scale pixels to desired (optimized with depth map).
1985  */
1986  depth_map=(Quantum *) AcquireQuantumMemory(MaxMap+1,sizeof(*depth_map));
1987  if (depth_map == (Quantum *) NULL)
1988  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1989  for (i=0; i <= (ssize_t) MaxMap; i++)
1990  depth_map[i]=ScaleAnyToQuantum(ScaleQuantumToAny((Quantum) i,range),
1991  range);
1992 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1993  #pragma omp parallel for schedule(static) shared(status) \
1994  magick_number_threads(image,image,image->rows,1)
1995 #endif
1996  for (y=0; y < (ssize_t) image->rows; y++)
1997  {
1998  ssize_t
1999  x;
2000 
2001  Quantum
2002  *magick_restrict q;
2003 
2004  if (status == MagickFalse)
2005  continue;
2006  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
2007  exception);
2008  if (q == (Quantum *) NULL)
2009  {
2010  status=MagickFalse;
2011  continue;
2012  }
2013  for (x=0; x < (ssize_t) image->columns; x++)
2014  {
2015  ssize_t
2016  i;
2017 
2018  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2019  {
2020  PixelChannel
2021  channel;
2022 
2023  PixelTrait
2024  traits;
2025 
2026  channel=GetPixelChannelChannel(image,i);
2027  traits=GetPixelChannelTraits(image,channel);
2028  if ((traits & UpdatePixelTrait) == 0)
2029  continue;
2030  q[i]=depth_map[ScaleQuantumToMap(q[i])];
2031  }
2032  q+=GetPixelChannels(image);
2033  }
2034  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2035  {
2036  status=MagickFalse;
2037  continue;
2038  }
2039  }
2040  image_view=DestroyCacheView(image_view);
2041  depth_map=(Quantum *) RelinquishMagickMemory(depth_map);
2042  if (status != MagickFalse)
2043  image->depth=depth;
2044  return(status);
2045  }
2046 #endif
2047  /*
2048  Scale pixels to desired depth.
2049  */
2050 #if defined(MAGICKCORE_OPENMP_SUPPORT)
2051  #pragma omp parallel for schedule(static) shared(status) \
2052  magick_number_threads(image,image,image->rows,1)
2053 #endif
2054  for (y=0; y < (ssize_t) image->rows; y++)
2055  {
2056  ssize_t
2057  x;
2058 
2059  Quantum
2060  *magick_restrict q;
2061 
2062  if (status == MagickFalse)
2063  continue;
2064  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
2065  if (q == (Quantum *) NULL)
2066  {
2067  status=MagickFalse;
2068  continue;
2069  }
2070  for (x=0; x < (ssize_t) image->columns; x++)
2071  {
2072  ssize_t
2073  i;
2074 
2075  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2076  {
2077  PixelChannel
2078  channel;
2079 
2080  PixelTrait
2081  traits;
2082 
2083  channel=GetPixelChannelChannel(image,i);
2084  traits=GetPixelChannelTraits(image,channel);
2085  if ((traits & UpdatePixelTrait) == 0)
2086  continue;
2088  q[i]),range),range);
2089  }
2090  q+=GetPixelChannels(image);
2091  }
2092  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
2093  {
2094  status=MagickFalse;
2095  continue;
2096  }
2097  }
2098  image_view=DestroyCacheView(image_view);
2099  if (status != MagickFalse)
2100  image->depth=depth;
2101  return(status);
2102 }
2103 
2104 /*
2105 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2106 % %
2107 % %
2108 % %
2109 % S e t I m a g e T y p e %
2110 % %
2111 % %
2112 % %
2113 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2114 %
2115 % SetImageType() sets the type of image. Choose from these types:
2116 %
2117 % Bilevel Grayscale GrayscaleMatte
2118 % Palette PaletteMatte TrueColor
2119 % TrueColorMatte ColorSeparation ColorSeparationMatte
2120 % OptimizeType
2121 %
2122 % The format of the SetImageType method is:
2123 %
2124 % MagickBooleanType SetImageType(Image *image,const ImageType type,
2125 % ExceptionInfo *exception)
2126 %
2127 % A description of each parameter follows:
2128 %
2129 % o image: the image.
2130 %
2131 % o type: Image type.
2132 %
2133 % o exception: return any errors or warnings in this structure.
2134 %
2135 */
2137  ExceptionInfo *exception)
2138 {
2139  const char
2140  *artifact;
2141 
2142  ImageInfo
2143  *image_info;
2144 
2146  status;
2147 
2148  QuantizeInfo
2149  *quantize_info;
2150 
2151  assert(image != (Image *) NULL);
2152  if (image->debug != MagickFalse)
2153  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
2154  assert(image->signature == MagickCoreSignature);
2155  status=MagickTrue;
2156  image_info=AcquireImageInfo();
2157  image_info->dither=image->dither;
2158  artifact=GetImageArtifact(image,"dither");
2159  if (artifact != (const char *) NULL)
2160  (void) SetImageOption(image_info,"dither",artifact);
2161  switch (type)
2162  {
2163  case BilevelType:
2164  {
2165  status=TransformImageColorspace(image,GRAYColorspace,exception);
2166  (void) NormalizeImage(image,exception);
2167  quantize_info=AcquireQuantizeInfo(image_info);
2168  quantize_info->number_colors=2;
2169  quantize_info->colorspace=GRAYColorspace;
2170  status=QuantizeImage(quantize_info,image,exception);
2171  quantize_info=DestroyQuantizeInfo(quantize_info);
2173  break;
2174  }
2175  case GrayscaleType:
2176  {
2177  status=TransformImageColorspace(image,GRAYColorspace,exception);
2179  break;
2180  }
2181  case GrayscaleAlphaType:
2182  {
2183  status=TransformImageColorspace(image,GRAYColorspace,exception);
2184  if (image->alpha_trait == UndefinedPixelTrait)
2185  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2186  break;
2187  }
2188  case PaletteType:
2189  {
2190  status=TransformImageColorspace(image,sRGBColorspace,exception);
2191  if ((image->storage_class == DirectClass) || (image->colors > 256))
2192  {
2193  quantize_info=AcquireQuantizeInfo(image_info);
2194  quantize_info->number_colors=256;
2195  status=QuantizeImage(quantize_info,image,exception);
2196  quantize_info=DestroyQuantizeInfo(quantize_info);
2197  }
2199  break;
2200  }
2202  {
2203  ChannelType
2204  channel_mask;
2205 
2206  status=TransformImageColorspace(image,sRGBColorspace,exception);
2207  if (image->alpha_trait == UndefinedPixelTrait)
2208  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2209  channel_mask=SetImageChannelMask(image,AlphaChannel);
2210  (void) BilevelImage(image,(double) QuantumRange/2.0,exception);
2211  (void) SetImageChannelMask(image,channel_mask);
2212  quantize_info=AcquireQuantizeInfo(image_info);
2213  status=QuantizeImage(quantize_info,image,exception);
2214  quantize_info=DestroyQuantizeInfo(quantize_info);
2215  break;
2216  }
2217  case PaletteAlphaType:
2218  {
2219  status=TransformImageColorspace(image,sRGBColorspace,exception);
2220  if (image->alpha_trait == UndefinedPixelTrait)
2221  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2222  quantize_info=AcquireQuantizeInfo(image_info);
2223  quantize_info->colorspace=TransparentColorspace;
2224  status=QuantizeImage(quantize_info,image,exception);
2225  quantize_info=DestroyQuantizeInfo(quantize_info);
2226  break;
2227  }
2228  case TrueColorType:
2229  {
2230  status=TransformImageColorspace(image,sRGBColorspace,exception);
2231  if (image->storage_class != DirectClass)
2232  status=SetImageStorageClass(image,DirectClass,exception);
2234  break;
2235  }
2236  case TrueColorAlphaType:
2237  {
2238  status=TransformImageColorspace(image,sRGBColorspace,exception);
2239  if (image->storage_class != DirectClass)
2240  status=SetImageStorageClass(image,DirectClass,exception);
2241  if (image->alpha_trait == UndefinedPixelTrait)
2242  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2243  break;
2244  }
2245  case ColorSeparationType:
2246  {
2247  status=TransformImageColorspace(image,CMYKColorspace,exception);
2248  if (image->storage_class != DirectClass)
2249  status=SetImageStorageClass(image,DirectClass,exception);
2251  break;
2252  }
2254  {
2255  status=TransformImageColorspace(image,CMYKColorspace,exception);
2256  if (image->storage_class != DirectClass)
2257  status=SetImageStorageClass(image,DirectClass,exception);
2258  if (image->alpha_trait == UndefinedPixelTrait)
2259  status=SetImageAlphaChannel(image,OpaqueAlphaChannel,exception);
2260  break;
2261  }
2262  case OptimizeType:
2263  case UndefinedType:
2264  break;
2265  }
2266  image_info=DestroyImageInfo(image_info);
2267  if (status == MagickFalse)
2268  return(status);
2269  image->type=type;
2270  return(MagickTrue);
2271 }
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
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:1922
#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: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 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: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:2136
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: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: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:3093
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: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:1781
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:1813
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
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:1850
#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