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