MagickCore  6.9.13-16
Convert, Edit, Or Compose Bitmap Images
attribute.c
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 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 "magick/studio.h"
44 #include "magick/artifact.h"
45 #include "magick/attribute.h"
46 #include "magick/blob.h"
47 #include "magick/blob-private.h"
48 #include "magick/cache.h"
49 #include "magick/cache-private.h"
50 #include "magick/cache-view.h"
51 #include "magick/client.h"
52 #include "magick/channel.h"
53 #include "magick/color.h"
54 #include "magick/color-private.h"
55 #include "magick/colormap.h"
56 #include "magick/colormap-private.h"
57 #include "magick/colorspace.h"
58 #include "magick/colorspace-private.h"
59 #include "magick/composite.h"
60 #include "magick/composite-private.h"
61 #include "magick/constitute.h"
62 #include "magick/deprecate.h"
63 #include "magick/draw.h"
64 #include "magick/draw-private.h"
65 #include "magick/effect.h"
66 #include "magick/enhance.h"
67 #include "magick/exception.h"
68 #include "magick/exception-private.h"
69 #include "magick/geometry.h"
70 #include "magick/histogram.h"
71 #include "magick/identify.h"
72 #include "magick/image.h"
73 #include "magick/image-private.h"
74 #include "magick/list.h"
75 #include "magick/log.h"
76 #include "magick/memory_.h"
77 #include "magick/magick.h"
78 #include "magick/monitor.h"
79 #include "magick/monitor-private.h"
80 #include "magick/option.h"
81 #include "magick/paint.h"
82 #include "magick/pixel.h"
83 #include "magick/pixel-private.h"
84 #include "magick/property.h"
85 #include "magick/quantize.h"
86 #include "magick/random_.h"
87 #include "magick/resource_.h"
88 #include "magick/semaphore.h"
89 #include "magick/segment.h"
90 #include "magick/splay-tree.h"
91 #include "magick/string_.h"
92 #include "magick/string-private.h"
93 #include "magick/thread-private.h"
94 #include "magick/threshold.h"
95 #include "magick/transform.h"
96 #include "magick/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 _CensusInfo
128 {
129  double
130  left,
131  right,
132  top,
133  bottom;
134 } CensusInfo;
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  const PixelPacket
148  *p;
149 
150  double
151  census;
152 
153  Image
154  *edge_image;
155 
157  background,
158  pixel;
159 
161  edge_geometry;
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  if (p == (const PixelPacket *) NULL)
201  return(0.0);
202  GetMagickPixelPacket(image,&background);
203  SetMagickPixelPacket(image,p,(IndexPacket *) NULL,&background);
204  artifact=GetImageArtifact(image,"background");
205  if (artifact != (const char *) NULL)
206  (void) QueryMagickColor(artifact,&background,exception);
207  artifact=GetImageArtifact(image,"trim:background-color");
208  if (artifact != (const char *) NULL)
209  (void) QueryMagickColor(artifact,&background,exception);
210  edge_geometry.width=width;
211  edge_geometry.height=height;
212  edge_geometry.x=x_offset;
213  edge_geometry.y=y_offset;
214  GravityAdjustGeometry(image->columns,image->rows,gravity,&edge_geometry);
215  edge_image=CropImage(image,&edge_geometry,exception);
216  if (edge_image == (Image *) NULL)
217  return(0.0);
218  census=0.0;
219  GetMagickPixelPacket(edge_image,&pixel);
220  edge_view=AcquireVirtualCacheView(edge_image,exception);
221  for (y=0; y < (ssize_t) edge_image->rows; y++)
222  {
223  ssize_t
224  x;
225 
226  p=GetCacheViewVirtualPixels(edge_view,0,y,edge_image->columns,1,exception);
227  if (p == (const PixelPacket *) NULL)
228  break;
229  for (x=0; x < (ssize_t) edge_image->columns; x++)
230  {
231  SetMagickPixelPacket(edge_image,p,(IndexPacket *) NULL,&pixel);
232  if (IsMagickColorSimilar(&pixel,&background) == MagickFalse)
233  census++;
234  p++;
235  }
236  }
237  census/=((double) edge_image->columns*edge_image->rows);
238  edge_view=DestroyCacheView(edge_view);
239  edge_image=DestroyImage(edge_image);
240  return(census);
241 }
242 
243 static inline double GetMinEdgeBackgroundCensus(const CensusInfo *edge)
244 {
245  double
246  census;
247 
248  census=MagickMin(MagickMin(MagickMin(edge->left,edge->right),edge->top),
249  edge->bottom);
250  return(census);
251 }
252 
253 static RectangleInfo GetEdgeBoundingBox(const Image *image,
254  ExceptionInfo *exception)
255 {
256  CacheView
257  *edge_view;
258 
259  CensusInfo
260  edge,
261  vertex;
262 
263  const char
264  *artifact;
265 
266  double
267  background_census,
268  percent_background;
269 
270  Image
271  *edge_image;
272 
274  bounds;
275 
276  /*
277  Get the image bounding box.
278  */
279  assert(image != (Image *) NULL);
280  assert(image->signature == MagickCoreSignature);
281  if (IsEventLogging() != MagickFalse)
282  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
283  SetGeometry(image,&bounds);
284  edge_image=CloneImage(image,0,0,MagickTrue,exception);
285  if (edge_image == (Image *) NULL)
286  return(bounds);
287  (void) ParseAbsoluteGeometry("0x0+0+0",&edge_image->page);
288  (void) memset(&vertex,0,sizeof(vertex));
289  edge_view=AcquireVirtualCacheView(edge_image,exception);
290  edge.left=GetEdgeBackgroundCensus(edge_image,edge_view,WestGravity,
291  1,0,0,0,exception);
292  edge.right=GetEdgeBackgroundCensus(edge_image,edge_view,EastGravity,
293  1,0,0,0,exception);
294  edge.top=GetEdgeBackgroundCensus(edge_image,edge_view,NorthGravity,
295  0,1,0,0,exception);
296  edge.bottom=GetEdgeBackgroundCensus(edge_image,edge_view,SouthGravity,
297  0,1,0,0,exception);
298  percent_background=1.0;
299  artifact=GetImageArtifact(edge_image,"trim:percent-background");
300  if (artifact != (const char *) NULL)
301  percent_background=StringToDouble(artifact,(char **) NULL)/100.0;
302  percent_background=MagickMin(MagickMax(1.0-percent_background,MagickEpsilon),
303  1.0);
304  background_census=GetMinEdgeBackgroundCensus(&edge);
305  for ( ; background_census < percent_background;
306  background_census=GetMinEdgeBackgroundCensus(&edge))
307  {
308  if ((bounds.width == 0) || (bounds.height == 0))
309  break;
310  if (fabs(edge.left-background_census) < MagickEpsilon)
311  {
312  /*
313  Trim left edge.
314  */
315  vertex.left++;
316  bounds.width--;
317  edge.left=GetEdgeBackgroundCensus(edge_image,edge_view,
318  NorthWestGravity,1,bounds.height,(ssize_t) vertex.left,(ssize_t)
319  vertex.top,exception);
320  edge.top=GetEdgeBackgroundCensus(edge_image,edge_view,
321  NorthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
322  vertex.top,exception);
323  edge.bottom=GetEdgeBackgroundCensus(edge_image,edge_view,
324  SouthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
325  vertex.bottom,exception);
326  continue;
327  }
328  if (fabs(edge.right-background_census) < MagickEpsilon)
329  {
330  /*
331  Trim right edge.
332  */
333  vertex.right++;
334  bounds.width--;
335  edge.right=GetEdgeBackgroundCensus(edge_image,edge_view,
336  NorthEastGravity,1,bounds.height,(ssize_t) vertex.right,(ssize_t)
337  vertex.top,exception);
338  edge.top=GetEdgeBackgroundCensus(edge_image,edge_view,
339  NorthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
340  vertex.top,exception);
341  edge.bottom=GetEdgeBackgroundCensus(edge_image,edge_view,
342  SouthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
343  vertex.bottom,exception);
344  continue;
345  }
346  if (fabs(edge.top-background_census) < MagickEpsilon)
347  {
348  /*
349  Trim top edge.
350  */
351  vertex.top++;
352  bounds.height--;
353  edge.left=GetEdgeBackgroundCensus(edge_image,edge_view,
354  NorthWestGravity,1,bounds.height,(ssize_t) vertex.left,(ssize_t)
355  vertex.top,exception);
356  edge.right=GetEdgeBackgroundCensus(edge_image,edge_view,
357  NorthEastGravity,1,bounds.height,(ssize_t) vertex.right,(ssize_t)
358  vertex.top,exception);
359  edge.top=GetEdgeBackgroundCensus(edge_image,edge_view,
360  NorthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
361  vertex.top,exception);
362  continue;
363  }
364  if (fabs(edge.bottom-background_census) < MagickEpsilon)
365  {
366  /*
367  Trim bottom edge.
368  */
369  vertex.bottom++;
370  bounds.height--;
371  edge.left=GetEdgeBackgroundCensus(edge_image,edge_view,
372  NorthWestGravity,1,bounds.height,(ssize_t) vertex.left,(ssize_t)
373  vertex.top,exception);
374  edge.right=GetEdgeBackgroundCensus(edge_image,edge_view,
375  NorthEastGravity,1,bounds.height,(ssize_t) vertex.right,(ssize_t)
376  vertex.top,exception);
377  edge.bottom=GetEdgeBackgroundCensus(edge_image,edge_view,
378  SouthWestGravity,bounds.width,1,(ssize_t) vertex.left,(ssize_t)
379  vertex.bottom,exception);
380  continue;
381  }
382  }
383  edge_view=DestroyCacheView(edge_view);
384  edge_image=DestroyImage(edge_image);
385  bounds.x=(ssize_t) vertex.left;
386  bounds.y=(ssize_t) vertex.top;
387  if ((bounds.width == 0) || (bounds.height == 0))
388  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
389  "GeometryDoesNotContainImage","`%s'",image->filename);
390  return(bounds);
391 }
392 
393 MagickExport RectangleInfo GetImageBoundingBox(const Image *image,
394  ExceptionInfo *exception)
395 {
396  CacheView
397  *image_view;
398 
399  const char
400  *artifact;
401 
402  MagickBooleanType
403  status;
404 
406  target[4],
407  zero;
408 
410  bounds;
411 
412  const PixelPacket
413  *p;
414 
415  ssize_t
416  y;
417 
418  assert(image != (Image *) NULL);
419  assert(image->signature == MagickCoreSignature);
420  if (IsEventLogging() != MagickFalse)
421  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
422  artifact=GetImageArtifact(image,"trim:percent-background");
423  if (artifact != (const char *) NULL)
424  return(GetEdgeBoundingBox(image,exception));
425  bounds.width=image->columns == 1 ? 1 : 0;
426  bounds.height=image->rows == 1 ? 1 : 0;
427  bounds.x=(ssize_t) image->columns;
428  bounds.y=(ssize_t) image->rows;
429  GetMagickPixelPacket(image,&target[0]);
430  image_view=AcquireVirtualCacheView(image,exception);
431  p=GetCacheViewVirtualPixels(image_view,0,0,1,1,exception);
432  if (p == (const PixelPacket *) NULL)
433  {
434  image_view=DestroyCacheView(image_view);
435  return(bounds);
436  }
437  SetMagickPixelPacket(image,p,GetCacheViewVirtualIndexQueue(image_view),
438  &target[0]);
439  GetMagickPixelPacket(image,&target[1]);
440  p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,0,1,1,
441  exception);
442  if (p != (const PixelPacket *) NULL)
443  SetMagickPixelPacket(image,p,GetCacheViewVirtualIndexQueue(image_view),
444  &target[1]);
445  GetMagickPixelPacket(image,&target[2]);
446  p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-1,1,1,
447  exception);
448  if (p != (const PixelPacket *) NULL)
449  SetMagickPixelPacket(image,p,GetCacheViewVirtualIndexQueue(image_view),
450  &target[2]);
451  GetMagickPixelPacket(image,&target[3]);
452  p=GetCacheViewVirtualPixels(image_view,(ssize_t) image->columns-1,
453  (ssize_t) image->rows-1,1,1,exception);
454  if (p != (const PixelPacket *) NULL)
455  SetMagickPixelPacket(image,p,GetCacheViewVirtualIndexQueue(image_view),
456  &target[3]);
457  status=MagickTrue;
458  GetMagickPixelPacket(image,&zero);
459 #if defined(MAGICKCORE_OPENMP_SUPPORT)
460  #pragma omp parallel for schedule(static) shared(status) \
461  magick_number_threads(image,image,image->rows,2)
462 #endif
463  for (y=0; y < (ssize_t) image->rows; y++)
464  {
466  pixel;
467 
469  bounding_box;
470 
471  const IndexPacket
472  *magick_restrict indexes;
473 
474  const PixelPacket
475  *magick_restrict p;
476 
477  ssize_t
478  x;
479 
480  if (status == MagickFalse)
481  continue;
482 #if defined(MAGICKCORE_OPENMP_SUPPORT)
483 # pragma omp critical (MagickCore_GetImageBoundingBox)
484 #endif
485  bounding_box=bounds;
486  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
487  if (p == (const PixelPacket *) NULL)
488  {
489  status=MagickFalse;
490  continue;
491  }
492  indexes=GetCacheViewVirtualIndexQueue(image_view);
493  pixel=zero;
494  for (x=0; x < (ssize_t) image->columns; x++)
495  {
496  SetMagickPixelPacket(image,p,indexes+x,&pixel);
497  if ((x < bounding_box.x) &&
498  (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
499  bounding_box.x=x;
500  if ((x > (ssize_t) bounding_box.width) &&
501  (IsMagickColorSimilar(&pixel,&target[1]) == MagickFalse))
502  bounding_box.width=(size_t) x;
503  if ((y < bounding_box.y) &&
504  (IsMagickColorSimilar(&pixel,&target[0]) == MagickFalse))
505  bounding_box.y=y;
506  if ((y > (ssize_t) bounding_box.height) &&
507  (IsMagickColorSimilar(&pixel,&target[2]) == MagickFalse))
508  bounding_box.height=(size_t) y;
509  if ((x < (ssize_t) bounding_box.width) &&
510  (y > (ssize_t) bounding_box.height) &&
511  (IsMagickColorSimilar(&pixel,&target[3]) == MagickFalse))
512  {
513  bounding_box.width=(size_t) x;
514  bounding_box.height=(size_t) y;
515  }
516  p++;
517  }
518 #if defined(MAGICKCORE_OPENMP_SUPPORT)
519 # pragma omp critical (MagickCore_GetImageBoundingBox)
520 #endif
521  {
522  if (bounding_box.x < bounds.x)
523  bounds.x=bounding_box.x;
524  if (bounding_box.y < bounds.y)
525  bounds.y=bounding_box.y;
526  if (bounding_box.width > bounds.width)
527  bounds.width=bounding_box.width;
528  if (bounding_box.height > bounds.height)
529  bounds.height=bounding_box.height;
530  }
531  }
532  image_view=DestroyCacheView(image_view);
533  if ((bounds.width == 0) || (bounds.height == 0))
534  (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
535  "GeometryDoesNotContainImage","`%s'",image->filename);
536  else
537  {
538  bounds.width-=(size_t) (bounds.x-1);
539  bounds.height-=(size_t) (bounds.y-1);
540  }
541  return(bounds);
542 }
543 
544 /*
545 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
546 % %
547 % %
548 % %
549 % G e t I m a g e C h a n n e l D e p t h %
550 % %
551 % %
552 % %
553 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
554 %
555 % GetImageChannelDepth() returns the depth of a particular image channel.
556 %
557 % The format of the GetImageChannelDepth method is:
558 %
559 % size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
560 % size_t GetImageChannelDepth(const Image *image,
561 % const ChannelType channel,ExceptionInfo *exception)
562 %
563 % A description of each parameter follows:
564 %
565 % o image: the image.
566 %
567 % o channel: the channel.
568 %
569 % o exception: return any errors or warnings in this structure.
570 %
571 */
572 MagickExport size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
573 {
574  return(GetImageChannelDepth(image,CompositeChannels,exception));
575 }
576 
577 MagickExport size_t GetImageChannelDepth(const Image *image,
578  const ChannelType channel,ExceptionInfo *exception)
579 {
580  CacheView
581  *image_view;
582 
583  MagickBooleanType
584  status;
585 
586  ssize_t
587  i;
588 
589  size_t
590  *current_depth,
591  depth,
592  number_threads;
593 
594  ssize_t
595  y;
596 
597  /*
598  Compute image depth.
599  */
600  assert(image != (Image *) NULL);
601 
602  assert(image->signature == MagickCoreSignature);
603  if (IsEventLogging() != MagickFalse)
604  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
605  number_threads=(size_t) GetMagickResourceLimit(ThreadResource);
606  current_depth=(size_t *) AcquireQuantumMemory(number_threads,
607  sizeof(*current_depth));
608  if (current_depth == (size_t *) NULL)
609  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
610  status=MagickTrue;
611  for (i=0; i < (ssize_t) number_threads; i++)
612  current_depth[i]=1;
613  if ((image->storage_class == PseudoClass) && (image->matte == MagickFalse))
614  {
615 #if defined(MAGICKCORE_OPENMP_SUPPORT)
616  #pragma omp parallel for schedule(static) shared(status) \
617  magick_number_threads(image,image,image->colors,1)
618 #endif
619  for (i=0; i < (ssize_t) image->colors; i++)
620  {
621  const int
622  id = GetOpenMPThreadId();
623 
624  while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
625  {
626  MagickBooleanType
627  atDepth;
628 
629  QuantumAny
630  range;
631 
632  atDepth=MagickTrue;
633  range=GetQuantumRange(current_depth[id]);
634  if ((channel & RedChannel) != 0)
635  if (IsPixelAtDepth(image->colormap[i].red,range) == MagickFalse)
636  atDepth=MagickFalse;
637  if ((atDepth != MagickFalse) && ((channel & GreenChannel) != 0))
638  if (IsPixelAtDepth(image->colormap[i].green,range) == MagickFalse)
639  atDepth=MagickFalse;
640  if ((atDepth != MagickFalse) && ((channel & BlueChannel) != 0))
641  if (IsPixelAtDepth(image->colormap[i].blue,range) == MagickFalse)
642  atDepth=MagickFalse;
643  if ((atDepth != MagickFalse))
644  break;
645  current_depth[id]++;
646  }
647  }
648  depth=current_depth[0];
649  for (i=1; i < (ssize_t) number_threads; i++)
650  if (depth < current_depth[i])
651  depth=current_depth[i];
652  current_depth=(size_t *) RelinquishMagickMemory(current_depth);
653  return(depth);
654  }
655  image_view=AcquireVirtualCacheView(image,exception);
656 #if !defined(MAGICKCORE_HDRI_SUPPORT)
657 DisableMSCWarning(4127)
658  if (1UL*QuantumRange <= MaxMap)
659 RestoreMSCWarning
660  {
661  size_t
662  *depth_map;
663 
664  /*
665  Scale pixels to desired (optimized with depth map).
666  */
667  depth_map=(size_t *) AcquireQuantumMemory(MaxMap+1,sizeof(*depth_map));
668  if (depth_map == (size_t *) NULL)
669  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
670  for (i=0; i <= (ssize_t) MaxMap; i++)
671  {
672  unsigned int
673  depth;
674 
675  for (depth=1; depth < MAGICKCORE_QUANTUM_DEPTH; depth++)
676  {
677  Quantum
678  pixel;
679 
680  QuantumAny
681  range;
682 
683  range=GetQuantumRange(depth);
684  pixel=(Quantum) i;
685  if (pixel == ScaleAnyToQuantum(ScaleQuantumToAny(pixel,range),range))
686  break;
687  }
688  depth_map[i]=depth;
689  }
690 #if defined(MAGICKCORE_OPENMP_SUPPORT)
691  #pragma omp parallel for schedule(static) shared(status) \
692  magick_number_threads(image,image,image->rows,1)
693 #endif
694  for (y=0; y < (ssize_t) image->rows; y++)
695  {
696  const int
697  id = GetOpenMPThreadId();
698 
699  const IndexPacket
700  *magick_restrict indexes;
701 
702  const PixelPacket
703  *magick_restrict p;
704 
705  ssize_t
706  x;
707 
708  if (status == MagickFalse)
709  continue;
710  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
711  if (p == (const PixelPacket *) NULL)
712  continue;
713  indexes=GetCacheViewVirtualIndexQueue(image_view);
714  for (x=0; x < (ssize_t) image->columns; x++)
715  {
716  Quantum
717  pixel;
718 
719  if ((channel & RedChannel) != 0)
720  {
721  pixel=GetPixelRed(p);
722  if (depth_map[ScaleQuantumToMap(pixel)] > current_depth[id])
723  current_depth[id]=depth_map[ScaleQuantumToMap(pixel)];
724  }
725  if ((channel & GreenChannel) != 0)
726  {
727  pixel=GetPixelGreen(p);
728  if (depth_map[ScaleQuantumToMap(pixel)] > current_depth[id])
729  current_depth[id]=depth_map[ScaleQuantumToMap(pixel)];
730  }
731  if ((channel & BlueChannel) != 0)
732  {
733  pixel=GetPixelBlue(p);
734  if (depth_map[ScaleQuantumToMap(pixel)] > current_depth[id])
735  current_depth[id]=depth_map[ScaleQuantumToMap(pixel)];
736  }
737  if (((channel & OpacityChannel) != 0) &&
738  (image->matte != MagickFalse))
739  {
740  pixel=GetPixelOpacity(p);
741  if (depth_map[ScaleQuantumToMap(pixel)] > current_depth[id])
742  current_depth[id]=depth_map[ScaleQuantumToMap(pixel)];
743  }
744  if (((channel & IndexChannel) != 0) &&
745  (image->colorspace == CMYKColorspace))
746  {
747  pixel=GetPixelIndex(indexes+x);
748  if (depth_map[ScaleQuantumToMap(pixel)] > current_depth[id])
749  current_depth[id]=depth_map[ScaleQuantumToMap(pixel)];
750  }
751  p++;
752  }
753  if (current_depth[id] == MAGICKCORE_QUANTUM_DEPTH)
754  status=MagickFalse;
755  }
756  image_view=DestroyCacheView(image_view);
757  depth=current_depth[0];
758  for (i=1; i < (ssize_t) number_threads; i++)
759  if (depth < current_depth[i])
760  depth=current_depth[i];
761  depth_map=(size_t *) RelinquishMagickMemory(depth_map);
762  current_depth=(size_t *) RelinquishMagickMemory(current_depth);
763  return(depth);
764  }
765 #endif
766 #if defined(MAGICKCORE_OPENMP_SUPPORT)
767  #pragma omp parallel for schedule(static) shared(status) \
768  magick_number_threads(image,image,image->rows,1)
769 #endif
770  for (y=0; y < (ssize_t) image->rows; y++)
771  {
772  const int
773  id = GetOpenMPThreadId();
774 
775  const IndexPacket
776  *magick_restrict indexes;
777 
778  const PixelPacket
779  *magick_restrict p;
780 
781  ssize_t
782  x;
783 
784  if (status == MagickFalse)
785  continue;
786  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
787  if (p == (const PixelPacket *) NULL)
788  continue;
789  indexes=GetCacheViewVirtualIndexQueue(image_view);
790  for (x=0; x < (ssize_t) image->columns; x++)
791  {
792  while (current_depth[id] < MAGICKCORE_QUANTUM_DEPTH)
793  {
794  MagickBooleanType
795  atDepth;
796 
797  QuantumAny
798  range;
799 
800  atDepth=MagickTrue;
801  range=GetQuantumRange(current_depth[id]);
802  if ((atDepth != MagickFalse) && ((channel & RedChannel) != 0))
803  if (IsPixelAtDepth(GetPixelRed(p),range) == MagickFalse)
804  atDepth=MagickFalse;
805  if ((atDepth != MagickFalse) && ((channel & GreenChannel) != 0))
806  if (IsPixelAtDepth(GetPixelGreen(p),range) == MagickFalse)
807  atDepth=MagickFalse;
808  if ((atDepth != MagickFalse) && ((channel & BlueChannel) != 0))
809  if (IsPixelAtDepth(GetPixelBlue(p),range) == MagickFalse)
810  atDepth=MagickFalse;
811  if ((atDepth != MagickFalse) && ((channel & OpacityChannel) != 0) &&
812  (image->matte != MagickFalse))
813  if (IsPixelAtDepth(GetPixelOpacity(p),range) == MagickFalse)
814  atDepth=MagickTrue;
815  if ((atDepth != MagickFalse) && ((channel & IndexChannel) != 0) &&
816  (image->colorspace == CMYKColorspace))
817  if (IsPixelAtDepth(GetPixelIndex(indexes+x),range) == MagickFalse)
818  atDepth=MagickFalse;
819  if ((atDepth != MagickFalse))
820  break;
821  current_depth[id]++;
822  }
823  p++;
824  }
825  if (current_depth[id] == MAGICKCORE_QUANTUM_DEPTH)
826  status=MagickFalse;
827  }
828  image_view=DestroyCacheView(image_view);
829  depth=current_depth[0];
830  for (i=1; i < (ssize_t) number_threads; i++)
831  if (depth < current_depth[i])
832  depth=current_depth[i];
833  current_depth=(size_t *) RelinquishMagickMemory(current_depth);
834  return(depth);
835 }
836 
837 /*
838 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
839 % %
840 % %
841 % %
842 % G e t I m a g e Q u a n t u m D e p t h %
843 % %
844 % %
845 % %
846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
847 %
848 % GetImageQuantumDepth() returns the depth of the image rounded to a legal
849 % quantum depth: 8, 16, or 32.
850 %
851 % The format of the GetImageQuantumDepth method is:
852 %
853 % size_t GetImageQuantumDepth(const Image *image,
854 % const MagickBooleanType constrain)
855 %
856 % A description of each parameter follows:
857 %
858 % o image: the image.
859 %
860 % o constrain: A value other than MagickFalse, constrains the depth to
861 % a maximum of MAGICKCORE_QUANTUM_DEPTH.
862 %
863 */
864 MagickExport size_t GetImageQuantumDepth(const Image *image,
865  const MagickBooleanType constrain)
866 {
867  size_t
868  depth;
869 
870  depth=image->depth;
871  if (depth <= 8)
872  depth=8;
873  else
874  if (depth <= 16)
875  depth=16;
876  else
877  if (depth <= 32)
878  depth=32;
879  else
880  if (depth <= 64)
881  depth=64;
882  if (constrain != MagickFalse)
883  depth=(size_t) MagickMin((double) depth,(double) MAGICKCORE_QUANTUM_DEPTH);
884  return(depth);
885 }
886 
887 /*
888 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
889 % %
890 % %
891 % %
892 % G e t I m a g e T y p e %
893 % %
894 % %
895 % %
896 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
897 %
898 % GetImageType() returns the potential type of image:
899 %
900 % Bilevel Grayscale GrayscaleMatte
901 % Palette PaletteMatte TrueColor
902 % TrueColorMatte ColorSeparation ColorSeparationMatte
903 %
904 % To ensure the image type matches its potential, use SetImageType():
905 %
906 % (void) SetImageType(image,GetImageType(image));
907 %
908 % The format of the GetImageType method is:
909 %
910 % ImageType GetImageType(const Image *image,ExceptionInfo *exception)
911 %
912 % A description of each parameter follows:
913 %
914 % o image: the image.
915 %
916 % o exception: return any errors or warnings in this structure.
917 %
918 */
919 MagickExport ImageType GetImageType(const Image *image,ExceptionInfo *exception)
920 {
921  assert(image != (Image *) NULL);
922  assert(image->signature == MagickCoreSignature);
923  if (IsEventLogging() != MagickFalse)
924  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
925  if (image->colorspace == CMYKColorspace)
926  {
927  if (image->matte == MagickFalse)
928  return(ColorSeparationType);
929  return(ColorSeparationMatteType);
930  }
931  if (IsMonochromeImage(image,exception) != MagickFalse)
932  return(BilevelType);
933  if (IsGrayImage(image,exception) != MagickFalse)
934  {
935  if (image->matte != MagickFalse)
936  return(GrayscaleMatteType);
937  return(GrayscaleType);
938  }
939  if (IsPaletteImage(image,exception) != MagickFalse)
940  {
941  if (image->matte != MagickFalse)
942  return(PaletteMatteType);
943  return(PaletteType);
944  }
945  if (image->matte != MagickFalse)
946  return(TrueColorMatteType);
947  return(TrueColorType);
948 }
949 
950 /*
951 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
952 % %
953 % %
954 % %
955 % I d e n t i f y I m a g e G r a y %
956 % %
957 % %
958 % %
959 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
960 %
961 % either 0 or QuantumRange. Otherwise undefined is returned.
962 %
963 % The format of the IdentifyImageGray method is:
964 %
965 % ImageType IdentifyImageGray(const Image *image,ExceptionInfo *exception)
966 %
967 % A description of each parameter follows:
968 %
969 % o image: the image.
970 %
971 % o exception: return any errors or warnings in this structure.
972 %
973 */
974 MagickExport ImageType IdentifyImageGray(const Image *image,
975  ExceptionInfo *exception)
976 {
977  CacheView
978  *image_view;
979 
980  ImageType
981  type = BilevelType;
982 
983  MagickBooleanType
984  status = MagickTrue;
985 
986  ssize_t
987  y;
988 
989  assert(image != (Image *) NULL);
990  assert(image->signature == MagickCoreSignature);
991  if (IsEventLogging() != MagickFalse)
992  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
993  if ((image->type == BilevelType) || (image->type == GrayscaleType) ||
994  (image->type == GrayscaleMatteType))
995  return(image->type);
996  if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
997  return(UndefinedType);
998  image_view=AcquireVirtualCacheView(image,exception);
999 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1000  #pragma omp parallel for schedule(static) shared(status,type) \
1001  magick_number_threads(image,image,image->rows,2)
1002 #endif
1003  for (y=0; y < (ssize_t) image->rows; y++)
1004  {
1005  const PixelPacket
1006  *p;
1007 
1008  ssize_t
1009  x;
1010 
1011  if (status == MagickFalse)
1012  continue;
1013  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1014  if (p == (const PixelPacket *) NULL)
1015  {
1016  status=MagickFalse;
1017  continue;
1018  }
1019  for (x=0; x < (ssize_t) image->columns; x++)
1020  {
1021  if (IsPixelGray(p) == MagickFalse)
1022  {
1023  status=MagickFalse;
1024  break;
1025  }
1026  if ((type == BilevelType) && (IsPixelMonochrome(p) == MagickFalse))
1027  type=GrayscaleType;
1028  p++;
1029  }
1030  }
1031  image_view=DestroyCacheView(image_view);
1032  if ((type == GrayscaleType) && (image->matte != MagickFalse))
1033  type=GrayscaleMatteType;
1034  if (status == MagickFalse)
1035  return(UndefinedType);
1036  return(type);
1037 }
1038 
1039 /*
1040 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1041 % %
1042 % %
1043 % %
1044 % I d e n t i f y I m a g e M o n o c h r o m e %
1045 % %
1046 % %
1047 % %
1048 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1049 %
1050 % IdentifyImageMonochrome() returns MagickTrue if all the pixels in the image
1051 % have the same red, green, and blue intensities and the intensity is either
1052 % 0 or QuantumRange.
1053 %
1054 % The format of the IdentifyImageMonochrome method is:
1055 %
1056 % MagickBooleanType IdentifyImageMonochrome(const Image *image,
1057 % ExceptionInfo *exception)
1058 %
1059 % A description of each parameter follows:
1060 %
1061 % o image: the image.
1062 %
1063 % o exception: return any errors or warnings in this structure.
1064 %
1065 */
1066 MagickExport MagickBooleanType IdentifyImageMonochrome(const Image *image,
1067  ExceptionInfo *exception)
1068 {
1069  CacheView
1070  *image_view;
1071 
1072  ImageType
1073  type = BilevelType;
1074 
1075  ssize_t
1076  y;
1077 
1078  assert(image != (Image *) NULL);
1079  assert(image->signature == MagickCoreSignature);
1080  if (IsEventLogging() != MagickFalse)
1081  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1082  if (image->type == BilevelType)
1083  return(MagickTrue);
1084  if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1085  return(MagickFalse);
1086  image_view=AcquireVirtualCacheView(image,exception);
1087 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1088  #pragma omp parallel for schedule(static) shared(type) \
1089  magick_number_threads(image,image,image->rows,2)
1090 #endif
1091  for (y=0; y < (ssize_t) image->rows; y++)
1092  {
1093  const PixelPacket
1094  *p;
1095 
1096  ssize_t
1097  x;
1098 
1099  if (type == UndefinedType)
1100  continue;
1101  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1102  if (p == (const PixelPacket *) NULL)
1103  {
1104  type=UndefinedType;
1105  continue;
1106  }
1107  for (x=0; x < (ssize_t) image->columns; x++)
1108  {
1109  if (IsPixelMonochrome(p) == MagickFalse)
1110  {
1111  type=UndefinedType;
1112  break;
1113  }
1114  p++;
1115  }
1116  }
1117  image_view=DestroyCacheView(image_view);
1118  return(type == BilevelType ? MagickTrue : MagickFalse);
1119 }
1120 
1121 /*
1122 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1123 % %
1124 % %
1125 % %
1126 % I d e n t i f y I m a g e T y p e %
1127 % %
1128 % %
1129 % %
1130 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1131 %
1132 % IdentifyImageType() returns the potential type of image:
1133 %
1134 % Bilevel Grayscale GrayscaleMatte
1135 % Palette PaletteMatte TrueColor
1136 % TrueColorMatte ColorSeparation ColorSeparationMatte
1137 %
1138 % To ensure the image type matches its potential, use SetImageType():
1139 %
1140 % (void) SetImageType(image,IdentifyImageType(image,exception),exception);
1141 %
1142 % The format of the IdentifyImageType method is:
1143 %
1144 % ImageType IdentifyImageType(const Image *image,ExceptionInfo *exception)
1145 %
1146 % A description of each parameter follows:
1147 %
1148 % o image: the image.
1149 %
1150 % o exception: return any errors or warnings in this structure.
1151 %
1152 */
1153 MagickExport ImageType IdentifyImageType(const Image *image,
1154  ExceptionInfo *exception)
1155 {
1156  assert(image != (Image *) NULL);
1157  assert(image->signature == MagickCoreSignature);
1158  if (IsEventLogging() != MagickFalse)
1159  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1160  if (image->colorspace == CMYKColorspace)
1161  {
1162  if (image->matte == MagickFalse)
1163  return(ColorSeparationType);
1164  return(ColorSeparationMatteType);
1165  }
1166  if (IdentifyImageMonochrome(image,exception) != MagickFalse)
1167  return(BilevelType);
1168  if (IdentifyImageGray(image,exception) != UndefinedType)
1169  {
1170  if (image->matte != MagickFalse)
1171  return(GrayscaleMatteType);
1172  return(GrayscaleType);
1173  }
1174  if (IdentifyPaletteImage(image,exception) != MagickFalse)
1175  {
1176  if (image->matte != MagickFalse)
1177  return(PaletteMatteType);
1178  return(PaletteType);
1179  }
1180  if (image->matte != MagickFalse)
1181  return(TrueColorMatteType);
1182  return(TrueColorType);
1183 }
1184 
1185 /*
1186 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1187 % %
1188 % %
1189 % %
1190 % I s G r a y I m a g e %
1191 % %
1192 % %
1193 % %
1194 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1195 %
1196 % IsGrayImage() returns MagickTrue if the type of the image is grayscale or
1197 % bi-level.
1198 %
1199 % The format of the IsGrayImage method is:
1200 %
1201 % MagickBooleanType IsGrayImage(const Image *image,
1202 % ExceptionInfo *exception)
1203 %
1204 % A description of each parameter follows:
1205 %
1206 % o image: the image.
1207 %
1208 % o exception: return any errors or warnings in this structure.
1209 %
1210 */
1211 MagickExport MagickBooleanType IsGrayImage(const Image *image,
1212  ExceptionInfo *magick_unused(exception))
1213 {
1214  assert(image != (Image *) NULL);
1215  assert(image->signature == MagickCoreSignature);
1216  magick_unreferenced(exception);
1217  if ((image->type == BilevelType) || (image->type == GrayscaleType) ||
1218  (image->type == GrayscaleMatteType))
1219  return(MagickTrue);
1220  return(MagickFalse);
1221 }
1222 
1223 /*
1224 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1225 % %
1226 % %
1227 % %
1228 % I s M o n o c h r o m e I m a g e %
1229 % %
1230 % %
1231 % %
1232 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1233 %
1234 % IsMonochromeImage() returns MagickTrue if type of the image is bi-level.
1235 %
1236 % The format of the IsMonochromeImage method is:
1237 %
1238 % MagickBooleanType IsMonochromeImage(const Image *image,
1239 % ExceptionInfo *exception)
1240 %
1241 % A description of each parameter follows:
1242 %
1243 % o image: the image.
1244 %
1245 % o exception: return any errors or warnings in this structure.
1246 %
1247 */
1248 MagickExport MagickBooleanType IsMonochromeImage(const Image *image,
1249  ExceptionInfo *magick_unused(exception))
1250 {
1251  assert(image != (Image *) NULL);
1252  assert(image->signature == MagickCoreSignature);
1253  magick_unreferenced(exception);
1254  if (image->type == BilevelType)
1255  return(MagickTrue);
1256  return(MagickFalse);
1257 }
1258 
1259 /*
1260 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1261 % %
1262 % %
1263 % %
1264 % I s O p a q u e I m a g e %
1265 % %
1266 % %
1267 % %
1268 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1269 %
1270 % IsOpaqueImage() returns MagickTrue if none of the pixels in the image have
1271 % an opacity value other than opaque (0).
1272 %
1273 % The format of the IsOpaqueImage method is:
1274 %
1275 % MagickBooleanType IsOpaqueImage(const Image *image,
1276 % ExceptionInfo *exception)
1277 %
1278 % A description of each parameter follows:
1279 %
1280 % o image: the image.
1281 %
1282 % o exception: return any errors or warnings in this structure.
1283 %
1284 */
1285 MagickExport MagickBooleanType IsOpaqueImage(const Image *image,
1286  ExceptionInfo *exception)
1287 {
1288  CacheView
1289  *image_view;
1290 
1291  MagickBooleanType
1292  opaque = MagickTrue;
1293 
1294  ssize_t
1295  y;
1296 
1297  /*
1298  Determine if image is opaque.
1299  */
1300  assert(image != (Image *) NULL);
1301  assert(image->signature == MagickCoreSignature);
1302  if (IsEventLogging() != MagickFalse)
1303  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1304  if (image->matte == MagickFalse)
1305  return(MagickTrue);
1306  image_view=AcquireVirtualCacheView(image,exception);
1307 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1308  #pragma omp parallel for schedule(static) shared(opaque) \
1309  magick_number_threads(image,image,image->rows,2)
1310 #endif
1311  for (y=0; y < (ssize_t) image->rows; y++)
1312  {
1313  const PixelPacket
1314  *p;
1315 
1316  ssize_t
1317  x;
1318 
1319  if (opaque == MagickFalse)
1320  continue;
1321  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1322  if (p == (const PixelPacket *) NULL)
1323  {
1324  opaque=MagickFalse;
1325  continue;
1326  }
1327  for (x=0; x < (ssize_t) image->columns; x++)
1328  {
1329  if (GetPixelOpacity(p) != OpaqueOpacity)
1330  {
1331  opaque=MagickFalse;
1332  break;
1333  }
1334  p++;
1335  }
1336  }
1337  image_view=DestroyCacheView(image_view);
1338  return(opaque);
1339 }
1340 
1341 /*
1342 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1343 % %
1344 % %
1345 % %
1346 % S e t I m a g e C h a n n e l D e p t h %
1347 % %
1348 % %
1349 % %
1350 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1351 %
1352 % SetImageChannelDepth() sets the depth of the image.
1353 %
1354 % The format of the SetImageChannelDepth method is:
1355 %
1356 % MagickBooleanType SetImageDepth(Image *image,const size_t depth)
1357 % MagickBooleanType SetImageChannelDepth(Image *image,
1358 % const ChannelType channel,const size_t depth)
1359 %
1360 % A description of each parameter follows:
1361 %
1362 % o image: the image.
1363 %
1364 % o channel: the channel.
1365 %
1366 % o depth: the image depth.
1367 %
1368 */
1369 
1370 MagickExport MagickBooleanType SetImageDepth(Image *image,
1371  const size_t depth)
1372 {
1373  return(SetImageChannelDepth(image,CompositeChannels,depth));
1374 }
1375 
1376 MagickExport MagickBooleanType SetImageChannelDepth(Image *image,
1377  const ChannelType channel,const size_t depth)
1378 {
1379  CacheView
1380  *image_view;
1381 
1383  *exception;
1384 
1385  MagickBooleanType
1386  status;
1387 
1388  QuantumAny
1389  range;
1390 
1391  ssize_t
1392  y;
1393 
1394  assert(image != (Image *) NULL);
1395  if (IsEventLogging() != MagickFalse)
1396  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1397  assert(image->signature == MagickCoreSignature);
1398  if (depth >= MAGICKCORE_QUANTUM_DEPTH)
1399  {
1400  image->depth=depth;
1401  return(MagickTrue);
1402  }
1403  range=GetQuantumRange(depth);
1404  if (image->storage_class == PseudoClass)
1405  {
1406  ssize_t
1407  i;
1408 
1409 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1410  #pragma omp parallel for schedule(static) shared(status) \
1411  magick_number_threads(image,image,image->rows,1)
1412 #endif
1413  for (i=0; i < (ssize_t) image->colors; i++)
1414  {
1415  if ((channel & RedChannel) != 0)
1416  image->colormap[i].red=ScaleAnyToQuantum(ScaleQuantumToAny(
1417  ClampPixel((MagickRealType) image->colormap[i].red),range),range);
1418  if ((channel & GreenChannel) != 0)
1419  image->colormap[i].green=ScaleAnyToQuantum(ScaleQuantumToAny(
1420  ClampPixel((MagickRealType) image->colormap[i].green),range),range);
1421  if ((channel & BlueChannel) != 0)
1422  image->colormap[i].blue=ScaleAnyToQuantum(ScaleQuantumToAny(
1423  ClampPixel((MagickRealType) image->colormap[i].blue),range),range);
1424  if ((channel & OpacityChannel) != 0)
1425  image->colormap[i].opacity=ScaleAnyToQuantum(ScaleQuantumToAny(
1426  ClampPixel((MagickRealType) image->colormap[i].opacity),range),
1427  range);
1428  }
1429  }
1430  status=MagickTrue;
1431  exception=(&image->exception);
1432  image_view=AcquireAuthenticCacheView(image,exception);
1433 #if !defined(MAGICKCORE_HDRI_SUPPORT)
1434 DisableMSCWarning(4127)
1435  if (1UL*QuantumRange <= MaxMap)
1436 RestoreMSCWarning
1437  {
1438  Quantum
1439  *depth_map;
1440 
1441  ssize_t
1442  i;
1443 
1444  /*
1445  Scale pixels to desired (optimized with depth map).
1446  */
1447  depth_map=(Quantum *) AcquireQuantumMemory(MaxMap+1,sizeof(*depth_map));
1448  if (depth_map == (Quantum *) NULL)
1449  ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
1450  for (i=0; i <= (ssize_t) MaxMap; i++)
1451  depth_map[i]=ScaleAnyToQuantum(ScaleQuantumToAny((Quantum) i,range),
1452  range);
1453 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1454  #pragma omp parallel for schedule(static) shared(status) \
1455  magick_number_threads(image,image,image->rows,2)
1456 #endif
1457  for (y=0; y < (ssize_t) image->rows; y++)
1458  {
1459  PixelPacket
1460  *magick_restrict q;
1461 
1462  ssize_t
1463  x;
1464 
1465  if (status == MagickFalse)
1466  continue;
1467  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1468  exception);
1469  if (q == (PixelPacket *) NULL)
1470  {
1471  status=MagickFalse;
1472  continue;
1473  }
1474  for (x=0; x < (ssize_t) image->columns; x++)
1475  {
1476  if ((channel & RedChannel) != 0)
1477  SetPixelRed(q,depth_map[ScaleQuantumToMap(GetPixelRed(q))]);
1478  if ((channel & GreenChannel) != 0)
1479  SetPixelGreen(q,depth_map[ScaleQuantumToMap(GetPixelGreen(q))]);
1480  if ((channel & BlueChannel) != 0)
1481  SetPixelBlue(q,depth_map[ScaleQuantumToMap(GetPixelBlue(q))]);
1482  if (((channel & OpacityChannel) != 0) &&
1483  (image->matte != MagickFalse))
1484  SetPixelOpacity(q,depth_map[ScaleQuantumToMap(GetPixelOpacity(q))]);
1485  q++;
1486  }
1487  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1488  {
1489  status=MagickFalse;
1490  continue;
1491  }
1492  }
1493  image_view=DestroyCacheView(image_view);
1494  depth_map=(Quantum *) RelinquishMagickMemory(depth_map);
1495  if (status != MagickFalse)
1496  image->depth=depth;
1497  return(status);
1498  }
1499 #endif
1500  /*
1501  Scale pixels to desired depth.
1502  */
1503 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1504  #pragma omp parallel for schedule(static) shared(status) \
1505  magick_number_threads(image,image,image->rows,2)
1506 #endif
1507  for (y=0; y < (ssize_t) image->rows; y++)
1508  {
1509  PixelPacket
1510  *magick_restrict q;
1511 
1512  ssize_t
1513  x;
1514 
1515  if (status == MagickFalse)
1516  continue;
1517  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception);
1518  if (q == (PixelPacket *) NULL)
1519  {
1520  status=MagickFalse;
1521  continue;
1522  }
1523  for (x=0; x < (ssize_t) image->columns; x++)
1524  {
1525  if ((channel & RedChannel) != 0)
1526  SetPixelRed(q,ScaleAnyToQuantum(ScaleQuantumToAny(ClampPixel(
1527  (MagickRealType) GetPixelRed(q)),range),range));
1528  if ((channel & GreenChannel) != 0)
1529  SetPixelGreen(q,ScaleAnyToQuantum(ScaleQuantumToAny(ClampPixel(
1530  (MagickRealType) GetPixelGreen(q)),range),range));
1531  if ((channel & BlueChannel) != 0)
1532  SetPixelBlue(q,ScaleAnyToQuantum(ScaleQuantumToAny(ClampPixel(
1533  (MagickRealType) GetPixelBlue(q)),range),range));
1534  if (((channel & OpacityChannel) != 0) && (image->matte != MagickFalse))
1535  SetPixelOpacity(q,ScaleAnyToQuantum(ScaleQuantumToAny(ClampPixel(
1536  (MagickRealType) GetPixelOpacity(q)),range),range));
1537  q++;
1538  }
1539  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1540  {
1541  status=MagickFalse;
1542  continue;
1543  }
1544  }
1545  image_view=DestroyCacheView(image_view);
1546  if (status != MagickFalse)
1547  image->depth=depth;
1548  return(status);
1549 }
1550 
1551 /*
1552 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1553 % %
1554 % %
1555 % %
1556 % S e t I m a g e T y p e %
1557 % %
1558 % %
1559 % %
1560 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1561 %
1562 % SetImageType() sets the type of image. Choose from these types:
1563 %
1564 % BilevelType, GrayscaleType, GrayscaleMatteType, PaletteType,
1565 % PaletteMatteType, TrueColorType, TrueColorMatteType,
1566 % ColorSeparationType, ColorSeparationMatteType, OptimizeType
1567 %
1568 % The format of the SetImageType method is:
1569 %
1570 % MagickBooleanType SetImageType(Image *image,const ImageType type)
1571 %
1572 % A description of each parameter follows:
1573 %
1574 % o image: the image.
1575 %
1576 % o type: Image type.
1577 %
1578 */
1579 MagickExport MagickBooleanType SetImageType(Image *image,const ImageType type)
1580 {
1581  const char
1582  *artifact;
1583 
1584  ImageInfo
1585  *image_info;
1586 
1587  MagickBooleanType
1588  status;
1589 
1590  QuantizeInfo
1591  *quantize_info;
1592 
1593  assert(image != (Image *) NULL);
1594  if (IsEventLogging() != MagickFalse)
1595  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1596  assert(image->signature == MagickCoreSignature);
1597  status=MagickTrue;
1598  image_info=AcquireImageInfo();
1599  image_info->dither=image->dither;
1600  artifact=GetImageArtifact(image,"dither");
1601  if (artifact != (const char *) NULL)
1602  (void) SetImageOption(image_info,"dither",artifact);
1603  switch (type)
1604  {
1605  case BilevelType:
1606  {
1607  if (IsGrayColorspace(image->colorspace) == MagickFalse)
1608  status=TransformImageColorspace(image,GRAYColorspace);
1609  (void) NormalizeImage(image);
1610  (void) BilevelImage(image,(double) QuantumRange/2.0);
1611  quantize_info=AcquireQuantizeInfo(image_info);
1612  quantize_info->number_colors=2;
1613  quantize_info->colorspace=GRAYColorspace;
1614  status=QuantizeImage(quantize_info,image);
1615  quantize_info=DestroyQuantizeInfo(quantize_info);
1616  image->matte=MagickFalse;
1617  break;
1618  }
1619  case GrayscaleType:
1620  {
1621  if (IsGrayColorspace(image->colorspace) == MagickFalse)
1622  status=TransformImageColorspace(image,GRAYColorspace);
1623  image->matte=MagickFalse;
1624  break;
1625  }
1626  case GrayscaleMatteType:
1627  {
1628  if (IsGrayColorspace(image->colorspace) == MagickFalse)
1629  status=TransformImageColorspace(image,GRAYColorspace);
1630  if (image->matte == MagickFalse)
1631  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1632  break;
1633  }
1634  case PaletteType:
1635  {
1636  if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1637  status=TransformImageColorspace(image,sRGBColorspace);
1638  if ((image->storage_class == DirectClass) || (image->colors > 256))
1639  {
1640  quantize_info=AcquireQuantizeInfo(image_info);
1641  quantize_info->number_colors=256;
1642  status=QuantizeImage(quantize_info,image);
1643  quantize_info=DestroyQuantizeInfo(quantize_info);
1644  }
1645  image->matte=MagickFalse;
1646  break;
1647  }
1648  case PaletteBilevelMatteType:
1649  {
1650  if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1651  status=TransformImageColorspace(image,sRGBColorspace);
1652  if (image->matte == MagickFalse)
1653  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1654  (void) BilevelImageChannel(image,AlphaChannel,(double) QuantumRange/2.0);
1655  quantize_info=AcquireQuantizeInfo(image_info);
1656  status=QuantizeImage(quantize_info,image);
1657  quantize_info=DestroyQuantizeInfo(quantize_info);
1658  break;
1659  }
1660  case PaletteMatteType:
1661  {
1662  if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1663  status=TransformImageColorspace(image,sRGBColorspace);
1664  if (image->matte == MagickFalse)
1665  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1666  quantize_info=AcquireQuantizeInfo(image_info);
1667  quantize_info->colorspace=TransparentColorspace;
1668  status=QuantizeImage(quantize_info,image);
1669  quantize_info=DestroyQuantizeInfo(quantize_info);
1670  break;
1671  }
1672  case TrueColorType:
1673  {
1674  if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1675  status=TransformImageColorspace(image,sRGBColorspace);
1676  if (image->storage_class != DirectClass)
1677  status=SetImageStorageClass(image,DirectClass);
1678  image->matte=MagickFalse;
1679  break;
1680  }
1681  case TrueColorMatteType:
1682  {
1683  if (IssRGBCompatibleColorspace(image->colorspace) == MagickFalse)
1684  status=TransformImageColorspace(image,sRGBColorspace);
1685  if (image->storage_class != DirectClass)
1686  status=SetImageStorageClass(image,DirectClass);
1687  if (image->matte == MagickFalse)
1688  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1689  break;
1690  }
1691  case ColorSeparationType:
1692  {
1693  if (image->colorspace != CMYKColorspace)
1694  status=TransformImageColorspace(image,CMYKColorspace);
1695  if (image->storage_class != DirectClass)
1696  status=SetImageStorageClass(image,DirectClass);
1697  image->matte=MagickFalse;
1698  break;
1699  }
1700  case ColorSeparationMatteType:
1701  {
1702  if (image->colorspace != CMYKColorspace)
1703  status=TransformImageColorspace(image,CMYKColorspace);
1704  if (image->storage_class != DirectClass)
1705  status=SetImageStorageClass(image,DirectClass);
1706  if (image->matte == MagickFalse)
1707  (void) SetImageAlphaChannel(image,OpaqueAlphaChannel);
1708  break;
1709  }
1710  case OptimizeType:
1711  case UndefinedType:
1712  break;
1713  }
1714  image_info=DestroyImageInfo(image_info);
1715  if (status == MagickFalse)
1716  return(MagickFalse);
1717  image->type=type;
1718  return(MagickTrue);
1719 }
Definition: image.h:133