Coverage Report

Created: 2026-07-25 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/magick/transform.c
Line
Count
Source
1
/*
2
% Copyright (C) 2003 - 2022 GraphicsMagick Group
3
% Copyright (C) 2002 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
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11
%                                                                             %
12
%                                                                             %
13
%                                                                             %
14
%       TTTTT  RRRR    AAA   N   N  SSSSS  FFFFF   OOO   RRRR   M   M         %
15
%         T    R   R  A   A  NN  N  SS     F      O   O  R   R  MM MM         %
16
%         T    RRRR   AAAAA  N N N   SSS   FFF    O   O  RRRR   M M M         %
17
%         T    R R    A   A  N  NN     SS  F      O   O  R R    M   M         %
18
%         T    R  R   A   A  N   N  SSSSS  F       OOO   R  R   M   M         %
19
%                                                                             %
20
%                                                                             %
21
%                   GraphicsMagick Image Transform Methods                    %
22
%                                                                             %
23
%                                                                             %
24
%                              Software Design                                %
25
%                                John Cristy                                  %
26
%                                 July 1992                                   %
27
%                                                                             %
28
%                                                                             %
29
%                                                                             %
30
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31
%
32
%
33
%
34
*/
35

36
/*
37
  Include declarations.
38
*/
39
#include "magick/studio.h"
40
#include "magick/analyze.h"
41
#include "magick/color.h"
42
#include "magick/composite.h"
43
#include "magick/log.h"
44
#include "magick/monitor.h"
45
#include "magick/pixel_cache.h"
46
#include "magick/resize.h"
47
#include "magick/texture.h"
48
#include "magick/transform.h"
49
#include "magick/utility.h"
50
#include "magick/log.h"
51

52
/*
53
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
54
%                                                                             %
55
%                                                                             %
56
%                                                                             %
57
%   C h o p I m a g e                                                         %
58
%                                                                             %
59
%                                                                             %
60
%                                                                             %
61
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62
%
63
%  Chop() removes a region of an image and collapses the image to occupy the
64
%  removed portion.
65
%
66
%  The format of the ChopImage method is:
67
%
68
%      Image *ChopImage(const Image *image,const RectangleInfo *chop_info
69
%        ExceptionInfo *exception)
70
%
71
%  A description of each parameter follows:
72
%
73
%    o image: The image.
74
%
75
%    o chop_info: Define the region of the image to chop.
76
%
77
%    o exception: Return any errors or warnings in this structure.
78
%
79
%
80
*/
81
MagickExport Image *ChopImage(const Image *image,const RectangleInfo *chop_info,
82
                              ExceptionInfo *exception)
83
0
{
84
0
#define ChopImageText "[%s] Chop..."
85
86
0
  Image
87
0
    *chop_image;
88
89
0
  unsigned long
90
0
    row_count=0;
91
92
0
  MagickBool
93
0
    monitor_active;
94
95
0
  long
96
0
    y;
97
98
0
  RectangleInfo
99
0
    clone_info;
100
101
0
  MagickPassFail
102
0
    status=MagickPass;
103
104
  /*
105
    Check chop geometry.
106
  */
107
0
  assert(image != (const Image *) NULL);
108
0
  assert(image->signature == MagickSignature);
109
0
  assert(exception != (ExceptionInfo *) NULL);
110
0
  assert(exception->signature == MagickSignature);
111
0
  assert(chop_info != (RectangleInfo *) NULL);
112
0
  if (((chop_info->x+(long) chop_info->width) < 0) ||
113
0
      ((chop_info->y+(long) chop_info->height) < 0) ||
114
0
      (chop_info->x > (long) image->columns) ||
115
0
      (chop_info->y > (long) image->rows))
116
0
    ThrowImageException3(OptionError,GeometryDoesNotContainImage,
117
0
                         UnableToChopImage);
118
0
  clone_info=(*chop_info);
119
0
  if ((clone_info.x+(long) clone_info.width) > (long) image->columns)
120
0
    clone_info.width=(unsigned long) ((long) image->columns-clone_info.x);
121
0
  if ((clone_info.y+(long) clone_info.height) > (long) image->rows)
122
0
    clone_info.height=(unsigned long) ((long) image->rows-clone_info.y);
123
0
  if (clone_info.x < 0)
124
0
    {
125
0
      clone_info.width-=(unsigned long) (-clone_info.x);
126
0
      clone_info.x=0;
127
0
    }
128
0
  if (clone_info.y < 0)
129
0
    {
130
0
      clone_info.height-=(unsigned long) (-clone_info.y);
131
0
      clone_info.y=0;
132
0
    }
133
0
  if ((clone_info.width >= image->columns) ||
134
0
      (clone_info.height >= image->rows))
135
0
    ThrowImageException3(OptionError,GeometryDoesNotContainImage,
136
0
                         UnableToChopImage);
137
  /*
138
    Initialize chop image attributes.
139
  */
140
0
  chop_image=CloneImage(image,image->columns-clone_info.width,
141
0
                        image->rows-clone_info.height,False,exception);
142
0
  if (chop_image == (Image *) NULL)
143
0
    return((Image *) NULL);
144
145
  /*
146
    Extract chop image.
147
  */
148
0
  monitor_active=MagickMonitorActive();
149
150
#if defined(HAVE_OPENMP)  && !defined(DisableSlowOpenMP)
151
#  if defined(TUNE_OPENMP)
152
#    pragma omp parallel for schedule(runtime) shared(row_count, status)
153
#  else
154
#    pragma omp parallel for schedule(static,4) shared(row_count, status)
155
#  endif
156
#endif
157
0
  for (y=0; y < (long) clone_info.y; y++)
158
0
    {
159
0
      register const PixelPacket
160
0
        *p;
161
162
0
      register const IndexPacket
163
0
        *indexes;
164
165
0
      register IndexPacket
166
0
        *chop_indexes;
167
168
0
      register long
169
0
        x;
170
171
0
      register PixelPacket
172
0
        *q;
173
174
0
      MagickBool
175
0
        thread_status;
176
177
0
      thread_status=status;
178
0
      if (thread_status == MagickFail)
179
0
        continue;
180
181
0
      p=AcquireImagePixels(image,0,y,image->columns,1,exception);
182
0
      q=SetImagePixelsEx(chop_image,0,y,chop_image->columns,1,exception);
183
0
      if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
184
0
        thread_status=MagickFail;
185
186
0
      if (thread_status != MagickFail)
187
0
        {
188
0
          indexes=AccessImmutableIndexes(image);
189
0
          chop_indexes=AccessMutableIndexes(chop_image);
190
0
          for (x=0; x < (long) image->columns; x++)
191
0
            {
192
0
              if ((x < clone_info.x) || (x >= (long) (clone_info.x+clone_info.width)))
193
0
                {
194
0
                  if ((indexes != (const IndexPacket *) NULL) &&
195
0
                      (chop_indexes != (IndexPacket *) NULL))
196
0
                    *chop_indexes++=indexes[x];
197
0
                  *q=(*p);
198
0
                  q++;
199
0
                }
200
0
              p++;
201
0
            }
202
0
          if (!SyncImagePixelsEx(chop_image,exception))
203
0
            thread_status=MagickFail;
204
0
        }
205
206
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
207
#  pragma omp atomic
208
#endif
209
0
      row_count++;
210
211
0
      if (monitor_active)
212
0
        {
213
0
          unsigned long
214
0
            thread_row_count;
215
216
#if defined(HAVE_OPENMP)
217
#  pragma omp flush (row_count)
218
#endif
219
0
          thread_row_count=row_count;
220
0
          if (QuantumTick(thread_row_count,chop_image->rows))
221
0
            if (!MagickMonitorFormatted(thread_row_count,chop_image->rows,exception,
222
0
                                        ChopImageText,image->filename))
223
0
              thread_status=MagickFail;
224
0
        }
225
226
0
      if (thread_status == MagickFail)
227
0
        {
228
0
          status=MagickFail;
229
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
230
#  pragma omp flush (status)
231
#endif
232
0
        }
233
0
    }
234
  /*
235
    Extract chop image.
236
  */
237
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
238
#  if defined(TUNE_OPENMP)
239
#    pragma omp parallel for schedule(runtime) shared(row_count, status)
240
#  else
241
#    pragma omp parallel for schedule(static,4) shared(row_count, status)
242
#  endif
243
#endif
244
0
  for (y=0; y < (long) (image->rows-(clone_info.y+clone_info.height)); y++)
245
0
    {
246
0
      register const PixelPacket
247
0
        *p;
248
249
0
      register const IndexPacket
250
0
        *indexes;
251
252
0
      register IndexPacket
253
0
        *chop_indexes;
254
255
0
      register long
256
0
        x;
257
258
0
      register PixelPacket
259
0
        *q;
260
261
0
      MagickBool
262
0
        thread_status;
263
264
0
      thread_status=status;
265
0
      if (thread_status == MagickFail)
266
0
        continue;
267
268
0
      p=AcquireImagePixels(image,0,clone_info.y+clone_info.height+y,image->columns,1,exception);
269
0
      q=SetImagePixelsEx(chop_image,0,clone_info.y+y,chop_image->columns,1,exception);
270
0
      if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
271
0
        thread_status=MagickFail;
272
273
0
      if (thread_status != MagickFail)
274
0
        {
275
0
          indexes=AccessImmutableIndexes(image);
276
0
          chop_indexes=AccessMutableIndexes(chop_image);
277
0
          for (x=0; x < (long) image->columns; x++)
278
0
            {
279
0
              if ((x < clone_info.x) || (x >= (long) (clone_info.x+clone_info.width)))
280
0
                {
281
0
                  if ((indexes != (const IndexPacket *) NULL) &&
282
0
                      (chop_indexes != (IndexPacket *) NULL))
283
0
                    *chop_indexes++=indexes[x];
284
0
                  *q=(*p);
285
0
                  q++;
286
0
                }
287
0
              p++;
288
0
            }
289
0
          if (!SyncImagePixelsEx(chop_image,exception))
290
0
            thread_status=MagickFail;
291
0
        }
292
293
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
294
#  pragma omp atomic
295
#endif
296
0
      row_count++;
297
298
0
      if (monitor_active)
299
0
        {
300
0
          unsigned long
301
0
            thread_row_count;
302
303
#if defined(HAVE_OPENMP)
304
#  pragma omp flush (row_count)
305
#endif
306
0
          thread_row_count=row_count;
307
0
          if (QuantumTick(thread_row_count,chop_image->rows))
308
0
            if (!MagickMonitorFormatted(thread_row_count,chop_image->rows,exception,
309
0
                                        ChopImageText,image->filename))
310
0
              thread_status=MagickFail;
311
0
        }
312
313
0
      if (thread_status == MagickFail)
314
0
        {
315
0
          status=MagickFail;
316
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
317
#  pragma omp flush (status)
318
#endif
319
0
        }
320
0
    }
321
0
  if (row_count < chop_image->rows)
322
0
    {
323
0
      DestroyImage(chop_image);
324
0
      return((Image *) NULL);
325
0
    }
326
0
  chop_image->is_grayscale=image->is_grayscale;
327
0
  return(chop_image);
328
0
}
329

330
/*
331
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
332
%                                                                             %
333
%                                                                             %
334
%                                                                             %
335
%     C o a l e s c e I m a g e s                                             %
336
%                                                                             %
337
%                                                                             %
338
%                                                                             %
339
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
340
%
341
%  CoalesceImages() composites a set of images while respecting any page
342
%  offsets and disposal methods.  GIF, MIFF, and MNG animation sequences
343
%  typically start with an image background and each subsequent image
344
%  varies in size and offset.  CoalesceImages() returns a new sequence
345
%  where each image in the sequence is the same size as the first and
346
%  composited with the next image in the sequence.
347
%
348
%  The format of the CoalesceImages method is:
349
%
350
%      Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
351
%
352
%  A description of each parameter follows:
353
%
354
%    o image: The image sequence.
355
%
356
%    o exception: Return any errors or warnings in this structure.
357
%
358
*/
359
MagickExport Image *CoalesceImages(const Image *image,ExceptionInfo *exception)
360
726
{
361
726
  Image
362
726
    *coalesce_image,
363
726
    *previous_image;
364
365
726
  register const Image
366
726
    *next;
367
368
726
  register long
369
726
    i;
370
371
726
  MagickBool
372
726
    found_transparency=False;
373
374
  /*
375
    Coalesce the image sequence.
376
  */
377
726
  assert(image != (Image *) NULL);
378
726
  assert(image->signature == MagickSignature);
379
726
  assert(exception != (ExceptionInfo *) NULL);
380
726
  assert(exception->signature == MagickSignature);
381
726
  if (image->next == (Image *) NULL)
382
0
    ThrowImageException3(ImageError,ImageSequenceIsRequired,
383
726
      UnableToCoalesceImage);
384
  /*
385
    Clone first image in sequence.
386
  */
387
726
  coalesce_image=CloneImage(image,0,0,True,exception);
388
726
  if (coalesce_image == (Image *) NULL)
389
0
    return((Image *) NULL);
390
726
  (void) memset(&coalesce_image->page,0,sizeof(RectangleInfo));
391
726
  previous_image=coalesce_image;
392
  /*
393
    Coalesce image.
394
  */
395
28.5k
  for (next=image->next; next != (Image *) NULL; next=next->next)
396
27.8k
  {
397
27.8k
    switch (next->dispose)
398
27.8k
    {
399
27.8k
      case UndefinedDispose:
400
27.8k
      case NoneDispose:
401
27.8k
      {
402
27.8k
        coalesce_image->next=CloneImage(coalesce_image,0,0,True,exception);
403
27.8k
        if (coalesce_image->next != (Image *) NULL)
404
27.8k
          previous_image=coalesce_image->next;
405
27.8k
        break;
406
27.8k
      }
407
0
      case BackgroundDispose:
408
0
      {
409
        /*
410
          Fill image with transparent color, if one exists.
411
        */
412
0
        coalesce_image->next=CloneImage(coalesce_image,0,0,True,exception);
413
0
        if (coalesce_image->next != (Image *) NULL) {
414
0
          for (i = 0; i < (long) coalesce_image->colors; i++) {
415
0
            if (coalesce_image->colormap[i].opacity == TransparentOpacity) {
416
0
              found_transparency = True;
417
0
              (void) SetImageColor(coalesce_image->next,&coalesce_image->colormap[i]);
418
0
              break;
419
0
            }
420
0
          }
421
0
          if (!found_transparency)
422
0
            (void) SetImage(coalesce_image->next,OpaqueOpacity);
423
0
        }
424
0
        break;
425
27.8k
      }
426
0
      case PreviousDispose:
427
0
      default:
428
0
      {
429
0
        coalesce_image->next=CloneImage(previous_image,0,0,True,exception);
430
0
        break;
431
0
      }
432
27.8k
    }
433
27.8k
    if (coalesce_image->next == (Image *) NULL)
434
0
      {
435
0
        DestroyImageList(coalesce_image);
436
0
        return((Image *) NULL);
437
0
      }
438
27.8k
    coalesce_image->next->previous=coalesce_image;
439
27.8k
    coalesce_image=coalesce_image->next;
440
27.8k
    coalesce_image->delay=next->delay;
441
27.8k
    coalesce_image->start_loop=next->start_loop;
442
27.8k
    (void) CompositeImage(coalesce_image,next->matte ? OverCompositeOp :
443
27.8k
      CopyCompositeOp,next,next->page.x,next->page.y);
444
27.8k
  }
445
446
28.5k
  while (coalesce_image->previous != (Image *) NULL)
447
27.8k
    coalesce_image=coalesce_image->previous;
448
726
  return(coalesce_image);
449
726
}
450

451
/*
452
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
453
%                                                                             %
454
%                                                                             %
455
%                                                                             %
456
%   C r o p I m a g e                                                         %
457
%                                                                             %
458
%                                                                             %
459
%                                                                             %
460
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
461
%
462
%  Use CropImage() to extract a region of the image starting at the offset
463
%  defined by geometry.  As a special feature, if the geometry "0x0" is
464
%  is passed, GetImageBoundingBox() is used to locate the edges of the
465
%  image and the image is cropped ("trimmed") to that boundary.
466
%
467
%  The format of the CropImage method is:
468
%
469
%      Image *CropImage(const Image *image,const RectangleInfo *geometry,
470
%        ExceptionInfo *exception)
471
%
472
%  A description of each parameter follows:
473
%
474
%    o image: The image.
475
%
476
%    o geometry: Define the region of the image to crop with members
477
%      x, y, width, and height.
478
%
479
%    o exception: Return any errors or warnings in this structure.
480
%
481
%
482
*/
483
MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry,
484
                              ExceptionInfo *exception)
485
1.17k
{
486
1.17k
  Image
487
1.17k
    *crop_image;
488
489
1.17k
  unsigned long
490
1.17k
    row_count=0;
491
492
1.17k
  MagickBool
493
1.17k
    monitor_active;
494
495
1.17k
  long
496
1.17k
    y;
497
498
1.17k
  RectangleInfo
499
1.17k
    page;
500
501
1.17k
  MagickPassFail
502
1.17k
    status=MagickPass;
503
504
1.17k
  MagickBool
505
1.17k
    transform_logging;
506
507
  /*
508
    Check crop geometry.
509
  */
510
1.17k
  assert(image != (const Image *) NULL);
511
1.17k
  assert(image->signature == MagickSignature);
512
1.17k
  assert(geometry != (const RectangleInfo *) NULL);
513
1.17k
  assert(exception != (ExceptionInfo *) NULL);
514
1.17k
  assert(exception->signature == MagickSignature);
515
1.17k
  transform_logging=IsEventLogged(TransformEvent);
516
1.17k
  if (transform_logging)
517
0
    (void) LogMagickEvent(TransformEvent,GetMagickModule(),
518
0
                          "Crop Geometry: %lux%lu%+ld%+ld",
519
0
                          geometry->width, geometry->height,
520
0
                          geometry->x, geometry->y);
521
1.17k
  if ((geometry->width != 0) || (geometry->height != 0))
522
1.17k
    {
523
1.17k
      if (((geometry->x+(long) geometry->width) < 0) ||
524
1.17k
          ((geometry->y+(long) geometry->height) < 0) ||
525
1.17k
          (geometry->x >= (long) image->columns) ||
526
1.17k
          (geometry->y >= (long) image->rows))
527
0
        ThrowImageException(OptionError,GeometryDoesNotContainImage,
528
1.17k
                            MagickMsg(ResourceLimitError,UnableToCropImage));
529
1.17k
    }
530
1.17k
  page=(*geometry);
531
1.17k
  if ((page.width != 0) || (page.height != 0))
532
1.17k
    {
533
1.17k
      if ((page.x+(long) page.width) > (long) image->columns)
534
0
        page.width=image->columns-page.x;
535
1.17k
      if ((page.y+(long) page.height) > (long) image->rows)
536
0
        page.height=image->rows-page.y;
537
1.17k
      if (page.x < 0)
538
0
        {
539
0
          page.width+=page.x;
540
0
          page.x=0;
541
0
        }
542
1.17k
      if (page.y < 0)
543
0
        {
544
0
          page.height+=page.y;
545
0
          page.y=0;
546
0
        }
547
1.17k
    }
548
0
  else
549
0
    {
550
      /*
551
        Set bounding box to the image dimensions.
552
      */
553
0
      page=GetImageBoundingBox(image,exception);
554
0
      page.width+=geometry->x*2;
555
0
      page.height+=geometry->y*2;
556
0
      page.x-=geometry->x;
557
0
      if (page.x < 0)
558
0
        page.x=0;
559
0
      page.y-=geometry->y;
560
0
      if (page.y < 0)
561
0
        page.y=0;
562
0
      if (transform_logging)
563
0
        (void) LogMagickEvent(TransformEvent,GetMagickModule(),
564
0
                              "Bounding Page: %lux%lu%+ld%+ld",
565
0
                              page.width, page.height, page.x, page.y);
566
0
      if ((((long) page.width+page.x) > (long) image->columns) ||
567
0
          (((long) page.height+page.y) > (long) image->rows))
568
0
        ThrowImageException(OptionError,GeometryDoesNotContainImage,
569
0
                            MagickMsg(ResourceLimitError,UnableToCropImage));
570
0
    }
571
1.17k
  if ((page.width == 0) || (page.height == 0))
572
0
    ThrowImageException(OptionError,GeometryDimensionsAreZero,
573
1.17k
                        MagickMsg(ResourceLimitError,UnableToCropImage));
574
1.17k
  if ((page.width == image->columns) && (page.height == image->rows) &&
575
0
      (page.x == 0) && (page.y == 0))
576
0
    return(CloneImage(image,0,0,True,exception));
577
  /*
578
    Initialize crop image attributes.
579
  */
580
1.17k
  crop_image=CloneImage(image,page.width,page.height,True,exception);
581
1.17k
  if (crop_image == (Image *) NULL)
582
0
    return((Image *) NULL);
583
  /*
584
    Extract crop image.
585
  */
586
1.17k
  crop_image->page=page;
587
1.17k
  if ((geometry->width == 0) || (geometry->height == 0))
588
0
    (void) memset(&crop_image->page,0,sizeof(RectangleInfo));
589
590
1.17k
  monitor_active=MagickMonitorActive();
591
592
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
593
#  if defined(TUNE_OPENMP)
594
#    pragma omp parallel for schedule(runtime) shared(row_count, status)
595
#  else
596
#    pragma omp parallel for schedule(static,4) shared(row_count, status)
597
#  endif
598
#endif
599
356k
  for (y=0; y < (long) crop_image->rows; y++)
600
355k
    {
601
355k
      const PixelPacket
602
355k
        *p;
603
604
355k
      const IndexPacket
605
355k
        *indexes;
606
607
355k
      IndexPacket
608
355k
        *crop_indexes;
609
610
355k
      PixelPacket
611
355k
        *q;
612
613
355k
      MagickBool
614
355k
        thread_status;
615
616
355k
      thread_status=status;
617
355k
      if (thread_status == MagickFail)
618
0
        continue;
619
620
355k
      p=AcquireImagePixels(image,page.x,page.y+y,crop_image->columns,1,exception);
621
355k
      q=SetImagePixelsEx(crop_image,0,y,crop_image->columns,1,exception);
622
355k
      if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
623
0
        thread_status=MagickFail;
624
625
355k
      if (thread_status != MagickFail)
626
355k
        {
627
355k
          (void) memcpy(q,p,crop_image->columns*sizeof(PixelPacket));
628
355k
          indexes=AccessImmutableIndexes(image);
629
355k
          crop_indexes=AccessMutableIndexes(crop_image);
630
355k
          if ((indexes != (const IndexPacket *) NULL) &&
631
553
              (crop_indexes != (IndexPacket *) NULL))
632
553
            (void) memcpy(crop_indexes,indexes,crop_image->columns*
633
553
                          sizeof(IndexPacket));
634
355k
          if (!SyncImagePixelsEx(crop_image,exception))
635
0
            thread_status=MagickFail;
636
355k
        }
637
638
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
639
#  pragma omp atomic
640
#endif
641
355k
      row_count++;
642
643
355k
      if (monitor_active)
644
0
        {
645
0
          unsigned long
646
0
            thread_row_count;
647
648
#if defined(HAVE_OPENMP)
649
#  pragma omp flush (row_count)
650
#endif
651
0
          thread_row_count=row_count;
652
0
          if (QuantumTick(thread_row_count,crop_image->rows))
653
0
            if (!MagickMonitorFormatted(thread_row_count,crop_image->rows,exception,
654
0
                                        "[%s] Crop: %lux%lu+%ld+%ld...",
655
0
                                        crop_image->filename,
656
0
                                        crop_image->columns,crop_image->rows,
657
0
                                        page.x,page.y))
658
0
              thread_status=MagickFail;
659
0
        }
660
661
355k
      if (thread_status == MagickFail)
662
0
        {
663
0
          status=MagickFail;
664
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
665
#  pragma omp flush (status)
666
#endif
667
0
        }
668
355k
    }
669
1.17k
  if (row_count < crop_image->rows)
670
0
    {
671
0
      DestroyImage(crop_image);
672
0
      return((Image *) NULL);
673
0
    }
674
1.17k
  crop_image->is_grayscale=image->is_grayscale;
675
1.17k
  return(crop_image);
676
1.17k
}
677

678
/*
679
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
680
%                                                                             %
681
%                                                                             %
682
%                                                                             %
683
%     D e c o n s t r u c t I m a g e s                                       %
684
%                                                                             %
685
%                                                                             %
686
%                                                                             %
687
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
688
%
689
%  DeconstructImages() compares each image with the next in a sequence and
690
%  returns the maximum bounding region of any pixel differences it discovers.
691
%
692
%  The format of the DeconstructImages method is:
693
%
694
%      Image *DeconstructImages(const Image *image,ExceptionInfo *exception)
695
%
696
%  A description of each parameter follows:
697
%
698
%    o image: The image.
699
%
700
%    o exception: Return any errors or warnings in this structure.
701
%
702
%
703
*/
704
MagickExport Image *DeconstructImages(const Image *image,
705
  ExceptionInfo *exception)
706
0
{
707
0
  Image
708
0
    *crop_image,
709
0
    *crop_next,
710
0
    *deconstruct_image;
711
712
0
  long
713
0
    y;
714
715
0
  RectangleInfo
716
0
    *bounds;
717
718
0
  register const Image
719
0
    *next;
720
721
0
  register const PixelPacket
722
0
    *p;
723
724
0
  register long
725
0
    i,
726
0
    x;
727
728
0
  register PixelPacket
729
0
    *q;
730
731
0
  assert(image != (const Image *) NULL);
732
0
  assert(image->signature == MagickSignature);
733
0
  assert(exception != (ExceptionInfo *) NULL);
734
0
  assert(exception->signature == MagickSignature);
735
0
  if (image->next == (Image *) NULL)
736
0
    ThrowImageException3(ImageError,ImageSequenceIsRequired,
737
0
      UnableToDeconstructImageSequence);
738
  /*
739
    Ensure the image are the same size.
740
  */
741
0
  for (next=image; next != (Image *) NULL; next=next->next)
742
0
  {
743
0
    if ((next->columns != image->columns) || (next->rows != image->rows))
744
0
      ThrowImageException(OptionError,ImagesAreNotTheSameSize,
745
0
        MagickMsg(ImageError,UnableToDeconstructImageSequence));
746
0
  }
747
  /*
748
    Allocate memory.
749
  */
750
0
  bounds=MagickAllocateMemory(RectangleInfo *,
751
0
    GetImageListLength(image)*sizeof(RectangleInfo));
752
0
  if (bounds == (RectangleInfo *) NULL)
753
0
    ThrowImageException(ResourceLimitError,MemoryAllocationFailed,
754
0
      MagickMsg(ImageError,UnableToDeconstructImageSequence));
755
  /*
756
    Compute the bounding box for each next in the sequence.
757
  */
758
0
  i=0;
759
0
  for (next=image->next; next != (const Image *) NULL; next=next->next)
760
0
  {
761
    /*
762
      Set bounding box to the next dimensions.
763
    */
764
0
    for (x=0; x < (long) next->columns; x++)
765
0
    {
766
0
      p=AcquireImagePixels(next,x,0,1,next->rows,exception);
767
0
      q=GetImagePixels(next->previous,x,0,1,next->previous->rows);
768
0
      if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
769
0
        break;
770
0
      for (y=0; y < (long) next->rows; y++)
771
0
      {
772
0
        if (!FuzzyColorMatch(p,q,next->fuzz))
773
0
          break;
774
0
        p++;
775
0
        q++;
776
0
      }
777
0
      if (y < (long) next->rows)
778
0
        break;
779
0
    }
780
0
    bounds[i].x=x;
781
0
    for (y=0; y < (long) next->rows; y++)
782
0
    {
783
0
      p=AcquireImagePixels(next,0,y,next->columns,1,exception);
784
0
      q=GetImagePixels(next->previous,0,y,next->previous->columns,1);
785
0
      if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
786
0
        break;
787
0
      for (x=0; x < (long) next->columns; x++)
788
0
      {
789
0
        if (!FuzzyColorMatch(p,q,next->fuzz))
790
0
          break;
791
0
        p++;
792
0
        q++;
793
0
      }
794
0
      if (x < (long) next->columns)
795
0
        break;
796
0
    }
797
0
    bounds[i].y=y;
798
0
    for (x=(long) next->columns-1; x >= 0; x--)
799
0
    {
800
0
      p=AcquireImagePixels(next,x,0,1,next->rows,exception);
801
0
      q=GetImagePixels(next->previous,x,0,1,next->previous->rows);
802
0
      if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
803
0
        break;
804
0
      for (y=0; y < (long) next->rows; y++)
805
0
      {
806
0
        if (!FuzzyColorMatch(p,q,next->fuzz))
807
0
          break;
808
0
        p++;
809
0
        q++;
810
0
      }
811
0
      if (y < (long) next->rows)
812
0
        break;
813
0
    }
814
0
    bounds[i].width=x-bounds[i].x+1;
815
0
    for (y=(long) next->rows-1; y >= 0; y--)
816
0
    {
817
0
      p=AcquireImagePixels(next,0,y,next->columns,1,exception);
818
0
      q=GetImagePixels(next->previous,0,y,next->previous->columns,1);
819
0
      if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
820
0
        break;
821
0
      for (x=0; x < (long) next->columns; x++)
822
0
      {
823
0
        if (!FuzzyColorMatch(p,q,next->fuzz))
824
0
          break;
825
0
        p++;
826
0
        q++;
827
0
      }
828
0
      if (x < (long) next->columns)
829
0
        break;
830
0
    }
831
0
    bounds[i].height=y-bounds[i].y+1;
832
0
    i++;
833
0
  }
834
  /*
835
    Clone first image in sequence.
836
  */
837
0
  deconstruct_image=CloneImage(image,0,0,True,exception);
838
0
  if (deconstruct_image == (Image *) NULL)
839
0
    {
840
0
      MagickFreeMemory(bounds);
841
0
      return((Image *) NULL);
842
0
    }
843
  /*
844
    Deconstruct the image sequence.
845
  */
846
0
  i=0;
847
0
  for (next=image->next; next != (Image *) NULL; next=next->next)
848
0
  {
849
0
    crop_image=CloneImage(next,0,0,True,exception);
850
0
    if (crop_image == (Image *) NULL)
851
0
      break;
852
0
    crop_next=CropImage(crop_image,&bounds[i++],exception);
853
0
    DestroyImage(crop_image);
854
0
    if (crop_next == (Image *) NULL)
855
0
      break;
856
0
    deconstruct_image->next=crop_next;
857
0
    crop_next->previous=deconstruct_image;
858
0
    deconstruct_image=deconstruct_image->next;
859
0
  }
860
0
  MagickFreeMemory(bounds);
861
0
  while (deconstruct_image->previous != (Image *) NULL)
862
0
    deconstruct_image=deconstruct_image->previous;
863
0
  if (next != (Image *) NULL)
864
0
    {
865
0
      DestroyImageList(deconstruct_image);
866
0
      return((Image *) NULL);
867
0
    }
868
0
  return(deconstruct_image);
869
0
}
870

871
/*
872
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
873
%                                                                             %
874
%                                                                             %
875
%                                                                             %
876
%   E x t e n t I m a g e                                                     %
877
%                                                                             %
878
%                                                                             %
879
%                                                                             %
880
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
881
%
882
%  Use ExtentImage() to change the image dimensions as specified by geometry
883
%  width and height.  The existing image content is composited at the position
884
%  specified by geometry x and y using the image compose method.  Existing
885
%  image content which falls outside the bounds of the new image dimensions
886
%  is discarded.
887
%
888
%  The format of the ExtentImage method is:
889
%
890
%      Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
891
%        ExceptionInfo *exception)
892
%
893
%  A description of each parameter follows:
894
%
895
%    o image: The image.
896
%
897
%    o geometry: Define the new image dimension with width and height, and
898
%        the top left coordinate to place the existing image content with
899
%        x and y.
900
%
901
%    o exception: Return any errors or warnings in this structure.
902
%
903
%
904
*/
905
MagickExport Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
906
                                ExceptionInfo *exception)
907
0
{
908
0
  Image
909
0
    *extent_image;
910
911
0
  assert(image != (const Image *) NULL);
912
0
  assert(image->signature == MagickSignature);
913
0
  assert(geometry != (const RectangleInfo *) NULL);
914
0
  assert(exception != (ExceptionInfo *) NULL);
915
0
  assert(exception->signature == MagickSignature);
916
917
  /*
918
    Allocate canvas image
919
  */
920
0
  if ((extent_image=CloneImage(image,geometry->width,geometry->height,
921
0
                               MagickTrue,exception)) == (Image *) NULL)
922
0
    return((Image *) NULL);
923
924
  /*
925
    Set canvas image color to background color
926
  */
927
0
  if ((SetImage(extent_image,image->background_color.opacity)) == MagickFail)
928
0
    {
929
0
      CopyException(exception,&extent_image->exception);
930
0
      DestroyImage(extent_image);
931
0
      return((Image *) NULL);
932
0
    }
933
934
  /*
935
    Composite existing image at position using requested composition
936
    operator.
937
  */
938
0
  if ((CompositeImage(extent_image,image->compose,image,geometry->x,
939
0
                      geometry->y)) == MagickFail)
940
0
    {
941
0
      CopyException(exception,&extent_image->exception);
942
0
      DestroyImage(extent_image);
943
0
      return((Image *) NULL);
944
0
    }
945
946
0
  return(extent_image);
947
0
}
948

949
/*
950
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
951
%                                                                             %
952
%                                                                             %
953
%                                                                             %
954
%     F l a t t e n I m a g e                                                 %
955
%                                                                             %
956
%                                                                             %
957
%                                                                             %
958
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
959
%
960
%  Method FlattenImage merges a sequence of images.  This is useful for
961
%  combining Photoshop layers into a single image.
962
%
963
%  The format of the FlattenImage method is:
964
%
965
%      Image *FlattenImage(const Image *image,ExceptionInfo *exception)
966
%
967
%  A description of each parameter follows:
968
%
969
%    o image: The image sequence.
970
%
971
%    o exception: Return any errors or warnings in this structure.
972
%
973
*/
974
MagickExport Image *FlattenImages(const Image *image,ExceptionInfo *exception)
975
0
{
976
0
  Image
977
0
    *flatten_image;
978
979
0
  register const Image
980
0
    *next;
981
982
  /*
983
    Flatten the image sequence.
984
  */
985
0
  assert(image != (Image *) NULL);
986
0
  assert(image->signature == MagickSignature);
987
0
  assert(exception != (ExceptionInfo *) NULL);
988
0
  assert(exception->signature == MagickSignature);
989
990
  /*
991
    Clone first image in sequence to serve as canvas image
992
  */
993
0
  flatten_image=CloneImage(image,0,0,True,exception);
994
995
  /*
996
    Apply background color under image if it has a matte channel.
997
  */
998
0
  if ((flatten_image != (Image *) NULL) && (flatten_image->matte))
999
0
    (void) MagickCompositeImageUnderColor(flatten_image,
1000
0
                                          &flatten_image->background_color,
1001
0
                                          exception);
1002
1003
0
  if ((flatten_image != (Image *) NULL) &&
1004
0
      (image->next != (Image *) NULL))
1005
0
    {
1006
      /*
1007
        Flatten remaining images onto canvas
1008
      */
1009
0
      for (next=image->next; next != (Image *) NULL; next=next->next)
1010
0
        (void) CompositeImage(flatten_image,next->compose,next,next->page.x,
1011
0
                              next->page.y);
1012
0
    }
1013
0
  return(flatten_image);
1014
0
}
1015

1016
/*
1017
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1018
%                                                                             %
1019
%                                                                             %
1020
%                                                                             %
1021
%   F l i p I m a g e                                                         %
1022
%                                                                             %
1023
%                                                                             %
1024
%                                                                             %
1025
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1026
%
1027
%  FlipImage() creates a vertical mirror image by reflecting the pixels
1028
%  around the central x-axis.
1029
%
1030
%  The format of the FlipImage method is:
1031
%
1032
%      Image *FlipImage(const Image *image,ExceptionInfo *exception)
1033
%
1034
%  A description of each parameter follows:
1035
%
1036
%    o image: The image.
1037
%
1038
%    o exception: Return any errors or warnings in this structure.
1039
%
1040
%
1041
*/
1042
MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
1043
13.9k
{
1044
13.9k
#define FlipImageText "[%s] Flip..."
1045
1046
13.9k
  Image
1047
13.9k
    *flip_image;
1048
1049
13.9k
  unsigned long
1050
13.9k
    row_count=0;
1051
1052
13.9k
  MagickBool
1053
13.9k
    monitor_active;
1054
1055
13.9k
  long
1056
13.9k
    y;
1057
1058
13.9k
  MagickPassFail
1059
13.9k
    status=MagickPass;
1060
1061
  /*
1062
    Initialize flip image attributes.
1063
  */
1064
13.9k
  assert(image != (const Image *) NULL);
1065
13.9k
  assert(image->signature == MagickSignature);
1066
13.9k
  assert(exception != (ExceptionInfo *) NULL);
1067
13.9k
  assert(exception->signature == MagickSignature);
1068
1069
13.9k
  if ((image->columns == 0UL) || (image->rows == 0UL))
1070
0
    ThrowImageException(ImageError,UnableToResizeImage,
1071
13.9k
                        MagickMsg(OptionError,NonzeroWidthAndHeightRequired));
1072
1073
13.9k
  if (((((size_t) Max(sizeof(PixelPacket),sizeof(IndexPacket)))*image->columns)/
1074
13.9k
       image->columns) != Max(sizeof(PixelPacket),sizeof(IndexPacket)))
1075
13.9k
    ThrowImageException(ImageError,WidthOrHeightExceedsLimit,image->filename);
1076
1077
13.9k
  flip_image=CloneImage(image,image->columns,image->rows,True,exception);
1078
13.9k
  if (flip_image == (Image *) NULL)
1079
0
    return((Image *) NULL);
1080
  /*
1081
    Flip each row.
1082
  */
1083
1084
13.9k
  monitor_active=MagickMonitorActive();
1085
1086
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1087
#  if defined(TUNE_OPENMP)
1088
#    pragma omp parallel for schedule(runtime) shared(row_count, status)
1089
#  else
1090
#    pragma omp parallel for schedule(static,4) shared(row_count, status)
1091
#  endif
1092
#endif
1093
2.45M
  for (y=0; y < (long) flip_image->rows; y++)
1094
2.44M
    {
1095
2.44M
      const PixelPacket
1096
2.44M
        *p;
1097
1098
2.44M
      const IndexPacket
1099
2.44M
        *indexes;
1100
1101
2.44M
      IndexPacket
1102
2.44M
        *flip_indexes;
1103
1104
2.44M
      PixelPacket
1105
2.44M
        *q;
1106
1107
2.44M
      MagickBool
1108
2.44M
        thread_status;
1109
1110
2.44M
      thread_status=status;
1111
2.44M
      if (thread_status == MagickFail)
1112
0
        continue;
1113
1114
2.44M
      p=AcquireImagePixels(image,0,y,image->columns,1,exception);
1115
2.44M
      q=SetImagePixelsEx(flip_image,0,(long) (flip_image->rows-y-1),
1116
2.44M
                         flip_image->columns,1,exception);
1117
2.44M
      if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1118
3.66k
        thread_status=MagickFail;
1119
1120
2.44M
      if (thread_status != MagickFail)
1121
2.43M
        {
1122
2.43M
          (void) memcpy(q,p,flip_image->columns*sizeof(PixelPacket));
1123
2.43M
          indexes=AccessImmutableIndexes(image);
1124
2.43M
          flip_indexes=AccessMutableIndexes(flip_image);
1125
2.43M
          if ((indexes != (IndexPacket *) NULL) &&
1126
2.35M
              (flip_indexes != (IndexPacket *) NULL))
1127
2.35M
            (void) memcpy(flip_indexes,indexes,image->columns*sizeof(IndexPacket));
1128
2.43M
          if (!SyncImagePixelsEx(flip_image,exception))
1129
0
            thread_status=MagickFail;
1130
2.43M
        }
1131
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1132
#  pragma omp atomic
1133
#endif
1134
2.44M
      row_count++;
1135
1136
2.44M
      if (monitor_active)
1137
0
        {
1138
0
          unsigned long
1139
0
            thread_row_count;
1140
1141
#if defined(HAVE_OPENMP)
1142
#  pragma omp flush (row_count)
1143
#endif
1144
0
          thread_row_count=row_count;
1145
0
          if (QuantumTick(thread_row_count,flip_image->rows))
1146
0
            if (!MagickMonitorFormatted(thread_row_count,flip_image->rows,exception,
1147
0
                                        FlipImageText,image->filename))
1148
0
              thread_status=MagickFail;
1149
0
        }
1150
1151
2.44M
      if (thread_status == MagickFail)
1152
3.66k
        {
1153
3.66k
          status=MagickFail;
1154
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1155
#  pragma omp flush (status)
1156
#endif
1157
3.66k
        }
1158
2.44M
    }
1159
13.9k
  if (row_count < flip_image->rows)
1160
0
    {
1161
0
      DestroyImage(flip_image);
1162
0
      return((Image *) NULL);
1163
0
    }
1164
13.9k
  flip_image->is_grayscale=image->is_grayscale;
1165
13.9k
  return(flip_image);
1166
13.9k
}
1167

1168
/*
1169
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1170
%                                                                             %
1171
%                                                                             %
1172
%                                                                             %
1173
%   F l o p I m a g e                                                         %
1174
%                                                                             %
1175
%                                                                             %
1176
%                                                                             %
1177
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1178
%
1179
%  FlopImage() creates a horizontal mirror image by reflecting the pixels
1180
%  around the central y-axis.
1181
%
1182
%  The format of the FlopImage method is:
1183
%
1184
%      Image *FlopImage(const Image *image,ExceptionInfo *exception)
1185
%
1186
%  A description of each parameter follows:
1187
%
1188
%    o image: The image.
1189
%
1190
%    o exception: Return any errors or warnings in this structure.
1191
%
1192
%
1193
*/
1194
MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
1195
121
{
1196
121
#define FlopImageText "[%s] Flop..."
1197
1198
121
  Image
1199
121
    *flop_image;
1200
1201
121
  unsigned long
1202
121
    row_count=0;
1203
1204
121
  MagickBool
1205
121
    monitor_active;
1206
1207
121
  long
1208
121
    y;
1209
1210
121
  MagickPassFail
1211
121
    status=MagickPass;
1212
1213
  /*
1214
    Initialize flop image attributes.
1215
  */
1216
121
  assert(image != (const Image *) NULL);
1217
121
  assert(image->signature == MagickSignature);
1218
121
  assert(exception != (ExceptionInfo *) NULL);
1219
121
  assert(exception->signature == MagickSignature);
1220
121
  flop_image=CloneImage(image,image->columns,image->rows,True,exception);
1221
121
  if (flop_image == (Image *) NULL)
1222
0
    return((Image *) NULL);
1223
  /*
1224
    Flop each row.
1225
  */
1226
1227
121
  monitor_active=MagickMonitorActive();
1228
1229
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1230
#  if defined(TUNE_OPENMP)
1231
#    pragma omp parallel for schedule(runtime) shared(row_count, status)
1232
#  else
1233
#    pragma omp parallel for schedule(static,4) shared(row_count, status)
1234
#  endif
1235
#endif
1236
3.06k
  for (y=0; y < (long) flop_image->rows; y++)
1237
2.94k
    {
1238
2.94k
      register const IndexPacket
1239
2.94k
        *indexes;
1240
1241
2.94k
      register IndexPacket
1242
2.94k
        *flop_indexes;
1243
1244
2.94k
      register const PixelPacket
1245
2.94k
        *p;
1246
1247
2.94k
      register long
1248
2.94k
        x;
1249
1250
2.94k
      register PixelPacket
1251
2.94k
        *q;
1252
1253
2.94k
      MagickBool
1254
2.94k
        thread_status;
1255
1256
2.94k
      thread_status=status;
1257
2.94k
      if (thread_status == MagickFail)
1258
0
        continue;
1259
1260
2.94k
      p=AcquireImagePixels(image,0,y,image->columns,1,exception);
1261
2.94k
      q=SetImagePixelsEx(flop_image,0,y,flop_image->columns,1,exception);
1262
2.94k
      if ((p == (PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
1263
0
        thread_status=MagickFail;
1264
1265
2.94k
      if (thread_status != MagickFail)
1266
2.94k
        {
1267
2.94k
          indexes=AccessImmutableIndexes(image);
1268
2.94k
          flop_indexes=AccessMutableIndexes(flop_image);
1269
2.94k
          q+=flop_image->columns;
1270
412k
          for (x=0; x < (long) flop_image->columns; x++)
1271
409k
            {
1272
409k
              if ((indexes != (const IndexPacket *) NULL) &&
1273
104k
                  (flop_indexes != (IndexPacket *) NULL))
1274
104k
                flop_indexes[flop_image->columns-x-1]=indexes[x];
1275
409k
              q--;
1276
409k
              *q=(*p);
1277
409k
              p++;
1278
409k
            }
1279
2.94k
          if (!SyncImagePixelsEx(flop_image,exception))
1280
0
            thread_status=MagickFail;
1281
2.94k
        }
1282
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1283
#  pragma omp atomic
1284
#endif
1285
2.94k
      row_count++;
1286
1287
2.94k
      if (monitor_active)
1288
0
        {
1289
0
          unsigned long
1290
0
            thread_row_count;
1291
1292
#if defined(HAVE_OPENMP)
1293
#  pragma omp flush (row_count)
1294
#endif
1295
0
          thread_row_count=row_count;
1296
0
          if (QuantumTick(thread_row_count,flop_image->rows))
1297
0
            if (!MagickMonitorFormatted(thread_row_count,flop_image->rows,exception,
1298
0
                                        FlopImageText,image->filename))
1299
0
              thread_status=MagickFail;
1300
0
        }
1301
1302
2.94k
      if (thread_status == MagickFail)
1303
0
        {
1304
0
          status=MagickFail;
1305
#if defined(HAVE_OPENMP) && !defined(DisableSlowOpenMP)
1306
#  pragma omp flush (status)
1307
#endif
1308
0
        }
1309
2.94k
    }
1310
121
  if (row_count < flop_image->rows)
1311
0
    {
1312
0
      DestroyImage(flop_image);
1313
0
      return((Image *) NULL);
1314
0
    }
1315
121
  flop_image->is_grayscale=image->is_grayscale;
1316
121
  return(flop_image);
1317
121
}
1318

1319
/*
1320
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1321
%                                                                             %
1322
%                                                                             %
1323
%                                                                             %
1324
+   G e t I m a g e M o s a i c D i m e n s i o n s                           %
1325
%                                                                             %
1326
%                                                                             %
1327
%                                                                             %
1328
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1329
%
1330
%  Method GetImageMosaicDimensions returns the bounding region of the canvas
1331
%  which supports the images in the list as would be returned by
1332
%  MosaicImages().  The bounding region is computed based on the image size
1333
%  and page offsets of each image in the image list.
1334
%
1335
%  The format of the GetImageMosaicDimensions method is:
1336
%
1337
%      RectangleInfo GetImageMosaicDimensions(const Image *image)
1338
%
1339
%  A description of each parameter follows:
1340
%
1341
%    o bounds: Method GetImageMosaicDimensions returns the bounding box of
1342
%      the image canvas.
1343
%
1344
%    o image: The image.
1345
%
1346
%    o exception: Return any errors or warnings in this structure.
1347
%
1348
%
1349
*/
1350
1351
static RectangleInfo GetImageMosaicDimensions(const Image *image) MAGICK_FUNC_PURE;
1352
1353
static RectangleInfo GetImageMosaicDimensions(const Image *image)
1354
0
{
1355
0
  RectangleInfo
1356
0
    page;
1357
1358
0
  register const Image
1359
0
    *next;
1360
1361
0
  page.width=image->columns;
1362
0
  page.height=image->rows;
1363
0
  page.x=0;
1364
0
  page.y=0;
1365
0
  for (next=image; next != (Image *) NULL; next=next->next)
1366
0
  {
1367
0
    page.x=next->page.x;
1368
0
    page.y=next->page.y;
1369
    /*
1370
      Without casts, unsigned underflow can occur here if page offset
1371
      is negative and has greater magnitude than image size.
1372
    */
1373
0
    if (((long) next->columns+page.x) > (long) page.width)
1374
0
      page.width=(long) next->columns+page.x;
1375
0
    if (next->page.width > page.width)
1376
0
      page.width=next->page.width;
1377
0
    if (((long) next->rows+page.y) > (long) page.height)
1378
0
      page.height=(long) next->rows+page.y;
1379
0
    if (next->page.height > page.height)
1380
0
      page.height=next->page.height;
1381
0
  }
1382
1383
0
  return page;
1384
0
}
1385

1386
/*
1387
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1388
%                                                                             %
1389
%                                                                             %
1390
%                                                                             %
1391
%     M o s a i c I m a g e s                                                 %
1392
%                                                                             %
1393
%                                                                             %
1394
%                                                                             %
1395
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1396
%
1397
%  MosaicImages() inlays an image sequence to form a single coherent picture.
1398
%  It returns a single image with each image in the sequence composited at
1399
%  the location defined by the page member of the image structure.
1400
%
1401
%  The format of the MosaicImage method is:
1402
%
1403
%      Image *MosaicImages(const Image *image,ExceptionInfo *exception)
1404
%
1405
%  A description of each parameter follows:
1406
%
1407
%    o image: The image.
1408
%
1409
%    o exception: Return any errors or warnings in this structure.
1410
%
1411
%
1412
*/
1413
MagickExport Image *MosaicImages(const Image *image,ExceptionInfo *exception)
1414
0
{
1415
0
#define MosaicImageText "[%s] Create mosaic..."
1416
1417
0
  Image
1418
0
    *mosaic_image;
1419
1420
0
  RectangleInfo
1421
0
    page;
1422
1423
0
  register const Image
1424
0
    *next;
1425
1426
0
  unsigned int
1427
0
    scene;
1428
1429
0
  MagickBool
1430
0
    matte;
1431
1432
0
  MagickPassFail
1433
0
    status;
1434
1435
0
  size_t
1436
0
    image_list_length;
1437
1438
0
  assert(image != (Image *) NULL);
1439
0
  assert(image->signature == MagickSignature);
1440
0
  assert(exception != (ExceptionInfo *) NULL);
1441
0
  assert(exception->signature == MagickSignature);
1442
1443
0
  image_list_length=GetImageListLength(image);
1444
1445
  /*
1446
    Determine mosaic bounding box.
1447
  */
1448
0
  page=GetImageMosaicDimensions(image);
1449
1450
  /*
1451
    Allocate canvas image.
1452
  */
1453
0
  mosaic_image=AllocateImage((ImageInfo *) NULL);
1454
0
  if (mosaic_image == (Image *) NULL)
1455
0
    return((Image *) NULL);
1456
0
  mosaic_image->columns=page.width;
1457
0
  mosaic_image->rows=page.height;
1458
1459
  /*
1460
    Canvas image supports transparency if any subordinate image uses
1461
    transparency.
1462
  */
1463
0
  matte=MagickTrue;
1464
0
  for (next=image; next != (Image *) NULL; next=next->next)
1465
0
    matte &= next->matte;
1466
0
  mosaic_image->matte=matte;
1467
1468
  /*
1469
    Canvas color is copied from background color of first image in
1470
    list.  Default canvas color is 'white' but opaque 'black' or
1471
    'transparent' is often best for composition.
1472
  */
1473
0
  mosaic_image->background_color=image->background_color;
1474
0
  (void) SetImage(mosaic_image,OpaqueOpacity);
1475
1476
  /*
1477
    Composite mosaic.
1478
  */
1479
0
  scene=0;
1480
0
  for (next=image; next != (Image *) NULL; next=next->next)
1481
0
  {
1482
0
    (void) CompositeImage(mosaic_image,next->compose,next,next->page.x,
1483
0
      next->page.y);
1484
0
    status=MagickMonitorFormatted(scene++,image_list_length,
1485
0
                                  exception,MosaicImageText,image->filename);
1486
0
    if (status == MagickFail)
1487
0
      break;
1488
0
  }
1489
0
  return(mosaic_image);
1490
0
}
1491

1492
/*
1493
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1494
%                                                                             %
1495
%                                                                             %
1496
%                                                                             %
1497
%   R o l l I m a g e                                                         %
1498
%                                                                             %
1499
%                                                                             %
1500
%                                                                             %
1501
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1502
%
1503
%  RollImage() offsets an image as defined by x_offset and y_offset.
1504
%
1505
%  The format of the RollImage method is:
1506
%
1507
%      Image *RollImage(const Image *image,const long x_offset,
1508
%        const long y_offset,ExceptionInfo *exception)
1509
%
1510
%  A description of each parameter follows:
1511
%
1512
%    o image: The image.
1513
%
1514
%    o x_offset: The number of columns to roll in the horizontal direction.
1515
%
1516
%    o y_offset: The number of rows to roll in the vertical direction.
1517
%
1518
%    o exception: Return any errors or warnings in this structure.
1519
%
1520
%
1521
*/
1522
MagickExport Image *RollImage(const Image *image,const long x_offset,
1523
                              const long y_offset,ExceptionInfo *exception)
1524
0
{
1525
0
  Image
1526
0
    *roll_image;
1527
1528
0
  RectangleInfo
1529
0
    offset;
1530
1531
  /*
1532
    Initialize roll image attributes.
1533
  */
1534
0
  assert(image != (const Image *) NULL);
1535
0
  assert(image->signature == MagickSignature);
1536
0
  assert(image->columns != 0);
1537
0
  assert(image->rows != 0);
1538
0
  assert(exception != (ExceptionInfo *) NULL);
1539
0
  assert(exception->signature == MagickSignature);
1540
0
  roll_image=CloneImage(image,image->columns,image->rows,True,exception);
1541
0
  if (roll_image == (Image *) NULL)
1542
0
    return((Image *) NULL);
1543
  /*
1544
    Roll image.
1545
  */
1546
0
  offset.x=x_offset;
1547
0
  offset.y=y_offset;
1548
0
  while (offset.x < 0)
1549
0
    offset.x+=image->columns;
1550
0
  while (offset.x >= (long) image->columns)
1551
0
    offset.x-=image->columns;
1552
0
  while (offset.y < 0)
1553
0
    offset.y+=image->rows;
1554
0
  while (offset.y >= (long) image->rows)
1555
0
    offset.y-=image->rows;
1556
1557
  /* Top left quadrant */
1558
0
  (void) CompositeImageRegion(CopyCompositeOp,0,offset.x,offset.y,image,
1559
0
                              image->columns-offset.x,image->rows-offset.y,
1560
0
                              roll_image,0,0,exception);
1561
1562
  /* Top right quadrant */
1563
0
  (void) CompositeImageRegion(CopyCompositeOp,0,image->columns-offset.x,offset.y,image,
1564
0
                              0,image->rows-offset.y,
1565
0
                              roll_image,offset.x,0,exception);
1566
1567
  /* Bottom left quadrant */
1568
0
  (void) CompositeImageRegion(CopyCompositeOp,0,offset.x,image->rows-offset.y,image,
1569
0
                              image->columns-offset.x,0,
1570
0
                              roll_image,0,offset.y,exception);
1571
1572
  /* Bottom right quadrant */
1573
0
  (void) CompositeImageRegion(CopyCompositeOp,0,image->columns-offset.x,image->rows-offset.y,image,
1574
0
                              0,0,
1575
0
                              roll_image,offset.x,offset.y,exception);
1576
1577
0
  roll_image->is_grayscale=image->is_grayscale;
1578
0
  return(roll_image);
1579
0
}
1580

1581
/*
1582
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1583
%                                                                             %
1584
%                                                                             %
1585
%                                                                             %
1586
%   S h a v e I m a g e                                                       %
1587
%                                                                             %
1588
%                                                                             %
1589
%                                                                             %
1590
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1591
%
1592
%  Method ShaveImage shaves pixels from the image edges.  It allocates the
1593
%  memory necessary for the new Image structure and returns a pointer to the
1594
%  new image.
1595
%
1596
%  The format of the ShaveImage method is:
1597
%
1598
%      Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1599
%        ExceptionInfo *exception)
1600
%
1601
%  A description of each parameter follows:
1602
%
1603
%    o shave_image: Method ShaveImage returns a pointer to the shaved
1604
%      image.  A null image is returned if there is a memory shortage or
1605
%      if the image width or height is zero.
1606
%
1607
%    o image: The image.
1608
%
1609
%    o shave_info: Specifies a pointer to a RectangleInfo which defines the
1610
%      region of the image to shave.
1611
%
1612
%    o exception: Return any errors or warnings in this structure.
1613
%
1614
%
1615
*/
1616
MagickExport Image *ShaveImage(const Image *image,
1617
  const RectangleInfo *shave_info,ExceptionInfo *exception)
1618
0
{
1619
0
  RectangleInfo
1620
0
    geometry;
1621
1622
0
  if (((2*shave_info->width) >= image->columns) ||
1623
0
      ((2*shave_info->height) >= image->rows))
1624
0
    ThrowImageException(OptionError,GeometryDoesNotContainImage,
1625
0
      MagickMsg(ResourceLimitError,UnableToShaveImage));
1626
0
  SetGeometry(image,&geometry);
1627
0
  geometry.width-=2*shave_info->width;
1628
0
  geometry.height-=2*shave_info->height;
1629
0
  geometry.x=(long) shave_info->width;
1630
0
  geometry.y=(long) shave_info->height;
1631
0
  return(CropImage(image,&geometry,exception));
1632
0
}
1633

1634
/*
1635
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1636
%                                                                             %
1637
%                                                                             %
1638
%                                                                             %
1639
%   T r a n s f o r m I m a g e                                               %
1640
%                                                                             %
1641
%                                                                             %
1642
%                                                                             %
1643
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1644
%
1645
%  TransformImage() is a convenience method that behaves like ResizeImage() or
1646
%  CropImage() but accepts scaling and/or cropping information as a region
1647
%  geometry specification.  If the operation fails, the original image handle
1648
%  is returned.
1649
%
1650
%  The format of the TransformImage method is:
1651
%
1652
%      MagickPassFail TransformImage(Image **image,const char *crop_geometry,
1653
%        const char *image_geometry)
1654
%
1655
%  A description of each parameter follows:
1656
%
1657
%    o image: The image to transform. The resulting transformed image is
1658
%      returned via this same parameter.
1659
%
1660
%    o crop_geometry: An optional crop geometry string in the form
1661
%      <width>x<height>{+-}<x>{+-}<y>{%}.  This geometry defines a subregion
1662
%      of the image to optionally crop.  If crop_geometry is non-NULL, then if
1663
%      the x and y offsets are present, a single image is generated, consisting
1664
%      of the pixels from the cropping region, otherwise a list of image tiles
1665
%      of the specified geometry, covering the entire input image is generated.
1666
%      In this case, the rightmost tiles and the bottom tiles are smaller if
1667
%      the specified geometry extends beyond the dimensions of the input image.
1668
%
1669
%    o image_geometry: An optional image geometry string in the form
1670
%      <width>x<height>.  This geometry defines the final size of the image,
1671
%      or image tiles.  If non-NULL, the resulting image (or image tiles)
1672
%      are resized similar to ZoomImage().
1673
%
1674
%
1675
*/
1676
MagickExport MagickPassFail TransformImage(Image **image,const char *crop_geometry,
1677
                                           const char *image_geometry)
1678
31.1k
{
1679
31.1k
  Image
1680
31.1k
    *previous,
1681
31.1k
    *resize_image,
1682
31.1k
    *transform_image;
1683
1684
31.1k
  RectangleInfo
1685
31.1k
    geometry;
1686
1687
31.1k
  int
1688
31.1k
    flags;
1689
1690
31.1k
  MagickPassFail
1691
31.1k
    status = MagickPass;
1692
1693
31.1k
  MagickBool
1694
31.1k
    transform_logging;
1695
1696
31.1k
  assert(image != (Image **) NULL);
1697
31.1k
  assert((*image)->signature == MagickSignature);
1698
1699
31.1k
  transform_logging=IsEventLogged(TransformEvent);
1700
31.1k
  transform_image=(*image);
1701
31.1k
  if (crop_geometry != (const char *) NULL)
1702
0
    {
1703
0
      Image
1704
0
        *crop_image;
1705
1706
      /*
1707
        Crop image to a user specified size.
1708
      */
1709
0
      crop_image=(Image *) NULL;
1710
0
      flags=GetImageGeometry(transform_image,crop_geometry,False,&geometry);
1711
0
      if (transform_logging)
1712
0
        (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1713
0
                              "Crop Geometry: %lux%lu%+ld%+ld",
1714
0
                              geometry.width, geometry.height,
1715
0
                              geometry.x, geometry.y);
1716
0
      if ((geometry.width == 0) || (geometry.height == 0) ||
1717
0
          ((flags & XValue) != 0) || ((flags & YValue) != 0))
1718
0
        {
1719
0
          crop_image=CropImage(transform_image,&geometry,&(*image)->exception);
1720
0
          if (crop_image == (Image *) NULL)
1721
0
            status = MagickFail;
1722
0
        }
1723
0
      else
1724
0
        if ((transform_image->columns > geometry.width) ||
1725
0
            (transform_image->rows > geometry.height))
1726
0
          {
1727
0
            Image
1728
0
              *next;
1729
1730
0
            long
1731
0
              x,
1732
0
              y;
1733
1734
0
            unsigned long
1735
0
              height,
1736
0
              width;
1737
1738
            /*
1739
              Crop repeatedly to create uniform subimages.
1740
            */
1741
0
            width=geometry.width;
1742
0
            height=geometry.height;
1743
0
            next=(Image *) NULL;
1744
0
            for (y=0; y < (long) transform_image->rows; y+=height)
1745
0
              {
1746
0
                for (x=0; x < (long) transform_image->columns; x+=width)
1747
0
                  {
1748
0
                    geometry.width=width;
1749
0
                    geometry.height=height;
1750
0
                    geometry.x=x;
1751
0
                    geometry.y=y;
1752
0
                    next=CropImage(transform_image,&geometry,&(*image)->exception);
1753
0
                    if (next == (Image *) NULL)
1754
0
                      break;
1755
0
                    if (crop_image == (Image *) NULL)
1756
0
                      crop_image=next;
1757
0
                    else
1758
0
                      {
1759
0
                        next->previous=crop_image;
1760
0
                        crop_image->next=next;
1761
0
                        crop_image=crop_image->next;
1762
0
                      }
1763
0
                  }
1764
0
                if (next == (Image *) NULL)
1765
0
                  {
1766
0
                    status=MagickFail;
1767
0
                    break;
1768
0
                  }
1769
0
              }
1770
0
          }
1771
0
      if (crop_image != (Image *) NULL)
1772
0
        {
1773
0
          previous=transform_image->previous;
1774
0
          crop_image->next=transform_image->next;
1775
0
          DestroyImage(transform_image);
1776
0
          transform_image=(Image *) NULL;
1777
0
          while (crop_image->previous != (Image *) NULL)
1778
0
            crop_image=crop_image->previous;
1779
0
          crop_image->previous=previous;
1780
0
          transform_image=crop_image;
1781
0
        }
1782
0
      *image=transform_image;
1783
0
    }
1784
31.1k
  if (image_geometry == (const char *) NULL)
1785
0
    return status;
1786
1787
  /*
1788
    Scale image to a user specified size.
1789
  */
1790
31.1k
  SetGeometry(transform_image,&geometry);
1791
31.1k
  flags=GetMagickGeometry(image_geometry,&geometry.x,&geometry.y,
1792
31.1k
                          &geometry.width,&geometry.height);
1793
31.1k
  if (transform_logging)
1794
0
    (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1795
0
                          "Transform Geometry: %lux%lu%+ld%+ld",
1796
0
                          geometry.width, geometry.height,
1797
0
                          geometry.x, geometry.y);
1798
31.1k
  if ((transform_image->columns == geometry.width) &&
1799
5.03k
      (transform_image->rows == geometry.height))
1800
4.94k
    return status;
1801
1802
  /*
1803
    Resize image.
1804
  */
1805
26.2k
  resize_image=ZoomImage(transform_image,geometry.width,geometry.height,
1806
26.2k
                         &(*image)->exception);
1807
26.2k
  if (resize_image == (Image *) NULL)
1808
628
    {
1809
628
      status=MagickFail;
1810
628
      return status;
1811
628
    }
1812
1813
25.5k
  ReplaceImageInList(&transform_image,resize_image);
1814
25.5k
  *image=transform_image;
1815
25.5k
  return status;
1816
26.2k
}