Coverage Report

Created: 2025-06-16 07:00

/src/imagemagick/MagickCore/transform.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
%                                                                             %
4
%                                                                             %
5
%                                                                             %
6
%       TTTTT  RRRR    AAA   N   N  SSSSS  FFFFF   OOO   RRRR   M   M         %
7
%         T    R   R  A   A  NN  N  SS     F      O   O  R   R  MM MM         %
8
%         T    RRRR   AAAAA  N N N   SSS   FFF    O   O  RRRR   M M M         %
9
%         T    R R    A   A  N  NN     SS  F      O   O  R R    M   M         %
10
%         T    R  R   A   A  N   N  SSSSS  F       OOO   R  R   M   M         %
11
%                                                                             %
12
%                                                                             %
13
%                    MagickCore Image Transform Methods                       %
14
%                                                                             %
15
%                              Software Design                                %
16
%                                   Cristy                                    %
17
%                                 July 1992                                   %
18
%                                                                             %
19
%                                                                             %
20
%  Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization         %
21
%  dedicated to making software imaging solutions freely available.           %
22
%                                                                             %
23
%  You may not use this file except in compliance with the License.  You may  %
24
%  obtain a copy of the License at                                            %
25
%                                                                             %
26
%    https://imagemagick.org/script/license.php                               %
27
%                                                                             %
28
%  Unless required by applicable law or agreed to in writing, software        %
29
%  distributed under the License is distributed on an "AS IS" BASIS,          %
30
%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31
%  See the License for the specific language governing permissions and        %
32
%  limitations under the License.                                             %
33
%                                                                             %
34
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35
%
36
%
37
*/
38

39
/*
40
  Include declarations.
41
*/
42
#include "MagickCore/studio.h"
43
#include "MagickCore/attribute.h"
44
#include "MagickCore/artifact.h"
45
#include "MagickCore/cache.h"
46
#include "MagickCore/cache-view.h"
47
#include "MagickCore/color.h"
48
#include "MagickCore/color-private.h"
49
#include "MagickCore/colorspace-private.h"
50
#include "MagickCore/composite.h"
51
#include "MagickCore/composite-private.h"
52
#include "MagickCore/distort.h"
53
#include "MagickCore/draw.h"
54
#include "MagickCore/effect.h"
55
#include "MagickCore/exception.h"
56
#include "MagickCore/exception-private.h"
57
#include "MagickCore/geometry.h"
58
#include "MagickCore/image.h"
59
#include "MagickCore/memory_.h"
60
#include "MagickCore/layer.h"
61
#include "MagickCore/list.h"
62
#include "MagickCore/monitor.h"
63
#include "MagickCore/monitor-private.h"
64
#include "MagickCore/pixel-accessor.h"
65
#include "MagickCore/profile-private.h"
66
#include "MagickCore/property.h"
67
#include "MagickCore/resource_.h"
68
#include "MagickCore/resize.h"
69
#include "MagickCore/statistic.h"
70
#include "MagickCore/string_.h"
71
#include "MagickCore/thread-private.h"
72
#include "MagickCore/transform.h"
73
#include "MagickCore/transform-private.h"
74

75
/*
76
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
77
%                                                                             %
78
%                                                                             %
79
%                                                                             %
80
%   A u t o O r i e n t I m a g e                                             %
81
%                                                                             %
82
%                                                                             %
83
%                                                                             %
84
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
85
%
86
%  AutoOrientImage() adjusts an image so that its orientation is suitable for
87
%  viewing (i.e. top-left orientation).
88
%
89
%  The format of the AutoOrientImage method is:
90
%
91
%      Image *AutoOrientImage(const Image *image,
92
%        const OrientationType orientation,ExceptionInfo *exception)
93
%
94
%  A description of each parameter follows:
95
%
96
%    o image: The image.
97
%
98
%    o orientation: Current image orientation.
99
%
100
%    o exception: Return any errors or warnings in this structure.
101
%
102
*/
103
MagickExport Image *AutoOrientImage(const Image *image,
104
  const OrientationType orientation,ExceptionInfo *exception)
105
0
{
106
0
  Image
107
0
    *orient_image;
108
109
0
  assert(image != (const Image *) NULL);
110
0
  assert(image->signature == MagickCoreSignature);
111
0
  assert(exception != (ExceptionInfo *) NULL);
112
0
  assert(exception->signature == MagickCoreSignature);
113
0
  orient_image=(Image *) NULL;
114
0
  switch (orientation)
115
0
  {
116
0
    case UndefinedOrientation:
117
0
    case TopLeftOrientation:
118
0
    default:
119
0
    {
120
0
      orient_image=CloneImage(image,0,0,MagickTrue,exception);
121
0
      break;
122
0
    }
123
0
    case TopRightOrientation:
124
0
    {
125
0
      orient_image=FlopImage(image,exception);
126
0
      break;
127
0
    }
128
0
    case BottomRightOrientation:
129
0
    {
130
0
      orient_image=RotateImage(image,180.0,exception);
131
0
      break;
132
0
    }
133
0
    case BottomLeftOrientation:
134
0
    {
135
0
      orient_image=FlipImage(image,exception);
136
0
      break;
137
0
    }
138
0
    case LeftTopOrientation:
139
0
    {
140
0
      orient_image=TransposeImage(image,exception);
141
0
      break;
142
0
    }
143
0
    case RightTopOrientation:
144
0
    {
145
0
      orient_image=RotateImage(image,90.0,exception);
146
0
      break;
147
0
    }
148
0
    case RightBottomOrientation:
149
0
    {
150
0
      orient_image=TransverseImage(image,exception);
151
0
      break;
152
0
    }
153
0
    case LeftBottomOrientation:
154
0
    {
155
0
      orient_image=RotateImage(image,270.0,exception);
156
0
      break;
157
0
    }
158
0
  }
159
0
  if (orient_image != (Image *) NULL)
160
0
    orient_image->orientation=TopLeftOrientation;
161
0
  return(orient_image);
162
0
}
163

164
/*
165
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
166
%                                                                             %
167
%                                                                             %
168
%                                                                             %
169
%   C h o p I m a g e                                                         %
170
%                                                                             %
171
%                                                                             %
172
%                                                                             %
173
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
174
%
175
%  ChopImage() removes a region of an image and collapses the image to occupy
176
%  the removed portion.
177
%
178
%  The format of the ChopImage method is:
179
%
180
%      Image *ChopImage(const Image *image,const RectangleInfo *chop_info)
181
%        ExceptionInfo *exception)
182
%
183
%  A description of each parameter follows:
184
%
185
%    o image: the image.
186
%
187
%    o chop_info: Define the region of the image to chop.
188
%
189
%    o exception: return any errors or warnings in this structure.
190
%
191
*/
192
MagickExport Image *ChopImage(const Image *image,const RectangleInfo *chop_info,
193
  ExceptionInfo *exception)
194
0
{
195
0
#define ChopImageTag  "Chop/Image"
196
197
0
  CacheView
198
0
    *chop_view,
199
0
    *image_view;
200
201
0
  Image
202
0
    *chop_image;
203
204
0
  MagickBooleanType
205
0
    status;
206
207
0
  MagickOffsetType
208
0
    progress;
209
210
0
  RectangleInfo
211
0
    extent;
212
213
0
  ssize_t
214
0
    y;
215
216
  /*
217
    Check chop geometry.
218
  */
219
0
  assert(image != (const Image *) NULL);
220
0
  assert(image->signature == MagickCoreSignature);
221
0
  assert(exception != (ExceptionInfo *) NULL);
222
0
  assert(exception->signature == MagickCoreSignature);
223
0
  assert(chop_info != (RectangleInfo *) NULL);
224
0
  if (IsEventLogging() != MagickFalse)
225
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
226
0
  if (((chop_info->x+(ssize_t) chop_info->width) < 0) ||
227
0
      ((chop_info->y+(ssize_t) chop_info->height) < 0) ||
228
0
      (chop_info->x > (ssize_t) image->columns) ||
229
0
      (chop_info->y > (ssize_t) image->rows))
230
0
    ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
231
0
  extent=(*chop_info);
232
0
  if ((extent.x+(ssize_t) extent.width) > (ssize_t) image->columns)
233
0
    extent.width=(size_t) ((ssize_t) image->columns-extent.x);
234
0
  if ((extent.y+(ssize_t) extent.height) > (ssize_t) image->rows)
235
0
    extent.height=(size_t) ((ssize_t) image->rows-extent.y);
236
0
  if (extent.x < 0)
237
0
    {
238
0
      extent.width-=(size_t) (-extent.x);
239
0
      extent.x=0;
240
0
    }
241
0
  if (extent.y < 0)
242
0
    {
243
0
      extent.height-=(size_t) (-extent.y);
244
0
      extent.y=0;
245
0
    }
246
0
  if ((extent.width >= image->columns) || (extent.height >= image->rows))
247
0
    ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
248
0
  chop_image=CloneImage(image,image->columns-extent.width,image->rows-
249
0
    extent.height,MagickTrue,exception);
250
0
  if (chop_image == (Image *) NULL)
251
0
    return((Image *) NULL);
252
  /*
253
    Extract chop image.
254
  */
255
0
  status=MagickTrue;
256
0
  progress=0;
257
0
  image_view=AcquireVirtualCacheView(image,exception);
258
0
  chop_view=AcquireAuthenticCacheView(chop_image,exception);
259
#if defined(MAGICKCORE_OPENMP_SUPPORT)
260
  #pragma omp parallel for schedule(static) shared(status) \
261
    magick_number_threads(image,chop_image,(size_t) extent.y,2)
262
#endif
263
0
  for (y=0; y < (ssize_t) extent.y; y++)
264
0
  {
265
0
    const Quantum
266
0
      *magick_restrict p;
267
268
0
    ssize_t
269
0
      x;
270
271
0
    Quantum
272
0
      *magick_restrict q;
273
274
0
    if (status == MagickFalse)
275
0
      continue;
276
0
    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
277
0
    q=QueueCacheViewAuthenticPixels(chop_view,0,y,chop_image->columns,1,
278
0
      exception);
279
0
    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
280
0
      {
281
0
        status=MagickFalse;
282
0
        continue;
283
0
      }
284
0
    for (x=0; x < (ssize_t) image->columns; x++)
285
0
    {
286
0
      if ((x < extent.x) || (x >= (extent.x+(ssize_t) extent.width)))
287
0
        {
288
0
          ssize_t
289
0
            i;
290
291
0
          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
292
0
          {
293
0
            PixelChannel channel = GetPixelChannelChannel(image,i);
294
0
            PixelTrait traits = GetPixelChannelTraits(image,channel);
295
0
            PixelTrait chop_traits=GetPixelChannelTraits(chop_image,channel);
296
0
            if ((traits == UndefinedPixelTrait) ||
297
0
                (chop_traits == UndefinedPixelTrait))
298
0
              continue;
299
0
            SetPixelChannel(chop_image,channel,p[i],q);
300
0
          }
301
0
          q+=(ptrdiff_t) GetPixelChannels(chop_image);
302
0
        }
303
0
      p+=(ptrdiff_t) GetPixelChannels(image);
304
0
    }
305
0
    if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
306
0
      status=MagickFalse;
307
0
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
308
0
      {
309
0
        MagickBooleanType
310
0
          proceed;
311
312
#if defined(MAGICKCORE_OPENMP_SUPPORT)
313
        #pragma omp atomic
314
#endif
315
0
        progress++;
316
0
        proceed=SetImageProgress(image,ChopImageTag,progress,image->rows);
317
0
        if (proceed == MagickFalse)
318
0
          status=MagickFalse;
319
0
      }
320
0
  }
321
  /*
322
    Extract chop image.
323
  */
324
#if defined(MAGICKCORE_OPENMP_SUPPORT)
325
  #pragma omp parallel for schedule(static) shared(progress,status) \
326
    magick_number_threads(image,chop_image,image->rows-((size_t) extent.y+extent.height),2)
327
#endif
328
0
  for (y=0; y < (ssize_t) (image->rows-((size_t) extent.y+extent.height)); y++)
329
0
  {
330
0
    const Quantum
331
0
      *magick_restrict p;
332
333
0
    ssize_t
334
0
      x;
335
336
0
    Quantum
337
0
      *magick_restrict q;
338
339
0
    if (status == MagickFalse)
340
0
      continue;
341
0
    p=GetCacheViewVirtualPixels(image_view,0,extent.y+(ssize_t) extent.height+y,
342
0
      image->columns,1,exception);
343
0
    q=QueueCacheViewAuthenticPixels(chop_view,0,extent.y+y,chop_image->columns,
344
0
      1,exception);
345
0
    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
346
0
      {
347
0
        status=MagickFalse;
348
0
        continue;
349
0
      }
350
0
    for (x=0; x < (ssize_t) image->columns; x++)
351
0
    {
352
0
      if ((x < extent.x) || (x >= (extent.x+(ssize_t) extent.width)))
353
0
        {
354
0
          ssize_t
355
0
            i;
356
357
0
          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
358
0
          {
359
0
            PixelChannel channel = GetPixelChannelChannel(image,i);
360
0
            PixelTrait traits = GetPixelChannelTraits(image,channel);
361
0
            PixelTrait chop_traits=GetPixelChannelTraits(chop_image,channel);
362
0
            if ((traits == UndefinedPixelTrait) ||
363
0
                (chop_traits == UndefinedPixelTrait))
364
0
              continue;
365
0
            SetPixelChannel(chop_image,channel,p[i],q);
366
0
          }
367
0
          q+=(ptrdiff_t) GetPixelChannels(chop_image);
368
0
        }
369
0
      p+=(ptrdiff_t) GetPixelChannels(image);
370
0
    }
371
0
    if (SyncCacheViewAuthenticPixels(chop_view,exception) == MagickFalse)
372
0
      status=MagickFalse;
373
0
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
374
0
      {
375
0
        MagickBooleanType
376
0
          proceed;
377
378
#if defined(MAGICKCORE_OPENMP_SUPPORT)
379
        #pragma omp atomic
380
#endif
381
0
        progress++;
382
0
        proceed=SetImageProgress(image,ChopImageTag,progress,image->rows);
383
0
        if (proceed == MagickFalse)
384
0
          status=MagickFalse;
385
0
      }
386
0
  }
387
0
  chop_view=DestroyCacheView(chop_view);
388
0
  image_view=DestroyCacheView(image_view);
389
0
  chop_image->type=image->type;
390
0
  if (status == MagickFalse)
391
0
    chop_image=DestroyImage(chop_image);
392
0
  return(chop_image);
393
0
}
394

395
/*
396
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
397
%                                                                             %
398
%                                                                             %
399
%                                                                             %
400
+     C o n s o l i d a t e C M Y K I m a g e                                 %
401
%                                                                             %
402
%                                                                             %
403
%                                                                             %
404
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
405
%
406
%  ConsolidateCMYKImage() consolidates separate C, M, Y, and K planes into a
407
%  single image.
408
%
409
%  The format of the ConsolidateCMYKImage method is:
410
%
411
%      Image *ConsolidateCMYKImage(const Image *image,ExceptionInfo *exception)
412
%
413
%  A description of each parameter follows:
414
%
415
%    o image: the image sequence.
416
%
417
%    o exception: return any errors or warnings in this structure.
418
%
419
*/
420
MagickExport Image *ConsolidateCMYKImages(const Image *images,
421
  ExceptionInfo *exception)
422
0
{
423
0
  CacheView
424
0
    *cmyk_view,
425
0
    *image_view;
426
427
0
  Image
428
0
    *cmyk_image,
429
0
    *cmyk_images;
430
431
0
  ssize_t
432
0
    j;
433
434
0
  ssize_t
435
0
    y;
436
437
  /*
438
    Consolidate separate C, M, Y, and K planes into a single image.
439
  */
440
0
  assert(images != (Image *) NULL);
441
0
  assert(images->signature == MagickCoreSignature);
442
0
  assert(exception != (ExceptionInfo *) NULL);
443
0
  assert(exception->signature == MagickCoreSignature);
444
0
  if (IsEventLogging() != MagickFalse)
445
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
446
0
  cmyk_images=NewImageList();
447
0
  for (j=0; j < (ssize_t) GetImageListLength(images); j+=4)
448
0
  {
449
0
    ssize_t
450
0
      i;
451
452
0
    assert(images != (Image *) NULL);
453
0
    cmyk_image=CloneImage(images,0,0,MagickTrue,
454
0
      exception);
455
0
    if (cmyk_image == (Image *) NULL)
456
0
      break;
457
0
    if (SetImageStorageClass(cmyk_image,DirectClass,exception) == MagickFalse)
458
0
      break;
459
0
    (void) SetImageColorspace(cmyk_image,CMYKColorspace,exception);
460
0
    for (i=0; i < 4; i++)
461
0
    {
462
0
      image_view=AcquireVirtualCacheView(images,exception);
463
0
      cmyk_view=AcquireAuthenticCacheView(cmyk_image,exception);
464
0
      for (y=0; y < (ssize_t) images->rows; y++)
465
0
      {
466
0
        const Quantum
467
0
          *magick_restrict p;
468
469
0
        ssize_t
470
0
          x;
471
472
0
        Quantum
473
0
          *magick_restrict q;
474
475
0
        p=GetCacheViewVirtualPixels(image_view,0,y,images->columns,1,exception);
476
0
        q=QueueCacheViewAuthenticPixels(cmyk_view,0,y,cmyk_image->columns,1,
477
0
          exception);
478
0
        if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
479
0
          break;
480
0
        for (x=0; x < (ssize_t) images->columns; x++)
481
0
        {
482
0
          Quantum
483
0
            pixel;
484
485
0
          pixel=ClampToQuantum((double) QuantumRange-
486
0
            GetPixelIntensity(images,p));
487
0
          switch (i)
488
0
          {
489
0
            case 0: SetPixelCyan(cmyk_image,pixel,q);  break;
490
0
            case 1: SetPixelMagenta(cmyk_image,pixel,q);  break;
491
0
            case 2: SetPixelYellow(cmyk_image,pixel,q);  break;
492
0
            case 3: SetPixelBlack(cmyk_image,pixel,q);  break;
493
0
            default: break;
494
0
          }
495
0
          p+=(ptrdiff_t) GetPixelChannels(images);
496
0
          q+=(ptrdiff_t) GetPixelChannels(cmyk_image);
497
0
        }
498
0
        if (SyncCacheViewAuthenticPixels(cmyk_view,exception) == MagickFalse)
499
0
          break;
500
0
      }
501
0
      cmyk_view=DestroyCacheView(cmyk_view);
502
0
      image_view=DestroyCacheView(image_view);
503
0
      images=GetNextImageInList(images);
504
0
      if (images == (Image *) NULL)
505
0
        break;
506
0
    }
507
0
    AppendImageToList(&cmyk_images,cmyk_image);
508
0
  }
509
0
  return(cmyk_images);
510
0
}
511

512
/*
513
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
514
%                                                                             %
515
%                                                                             %
516
%                                                                             %
517
%   C r o p I m a g e                                                         %
518
%                                                                             %
519
%                                                                             %
520
%                                                                             %
521
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
522
%
523
%  CropImage() extracts a region of the image starting at the offset defined
524
%  by geometry.  Region must be fully defined, and no special handling of
525
%  geometry flags is performed.
526
%
527
%  The format of the CropImage method is:
528
%
529
%      Image *CropImage(const Image *image,const RectangleInfo *geometry,
530
%        ExceptionInfo *exception)
531
%
532
%  A description of each parameter follows:
533
%
534
%    o image: the image.
535
%
536
%    o geometry: Define the region of the image to crop with members
537
%      x, y, width, and height.
538
%
539
%    o exception: return any errors or warnings in this structure.
540
%
541
*/
542
MagickExport Image *CropImage(const Image *image,const RectangleInfo *geometry,
543
  ExceptionInfo *exception)
544
6.43k
{
545
6.43k
#define CropImageTag  "Crop/Image"
546
547
6.43k
  CacheView
548
6.43k
    *crop_view,
549
6.43k
    *image_view;
550
551
6.43k
  Image
552
6.43k
    *crop_image;
553
554
6.43k
  MagickBooleanType
555
6.43k
    status;
556
557
6.43k
  MagickOffsetType
558
6.43k
    progress;
559
560
6.43k
  OffsetInfo
561
6.43k
    offset;
562
563
6.43k
  RectangleInfo
564
6.43k
    bounding_box,
565
6.43k
    page;
566
567
6.43k
  ssize_t
568
6.43k
    y;
569
570
  /*
571
    Check crop geometry.
572
  */
573
6.43k
  assert(image != (const Image *) NULL);
574
6.43k
  assert(image->signature == MagickCoreSignature);
575
6.43k
  assert(geometry != (const RectangleInfo *) NULL);
576
6.43k
  assert(exception != (ExceptionInfo *) NULL);
577
6.43k
  assert(exception->signature == MagickCoreSignature);
578
6.43k
  if (IsEventLogging() != MagickFalse)
579
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
580
6.43k
  bounding_box=image->page;
581
6.43k
  if ((bounding_box.width == 0) || (bounding_box.height == 0))
582
2.85k
    {
583
2.85k
      bounding_box.width=image->columns;
584
2.85k
      bounding_box.height=image->rows;
585
2.85k
    }
586
6.43k
  page=(*geometry);
587
6.43k
  if (page.width == 0)
588
1.65k
    page.width=bounding_box.width;
589
6.43k
  if (page.height == 0)
590
347
    page.height=bounding_box.height;
591
6.43k
  if ((((double) bounding_box.x-page.x) >= (double) page.width) ||
592
6.43k
      (((double) bounding_box.y-page.y) >= (double) page.height) ||
593
6.43k
      (((double) page.x-bounding_box.x) > (double) image->columns) ||
594
6.43k
      (((double) page.y-bounding_box.y) > (double) image->rows))
595
701
    {
596
      /*
597
        Crop is not within virtual canvas, return 1 pixel transparent image.
598
      */
599
701
      (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
600
701
        "GeometryDoesNotContainImage","(\"%.20gx%.20g%+.20g%+.20g\") `%s'",
601
701
        (double) geometry->width,(double) geometry->height,
602
701
        (double) geometry->x,(double) geometry->y,image->filename);
603
701
      crop_image=CloneImage(image,1,1,MagickTrue,exception);
604
701
      if (crop_image == (Image *) NULL)
605
0
        return((Image *) NULL);
606
701
      crop_image->background_color.alpha_trait=BlendPixelTrait;
607
701
      crop_image->background_color.alpha=(MagickRealType) TransparentAlpha;
608
701
      (void) SetImageBackgroundColor(crop_image,exception);
609
701
      crop_image->page=bounding_box;
610
701
      crop_image->page.x=(-1);
611
701
      crop_image->page.y=(-1);
612
701
      if (crop_image->dispose == BackgroundDispose)
613
0
        crop_image->dispose=NoneDispose;
614
701
      return(crop_image);
615
701
    }
616
5.73k
  if ((page.x < 0) && (bounding_box.x >= 0))
617
1.28k
    {
618
1.28k
      page.width=CastDoubleToSizeT((double) page.width+page.x-bounding_box.x);
619
1.28k
      page.x=0;
620
1.28k
    }
621
4.44k
  else
622
4.44k
    {
623
4.44k
      page.width=CastDoubleToSizeT((double) page.width-(bounding_box.x-page.x));
624
4.44k
      page.x-=bounding_box.x;
625
4.44k
      if (page.x < 0)
626
120
        page.x=0;
627
4.44k
    }
628
5.73k
  if ((page.y < 0) && (bounding_box.y >= 0))
629
482
    {
630
482
      page.height=CastDoubleToSizeT((double) page.height+page.y-bounding_box.y);
631
482
      page.y=0;
632
482
    }
633
5.25k
  else
634
5.25k
    {
635
5.25k
      page.height=CastDoubleToSizeT((double) page.height-(bounding_box.y-page.y));
636
5.25k
      page.y-=bounding_box.y;
637
5.25k
      if (page.y < 0)
638
104
        page.y=0;
639
5.25k
    }
640
5.73k
  if ((page.x+(ssize_t) page.width) > (ssize_t) image->columns)
641
1.24k
    page.width=(size_t) ((ssize_t) image->columns-page.x);
642
5.73k
  if ((geometry->width != 0) && (page.width > geometry->width))
643
13
    page.width=geometry->width;
644
5.73k
  if ((page.y+(ssize_t) page.height) > (ssize_t) image->rows)
645
1.33k
    page.height=(size_t) ((ssize_t) image->rows-page.y);
646
5.73k
  if ((geometry->height != 0) && (page.height > geometry->height))
647
133
    page.height=geometry->height;
648
5.73k
  bounding_box.x+=page.x;
649
5.73k
  bounding_box.y+=page.y;
650
5.73k
  if ((page.width == 0) || (page.height == 0))
651
232
    {
652
232
      (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning,
653
232
        "GeometryDoesNotContainImage","`%s'",image->filename);
654
232
      return((Image *) NULL);
655
232
    }
656
  /*
657
    Initialize crop image attributes.
658
  */
659
5.50k
  crop_image=CloneImage(image,page.width,page.height,MagickTrue,exception);
660
5.50k
  if (crop_image == (Image *) NULL)
661
1.33k
    return((Image *) NULL);
662
4.17k
  crop_image->page.width=image->page.width;
663
4.17k
  crop_image->page.height=image->page.height;
664
4.17k
  offset.x=bounding_box.x+(ssize_t) bounding_box.width;
665
4.17k
  offset.y=bounding_box.y+(ssize_t) bounding_box.height;
666
4.17k
  if ((offset.x > (ssize_t) image->page.width) ||
667
4.17k
      (offset.y > (ssize_t) image->page.height))
668
2.98k
    {
669
2.98k
      crop_image->page.width=bounding_box.width;
670
2.98k
      crop_image->page.height=bounding_box.height;
671
2.98k
    }
672
4.17k
  crop_image->page.x=bounding_box.x;
673
4.17k
  crop_image->page.y=bounding_box.y;
674
  /*
675
    Crop image.
676
  */
677
4.17k
  status=MagickTrue;
678
4.17k
  progress=0;
679
4.17k
  image_view=AcquireVirtualCacheView(image,exception);
680
4.17k
  crop_view=AcquireAuthenticCacheView(crop_image,exception);
681
#if defined(MAGICKCORE_OPENMP_SUPPORT)
682
  #pragma omp parallel for schedule(static) shared(status) \
683
    magick_number_threads(image,crop_image,crop_image->rows,2)
684
#endif
685
284k
  for (y=0; y < (ssize_t) crop_image->rows; y++)
686
280k
  {
687
280k
    const Quantum
688
280k
      *magick_restrict p;
689
690
280k
    Quantum
691
280k
      *magick_restrict q;
692
693
280k
    ssize_t
694
280k
      x;
695
696
280k
    if (status == MagickFalse)
697
3.39k
      continue;
698
276k
    p=GetCacheViewVirtualPixels(image_view,page.x,page.y+y,crop_image->columns,
699
276k
      1,exception);
700
276k
    q=QueueCacheViewAuthenticPixels(crop_view,0,y,crop_image->columns,1,
701
276k
      exception);
702
276k
    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
703
125
      {
704
125
        status=MagickFalse;
705
125
        continue;
706
125
      }
707
55.9M
    for (x=0; x < (ssize_t) crop_image->columns; x++)
708
55.6M
    {
709
55.6M
      ssize_t
710
55.6M
        i;
711
712
275M
      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
713
219M
      {
714
219M
        PixelChannel channel = GetPixelChannelChannel(image,i);
715
219M
        PixelTrait traits = GetPixelChannelTraits(image,channel);
716
219M
        PixelTrait crop_traits=GetPixelChannelTraits(crop_image,channel);
717
219M
        if ((traits == UndefinedPixelTrait) ||
718
219M
            (crop_traits == UndefinedPixelTrait))
719
0
          continue;
720
219M
        SetPixelChannel(crop_image,channel,p[i],q);
721
219M
      }
722
55.6M
      p+=(ptrdiff_t) GetPixelChannels(image);
723
55.6M
      q+=(ptrdiff_t) GetPixelChannels(crop_image);
724
55.6M
    }
725
276k
    if (SyncCacheViewAuthenticPixels(crop_view,exception) == MagickFalse)
726
0
      status=MagickFalse;
727
276k
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
728
0
      {
729
0
        MagickBooleanType
730
0
          proceed;
731
732
#if defined(MAGICKCORE_OPENMP_SUPPORT)
733
        #pragma omp atomic
734
#endif
735
0
        progress++;
736
0
        proceed=SetImageProgress(image,CropImageTag,progress,image->rows);
737
0
        if (proceed == MagickFalse)
738
0
          status=MagickFalse;
739
0
      }
740
276k
  }
741
4.17k
  crop_view=DestroyCacheView(crop_view);
742
4.17k
  image_view=DestroyCacheView(image_view);
743
4.17k
  crop_image->type=image->type;
744
4.17k
  if (status == MagickFalse)
745
125
    crop_image=DestroyImage(crop_image);
746
4.17k
  return(crop_image);
747
5.50k
}
748

749
/*
750
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
751
%                                                                             %
752
%                                                                             %
753
%                                                                             %
754
%   C r o p I m a g e T o T i l e s                                           %
755
%                                                                             %
756
%                                                                             %
757
%                                                                             %
758
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
759
%
760
%  CropImageToTiles() crops a single image, into a possible list of tiles.
761
%  This may include a single sub-region of the image.  This basically applies
762
%  all the normal geometry flags for Crop.
763
%
764
%      Image *CropImageToTiles(const Image *image,
765
%        const RectangleInfo *crop_geometry, ExceptionInfo *exception)
766
%
767
%  A description of each parameter follows:
768
%
769
%    o image: the image The transformed image is returned as this parameter.
770
%
771
%    o crop_geometry: A crop geometry string.
772
%
773
%    o exception: return any errors or warnings in this structure.
774
%
775
*/
776
777
static inline ssize_t PixelRoundOffset(double x)
778
0
{
779
  /*
780
    Round the fraction to nearest integer.
781
  */
782
0
  if ((x-floor(x)) < (ceil(x)-x))
783
0
    return(CastDoubleToSsizeT(floor(x)));
784
0
  return(CastDoubleToSsizeT(ceil(x)));
785
0
}
786
787
MagickExport Image *CropImageToTiles(const Image *image,
788
  const char *crop_geometry,ExceptionInfo *exception)
789
0
{
790
0
  Image
791
0
    *next,
792
0
    *crop_image;
793
794
0
  MagickStatusType
795
0
    flags;
796
797
0
  RectangleInfo
798
0
    geometry;
799
800
0
  assert(image != (Image *) NULL);
801
0
  assert(image->signature == MagickCoreSignature);
802
0
  if (IsEventLogging() != MagickFalse)
803
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
804
0
  flags=ParseGravityGeometry(image,crop_geometry,&geometry,exception);
805
0
  if ((flags & AreaValue) != 0)
806
0
    {
807
0
      PointInfo
808
0
        delta,
809
0
        offset;
810
811
0
      RectangleInfo
812
0
        crop;
813
814
0
      size_t
815
0
        height,
816
0
        width;
817
818
      /*
819
        Crop into NxM tiles (@ flag).
820
      */
821
0
      crop_image=NewImageList();
822
0
      width=image->columns;
823
0
      height=image->rows;
824
0
      if (geometry.width == 0)
825
0
        geometry.width=1;
826
0
      if (geometry.height == 0)
827
0
        geometry.height=1;
828
0
      if ((flags & AspectValue) == 0)
829
0
        {
830
0
          width=(size_t) ((ssize_t) width-(geometry.x < 0 ? -1 : 1)*geometry.x);
831
0
          height=(size_t) ((ssize_t) height-(geometry.y < 0 ? -1 : 1)*
832
0
            geometry.y);
833
0
        }
834
0
      else
835
0
        {
836
0
          width=(size_t) ((ssize_t) width+(geometry.x < 0 ? -1 : 1)*geometry.x);
837
0
          height=(size_t) ((ssize_t) height+(geometry.y < 0 ? -1 : 1)*
838
0
            geometry.y);
839
0
        }
840
0
      delta.x=(double) width/geometry.width;
841
0
      delta.y=(double) height/geometry.height;
842
0
      if (delta.x < 1.0)
843
0
        delta.x=1.0;
844
0
      if (delta.y < 1.0)
845
0
        delta.y=1.0;
846
0
      for (offset.y=0; offset.y < (double) height; )
847
0
      {
848
0
        if ((flags & AspectValue) == 0)
849
0
          {
850
0
            crop.y=PixelRoundOffset((double) (offset.y-
851
0
              (geometry.y > 0 ? 0 : geometry.y)));
852
0
            offset.y+=delta.y;   /* increment now to find width */
853
0
            crop.height=(size_t) PixelRoundOffset((double) (offset.y+
854
0
              (geometry.y < 0 ? 0 : geometry.y)));
855
0
          }
856
0
        else
857
0
          {
858
0
            crop.y=PixelRoundOffset((double) (offset.y-
859
0
              (geometry.y > 0 ? geometry.y : 0)));
860
0
            offset.y+=delta.y;  /* increment now to find width */
861
0
            crop.height=(size_t) PixelRoundOffset((double)
862
0
              (offset.y+(geometry.y < -1 ? geometry.y : 0)));
863
0
          }
864
0
        crop.height=(size_t) ((ssize_t) crop.height-crop.y);
865
0
        crop.y+=image->page.y;
866
0
        for (offset.x=0; offset.x < (double) width; )
867
0
        {
868
0
          if ((flags & AspectValue) == 0)
869
0
            {
870
0
              crop.x=PixelRoundOffset((double) (offset.x-
871
0
                (geometry.x > 0 ? 0 : geometry.x)));
872
0
              offset.x+=delta.x;  /* increment now to find height */
873
0
              crop.width=(size_t) PixelRoundOffset((double) (offset.x+
874
0
                (geometry.x < 0 ? 0 : geometry.x)));
875
0
            }
876
0
          else
877
0
            {
878
0
              crop.x=PixelRoundOffset((double) (offset.x-
879
0
                (geometry.x > 0 ? geometry.x : 0)));
880
0
              offset.x+=delta.x;  /* increment now to find height */
881
0
              crop.width=(size_t) PixelRoundOffset((double) (offset.x+
882
0
                (geometry.x < 0 ? geometry.x : 0)));
883
0
            }
884
0
          crop.width=(size_t) ((ssize_t) crop.width-crop.x);
885
0
          crop.x+=image->page.x;
886
0
          next=CropImage(image,&crop,exception);
887
0
          if (next != (Image *) NULL)
888
0
            AppendImageToList(&crop_image,next);
889
0
        }
890
0
      }
891
0
      ClearMagickException(exception);
892
0
      return(crop_image);
893
0
    }
894
0
  if (((geometry.width == 0) && (geometry.height == 0)) ||
895
0
      ((flags & XValue) != 0) || ((flags & YValue) != 0))
896
0
    {
897
      /*
898
        Crop a single region at +X+Y.
899
      */
900
0
      crop_image=CropImage(image,&geometry,exception);
901
0
      if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0))
902
0
        {
903
0
          crop_image->page.width=geometry.width;
904
0
          crop_image->page.height=geometry.height;
905
0
          crop_image->page.x-=geometry.x;
906
0
          crop_image->page.y-=geometry.y;
907
0
        }
908
0
      return(crop_image);
909
0
    }
910
0
  if ((image->columns > geometry.width) || (image->rows > geometry.height))
911
0
    {
912
0
      RectangleInfo
913
0
        page;
914
915
0
      size_t
916
0
        height,
917
0
        width;
918
919
0
      ssize_t
920
0
        x,
921
0
        y;
922
923
      /*
924
        Crop into tiles of fixed size WxH.
925
      */
926
0
      page=image->page;
927
0
      if (page.width == 0)
928
0
        page.width=image->columns;
929
0
      if (page.height == 0)
930
0
        page.height=image->rows;
931
0
      width=geometry.width;
932
0
      if (width == 0)
933
0
        width=page.width;
934
0
      height=geometry.height;
935
0
      if (height == 0)
936
0
        height=page.height;
937
0
      next=(Image *) NULL;
938
0
      crop_image=NewImageList();
939
0
      for (y=0; y < (ssize_t) page.height; y+=(ssize_t) height)
940
0
      {
941
0
        for (x=0; x < (ssize_t) page.width; x+=(ssize_t) width)
942
0
        {
943
0
          geometry.width=width;
944
0
          geometry.height=height;
945
0
          geometry.x=x;
946
0
          geometry.y=y;
947
0
          next=CropImage(image,&geometry,exception);
948
0
          if (next == (Image *) NULL)
949
0
            break;
950
0
          AppendImageToList(&crop_image,next);
951
0
        }
952
0
        if (next == (Image *) NULL)
953
0
          break;
954
0
      }
955
0
      return(crop_image);
956
0
    }
957
0
  return(CloneImage(image,0,0,MagickTrue,exception));
958
0
}
959

960
/*
961
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
962
%                                                                             %
963
%                                                                             %
964
%                                                                             %
965
%   E x c e r p t I m a g e                                                   %
966
%                                                                             %
967
%                                                                             %
968
%                                                                             %
969
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
970
%
971
%  ExcerptImage() returns a excerpt of the image as defined by the geometry.
972
%
973
%  The format of the ExcerptImage method is:
974
%
975
%      Image *ExcerptImage(const Image *image,const RectangleInfo *geometry,
976
%        ExceptionInfo *exception)
977
%
978
%  A description of each parameter follows:
979
%
980
%    o image: the image.
981
%
982
%    o geometry: Define the region of the image to extend with members
983
%      x, y, width, and height.
984
%
985
%    o exception: return any errors or warnings in this structure.
986
%
987
*/
988
MagickExport Image *ExcerptImage(const Image *image,
989
  const RectangleInfo *geometry,ExceptionInfo *exception)
990
0
{
991
0
#define ExcerptImageTag  "Excerpt/Image"
992
993
0
  CacheView
994
0
    *excerpt_view,
995
0
    *image_view;
996
997
0
  Image
998
0
    *excerpt_image;
999
1000
0
  MagickBooleanType
1001
0
    status;
1002
1003
0
  MagickOffsetType
1004
0
    progress;
1005
1006
0
  ssize_t
1007
0
    y;
1008
1009
  /*
1010
    Allocate excerpt image.
1011
  */
1012
0
  assert(image != (const Image *) NULL);
1013
0
  assert(image->signature == MagickCoreSignature);
1014
0
  assert(geometry != (const RectangleInfo *) NULL);
1015
0
  assert(exception != (ExceptionInfo *) NULL);
1016
0
  assert(exception->signature == MagickCoreSignature);
1017
0
  if (IsEventLogging() != MagickFalse)
1018
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1019
0
  excerpt_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
1020
0
    exception);
1021
0
  if (excerpt_image == (Image *) NULL)
1022
0
    return((Image *) NULL);
1023
  /*
1024
    Excerpt each row.
1025
  */
1026
0
  status=MagickTrue;
1027
0
  progress=0;
1028
0
  image_view=AcquireVirtualCacheView(image,exception);
1029
0
  excerpt_view=AcquireAuthenticCacheView(excerpt_image,exception);
1030
#if defined(MAGICKCORE_OPENMP_SUPPORT)
1031
  #pragma omp parallel for schedule(static) shared(progress,status) \
1032
    magick_number_threads(image,excerpt_image,excerpt_image->rows,2)
1033
#endif
1034
0
  for (y=0; y < (ssize_t) excerpt_image->rows; y++)
1035
0
  {
1036
0
    const Quantum
1037
0
      *magick_restrict p;
1038
1039
0
    Quantum
1040
0
      *magick_restrict q;
1041
1042
0
    ssize_t
1043
0
      x;
1044
1045
0
    if (status == MagickFalse)
1046
0
      continue;
1047
0
    p=GetCacheViewVirtualPixels(image_view,geometry->x,geometry->y+y,
1048
0
      geometry->width,1,exception);
1049
0
    q=GetCacheViewAuthenticPixels(excerpt_view,0,y,excerpt_image->columns,1,
1050
0
      exception);
1051
0
    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1052
0
      {
1053
0
        status=MagickFalse;
1054
0
        continue;
1055
0
      }
1056
0
    for (x=0; x < (ssize_t) excerpt_image->columns; x++)
1057
0
    {
1058
0
      ssize_t
1059
0
        i;
1060
1061
0
      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1062
0
      {
1063
0
        PixelChannel channel = GetPixelChannelChannel(image,i);
1064
0
        PixelTrait traits = GetPixelChannelTraits(image,channel);
1065
0
        PixelTrait excerpt_traits=GetPixelChannelTraits(excerpt_image,channel);
1066
0
        if ((traits == UndefinedPixelTrait) ||
1067
0
            (excerpt_traits == UndefinedPixelTrait))
1068
0
          continue;
1069
0
        SetPixelChannel(excerpt_image,channel,p[i],q);
1070
0
      }
1071
0
      p+=(ptrdiff_t) GetPixelChannels(image);
1072
0
      q+=(ptrdiff_t) GetPixelChannels(excerpt_image);
1073
0
    }
1074
0
    if (SyncCacheViewAuthenticPixels(excerpt_view,exception) == MagickFalse)
1075
0
      status=MagickFalse;
1076
0
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1077
0
      {
1078
0
        MagickBooleanType
1079
0
          proceed;
1080
1081
#if defined(MAGICKCORE_OPENMP_SUPPORT)
1082
        #pragma omp atomic
1083
#endif
1084
0
        progress++;
1085
0
        proceed=SetImageProgress(image,ExcerptImageTag,progress,image->rows);
1086
0
        if (proceed == MagickFalse)
1087
0
          status=MagickFalse;
1088
0
      }
1089
0
  }
1090
0
  excerpt_view=DestroyCacheView(excerpt_view);
1091
0
  image_view=DestroyCacheView(image_view);
1092
0
  excerpt_image->type=image->type;
1093
0
  if (status == MagickFalse)
1094
0
    excerpt_image=DestroyImage(excerpt_image);
1095
0
  return(excerpt_image);
1096
0
}
1097

1098
/*
1099
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1100
%                                                                             %
1101
%                                                                             %
1102
%                                                                             %
1103
%   E x t e n t I m a g e                                                     %
1104
%                                                                             %
1105
%                                                                             %
1106
%                                                                             %
1107
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1108
%
1109
%  ExtentImage() extends the image as defined by the geometry, gravity, and
1110
%  image background color.  Set the (x,y) offset of the geometry to move the
1111
%  original image relative to the extended image.
1112
%
1113
%  The format of the ExtentImage method is:
1114
%
1115
%      Image *ExtentImage(const Image *image,const RectangleInfo *geometry,
1116
%        ExceptionInfo *exception)
1117
%
1118
%  A description of each parameter follows:
1119
%
1120
%    o image: the image.
1121
%
1122
%    o geometry: Define the region of the image to extend with members
1123
%      x, y, width, and height.
1124
%
1125
%    o exception: return any errors or warnings in this structure.
1126
%
1127
*/
1128
MagickExport Image *ExtentImage(const Image *image,
1129
  const RectangleInfo *geometry,ExceptionInfo *exception)
1130
0
{
1131
0
  Image
1132
0
    *extent_image;
1133
1134
0
  MagickBooleanType
1135
0
    status;
1136
1137
  /*
1138
    Allocate extent image.
1139
  */
1140
0
  assert(image != (const Image *) NULL);
1141
0
  assert(image->signature == MagickCoreSignature);
1142
0
  assert(geometry != (const RectangleInfo *) NULL);
1143
0
  assert(exception != (ExceptionInfo *) NULL);
1144
0
  assert(exception->signature == MagickCoreSignature);
1145
0
  if (IsEventLogging() != MagickFalse)
1146
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1147
0
  extent_image=CloneImage(image,geometry->width,geometry->height,MagickTrue,
1148
0
    exception);
1149
0
  if (extent_image == (Image *) NULL)
1150
0
    return((Image *) NULL);
1151
0
  status=SetImageBackgroundColor(extent_image,exception);
1152
0
  if (status == MagickFalse)
1153
0
    {
1154
0
      extent_image=DestroyImage(extent_image);
1155
0
      return((Image *) NULL);
1156
0
    }
1157
0
  DisableCompositeClampUnlessSpecified(extent_image);
1158
0
  status=CompositeImage(extent_image,image,image->compose,MagickTrue,
1159
0
    -geometry->x,-geometry->y,exception);
1160
0
  if (status != MagickFalse)
1161
0
    Update8BIMClipPath(extent_image,image->columns,image->rows,geometry);
1162
0
  return(extent_image);
1163
0
}
1164

1165
/*
1166
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1167
%                                                                             %
1168
%                                                                             %
1169
%                                                                             %
1170
%   F l i p I m a g e                                                         %
1171
%                                                                             %
1172
%                                                                             %
1173
%                                                                             %
1174
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1175
%
1176
%  FlipImage() creates a vertical mirror image by reflecting the pixels
1177
%  around the central x-axis.
1178
%
1179
%  The format of the FlipImage method is:
1180
%
1181
%      Image *FlipImage(const Image *image,ExceptionInfo *exception)
1182
%
1183
%  A description of each parameter follows:
1184
%
1185
%    o image: the image.
1186
%
1187
%    o exception: return any errors or warnings in this structure.
1188
%
1189
*/
1190
MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception)
1191
13.5k
{
1192
13.5k
#define FlipImageTag  "Flip/Image"
1193
1194
13.5k
  CacheView
1195
13.5k
    *flip_view,
1196
13.5k
    *image_view;
1197
1198
13.5k
  Image
1199
13.5k
    *flip_image;
1200
1201
13.5k
  MagickBooleanType
1202
13.5k
    status;
1203
1204
13.5k
  MagickOffsetType
1205
13.5k
    progress;
1206
1207
13.5k
  RectangleInfo
1208
13.5k
    page;
1209
1210
13.5k
  ssize_t
1211
13.5k
    y;
1212
1213
13.5k
  assert(image != (const Image *) NULL);
1214
13.5k
  assert(image->signature == MagickCoreSignature);
1215
13.5k
  assert(exception != (ExceptionInfo *) NULL);
1216
13.5k
  assert(exception->signature == MagickCoreSignature);
1217
13.5k
  if (IsEventLogging() != MagickFalse)
1218
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1219
13.5k
  flip_image=CloneImage(image,0,0,MagickTrue,exception);
1220
13.5k
  if (flip_image == (Image *) NULL)
1221
0
    return((Image *) NULL);
1222
  /*
1223
    Flip image.
1224
  */
1225
13.5k
  status=MagickTrue;
1226
13.5k
  progress=0;
1227
13.5k
  page=image->page;
1228
13.5k
  image_view=AcquireVirtualCacheView(image,exception);
1229
13.5k
  flip_view=AcquireAuthenticCacheView(flip_image,exception);
1230
#if defined(MAGICKCORE_OPENMP_SUPPORT)
1231
  #pragma omp parallel for schedule(static) shared(status) \
1232
    magick_number_threads(image,flip_image,flip_image->rows,2)
1233
#endif
1234
4.08M
  for (y=0; y < (ssize_t) flip_image->rows; y++)
1235
4.07M
  {
1236
4.07M
    const Quantum
1237
4.07M
      *magick_restrict p;
1238
1239
4.07M
    Quantum
1240
4.07M
      *magick_restrict q;
1241
1242
4.07M
    ssize_t
1243
4.07M
      x;
1244
1245
4.07M
    if (status == MagickFalse)
1246
94.1k
      continue;
1247
3.98M
    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1248
3.98M
    q=QueueCacheViewAuthenticPixels(flip_view,0,((ssize_t) flip_image->rows-y-
1249
3.98M
      1),flip_image->columns,1,exception);
1250
3.98M
    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1251
10
      {
1252
10
        status=MagickFalse;
1253
10
        continue;
1254
10
      }
1255
2.54G
    for (x=0; x < (ssize_t) flip_image->columns; x++)
1256
2.53G
    {
1257
2.53G
      ssize_t
1258
2.53G
        i;
1259
1260
9.43G
      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1261
6.89G
      {
1262
6.89G
        PixelChannel channel = GetPixelChannelChannel(image,i);
1263
6.89G
        PixelTrait traits = GetPixelChannelTraits(image,channel);
1264
6.89G
        PixelTrait flip_traits=GetPixelChannelTraits(flip_image,channel);
1265
6.89G
        if ((traits == UndefinedPixelTrait) ||
1266
6.89G
            (flip_traits == UndefinedPixelTrait))
1267
0
          continue;
1268
6.89G
        SetPixelChannel(flip_image,channel,p[i],q);
1269
6.89G
      }
1270
2.53G
      p+=(ptrdiff_t) GetPixelChannels(image);
1271
2.53G
      q+=(ptrdiff_t) GetPixelChannels(flip_image);
1272
2.53G
    }
1273
3.98M
    if (SyncCacheViewAuthenticPixels(flip_view,exception) == MagickFalse)
1274
0
      status=MagickFalse;
1275
3.98M
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1276
0
      {
1277
0
        MagickBooleanType
1278
0
          proceed;
1279
1280
#if defined(MAGICKCORE_OPENMP_SUPPORT)
1281
        #pragma omp atomic
1282
#endif
1283
0
        progress++;
1284
0
        proceed=SetImageProgress(image,FlipImageTag,progress,image->rows);
1285
0
        if (proceed == MagickFalse)
1286
0
          status=MagickFalse;
1287
0
      }
1288
3.98M
  }
1289
13.5k
  flip_view=DestroyCacheView(flip_view);
1290
13.5k
  image_view=DestroyCacheView(image_view);
1291
13.5k
  flip_image->type=image->type;
1292
13.5k
  if (page.height != 0)
1293
8.59k
    page.y=((ssize_t) page.height-(ssize_t) flip_image->rows-page.y);
1294
13.5k
  flip_image->page=page;
1295
13.5k
  if (status == MagickFalse)
1296
10
    flip_image=DestroyImage(flip_image);
1297
13.5k
  return(flip_image);
1298
13.5k
}
1299

1300
/*
1301
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1302
%                                                                             %
1303
%                                                                             %
1304
%                                                                             %
1305
%   F l o p I m a g e                                                         %
1306
%                                                                             %
1307
%                                                                             %
1308
%                                                                             %
1309
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1310
%
1311
%  FlopImage() creates a horizontal mirror image by reflecting the pixels
1312
%  around the central y-axis.
1313
%
1314
%  The format of the FlopImage method is:
1315
%
1316
%      Image *FlopImage(const Image *image,ExceptionInfo *exception)
1317
%
1318
%  A description of each parameter follows:
1319
%
1320
%    o image: the image.
1321
%
1322
%    o exception: return any errors or warnings in this structure.
1323
%
1324
*/
1325
MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception)
1326
793
{
1327
793
#define FlopImageTag  "Flop/Image"
1328
1329
793
  CacheView
1330
793
    *flop_view,
1331
793
    *image_view;
1332
1333
793
  Image
1334
793
    *flop_image;
1335
1336
793
  MagickBooleanType
1337
793
    status;
1338
1339
793
  MagickOffsetType
1340
793
    progress;
1341
1342
793
  RectangleInfo
1343
793
    page;
1344
1345
793
  ssize_t
1346
793
    y;
1347
1348
793
  assert(image != (const Image *) NULL);
1349
793
  assert(image->signature == MagickCoreSignature);
1350
793
  assert(exception != (ExceptionInfo *) NULL);
1351
793
  assert(exception->signature == MagickCoreSignature);
1352
793
  if (IsEventLogging() != MagickFalse)
1353
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1354
793
  flop_image=CloneImage(image,0,0,MagickTrue,exception);
1355
793
  if (flop_image == (Image *) NULL)
1356
0
    return((Image *) NULL);
1357
  /*
1358
    Flop each row.
1359
  */
1360
793
  status=MagickTrue;
1361
793
  progress=0;
1362
793
  page=image->page;
1363
793
  image_view=AcquireVirtualCacheView(image,exception);
1364
793
  flop_view=AcquireAuthenticCacheView(flop_image,exception);
1365
#if defined(MAGICKCORE_OPENMP_SUPPORT)
1366
  #pragma omp parallel for schedule(static) shared(status) \
1367
    magick_number_threads(image,flop_image,flop_image->rows,2)
1368
#endif
1369
76.0k
  for (y=0; y < (ssize_t) flop_image->rows; y++)
1370
75.2k
  {
1371
75.2k
    const Quantum
1372
75.2k
      *magick_restrict p;
1373
1374
75.2k
    ssize_t
1375
75.2k
      x;
1376
1377
75.2k
    Quantum
1378
75.2k
      *magick_restrict q;
1379
1380
75.2k
    if (status == MagickFalse)
1381
0
      continue;
1382
75.2k
    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1383
75.2k
    q=QueueCacheViewAuthenticPixels(flop_view,0,y,flop_image->columns,1,
1384
75.2k
      exception);
1385
75.2k
    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1386
0
      {
1387
0
        status=MagickFalse;
1388
0
        continue;
1389
0
      }
1390
75.2k
    q+=(ptrdiff_t) GetPixelChannels(flop_image)*flop_image->columns;
1391
5.25M
    for (x=0; x < (ssize_t) flop_image->columns; x++)
1392
5.17M
    {
1393
5.17M
      ssize_t
1394
5.17M
        i;
1395
1396
5.17M
      q-=GetPixelChannels(flop_image);
1397
25.8M
      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1398
20.7M
      {
1399
20.7M
        PixelChannel channel = GetPixelChannelChannel(image,i);
1400
20.7M
        PixelTrait traits = GetPixelChannelTraits(image,channel);
1401
20.7M
        PixelTrait flop_traits=GetPixelChannelTraits(flop_image,channel);
1402
20.7M
        if ((traits == UndefinedPixelTrait) ||
1403
20.7M
            (flop_traits == UndefinedPixelTrait))
1404
0
          continue;
1405
20.7M
        SetPixelChannel(flop_image,channel,p[i],q);
1406
20.7M
      }
1407
5.17M
      p+=(ptrdiff_t) GetPixelChannels(image);
1408
5.17M
    }
1409
75.2k
    if (SyncCacheViewAuthenticPixels(flop_view,exception) == MagickFalse)
1410
0
      status=MagickFalse;
1411
75.2k
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1412
0
      {
1413
0
        MagickBooleanType
1414
0
          proceed;
1415
1416
#if defined(MAGICKCORE_OPENMP_SUPPORT)
1417
        #pragma omp atomic
1418
#endif
1419
0
        progress++;
1420
0
        proceed=SetImageProgress(image,FlopImageTag,progress,image->rows);
1421
0
        if (proceed == MagickFalse)
1422
0
          status=MagickFalse;
1423
0
      }
1424
75.2k
  }
1425
793
  flop_view=DestroyCacheView(flop_view);
1426
793
  image_view=DestroyCacheView(image_view);
1427
793
  flop_image->type=image->type;
1428
793
  if (page.width != 0)
1429
507
    page.x=((ssize_t) page.width-(ssize_t) flop_image->columns-page.x);
1430
793
  flop_image->page=page;
1431
793
  if (status == MagickFalse)
1432
0
    flop_image=DestroyImage(flop_image);
1433
793
  return(flop_image);
1434
793
}
1435

1436
/*
1437
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1438
%                                                                             %
1439
%                                                                             %
1440
%                                                                             %
1441
%   R o l l I m a g e                                                         %
1442
%                                                                             %
1443
%                                                                             %
1444
%                                                                             %
1445
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1446
%
1447
%  RollImage() offsets an image as defined by x_offset and y_offset.
1448
%
1449
%  The format of the RollImage method is:
1450
%
1451
%      Image *RollImage(const Image *image,const ssize_t x_offset,
1452
%        const ssize_t y_offset,ExceptionInfo *exception)
1453
%
1454
%  A description of each parameter follows:
1455
%
1456
%    o image: the image.
1457
%
1458
%    o x_offset: the number of columns to roll in the horizontal direction.
1459
%
1460
%    o y_offset: the number of rows to roll in the vertical direction.
1461
%
1462
%    o exception: return any errors or warnings in this structure.
1463
%
1464
*/
1465
1466
static MagickBooleanType CopyImageRegion(Image *destination,const Image *source,  const size_t columns,const size_t rows,const ssize_t sx,const ssize_t sy,
1467
  const ssize_t dx,const ssize_t dy,ExceptionInfo *exception)
1468
0
{
1469
0
  CacheView
1470
0
    *source_view,
1471
0
    *destination_view;
1472
1473
0
  MagickBooleanType
1474
0
    status;
1475
1476
0
  ssize_t
1477
0
    y;
1478
1479
0
  if (columns == 0)
1480
0
    return(MagickTrue);
1481
0
  status=MagickTrue;
1482
0
  source_view=AcquireVirtualCacheView(source,exception);
1483
0
  destination_view=AcquireAuthenticCacheView(destination,exception);
1484
#if defined(MAGICKCORE_OPENMP_SUPPORT)
1485
  #pragma omp parallel for schedule(static) shared(status) \
1486
    magick_number_threads(source,destination,rows,2)
1487
#endif
1488
0
  for (y=0; y < (ssize_t) rows; y++)
1489
0
  {
1490
0
    MagickBooleanType
1491
0
      sync;
1492
1493
0
    const Quantum
1494
0
      *magick_restrict p;
1495
1496
0
    Quantum
1497
0
      *magick_restrict q;
1498
1499
0
    ssize_t
1500
0
      x;
1501
1502
    /*
1503
      Transfer scanline.
1504
    */
1505
0
    if (status == MagickFalse)
1506
0
      continue;
1507
0
    p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception);
1508
0
    q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception);
1509
0
    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1510
0
      {
1511
0
        status=MagickFalse;
1512
0
        continue;
1513
0
      }
1514
0
    for (x=0; x < (ssize_t) columns; x++)
1515
0
    {
1516
0
      ssize_t
1517
0
        i;
1518
1519
0
      for (i=0; i < (ssize_t) GetPixelChannels(source); i++)
1520
0
      {
1521
0
        PixelChannel channel = GetPixelChannelChannel(source,i);
1522
0
        PixelTrait source_traits=GetPixelChannelTraits(source,channel);
1523
0
        PixelTrait destination_traits=GetPixelChannelTraits(destination,
1524
0
          channel);
1525
0
        if ((source_traits == UndefinedPixelTrait) ||
1526
0
            (destination_traits == UndefinedPixelTrait))
1527
0
          continue;
1528
0
        SetPixelChannel(destination,channel,p[i],q);
1529
0
      }
1530
0
      p+=(ptrdiff_t) GetPixelChannels(source);
1531
0
      q+=(ptrdiff_t) GetPixelChannels(destination);
1532
0
    }
1533
0
    sync=SyncCacheViewAuthenticPixels(destination_view,exception);
1534
0
    if (sync == MagickFalse)
1535
0
      status=MagickFalse;
1536
0
  }
1537
0
  destination_view=DestroyCacheView(destination_view);
1538
0
  source_view=DestroyCacheView(source_view);
1539
0
  return(status);
1540
0
}
1541
1542
MagickExport Image *RollImage(const Image *image,const ssize_t x_offset,
1543
  const ssize_t y_offset,ExceptionInfo *exception)
1544
0
{
1545
0
#define RollImageTag  "Roll/Image"
1546
1547
0
  Image
1548
0
    *roll_image;
1549
1550
0
  MagickStatusType
1551
0
    status;
1552
1553
0
  RectangleInfo
1554
0
    offset;
1555
1556
  /*
1557
    Initialize roll image attributes.
1558
  */
1559
0
  assert(image != (const Image *) NULL);
1560
0
  assert(image->signature == MagickCoreSignature);
1561
0
  assert(exception != (ExceptionInfo *) NULL);
1562
0
  assert(exception->signature == MagickCoreSignature);
1563
0
  if (IsEventLogging() != MagickFalse)
1564
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1565
0
  roll_image=CloneImage(image,0,0,MagickTrue,exception);
1566
0
  if (roll_image == (Image *) NULL)
1567
0
    return((Image *) NULL);
1568
0
  offset.x=x_offset;
1569
0
  offset.y=y_offset;
1570
0
  while (offset.x < 0)
1571
0
    offset.x+=(ssize_t) image->columns;
1572
0
  while (offset.x >= (ssize_t) image->columns)
1573
0
    offset.x-=(ssize_t) image->columns;
1574
0
  while (offset.y < 0)
1575
0
    offset.y+=(ssize_t) image->rows;
1576
0
  while (offset.y >= (ssize_t) image->rows)
1577
0
    offset.y-=(ssize_t) image->rows;
1578
  /*
1579
    Roll image.
1580
  */
1581
0
  status=CopyImageRegion(roll_image,image,(size_t) offset.x,
1582
0
    (size_t) offset.y,(ssize_t) image->columns-offset.x,(ssize_t) image->rows-
1583
0
    offset.y,0,0,exception);
1584
0
  (void) SetImageProgress(image,RollImageTag,0,3);
1585
0
  status&=(MagickStatusType) CopyImageRegion(roll_image,image,(size_t)
1586
0
    ((ssize_t) image->columns-offset.x),(size_t) offset.y,0,(ssize_t)
1587
0
    image->rows-offset.y,offset.x,0,exception);
1588
0
  (void) SetImageProgress(image,RollImageTag,1,3);
1589
0
  status&=(MagickStatusType) CopyImageRegion(roll_image,image,(size_t)
1590
0
    offset.x,(size_t) ((ssize_t) image->rows-offset.y),(ssize_t)
1591
0
    image->columns-offset.x,0,0,offset.y,exception);
1592
0
  (void) SetImageProgress(image,RollImageTag,2,3);
1593
0
  status&=(MagickStatusType) CopyImageRegion(roll_image,image,(size_t)
1594
0
    ((ssize_t) image->columns-offset.x),(size_t) ((ssize_t) image->rows-
1595
0
    offset.y),0,0,offset.x,offset.y,exception);
1596
0
  (void) SetImageProgress(image,RollImageTag,3,3);
1597
0
  roll_image->type=image->type;
1598
0
  if (status == MagickFalse)
1599
0
    roll_image=DestroyImage(roll_image);
1600
0
  return(roll_image);
1601
0
}
1602

1603
/*
1604
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1605
%                                                                             %
1606
%                                                                             %
1607
%                                                                             %
1608
%   S h a v e I m a g e                                                       %
1609
%                                                                             %
1610
%                                                                             %
1611
%                                                                             %
1612
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1613
%
1614
%  ShaveImage() shaves pixels from the image edges.  It allocates the memory
1615
%  necessary for the new Image structure and returns a pointer to the new
1616
%  image.
1617
%
1618
%  The format of the ShaveImage method is:
1619
%
1620
%      Image *ShaveImage(const Image *image,const RectangleInfo *shave_info,
1621
%        ExceptionInfo *exception)
1622
%
1623
%  A description of each parameter follows:
1624
%
1625
%    o shave_image: Method ShaveImage returns a pointer to the shaved
1626
%      image.  A null image is returned if there is a memory shortage or
1627
%      if the image width or height is zero.
1628
%
1629
%    o image: the image.
1630
%
1631
%    o shave_info: Specifies a pointer to a RectangleInfo which defines the
1632
%      region of the image to crop.
1633
%
1634
%    o exception: return any errors or warnings in this structure.
1635
%
1636
*/
1637
MagickExport Image *ShaveImage(const Image *image,
1638
  const RectangleInfo *shave_info,ExceptionInfo *exception)
1639
0
{
1640
0
  Image
1641
0
    *shave_image;
1642
1643
0
  RectangleInfo
1644
0
    geometry;
1645
1646
0
  assert(image != (const Image *) NULL);
1647
0
  assert(image->signature == MagickCoreSignature);
1648
0
  if (IsEventLogging() != MagickFalse)
1649
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1650
0
  if (((2*shave_info->width) >= image->columns) ||
1651
0
      ((2*shave_info->height) >= image->rows))
1652
0
    ThrowImageException(OptionWarning,"GeometryDoesNotContainImage");
1653
0
  SetGeometry(image,&geometry);
1654
0
  geometry.width-=2*shave_info->width;
1655
0
  geometry.height-=2*shave_info->height;
1656
0
  geometry.x=(ssize_t) shave_info->width+image->page.x;
1657
0
  geometry.y=(ssize_t) shave_info->height+image->page.y;
1658
0
  shave_image=CropImage(image,&geometry,exception);
1659
0
  if (shave_image == (Image *) NULL)
1660
0
    return((Image *) NULL);
1661
0
  shave_image->page.width-=2*shave_info->width;
1662
0
  shave_image->page.height-=2*shave_info->height;
1663
0
  shave_image->page.x-=(ssize_t) shave_info->width;
1664
0
  shave_image->page.y-=(ssize_t) shave_info->height;
1665
0
  return(shave_image);
1666
0
}
1667

1668
/*
1669
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1670
%                                                                             %
1671
%                                                                             %
1672
%                                                                             %
1673
%   S p l i c e I m a g e                                                     %
1674
%                                                                             %
1675
%                                                                             %
1676
%                                                                             %
1677
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1678
%
1679
%  SpliceImage() splices a solid color into the image as defined by the
1680
%  geometry.
1681
%
1682
%  The format of the SpliceImage method is:
1683
%
1684
%      Image *SpliceImage(const Image *image,const RectangleInfo *geometry,
1685
%        ExceptionInfo *exception)
1686
%
1687
%  A description of each parameter follows:
1688
%
1689
%    o image: the image.
1690
%
1691
%    o geometry: Define the region of the image to splice with members
1692
%      x, y, width, and height.
1693
%
1694
%    o exception: return any errors or warnings in this structure.
1695
%
1696
*/
1697
MagickExport Image *SpliceImage(const Image *image,
1698
  const RectangleInfo *geometry,ExceptionInfo *exception)
1699
0
{
1700
0
#define SpliceImageTag  "Splice/Image"
1701
1702
0
  CacheView
1703
0
    *image_view,
1704
0
    *splice_view;
1705
1706
0
  Image
1707
0
    *splice_image;
1708
1709
0
  MagickBooleanType
1710
0
    status;
1711
1712
0
  MagickOffsetType
1713
0
    progress;
1714
1715
0
  RectangleInfo
1716
0
    splice_geometry;
1717
1718
0
  ssize_t
1719
0
    columns,
1720
0
    y;
1721
1722
  /*
1723
    Allocate splice image.
1724
  */
1725
0
  assert(image != (const Image *) NULL);
1726
0
  assert(image->signature == MagickCoreSignature);
1727
0
  if (IsEventLogging() != MagickFalse)
1728
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1729
0
  assert(geometry != (const RectangleInfo *) NULL);
1730
0
  assert(exception != (ExceptionInfo *) NULL);
1731
0
  assert(exception->signature == MagickCoreSignature);
1732
0
  splice_geometry=(*geometry);
1733
0
  splice_image=CloneImage(image,image->columns+splice_geometry.width,
1734
0
    image->rows+splice_geometry.height,MagickTrue,exception);
1735
0
  if (splice_image == (Image *) NULL)
1736
0
    return((Image *) NULL);
1737
0
  if (SetImageStorageClass(splice_image,DirectClass,exception) == MagickFalse)
1738
0
    {
1739
0
      splice_image=DestroyImage(splice_image);
1740
0
      return((Image *) NULL);
1741
0
    }
1742
0
  if ((IsPixelInfoGray(&splice_image->background_color) == MagickFalse) &&
1743
0
      (IsGrayColorspace(splice_image->colorspace) != MagickFalse))
1744
0
    (void) SetImageColorspace(splice_image,sRGBColorspace,exception);
1745
0
  if ((splice_image->background_color.alpha_trait != UndefinedPixelTrait) &&
1746
0
      (splice_image->alpha_trait == UndefinedPixelTrait))
1747
0
    (void) SetImageAlpha(splice_image,OpaqueAlpha,exception);
1748
0
  (void) SetImageBackgroundColor(splice_image,exception);
1749
  /*
1750
    Respect image geometry.
1751
  */
1752
0
  switch (image->gravity)
1753
0
  {
1754
0
    default:
1755
0
    case UndefinedGravity:
1756
0
    case NorthWestGravity:
1757
0
      break;
1758
0
    case NorthGravity:
1759
0
    {
1760
0
      splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1761
0
      break;
1762
0
    }
1763
0
    case NorthEastGravity:
1764
0
    {
1765
0
      splice_geometry.x+=(ssize_t) splice_geometry.width;
1766
0
      break;
1767
0
    }
1768
0
    case WestGravity:
1769
0
    {
1770
0
      splice_geometry.y+=(ssize_t) splice_geometry.width/2;
1771
0
      break;
1772
0
    }
1773
0
    case CenterGravity:
1774
0
    {
1775
0
      splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1776
0
      splice_geometry.y+=(ssize_t) splice_geometry.height/2;
1777
0
      break;
1778
0
    }
1779
0
    case EastGravity:
1780
0
    {
1781
0
      splice_geometry.x+=(ssize_t) splice_geometry.width;
1782
0
      splice_geometry.y+=(ssize_t) splice_geometry.height/2;
1783
0
      break;
1784
0
    }
1785
0
    case SouthWestGravity:
1786
0
    {
1787
0
      splice_geometry.y+=(ssize_t) splice_geometry.height;
1788
0
      break;
1789
0
    }
1790
0
    case SouthGravity:
1791
0
    {
1792
0
      splice_geometry.x+=(ssize_t) splice_geometry.width/2;
1793
0
      splice_geometry.y+=(ssize_t) splice_geometry.height;
1794
0
      break;
1795
0
    }
1796
0
    case SouthEastGravity:
1797
0
    {
1798
0
      splice_geometry.x+=(ssize_t) splice_geometry.width;
1799
0
      splice_geometry.y+=(ssize_t) splice_geometry.height;
1800
0
      break;
1801
0
    }
1802
0
  }
1803
  /*
1804
    Splice image.
1805
  */
1806
0
  status=MagickTrue;
1807
0
  progress=0;
1808
0
  columns=MagickMin(splice_geometry.x,(ssize_t) splice_image->columns);
1809
0
  image_view=AcquireVirtualCacheView(image,exception);
1810
0
  splice_view=AcquireAuthenticCacheView(splice_image,exception);
1811
#if defined(MAGICKCORE_OPENMP_SUPPORT)
1812
  #pragma omp parallel for schedule(static) shared(progress,status) \
1813
    magick_number_threads(image,splice_image,(size_t) splice_geometry.y,2)
1814
#endif
1815
0
  for (y=0; y < (ssize_t) splice_geometry.y; y++)
1816
0
  {
1817
0
    const Quantum
1818
0
      *magick_restrict p;
1819
1820
0
    ssize_t
1821
0
      x;
1822
1823
0
    Quantum
1824
0
      *magick_restrict q;
1825
1826
0
    if (status == MagickFalse)
1827
0
      continue;
1828
0
    p=GetCacheViewVirtualPixels(image_view,0,y,splice_image->columns,1,
1829
0
      exception);
1830
0
    q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1831
0
      exception);
1832
0
    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1833
0
      {
1834
0
        status=MagickFalse;
1835
0
        continue;
1836
0
      }
1837
0
    for (x=0; x < columns; x++)
1838
0
    {
1839
0
      ssize_t
1840
0
        i;
1841
1842
0
      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1843
0
      {
1844
0
        PixelChannel channel = GetPixelChannelChannel(image,i);
1845
0
        PixelTrait traits = GetPixelChannelTraits(image,channel);
1846
0
        PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1847
0
        if ((traits == UndefinedPixelTrait) ||
1848
0
            (splice_traits == UndefinedPixelTrait))
1849
0
          continue;
1850
0
        SetPixelChannel(splice_image,channel,p[i],q);
1851
0
      }
1852
0
      SetPixelRed(splice_image,GetPixelRed(image,p),q);
1853
0
      SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1854
0
      SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1855
0
      SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1856
0
      p+=(ptrdiff_t) GetPixelChannels(image);
1857
0
      q+=(ptrdiff_t) GetPixelChannels(splice_image);
1858
0
    }
1859
0
    for ( ; x < (splice_geometry.x+(ssize_t) splice_geometry.width); x++)
1860
0
      q+=(ptrdiff_t) GetPixelChannels(splice_image);
1861
0
    for ( ; x < (ssize_t) splice_image->columns; x++)
1862
0
    {
1863
0
      ssize_t
1864
0
        i;
1865
1866
0
      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1867
0
      {
1868
0
        PixelChannel channel = GetPixelChannelChannel(image,i);
1869
0
        PixelTrait traits = GetPixelChannelTraits(image,channel);
1870
0
        PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1871
0
        if ((traits == UndefinedPixelTrait) ||
1872
0
            (splice_traits == UndefinedPixelTrait))
1873
0
          continue;
1874
0
        SetPixelChannel(splice_image,channel,p[i],q);
1875
0
      }
1876
0
      SetPixelRed(splice_image,GetPixelRed(image,p),q);
1877
0
      SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1878
0
      SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1879
0
      SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1880
0
      p+=(ptrdiff_t) GetPixelChannels(image);
1881
0
      q+=(ptrdiff_t) GetPixelChannels(splice_image);
1882
0
    }
1883
0
    if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1884
0
      status=MagickFalse;
1885
0
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1886
0
      {
1887
0
        MagickBooleanType
1888
0
          proceed;
1889
1890
#if defined(MAGICKCORE_OPENMP_SUPPORT)
1891
        #pragma omp atomic
1892
#endif
1893
0
        progress++;
1894
0
        proceed=SetImageProgress(image,SpliceImageTag,progress,
1895
0
          splice_image->rows);
1896
0
        if (proceed == MagickFalse)
1897
0
          status=MagickFalse;
1898
0
      }
1899
0
  }
1900
#if defined(MAGICKCORE_OPENMP_SUPPORT)
1901
  #pragma omp parallel for schedule(static) shared(progress,status) \
1902
    magick_number_threads(image,splice_image,splice_image->rows,2)
1903
#endif
1904
0
  for (y=splice_geometry.y+(ssize_t) splice_geometry.height; y < (ssize_t) splice_image->rows; y++)
1905
0
  {
1906
0
    const Quantum
1907
0
      *magick_restrict p;
1908
1909
0
    ssize_t
1910
0
      x;
1911
1912
0
    Quantum
1913
0
      *magick_restrict q;
1914
1915
0
    if (status == MagickFalse)
1916
0
      continue;
1917
0
    if ((y < 0) || (y >= (ssize_t) splice_image->rows))
1918
0
      continue;
1919
0
    p=GetCacheViewVirtualPixels(image_view,0,y-(ssize_t) splice_geometry.height,
1920
0
      splice_image->columns,1,exception);
1921
0
    q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1,
1922
0
      exception);
1923
0
    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
1924
0
      {
1925
0
        status=MagickFalse;
1926
0
        continue;
1927
0
      }
1928
0
    for (x=0; x < columns; x++)
1929
0
    {
1930
0
      ssize_t
1931
0
        i;
1932
1933
0
      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1934
0
      {
1935
0
        PixelChannel channel = GetPixelChannelChannel(image,i);
1936
0
        PixelTrait traits = GetPixelChannelTraits(image,channel);
1937
0
        PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1938
0
        if ((traits == UndefinedPixelTrait) ||
1939
0
            (splice_traits == UndefinedPixelTrait))
1940
0
          continue;
1941
0
        SetPixelChannel(splice_image,channel,p[i],q);
1942
0
      }
1943
0
      SetPixelRed(splice_image,GetPixelRed(image,p),q);
1944
0
      SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1945
0
      SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1946
0
      SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1947
0
      p+=(ptrdiff_t) GetPixelChannels(image);
1948
0
      q+=(ptrdiff_t) GetPixelChannels(splice_image);
1949
0
    }
1950
0
    for ( ; x < (splice_geometry.x+(ssize_t) splice_geometry.width); x++)
1951
0
      q+=(ptrdiff_t) GetPixelChannels(splice_image);
1952
0
    for ( ; x < (ssize_t) splice_image->columns; x++)
1953
0
    {
1954
0
      ssize_t
1955
0
        i;
1956
1957
0
      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1958
0
      {
1959
0
        PixelChannel channel = GetPixelChannelChannel(image,i);
1960
0
        PixelTrait traits = GetPixelChannelTraits(image,channel);
1961
0
        PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel);
1962
0
        if ((traits == UndefinedPixelTrait) ||
1963
0
            (splice_traits == UndefinedPixelTrait))
1964
0
          continue;
1965
0
        SetPixelChannel(splice_image,channel,p[i],q);
1966
0
      }
1967
0
      SetPixelRed(splice_image,GetPixelRed(image,p),q);
1968
0
      SetPixelGreen(splice_image,GetPixelGreen(image,p),q);
1969
0
      SetPixelBlue(splice_image,GetPixelBlue(image,p),q);
1970
0
      SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q);
1971
0
      p+=(ptrdiff_t) GetPixelChannels(image);
1972
0
      q+=(ptrdiff_t) GetPixelChannels(splice_image);
1973
0
    }
1974
0
    if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse)
1975
0
      status=MagickFalse;
1976
0
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
1977
0
      {
1978
0
        MagickBooleanType
1979
0
          proceed;
1980
1981
#if defined(MAGICKCORE_OPENMP_SUPPORT)
1982
        #pragma omp atomic
1983
#endif
1984
0
        progress++;
1985
0
        proceed=SetImageProgress(image,SpliceImageTag,progress,
1986
0
          splice_image->rows);
1987
0
        if (proceed == MagickFalse)
1988
0
          status=MagickFalse;
1989
0
      }
1990
0
  }
1991
0
  splice_view=DestroyCacheView(splice_view);
1992
0
  image_view=DestroyCacheView(image_view);
1993
0
  if (status == MagickFalse)
1994
0
    splice_image=DestroyImage(splice_image);
1995
0
  return(splice_image);
1996
0
}
1997

1998
/*
1999
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2000
%                                                                             %
2001
%                                                                             %
2002
%                                                                             %
2003
%   T r a n s f o r m I m a g e                                               %
2004
%                                                                             %
2005
%                                                                             %
2006
%                                                                             %
2007
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2008
%
2009
%  TransformImage() is a convenience method that behaves like ResizeImage() or
2010
%  CropImage() but accepts scaling and/or cropping information as a region
2011
%  geometry specification.  If the operation fails, the original image handle
2012
%  is left as is.
2013
%
2014
%  This should only be used for single images.
2015
%
2016
%  This function destroys what it assumes to be a single image list.
2017
%  If the input image is part of a larger list, all other images in that list
2018
%  will be simply 'lost', not destroyed.
2019
%
2020
%  Also if the crop generates a list of images only the first image is resized.
2021
%  And finally if the crop succeeds and the resize failed, you will get a
2022
%  cropped image, as well as a 'false' or 'failed' report.
2023
%
2024
%  This function and should probably be deprecated in favor of direct calls
2025
%  to CropImageToTiles() or ResizeImage(), as appropriate.
2026
%
2027
%  The format of the TransformImage method is:
2028
%
2029
%      MagickBooleanType TransformImage(Image **image,const char *crop_geometry,
2030
%        const char *image_geometry,ExceptionInfo *exception)
2031
%
2032
%  A description of each parameter follows:
2033
%
2034
%    o image: the image The transformed image is returned as this parameter.
2035
%
2036
%    o crop_geometry: A crop geometry string.  This geometry defines a
2037
%      subregion of the image to crop.
2038
%
2039
%    o image_geometry: An image geometry string.  This geometry defines the
2040
%      final size of the image.
2041
%
2042
%    o exception: return any errors or warnings in this structure.
2043
%
2044
*/
2045
MagickPrivate MagickBooleanType TransformImage(Image **image,
2046
  const char *crop_geometry,const char *image_geometry,ExceptionInfo *exception)
2047
4.92k
{
2048
4.92k
  Image
2049
4.92k
    *resize_image,
2050
4.92k
    *transform_image;
2051
2052
4.92k
  RectangleInfo
2053
4.92k
    geometry;
2054
2055
4.92k
  assert(image != (Image **) NULL);
2056
4.92k
  assert((*image)->signature == MagickCoreSignature);
2057
4.92k
  if (IsEventLogging() != MagickFalse)
2058
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename);
2059
4.92k
  transform_image=(*image);
2060
4.92k
  if (crop_geometry != (const char *) NULL)
2061
0
    {
2062
0
      Image
2063
0
        *crop_image;
2064
2065
      /*
2066
        Crop image to a user specified size.
2067
      */
2068
0
      crop_image=CropImageToTiles(*image,crop_geometry,exception);
2069
0
      if (crop_image == (Image *) NULL)
2070
0
        transform_image=CloneImage(*image,0,0,MagickTrue,exception);
2071
0
      else
2072
0
        {
2073
0
          transform_image=DestroyImage(transform_image);
2074
0
          transform_image=GetFirstImageInList(crop_image);
2075
0
        }
2076
0
      *image=transform_image;
2077
0
    }
2078
4.92k
  if (image_geometry == (const char *) NULL)
2079
0
    return(MagickTrue);
2080
  /*
2081
    Scale image to a user specified size.
2082
  */
2083
4.92k
  (void) ParseRegionGeometry(transform_image,image_geometry,&geometry,
2084
4.92k
    exception);
2085
4.92k
  if ((transform_image->columns == geometry.width) &&
2086
4.92k
      (transform_image->rows == geometry.height))
2087
387
    return(MagickTrue);
2088
4.53k
  resize_image=ResizeImage(transform_image,geometry.width,geometry.height,
2089
4.53k
    transform_image->filter,exception);
2090
4.53k
  if (resize_image == (Image *) NULL)
2091
2.37k
    return(MagickFalse);
2092
2.16k
  transform_image=DestroyImage(transform_image);
2093
2.16k
  transform_image=resize_image;
2094
2.16k
  *image=transform_image;
2095
2.16k
  return(MagickTrue);
2096
4.53k
}
2097

2098
/*
2099
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2100
%                                                                             %
2101
%                                                                             %
2102
%                                                                             %
2103
%   T r a n s p o s e I m a g e                                               %
2104
%                                                                             %
2105
%                                                                             %
2106
%                                                                             %
2107
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2108
%
2109
%  TransposeImage() creates a horizontal mirror image by reflecting the pixels
2110
%  around the central y-axis while rotating them by 90 degrees.
2111
%
2112
%  The format of the TransposeImage method is:
2113
%
2114
%      Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2115
%
2116
%  A description of each parameter follows:
2117
%
2118
%    o image: the image.
2119
%
2120
%    o exception: return any errors or warnings in this structure.
2121
%
2122
*/
2123
MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception)
2124
0
{
2125
0
#define TransposeImageTag  "Transpose/Image"
2126
2127
0
  CacheView
2128
0
    *image_view,
2129
0
    *transpose_view;
2130
2131
0
  Image
2132
0
    *transpose_image;
2133
2134
0
  MagickBooleanType
2135
0
    status;
2136
2137
0
  MagickOffsetType
2138
0
    progress;
2139
2140
0
  RectangleInfo
2141
0
    page;
2142
2143
0
  ssize_t
2144
0
    y;
2145
2146
0
  assert(image != (const Image *) NULL);
2147
0
  assert(image->signature == MagickCoreSignature);
2148
0
  assert(exception != (ExceptionInfo *) NULL);
2149
0
  assert(exception->signature == MagickCoreSignature);
2150
0
  if (IsEventLogging() != MagickFalse)
2151
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2152
0
  transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2153
0
    exception);
2154
0
  if (transpose_image == (Image *) NULL)
2155
0
    return((Image *) NULL);
2156
  /*
2157
    Transpose image.
2158
  */
2159
0
  status=MagickTrue;
2160
0
  progress=0;
2161
0
  image_view=AcquireVirtualCacheView(image,exception);
2162
0
  transpose_view=AcquireAuthenticCacheView(transpose_image,exception);
2163
#if defined(MAGICKCORE_OPENMP_SUPPORT)
2164
  #pragma omp parallel for schedule(static) shared(progress,status) \
2165
    magick_number_threads(image,transpose_image,image->rows,2)
2166
#endif
2167
0
  for (y=0; y < (ssize_t) image->rows; y++)
2168
0
  {
2169
0
    const Quantum
2170
0
      *magick_restrict p;
2171
2172
0
    Quantum
2173
0
      *magick_restrict q;
2174
2175
0
    ssize_t
2176
0
      x;
2177
2178
0
    if (status == MagickFalse)
2179
0
      continue;
2180
0
    p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-y-1,
2181
0
      image->columns,1,exception);
2182
0
    q=QueueCacheViewAuthenticPixels(transpose_view,(ssize_t) image->rows-y-1,
2183
0
      0,1,transpose_image->rows,exception);
2184
0
    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2185
0
      {
2186
0
        status=MagickFalse;
2187
0
        continue;
2188
0
      }
2189
0
    for (x=0; x < (ssize_t) image->columns; x++)
2190
0
    {
2191
0
      ssize_t
2192
0
        i;
2193
2194
0
      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2195
0
      {
2196
0
        PixelChannel channel = GetPixelChannelChannel(image,i);
2197
0
        PixelTrait traits = GetPixelChannelTraits(image,channel);
2198
0
        PixelTrait transpose_traits=GetPixelChannelTraits(transpose_image,
2199
0
          channel);
2200
0
        if ((traits == UndefinedPixelTrait) ||
2201
0
            (transpose_traits == UndefinedPixelTrait))
2202
0
          continue;
2203
0
        SetPixelChannel(transpose_image,channel,p[i],q);
2204
0
      }
2205
0
      p+=(ptrdiff_t) GetPixelChannels(image);
2206
0
      q+=(ptrdiff_t) GetPixelChannels(transpose_image);
2207
0
    }
2208
0
    if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse)
2209
0
      status=MagickFalse;
2210
0
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
2211
0
      {
2212
0
        MagickBooleanType
2213
0
          proceed;
2214
2215
#if defined(MAGICKCORE_OPENMP_SUPPORT)
2216
        #pragma omp atomic
2217
#endif
2218
0
        progress++;
2219
0
        proceed=SetImageProgress(image,TransposeImageTag,progress,image->rows);
2220
0
        if (proceed == MagickFalse)
2221
0
          status=MagickFalse;
2222
0
      }
2223
0
  }
2224
0
  transpose_view=DestroyCacheView(transpose_view);
2225
0
  image_view=DestroyCacheView(image_view);
2226
0
  transpose_image->type=image->type;
2227
0
  page=transpose_image->page;
2228
0
  Swap(page.width,page.height);
2229
0
  Swap(page.x,page.y);
2230
0
  transpose_image->page=page;
2231
0
  if (status == MagickFalse)
2232
0
    transpose_image=DestroyImage(transpose_image);
2233
0
  return(transpose_image);
2234
0
}
2235

2236
/*
2237
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2238
%                                                                             %
2239
%                                                                             %
2240
%                                                                             %
2241
%   T r a n s v e r s e I m a g e                                             %
2242
%                                                                             %
2243
%                                                                             %
2244
%                                                                             %
2245
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2246
%
2247
%  TransverseImage() creates a vertical mirror image by reflecting the pixels
2248
%  around the central x-axis while rotating them by 270 degrees.
2249
%
2250
%  The format of the TransverseImage method is:
2251
%
2252
%      Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2253
%
2254
%  A description of each parameter follows:
2255
%
2256
%    o image: the image.
2257
%
2258
%    o exception: return any errors or warnings in this structure.
2259
%
2260
*/
2261
MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception)
2262
0
{
2263
0
#define TransverseImageTag  "Transverse/Image"
2264
2265
0
  CacheView
2266
0
    *image_view,
2267
0
    *transverse_view;
2268
2269
0
  Image
2270
0
    *transverse_image;
2271
2272
0
  MagickBooleanType
2273
0
    status;
2274
2275
0
  MagickOffsetType
2276
0
    progress;
2277
2278
0
  RectangleInfo
2279
0
    page;
2280
2281
0
  ssize_t
2282
0
    y;
2283
2284
0
  assert(image != (const Image *) NULL);
2285
0
  assert(image->signature == MagickCoreSignature);
2286
0
  assert(exception != (ExceptionInfo *) NULL);
2287
0
  assert(exception->signature == MagickCoreSignature);
2288
0
  if (IsEventLogging() != MagickFalse)
2289
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2290
0
  transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue,
2291
0
    exception);
2292
0
  if (transverse_image == (Image *) NULL)
2293
0
    return((Image *) NULL);
2294
  /*
2295
    Transverse image.
2296
  */
2297
0
  status=MagickTrue;
2298
0
  progress=0;
2299
0
  image_view=AcquireVirtualCacheView(image,exception);
2300
0
  transverse_view=AcquireAuthenticCacheView(transverse_image,exception);
2301
#if defined(MAGICKCORE_OPENMP_SUPPORT)
2302
  #pragma omp parallel for schedule(static) shared(progress,status) \
2303
    magick_number_threads(image,transverse_image,image->rows,2)
2304
#endif
2305
0
  for (y=0; y < (ssize_t) image->rows; y++)
2306
0
  {
2307
0
    MagickBooleanType
2308
0
      sync;
2309
2310
0
    const Quantum
2311
0
      *magick_restrict p;
2312
2313
0
    Quantum
2314
0
      *magick_restrict q;
2315
2316
0
    ssize_t
2317
0
      x;
2318
2319
0
    if (status == MagickFalse)
2320
0
      continue;
2321
0
    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
2322
0
    q=QueueCacheViewAuthenticPixels(transverse_view,(ssize_t) image->rows-y-1,
2323
0
      0,1,transverse_image->rows,exception);
2324
0
    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2325
0
      {
2326
0
        status=MagickFalse;
2327
0
        continue;
2328
0
      }
2329
0
    q+=(ptrdiff_t) GetPixelChannels(transverse_image)*image->columns;
2330
0
    for (x=0; x < (ssize_t) image->columns; x++)
2331
0
    {
2332
0
      ssize_t
2333
0
        i;
2334
2335
0
      q-=GetPixelChannels(transverse_image);
2336
0
      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2337
0
      {
2338
0
        PixelChannel channel = GetPixelChannelChannel(image,i);
2339
0
        PixelTrait traits = GetPixelChannelTraits(image,channel);
2340
0
        PixelTrait transverse_traits=GetPixelChannelTraits(transverse_image,
2341
0
          channel);
2342
0
        if ((traits == UndefinedPixelTrait) ||
2343
0
            (transverse_traits == UndefinedPixelTrait))
2344
0
          continue;
2345
0
        SetPixelChannel(transverse_image,channel,p[i],q);
2346
0
      }
2347
0
      p+=(ptrdiff_t) GetPixelChannels(image);
2348
0
    }
2349
0
    sync=SyncCacheViewAuthenticPixels(transverse_view,exception);
2350
0
    if (sync == MagickFalse)
2351
0
      status=MagickFalse;
2352
0
    if (image->progress_monitor != (MagickProgressMonitor) NULL)
2353
0
      {
2354
0
        MagickBooleanType
2355
0
          proceed;
2356
2357
#if defined(MAGICKCORE_OPENMP_SUPPORT)
2358
        #pragma omp atomic
2359
#endif
2360
0
        progress++;
2361
0
        proceed=SetImageProgress(image,TransverseImageTag,progress,image->rows);
2362
0
        if (proceed == MagickFalse)
2363
0
          status=MagickFalse;
2364
0
      }
2365
0
  }
2366
0
  transverse_view=DestroyCacheView(transverse_view);
2367
0
  image_view=DestroyCacheView(image_view);
2368
0
  transverse_image->type=image->type;
2369
0
  page=transverse_image->page;
2370
0
  Swap(page.width,page.height);
2371
0
  Swap(page.x,page.y);
2372
0
  if (page.width != 0)
2373
0
    page.x=(ssize_t) page.width-(ssize_t) transverse_image->columns-page.x;
2374
0
  if (page.height != 0)
2375
0
    page.y=(ssize_t) page.height-(ssize_t) transverse_image->rows-page.y;
2376
0
  transverse_image->page=page;
2377
0
  if (status == MagickFalse)
2378
0
    transverse_image=DestroyImage(transverse_image);
2379
0
  return(transverse_image);
2380
0
}
2381

2382
/*
2383
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2384
%                                                                             %
2385
%                                                                             %
2386
%                                                                             %
2387
%   T r i m I m a g e                                                         %
2388
%                                                                             %
2389
%                                                                             %
2390
%                                                                             %
2391
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2392
%
2393
%  TrimImage() trims pixels from the image edges.  It allocates the memory
2394
%  necessary for the new Image structure and returns a pointer to the new
2395
%  image.
2396
%
2397
%  The format of the TrimImage method is:
2398
%
2399
%      Image *TrimImage(const Image *image,ExceptionInfo *exception)
2400
%
2401
%  A description of each parameter follows:
2402
%
2403
%    o image: the image.
2404
%
2405
%    o exception: return any errors or warnings in this structure.
2406
%
2407
*/
2408
MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception)
2409
0
{
2410
0
  const char
2411
0
    *artifact;
2412
2413
0
  Image
2414
0
    *trim_image;
2415
2416
0
  RectangleInfo
2417
0
    geometry,
2418
0
    page;
2419
2420
0
  assert(image != (const Image *) NULL);
2421
0
  assert(image->signature == MagickCoreSignature);
2422
0
  if (IsEventLogging() != MagickFalse)
2423
0
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
2424
0
  geometry=GetImageBoundingBox(image,exception);
2425
0
  if ((geometry.width == 0) || (geometry.height == 0))
2426
0
    {
2427
0
      Image
2428
0
        *crop_image;
2429
2430
0
      crop_image=CloneImage(image,1,1,MagickTrue,exception);
2431
0
      if (crop_image == (Image *) NULL)
2432
0
        return((Image *) NULL);
2433
0
      crop_image->background_color.alpha_trait=BlendPixelTrait;
2434
0
      crop_image->background_color.alpha=(MagickRealType) TransparentAlpha;
2435
0
      (void) SetImageBackgroundColor(crop_image,exception);
2436
0
      crop_image->page=image->page;
2437
0
      crop_image->page.x=(-1);
2438
0
      crop_image->page.y=(-1);
2439
0
      return(crop_image);
2440
0
    }
2441
0
  page=geometry;
2442
0
  artifact=GetImageArtifact(image,"trim:minSize");
2443
0
  if (artifact != (const char *) NULL)
2444
0
    (void) ParseAbsoluteGeometry(artifact,&page);
2445
0
  if ((geometry.width < page.width) && (geometry.height < page.height))
2446
0
    {
2447
      /*
2448
        Limit trim to a minimum size.
2449
      */
2450
0
      switch (image->gravity)
2451
0
      {
2452
0
        case CenterGravity:
2453
0
        {
2454
0
          geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width)/2;
2455
0
          geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height)/2;
2456
0
          break;
2457
0
        }
2458
0
        case NorthWestGravity:
2459
0
        {
2460
0
          geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width);
2461
0
          geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height);
2462
0
          break;
2463
0
        }
2464
0
        case NorthGravity:
2465
0
        {
2466
0
          geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width)/2;
2467
0
          geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height);
2468
0
          break;
2469
0
        }
2470
0
        case NorthEastGravity:
2471
0
        {
2472
0
          geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height);
2473
0
          break;
2474
0
        }
2475
0
        case EastGravity:
2476
0
        {
2477
0
          geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height)/2;
2478
0
          break;
2479
0
        }
2480
0
        case SouthEastGravity:
2481
0
          break;
2482
0
        case SouthGravity:
2483
0
        {
2484
0
          geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width)/2;
2485
0
          break;
2486
0
        }
2487
0
        case SouthWestGravity:
2488
0
        {
2489
0
          geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width);
2490
0
          break;
2491
0
        }
2492
0
        case WestGravity:
2493
0
        {
2494
0
          geometry.x-=((ssize_t) page.width-(ssize_t) geometry.width);
2495
0
          geometry.y-=((ssize_t) page.height-(ssize_t) geometry.height)/2;
2496
0
          break;
2497
0
        }
2498
0
        default:
2499
0
          break;
2500
0
      }
2501
0
      geometry.width=page.width;
2502
0
      geometry.height=page.height;
2503
0
    }
2504
0
  geometry.x+=image->page.x;
2505
0
  geometry.y+=image->page.y;
2506
0
  trim_image=CropImage(image,&geometry,exception);
2507
0
  if (trim_image != (Image *) NULL)
2508
0
    Update8BIMClipPath(trim_image,image->columns,image->rows,&geometry);
2509
0
  return(trim_image);
2510
0
}