Coverage Report

Created: 2026-06-16 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/graphicsmagick/magick/shear.c
Line
Count
Source
1
/*
2
% Copyright (C) 2003 - 2019 GraphicsMagick Group
3
% Copyright (C) 2002 ImageMagick Studio
4
% Copyright 1991-1999 E. I. du Pont de Nemours and Company
5
%
6
% This program is covered by multiple licenses, which are described in
7
% Copyright.txt. You should have received a copy of Copyright.txt with this
8
% package; otherwise see http://www.graphicsmagick.org/www/Copyright.html.
9
%
10
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
11
%                                                                             %
12
%                                                                             %
13
%                                                                             %
14
%                      SSSSS  H   H  EEEEE   AAA    RRRR                      %
15
%                      SS     H   H  E      A   A   R   R                     %
16
%                       SSS   HHHHH  EEE    AAAAA   RRRR                      %
17
%                         SS  H   H  E      A   A   R R                       %
18
%                      SSSSS  H   H  EEEEE  A   A   R  R                      %
19
%                                                                             %
20
%                                                                             %
21
%            Methods to Shear or Rotate an Image by an Arbitrary Angle        %
22
%                                                                             %
23
%                                                                             %
24
%                               Software Design                               %
25
%                                 John Cristy                                 %
26
%                                  July 1992                                  %
27
%                                                                             %
28
%                                                                             %
29
%                                                                             %
30
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
31
%
32
%  Method RotateImage, XShearImage, and YShearImage is based on the paper
33
%  "A Fast Algorithm for General Raster Rotation" by Alan W. Paeth,
34
%  Graphics Interface '86 (Vancouver).  RotateImage is adapted from a similar
35
%  method based on the Paeth paper written by Michael Halle of the Spatial
36
%  Imaging Group, MIT Media Lab.
37
%
38
%
39
*/
40

41
/*
42
  Include declarations.
43
*/
44
#include "magick/studio.h"
45
#include "magick/alpha_composite.h"
46
#include "magick/attribute.h"
47
#include "magick/color.h"
48
#include "magick/decorate.h"
49
#include "magick/log.h"
50
#include "magick/monitor.h"
51
#include "magick/pixel_cache.h"
52
#include "magick/render.h"
53
#include "magick/shear.h"
54
#include "magick/transform.h"
55
#include "magick/utility.h"
56

57
/*
58
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
59
%                                                                             %
60
%                                                                             %
61
%     A f f i n e T r a n s f o r m I m a g e                                 %
62
%                                                                             %
63
%                                                                             %
64
%                                                                             %
65
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66
%
67
%  AffineTransformImage() transforms an image as dictated by the affine matrix.
68
%  It allocates the memory necessary for the new Image structure and returns
69
%  a pointer to the new image.
70
%
71
%  The format of the AffineTransformImage method is:
72
%
73
%      Image *AffineTransformImage(const Image *image,
74
%        AffineMatrix *affine,ExceptionInfo *exception)
75
%
76
%  A description of each parameter follows:
77
%
78
%    o image: The image.
79
%
80
%    o affine: The affine transform.
81
%
82
%    o exception: Return any errors or warnings in this structure.
83
%
84
%
85
*/
86
MagickExport Image *
87
AffineTransformImage(const Image *image,const AffineMatrix *affine,
88
                     ExceptionInfo *exception)
89
0
{
90
0
  AffineMatrix
91
0
    transform;
92
93
0
  Image
94
0
    *affine_image;
95
96
0
  long
97
0
    y;
98
99
0
  PointInfo
100
0
    extent[4],
101
0
    min,
102
0
    max;
103
104
0
  register long
105
0
    i,
106
0
    x;
107
108
  /*
109
    Determine bounding box.
110
  */
111
0
  assert(image != (const Image *) NULL);
112
0
  assert(image->signature == MagickSignature);
113
0
  assert(affine != (AffineMatrix *) NULL);
114
0
  assert(exception != (ExceptionInfo *) NULL);
115
0
  assert(exception->signature == MagickSignature);
116
0
  extent[0].x=0;
117
0
  extent[0].y=0;
118
0
  extent[1].x=image->columns;
119
0
  extent[1].y=0;
120
0
  extent[2].x=image->columns;
121
0
  extent[2].y=image->rows;
122
0
  extent[3].x=0;
123
0
  extent[3].y=image->rows;
124
0
  for (i=0; i < 4; i++)
125
0
  {
126
0
    x=(long) (extent[i].x+0.5);
127
0
    y=(long) (extent[i].y+0.5);
128
0
    extent[i].x=x*affine->sx+y*affine->ry+affine->tx;
129
0
    extent[i].y=x*affine->rx+y*affine->sy+affine->ty;
130
0
  }
131
0
  min=extent[0];
132
0
  max=extent[0];
133
0
  for (i=1; i < 4; i++)
134
0
  {
135
0
    if (min.x > extent[i].x)
136
0
      min.x=extent[i].x;
137
0
    if (min.y > extent[i].y)
138
0
      min.y=extent[i].y;
139
0
    if (max.x < extent[i].x)
140
0
      max.x=extent[i].x;
141
0
    if (max.y < extent[i].y)
142
0
      max.y=extent[i].y;
143
0
  }
144
  /*
145
    Affine transform image.
146
  */
147
0
  affine_image=CloneImage(image,(unsigned long) ceil(max.x-min.x-0.5),
148
0
    (unsigned long) ceil(max.y-min.y-0.5),True,exception);
149
0
  if (affine_image == (Image *) NULL)
150
0
    return((Image *) NULL);
151
0
  (void) SetImage(affine_image,TransparentOpacity);
152
0
  transform.sx=affine->sx;
153
0
  transform.rx=affine->rx;
154
0
  transform.ry=affine->ry;
155
0
  transform.sy=affine->sy;
156
0
  transform.tx=(-min.x);
157
0
  transform.ty=(-min.y);
158
0
  (void) DrawAffineImage(affine_image,image,&transform);
159
0
  return(affine_image);
160
0
}
161

162
/*
163
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
164
%                                                                             %
165
%                                                                             %
166
%                                                                             %
167
%   A u t o O r i e n t I m a g e                                             %
168
%                                                                             %
169
%                                                                             %
170
%                                                                             %
171
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
172
%
173
%  AutoOrientImage() returns an image adjusted so that its orientation is
174
%  suitable for viewing (i.e. top-left orientation).
175
%
176
%  The format of the AutoOrientImage method is:
177
%
178
%      Image *AutoOrientImage(const Image *image,
179
%        const OrientationType current_orientation,
180
%        ExceptionInfo *exception)
181
%
182
%  A description of each parameter follows:
183
%
184
%    o image: The image.
185
%
186
%    o current_orientation: Current image orientation (normally same as
187
%                   image->orientation).
188
%
189
%    o exception: Return any errors or warnings in this structure.
190
%
191
%
192
*/
193
MagickExport Image *
194
AutoOrientImage(const Image *image,
195
                const OrientationType current_orientation,
196
                ExceptionInfo *exception)
197
0
{
198
0
  Image
199
0
    *orient_image;
200
201
0
  assert(image != (const Image *) NULL);
202
0
  assert(image->signature == MagickSignature);
203
0
  assert(exception != (ExceptionInfo *) NULL);
204
0
  assert(exception->signature == MagickSignature);
205
206
0
  orient_image=(Image *) NULL;
207
208
  /*
209
       1        2       3      4         5            6           7          8
210
211
    888888  888888      88  88      8888888888  88                  88  8888888888
212
    88          88      88  88      88  88      88  88          88  88      88  88
213
    8888      8888    8888  8888    88          8888888888  8888888888          88
214
    88          88      88  88
215
    88          88  888888  888888
216
   */
217
218
0
  switch(current_orientation)
219
0
    {
220
0
    case UndefinedOrientation:
221
0
    case TopLeftOrientation: /* 1 */
222
0
    default:
223
0
      {
224
0
        orient_image=CloneImage(image,0,0,MagickTrue,exception);
225
0
        break;
226
0
      }
227
0
    case TopRightOrientation: /* 2 */
228
0
      {
229
0
        orient_image=FlopImage(image,exception);
230
0
        break;
231
0
      }
232
0
    case BottomRightOrientation: /* 3 */
233
0
      {
234
0
        orient_image=RotateImage(image,180.0,exception);
235
0
        break;
236
0
      }
237
0
    case BottomLeftOrientation: /* 4 */
238
0
      {
239
0
        orient_image=FlipImage(image,exception);
240
0
        break;
241
0
      }
242
0
    case LeftTopOrientation: /* 5 */
243
0
      {
244
0
        Image
245
0
          *rotate_image;
246
247
0
        rotate_image=RotateImage(image,90,exception);
248
0
        if (rotate_image != (Image *) NULL)
249
0
          {
250
0
            orient_image=FlopImage(rotate_image,exception);
251
0
            DestroyImage(rotate_image);
252
0
          }
253
254
0
        break;
255
0
      }
256
0
    case RightTopOrientation: /* 6 */
257
0
      {
258
0
        orient_image=RotateImage(image,90.0,exception);
259
0
        break;
260
0
      }
261
0
    case RightBottomOrientation: /* 7 */
262
0
      {
263
0
        Image
264
0
          *rotate_image;
265
266
0
        rotate_image=RotateImage(image,270,exception);
267
0
        if (rotate_image != (Image *) NULL)
268
0
          {
269
0
            orient_image=FlopImage(rotate_image,exception);
270
0
            DestroyImage(rotate_image);
271
0
          }
272
0
        break;
273
0
      }
274
0
    case LeftBottomOrientation: /* 8 */
275
0
      {
276
0
        orient_image=RotateImage(image,270.0,exception);
277
0
        break;
278
0
      }
279
0
    };
280
281
0
  if (orient_image != (Image *) NULL)
282
0
    {
283
0
      orient_image->orientation=TopLeftOrientation;
284
0
      SetImageAttribute(orient_image, "EXIF:Orientation", "1");
285
0
    }
286
287
0
  return orient_image;
288
0
}
289

290
/*
291
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
292
%                                                                             %
293
%                                                                             %
294
%                                                                             %
295
+   C r o p T o F i t I m a g e                                               %
296
%                                                                             %
297
%                                                                             %
298
%                                                                             %
299
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
300
%
301
%  Method CropToFitImage crops the sheared image as determined by the bounding
302
%  box as defined by width and height and shearing angles.
303
%
304
%  The format of the CropToFitImage method is:
305
%
306
%      MagickPassFail CropToFitImage(Image **image,const double x_shear,
307
%        const double x_shear,const double width,const double height,
308
%        const unsigne int rotate,ExceptionInfo *exception)
309
%
310
%  A description of each parameter follows.
311
%
312
%    o image: The address of a structure of type Image.
313
%
314
%    o x_shear, y_shear, width, height: Defines a region of the image to crop.
315
%
316
%    o exception: Return any errors or warnings in this structure.
317
%
318
%
319
*/
320
static MagickPassFail
321
CropToFitImage(Image **image,
322
               const double x_shear,const double y_shear,
323
               const double width,const double height,
324
               const unsigned int rotate,ExceptionInfo *exception)
325
366
{
326
366
  Image
327
366
    *crop_image;
328
329
366
  PointInfo
330
366
    extent[4],
331
366
    min,
332
366
    max;
333
334
366
  RectangleInfo
335
366
    geometry;
336
337
366
  register long
338
366
    i;
339
340
  /*
341
    Calculate the rotated image size.
342
  */
343
366
  extent[0].x=(-width/2.0);
344
366
  extent[0].y=(-height/2.0);
345
366
  extent[1].x=width/2.0;
346
366
  extent[1].y=(-height/2.0);
347
366
  extent[2].x=(-width/2.0);
348
366
  extent[2].y=height/2.0;
349
366
  extent[3].x=width/2.0;
350
366
  extent[3].y=height/2.0;
351
1.83k
  for (i=0; i < 4; i++)
352
1.46k
  {
353
1.46k
    extent[i].x+=x_shear*extent[i].y;
354
1.46k
    extent[i].y+=y_shear*extent[i].x;
355
1.46k
    if (rotate)
356
1.46k
      extent[i].x+=x_shear*extent[i].y;
357
1.46k
    extent[i].x+=(double) (*image)->columns/2.0;
358
1.46k
    extent[i].y+=(double) (*image)->rows/2.0;
359
1.46k
  }
360
366
  min=extent[0];
361
366
  max=extent[0];
362
1.46k
  for (i=1; i < 4; i++)
363
1.09k
  {
364
1.09k
    if (min.x > extent[i].x)
365
165
      min.x=extent[i].x;
366
1.09k
    if (min.y > extent[i].y)
367
201
      min.y=extent[i].y;
368
1.09k
    if (max.x < extent[i].x)
369
661
      max.x=extent[i].x;
370
1.09k
    if (max.y < extent[i].y)
371
638
      max.y=extent[i].y;
372
1.09k
  }
373
366
  geometry.width=(unsigned long) floor(max.x-min.x+0.5);
374
366
  geometry.height=(unsigned long) floor(max.y-min.y+0.5);
375
366
  geometry.x=(long) ceil(min.x-0.5);
376
366
  geometry.y=(long) ceil(min.y-0.5);
377
366
  crop_image=CropImage(*image,&geometry,exception);
378
366
  if (crop_image != (Image *) NULL)
379
366
    crop_image->page=(*image)->page;
380
366
  DestroyImage(*image);
381
366
  *image=crop_image;
382
383
366
  return (*image != (Image *) NULL ? MagickPass : MagickFail);
384
366
}
385

386
/*
387
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
388
%                                                                             %
389
%                                                                             %
390
%                                                                             %
391
+   I n t e g r a l R o t a t e I m a g e                                     %
392
%                                                                             %
393
%                                                                             %
394
%                                                                             %
395
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
396
%
397
%  Method IntegralRotateImage rotates the image an integral of 90 degrees.
398
%  It allocates the memory necessary for the new Image structure and returns
399
%  a pointer to the rotated image.
400
%
401
%  The format of the IntegralRotateImage method is:
402
%
403
%      Image *IntegralRotateImage(const Image *image,unsigned int rotations,
404
%        ExceptionInfo *exception)
405
%
406
%  A description of each parameter follows.
407
%
408
%    o rotate_image: Method IntegralRotateImage returns a pointer to the
409
%      rotated image.  A null image is returned if there is a a memory shortage.
410
%
411
%    o image: The image.
412
%
413
%    o rotations: Specifies the number of 90 degree rotations.
414
%
415
%    o exception: Return any errors or warnings in this structure.
416
%
417
%
418
*/
419
#if 1
420
#if !defined(DisableSlowOpenMP)
421
#  define IntegralRotateImageUseOpenMP
422
#  define RotateThreads (Min(2,omp_get_max_threads()))
423
#endif
424
#endif
425
static Image *
426
IntegralRotateImage(const Image *image,unsigned int rotations,
427
                    ExceptionInfo *exception)
428
1.34k
{
429
1.34k
  char
430
1.34k
    message[MaxTextExtent];
431
432
1.34k
  Image
433
1.34k
    *rotate_image;
434
435
1.34k
  RectangleInfo
436
1.34k
    page;
437
438
1.34k
  long
439
1.34k
    tile_width_max,
440
1.34k
    tile_height_max;
441
442
1.34k
  MagickPassFail
443
1.34k
    status=MagickPass;
444
445
  /*
446
    Initialize rotated image attributes.
447
  */
448
1.34k
  assert(image != (Image *) NULL);
449
1.34k
  page=image->page;
450
1.34k
  rotations%=4;
451
452
1.34k
  {
453
    /*
454
      Clone appropriately to create rotate image.
455
    */
456
1.34k
    unsigned long
457
1.34k
      clone_columns=0,
458
1.34k
      clone_rows=0;
459
460
1.34k
    switch (rotations)
461
1.34k
      {
462
62
      case 0:
463
62
        clone_columns=0;
464
62
        clone_rows=0;
465
62
        break;
466
102
      case 2:
467
102
        clone_columns=image->columns;
468
102
        clone_rows=image->rows;
469
102
        break;
470
1.04k
      case 1:
471
1.18k
      case 3:
472
1.18k
        clone_columns=image->rows;
473
1.18k
        clone_rows=image->columns;
474
1.18k
        break;
475
1.34k
      }
476
1.34k
    rotate_image=CloneImage(image,clone_columns,clone_rows,True,exception);
477
1.34k
    if (rotate_image == (Image *) NULL)
478
0
      return((Image *) NULL);
479
1.34k
    if (rotations != 0)
480
1.28k
      if (ModifyCache(rotate_image,exception) != MagickPass)
481
1
        {
482
1
          DestroyImage(rotate_image);
483
1
          return (Image *) NULL;
484
1
        }
485
1.34k
  }
486
487
1.34k
  tile_height_max=tile_width_max=2048/sizeof(PixelPacket); /* 2k x 2k = 4MB */
488
1.34k
  if ((rotations == 1) || (rotations == 3))
489
1.17k
    {
490
      /*
491
        Allow override of tile geometry for testing.
492
      */
493
1.17k
      const char *
494
1.17k
        value;
495
496
1.17k
      if (!GetPixelCacheInCore(image) || !GetPixelCacheInCore(rotate_image))
497
0
        tile_height_max=tile_width_max=8192/sizeof(PixelPacket); /* 8k x 8k = 64MB */
498
499
1.17k
      if ((value=getenv("MAGICK_ROTATE_TILE_GEOMETRY")))
500
0
        {
501
0
          double
502
0
            width,
503
0
            height;
504
505
0
          if (GetMagickDimension(value,&width,&height,NULL,NULL) == 2)
506
0
            {
507
0
              tile_height_max=(unsigned long) height;
508
0
              tile_width_max=(unsigned long) width;
509
0
            }
510
0
        }
511
1.17k
    }
512
513
  /*
514
    Integral rotate the image.
515
  */
516
1.34k
  switch (rotations)
517
1.34k
    {
518
62
    case 0:
519
62
      {
520
        /*
521
          Rotate 0 degrees (nothing more to do).
522
        */
523
62
        (void) strlcpy(message,"[%s] Rotate: 0 degrees...",sizeof(message));
524
62
        if (!MagickMonitorFormatted(image->rows-1,image->rows,exception,
525
62
                                    message,image->filename))
526
0
          status=MagickFail;
527
62
        break;
528
0
      }
529
1.04k
    case 1:
530
1.04k
      {
531
        /*
532
          Rotate 90 degrees.
533
        */
534
1.04k
        magick_int64_t
535
1.04k
          tile;
536
537
1.04k
        magick_uint64_t
538
1.04k
          total_tiles;
539
540
1.04k
        long
541
1.04k
          tile_y;
542
543
1.04k
        MagickBool
544
1.04k
          monitor_active;
545
546
#if defined(IntegralRotateImageUseOpenMP)
547
#  if defined(HAVE_OPENMP)
548
        int
549
          rotate_threads = RotateThreads;
550
#  endif
551
#endif
552
553
1.04k
        (void) strlcpy(message,"[%s] Rotate: 90 degrees...",sizeof(message));
554
1.04k
        total_tiles=((((size_t) image->rows/tile_height_max)+1)*
555
1.04k
                     (((size_t) image->columns/tile_width_max)+1));
556
1.04k
        tile=0;
557
558
1.04k
        monitor_active=MagickMonitorActive();
559
560
#if defined(IntegralRotateImageUseOpenMP)
561
#  if defined(HAVE_OPENMP)
562
#    if defined(TUNE_OPENMP)
563
#      pragma omp parallel for schedule(runtime) shared(status, tile)
564
#    else
565
#      pragma omp parallel for num_threads(rotate_threads) schedule(static,1) shared(status, tile)
566
#    endif
567
#  endif
568
#endif
569
2.23k
        for (tile_y=0; tile_y < (long) image->rows; tile_y+=tile_height_max)
570
1.18k
          {
571
1.18k
            long
572
1.18k
              tile_x;
573
574
1.18k
            MagickPassFail
575
1.18k
              thread_status;
576
577
1.18k
            thread_status=status;
578
1.18k
            if (thread_status == MagickFail)
579
0
              continue;
580
581
2.62k
            for (tile_x=0; tile_x < (long) image->columns; tile_x+=tile_width_max)
582
1.44k
              {
583
1.44k
                long
584
1.44k
                  dest_tile_x,
585
1.44k
                  dest_tile_y;
586
587
1.44k
                long
588
1.44k
                  tile_width,
589
1.44k
                  tile_height;
590
591
1.44k
                const PixelPacket
592
1.44k
                  *tile_pixels=(const PixelPacket *) NULL;
593
594
1.44k
                long
595
1.44k
                  y;
596
597
                /*
598
                  Compute image region corresponding to tile.
599
                */
600
1.44k
                if ((unsigned long) tile_x+tile_width_max > image->columns)
601
1.17k
                  tile_width=(tile_width_max-(tile_x+tile_width_max-image->columns));
602
265
                else
603
265
                  tile_width=tile_width_max;
604
1.44k
                if ((unsigned long) tile_y+tile_height_max > image->rows)
605
1.26k
                  tile_height=(tile_height_max-(tile_y+tile_height_max-image->rows));
606
181
                else
607
181
                  tile_height=tile_height_max;
608
                /*
609
                  Acquire tile
610
                */
611
1.44k
                tile_pixels=AcquireImagePixels(image,tile_x,tile_y,
612
1.44k
                                               tile_width,tile_height,exception);
613
1.44k
                if (tile_pixels == (const PixelPacket *) NULL)
614
0
                  {
615
0
                    thread_status=MagickFail;
616
0
                    break;
617
0
                  }
618
                /*
619
                  Compute destination tile coordinates.
620
                */
621
1.44k
                dest_tile_x=rotate_image->columns-(tile_y+tile_height);
622
1.44k
                dest_tile_y=tile_x;
623
                /*
624
                  Rotate tile
625
                */
626
127k
                for (y=0; y < tile_width; y++)
627
125k
                  {
628
125k
                    register const PixelPacket
629
125k
                      *p;
630
631
125k
                    register PixelPacket
632
125k
                      *q;
633
634
125k
                    register const IndexPacket
635
125k
                      *indexes;
636
637
125k
                    IndexPacket
638
125k
                      *rotate_indexes;
639
640
125k
                    register long
641
125k
                      x;
642
643
125k
                    q=SetImagePixelsEx(rotate_image,dest_tile_x,dest_tile_y+y,
644
125k
                                       tile_height,1,exception);
645
125k
                    if (q == (PixelPacket *) NULL)
646
0
                      {
647
0
                        thread_status=MagickFail;
648
0
                        break;
649
0
                      }
650
                    /*
651
                      DirectClass pixels
652
                    */
653
125k
                    p=tile_pixels+((size_t) tile_height-1)*tile_width + y;
654
7.61M
                    for (x=tile_height; x != 0; x--)
655
7.48M
                      {
656
7.48M
                        *q = *p;
657
7.48M
                        q++;
658
7.48M
                        p-=tile_width;
659
7.48M
                      }
660
                    /*
661
                      Indexes
662
                    */
663
125k
                    indexes=AccessImmutableIndexes(image);
664
125k
                    if (indexes != (IndexPacket *) NULL)
665
24.1k
                      {
666
24.1k
                        rotate_indexes=AccessMutableIndexes(rotate_image);
667
24.1k
                        if (rotate_indexes != (IndexPacket *) NULL)
668
24.1k
                          {
669
24.1k
                            register IndexPacket
670
24.1k
                              *iq;
671
672
24.1k
                            register const IndexPacket
673
24.1k
                              *ip;
674
675
24.1k
                            iq=rotate_indexes;
676
24.1k
                            ip=indexes+((size_t) tile_height-1)*tile_width + y;
677
143k
                            for (x=tile_height; x != 0; x--)
678
118k
                              {
679
118k
                                *iq = *ip;
680
118k
                                iq++;
681
118k
                                ip -= tile_width;
682
118k
                              }
683
24.1k
                          }
684
24.1k
                      }
685
125k
                    if (!SyncImagePixelsEx(rotate_image,exception))
686
0
                      {
687
0
                        thread_status=MagickFail;
688
0
                        break;
689
0
                      }
690
125k
                  }
691
692
1.44k
                if (monitor_active)
693
0
                  {
694
0
                    unsigned long
695
0
                      thread_tile;
696
697
#if defined(IntegralRotateImageUseOpenMP)
698
#  if defined(HAVE_OPENMP)
699
#    pragma omp atomic
700
#  endif
701
#endif
702
0
                    tile++;
703
#if defined(HAVE_OPENMP)
704
#  pragma omp flush (tile)
705
#endif
706
0
                    thread_tile=tile;
707
0
                    if (QuantumTick(thread_tile,total_tiles))
708
0
                      if (!MagickMonitorFormatted(thread_tile,total_tiles,exception,
709
0
                                                  message,image->filename))
710
0
                        thread_status=MagickFail;
711
0
                  }
712
713
1.44k
                if (thread_status == MagickFail)
714
0
                  {
715
0
                    status=MagickFail;
716
#if defined(IntegralRotateImageUseOpenMP)
717
#  if defined(HAVE_OPENMP)
718
#    pragma omp flush (status)
719
#  endif
720
#endif
721
0
                  }
722
1.44k
              }
723
1.18k
            if (thread_status == MagickFail)
724
0
              {
725
0
                status = thread_status;
726
#if defined(IntegralRotateImageUseOpenMP)
727
#  if defined(HAVE_OPENMP)
728
#    pragma omp flush (status)
729
#  endif
730
#endif
731
0
              }
732
1.18k
          }
733
1.04k
        Swap(page.width,page.height);
734
1.04k
        Swap(page.x,page.y);
735
1.04k
        page.x=(long) (page.width-rotate_image->columns-page.x);
736
1.04k
        break;
737
0
      }
738
102
    case 2:
739
102
      {
740
        /*
741
          Rotate 180 degrees.
742
        */
743
102
        long
744
102
          y;
745
746
102
        unsigned long
747
102
          row_count=0;
748
749
102
        MagickBool
750
102
          monitor_active;
751
752
#if defined(IntegralRotateImageUseOpenMP)
753
#  if defined(HAVE_OPENMP)
754
        int
755
          rotate_threads = RotateThreads;
756
#  endif
757
#endif
758
759
102
        (void) strlcpy(message,"[%s] Rotate: 180 degrees...",sizeof(message));
760
761
102
        monitor_active=MagickMonitorActive();
762
763
#if defined(IntegralRotateImageUseOpenMP)
764
#  if defined(HAVE_OPENMP)
765
#    if defined(TUNE_OPENMP)
766
#      pragma omp parallel for schedule(runtime) shared(row_count, status)
767
#    else
768
#      pragma omp parallel for num_threads(rotate_threads) schedule(static,8) shared(row_count, status)
769
#    endif
770
#  endif
771
#endif
772
4.27k
        for (y=0; y < (long) image->rows; y++)
773
4.17k
          {
774
4.17k
            register const PixelPacket
775
4.17k
              *p;
776
777
4.17k
            register PixelPacket
778
4.17k
              *q;
779
780
4.17k
            register const IndexPacket
781
4.17k
              *indexes;
782
783
4.17k
            IndexPacket
784
4.17k
              *rotate_indexes;
785
786
4.17k
            register long
787
4.17k
              x;
788
789
4.17k
            MagickPassFail
790
4.17k
              thread_status;
791
792
4.17k
            thread_status=status;
793
4.17k
            if (thread_status == MagickFail)
794
0
              continue;
795
796
4.17k
            p=AcquireImagePixels(image,0,y,image->columns,1,exception);
797
4.17k
            q=SetImagePixelsEx(rotate_image,0,(long) (image->rows-y-1),
798
4.17k
                               image->columns,1,exception);
799
4.17k
            if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
800
0
              thread_status=MagickFail;
801
4.17k
            if (thread_status != MagickFail)
802
4.17k
              {
803
4.17k
                q+=image->columns;
804
4.17k
                indexes=AccessImmutableIndexes(image);
805
4.17k
                rotate_indexes=AccessMutableIndexes(rotate_image);
806
4.17k
                if ((indexes != (IndexPacket *) NULL) &&
807
3.33k
                    (rotate_indexes != (IndexPacket *) NULL))
808
546k
                  for (x=0; x < (long) image->columns; x++)
809
543k
                    rotate_indexes[image->columns-x-1]=indexes[x];
810
579k
                for (x=0; x < (long) image->columns; x++)
811
575k
                  *--q=(*p++);
812
4.17k
                if (!SyncImagePixelsEx(rotate_image,exception))
813
0
                  thread_status=MagickFail;
814
4.17k
              }
815
816
4.17k
            if (monitor_active)
817
0
              {
818
0
                unsigned long
819
0
                  thread_row_count;
820
821
#if defined(IntegralRotateImageUseOpenMP)
822
#  if defined(HAVE_OPENMP)
823
#    pragma omp atomic
824
#  endif
825
#endif
826
0
                row_count++;
827
#if defined(HAVE_OPENMP)
828
#  pragma omp flush (row_count)
829
#endif
830
0
                thread_row_count=row_count;
831
0
                if (QuantumTick(thread_row_count,image->rows))
832
0
                  if (!MagickMonitorFormatted(thread_row_count,image->rows,exception,
833
0
                                              message,image->filename))
834
0
                    thread_status=MagickFail;
835
0
              }
836
837
4.17k
            if (thread_status == MagickFail)
838
0
              {
839
0
                status=MagickFail;
840
#if defined(IntegralRotateImageUseOpenMP)
841
#  if defined(HAVE_OPENMP)
842
#    pragma omp flush (status)
843
#  endif
844
#endif
845
0
              }
846
4.17k
          }
847
102
        page.x=(long) (page.width-rotate_image->columns-page.x);
848
102
        page.y=(long) (page.height-rotate_image->rows-page.y);
849
102
        break;
850
0
      }
851
133
    case 3:
852
133
      {
853
        /*
854
          Rotate 270 degrees.
855
        */
856
857
133
        magick_int64_t
858
133
          tile;
859
860
133
        magick_uint64_t
861
133
          total_tiles;
862
863
133
        long
864
133
          tile_y;
865
866
133
        MagickBool
867
133
          monitor_active;
868
869
#if defined(IntegralRotateImageUseOpenMP)
870
#  if defined(HAVE_OPENMP)
871
        int
872
          rotate_threads = RotateThreads;
873
#  endif
874
#endif
875
876
133
        (void) strlcpy(message,"[%s] Rotate: 270 degrees...",sizeof(message));
877
133
        total_tiles=((((size_t) image->rows/tile_height_max)+1)*
878
133
                     (((size_t) image->columns/tile_width_max)+1));
879
133
        tile=0;
880
881
133
        monitor_active=MagickMonitorActive();
882
883
#if defined(IntegralRotateImageUseOpenMP)
884
#  if defined(HAVE_OPENMP)
885
#    if defined(TUNE_OPENMP)
886
#      pragma omp parallel for schedule(runtime) shared(status, tile)
887
#    else
888
#      pragma omp parallel for num_threads(rotate_threads) schedule(static,1) shared(status, tile)
889
#    endif
890
#  endif
891
#endif
892
273
        for (tile_y=0; tile_y < (long) image->rows; tile_y+=tile_height_max)
893
140
          {
894
140
            long
895
140
              tile_x;
896
897
140
            MagickPassFail
898
140
              thread_status;
899
900
140
            thread_status=status;
901
140
            if (thread_status == MagickFail)
902
0
              continue;
903
904
342
            for (tile_x=0; tile_x < (long) image->columns; tile_x+=tile_width_max)
905
202
              {
906
202
                long
907
202
                  tile_width,
908
202
                  tile_height;
909
910
202
                long
911
202
                  dest_tile_x=0,
912
202
                  dest_tile_y=0;
913
914
202
                long
915
202
                  y=0;
916
917
202
                const PixelPacket
918
202
                  *tile_pixels = (const PixelPacket *) NULL;
919
920
                /*
921
                  Compute image region corresponding to tile.
922
                */
923
202
                if ((unsigned long) tile_x+tile_width_max > image->columns)
924
140
                  tile_width=(tile_width_max-(tile_x+tile_width_max-image->columns));
925
62
                else
926
62
                  tile_width=tile_width_max;
927
202
                if ((unsigned long) tile_y+tile_height_max > image->rows)
928
193
                  tile_height=(tile_height_max-(tile_y+tile_height_max-image->rows));
929
9
                else
930
9
                  tile_height=tile_height_max;
931
                /*
932
                  Acquire tile
933
                */
934
202
                tile_pixels=AcquireImagePixels(image,tile_x,tile_y,
935
202
                                               tile_width,tile_height,exception);
936
202
                if (tile_pixels == (const PixelPacket *) NULL)
937
0
                  {
938
0
                    thread_status=MagickFail;
939
0
                    break;
940
0
                  }
941
                /*
942
                  Compute destination tile coordinates.
943
                */
944
202
                dest_tile_x=tile_y;
945
202
                dest_tile_y=rotate_image->rows-(tile_x+tile_width);
946
                /*
947
                  Rotate tile
948
                */
949
32.0k
                for (y=0; y < tile_width; y++)
950
31.8k
                  {
951
31.8k
                    register const PixelPacket
952
31.8k
                      *p;
953
954
31.8k
                    register PixelPacket
955
31.8k
                      *q;
956
957
31.8k
                    register const IndexPacket
958
31.8k
                      *indexes;
959
960
31.8k
                    register long
961
31.8k
                      x;
962
963
31.8k
                    IndexPacket
964
31.8k
                      *rotate_indexes;
965
966
31.8k
                    q=SetImagePixelsEx(rotate_image,dest_tile_x,dest_tile_y+y,
967
31.8k
                                       tile_height,1,exception);
968
31.8k
                    if (q == (PixelPacket *) NULL)
969
0
                      {
970
0
                        thread_status=MagickFail;
971
0
                        break;
972
0
                      }
973
                    /*
974
                      DirectClass pixels
975
                    */
976
31.8k
                    p=tile_pixels+((size_t) tile_width-1-y);
977
1.64M
                    for (x=tile_height; x != 0; x--)
978
1.61M
                      {
979
1.61M
                        *q = *p;
980
1.61M
                        q++;
981
1.61M
                        p += tile_width;
982
1.61M
                      }
983
                    /*
984
                      Indexes
985
                    */
986
31.8k
                    indexes=AccessImmutableIndexes(image);
987
31.8k
                    if (indexes != (IndexPacket *) NULL)
988
10.4k
                      {
989
10.4k
                        rotate_indexes=AccessMutableIndexes(rotate_image);
990
10.4k
                        if (rotate_indexes != (IndexPacket *) NULL)
991
10.4k
                          {
992
10.4k
                            register IndexPacket
993
10.4k
                              *iq;
994
995
10.4k
                            register const IndexPacket
996
10.4k
                              *ip;
997
998
10.4k
                            iq=rotate_indexes;
999
10.4k
                            ip=indexes+((size_t) tile_width-1-y);
1000
238k
                            for (x=tile_height; x != 0; x--)
1001
228k
                              {
1002
228k
                                *iq = *ip;
1003
228k
                                iq++;
1004
228k
                                ip += tile_width;
1005
228k
                              }
1006
10.4k
                          }
1007
10.4k
                      }
1008
31.8k
                    if (!SyncImagePixelsEx(rotate_image,exception))
1009
0
                      {
1010
0
                        thread_status=MagickFail;
1011
0
                        break;
1012
0
                      }
1013
31.8k
                  }
1014
1015
202
                if (monitor_active)
1016
0
                  {
1017
0
                    unsigned long
1018
0
                      thread_tile;
1019
1020
#if defined(IntegralRotateImageUseOpenMP)
1021
#  if defined(HAVE_OPENMP)
1022
#    pragma omp atomic
1023
#  endif
1024
#endif
1025
0
                    tile++;
1026
#if defined(HAVE_OPENMP)
1027
#  pragma omp flush (tile)
1028
#endif
1029
0
                    thread_tile=tile;
1030
0
                    if (QuantumTick(thread_tile,total_tiles))
1031
0
                      if (!MagickMonitorFormatted(thread_tile,total_tiles,exception,
1032
0
                                                  message,image->filename))
1033
0
                        thread_status=MagickFail;
1034
0
                  }
1035
1036
202
                if (thread_status == MagickFail)
1037
0
                  {
1038
0
                    status=MagickFail;
1039
#if defined(IntegralRotateImageUseOpenMP)
1040
#  if defined(HAVE_OPENMP)
1041
#  pragma omp flush (status)
1042
#  endif
1043
#endif
1044
0
                  }
1045
1046
202
                if (thread_status == MagickFail)
1047
0
                  break;
1048
202
              }
1049
1050
140
            if (thread_status == MagickFail)
1051
0
              {
1052
0
                status = thread_status;
1053
#if defined(IntegralRotateImageUseOpenMP)
1054
#  if defined(HAVE_OPENMP)
1055
#  pragma omp flush (status)
1056
#  endif
1057
#endif
1058
0
              }
1059
140
          }
1060
133
        Swap(page.width,page.height);
1061
133
        Swap(page.x,page.y);
1062
133
        page.y=(long) (page.height-rotate_image->rows-page.y);
1063
133
        break;
1064
0
      }
1065
1.34k
    }
1066
1067
1.34k
  if (status == MagickFail)
1068
0
    {
1069
0
      DestroyImage(rotate_image);
1070
0
      rotate_image = (Image *) NULL;
1071
0
    }
1072
1.34k
  else
1073
1.34k
    {
1074
1.34k
      rotate_image->page=page;
1075
1.34k
      rotate_image->is_grayscale=image->is_grayscale;
1076
1.34k
      rotate_image->is_monochrome=image->is_monochrome;
1077
1.34k
    }
1078
1.34k
  return(rotate_image);
1079
1.34k
}
1080

1081
/*
1082
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1083
%                                                                             %
1084
%                                                                             %
1085
%                                                                             %
1086
+   X S h e a r I m a g e                                                     %
1087
%                                                                             %
1088
%                                                                             %
1089
%                                                                             %
1090
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1091
%
1092
%  Procedure XShearImage shears the image in the X direction with a shear angle
1093
%  of 'degrees'.  Positive angles shear counter-clockwise (right-hand rule),
1094
%  and negative angles shear clockwise.  Angles are measured relative to a
1095
%  vertical Y-axis.  X shears will widen an image creating 'empty' triangles
1096
%  on the left and right sides of the source image.
1097
%
1098
%  The format of the XShearImage method is:
1099
%
1100
%      MagickPassFail XShearImage(Image *image,const double degrees,
1101
%        const unsigned long width,const unsigned long height,
1102
%        const long x_offset,long y_offset,ExceptionInfo *exception)
1103
%
1104
%  A description of each parameter follows.
1105
%
1106
%    o image: The image.
1107
%
1108
%    o degrees: A double representing the shearing angle along the X axis.
1109
%
1110
%    o width, height, x_offset, y_offset: Defines a region of the image
1111
%      to shear.
1112
%
1113
%    o exception: Return any errors or warnings in this structure.
1114
%
1115
*/
1116
1117
static MagickPassFail
1118
XShearImage(Image *image,const double degrees,
1119
            const unsigned long width,const unsigned long height,
1120
            const long x_offset,long y_offset,ExceptionInfo *exception)
1121
736
{
1122
736
#define XShearImageText  "[%s] X Shear: %+g degrees, region %lux%lu%+ld%+ld...  "
1123
1124
736
  long
1125
736
    y,
1126
736
    xr_offset;
1127
1128
736
  unsigned long
1129
736
    row_count=0;
1130
1131
736
  MagickBool
1132
736
    monitor_active;
1133
1134
736
  unsigned int
1135
736
    is_grayscale;
1136
1137
736
  MagickPassFail
1138
736
    status=MagickPass;
1139
1140
736
  assert(image != (Image *) NULL);
1141
736
  is_grayscale=image->is_grayscale;
1142
1143
736
  assert(x_offset >= 0);
1144
736
  assert(x_offset < (long) image->columns);
1145
736
  assert(y_offset >= 0);
1146
736
  assert(y_offset < (long) image->rows);
1147
736
  assert(width <= (image->columns-(unsigned long) x_offset));
1148
736
  assert(height <= (image->rows-(unsigned long) y_offset));
1149
736
  xr_offset=image->columns-width-x_offset;
1150
1151
736
  monitor_active=MagickMonitorActive();
1152
1153
#if defined(HAVE_OPENMP)
1154
#  if defined(TUNE_OPENMP)
1155
#    pragma omp parallel for schedule(runtime) shared(row_count, status)
1156
#  else
1157
#    if defined(USE_STATIC_SCHEDULING_ONLY)
1158
#      pragma omp parallel for schedule(static) shared(row_count, status)
1159
#    else
1160
#      pragma omp parallel for schedule(dynamic) shared(row_count, status)
1161
#    endif
1162
#  endif
1163
#endif
1164
111k
  for (y=0; y < (long) height; y++)
1165
110k
    {
1166
110k
      double
1167
110k
        alpha,
1168
110k
        displacement;
1169
1170
110k
      long
1171
110k
        step,
1172
110k
        skip;
1173
1174
110k
      PixelPacket
1175
110k
        pixel;
1176
1177
110k
      register long
1178
110k
        i;
1179
1180
110k
      register PixelPacket
1181
110k
        *p,
1182
110k
        *q;
1183
1184
110k
      enum
1185
110k
      {
1186
110k
        LEFT,
1187
110k
        RIGHT
1188
110k
      } direction;
1189
1190
110k
      MagickPassFail
1191
110k
        thread_status;
1192
1193
110k
      thread_status=status;
1194
110k
      if (thread_status == MagickFail)
1195
3.84k
        continue;
1196
1197
106k
      displacement=degrees*(y-height/2.0);
1198
106k
      if (displacement == 0.0)
1199
392
        continue;
1200
106k
      if (displacement > 0.0)
1201
53.1k
        direction=RIGHT;
1202
53.1k
      else
1203
53.1k
        {
1204
53.1k
          displacement*=(-1.0);
1205
53.1k
          direction=LEFT;
1206
53.1k
        }
1207
106k
      step=(long) floor(displacement);
1208
106k
      alpha=MaxRGBDouble*(displacement-step);
1209
106k
      if (alpha == 0.0)
1210
0
        {
1211
          /*
1212
            No fractional displacement-- just copy.
1213
          */
1214
0
          switch (direction)
1215
0
            {
1216
0
            case LEFT:
1217
0
              {
1218
                /*
1219
                  Transfer pixels left-to-right.
1220
                */
1221
0
                skip = (step > x_offset) ? step - x_offset : 0;
1222
0
                p=GetImagePixelsEx(image,0,y+y_offset,image->columns,1,exception);
1223
0
                if (p == (PixelPacket *) NULL)
1224
0
                  {
1225
0
                    thread_status=MagickFail;
1226
0
                    break;
1227
0
                  }
1228
0
                p+= (ptrdiff_t)x_offset+skip;
1229
0
                q=p-step;
1230
0
                (void) memcpy(q,p,((size_t) width-(size_t) skip)*sizeof(PixelPacket));
1231
0
                q+=width;
1232
0
                for (i=0; i < step; i++)
1233
0
                  *q++=image->background_color;
1234
0
                break;
1235
0
              }
1236
0
            case RIGHT:
1237
0
              {
1238
                /*
1239
                  Transfer pixels right-to-left.
1240
                */
1241
0
                skip = (step > xr_offset) ? step - xr_offset : 0;
1242
0
                p=GetImagePixelsEx(image,0,y+y_offset,image->columns,1,exception);
1243
0
                if (p == (PixelPacket *) NULL)
1244
0
                  {
1245
0
                    thread_status=MagickFail;
1246
0
                    break;
1247
0
                  }
1248
0
                p+= (ptrdiff_t)x_offset+width-skip;
1249
0
                q=p+step;
1250
0
                for (i=0; i < ((long) width - skip); i++)
1251
0
                  *--q=(*--p);
1252
0
                for (i=0; i < step; i++)
1253
0
                  *--q=image->background_color;
1254
0
                break;
1255
0
              }
1256
0
            }
1257
0
          if (!SyncImagePixelsEx(image,exception))
1258
0
            thread_status=MagickFail;
1259
1260
0
          if (monitor_active)
1261
0
            {
1262
0
              unsigned long
1263
0
                thread_row_count;
1264
1265
#if defined(HAVE_OPENMP)
1266
#  pragma omp atomic
1267
#endif
1268
0
              row_count++;
1269
#if defined(HAVE_OPENMP)
1270
#  pragma omp flush (row_count)
1271
#endif
1272
0
              thread_row_count=row_count;
1273
0
              if (QuantumTick(thread_row_count,height))
1274
0
                if (!MagickMonitorFormatted(thread_row_count,height,exception,
1275
0
                                            XShearImageText,image->filename,
1276
0
                                            degrees,width,height,
1277
0
                                            x_offset,y_offset))
1278
0
                  thread_status=MagickFail;
1279
0
            }
1280
1281
0
          if (thread_status == MagickFail)
1282
0
            {
1283
0
              status=MagickFail;
1284
#if defined(HAVE_OPENMP)
1285
#  pragma omp flush (status)
1286
#endif
1287
0
            }
1288
1289
0
          continue;
1290
0
        }
1291
      /*
1292
        Fractional displacement.
1293
      */
1294
106k
      step++;
1295
106k
      pixel=image->background_color;
1296
106k
      switch (direction)
1297
106k
        {
1298
53.1k
        case LEFT:
1299
53.1k
          {
1300
            /*
1301
              Transfer pixels left-to-right.
1302
            */
1303
53.1k
            p=GetImagePixelsEx(image,0,y+y_offset,image->columns,1,exception);
1304
53.1k
            if (p == (PixelPacket *) NULL)
1305
3
              {
1306
3
                thread_status=MagickFail;
1307
3
                break;
1308
3
              }
1309
53.1k
            p+=x_offset;
1310
53.1k
            q=p-step;
1311
7.19M
            for (i=0; i < (long) width; i++)
1312
7.14M
              {
1313
7.14M
                if ((x_offset+i) < step)
1314
0
                  {
1315
0
                    pixel=(*++p);
1316
0
                    q++;
1317
0
                    continue;
1318
0
                  }
1319
7.14M
                BlendCompositePixel(q,&pixel,p,alpha);
1320
7.14M
                q++;
1321
7.14M
                pixel=(*p++);
1322
7.14M
              }
1323
53.1k
            BlendCompositePixel(q,&pixel,&image->background_color,alpha);
1324
53.1k
            q++;
1325
1.65M
            for (i=0; i < (step-1); i++)
1326
1.59M
              *q++=image->background_color;
1327
53.1k
            break;
1328
53.1k
          }
1329
53.1k
        case RIGHT:
1330
53.1k
          {
1331
            /*
1332
              Transfer pixels right-to-left.
1333
            */
1334
53.1k
            p=GetImagePixelsEx(image,0,y+y_offset,image->columns,1,exception);
1335
53.1k
            if (p == (PixelPacket *) NULL)
1336
1
              {
1337
1
                thread_status=MagickFail;
1338
1
                break;
1339
1
              }
1340
53.1k
            p+= (ptrdiff_t)x_offset+width;
1341
53.1k
            q=p+step;
1342
7.21M
            for (i=0; i < (long) width; i++)
1343
7.15M
              {
1344
7.15M
                p--;
1345
7.15M
                q--;
1346
7.15M
                if ((x_offset+width+step-i) > image->columns)
1347
0
                  continue;
1348
7.15M
                BlendCompositePixel(q,&pixel,p,alpha);
1349
7.15M
                pixel=(*p);
1350
7.15M
              }
1351
53.1k
            --q;
1352
53.1k
            BlendCompositePixel(q,&pixel,&image->background_color,alpha);
1353
1.65M
            for (i=0; i < (step-1); i++)
1354
1.60M
              *--q=image->background_color;
1355
53.1k
            break;
1356
53.1k
          }
1357
106k
        }
1358
106k
      if (!SyncImagePixelsEx(image,exception))
1359
0
        thread_status=MagickFail;
1360
1361
106k
      if (monitor_active)
1362
0
        {
1363
0
          unsigned long
1364
0
            thread_row_count;
1365
1366
#if defined(HAVE_OPENMP)
1367
#  pragma omp atomic
1368
#endif
1369
0
          row_count++;
1370
#if defined(HAVE_OPENMP)
1371
#  pragma omp flush (row_count)
1372
#endif
1373
0
          thread_row_count=row_count;
1374
0
          if (QuantumTick(thread_row_count,height))
1375
0
            if (!MagickMonitorFormatted(thread_row_count,height,exception,
1376
0
                                        XShearImageText,image->filename,
1377
0
                                        degrees,width,height,
1378
0
                                        x_offset,y_offset))
1379
0
              thread_status=MagickFail;
1380
0
        }
1381
1382
106k
      if (thread_status == MagickFail)
1383
4
        {
1384
4
          status=MagickFail;
1385
#if defined(HAVE_OPENMP)
1386
#  pragma omp flush (status)
1387
#endif
1388
4
        }
1389
106k
    }
1390
736
  if (is_grayscale && IsGray(image->background_color))
1391
0
    image->is_grayscale=True;
1392
1393
736
  return status;
1394
736
}
1395

1396
/*
1397
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1398
%                                                                             %
1399
%                                                                             %
1400
%                                                                             %
1401
+   Y S h e a r I m a g e                                                     %
1402
%                                                                             %
1403
%                                                                             %
1404
%                                                                             %
1405
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1406
%
1407
%  Procedure YShearImage shears the image in the Y direction with a shear
1408
%  angle of 'degrees'.  Positive angles shear counter-clockwise (right-hand
1409
%  rule), and negative angles shear clockwise.  Angles are measured relative
1410
%  to a horizontal X-axis.  Y shears will increase the height of an image
1411
%  creating 'empty' triangles on the top and bottom of the source image.
1412
%
1413
%  The format of the YShearImage method is:
1414
%
1415
%      MagickPassFail YShearImage(Image *image,const double degrees,
1416
%        const unsigned long width,const unsigned long height,long x_offset,
1417
%        const long y_offset,ExceptionInfo *exception)
1418
%
1419
%  A description of each parameter follows.
1420
%
1421
%    o image: The image.
1422
%
1423
%    o degrees: A double representing the shearing angle along the Y axis.
1424
%
1425
%    o width, height, x_offset, y_offset: Defines a region of the image
1426
%      to shear.
1427
%
1428
%    o exception: Return any errors or warnings in this structure.
1429
%
1430
%
1431
*/
1432
static MagickPassFail
1433
YShearImage(Image *image,const double degrees,
1434
            const unsigned long width,const unsigned long height,long x_offset,
1435
            const long y_offset,ExceptionInfo *exception)
1436
366
{
1437
366
#define YShearImageText  "[%s] Y Shear: %+g degrees, region %lux%lu%+ld%+ld...  "
1438
1439
366
  long
1440
366
    x,
1441
366
    yr_offset;
1442
1443
366
  unsigned long
1444
366
    row_count=0;
1445
1446
366
  MagickBool
1447
366
    monitor_active;
1448
1449
366
  unsigned int
1450
366
    is_grayscale;
1451
1452
366
  MagickPassFail
1453
366
    status=MagickPass;
1454
1455
366
  assert(image != (Image *) NULL);
1456
366
  is_grayscale=image->is_grayscale;
1457
1458
366
  assert(x_offset >= 0);
1459
366
  assert(x_offset < (long) image->columns);
1460
366
  assert(y_offset >= 0);
1461
366
  assert(y_offset < (long) image->rows);
1462
366
  assert(width <= (image->columns-(unsigned long) x_offset));
1463
366
  assert(height <= (image->rows-(unsigned long) y_offset));
1464
366
  yr_offset=image->rows-height-y_offset;
1465
1466
366
  monitor_active=MagickMonitorActive();
1467
1468
#if defined(HAVE_OPENMP)
1469
#  if defined(TUNE_OPENMP)
1470
#    pragma omp parallel for schedule(runtime) shared(row_count, status)
1471
#  else
1472
#    if defined(USE_STATIC_SCHEDULING_ONLY)
1473
#      pragma omp parallel for schedule(static) shared(row_count, status)
1474
#    else
1475
#      pragma omp parallel for schedule(dynamic) shared(row_count, status)
1476
#    endif
1477
#  endif
1478
#endif
1479
45.5k
  for (x=0; x < (long) width; x++)
1480
45.2k
    {
1481
45.2k
      double
1482
45.2k
        alpha,
1483
45.2k
        displacement;
1484
1485
45.2k
      enum
1486
45.2k
      {
1487
45.2k
        UP,
1488
45.2k
        DOWN
1489
45.2k
      } direction;
1490
1491
45.2k
      long
1492
45.2k
        step,
1493
45.2k
        skip;
1494
1495
45.2k
      register PixelPacket
1496
45.2k
        *p,
1497
45.2k
        *q;
1498
1499
45.2k
      register long
1500
45.2k
        i;
1501
1502
45.2k
      PixelPacket
1503
45.2k
        pixel;
1504
1505
45.2k
      MagickPassFail
1506
45.2k
        thread_status;
1507
1508
45.2k
      thread_status=status;
1509
45.2k
      if (thread_status == MagickFail)
1510
0
        continue;
1511
1512
45.2k
      displacement=degrees*(x-width/2.0);
1513
45.2k
      if (displacement == 0.0)
1514
207
        continue;
1515
45.0k
      if (displacement > 0.0)
1516
22.5k
        direction=DOWN;
1517
22.4k
      else
1518
22.4k
        {
1519
22.4k
          displacement*=(-1.0);
1520
22.4k
          direction=UP;
1521
22.4k
        }
1522
45.0k
      step=(long) floor(displacement);
1523
45.0k
      alpha=(double) MaxRGB*(displacement-step);
1524
45.0k
      if (alpha == 0.0)
1525
0
        {
1526
          /*
1527
            No fractional displacement-- just copy the pixels.
1528
          */
1529
0
          switch (direction)
1530
0
            {
1531
0
            case UP:
1532
0
              {
1533
                /*
1534
                  Transfer pixels top-to-bottom.
1535
                */
1536
0
                skip = (step > y_offset) ? step - y_offset : 0;
1537
0
                p=GetImagePixelsEx(image,x+x_offset,0,1,image->rows,exception);
1538
0
                if (p == (PixelPacket *) NULL)
1539
0
                  {
1540
0
                    thread_status=MagickFail;
1541
0
                    break;
1542
0
                  }
1543
0
                p+= (ptrdiff_t)y_offset+skip;
1544
0
                q=p-step;
1545
0
                (void) memcpy(q,p,((size_t) height-(size_t)skip)*sizeof(PixelPacket));
1546
0
                q+=height;
1547
0
                for (i=0; i < (long) step; i++)
1548
0
                  *q++=image->background_color;
1549
0
                break;
1550
0
              }
1551
0
            case DOWN:
1552
0
              {
1553
                /*
1554
                  Transfer pixels bottom-to-top.
1555
                */
1556
0
                skip = (step > yr_offset) ? step - yr_offset : 0;
1557
0
                p=GetImagePixelsEx(image,x+x_offset,0,1,image->rows,exception);
1558
0
                if (p == (PixelPacket *) NULL)
1559
0
                  {
1560
0
                    thread_status=MagickFail;
1561
0
                    break;
1562
0
                  }
1563
0
                p+= (ptrdiff_t)y_offset+height-skip;
1564
0
                q=p+step;
1565
0
                for (i=0; i < ((long) height - skip); i++)
1566
0
                  *--q=(*--p);
1567
0
                for (i=0; i < step; i++)
1568
0
                  *--q=image->background_color;
1569
0
                break;
1570
0
              }
1571
0
            }
1572
0
          if (!SyncImagePixelsEx(image,exception))
1573
0
            thread_status=MagickFail;
1574
1575
0
          if (monitor_active)
1576
0
            {
1577
0
              unsigned long
1578
0
                thread_row_count;
1579
1580
#if defined(HAVE_OPENMP)
1581
#  pragma omp atomic
1582
#endif
1583
0
              row_count++;
1584
#if defined(HAVE_OPENMP)
1585
#  pragma omp flush (row_count)
1586
#endif
1587
0
              thread_row_count=row_count;
1588
0
              if (QuantumTick(thread_row_count,width))
1589
0
                if (!MagickMonitorFormatted(thread_row_count,width,exception,
1590
0
                                            YShearImageText,image->filename,
1591
0
                                            degrees,width,height,
1592
0
                                            x_offset,y_offset))
1593
0
                  thread_status=MagickFail;
1594
0
            }
1595
1596
0
          if (thread_status == MagickFail)
1597
0
            {
1598
0
              status=MagickFail;
1599
#if defined(HAVE_OPENMP)
1600
#  pragma omp flush (status)
1601
#endif
1602
0
            }
1603
1604
0
          continue;
1605
0
        }
1606
      /*
1607
        Fractional displacement.
1608
      */
1609
45.0k
      step++;
1610
45.0k
      pixel=image->background_color;
1611
45.0k
      switch (direction)
1612
45.0k
        {
1613
22.4k
        case UP:
1614
22.4k
          {
1615
            /*
1616
              Transfer pixels top-to-bottom.
1617
            */
1618
22.4k
            p=GetImagePixelsEx(image,x+x_offset,0,1,image->rows,exception);
1619
22.4k
            if (p == (PixelPacket *) NULL)
1620
0
              {
1621
0
                thread_status=MagickFail;
1622
0
                break;
1623
0
              }
1624
22.4k
            p+=y_offset;
1625
22.4k
            q=p-step;
1626
3.34M
            for (i=0; i < (long) height; i++)
1627
3.32M
              {
1628
3.32M
                if ((y_offset+i) < step)
1629
0
                  {
1630
0
                    pixel=(*++p);
1631
0
                    q++;
1632
0
                    continue;
1633
0
                  }
1634
3.32M
                BlendCompositePixel(q,&pixel,p,alpha);
1635
3.32M
                q++;
1636
3.32M
                pixel=(*p++);
1637
3.32M
              }
1638
22.4k
            BlendCompositePixel(q,&pixel,&image->background_color,alpha);
1639
22.4k
            q++;
1640
797k
            for (i=0; i < (step-1); i++)
1641
775k
              *q++=image->background_color;
1642
22.4k
            break;
1643
22.4k
          }
1644
22.5k
        case DOWN:
1645
22.5k
          {
1646
            /*
1647
              Transfer pixels bottom-to-top.
1648
            */
1649
22.5k
            p=GetImagePixelsEx(image,x+x_offset,0,1,image->rows,exception);
1650
22.5k
            if (p == (PixelPacket *) NULL)
1651
0
              {
1652
0
                thread_status=MagickFail;
1653
0
                break;
1654
0
              }
1655
22.5k
            p+= (ptrdiff_t)y_offset+height;
1656
22.5k
            q=p+step;
1657
3.35M
            for (i=0; i < (long) height; i++)
1658
3.32M
              {
1659
3.32M
                p--;
1660
3.32M
                q--;
1661
3.32M
                if ((y_offset+height+step-i) > image->rows)
1662
0
                  continue;
1663
3.32M
                BlendCompositePixel(q,&pixel,p,alpha);
1664
3.32M
                pixel=(*p);
1665
3.32M
              }
1666
22.5k
            --q;
1667
22.5k
            BlendCompositePixel(q,&pixel,&image->background_color,alpha);
1668
796k
            for (i=0; i < (step-1); i++)
1669
773k
              *--q=image->background_color;
1670
22.5k
            break;
1671
22.5k
          }
1672
45.0k
        }
1673
45.0k
      if (!SyncImagePixelsEx(image,exception))
1674
0
        thread_status=MagickFail;
1675
1676
45.0k
      if (monitor_active)
1677
0
        {
1678
0
          unsigned long
1679
0
            thread_row_count;
1680
1681
#if defined(HAVE_OPENMP)
1682
#  pragma omp atomic
1683
#endif
1684
0
          row_count++;
1685
#if defined(HAVE_OPENMP)
1686
#  pragma omp flush (row_count)
1687
#endif
1688
0
          thread_row_count=row_count;
1689
0
          if (QuantumTick(thread_row_count,width))
1690
0
            if (!MagickMonitorFormatted(thread_row_count,width,exception,
1691
0
                                        YShearImageText,image->filename,
1692
0
                                        degrees,width,height,
1693
0
                                        x_offset,y_offset))
1694
0
              thread_status=MagickFail;
1695
0
        }
1696
1697
45.0k
      if (thread_status == MagickFail)
1698
0
        {
1699
0
          status=MagickFail;
1700
#if defined(HAVE_OPENMP)
1701
#  pragma omp flush (status)
1702
#endif
1703
0
        }
1704
45.0k
    }
1705
366
  if (is_grayscale && IsGray(image->background_color))
1706
0
    image->is_grayscale=True;
1707
1708
366
  return status;
1709
366
}
1710

1711
/*
1712
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1713
%                                                                             %
1714
%                                                                             %
1715
%                                                                             %
1716
%   R o t a t e I m a g e                                                     %
1717
%                                                                             %
1718
%                                                                             %
1719
%                                                                             %
1720
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1721
%
1722
%  Method RotateImage creates a new image that is a rotated copy of an
1723
%  existing one.  Positive angles rotate counter-clockwise (right-hand rule),
1724
%  while negative angles rotate clockwise.  Rotated images are usually larger
1725
%  than the originals and have 'empty' triangular corners.  X axis.  Empty
1726
%  triangles left over from shearing the image are filled with the color
1727
%  specified by the image background_color.  RotateImage allocates the memory
1728
%  necessary for the new Image structure and returns a pointer to the new
1729
%  image.
1730
%
1731
%  Method RotateImage is based on the paper "A Fast Algorithm for General
1732
%  Raster Rotatation" by Alan W. Paeth.  RotateImage is adapted from a similar
1733
%  method based on the Paeth paper written by Michael Halle of the Spatial
1734
%  Imaging Group, MIT Media Lab.
1735
%
1736
%  The format of the RotateImage method is:
1737
%
1738
%      Image *RotateImage(const Image *image,const double degrees,
1739
%        ExceptionInfo *exception)
1740
%
1741
%  A description of each parameter follows.
1742
%
1743
%    o status: Method RotateImage returns a pointer to the image after
1744
%      rotating.  A null image is returned if there is a memory shortage.
1745
%
1746
%    o image: The image;  returned from
1747
%      ReadImage.
1748
%
1749
%    o degrees: Specifies the number of degrees to rotate the image.
1750
%
1751
%    o exception: Return any errors or warnings in this structure.
1752
%
1753
%
1754
*/
1755
MagickExport Image *
1756
RotateImage(const Image *image,const double degrees,ExceptionInfo *exception)
1757
1.34k
{
1758
1.34k
  double
1759
1.34k
    angle;
1760
1761
1.34k
  Image
1762
1.34k
    *integral_image = (Image *) NULL,
1763
1.34k
    *rotate_image = (Image *) NULL;
1764
1765
1.34k
  long
1766
1.34k
    x_offset,
1767
1.34k
    y_offset;
1768
1769
1.34k
  PointInfo
1770
1.34k
    shear;
1771
1772
1.34k
  RectangleInfo
1773
1.34k
    border_info;
1774
1775
1.34k
  unsigned long
1776
1.34k
    height,
1777
1.34k
    rotations,
1778
1.34k
    width,
1779
1.34k
    shear1_width,
1780
1.34k
    shear2_height,
1781
1.34k
    shear3_width,
1782
1.34k
    max_width,
1783
1.34k
    max_height;
1784
1785
  /*
1786
    Adjust rotation angle.
1787
  */
1788
1.34k
  assert(image != (Image *) NULL);
1789
1.34k
  assert(image->signature == MagickSignature);
1790
1.34k
  assert(exception != (ExceptionInfo *) NULL);
1791
1.34k
  assert(exception->signature == MagickSignature);
1792
1.34k
  angle = degrees - 360.0*(int)(degrees / 360);
1793
1.34k
  if(angle < -45.0) angle+=360.0;
1794
1795
3.06k
  for (rotations=0; angle > 45.0; rotations++)
1796
1.72k
    angle-=90.0;
1797
1.34k
  rotations%=4;
1798
  /*
1799
    Calculate shear equations.
1800
  */
1801
1.34k
  integral_image=IntegralRotateImage(image,rotations,exception);
1802
1.34k
  if (integral_image == (Image *) NULL)
1803
1
    goto rotate_image_exception;
1804
1805
1.34k
  shear.x=(-tan(DegreesToRadians(angle)/2.0));
1806
1.34k
  shear.y=sin(DegreesToRadians(angle));
1807
1.34k
  if ((shear.x == 0.0) || (shear.y == 0.0))
1808
973
    return(integral_image);
1809
  /*
1810
    Compute image size.
1811
  */
1812
370
  width=integral_image->columns;
1813
370
  height=integral_image->rows;
1814
370
  shear1_width=(unsigned long) floor(fabs(height*shear.x)+width+0.5);
1815
370
  shear2_height=(unsigned long) floor(fabs(shear1_width*shear.y)+height+0.5);
1816
370
  shear3_width=(unsigned long) floor(fabs(shear2_height*shear.x)+shear1_width+0.5);
1817
  /*
1818
    Compute maximum bounds to perform 3 shear operations.
1819
    Add extra pixels to account for fractional displacement
1820
  */
1821
370
  max_width = ((shear3_width > shear1_width) ? shear3_width : shear1_width) + 2;
1822
370
  max_height = shear2_height + 2;
1823
370
  x_offset = (long) floor((max_width-width)/2.0+0.5);
1824
370
  y_offset = (long) floor((max_height-height)/2.0+0.5);
1825
  /*
1826
    Surround image with a border.
1827
  */
1828
370
  integral_image->border_color=integral_image->background_color;
1829
370
  border_info.width=x_offset;
1830
370
  border_info.height=y_offset;
1831
370
  rotate_image=BorderImage(integral_image,&border_info,exception);
1832
370
  DestroyImage(integral_image);
1833
370
  integral_image=(Image *) NULL;
1834
370
  if (rotate_image == (Image *) NULL)
1835
0
    goto rotate_image_exception;
1836
1837
  /*
1838
    Rotate the image.
1839
  */
1840
370
  rotate_image->storage_class=DirectClass;
1841
370
  rotate_image->matte|=rotate_image->background_color.opacity != OpaqueOpacity;
1842
1843
370
  if (XShearImage(rotate_image,shear.x,width,height,
1844
370
          x_offset,y_offset,exception) != MagickPass)
1845
4
    goto rotate_image_exception;
1846
1847
366
  if (YShearImage(rotate_image,shear.y,shear1_width,height,
1848
366
                  (long) (rotate_image->columns-shear1_width)/2,y_offset,exception)
1849
366
      != MagickPass)
1850
0
    goto rotate_image_exception;
1851
1852
366
  if (XShearImage(rotate_image,shear.x,shear1_width,shear2_height,
1853
366
                  (long) (rotate_image->columns-shear1_width)/2,
1854
366
                  (long) (rotate_image->rows-shear2_height)/2,exception)
1855
366
     != MagickPass)
1856
0
    goto rotate_image_exception;
1857
1858
366
  if (CropToFitImage(&rotate_image,shear.x,shear.y,width,height,True,exception)
1859
366
      != MagickPass)
1860
0
    goto rotate_image_exception;
1861
1862
366
  rotate_image->page.width=0;
1863
366
  rotate_image->page.height=0;
1864
366
  return(rotate_image);
1865
1866
5
 rotate_image_exception:
1867
1868
5
  if (rotate_image != (Image *) NULL)
1869
4
    DestroyImage(rotate_image);
1870
5
  return (Image *) NULL;
1871
366
}
1872

1873
/*
1874
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1875
%                                                                             %
1876
%                                                                             %
1877
%                                                                             %
1878
%   S h e a r I m a g e                                                       %
1879
%                                                                             %
1880
%                                                                             %
1881
%                                                                             %
1882
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1883
%
1884
%  Method ShearImage creates a new image that is a shear_image copy of an
1885
%  existing one.  Shearing slides one edge of an image along the X or Y
1886
%  axis, creating a parallelogram.  An X direction shear slides an edge
1887
%  along the X axis, while a Y direction shear slides an edge along the Y
1888
%  axis.  The amount of the shear is controlled by a shear angle.  For X
1889
%  direction shears, x_shear is measured relative to the Y axis, and
1890
%  similarly, for Y direction shears y_shear is measured relative to the
1891
%  X axis.  Empty triangles left over from shearing the image are filled
1892
%  with the color defined by the pixel at location (0,0).  ShearImage
1893
%  allocates the memory necessary for the new Image structure and returns
1894
%  a pointer to the new image.
1895
%
1896
%  Method ShearImage is based on the paper "A Fast Algorithm for General
1897
%  Raster Rotatation" by Alan W. Paeth.
1898
%
1899
%  The format of the ShearImage method is:
1900
%
1901
%      Image *ShearImage(const Image *image,const double x_shear,
1902
%        const double y_shear,ExceptionInfo *exception)
1903
%
1904
%  A description of each parameter follows.
1905
%
1906
%    o status: Method ShearImage returns a pointer to the image after
1907
%      rotating.  A null image is returned if there is a memory shortage.
1908
%
1909
%    o image: The image;  returned from
1910
%      ReadImage.
1911
%
1912
%    o x_shear, y_shear: Specifies the number of degrees to shear the image.
1913
%
1914
%    o exception: Return any errors or warnings in this structure.
1915
%
1916
%
1917
*/
1918
MagickExport Image *
1919
ShearImage(const Image *image,const double x_shear,
1920
           const double y_shear,ExceptionInfo *exception)
1921
0
{
1922
0
  Image
1923
0
    *integral_image = (Image *) NULL,
1924
0
    *shear_image = (Image *) NULL;
1925
1926
0
  long
1927
0
    x_offset,
1928
0
    y_offset;
1929
1930
0
  PointInfo
1931
0
    shear;
1932
1933
0
  RectangleInfo
1934
0
    border_info;
1935
1936
0
  unsigned long
1937
0
    y_width;
1938
1939
0
  assert(image != (Image *) NULL);
1940
0
  assert(image->signature == MagickSignature);
1941
0
  assert(exception != (ExceptionInfo *) NULL);
1942
0
  assert(exception->signature == MagickSignature);
1943
0
  if ((x_shear == 180.0) || (y_shear == 180.0))
1944
0
    ThrowImageException3(ImageError,UnableToShearImage,AngleIsDiscontinuous);
1945
1946
  /*
1947
    Initialize shear angle.
1948
  */
1949
0
  integral_image=IntegralRotateImage(image,0,exception);
1950
0
  if (integral_image == (Image *) NULL)
1951
0
    goto shear_image_exception;
1952
0
  shear.x=(-tan(DegreesToRadians(x_shear)/2.0));
1953
0
  shear.y=sin(DegreesToRadians(y_shear));
1954
0
  (void) LogMagickEvent(TransformEvent,GetMagickModule(),
1955
0
                        "Shear angles x,y: %g,%g degrees", shear.x, shear.y);
1956
0
  if ((shear.x == 0.0) && (shear.y == 0.0))
1957
0
    return(integral_image);
1958
1959
  /*
1960
    Compute image size.
1961
  */
1962
0
  x_offset=(long) ceil(fabs(2.0*image->rows*shear.x)-0.5);
1963
0
  y_width=(unsigned long) floor(fabs(image->rows*shear.x)+image->columns+0.5);
1964
0
  y_offset=(long) ceil(fabs(y_width*shear.y)-0.5);
1965
  /*
1966
    Surround image with border.
1967
  */
1968
0
  integral_image->border_color=integral_image->background_color;
1969
0
  border_info.width=x_offset;
1970
0
  border_info.height=y_offset;
1971
0
  shear_image=BorderImage(integral_image,&border_info,exception);
1972
0
  DestroyImage(integral_image);
1973
0
  integral_image=(Image *) NULL;
1974
0
  if (shear_image == (Image *) NULL)
1975
0
    goto shear_image_exception;
1976
  /*
1977
    Shear the image.
1978
  */
1979
0
  shear_image->storage_class=DirectClass;
1980
0
  shear_image->matte|=shear_image->background_color.opacity != OpaqueOpacity;
1981
1982
0
  if (XShearImage(shear_image,shear.x,image->columns,image->rows,x_offset,
1983
0
                  (long) (shear_image->rows-image->rows)/2,exception)
1984
0
      != MagickPass)
1985
0
    goto shear_image_exception;
1986
1987
0
  if (YShearImage(shear_image,shear.y,y_width,image->rows,
1988
0
                  (long) (shear_image->columns-y_width)/2,y_offset,exception)
1989
0
      != MagickPass)
1990
0
    goto shear_image_exception;
1991
1992
0
  if (CropToFitImage(&shear_image,shear.x,shear.y,image->columns,image->rows,
1993
0
                     False,exception) != MagickPass)
1994
0
    goto shear_image_exception;
1995
1996
0
  shear_image->page.width=0;
1997
0
  shear_image->page.height=0;
1998
1999
0
  return(shear_image);
2000
2001
0
 shear_image_exception:
2002
2003
0
  DestroyImage(integral_image);
2004
0
  DestroyImage(shear_image);
2005
  return (Image *) NULL;
2006
0
}