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