~ubuntu-branches/ubuntu/intrepid/graphicsmagick/intrepid

« back to all changes in this revision

Viewing changes to magick/shear.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel Kobras
  • Date: 2006-05-06 16:28:08 UTC
  • Revision ID: james.westby@ubuntu.com-20060506162808-vt2ni3r5nytcszms
Tags: upstream-1.1.7
ImportĀ upstreamĀ versionĀ 1.1.7

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
% Copyright (C) 2003 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 Rotatation" 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/cache.h"
 
46
#include "magick/decorate.h"
 
47
#include "magick/monitor.h"
 
48
#include "magick/render.h"
 
49
#include "magick/shear.h"
 
50
#include "magick/transform.h"
 
51
 
 
52
/*
 
53
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
54
%                                                                             %
 
55
%                                                                             %
 
56
%     A f f i n e T r a n s f o r m I m a g e                                 %
 
57
%                                                                             %
 
58
%                                                                             %
 
59
%                                                                             %
 
60
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
61
%
 
62
%  AffineTransformImage() transforms an image as dictated by the affine matrix.
 
63
%  It allocates the memory necessary for the new Image structure and returns
 
64
%  a pointer to the new image.
 
65
%
 
66
%  The format of the AffineTransformImage method is:
 
67
%
 
68
%      Image *AffineTransformImage(const Image *image,
 
69
%        AffineMatrix *affine,ExceptionInfo *exception)
 
70
%
 
71
%  A description of each parameter follows:
 
72
%
 
73
%    o image: The image.
 
74
%
 
75
%    o affine: The affine transform.
 
76
%
 
77
%    o exception: Return any errors or warnings in this structure.
 
78
%
 
79
%
 
80
*/
 
81
MagickExport Image *AffineTransformImage(const Image *image,
 
82
  const AffineMatrix *affine,ExceptionInfo *exception)
 
83
{
 
84
  AffineMatrix
 
85
    transform;
 
86
 
 
87
  Image
 
88
    *affine_image;
 
89
 
 
90
  long
 
91
    y;
 
92
 
 
93
  PointInfo
 
94
    extent[4],
 
95
    min,
 
96
    max;
 
97
 
 
98
  register long
 
99
    i,
 
100
    x;
 
101
 
 
102
  /*
 
103
    Determine bounding box.
 
104
  */
 
105
  assert(image != (const Image *) NULL);
 
106
  assert(image->signature == MagickSignature);
 
107
  assert(affine != (AffineMatrix *) NULL);
 
108
  assert(exception != (ExceptionInfo *) NULL);
 
109
  assert(exception->signature == MagickSignature);
 
110
  extent[0].x=0;
 
111
  extent[0].y=0;
 
112
  extent[1].x=image->columns;
 
113
  extent[1].y=0;
 
114
  extent[2].x=image->columns;
 
115
  extent[2].y=image->rows;
 
116
  extent[3].x=0;
 
117
  extent[3].y=image->rows;
 
118
  for (i=0; i < 4; i++)
 
119
  {
 
120
    x=(long) (extent[i].x+0.5);
 
121
    y=(long) (extent[i].y+0.5);
 
122
    extent[i].x=x*affine->sx+y*affine->ry+affine->tx;
 
123
    extent[i].y=x*affine->rx+y*affine->sy+affine->ty;
 
124
  }
 
125
  min=extent[0];
 
126
  max=extent[0];
 
127
  for (i=1; i < 4; i++)
 
128
  {
 
129
    if (min.x > extent[i].x)
 
130
      min.x=extent[i].x;
 
131
    if (min.y > extent[i].y)
 
132
      min.y=extent[i].y;
 
133
    if (max.x < extent[i].x)
 
134
      max.x=extent[i].x;
 
135
    if (max.y < extent[i].y)
 
136
      max.y=extent[i].y;
 
137
  }
 
138
  /*
 
139
    Affine transform image.
 
140
  */
 
141
  affine_image=CloneImage(image,(unsigned long) ceil(max.x-min.x-0.5),
 
142
    (unsigned long) ceil(max.y-min.y-0.5),True,exception);
 
143
  if (affine_image == (Image *) NULL)
 
144
    return((Image *) NULL);
 
145
  SetImage(affine_image,TransparentOpacity);
 
146
  transform.sx=affine->sx;
 
147
  transform.rx=affine->rx;
 
148
  transform.ry=affine->ry;
 
149
  transform.sy=affine->sy;
 
150
  transform.tx=(-min.x);
 
151
  transform.ty=(-min.y);
 
152
  DrawAffineImage(affine_image,image,&transform);
 
153
  return(affine_image);
 
154
}
 
155
 
 
156
/*
 
157
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
158
%                                                                             %
 
159
%                                                                             %
 
160
%                                                                             %
 
161
+   C r o p T o F i t I m a g e                                               %
 
162
%                                                                             %
 
163
%                                                                             %
 
164
%                                                                             %
 
165
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
166
%
 
167
%  Method CropToFitImage crops the sheared image as determined by the bounding
 
168
%  box as defined by width and height and shearing angles.
 
169
%
 
170
%  The format of the CropToFitImage method is:
 
171
%
 
172
%      Image *CropToFitImage(Image **image,const double x_shear,
 
173
%        const double x_shear,const double width,const double height,
 
174
%        const unsigne int rotate,ExceptionInfo *exception)
 
175
%
 
176
%  A description of each parameter follows.
 
177
%
 
178
%    o image: The address of a structure of type Image.
 
179
%
 
180
%    o x_shear, y_shear, width, height: Defines a region of the image to crop.
 
181
%
 
182
%    o exception: Return any errors or warnings in this structure.
 
183
%
 
184
%
 
185
*/
 
186
static void CropToFitImage(Image **image,const double x_shear,
 
187
  const double y_shear,const double width,const double height,
 
188
  const unsigned int rotate,ExceptionInfo *exception)
 
189
{
 
190
  Image
 
191
    *crop_image;
 
192
 
 
193
  PointInfo
 
194
    extent[4],
 
195
    min,
 
196
    max;
 
197
 
 
198
  RectangleInfo
 
199
    geometry;
 
200
 
 
201
  register long
 
202
    i;
 
203
 
 
204
  /*
 
205
    Calculate the rotated image size.
 
206
  */
 
207
  extent[0].x=(-width/2.0);
 
208
  extent[0].y=(-height/2.0);
 
209
  extent[1].x=width/2.0;
 
210
  extent[1].y=(-height/2.0);
 
211
  extent[2].x=(-width/2.0);
 
212
  extent[2].y=height/2.0;
 
213
  extent[3].x=width/2.0;
 
214
  extent[3].y=height/2.0;
 
215
  for (i=0; i < 4; i++)
 
216
  {
 
217
    extent[i].x+=x_shear*extent[i].y;
 
218
    extent[i].y+=y_shear*extent[i].x;
 
219
    if (rotate)
 
220
      extent[i].x+=x_shear*extent[i].y;
 
221
    extent[i].x+=(double) (*image)->columns/2.0;
 
222
    extent[i].y+=(double) (*image)->rows/2.0;
 
223
  }
 
224
  min=extent[0];
 
225
  max=extent[0];
 
226
  for (i=1; i < 4; i++)
 
227
  {
 
228
    if (min.x > extent[i].x)
 
229
      min.x=extent[i].x;
 
230
    if (min.y > extent[i].y)
 
231
      min.y=extent[i].y;
 
232
    if (max.x < extent[i].x)
 
233
      max.x=extent[i].x;
 
234
    if (max.y < extent[i].y)
 
235
      max.y=extent[i].y;
 
236
  }
 
237
  geometry.width=(unsigned long) floor(max.x-min.x+0.5);
 
238
  geometry.height=(unsigned long) floor(max.y-min.y+0.5);
 
239
  geometry.x=(long) ceil(min.x-0.5);
 
240
  geometry.y=(long) ceil(min.y-0.5);
 
241
  crop_image=CropImage(*image,&geometry,exception);
 
242
  if (crop_image != (Image *) NULL)
 
243
    {
 
244
      crop_image->page=(*image)->page;
 
245
      DestroyImage(*image);
 
246
      *image=crop_image;
 
247
    }
 
248
}
 
249
 
 
250
/*
 
251
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
252
%                                                                             %
 
253
%                                                                             %
 
254
%                                                                             %
 
255
+   I n t e g r a l R o t a t e I m a g e                                     %
 
256
%                                                                             %
 
257
%                                                                             %
 
258
%                                                                             %
 
259
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
260
%
 
261
%  Method IntegralRotateImage rotates the image an integral of 90 degrees.
 
262
%  It allocates the memory necessary for the new Image structure and returns
 
263
%  a pointer to the rotated image.
 
264
%
 
265
%  The format of the IntegralRotateImage method is:
 
266
%
 
267
%      Image *IntegralRotateImage(const Image *image,unsigned int rotations,
 
268
%        ExceptionInfo *exception)
 
269
%
 
270
%  A description of each parameter follows.
 
271
%
 
272
%    o rotate_image: Method IntegralRotateImage returns a pointer to the
 
273
%      rotated image.  A null image is returned if there is a a memory shortage.
 
274
%
 
275
%    o image: The image.
 
276
%
 
277
%    o rotations: Specifies the number of 90 degree rotations.
 
278
%
 
279
%
 
280
*/
 
281
static Image *IntegralRotateImage(const Image *image,unsigned int rotations,
 
282
  ExceptionInfo *exception)
 
283
{
 
284
#define RotateImageText  "  Rotate image...  "
 
285
 
 
286
  Image
 
287
    *rotate_image;
 
288
 
 
289
  long
 
290
    y;
 
291
 
 
292
  RectangleInfo
 
293
    page;
 
294
 
 
295
  register IndexPacket
 
296
    *indexes,
 
297
    *rotate_indexes;
 
298
 
 
299
  register const PixelPacket
 
300
    *p;
 
301
 
 
302
  register long
 
303
    x;
 
304
 
 
305
  register PixelPacket
 
306
    *q;
 
307
 
 
308
  /*
 
309
    Initialize rotated image attributes.
 
310
  */
 
311
  assert(image != (Image *) NULL);
 
312
  page=image->page;
 
313
  rotations%=4;
 
314
  if ((rotations == 1) || (rotations == 3))
 
315
    rotate_image=CloneImage(image,image->rows,image->columns,True,exception);
 
316
  else
 
317
    rotate_image=CloneImage(image,image->columns,image->rows,True,exception);
 
318
  if (rotate_image == (Image *) NULL)
 
319
    return((Image *) NULL);
 
320
  /*
 
321
    Integral rotate the image.
 
322
  */
 
323
  switch (rotations)
 
324
  {
 
325
    case 0:
 
326
    {
 
327
      /*
 
328
        Rotate 0 degrees.
 
329
      */
 
330
      for (y=0; y < (long) image->rows; y++)
 
331
      {
 
332
        p=AcquireImagePixels(image,0,y,image->columns,1,exception);
 
333
        q=SetImagePixels(rotate_image,0,y,rotate_image->columns,1);
 
334
        if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
 
335
          break;
 
336
        (void) memcpy(q,p,image->columns*sizeof(PixelPacket));
 
337
        indexes=GetIndexes(image);
 
338
        rotate_indexes=GetIndexes(rotate_image);
 
339
        if ((indexes != (IndexPacket *) NULL) &&
 
340
            (rotate_indexes != (IndexPacket *) NULL))
 
341
          (void) memcpy(rotate_indexes,indexes,image->columns*
 
342
            sizeof(IndexPacket));
 
343
        if (!SyncImagePixels(rotate_image))
 
344
          break;
 
345
        if (QuantumTick(y,image->rows))
 
346
          if (!MagickMonitor(RotateImageText,y,image->rows,exception))
 
347
            break;
 
348
      }
 
349
      break;
 
350
    }
 
351
    case 1:
 
352
    {
 
353
      /*
 
354
        Rotate 90 degrees.
 
355
      */
 
356
      for (y=0; y < (long) image->rows; y++)
 
357
      {
 
358
        p=AcquireImagePixels(image,0,y,image->columns,1,exception);
 
359
        q=SetImagePixels(rotate_image,(long) (image->rows-y-1),0,1,
 
360
          rotate_image->rows);
 
361
        if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
 
362
          break;
 
363
        (void) memcpy(q,p,image->columns*sizeof(PixelPacket));
 
364
        indexes=GetIndexes(image);
 
365
        rotate_indexes=GetIndexes(rotate_image);
 
366
        if ((indexes != (IndexPacket *) NULL) &&
 
367
            (rotate_indexes != (IndexPacket *) NULL))
 
368
          (void) memcpy(rotate_indexes,indexes,image->columns*
 
369
            sizeof(IndexPacket));
 
370
        if (!SyncImagePixels(rotate_image))
 
371
          break;
 
372
        if (QuantumTick(y,image->rows))
 
373
          if (!MagickMonitor(RotateImageText,y,image->rows,exception))
 
374
            break;
 
375
      }
 
376
      Swap(page.width,page.height);
 
377
      Swap(page.x,page.y);
 
378
      page.x=(long) (page.width-rotate_image->columns-page.x);
 
379
      break;
 
380
    }
 
381
    case 2:
 
382
    {
 
383
      /*
 
384
        Rotate 180 degrees.
 
385
      */
 
386
      for (y=0; y < (long) image->rows; y++)
 
387
      {
 
388
        p=AcquireImagePixels(image,0,y,image->columns,1,exception);
 
389
        q=SetImagePixels(rotate_image,0,(long) (image->rows-y-1),
 
390
          image->columns,1);
 
391
        if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
 
392
          break;
 
393
        q+=image->columns;
 
394
        indexes=GetIndexes(image);
 
395
        rotate_indexes=GetIndexes(rotate_image);
 
396
        if ((indexes != (IndexPacket *) NULL) &&
 
397
            (rotate_indexes != (IndexPacket *) NULL))
 
398
          for (x=0; x < (long) image->columns; x++)
 
399
            rotate_indexes[image->columns-x-1]=indexes[x];
 
400
        for (x=0; x < (long) image->columns; x++)
 
401
          *--q=(*p++);
 
402
        if (!SyncImagePixels(rotate_image))
 
403
          break;
 
404
        if (QuantumTick(y,image->rows))
 
405
          if (!MagickMonitor(RotateImageText,y,image->rows,exception))
 
406
            break;
 
407
      }
 
408
      page.x=(long) (page.width-rotate_image->columns-page.x);
 
409
      page.y=(long) (page.height-rotate_image->rows-page.y);
 
410
      break;
 
411
    }
 
412
    case 3:
 
413
    {
 
414
      /*
 
415
        Rotate 270 degrees.
 
416
      */
 
417
      for (y=0; y < (long) image->rows; y++)
 
418
      {
 
419
        p=AcquireImagePixels(image,0,y,image->columns,1,exception);
 
420
        q=SetImagePixels(rotate_image,y,0,1,rotate_image->rows);
 
421
        if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL))
 
422
          break;
 
423
        q+=image->columns;
 
424
        for (x=0; x < (long) image->columns; x++)
 
425
          *--q=(*p++);
 
426
        indexes=GetIndexes(image);
 
427
        rotate_indexes=GetIndexes(rotate_image);
 
428
        if ((indexes != (IndexPacket *) NULL) &&
 
429
            (rotate_indexes != (IndexPacket *) NULL))
 
430
          for (x=0; x < (long) image->columns; x++)
 
431
            rotate_indexes[image->columns-x-1]=indexes[x];
 
432
        if (!SyncImagePixels(rotate_image))
 
433
          break;
 
434
        if (QuantumTick(y,image->rows))
 
435
          if (!MagickMonitor(RotateImageText,y,image->rows,exception))
 
436
            break;
 
437
      }
 
438
      Swap(page.width,page.height);
 
439
      Swap(page.x,page.y);
 
440
      page.y=(long) (page.height-rotate_image->rows-page.y);
 
441
      break;
 
442
    }
 
443
  }
 
444
  rotate_image->page=page;
 
445
  rotate_image->is_grayscale=image->is_grayscale;
 
446
  return(rotate_image);
 
447
}
 
448
 
 
449
/*
 
450
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
451
%                                                                             %
 
452
%                                                                             %
 
453
%                                                                             %
 
454
+   X S h e a r I m a g e                                                     %
 
455
%                                                                             %
 
456
%                                                                             %
 
457
%                                                                             %
 
458
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
459
%
 
460
%  Procedure XShearImage shears the image in the X direction with a shear angle
 
461
%  of 'degrees'.  Positive angles shear counter-clockwise (right-hand rule),
 
462
%  and negative angles shear clockwise.  Angles are measured relative to a
 
463
%  vertical Y-axis.  X shears will widen an image creating 'empty' triangles
 
464
%  on the left and right sides of the source image.
 
465
%
 
466
%  The format of the XShearImage method is:
 
467
%
 
468
%      void XShearImage(Image *image,const double degrees,
 
469
%        const unsigned long width,const unsigned long height,
 
470
%        const long x_offset,long y_offset)
 
471
%
 
472
%  A description of each parameter follows.
 
473
%
 
474
%    o image: The image.
 
475
%
 
476
%    o degrees: A double representing the shearing angle along the X axis.
 
477
%
 
478
%    o width, height, x_offset, y_offset: Defines a region of the image
 
479
%      to shear.
 
480
%
 
481
*/
 
482
 
 
483
static inline PixelPacket BlendComposite(const PixelPacket *p,
 
484
  const PixelPacket *q,const double alpha)
 
485
{
 
486
  double
 
487
    color;
 
488
 
 
489
  PixelPacket
 
490
    composite;
 
491
 
 
492
  color=((double) p->red*(MaxRGB-alpha)+q->red*alpha)/MaxRGB;
 
493
  composite.red=(Quantum)
 
494
    ((color < 0) ? 0 : (color > MaxRGB) ? MaxRGB : color+0.5);
 
495
  color=((double) p->green*(MaxRGB-alpha)+q->green*alpha)/MaxRGB;
 
496
  composite.green=(Quantum)
 
497
    ((color < 0) ? 0 : (color > MaxRGB) ? MaxRGB : color+0.5);
 
498
  color=((double) p->blue*(MaxRGB-alpha)+q->blue*alpha)/MaxRGB;
 
499
  composite.blue=(Quantum)
 
500
    ((color < 0) ? 0 : (color > MaxRGB) ? MaxRGB : color+0.5);
 
501
  composite.opacity=p->opacity;
 
502
  return(composite);
 
503
}
 
504
 
 
505
static void XShearImage(Image *image,const double degrees,
 
506
  const unsigned long width,const unsigned long height,const long x_offset,
 
507
  long y_offset)
 
508
{
 
509
#define XShearImageText  "  X Shear image...  "
 
510
 
 
511
  double
 
512
    alpha,
 
513
    displacement;
 
514
 
 
515
  enum {LEFT, RIGHT}
 
516
    direction;
 
517
 
 
518
  long
 
519
    step,
 
520
    y;
 
521
 
 
522
  PixelPacket
 
523
    pixel;
 
524
 
 
525
  register long
 
526
    i;
 
527
 
 
528
  unsigned int
 
529
    is_grayscale;
 
530
 
 
531
  register PixelPacket
 
532
    *p,
 
533
    *q;
 
534
 
 
535
  assert(image != (Image *) NULL);
 
536
  is_grayscale=image->is_grayscale;
 
537
 
 
538
  y_offset--;
 
539
  for (y=0; y < (long) height; y++)
 
540
  {
 
541
    y_offset++;
 
542
    displacement=degrees*(y-height/2.0);
 
543
    if (displacement == 0.0)
 
544
      continue;
 
545
    if (displacement > 0.0)
 
546
      direction=RIGHT;
 
547
    else
 
548
      {
 
549
        displacement*=(-1.0);
 
550
        direction=LEFT;
 
551
      }
 
552
    step=(long) floor(displacement);
 
553
    alpha=(double) MaxRGB*(displacement-step);
 
554
    if (alpha == 0.0)
 
555
      {
 
556
        /*
 
557
          No fractional displacement-- just copy.
 
558
        */
 
559
        switch (direction)
 
560
        {
 
561
          case LEFT:
 
562
          {
 
563
            /*
 
564
              Transfer pixels left-to-right.
 
565
            */
 
566
            if (step > x_offset)
 
567
              break;
 
568
            p=GetImagePixels(image,0,y_offset,image->columns,1);
 
569
            if (p == (PixelPacket *) NULL)
 
570
              break;
 
571
            p+=x_offset;
 
572
            q=p-step;
 
573
            (void) memcpy(q,p,width*sizeof(PixelPacket));
 
574
            q+=width;
 
575
            for (i=0; i < (long) step; i++)
 
576
              *q++=image->background_color;
 
577
            break;
 
578
          }
 
579
          case RIGHT:
 
580
          {
 
581
            /*
 
582
              Transfer pixels right-to-left.
 
583
            */
 
584
            p=GetImagePixels(image,0,y_offset,image->columns,1);
 
585
            if (p == (PixelPacket *) NULL)
 
586
              break;
 
587
            p+=x_offset+width;
 
588
            q=p+step;
 
589
            for (i=0; i < (long) width; i++)
 
590
              *--q=(*--p);
 
591
            for (i=0; i < (long) step; i++)
 
592
              *--q=image->background_color;
 
593
            break;
 
594
          }
 
595
        }
 
596
        if (!SyncImagePixels(image))
 
597
          break;
 
598
        continue;
 
599
      }
 
600
    /*
 
601
      Fractional displacement.
 
602
    */
 
603
    step++;
 
604
    pixel=image->background_color;
 
605
    switch (direction)
 
606
    {
 
607
      case LEFT:
 
608
      {
 
609
        /*
 
610
          Transfer pixels left-to-right.
 
611
        */
 
612
        if (step > x_offset)
 
613
          break;
 
614
        p=GetImagePixels(image,0,y_offset,image->columns,1);
 
615
        if (p == (PixelPacket *) NULL)
 
616
          break;
 
617
        p+=x_offset;
 
618
        q=p-step;
 
619
        for (i=0; i < (long) width; i++)
 
620
        {
 
621
          if ((x_offset+i) < step)
 
622
            {
 
623
              pixel=(*++p);
 
624
              q++;
 
625
              continue;
 
626
            }
 
627
          *q++=BlendComposite(&pixel,p,alpha);
 
628
          pixel=(*p++);
 
629
        }
 
630
        *q++=BlendComposite(&pixel,&image->background_color,alpha);
 
631
        for (i=0; i < (step-1); i++)
 
632
          *q++=image->background_color;
 
633
        break;
 
634
      }
 
635
      case RIGHT:
 
636
      {
 
637
        /*
 
638
          Transfer pixels right-to-left.
 
639
        */
 
640
        p=GetImagePixels(image,0,y_offset,image->columns,1);
 
641
        if (p == (PixelPacket *) NULL)
 
642
          break;
 
643
        p+=x_offset+width;
 
644
        q=p+step;
 
645
        for (i=0; i < (long) width; i++)
 
646
        {
 
647
          p--;
 
648
          q--;
 
649
          if ((x_offset+width+step-i) >= image->columns)
 
650
            continue;
 
651
          *q=BlendComposite(&pixel,p,alpha);
 
652
          pixel=(*p);
 
653
        }
 
654
        *--q=BlendComposite(&pixel,&image->background_color,alpha);
 
655
        for (i=0; i < (step-1); i++)
 
656
          *--q=image->background_color;
 
657
        break;
 
658
      }
 
659
    }
 
660
    if (!SyncImagePixels(image))
 
661
      break;
 
662
    if (QuantumTick(y,height))
 
663
      if (!MagickMonitor(XShearImageText,y,height,&image->exception))
 
664
        break;
 
665
  }
 
666
  if (is_grayscale && IsGray(image->background_color))
 
667
    image->is_grayscale=True;
 
668
}
 
669
 
 
670
/*
 
671
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
672
%                                                                             %
 
673
%                                                                             %
 
674
%                                                                             %
 
675
+   Y S h e a r I m a g e                                                     %
 
676
%                                                                             %
 
677
%                                                                             %
 
678
%                                                                             %
 
679
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
680
%
 
681
%  Procedure YShearImage shears the image in the Y direction with a shear
 
682
%  angle of 'degrees'.  Positive angles shear counter-clockwise (right-hand
 
683
%  rule), and negative angles shear clockwise.  Angles are measured relative
 
684
%  to a horizontal X-axis.  Y shears will increase the height of an image
 
685
%  creating 'empty' triangles on the top and bottom of the source image.
 
686
%
 
687
%  The format of the YShearImage method is:
 
688
%
 
689
%      void YShearImage(Image *image,const double degrees,
 
690
%        const unsigned long width,const unsigned long height,long x_offset,
 
691
%        const long y_offset)
 
692
%
 
693
%  A description of each parameter follows.
 
694
%
 
695
%    o image: The image.
 
696
%
 
697
%    o degrees: A double representing the shearing angle along the Y axis.
 
698
%
 
699
%    o width, height, x_offset, y_offset: Defines a region of the image
 
700
%      to shear.
 
701
%
 
702
%
 
703
*/
 
704
static void YShearImage(Image *image,const double degrees,
 
705
  const unsigned long width,const unsigned long height,long x_offset,
 
706
  const long y_offset)
 
707
{
 
708
#define YShearImageText  "  Y Shear image...  "
 
709
 
 
710
  double
 
711
    alpha,
 
712
    displacement;
 
713
 
 
714
  enum {UP, DOWN}
 
715
    direction;
 
716
 
 
717
  long
 
718
    step,
 
719
    y;
 
720
 
 
721
  register PixelPacket
 
722
    *p,
 
723
    *q;
 
724
 
 
725
  register long
 
726
    i;
 
727
 
 
728
  unsigned int
 
729
    is_grayscale;
 
730
 
 
731
  PixelPacket
 
732
    pixel;
 
733
 
 
734
  assert(image != (Image *) NULL);
 
735
  is_grayscale=image->is_grayscale;
 
736
  x_offset--;
 
737
  for (y=0; y < (long) width; y++)
 
738
  {
 
739
    x_offset++;
 
740
    displacement=degrees*(y-width/2.0);
 
741
    if (displacement == 0.0)
 
742
      continue;
 
743
    if (displacement > 0.0)
 
744
      direction=DOWN;
 
745
    else
 
746
      {
 
747
        displacement*=(-1.0);
 
748
        direction=UP;
 
749
      }
 
750
    step=(long) floor(displacement);
 
751
    alpha=(double) MaxRGB*(displacement-step);
 
752
    if (alpha == 0.0)
 
753
      {
 
754
        /*
 
755
          No fractional displacement-- just copy the pixels.
 
756
        */
 
757
        switch (direction)
 
758
        {
 
759
          case UP:
 
760
          {
 
761
            /*
 
762
              Transfer pixels top-to-bottom.
 
763
            */
 
764
            if (step > y_offset)
 
765
              break;
 
766
            p=GetImagePixels(image,x_offset,0,1,image->rows);
 
767
            if (p == (PixelPacket *) NULL)
 
768
              break;
 
769
            p+=y_offset;
 
770
            q=p-step;
 
771
            (void) memcpy(q,p,height*sizeof(PixelPacket));
 
772
            q+=height;
 
773
            for (i=0; i < (long) step; i++)
 
774
              *q++=image->background_color;
 
775
            break;
 
776
          }
 
777
          case DOWN:
 
778
          {
 
779
            /*
 
780
              Transfer pixels bottom-to-top.
 
781
            */
 
782
            p=GetImagePixels(image,x_offset,0,1,image->rows);
 
783
            if (p == (PixelPacket *) NULL)
 
784
              break;
 
785
            p+=y_offset+height;
 
786
            q=p+step;
 
787
            for (i=0; i < (long) height; i++)
 
788
              *--q=(*--p);
 
789
            for (i=0; i < (long) step; i++)
 
790
              *--q=image->background_color;
 
791
            break;
 
792
          }
 
793
        }
 
794
        if (!SyncImagePixels(image))
 
795
          break;
 
796
        continue;
 
797
      }
 
798
    /*
 
799
      Fractional displacment.
 
800
    */
 
801
    step++;
 
802
    pixel=image->background_color;
 
803
    switch (direction)
 
804
    {
 
805
      case UP:
 
806
      {
 
807
        /*
 
808
          Transfer pixels top-to-bottom.
 
809
        */
 
810
        if (step > y_offset)
 
811
          break;
 
812
        p=GetImagePixels(image,x_offset,0,1,image->rows);
 
813
        if (p == (PixelPacket *) NULL)
 
814
          break;
 
815
        p+=y_offset;
 
816
        q=p-step;
 
817
        for (i=0; i < (long) height; i++)
 
818
        {
 
819
          if ((y_offset+i) < step)
 
820
            {
 
821
              pixel=(*++p);
 
822
              q++;
 
823
              continue;
 
824
            }
 
825
          *q++=BlendComposite(&pixel,p,alpha);
 
826
          pixel=(*p++);
 
827
        }
 
828
        *q++=BlendComposite(&pixel,&image->background_color,alpha);
 
829
        for (i=0; i < (step-1); i++)
 
830
          *q++=image->background_color;
 
831
        break;
 
832
      }
 
833
      case DOWN:
 
834
      {
 
835
        /*
 
836
          Transfer pixels bottom-to-top.
 
837
        */
 
838
        p=GetImagePixels(image,x_offset,0,1,image->rows);
 
839
        if (p == (PixelPacket *) NULL)
 
840
          break;
 
841
        p+=y_offset+height;
 
842
        q=p+step;
 
843
        for (i=0; i < (long) height; i++)
 
844
        {
 
845
          p--;
 
846
          q--;
 
847
          if ((y_offset+height+step-i) >= image->rows)
 
848
            continue;
 
849
          *q=BlendComposite(&pixel,p,alpha);
 
850
          pixel=(*p);
 
851
        }
 
852
        *--q=BlendComposite(&pixel,&image->background_color,alpha);
 
853
        for (i=0; i < (step-1); i++)
 
854
          *--q=image->background_color;
 
855
        break;
 
856
      }
 
857
    }
 
858
    if (!SyncImagePixels(image))
 
859
      break;
 
860
    if (QuantumTick(y,width))
 
861
      if (!MagickMonitor(YShearImageText,y,width,&image->exception))
 
862
        break;
 
863
  }
 
864
  if (is_grayscale && IsGray(image->background_color))
 
865
    image->is_grayscale=True;
 
866
}
 
867
 
 
868
/*
 
869
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
870
%                                                                             %
 
871
%                                                                             %
 
872
%                                                                             %
 
873
%   R o t a t e I m a g e                                                     %
 
874
%                                                                             %
 
875
%                                                                             %
 
876
%                                                                             %
 
877
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
878
%
 
879
%  Method RotateImage creates a new image that is a rotated copy of an
 
880
%  existing one.  Positive angles rotate counter-clockwise (right-hand rule),
 
881
%  while negative angles rotate clockwise.  Rotated images are usually larger
 
882
%  than the originals and have 'empty' triangular corners.  X axis.  Empty
 
883
%  triangles left over from shearing the image are filled with the color
 
884
%  specified by the image background_color.  RotateImage allocates the memory
 
885
%  necessary for the new Image structure and returns a pointer to the new
 
886
%  image.
 
887
%
 
888
%  Method RotateImage is based on the paper "A Fast Algorithm for General
 
889
%  Raster Rotatation" by Alan W. Paeth.  RotateImage is adapted from a similar
 
890
%  method based on the Paeth paper written by Michael Halle of the Spatial
 
891
%  Imaging Group, MIT Media Lab.
 
892
%
 
893
%  The format of the RotateImage method is:
 
894
%
 
895
%      Image *RotateImage(const Image *image,const double degrees,
 
896
%        ExceptionInfo *exception)
 
897
%
 
898
%  A description of each parameter follows.
 
899
%
 
900
%    o status: Method RotateImage returns a pointer to the image after
 
901
%      rotating.  A null image is returned if there is a memory shortage.
 
902
%
 
903
%    o image: The image;  returned from
 
904
%      ReadImage.
 
905
%
 
906
%    o degrees: Specifies the number of degrees to rotate the image.
 
907
%
 
908
%    o exception: Return any errors or warnings in this structure.
 
909
%
 
910
%
 
911
*/
 
912
MagickExport Image *RotateImage(const Image *image,const double degrees,
 
913
  ExceptionInfo *exception)
 
914
{
 
915
  double
 
916
    angle;
 
917
 
 
918
  Image
 
919
    *integral_image,
 
920
    *rotate_image;
 
921
 
 
922
  long
 
923
    x_offset,
 
924
    y_offset;
 
925
 
 
926
  PointInfo
 
927
    shear;
 
928
 
 
929
  RectangleInfo
 
930
    border_info;
 
931
 
 
932
  unsigned long
 
933
    height,
 
934
    rotations,
 
935
    width,
 
936
    y_width;
 
937
 
 
938
  /*
 
939
    Adjust rotation angle.
 
940
  */
 
941
  assert(image != (Image *) NULL);
 
942
  assert(image->signature == MagickSignature);
 
943
  assert(exception != (ExceptionInfo *) NULL);
 
944
  assert(exception->signature == MagickSignature);
 
945
  angle=degrees;
 
946
  while (angle < -45.0)
 
947
    angle+=360.0;
 
948
  for (rotations=0; angle > 45.0; rotations++)
 
949
    angle-=90.0;
 
950
  rotations%=4;
 
951
  /*
 
952
    Calculate shear equations.
 
953
  */
 
954
  integral_image=IntegralRotateImage(image,rotations,exception);
 
955
  if (integral_image == (Image *) NULL)
 
956
    ThrowImageException3(ResourceLimitError,MemoryAllocationFailed,
 
957
      UnableToRotateImage);
 
958
  shear.x=(-tan(DegreesToRadians(angle)/2.0));
 
959
  shear.y=sin(DegreesToRadians(angle));
 
960
  if ((shear.x == 0.0) || (shear.y == 0.0))
 
961
    return(integral_image);
 
962
  /*
 
963
    Compute image size.
 
964
  */
 
965
  width=image->columns;
 
966
  height=image->rows;
 
967
  if ((rotations == 1) || (rotations == 3))
 
968
    {
 
969
      width=image->rows;
 
970
      height=image->columns;
 
971
    }
 
972
  x_offset=(long) ceil(fabs(2.0*height*shear.y)-0.5);
 
973
  y_width=(unsigned long) floor(fabs(height*shear.x)+width+0.5);
 
974
  y_offset=(long) ceil(fabs(y_width*shear.y)-0.5);
 
975
  /*
 
976
    Surround image with a border.
 
977
  */
 
978
  integral_image->border_color=integral_image->background_color;
 
979
  border_info.width=x_offset;
 
980
  border_info.height=y_offset;
 
981
  rotate_image=BorderImage(integral_image,&border_info,exception);
 
982
  DestroyImage(integral_image);
 
983
  if (rotate_image == (Image *) NULL)
 
984
    ThrowImageException3(ResourceLimitError,MemoryAllocationFailed,
 
985
      UnableToRotateImage);
 
986
  /*
 
987
    Rotate the image.
 
988
  */
 
989
  rotate_image->storage_class=DirectClass;
 
990
  rotate_image->matte|=rotate_image->background_color.opacity != OpaqueOpacity;
 
991
  XShearImage(rotate_image,shear.x,width,height,x_offset,
 
992
    (long) (rotate_image->rows-height)/2);
 
993
  YShearImage(rotate_image,shear.y,y_width,height,
 
994
    (long) (rotate_image->columns-y_width)/2,y_offset);
 
995
  XShearImage(rotate_image,shear.x,y_width,rotate_image->rows,
 
996
    (long) (rotate_image->columns-y_width)/2,0);
 
997
  CropToFitImage(&rotate_image,shear.x,shear.y,width,height,True,exception);
 
998
  rotate_image->page.width=0;
 
999
  rotate_image->page.height=0;
 
1000
  return(rotate_image);
 
1001
}
 
1002
 
 
1003
/*
 
1004
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1005
%                                                                             %
 
1006
%                                                                             %
 
1007
%                                                                             %
 
1008
%   S h e a r I m a g e                                                       %
 
1009
%                                                                             %
 
1010
%                                                                             %
 
1011
%                                                                             %
 
1012
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1013
%
 
1014
%  Method ShearImage creates a new image that is a shear_image copy of an
 
1015
%  existing one.  Shearing slides one edge of an image along the X or Y
 
1016
%  axis, creating a parallelogram.  An X direction shear slides an edge
 
1017
%  along the X axis, while a Y direction shear slides an edge along the Y
 
1018
%  axis.  The amount of the shear is controlled by a shear angle.  For X
 
1019
%  direction shears, x_shear is measured relative to the Y axis, and
 
1020
%  similarly, for Y direction shears y_shear is measured relative to the
 
1021
%  X axis.  Empty triangles left over from shearing the image are filled
 
1022
%  with the color defined by the pixel at location (0,0).  ShearImage
 
1023
%  allocates the memory necessary for the new Image structure and returns
 
1024
%  a pointer to the new image.
 
1025
%
 
1026
%  Method ShearImage is based on the paper "A Fast Algorithm for General
 
1027
%  Raster Rotatation" by Alan W. Paeth.
 
1028
%
 
1029
%  The format of the ShearImage method is:
 
1030
%
 
1031
%      Image *ShearImage(const Image *image,const double x_shear,
 
1032
%        const double y_shear,ExceptionInfo *exception)
 
1033
%
 
1034
%  A description of each parameter follows.
 
1035
%
 
1036
%    o status: Method ShearImage returns a pointer to the image after
 
1037
%      rotating.  A null image is returned if there is a memory shortage.
 
1038
%
 
1039
%    o image: The image;  returned from
 
1040
%      ReadImage.
 
1041
%
 
1042
%    o x_shear, y_shear: Specifies the number of degrees to shear the image.
 
1043
%
 
1044
%    o exception: Return any errors or warnings in this structure.
 
1045
%
 
1046
%
 
1047
*/
 
1048
MagickExport Image *ShearImage(const Image *image,const double x_shear,
 
1049
  const double y_shear,ExceptionInfo *exception)
 
1050
{
 
1051
  Image
 
1052
    *integral_image,
 
1053
    *shear_image;
 
1054
 
 
1055
  long
 
1056
    x_offset,
 
1057
    y_offset;
 
1058
 
 
1059
  PointInfo
 
1060
    shear;
 
1061
 
 
1062
  RectangleInfo
 
1063
    border_info;
 
1064
 
 
1065
  unsigned long
 
1066
    y_width;
 
1067
 
 
1068
  assert(image != (Image *) NULL);
 
1069
  assert(image->signature == MagickSignature);
 
1070
  assert(exception != (ExceptionInfo *) NULL);
 
1071
  assert(exception->signature == MagickSignature);
 
1072
  if ((x_shear == 180.0) || (y_shear == 180.0))
 
1073
    ThrowImageException3(ImageError,UnableToShearImage,AngleIsDiscontinuous);
 
1074
 
 
1075
  /*
 
1076
    Initialize shear angle.
 
1077
  */
 
1078
  integral_image=IntegralRotateImage(image,0,exception);
 
1079
  if (integral_image == (Image *) NULL)
 
1080
    ThrowImageException3(ResourceLimitError,MemoryAllocationFailed,
 
1081
      UnableToShearImage);
 
1082
  shear.x=(-tan(DegreesToRadians(x_shear)/2.0));
 
1083
  shear.y=sin(DegreesToRadians(y_shear));
 
1084
  if ((shear.x == 0.0) || (shear.y == 0.0))
 
1085
    return(integral_image);
 
1086
 
 
1087
  /*
 
1088
    Compute image size.
 
1089
  */
 
1090
  x_offset=(long) ceil(fabs(2.0*image->rows*shear.x)-0.5);
 
1091
  y_width=(unsigned long) floor(fabs(image->rows*shear.x)+image->columns+0.5);
 
1092
  y_offset=(long) ceil(fabs(y_width*shear.y)-0.5);
 
1093
  /*
 
1094
    Surround image with border.
 
1095
  */
 
1096
  integral_image->border_color=integral_image->background_color;
 
1097
  border_info.width=x_offset;
 
1098
  border_info.height=y_offset;
 
1099
  shear_image=BorderImage(integral_image,&border_info,exception);
 
1100
  if (shear_image == (Image *) NULL)
 
1101
    ThrowImageException3(ResourceLimitError,MemoryAllocationFailed,
 
1102
      UnableToShearImage);
 
1103
  DestroyImage(integral_image);
 
1104
  /*
 
1105
    Shear the image.
 
1106
  */
 
1107
  shear_image->storage_class=DirectClass;
 
1108
  shear_image->matte|=shear_image->background_color.opacity != OpaqueOpacity;
 
1109
  XShearImage(shear_image,shear.x,image->columns,image->rows,x_offset,
 
1110
    (long) (shear_image->rows-image->rows)/2);
 
1111
  YShearImage(shear_image,shear.y,y_width,image->rows,
 
1112
    (long) (shear_image->columns-y_width)/2,y_offset);
 
1113
  CropToFitImage(&shear_image,shear.x,shear.y,image->columns,image->rows,
 
1114
    False,exception);
 
1115
  shear_image->page.width=0;
 
1116
  shear_image->page.height=0;
 
1117
  return(shear_image);
 
1118
}