Coverage Report

Created: 2026-09-06 07:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/magick/analyze.c
Line
Count
Source
1
/*
2
% Copyright (C) 2003-2025 GraphicsMagick Group
3
% Copyright (C) 2003 ImageMagick Studio
4
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
5
%
6
% This program is covered by multiple licenses, which are described in
7
% Copyright.txt. You should have received a copy of Copyright.txt with this
8
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9
%
10
% GraphicsMagick Image Analysis Methods
11
%
12
*/
13

14
/*
15
  Include declarations.
16
*/
17
#include "magick/studio.h"
18
#include "magick/analyze.h"
19
#include "magick/color.h"
20
#include "magick/log.h"
21
#include "magick/monitor.h"
22
#include "magick/pixel_cache.h"
23
#include "magick/pixel_iterator.h"
24
#include "magick/utility.h"
25

26
/*
27
  Constant declaration.
28
*/
29

30
/*
31
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
32
%                                                                             %
33
%                                                                             %
34
%                                                                             %
35
+   G e t I m a g e B o u n d i n g B o x                                     %
36
%                                                                             %
37
%                                                                             %
38
%                                                                             %
39
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40
%
41
%  Method GetImageBoundingBox returns the bounding box of an image canvas.
42
%  If the image has an opacity channel then return a bounding box based
43
%  only on the opacity channel, otherwise return the bounding box of the
44
%  image based on the current image fuzz setting.
45
%
46
%  The format of the GetImageBoundingBox method is:
47
%
48
%      RectangleInfo GetImageBoundingBox(const Image *image,
49
%        ExceptionInfo *exception)
50
%
51
%  A description of each parameter follows:
52
%
53
%    o bounds: Method GetImageBoundingBox returns the bounding box of an
54
%      image canvas.
55
%
56
%    o image: The image.
57
%
58
%    o exception: Return any errors or warnings in this structure.
59
%
60
%
61
*/
62
0
#define GetImageBoundingBoxText "[%s] Get bounding box..."
63
MagickExport RectangleInfo GetImageBoundingBox(const Image *image,
64
                                               ExceptionInfo *exception)
65
0
{
66
0
  MagickPassFail
67
0
    status=MagickPass;
68
69
0
  long
70
0
    y;
71
72
0
  unsigned long
73
0
    row_count=0;
74
75
0
  MagickBool
76
0
    monitor_active;
77
78
0
  PixelPacket
79
0
    corners[3];
80
81
0
  RectangleInfo
82
0
    bounds;
83
84
0
  assert(image != (Image *) NULL);
85
0
  assert(image->signature == MagickSignature);
86
87
0
  monitor_active=MagickMonitorActive();
88
0
  bounds.width=0;
89
0
  bounds.height=0;
90
0
  bounds.x=(long) image->columns;
91
0
  bounds.y=(long) image->rows;
92
93
0
  if ((image->columns == 0) || (image->rows == 0))
94
0
    return(bounds);
95
96
0
  (void) AcquireOnePixelByReference(image,&corners[0],0,0,exception);
97
0
  (void) AcquireOnePixelByReference(image,&corners[1],(long) image->columns-1,0,exception);
98
0
  (void) AcquireOnePixelByReference(image,&corners[2],0,(long) image->rows-1,exception);
99
#if defined(HAVE_OPENMP)
100
#  pragma omp parallel for schedule(static,4) shared(bounds, row_count, status)
101
#endif
102
0
  for (y=0; y < (long) image->rows; y++)
103
0
    {
104
0
      register const PixelPacket
105
0
        * restrict p;
106
107
0
      register long
108
0
        x;
109
110
0
      RectangleInfo
111
0
        thread_bounds;
112
113
0
      MagickPassFail
114
0
        thread_status;
115
116
#if defined(HAVE_OPENMP)
117
#  pragma omp critical (GM_GetImageBoundingBox)
118
#endif
119
0
      {
120
0
        thread_status=status;
121
0
        thread_bounds=bounds;
122
0
      }
123
0
      if (thread_status == MagickFail)
124
0
        continue;
125
126
0
      p=AcquireImagePixels(image,0,y,image->columns,1,exception);
127
0
      if (p == (const PixelPacket *) NULL)
128
0
        thread_status=MagickFail;
129
0
      if (thread_status != MagickFail)
130
0
        {
131
0
          if ((image->matte) &&
132
0
              (corners[0].opacity != OpaqueOpacity) &&
133
0
              (corners[0].opacity == corners[1].opacity) &&
134
0
              (corners[1].opacity == corners[2].opacity))
135
            /*
136
              Consider only the opacity channel. Not currently fuzzy
137
              so only applied for simple transparency.
138
            */
139
0
            for (x=0; x < (long) image->columns; x++)
140
0
              {
141
0
                if (p->opacity != corners[0].opacity)
142
0
                  if (x < thread_bounds.x)
143
0
                    thread_bounds.x=x;
144
0
                if (p->opacity != corners[1].opacity)
145
0
                  if (x > (long) thread_bounds.width)
146
0
                    thread_bounds.width=x;
147
0
                if (p->opacity != corners[0].opacity)
148
0
                  if (y < thread_bounds.y)
149
0
                    thread_bounds.y=y;
150
0
                if (p->opacity != corners[2].opacity)
151
0
                  if (y > (long) thread_bounds.height)
152
0
                    thread_bounds.height=y;
153
0
                p++;
154
0
              }
155
0
          else if (image->fuzz <= MagickEpsilon)
156
0
            {
157
              /*
158
                Consider only the RGB channels using absolute comparison
159
              */
160
0
              for (x=0; x < (long) image->columns; x++)
161
0
                {
162
0
                  if (!ColorMatch(p,&corners[0]))
163
0
                    if (x < thread_bounds.x)
164
0
                      thread_bounds.x=x;
165
0
                  if (!ColorMatch(p,&corners[1]))
166
0
                    if (x > (long) thread_bounds.width)
167
0
                      thread_bounds.width=x;
168
0
                  if (!ColorMatch(p,&corners[0]))
169
0
                    if (y < thread_bounds.y)
170
0
                      thread_bounds.y=y;
171
0
                  if (!ColorMatch(p,&corners[2]))
172
0
                    if (y > (long) thread_bounds.height)
173
0
                      thread_bounds.height=y;
174
0
                  p++;
175
0
                }
176
0
            }
177
0
          else
178
            /*
179
              Consider only the RGB channels using fuzzy comparison
180
            */
181
0
            for (x=0; x < (long) image->columns; x++)
182
0
              {
183
0
                if (!FuzzyColorMatch(p,&corners[0],image->fuzz))
184
0
                  if (x < thread_bounds.x)
185
0
                    thread_bounds.x=x;
186
0
                if (!FuzzyColorMatch(p,&corners[1],image->fuzz))
187
0
                  if (x > (long) thread_bounds.width)
188
0
                    thread_bounds.width=x;
189
0
                if (!FuzzyColorMatch(p,&corners[0],image->fuzz))
190
0
                  if (y < thread_bounds.y)
191
0
                    thread_bounds.y=y;
192
0
                if (!FuzzyColorMatch(p,&corners[2],image->fuzz))
193
0
                  if (y > (long) thread_bounds.height)
194
0
                    thread_bounds.height=y;
195
0
                p++;
196
0
              }
197
0
        }
198
199
0
      if (monitor_active)
200
0
        {
201
0
          unsigned long
202
0
            thread_row_count;
203
204
#if defined(HAVE_OPENMP)
205
#  pragma omp atomic
206
#endif
207
0
          row_count++;
208
#if defined(HAVE_OPENMP)
209
#  pragma omp flush (row_count)
210
#endif
211
0
          thread_row_count=row_count;
212
0
          if (QuantumTick(thread_row_count,image->rows))
213
0
            if (!MagickMonitorFormatted(thread_row_count,image->rows,exception,
214
0
                                        GetImageBoundingBoxText,image->filename))
215
0
              thread_status=MagickFail;
216
0
        }
217
218
#if defined(HAVE_OPENMP)
219
#  pragma omp critical (GM_GetImageBoundingBox)
220
#endif
221
0
      {
222
0
        if (thread_bounds.x < bounds.x)
223
0
          bounds.x=thread_bounds.x;
224
0
        if (thread_bounds.y < bounds.y)
225
0
          bounds.y=thread_bounds.y;
226
0
        if (thread_bounds.width > bounds.width)
227
0
          bounds.width=thread_bounds.width;
228
0
        if (thread_bounds.height > bounds.height)
229
0
          bounds.height=thread_bounds.height;
230
0
      }
231
232
0
      if (thread_status == MagickFail)
233
0
        {
234
0
          status=MagickFail;
235
#if defined(HAVE_OPENMP)
236
#  pragma omp flush (status)
237
#endif
238
0
        }
239
0
    }
240
0
  if (bounds.width != 0)
241
0
    bounds.width-=(bounds.x-1);
242
0
  if (bounds.height != 0)
243
0
    bounds.height-=(bounds.y-1);
244
0
  if (bounds.x < 0)
245
0
    bounds.x=0;
246
0
  if (bounds.y < 0)
247
0
    bounds.y=0;
248
  /*
249
    If we fail to find smaller bounds, then return original image
250
    dimensions.
251
  */
252
0
  if ((bounds.width == 0) || (bounds.height == 0))
253
0
    {
254
0
      bounds.width=image->columns;
255
0
      bounds.height=image->rows;
256
0
      bounds.x=0;
257
0
      bounds.y=0;
258
0
    }
259
0
  if (IsEventLogged(TransformEvent))
260
0
    (void) LogMagickEvent(TransformEvent,GetMagickModule(),
261
0
                          "Bounding Box: %lux%lu%+ld%+ld",
262
0
                          bounds.width, bounds.height, bounds.x, bounds.y);
263
264
0
  return(bounds);
265
0
}
266

267
/*
268
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
269
%                                                                             %
270
%                                                                             %
271
%                                                                             %
272
%   G e t I m a g e D e p t h                                                 %
273
%                                                                             %
274
%                                                                             %
275
%                                                                             %
276
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
277
%
278
%  GetImageDepth() returns the minimum bit depth of the image required to
279
%  ensure that data is not lost in the red, green, blue, and opacity, channels.
280
%  Pixel components are stored in a Quantum, which is 8, 16, or 32 bits
281
%  depending on the QuantumDepth value set when the software is compiled.
282
%  GetImageDepth() returns the smallest modulus storage size which supports
283
%  the scale of the pixel within the range (i.e. no information is lost).
284
%  As an example, the value one is returned for a black and white image
285
%  since only one bit of resolution is required to represent a black and white
286
%  image.
287
%
288
%  The format of the GetImageDepth method is:
289
%
290
%      unsigned long GetImageDepth(const Image *image,ExceptionInfo *exception)
291
%
292
%  A description of each parameter follows:
293
%
294
%    o image: The image.
295
%
296
%    o exception: Return any errors or warnings in this structure.
297
%
298
%
299
*/
300
#if MaxMap == MaxRGB
301
static inline unsigned char MinimumDepthForValue(const Quantum quantum)
302
0
{
303
0
  register unsigned int
304
0
    depth,
305
0
    scale;
306
307
0
  for (depth=1 ; depth <= (unsigned int) QuantumDepth; depth++)
308
0
    {
309
0
      scale=MaxRGB / (MaxRGB >> (QuantumDepth-depth));
310
0
      if (quantum == scale*(quantum/scale))
311
0
        break;
312
0
    }
313
314
0
  return (unsigned char) depth;
315
0
}
316
static magick_uint8_t* AllocateDepthMap(void)
317
0
{
318
0
  magick_uint8_t
319
0
    *map;
320
321
0
  map = MagickAllocateResourceLimitedArray(unsigned char *, MaxMap+1, sizeof(magick_uint8_t));
322
0
  if (map != (unsigned char *) NULL)
323
0
    {
324
0
      unsigned int
325
0
        i;
326
327
0
      for (i=0; i <= MaxMap; i++)
328
0
        map[i] = (magick_uint8_t) MinimumDepthForValue(i);
329
0
    }
330
0
  return map;
331
0
}
332
#endif /* MaxMap == MaxRGB */
333
0
#define GetImageDepthText "[%s] Get depth..."
334
335
static MagickPassFail
336
GetImageDepthCallBack(void *mutable_data,          /* User provided mutable data */
337
                      const void *immutable_data,  /* User provided immutable data */
338
                      const Image * restrict image,          /* Input image */
339
                      const PixelPacket * restrict pixels,   /* Pixel row */
340
                      const IndexPacket * restrict indexes,  /* Pixel indexes */
341
                      const long npixels,          /* Number of pixels in row */
342
                      ExceptionInfo * restrict exception     /* Exception report */
343
                      )
344
0
{
345
0
  unsigned int
346
0
    *current_depth=(unsigned int *) mutable_data;
347
348
0
  magick_uint8_t
349
0
    *map = (magick_uint8_t *) immutable_data;
350
351
0
  register unsigned int
352
0
    depth;
353
354
0
  register long
355
0
    i;
356
357
0
  ARG_NOT_USED(indexes);
358
0
  ARG_NOT_USED(exception);
359
360
#if defined(HAVE_OPENMP)
361
#  pragma omp critical (GM_GetImageDepthCallBack)
362
#endif
363
0
  {
364
0
    depth=*current_depth;
365
0
  }
366
367
0
#if MaxMap == MaxRGB
368
0
  if (map)
369
0
    {
370
      /*
371
        Use fast table lookups if we can
372
      */
373
0
      for (i=0; i < npixels; i++)
374
0
        {
375
0
          depth=Max(depth,map[pixels[i].red]);
376
0
          depth=Max(depth,map[pixels[i].green]);
377
0
          depth=Max(depth,map[pixels[i].blue]);
378
0
          if (image->matte)
379
0
            depth=Max(depth,map[pixels[i].opacity]);
380
0
          if (depth == QuantumDepth)
381
0
            break;
382
0
        }
383
0
    }
384
#else
385
    {
386
      /*
387
        Use the slow, sure, way (Q32 only)
388
      */
389
      register unsigned int
390
        scale;
391
392
      ARG_NOT_USED(map);
393
      scale=MaxRGB / (MaxRGB >> (QuantumDepth-depth));
394
      i=0;
395
      while (i < npixels)
396
        {
397
          if ((pixels[i].red != scale*(pixels[i].red/scale)) ||
398
              (pixels[i].green != scale*(pixels[i].green/scale)) ||
399
              (pixels[i].blue != scale*(pixels[i].blue/scale)) ||
400
              (image->matte &&
401
               (pixels[i].opacity != scale*((pixels[i].opacity/scale)))))
402
            {
403
              depth++;
404
              if (depth == QuantumDepth)
405
                break;
406
              scale=MaxRGB / (MaxRGB >> (QuantumDepth-depth));
407
              continue;
408
            }
409
          i++;
410
        }
411
    }
412
#endif
413
414
#if defined(HAVE_OPENMP)
415
#  pragma omp critical (GM_GetImageDepthCallBack)
416
#endif
417
0
  {
418
0
    if (depth > *current_depth)
419
0
      *current_depth=depth;
420
0
  }
421
422
0
  return (depth >= QuantumDepth ? MagickFail : MagickPass);
423
0
}
424
425
MagickExport unsigned long GetImageDepth(const Image *image,
426
                                         ExceptionInfo *exception)
427
9.06k
{
428
9.06k
  magick_uint8_t
429
9.06k
    *map = (magick_uint8_t *) NULL;
430
431
9.06k
  unsigned int
432
9.06k
    depth=1;
433
434
9.06k
  assert(image != (Image *) NULL);
435
9.06k
  assert(image->signature == MagickSignature);
436
437
9.06k
  if (image->is_monochrome)
438
9.06k
    return depth;
439
440
0
#if MaxMap == MaxRGB
441
  /*
442
    Use fast table lookups if we can
443
  */
444
0
  map = AllocateDepthMap();
445
0
#endif
446
0
  if ((image->storage_class == PseudoClass) && !(image->matte))
447
0
    {
448
      /*
449
        PseudoClass
450
      */
451
0
      (void) GetImageDepthCallBack(&depth,map,image,
452
0
                                   image->colormap,
453
0
                                   (IndexPacket *) NULL,
454
0
                                   image->colors,
455
0
                                   exception);
456
0
    }
457
0
  else
458
0
    {
459
      /*
460
        DirectClass.
461
462
        Notice that all pixels in the image must be inspected if the
463
        image depth is less than QuantumDepth.
464
      */
465
466
0
      (void) PixelIterateMonoRead(GetImageDepthCallBack,
467
0
                                  NULL,
468
0
                                  GetImageDepthText,
469
0
                                  &depth,map,0,0,image->columns,
470
0
                                  image->rows,image,exception);
471
0
    }
472
473
0
  MagickFreeResourceLimitedMemory(magick_uint8_t *,map);
474
475
0
  return depth;
476
9.06k
}
477

478
/*
479
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
480
%                                                                             %
481
%                                                                             %
482
%                                                                             %
483
%   G e t I m a g e C h a r a c t e r i s t i c s                             %
484
%                                                                             %
485
%                                                                             %
486
%                                                                             %
487
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
488
%
489
%  GetImageCharacteristics obtains the basic characteristics of the image
490
%  and stores the characterisistics in the user provided
491
%  ImageCharacteristics structure.  If optimize is set to MagickTrue, then
492
%  exhaustive testing of the image pixels is performed (as required).
493
%  MagickPass is returned if this method executes without error.
494
%
495
%  The format of the GetImageCharacteristics method is:
496
%
497
%      MagickPassFail GetImageCharacteristics(const Image *image,
498
%                               ImageCharacteristics *characteristics,
499
%                               MagickBool optimize,
500
%                               ExceptionInfo *exception)
501
%
502
%  A description of each parameter follows:
503
%
504
%    o image: The image.
505
%
506
%    o characteristics: An ImageCharacteristics structure to update.
507
%
508
%    o optimize: Inspect image pixels (if required)
509
%
510
%    o exception: Any errors are reported here.
511
%
512
*/
513
42.5k
#define AnalyzeImageText "[%s] Analyze...  "
514
MagickExport MagickPassFail GetImageCharacteristics(const Image *image,
515
                                                    ImageCharacteristics *characteristics,
516
                                                    const MagickBool optimize,
517
                                                    ExceptionInfo *exception)
518
83.9k
{
519
83.9k
  unsigned long
520
83.9k
    y;
521
522
83.9k
  register const PixelPacket
523
83.9k
    *p;
524
525
83.9k
  register unsigned long
526
83.9k
    x;
527
528
83.9k
  MagickBool
529
83.9k
    broke_loop = MagickFalse;
530
531
83.9k
  MagickPassFail
532
83.9k
    status = MagickPass;
533
534
83.9k
  assert(image != (Image *) NULL);
535
83.9k
  assert(image->signature == MagickSignature);
536
83.9k
  assert(characteristics != (ImageCharacteristics *) NULL);
537
83.9k
  assert(exception != (ExceptionInfo *) NULL);
538
539
83.9k
  characteristics->cmyk = (image->colorspace == CMYKColorspace ? MagickTrue : MagickFalse);
540
83.9k
  characteristics->grayscale = (image->is_grayscale ? MagickTrue : MagickFalse);
541
83.9k
  characteristics->monochrome = (image->is_monochrome ? MagickTrue : MagickFalse);
542
83.9k
  characteristics->opaque = (image->matte ? MagickFalse : MagickTrue);
543
83.9k
  characteristics->palette = (image->storage_class == PseudoClass ? MagickTrue : MagickFalse);
544
545
83.9k
  if ((optimize) && (GetPixelCachePresent(image)))
546
31.3k
    {
547
31.3k
      MagickBool
548
31.3k
        grayscale,
549
31.3k
        monochrome,
550
31.3k
        opaque;
551
552
      /* Predicate to test */
553
31.3k
      grayscale=(image->is_grayscale ? MagickFalse : MagickTrue);
554
31.3k
      monochrome=(image->is_monochrome ? MagickFalse : MagickTrue);
555
31.3k
      opaque=(image->matte ? MagickTrue : MagickFalse);
556
31.3k
      switch (image->storage_class)
557
31.3k
        {
558
1.45k
        case DirectClass:
559
1.45k
        case UndefinedClass:
560
1.45k
          {
561
29.9k
            for (y=0; y < image->rows; y++)
562
29.6k
              {
563
29.6k
                p=AcquireImagePixels(image,0,y,image->columns,1,exception);
564
29.6k
                if (p == (const PixelPacket *) NULL)
565
0
                  {
566
0
                    status = MagickFail;
567
0
                    break;
568
0
                  }
569
20.7M
                for (x=image->columns; x != 0; x--)
570
20.6M
                  {
571
20.6M
                    grayscale = ((grayscale) &&
572
20.2M
                                 (p->red == p->green) && (p->red == p->blue));
573
20.6M
                    monochrome = ((monochrome) && (grayscale) &&
574
1.46M
                                  ((0 == p->red) || (MaxRGB == p->red)));
575
20.6M
                    opaque = ((opaque) &&
576
1.25M
                              (p->opacity == OpaqueOpacity));
577
20.6M
                    if (!grayscale &&
578
440k
                        !monochrome &&
579
440k
                        !opaque)
580
1.15k
                      {
581
1.15k
                        broke_loop=MagickTrue;
582
1.15k
                        break;
583
1.15k
                      }
584
20.6M
                    p++;
585
20.6M
                  }
586
29.6k
                if (!grayscale &&
587
5.06k
                    !monochrome &&
588
5.06k
                    !opaque)
589
1.15k
                  break;
590
28.4k
                if (QuantumTick(y,image->rows))
591
11.4k
                  if (!MagickMonitorFormatted(y,image->rows,exception,
592
11.4k
                                              AnalyzeImageText,image->filename))
593
0
                    break;
594
28.4k
              }
595
1.45k
            break;
596
1.45k
          }
597
29.9k
        case PseudoClass:
598
29.9k
          {
599
29.9k
            p=image->colormap;
600
36.0k
            for (x=image->colors; x != 0; x--)
601
36.0k
              {
602
36.0k
                grayscale = ((grayscale) &&
603
23.6k
                             (p->red == p->green) && (p->red == p->blue));
604
36.0k
                monochrome = ((monochrome) && (grayscale) &&
605
4.41k
                              ((0 == p->red) || (MaxRGB == p->red)));
606
36.0k
                if (!grayscale &&
607
29.9k
                    !monochrome)
608
29.9k
                  {
609
29.9k
                    broke_loop=MagickTrue;
610
29.9k
                    break;
611
29.9k
                  }
612
6.09k
                p++;
613
6.09k
              }
614
29.9k
            if (opaque)
615
41
              {
616
107
                for (y=0; y < image->rows; y++)
617
105
                  {
618
105
                    p=AcquireImagePixels(image,0,y,image->columns,1,exception);
619
105
                    if (p == (const PixelPacket *) NULL)
620
0
                      {
621
0
                        status = MagickFail;
622
0
                        break;
623
0
                      }
624
8.30k
                    for (x=image->columns; x != 0; x--)
625
8.23k
                      {
626
8.23k
                        opaque = ((opaque) &&
627
8.23k
                                  (p->opacity == OpaqueOpacity));
628
8.23k
                        if (!opaque)
629
39
                          {
630
39
                            broke_loop=MagickTrue;
631
39
                            break;
632
39
                          }
633
8.19k
                        p++;
634
8.19k
                      }
635
105
                    if (!opaque)
636
39
                      break;
637
66
                    if (QuantumTick(y,image->rows))
638
66
                      if (!MagickMonitorFormatted(y,image->rows,exception,
639
66
                                                  AnalyzeImageText,image->filename))
640
0
                        break;
641
66
                  }
642
41
              }
643
29.9k
            break;
644
1.45k
          }
645
31.3k
        }
646
31.3k
      if (!characteristics->grayscale)
647
19.0k
        {
648
19.0k
          characteristics->grayscale=grayscale;
649
19.0k
          ((Image *)image)->is_grayscale=grayscale; /* Intentionally ignore const */
650
19.0k
        }
651
31.3k
      if (!characteristics->monochrome)
652
23.6k
        {
653
23.6k
          characteristics->monochrome=monochrome;
654
23.6k
          ((Image *)image)->is_monochrome=monochrome; /* Intentionally ignore const */
655
23.6k
        }
656
31.3k
      if (!characteristics->opaque)
657
1.16k
        characteristics->opaque=opaque;
658
31.3k
    }
659
660
  /*
661
    Force progress indication to 100%
662
  */
663
83.9k
  if (broke_loop)
664
31.0k
    (void) MagickMonitorFormatted(image->rows-1,image->rows,exception,
665
31.0k
                                  AnalyzeImageText,image->filename);
666
/*   printf("status=%s, cmyk=%u, grayscale=%u, monochrome=%u, opaque=%u, palette=%u\n", */
667
/*          (status == MagickFail ? "Fail" : "Pass"),characteristics->cmyk,characteristics->grayscale, */
668
/*          characteristics->monochrome,characteristics->opaque,characteristics->palette); */
669
670
83.9k
  return status;
671
83.9k
}
672

673
/*
674
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
675
%                                                                             %
676
%                                                                             %
677
%                                                                             %
678
%   G e t I m a g e T y p e                                                   %
679
%                                                                             %
680
%                                                                             %
681
%                                                                             %
682
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
683
%
684
%  GetImageType() returns the type of image:
685
%
686
%        Bilevel        Grayscale       GrayscaleMatte
687
%        Palette        PaletteMatte    TrueColor
688
%        TrueColorMatte ColorSeparation ColorSeparationMatte
689
%
690
%
691
%  The format of the GetImageType method is:
692
%
693
%      ImageType GetImageType(const Image *image,ExceptionInfo *exception)
694
%
695
%  A description of each parameter follows:
696
%
697
%    o image: The image.
698
%
699
%    o exception: Return any errors or warnings in this structure.
700
%
701
%
702
*/
703
MagickExport ImageType
704
GetImageType(const Image *image,ExceptionInfo *exception)
705
574
{
706
574
  ImageCharacteristics
707
574
    characteristics;
708
709
574
  ImageType
710
574
    image_type;
711
712
574
  assert(image != (Image *) NULL);
713
574
  assert(image->signature == MagickSignature);
714
715
574
  image_type=UndefinedType;
716
574
  if (GetImageCharacteristics(image,&characteristics,MagickTrue,exception))
717
574
    {
718
574
      if (characteristics.cmyk)
719
0
        image_type=(characteristics.opaque ? ColorSeparationType : ColorSeparationMatteType);
720
574
      else if (characteristics.monochrome)
721
131
        image_type=BilevelType;
722
443
      else if (characteristics.grayscale)
723
443
        image_type=(characteristics.opaque ? GrayscaleType : GrayscaleMatteType);
724
0
      else if (characteristics.palette)
725
0
        image_type=(characteristics.opaque ? PaletteType : PaletteMatteType);
726
0
      else
727
0
        image_type=(characteristics.opaque ? TrueColorType : TrueColorMatteType);
728
574
    }
729
574
  return image_type;
730
574
}
731

732
/*
733
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
734
%                                                                             %
735
%                                                                             %
736
%                                                                             %
737
%     I s G r a y I m a g e                                                   %
738
%                                                                             %
739
%                                                                             %
740
%                                                                             %
741
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
742
%
743
%  IsGrayImage() returns MagickTrue if all the pixels in the image have the same
744
%  red, green, and blue intensities.
745
%
746
%  The format of the IsGrayImage method is:
747
%
748
%      MagickBool IsGrayImage(const Image *image,ExceptionInfo *exception)
749
%
750
%  A description of each parameter follows:
751
%
752
%    o status: Method IsGrayImage returns MagickTrue if the image is grayscale
753
%      otherwise MagickFalse is returned.
754
%
755
%    o image: The image.
756
%
757
%    o exception: Return any errors or warnings in this structure.
758
%
759
%
760
*/
761
64.0k
#define AnalyzeGrayImageText "[%s] Analyze for gray..."
762
MagickExport MagickBool IsGrayImage(const Image *image,
763
  ExceptionInfo *exception)
764
81.1k
{
765
81.1k
  unsigned long
766
81.1k
    y;
767
768
81.1k
  register const PixelPacket
769
81.1k
    *p;
770
771
81.1k
  register unsigned long
772
81.1k
    x;
773
774
81.1k
  MagickBool
775
81.1k
    is_grayscale;
776
777
81.1k
  assert(image != (Image *) NULL);
778
81.1k
  assert(image->signature == MagickSignature);
779
81.1k
  assert(exception != (ExceptionInfo *) NULL);
780
81.1k
  if (image->colorspace == CMYKColorspace)
781
18
    return(MagickFalse);
782
81.1k
  if (image->is_grayscale)
783
30.6k
    return(MagickTrue);
784
50.4k
  is_grayscale=MagickTrue;
785
50.4k
  switch (image->storage_class)
786
50.4k
  {
787
22.1k
    case DirectClass:
788
22.1k
    case UndefinedClass:
789
22.1k
    {
790
22.1k
      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
791
22.1k
                            "IsGrayImage(): Exhaustive pixel test!");
792
120k
      for (y=0; y < image->rows; y++)
793
115k
      {
794
115k
        p=AcquireImagePixels(image,0,y,image->columns,1,exception);
795
115k
        if (p == (const PixelPacket *) NULL)
796
61
          return(MagickFalse);
797
14.4M
        for (x=image->columns; x != 0; x--)
798
14.3M
        {
799
14.3M
          if ((p->red != p->green) || (p->green != p->blue))
800
17.3k
            {
801
17.3k
              is_grayscale=MagickFalse;
802
17.3k
              break;
803
17.3k
            }
804
14.3M
          p++;
805
14.3M
        }
806
115k
        if (!is_grayscale)
807
17.3k
          break;
808
98.1k
        if (QuantumTick(y,image->rows))
809
35.9k
          if (!MagickMonitorFormatted(y,image->rows,
810
35.9k
                                      exception,AnalyzeGrayImageText,
811
35.9k
                                      image->filename))
812
0
            break;
813
98.1k
      }
814
22.1k
      break;
815
22.1k
    }
816
28.2k
    case PseudoClass:
817
28.2k
    {
818
28.2k
      p=image->colormap;
819
52.9M
      for (x=image->colors; x != 0; x--)
820
52.9M
        {
821
52.9M
          if ((p->red != p->green) || (p->green != p->blue))
822
10.8k
            {
823
10.8k
              is_grayscale=MagickFalse;
824
10.8k
              break;
825
10.8k
            }
826
52.9M
          p++;
827
52.9M
        }
828
28.2k
      break;
829
22.1k
    }
830
50.4k
  }
831
832
  /*
833
    Force progress indication to 100%
834
  */
835
50.3k
  if (!is_grayscale)
836
28.1k
    (void) MagickMonitorFormatted(image->rows-1,image->rows,exception,
837
28.1k
                                  AnalyzeGrayImageText,image->filename);
838
839
50.3k
  ((Image *)image)->is_grayscale=is_grayscale;
840
50.3k
  return(is_grayscale);
841
50.4k
}
842

843
/*
844
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
845
%                                                                             %
846
%                                                                             %
847
%                                                                             %
848
%   I s M o n o c h r o m e I m a g e                                         %
849
%                                                                             %
850
%                                                                             %
851
%                                                                             %
852
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
853
%
854
%  IsMonochromeImage() returns MagickTrue if all the pixels in the image have
855
%  the same red, green, and blue intensities and the intensity is either
856
%  0 or MaxRGB.
857
%
858
%  The format of the IsMonochromeImage method is:
859
%
860
%      MagickBool IsMonochromeImage(const Image *image,
861
%        ExceptionInfo *exception)
862
%
863
%  A description of each parameter follows:
864
%
865
%    o image: The image.
866
%
867
%    o exception: Return any errors or warnings in this structure.
868
%
869
%
870
*/
871
51.5k
#define AnalyzeBilevelImageText "[%s] Analyze for bilevel..."
872
MagickExport MagickBool IsMonochromeImage(const Image *image,
873
  ExceptionInfo *exception)
874
68.4k
{
875
68.4k
  unsigned long
876
68.4k
    y;
877
878
68.4k
  register const PixelPacket
879
68.4k
    *p;
880
881
68.4k
  register unsigned long
882
68.4k
    x;
883
884
68.4k
  MagickBool
885
68.4k
    is_monochrome;
886
887
68.4k
  assert(image != (Image *) NULL);
888
68.4k
  assert(image->signature == MagickSignature);
889
68.4k
  assert(exception != (ExceptionInfo *) NULL);
890
68.4k
  if (image->colorspace == CMYKColorspace)
891
342
    return(MagickFalse);
892
68.0k
  if (image->is_monochrome)
893
3.67k
    return(MagickTrue);
894
64.4k
  is_monochrome=MagickTrue;
895
64.4k
  switch (image->storage_class)
896
64.4k
  {
897
0
    case DirectClass:
898
0
    case UndefinedClass:
899
0
    {
900
0
      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
901
0
                            "IsMonochromeImage(): Exhaustive pixel test!");
902
0
      for (y=0; y < image->rows; y++)
903
0
      {
904
0
        p=AcquireImagePixels(image,0,y,image->columns,1,exception);
905
0
        if (p == (const PixelPacket *) NULL)
906
0
          return(MagickFalse);
907
0
        for (x=image->columns; x != 0; x--)
908
0
        {
909
0
          if ((p->red != p->green) || (p->green != p->blue) ||
910
0
              ((p->red != 0) && (p->red != MaxRGB)))
911
0
            {
912
0
              is_monochrome=MagickFalse;
913
0
              break;
914
0
            }
915
0
          p++;
916
0
        }
917
0
        if (!is_monochrome)
918
0
          break;
919
0
        if (QuantumTick(y,image->rows))
920
0
          if (!MagickMonitorFormatted(y,image->rows,exception,
921
0
                                      AnalyzeBilevelImageText,image->filename))
922
0
            break;
923
0
      }
924
0
      break;
925
0
    }
926
64.4k
    case PseudoClass:
927
64.4k
    {
928
64.4k
      p=image->colormap;
929
296k
      for (x=image->colors; x != 0; x--)
930
284k
      {
931
284k
        if ((p->red != p->green) || (p->green != p->blue) ||
932
276k
            ((p->red != 0) && (p->red != MaxRGB)))
933
51.5k
          {
934
51.5k
            is_monochrome=MagickFalse;
935
51.5k
            break;
936
51.5k
          }
937
232k
        p++;
938
232k
      }
939
64.4k
      break;
940
0
    }
941
64.4k
  }
942
943
  /*
944
    Force progress indication to 100%
945
  */
946
64.4k
  if (!is_monochrome)
947
51.5k
    (void) MagickMonitorFormatted(image->rows-1,image->rows,exception,
948
51.5k
                                  AnalyzeBilevelImageText,image->filename);
949
950
64.4k
  ((Image *)image)->is_monochrome=is_monochrome;
951
64.4k
  return(is_monochrome);
952
64.4k
}
953

954
/*
955
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
956
%                                                                             %
957
%                                                                             %
958
%                                                                             %
959
%     I s O p a q u e I m a g e                                               %
960
%                                                                             %
961
%                                                                             %
962
%                                                                             %
963
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
964
%
965
%  IsOpaqueImage() returns MagickTrue if none of the pixels in the image have an
966
%  opacity value other than opaque (0).
967
%
968
%  The format of the IsOpaqueImage method is:
969
%
970
%      MagickBool IsOpaqueImage(const Image *image,ExceptionInfo *exception)
971
%
972
%  A description of each parameter follows:
973
%
974
%    o status: Method IsOpaqueImage returns MagickFalse if the image has one or more
975
%      pixels that are transparent otherwise MagickTrue is returned.
976
%
977
%    o image: The image.
978
%
979
%    o exception: Return any errors or warnings in this structure.
980
%
981
%
982
*/
983
0
#define AnalyzeOpaqueImageText "[%s] Analyze for opacity..."
984
MagickExport MagickBool IsOpaqueImage(const Image *image,
985
  ExceptionInfo *exception)
986
0
{
987
0
  unsigned long
988
0
    y;
989
990
0
  register const PixelPacket
991
0
    *p;
992
993
0
  register unsigned long
994
0
    x;
995
996
0
  MagickBool
997
0
    is_opaque;
998
999
  /*
1000
    Determine if image is opaque.
1001
  */
1002
0
  assert(image != (Image *) NULL);
1003
0
  assert(image->signature == MagickSignature);
1004
0
  if (!image->matte)
1005
0
    return(MagickTrue);
1006
0
  is_opaque=MagickTrue;
1007
0
  (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1008
0
                        "IsOpaqueImage(): Exhaustive pixel test!");
1009
0
  for (y=0; y < image->rows; y++)
1010
0
    {
1011
0
      p=AcquireImagePixels(image,0,y,image->columns,1,exception);
1012
0
      if (p == (const PixelPacket *) NULL)
1013
0
        return(MagickFalse);
1014
0
      for (x=image->columns; x > 0; x--)
1015
0
        {
1016
0
          if (p->opacity != OpaqueOpacity)
1017
0
            {
1018
0
              is_opaque=MagickFalse;
1019
0
              break;
1020
0
            }
1021
0
          p++;
1022
0
        }
1023
0
      if (!is_opaque)
1024
0
        break;
1025
0
      if (QuantumTick(y,image->rows))
1026
0
        if (!MagickMonitorFormatted(y,image->rows,exception,
1027
0
                                    AnalyzeOpaqueImageText,image->filename))
1028
0
          break;
1029
0
    }
1030
1031
  /*
1032
    Force progress indication to 100%
1033
  */
1034
0
  if (!is_opaque)
1035
0
    (void) MagickMonitorFormatted(image->rows-1,image->rows,exception,
1036
0
                                  AnalyzeOpaqueImageText,image->filename);
1037
1038
0
  return(is_opaque);
1039
0
}