MagickCore 6.9.13
Loading...
Searching...
No Matches
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
127typedef struct _CensusInfo
128{
129 double
130 left,
131 right,
132 top,
133 bottom;
134} CensusInfo;
135
136static 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{
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
243static 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
253static RectangleInfo GetEdgeBoundingBox(const Image *image,
254 ExceptionInfo *exception)
255{
257 *edge_view;
258
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
393MagickExport RectangleInfo GetImageBoundingBox(const Image *image,
394 ExceptionInfo *exception)
395{
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*/
572MagickExport size_t GetImageDepth(const Image *image,ExceptionInfo *exception)
573{
574 return(GetImageChannelDepth(image,CompositeChannels,exception));
575}
576
577MagickExport size_t GetImageChannelDepth(const Image *image,
578 const ChannelType channel,ExceptionInfo *exception)
579{
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)
657DisableMSCWarning(4127)
658 if (1UL*QuantumRange <= MaxMap)
659RestoreMSCWarning
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*/
864MagickExport 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*/
919MagickExport 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*/
974MagickExport ImageType IdentifyImageGray(const Image *image,
975 ExceptionInfo *exception)
976{
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*/
1066MagickExport 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*/
1153MagickExport 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*/
1211MagickExport 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*/
1248MagickExport 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*/
1285MagickExport 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
1370MagickExport MagickBooleanType SetImageDepth(Image *image,
1371 const size_t depth)
1372{
1373 return(SetImageChannelDepth(image,CompositeChannels,depth));
1374}
1375
1376MagickExport 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)
1434DisableMSCWarning(4127)
1435 if (1UL*QuantumRange <= MaxMap)
1436RestoreMSCWarning
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 {
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 {
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*/
1579MagickExport 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
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}