Coverage Report

Created: 2025-07-23 08:18

/src/graphicsmagick/magick/analyze.c
Line
Count
Source (jump to first uncovered line)
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
3.41k
{
428
3.41k
  magick_uint8_t
429
3.41k
    *map = (magick_uint8_t *) NULL;
430
431
3.41k
  unsigned int
432
3.41k
    depth=1;
433
434
3.41k
  assert(image != (Image *) NULL);
435
3.41k
  assert(image->signature == MagickSignature);
436
437
3.41k
  if (image->is_monochrome)
438
3.41k
    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(map);
474
475
0
  return depth;
476
3.41k
}
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
530
#define AnalyzeImageText "[%s] Analyze...  "
514
MagickExport MagickPassFail GetImageCharacteristics(const Image *image,
515
                                                    ImageCharacteristics *characteristics,
516
                                                    const MagickBool optimize,
517
                                                    ExceptionInfo *exception)
518
84.4k
{
519
84.4k
  unsigned long
520
84.4k
    y;
521
522
84.4k
  register const PixelPacket
523
84.4k
    *p;
524
525
84.4k
  register unsigned long
526
84.4k
    x;
527
528
84.4k
  MagickBool
529
84.4k
    broke_loop = MagickFalse;
530
531
84.4k
  MagickPassFail
532
84.4k
    status = MagickPass;
533
534
84.4k
  assert(image != (Image *) NULL);
535
84.4k
  assert(image->signature == MagickSignature);
536
84.4k
  assert(characteristics != (ImageCharacteristics *) NULL);
537
84.4k
  assert(exception != (ExceptionInfo *) NULL);
538
539
84.4k
  characteristics->cmyk = (image->colorspace == CMYKColorspace ? MagickTrue : MagickFalse);
540
84.4k
  characteristics->grayscale = (image->is_grayscale ? MagickTrue : MagickFalse);
541
84.4k
  characteristics->monochrome = (image->is_monochrome ? MagickTrue : MagickFalse);
542
84.4k
  characteristics->opaque = (image->matte ? MagickFalse : MagickTrue);
543
84.4k
  characteristics->palette = (image->storage_class == PseudoClass ? MagickTrue : MagickFalse);
544
545
84.4k
  if ((optimize) && (GetPixelCachePresent(image)))
546
530
    {
547
530
      MagickBool
548
530
        grayscale,
549
530
        monochrome,
550
530
        opaque;
551
552
      /* Predicate to test */
553
530
      grayscale=(image->is_grayscale ? MagickFalse : MagickTrue);
554
530
      monochrome=(image->is_monochrome ? MagickFalse : MagickTrue);
555
530
      opaque=(image->matte ? MagickTrue : MagickFalse);
556
530
      switch (image->storage_class)
557
530
        {
558
0
        case DirectClass:
559
0
        case UndefinedClass:
560
0
          {
561
0
            for (y=0; y < image->rows; y++)
562
0
              {
563
0
                p=AcquireImagePixels(image,0,y,image->columns,1,exception);
564
0
                if (p == (const PixelPacket *) NULL)
565
0
                  {
566
0
                    status = MagickFail;
567
0
                    break;
568
0
                  }
569
0
                for (x=image->columns; x != 0; x--)
570
0
                  {
571
0
                    grayscale = ((grayscale) &&
572
0
                                 (p->red == p->green) && (p->red == p->blue));
573
0
                    monochrome = ((monochrome) && (grayscale) &&
574
0
                                  ((0 == p->red) || (MaxRGB == p->red)));
575
0
                    opaque = ((opaque) &&
576
0
                              (p->opacity == OpaqueOpacity));
577
0
                    if (!grayscale &&
578
0
                        !monochrome &&
579
0
                        !opaque)
580
0
                      {
581
0
                        broke_loop=MagickTrue;
582
0
                        break;
583
0
                      }
584
0
                    p++;
585
0
                  }
586
0
                if (!grayscale &&
587
0
                    !monochrome &&
588
0
                    !opaque)
589
0
                  break;
590
0
                if (QuantumTick(y,image->rows))
591
0
                  if (!MagickMonitorFormatted(y,image->rows,exception,
592
0
                                              AnalyzeImageText,image->filename))
593
0
                    break;
594
0
              }
595
0
            break;
596
0
          }
597
530
        case PseudoClass:
598
530
          {
599
530
            p=image->colormap;
600
530
            for (x=image->colors; x != 0; x--)
601
530
              {
602
530
                grayscale = ((grayscale) &&
603
530
                             (p->red == p->green) && (p->red == p->blue));
604
530
                monochrome = ((monochrome) && (grayscale) &&
605
530
                              ((0 == p->red) || (MaxRGB == p->red)));
606
530
                if (!grayscale &&
607
530
                    !monochrome)
608
530
                  {
609
530
                    broke_loop=MagickTrue;
610
530
                    break;
611
530
                  }
612
0
                p++;
613
0
              }
614
530
            if (opaque)
615
0
              {
616
0
                for (y=0; y < image->rows; y++)
617
0
                  {
618
0
                    p=AcquireImagePixels(image,0,y,image->columns,1,exception);
619
0
                    if (p == (const PixelPacket *) NULL)
620
0
                      {
621
0
                        status = MagickFail;
622
0
                        break;
623
0
                      }
624
0
                    for (x=image->columns; x != 0; x--)
625
0
                      {
626
0
                        opaque = ((opaque) &&
627
0
                                  (p->opacity == OpaqueOpacity));
628
0
                        if (!opaque)
629
0
                          {
630
0
                            broke_loop=MagickTrue;
631
0
                            break;
632
0
                          }
633
0
                        p++;
634
0
                      }
635
0
                    if (!opaque)
636
0
                      break;
637
0
                    if (QuantumTick(y,image->rows))
638
0
                      if (!MagickMonitorFormatted(y,image->rows,exception,
639
0
                                                  AnalyzeImageText,image->filename))
640
0
                        break;
641
0
                  }
642
0
              }
643
530
            break;
644
0
          }
645
530
        }
646
530
      if (!characteristics->grayscale)
647
0
        {
648
0
          characteristics->grayscale=grayscale;
649
0
          ((Image *)image)->is_grayscale=grayscale; /* Intentionally ignore const */
650
0
        }
651
530
      if (!characteristics->monochrome)
652
413
        {
653
413
          characteristics->monochrome=monochrome;
654
413
          ((Image *)image)->is_monochrome=monochrome; /* Intentionally ignore const */
655
413
        }
656
530
      if (!characteristics->opaque)
657
0
        characteristics->opaque=opaque;
658
530
    }
659
660
  /*
661
    Force progress indication to 100%
662
  */
663
84.4k
  if (broke_loop)
664
530
    (void) MagickMonitorFormatted(image->rows-1,image->rows,exception,
665
530
                                  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
84.4k
  return status;
671
84.4k
}
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
530
{
706
530
  ImageCharacteristics
707
530
    characteristics;
708
709
530
  ImageType
710
530
    image_type;
711
712
530
  assert(image != (Image *) NULL);
713
530
  assert(image->signature == MagickSignature);
714
715
530
  image_type=UndefinedType;
716
530
  if (GetImageCharacteristics(image,&characteristics,MagickTrue,exception))
717
530
    {
718
530
      if (characteristics.cmyk)
719
0
        image_type=(characteristics.opaque ? ColorSeparationType : ColorSeparationMatteType);
720
530
      else if (characteristics.monochrome)
721
117
        image_type=BilevelType;
722
413
      else if (characteristics.grayscale)
723
413
        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
530
    }
729
530
  return image_type;
730
530
}
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
84.3k
#define AnalyzeGrayImageText "[%s] Analyze for gray..."
762
MagickExport MagickBool IsGrayImage(const Image *image,
763
  ExceptionInfo *exception)
764
122k
{
765
122k
  unsigned long
766
122k
    y;
767
768
122k
  register const PixelPacket
769
122k
    *p;
770
771
122k
  register unsigned long
772
122k
    x;
773
774
122k
  MagickBool
775
122k
    is_grayscale;
776
777
122k
  assert(image != (Image *) NULL);
778
122k
  assert(image->signature == MagickSignature);
779
122k
  assert(exception != (ExceptionInfo *) NULL);
780
122k
  if (image->colorspace == CMYKColorspace)
781
17
    return(MagickFalse);
782
122k
  if (image->is_grayscale)
783
36.2k
    return(MagickTrue);
784
86.1k
  is_grayscale=MagickTrue;
785
86.1k
  switch (image->storage_class)
786
86.1k
  {
787
26.2k
    case DirectClass:
788
26.2k
    case UndefinedClass:
789
26.2k
    {
790
26.2k
      (void) LogMagickEvent(TransformEvent,GetMagickModule(),
791
26.2k
                            "IsGrayImage(): Exhaustive pixel test!");
792
102k
      for (y=0; y < image->rows; y++)
793
90.5k
      {
794
90.5k
        p=AcquireImagePixels(image,0,y,image->columns,1,exception);
795
90.5k
        if (p == (const PixelPacket *) NULL)
796
1
          return(MagickFalse);
797
7.05M
        for (x=image->columns; x != 0; x--)
798
6.97M
        {
799
6.97M
          if ((p->red != p->green) || (p->green != p->blue))
800
14.0k
            {
801
14.0k
              is_grayscale=MagickFalse;
802
14.0k
              break;
803
14.0k
            }
804
6.96M
          p++;
805
6.96M
        }
806
90.5k
        if (!is_grayscale)
807
14.0k
          break;
808
76.5k
        if (QuantumTick(y,image->rows))
809
35.1k
          if (!MagickMonitorFormatted(y,image->rows,
810
35.1k
                                      exception,AnalyzeGrayImageText,
811
35.1k
                                      image->filename))
812
0
            break;
813
76.5k
      }
814
26.2k
      break;
815
26.2k
    }
816
59.8k
    case PseudoClass:
817
59.8k
    {
818
59.8k
      p=image->colormap;
819
64.1M
      for (x=image->colors; x != 0; x--)
820
64.1M
        {
821
64.1M
          if ((p->red != p->green) || (p->green != p->blue))
822
35.1k
            {
823
35.1k
              is_grayscale=MagickFalse;
824
35.1k
              break;
825
35.1k
            }
826
64.0M
          p++;
827
64.0M
        }
828
59.8k
      break;
829
26.2k
    }
830
86.1k
  }
831
832
  /*
833
    Force progress indication to 100%
834
  */
835
86.1k
  if (!is_grayscale)
836
49.2k
    (void) MagickMonitorFormatted(image->rows-1,image->rows,exception,
837
49.2k
                                  AnalyzeGrayImageText,image->filename);
838
839
86.1k
  ((Image *)image)->is_grayscale=is_grayscale;
840
86.1k
  return(is_grayscale);
841
86.1k
}
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
54.8k
#define AnalyzeBilevelImageText "[%s] Analyze for bilevel..."
872
MagickExport MagickBool IsMonochromeImage(const Image *image,
873
  ExceptionInfo *exception)
874
93.7k
{
875
93.7k
  unsigned long
876
93.7k
    y;
877
878
93.7k
  register const PixelPacket
879
93.7k
    *p;
880
881
93.7k
  register unsigned long
882
93.7k
    x;
883
884
93.7k
  MagickBool
885
93.7k
    is_monochrome;
886
887
93.7k
  assert(image != (Image *) NULL);
888
93.7k
  assert(image->signature == MagickSignature);
889
93.7k
  assert(exception != (ExceptionInfo *) NULL);
890
93.7k
  if (image->colorspace == CMYKColorspace)
891
349
    return(MagickFalse);
892
93.3k
  if (image->is_monochrome)
893
17.5k
    return(MagickTrue);
894
75.8k
  is_monochrome=MagickTrue;
895
75.8k
  switch (image->storage_class)
896
75.8k
  {
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
75.8k
    case PseudoClass:
927
75.8k
    {
928
75.8k
      p=image->colormap;
929
482k
      for (x=image->colors; x != 0; x--)
930
461k
      {
931
461k
        if ((p->red != p->green) || (p->green != p->blue) ||
932
461k
            ((p->red != 0) && (p->red != MaxRGB)))
933
54.8k
          {
934
54.8k
            is_monochrome=MagickFalse;
935
54.8k
            break;
936
54.8k
          }
937
407k
        p++;
938
407k
      }
939
75.8k
      break;
940
0
    }
941
75.8k
  }
942
943
  /*
944
    Force progress indication to 100%
945
  */
946
75.8k
  if (!is_monochrome)
947
54.8k
    (void) MagickMonitorFormatted(image->rows-1,image->rows,exception,
948
54.8k
                                  AnalyzeBilevelImageText,image->filename);
949
950
75.8k
  ((Image *)image)->is_monochrome=is_monochrome;
951
75.8k
  return(is_monochrome);
952
75.8k
}
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
}