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