Coverage Report

Created: 2026-09-06 07:31

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
343
{
326
343
  Image
327
343
    *crop_image;
328
329
343
  PointInfo
330
343
    extent[4],
331
343
    min,
332
343
    max;
333
334
343
  RectangleInfo
335
343
    geometry;
336
337
343
  register long
338
343
    i;
339
340
  /*
341
    Calculate the rotated image size.
342
  */
343
343
  extent[0].x=(-width/2.0);
344
343
  extent[0].y=(-height/2.0);
345
343
  extent[1].x=width/2.0;
346
343
  extent[1].y=(-height/2.0);
347
343
  extent[2].x=(-width/2.0);
348
343
  extent[2].y=height/2.0;
349
343
  extent[3].x=width/2.0;
350
343
  extent[3].y=height/2.0;
351
1.71k
  for (i=0; i < 4; i++)
352
1.37k
  {
353
1.37k
    extent[i].x+=x_shear*extent[i].y;
354
1.37k
    extent[i].y+=y_shear*extent[i].x;
355
1.37k
    if (rotate)
356
1.37k
      extent[i].x+=x_shear*extent[i].y;
357
1.37k
    extent[i].x+=(double) (*image)->columns/2.0;
358
1.37k
    extent[i].y+=(double) (*image)->rows/2.0;
359
1.37k
  }
360
343
  min=extent[0];
361
343
  max=extent[0];
362
1.37k
  for (i=1; i < 4; i++)
363
1.02k
  {
364
1.02k
    if (min.x > extent[i].x)
365
156
      min.x=extent[i].x;
366
1.02k
    if (min.y > extent[i].y)
367
187
      min.y=extent[i].y;
368
1.02k
    if (max.x < extent[i].x)
369
617
      max.x=extent[i].x;
370
1.02k
    if (max.y < extent[i].y)
371
599
      max.y=extent[i].y;
372
1.02k
  }
373
343
  geometry.width=(unsigned long) floor(max.x-min.x+0.5);
374
343
  geometry.height=(unsigned long) floor(max.y-min.y+0.5);
375
343
  geometry.x=(long) ceil(min.x-0.5);
376
343
  geometry.y=(long) ceil(min.y-0.5);
377
343
  crop_image=CropImage(*image,&geometry,exception);
378
343
  if (crop_image != (Image *) NULL)
379
343
    crop_image->page=(*image)->page;
380
343
  DestroyImage(*image);
381
343
  *image=crop_image;
382
383
343
  return (*image != (Image *) NULL ? MagickPass : MagickFail);
384
343
}
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.27k
{
429
1.27k
  char
430
1.27k
    message[MaxTextExtent];
431
432
1.27k
  Image
433
1.27k
    *rotate_image;
434
435
1.27k
  RectangleInfo
436
1.27k
    page;
437
438
1.27k
  long
439
1.27k
    tile_width_max,
440
1.27k
    tile_height_max;
441
442
1.27k
  MagickPassFail
443
1.27k
    status=MagickPass;
444
445
  /*
446
    Initialize rotated image attributes.
447
  */
448
1.27k
  assert(image != (Image *) NULL);
449
1.27k
  page=image->page;
450
1.27k
  rotations%=4;
451
452
1.27k
  {
453
    /*
454
      Clone appropriately to create rotate image.
455
    */
456
1.27k
    unsigned long
457
1.27k
      clone_columns=0,
458
1.27k
      clone_rows=0;
459
460
1.27k
    switch (rotations)
461
1.27k
      {
462
59
      case 0:
463
59
        clone_columns=0;
464
59
        clone_rows=0;
465
59
        break;
466
99
      case 2:
467
99
        clone_columns=image->columns;
468
99
        clone_rows=image->rows;
469
99
        break;
470
996
      case 1:
471
1.11k
      case 3:
472
1.11k
        clone_columns=image->rows;
473
1.11k
        clone_rows=image->columns;
474
1.11k
        break;
475
1.27k
      }
476
1.27k
    rotate_image=CloneImage(image,clone_columns,clone_rows,True,exception);
477
1.27k
    if (rotate_image == (Image *) NULL)
478
0
      return((Image *) NULL);
479
1.27k
    if (rotations != 0)
480
1.21k
      if (ModifyCache(rotate_image,exception) != MagickPass)
481
1
        {
482
1
          DestroyImage(rotate_image);
483
1
          return (Image *) NULL;
484
1
        }
485
1.27k
  }
486
487
1.27k
  tile_height_max=tile_width_max=2048/sizeof(PixelPacket); /* 2k x 2k = 4MB */
488
1.27k
  if ((rotations == 1) || (rotations == 3))
489
1.11k
    {
490
      /*
491
        Allow override of tile geometry for testing.
492
      */
493
1.11k
      const char *
494
1.11k
        value;
495
496
1.11k
      if (!GetPixelCacheInCore(image) || !GetPixelCacheInCore(rotate_image))
497
0
        tile_height_max=tile_width_max=8192/sizeof(PixelPacket); /* 8k x 8k = 64MB */
498
499
1.11k
      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.11k
    }
512
513
  /*
514
    Integral rotate the image.
515
  */
516
1.27k
  switch (rotations)
517
1.27k
    {
518
59
    case 0:
519
59
      {
520
        /*
521
          Rotate 0 degrees (nothing more to do).
522
        */
523
59
        (void) strlcpy(message,"[%s] Rotate: 0 degrees...",sizeof(message));
524
59
        if (!MagickMonitorFormatted(image->rows-1,image->rows,exception,
525
59
                                    message,image->filename))
526
0
          status=MagickFail;
527
59
        break;
528
0
      }
529
995
    case 1:
530
995
      {
531
        /*
532
          Rotate 90 degrees.
533
        */
534
995
        magick_int64_t
535
995
          tile;
536
537
995
        magick_uint64_t
538
995
          total_tiles;
539
540
995
        long
541
995
          tile_y;
542
543
995
        MagickBool
544
995
          monitor_active;
545
546
#if defined(IntegralRotateImageUseOpenMP)
547
#  if defined(HAVE_OPENMP)
548
        int
549
          rotate_threads = RotateThreads;
550
#  endif
551
#endif
552
553
995
        (void) strlcpy(message,"[%s] Rotate: 90 degrees...",sizeof(message));
554
995
        total_tiles=((((size_t) image->rows/tile_height_max)+1)*
555
995
                     (((size_t) image->columns/tile_width_max)+1));
556
995
        tile=0;
557
558
995
        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.13k
        for (tile_y=0; tile_y < (long) image->rows; tile_y+=tile_height_max)
570
1.14k
          {
571
1.14k
            long
572
1.14k
              tile_x;
573
574
1.14k
            MagickPassFail
575
1.14k
              thread_status;
576
577
1.14k
            thread_status=status;
578
1.14k
            if (thread_status == MagickFail)
579
0
              continue;
580
581
2.54k
            for (tile_x=0; tile_x < (long) image->columns; tile_x+=tile_width_max)
582
1.40k
              {
583
1.40k
                long
584
1.40k
                  dest_tile_x,
585
1.40k
                  dest_tile_y;
586
587
1.40k
                long
588
1.40k
                  tile_width,
589
1.40k
                  tile_height;
590
591
1.40k
                const PixelPacket
592
1.40k
                  *tile_pixels=(const PixelPacket *) NULL;
593
594
1.40k
                long
595
1.40k
                  y;
596
597
                /*
598
                  Compute image region corresponding to tile.
599
                */
600
1.40k
                if ((unsigned long) tile_x+tile_width_max > image->columns)
601
1.13k
                  tile_width=(tile_width_max-(tile_x+tile_width_max-image->columns));
602
273
                else
603
273
                  tile_width=tile_width_max;
604
1.40k
                if ((unsigned long) tile_y+tile_height_max > image->rows)
605
1.21k
                  tile_height=(tile_height_max-(tile_y+tile_height_max-image->rows));
606
193
                else
607
193
                  tile_height=tile_height_max;
608
                /*
609
                  Acquire tile
610
                */
611
1.40k
                tile_pixels=AcquireImagePixels(image,tile_x,tile_y,
612
1.40k
                                               tile_width,tile_height,exception);
613
1.40k
                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.40k
                dest_tile_x=rotate_image->columns-(tile_y+tile_height);
622
1.40k
                dest_tile_y=tile_x;
623
                /*
624
                  Rotate tile
625
                */
626
124k
                for (y=0; y < tile_width; y++)
627
122k
                  {
628
122k
                    register const PixelPacket
629
122k
                      *p;
630
631
122k
                    register PixelPacket
632
122k
                      *q;
633
634
122k
                    register const IndexPacket
635
122k
                      *indexes;
636
637
122k
                    IndexPacket
638
122k
                      *rotate_indexes;
639
640
122k
                    register long
641
122k
                      x;
642
643
122k
                    q=SetImagePixelsEx(rotate_image,dest_tile_x,dest_tile_y+y,
644
122k
                                       tile_height,1,exception);
645
122k
                    if (q == (PixelPacket *) NULL)
646
0
                      {
647
0
                        thread_status=MagickFail;
648
0
                        break;
649
0
                      }
650
                    /*
651
                      DirectClass pixels
652
                    */
653
122k
                    p=tile_pixels+((size_t) tile_height-1)*tile_width + y;
654
7.98M
                    for (x=tile_height; x != 0; x--)
655
7.85M
                      {
656
7.85M
                        *q = *p;
657
7.85M
                        q++;
658
7.85M
                        p-=tile_width;
659
7.85M
                      }
660
                    /*
661
                      Indexes
662
                    */
663
122k
                    indexes=AccessImmutableIndexes(image);
664
122k
                    if (indexes != (IndexPacket *) NULL)
665
23.5k
                      {
666
23.5k
                        rotate_indexes=AccessMutableIndexes(rotate_image);
667
23.5k
                        if (rotate_indexes != (IndexPacket *) NULL)
668
23.5k
                          {
669
23.5k
                            register IndexPacket
670
23.5k
                              *iq;
671
672
23.5k
                            register const IndexPacket
673
23.5k
                              *ip;
674
675
23.5k
                            iq=rotate_indexes;
676
23.5k
                            ip=indexes+((size_t) tile_height-1)*tile_width + y;
677
121k
                            for (x=tile_height; x != 0; x--)
678
98.0k
                              {
679
98.0k
                                *iq = *ip;
680
98.0k
                                iq++;
681
98.0k
                                ip -= tile_width;
682
98.0k
                              }
683
23.5k
                          }
684
23.5k
                      }
685
122k
                    if (!SyncImagePixelsEx(rotate_image,exception))
686
0
                      {
687
0
                        thread_status=MagickFail;
688
0
                        break;
689
0
                      }
690
122k
                  }
691
692
1.40k
                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.40k
                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.40k
              }
723
1.14k
            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.14k
          }
733
995
        Swap(page.width,page.height);
734
995
        Swap(page.x,page.y);
735
995
        page.x=(long) (page.width-rotate_image->columns-page.x);
736
995
        break;
737
0
      }
738
99
    case 2:
739
99
      {
740
        /*
741
          Rotate 180 degrees.
742
        */
743
99
        long
744
99
          y;
745
746
99
        unsigned long
747
99
          row_count=0;
748
749
99
        MagickBool
750
99
          monitor_active;
751
752
#if defined(IntegralRotateImageUseOpenMP)
753
#  if defined(HAVE_OPENMP)
754
        int
755
          rotate_threads = RotateThreads;
756
#  endif
757
#endif
758
759
99
        (void) strlcpy(message,"[%s] Rotate: 180 degrees...",sizeof(message));
760
761
99
        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.01k
        for (y=0; y < (long) image->rows; y++)
773
3.91k
          {
774
3.91k
            register const PixelPacket
775
3.91k
              *p;
776
777
3.91k
            register PixelPacket
778
3.91k
              *q;
779
780
3.91k
            register const IndexPacket
781
3.91k
              *indexes;
782
783
3.91k
            IndexPacket
784
3.91k
              *rotate_indexes;
785
786
3.91k
            register long
787
3.91k
              x;
788
789
3.91k
            MagickPassFail
790
3.91k
              thread_status;
791
792
3.91k
            thread_status=status;
793
3.91k
            if (thread_status == MagickFail)
794
0
              continue;
795
796
3.91k
            p=AcquireImagePixels(image,0,y,image->columns,1,exception);
797
3.91k
            q=SetImagePixelsEx(rotate_image,0,(long) (image->rows-y-1),
798
3.91k
                               image->columns,1,exception);
799
3.91k
            if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
800
0
              thread_status=MagickFail;
801
3.91k
            if (thread_status != MagickFail)
802
3.91k
              {
803
3.91k
                q+=image->columns;
804
3.91k
                indexes=AccessImmutableIndexes(image);
805
3.91k
                rotate_indexes=AccessMutableIndexes(rotate_image);
806
3.91k
                if ((indexes != (IndexPacket *) NULL) &&
807
3.07k
                    (rotate_indexes != (IndexPacket *) NULL))
808
548k
                  for (x=0; x < (long) image->columns; x++)
809
544k
                    rotate_indexes[image->columns-x-1]=indexes[x];
810
580k
                for (x=0; x < (long) image->columns; x++)
811
576k
                  *--q=(*p++);
812
3.91k
                if (!SyncImagePixelsEx(rotate_image,exception))
813
0
                  thread_status=MagickFail;
814
3.91k
              }
815
816
3.91k
            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
3.91k
            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
3.91k
          }
847
99
        page.x=(long) (page.width-rotate_image->columns-page.x);
848
99
        page.y=(long) (page.height-rotate_image->rows-page.y);
849
99
        break;
850
0
      }
851
118
    case 3:
852
118
      {
853
        /*
854
          Rotate 270 degrees.
855
        */
856
857
118
        magick_int64_t
858
118
          tile;
859
860
118
        magick_uint64_t
861
118
          total_tiles;
862
863
118
        long
864
118
          tile_y;
865
866
118
        MagickBool
867
118
          monitor_active;
868
869
#if defined(IntegralRotateImageUseOpenMP)
870
#  if defined(HAVE_OPENMP)
871
        int
872
          rotate_threads = RotateThreads;
873
#  endif
874
#endif
875
876
118
        (void) strlcpy(message,"[%s] Rotate: 270 degrees...",sizeof(message));
877
118
        total_tiles=((((size_t) image->rows/tile_height_max)+1)*
878
118
                     (((size_t) image->columns/tile_width_max)+1));
879
118
        tile=0;
880
881
118
        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
243
        for (tile_y=0; tile_y < (long) image->rows; tile_y+=tile_height_max)
893
125
          {
894
125
            long
895
125
              tile_x;
896
897
125
            MagickPassFail
898
125
              thread_status;
899
900
125
            thread_status=status;
901
125
            if (thread_status == MagickFail)
902
0
              continue;
903
904
312
            for (tile_x=0; tile_x < (long) image->columns; tile_x+=tile_width_max)
905
187
              {
906
187
                long
907
187
                  tile_width,
908
187
                  tile_height;
909
910
187
                long
911
187
                  dest_tile_x=0,
912
187
                  dest_tile_y=0;
913
914
187
                long
915
187
                  y=0;
916
917
187
                const PixelPacket
918
187
                  *tile_pixels = (const PixelPacket *) NULL;
919
920
                /*
921
                  Compute image region corresponding to tile.
922
                */
923
187
                if ((unsigned long) tile_x+tile_width_max > image->columns)
924
125
                  tile_width=(tile_width_max-(tile_x+tile_width_max-image->columns));
925
62
                else
926
62
                  tile_width=tile_width_max;
927
187
                if ((unsigned long) tile_y+tile_height_max > image->rows)
928
178
                  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
187
                tile_pixels=AcquireImagePixels(image,tile_x,tile_y,
935
187
                                               tile_width,tile_height,exception);
936
187
                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
187
                dest_tile_x=tile_y;
945
187
                dest_tile_y=rotate_image->rows-(tile_x+tile_width);
946
                /*
947
                  Rotate tile
948
                */
949
29.9k
                for (y=0; y < tile_width; y++)
950
29.7k
                  {
951
29.7k
                    register const PixelPacket
952
29.7k
                      *p;
953
954
29.7k
                    register PixelPacket
955
29.7k
                      *q;
956
957
29.7k
                    register const IndexPacket
958
29.7k
                      *indexes;
959
960
29.7k
                    register long
961
29.7k
                      x;
962
963
29.7k
                    IndexPacket
964
29.7k
                      *rotate_indexes;
965
966
29.7k
                    q=SetImagePixelsEx(rotate_image,dest_tile_x,dest_tile_y+y,
967
29.7k
                                       tile_height,1,exception);
968
29.7k
                    if (q == (PixelPacket *) NULL)
969
0
                      {
970
0
                        thread_status=MagickFail;
971
0
                        break;
972
0
                      }
973
                    /*
974
                      DirectClass pixels
975
                    */
976
29.7k
                    p=tile_pixels+((size_t) tile_width-1-y);
977
1.41M
                    for (x=tile_height; x != 0; x--)
978
1.38M
                      {
979
1.38M
                        *q = *p;
980
1.38M
                        q++;
981
1.38M
                        p += tile_width;
982
1.38M
                      }
983
                    /*
984
                      Indexes
985
                    */
986
29.7k
                    indexes=AccessImmutableIndexes(image);
987
29.7k
                    if (indexes != (IndexPacket *) NULL)
988
10.2k
                      {
989
10.2k
                        rotate_indexes=AccessMutableIndexes(rotate_image);
990
10.2k
                        if (rotate_indexes != (IndexPacket *) NULL)
991
10.2k
                          {
992
10.2k
                            register IndexPacket
993
10.2k
                              *iq;
994
995
10.2k
                            register const IndexPacket
996
10.2k
                              *ip;
997
998
10.2k
                            iq=rotate_indexes;
999
10.2k
                            ip=indexes+((size_t) tile_width-1-y);
1000
237k
                            for (x=tile_height; x != 0; x--)
1001
227k
                              {
1002
227k
                                *iq = *ip;
1003
227k
                                iq++;
1004
227k
                                ip += tile_width;
1005
227k
                              }
1006
10.2k
                          }
1007
10.2k
                      }
1008
29.7k
                    if (!SyncImagePixelsEx(rotate_image,exception))
1009
0
                      {
1010
0
                        thread_status=MagickFail;
1011
0
                        break;
1012
0
                      }
1013
29.7k
                  }
1014
1015
187
                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
187
                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
187
                if (thread_status == MagickFail)
1047
0
                  break;
1048
187
              }
1049
1050
125
            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
125
          }
1060
118
        Swap(page.width,page.height);
1061
118
        Swap(page.x,page.y);
1062
118
        page.y=(long) (page.height-rotate_image->rows-page.y);
1063
118
        break;
1064
0
      }
1065
1.27k
    }
1066
1067
1.27k
  if (status == MagickFail)
1068
0
    {
1069
0
      DestroyImage(rotate_image);
1070
0
      rotate_image = (Image *) NULL;
1071
0
    }
1072
1.27k
  else
1073
1.27k
    {
1074
1.27k
      rotate_image->page=page;
1075
1.27k
      rotate_image->is_grayscale=image->is_grayscale;
1076
1.27k
      rotate_image->is_monochrome=image->is_monochrome;
1077
1.27k
    }
1078
1.27k
  return(rotate_image);
1079
1.27k
}
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
694
{
1122
694
#define XShearImageText  "[%s] X Shear: %+g degrees, region %lux%lu%+ld%+ld...  "
1123
1124
694
  long
1125
694
    y,
1126
694
    xr_offset;
1127
1128
694
  unsigned long
1129
694
    row_count=0;
1130
1131
694
  MagickBool
1132
694
    monitor_active;
1133
1134
694
  unsigned int
1135
694
    is_grayscale;
1136
1137
694
  MagickPassFail
1138
694
    status=MagickPass;
1139
1140
694
  assert(image != (Image *) NULL);
1141
694
  is_grayscale=image->is_grayscale;
1142
1143
694
  assert(x_offset >= 0);
1144
694
  assert(x_offset < (long) image->columns);
1145
694
  assert(y_offset >= 0);
1146
694
  assert(y_offset < (long) image->rows);
1147
694
  assert(width <= (image->columns-(unsigned long) x_offset));
1148
694
  assert(height <= (image->rows-(unsigned long) y_offset));
1149
694
  xr_offset=image->columns-width-x_offset;
1150
1151
694
  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
107k
  for (y=0; y < (long) height; y++)
1165
106k
    {
1166
106k
      double
1167
106k
        alpha,
1168
106k
        displacement;
1169
1170
106k
      long
1171
106k
        step,
1172
106k
        skip;
1173
1174
106k
      PixelPacket
1175
106k
        pixel;
1176
1177
106k
      register long
1178
106k
        i;
1179
1180
106k
      register PixelPacket
1181
106k
        *p,
1182
106k
        *q;
1183
1184
106k
      enum
1185
106k
      {
1186
106k
        LEFT,
1187
106k
        RIGHT
1188
106k
      } direction;
1189
1190
106k
      MagickPassFail
1191
106k
        thread_status;
1192
1193
106k
      thread_status=status;
1194
106k
      if (thread_status == MagickFail)
1195
3.84k
        continue;
1196
1197
102k
      displacement=degrees*(y-height/2.0);
1198
102k
      if (displacement == 0.0)
1199
367
        continue;
1200
102k
      if (displacement > 0.0)
1201
51.1k
        direction=RIGHT;
1202
51.1k
      else
1203
51.1k
        {
1204
51.1k
          displacement*=(-1.0);
1205
51.1k
          direction=LEFT;
1206
51.1k
        }
1207
102k
      step=(long) floor(displacement);
1208
102k
      alpha=MaxRGBDouble*(displacement-step);
1209
102k
      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
102k
      step++;
1295
102k
      pixel=image->background_color;
1296
102k
      switch (direction)
1297
102k
        {
1298
51.1k
        case LEFT:
1299
51.1k
          {
1300
            /*
1301
              Transfer pixels left-to-right.
1302
            */
1303
51.1k
            p=GetImagePixelsEx(image,0,y+y_offset,image->columns,1,exception);
1304
51.1k
            if (p == (PixelPacket *) NULL)
1305
4
              {
1306
4
                thread_status=MagickFail;
1307
4
                break;
1308
4
              }
1309
51.1k
            p+=x_offset;
1310
51.1k
            q=p-step;
1311
7.23M
            for (i=0; i < (long) width; i++)
1312
7.18M
              {
1313
7.18M
                if ((x_offset+i) < step)
1314
0
                  {
1315
0
                    pixel=(*++p);
1316
0
                    q++;
1317
0
                    continue;
1318
0
                  }
1319
7.18M
                BlendCompositePixel(q,&pixel,p,alpha);
1320
7.18M
                q++;
1321
7.18M
                pixel=(*p++);
1322
7.18M
              }
1323
51.1k
            BlendCompositePixel(q,&pixel,&image->background_color,alpha);
1324
51.1k
            q++;
1325
1.61M
            for (i=0; i < (step-1); i++)
1326
1.56M
              *q++=image->background_color;
1327
51.1k
            break;
1328
51.1k
          }
1329
51.1k
        case RIGHT:
1330
51.1k
          {
1331
            /*
1332
              Transfer pixels right-to-left.
1333
            */
1334
51.1k
            p=GetImagePixelsEx(image,0,y+y_offset,image->columns,1,exception);
1335
51.1k
            if (p == (PixelPacket *) NULL)
1336
4
              {
1337
4
                thread_status=MagickFail;
1338
4
                break;
1339
4
              }
1340
51.1k
            p+= (ptrdiff_t)x_offset+width;
1341
51.1k
            q=p+step;
1342
7.25M
            for (i=0; i < (long) width; i++)
1343
7.20M
              {
1344
7.20M
                p--;
1345
7.20M
                q--;
1346
7.20M
                if ((x_offset+width+step-i) > image->columns)
1347
0
                  continue;
1348
7.20M
                BlendCompositePixel(q,&pixel,p,alpha);
1349
7.20M
                pixel=(*p);
1350
7.20M
              }
1351
51.1k
            --q;
1352
51.1k
            BlendCompositePixel(q,&pixel,&image->background_color,alpha);
1353
1.61M
            for (i=0; i < (step-1); i++)
1354
1.56M
              *--q=image->background_color;
1355
51.1k
            break;
1356
51.1k
          }
1357
102k
        }
1358
102k
      if (!SyncImagePixelsEx(image,exception))
1359
0
        thread_status=MagickFail;
1360
1361
102k
      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
102k
      if (thread_status == MagickFail)
1383
8
        {
1384
8
          status=MagickFail;
1385
#if defined(HAVE_OPENMP)
1386
#  pragma omp flush (status)
1387
#endif
1388
8
        }
1389
102k
    }
1390
694
  if (is_grayscale && IsGray(image->background_color))
1391
0
    image->is_grayscale=True;
1392
1393
694
  return status;
1394
694
}
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
343
{
1437
343
#define YShearImageText  "[%s] Y Shear: %+g degrees, region %lux%lu%+ld%+ld...  "
1438
1439
343
  long
1440
343
    x,
1441
343
    yr_offset;
1442
1443
343
  unsigned long
1444
343
    row_count=0;
1445
1446
343
  MagickBool
1447
343
    monitor_active;
1448
1449
343
  unsigned int
1450
343
    is_grayscale;
1451
1452
343
  MagickPassFail
1453
343
    status=MagickPass;
1454
1455
343
  assert(image != (Image *) NULL);
1456
343
  is_grayscale=image->is_grayscale;
1457
1458
343
  assert(x_offset >= 0);
1459
343
  assert(x_offset < (long) image->columns);
1460
343
  assert(y_offset >= 0);
1461
343
  assert(y_offset < (long) image->rows);
1462
343
  assert(width <= (image->columns-(unsigned long) x_offset));
1463
343
  assert(height <= (image->rows-(unsigned long) y_offset));
1464
343
  yr_offset=image->rows-height-y_offset;
1465
1466
343
  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
44.7k
  for (x=0; x < (long) width; x++)
1480
44.3k
    {
1481
44.3k
      double
1482
44.3k
        alpha,
1483
44.3k
        displacement;
1484
1485
44.3k
      enum
1486
44.3k
      {
1487
44.3k
        UP,
1488
44.3k
        DOWN
1489
44.3k
      } direction;
1490
1491
44.3k
      long
1492
44.3k
        step,
1493
44.3k
        skip;
1494
1495
44.3k
      register PixelPacket
1496
44.3k
        *p,
1497
44.3k
        *q;
1498
1499
44.3k
      register long
1500
44.3k
        i;
1501
1502
44.3k
      PixelPacket
1503
44.3k
        pixel;
1504
1505
44.3k
      MagickPassFail
1506
44.3k
        thread_status;
1507
1508
44.3k
      thread_status=status;
1509
44.3k
      if (thread_status == MagickFail)
1510
0
        continue;
1511
1512
44.3k
      displacement=degrees*(x-width/2.0);
1513
44.3k
      if (displacement == 0.0)
1514
194
        continue;
1515
44.2k
      if (displacement > 0.0)
1516
22.1k
        direction=DOWN;
1517
22.0k
      else
1518
22.0k
        {
1519
22.0k
          displacement*=(-1.0);
1520
22.0k
          direction=UP;
1521
22.0k
        }
1522
44.2k
      step=(long) floor(displacement);
1523
44.2k
      alpha=(double) MaxRGB*(displacement-step);
1524
44.2k
      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
44.2k
      step++;
1610
44.2k
      pixel=image->background_color;
1611
44.2k
      switch (direction)
1612
44.2k
        {
1613
22.0k
        case UP:
1614
22.0k
          {
1615
            /*
1616
              Transfer pixels top-to-bottom.
1617
            */
1618
22.0k
            p=GetImagePixelsEx(image,x+x_offset,0,1,image->rows,exception);
1619
22.0k
            if (p == (PixelPacket *) NULL)
1620
0
              {
1621
0
                thread_status=MagickFail;
1622
0
                break;
1623
0
              }
1624
22.0k
            p+=y_offset;
1625
22.0k
            q=p-step;
1626
3.27M
            for (i=0; i < (long) height; i++)
1627
3.25M
              {
1628
3.25M
                if ((y_offset+i) < step)
1629
0
                  {
1630
0
                    pixel=(*++p);
1631
0
                    q++;
1632
0
                    continue;
1633
0
                  }
1634
3.25M
                BlendCompositePixel(q,&pixel,p,alpha);
1635
3.25M
                q++;
1636
3.25M
                pixel=(*p++);
1637
3.25M
              }
1638
22.0k
            BlendCompositePixel(q,&pixel,&image->background_color,alpha);
1639
22.0k
            q++;
1640
827k
            for (i=0; i < (step-1); i++)
1641
805k
              *q++=image->background_color;
1642
22.0k
            break;
1643
22.0k
          }
1644
22.1k
        case DOWN:
1645
22.1k
          {
1646
            /*
1647
              Transfer pixels bottom-to-top.
1648
            */
1649
22.1k
            p=GetImagePixelsEx(image,x+x_offset,0,1,image->rows,exception);
1650
22.1k
            if (p == (PixelPacket *) NULL)
1651
0
              {
1652
0
                thread_status=MagickFail;
1653
0
                break;
1654
0
              }
1655
22.1k
            p+= (ptrdiff_t)y_offset+height;
1656
22.1k
            q=p+step;
1657
3.28M
            for (i=0; i < (long) height; i++)
1658
3.25M
              {
1659
3.25M
                p--;
1660
3.25M
                q--;
1661
3.25M
                if ((y_offset+height+step-i) > image->rows)
1662
0
                  continue;
1663
3.25M
                BlendCompositePixel(q,&pixel,p,alpha);
1664
3.25M
                pixel=(*p);
1665
3.25M
              }
1666
22.1k
            --q;
1667
22.1k
            BlendCompositePixel(q,&pixel,&image->background_color,alpha);
1668
825k
            for (i=0; i < (step-1); i++)
1669
803k
              *--q=image->background_color;
1670
22.1k
            break;
1671
22.1k
          }
1672
44.2k
        }
1673
44.2k
      if (!SyncImagePixelsEx(image,exception))
1674
0
        thread_status=MagickFail;
1675
1676
44.2k
      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
44.2k
      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
44.2k
    }
1705
343
  if (is_grayscale && IsGray(image->background_color))
1706
0
    image->is_grayscale=True;
1707
1708
343
  return status;
1709
343
}
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.27k
{
1758
1.27k
  double
1759
1.27k
    angle;
1760
1761
1.27k
  Image
1762
1.27k
    *integral_image = (Image *) NULL,
1763
1.27k
    *rotate_image = (Image *) NULL;
1764
1765
1.27k
  long
1766
1.27k
    x_offset,
1767
1.27k
    y_offset;
1768
1769
1.27k
  PointInfo
1770
1.27k
    shear;
1771
1772
1.27k
  RectangleInfo
1773
1.27k
    border_info;
1774
1775
1.27k
  unsigned long
1776
1.27k
    height,
1777
1.27k
    rotations,
1778
1.27k
    width,
1779
1.27k
    shear1_width,
1780
1.27k
    shear2_height,
1781
1.27k
    shear3_width,
1782
1.27k
    max_width,
1783
1.27k
    max_height;
1784
1785
  /*
1786
    Adjust rotation angle.
1787
  */
1788
1.27k
  assert(image != (Image *) NULL);
1789
1.27k
  assert(image->signature == MagickSignature);
1790
1.27k
  assert(exception != (ExceptionInfo *) NULL);
1791
1.27k
  assert(exception->signature == MagickSignature);
1792
1.27k
  angle = degrees - 360.0*(int)(degrees / 360);
1793
1.27k
  if(angle < -45.0) angle+=360.0;
1794
1795
2.88k
  for (rotations=0; angle > 45.0; rotations++)
1796
1.60k
    angle-=90.0;
1797
1.27k
  rotations%=4;
1798
  /*
1799
    Calculate shear equations.
1800
  */
1801
1.27k
  integral_image=IntegralRotateImage(image,rotations,exception);
1802
1.27k
  if (integral_image == (Image *) NULL)
1803
1
    goto rotate_image_exception;
1804
1805
1.27k
  shear.x=(-tan(DegreesToRadians(angle)/2.0));
1806
1.27k
  shear.y=sin(DegreesToRadians(angle));
1807
1.27k
  if ((shear.x == 0.0) || (shear.y == 0.0))
1808
920
    return(integral_image);
1809
  /*
1810
    Compute image size.
1811
  */
1812
351
  width=integral_image->columns;
1813
351
  height=integral_image->rows;
1814
351
  shear1_width=(unsigned long) floor(fabs(height*shear.x)+width+0.5);
1815
351
  shear2_height=(unsigned long) floor(fabs(shear1_width*shear.y)+height+0.5);
1816
351
  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
351
  max_width = ((shear3_width > shear1_width) ? shear3_width : shear1_width) + 2;
1822
351
  max_height = shear2_height + 2;
1823
351
  x_offset = (long) floor((max_width-width)/2.0+0.5);
1824
351
  y_offset = (long) floor((max_height-height)/2.0+0.5);
1825
  /*
1826
    Surround image with a border.
1827
  */
1828
351
  integral_image->border_color=integral_image->background_color;
1829
351
  border_info.width=x_offset;
1830
351
  border_info.height=y_offset;
1831
351
  rotate_image=BorderImage(integral_image,&border_info,exception);
1832
351
  DestroyImage(integral_image);
1833
351
  integral_image=(Image *) NULL;
1834
351
  if (rotate_image == (Image *) NULL)
1835
0
    goto rotate_image_exception;
1836
1837
  /*
1838
    Rotate the image.
1839
  */
1840
351
  rotate_image->storage_class=DirectClass;
1841
351
  rotate_image->matte|=rotate_image->background_color.opacity != OpaqueOpacity;
1842
1843
351
  if (XShearImage(rotate_image,shear.x,width,height,
1844
351
          x_offset,y_offset,exception) != MagickPass)
1845
8
    goto rotate_image_exception;
1846
1847
343
  if (YShearImage(rotate_image,shear.y,shear1_width,height,
1848
343
                  (long) (rotate_image->columns-shear1_width)/2,y_offset,exception)
1849
343
      != MagickPass)
1850
0
    goto rotate_image_exception;
1851
1852
343
  if (XShearImage(rotate_image,shear.x,shear1_width,shear2_height,
1853
343
                  (long) (rotate_image->columns-shear1_width)/2,
1854
343
                  (long) (rotate_image->rows-shear2_height)/2,exception)
1855
343
     != MagickPass)
1856
0
    goto rotate_image_exception;
1857
1858
343
  if (CropToFitImage(&rotate_image,shear.x,shear.y,width,height,True,exception)
1859
343
      != MagickPass)
1860
0
    goto rotate_image_exception;
1861
1862
343
  rotate_image->page.width=0;
1863
343
  rotate_image->page.height=0;
1864
343
  return(rotate_image);
1865
1866
9
 rotate_image_exception:
1867
1868
9
  if (rotate_image != (Image *) NULL)
1869
8
    DestroyImage(rotate_image);
1870
9
  return (Image *) NULL;
1871
343
}
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
}