transform.c revision 8969a36f06b585ff612855f3808de07f1f5d57b0
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-2014 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=RotateImage(image,90.0,exception); 141 break; 142 } 143 case RightBottomOrientation: 144 { 145 orient_image=TransverseImage(image,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 ((size_t) (page.x+page.width) > 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 ((size_t) (page.y+page.height) > 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 break; 882 AppendImageToList(&crop_image,next); 883 } 884 if (next == (Image *) NULL) 885 break; 886 } 887 ClearMagickException(exception); 888 return(crop_image); 889 } 890 if (((geometry.width == 0) && (geometry.height == 0)) || 891 ((flags & XValue) != 0) || ((flags & YValue) != 0)) 892 { 893 /* 894 Crop a single region at +X+Y. 895 */ 896 crop_image=CropImage(image,&geometry,exception); 897 if ((crop_image != (Image *) NULL) && ((flags & AspectValue) != 0)) 898 { 899 crop_image->page.width=geometry.width; 900 crop_image->page.height=geometry.height; 901 crop_image->page.x-=geometry.x; 902 crop_image->page.y-=geometry.y; 903 } 904 return(crop_image); 905 } 906 if ((image->columns > geometry.width) || (image->rows > geometry.height)) 907 { 908 RectangleInfo 909 page; 910 911 size_t 912 height, 913 width; 914 915 ssize_t 916 x, 917 y; 918 919 /* 920 Crop into tiles of fixed size WxH. 921 */ 922 page=image->page; 923 if (page.width == 0) 924 page.width=image->columns; 925 if (page.height == 0) 926 page.height=image->rows; 927 width=geometry.width; 928 if (width == 0) 929 width=page.width; 930 height=geometry.height; 931 if (height == 0) 932 height=page.height; 933 next=NewImageList(); 934 for (y=0; y < (ssize_t) page.height; y+=(ssize_t) height) 935 { 936 for (x=0; x < (ssize_t) page.width; x+=(ssize_t) width) 937 { 938 geometry.width=width; 939 geometry.height=height; 940 geometry.x=x; 941 geometry.y=y; 942 next=CropImage(image,&geometry,exception); 943 if (next == (Image *) NULL) 944 break; 945 AppendImageToList(&crop_image,next); 946 } 947 if (next == (Image *) NULL) 948 break; 949 } 950 return(crop_image); 951 } 952 return(CloneImage(image,0,0,MagickTrue,exception)); 953} 954 955/* 956%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 957% % 958% % 959% % 960% E x c e r p t I m a g e % 961% % 962% % 963% % 964%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 965% 966% ExcerptImage() returns a excerpt of the image as defined by the geometry. 967% 968% The format of the ExcerptImage method is: 969% 970% Image *ExcerptImage(const Image *image,const RectangleInfo *geometry, 971% ExceptionInfo *exception) 972% 973% A description of each parameter follows: 974% 975% o image: the image. 976% 977% o geometry: Define the region of the image to extend with members 978% x, y, width, and height. 979% 980% o exception: return any errors or warnings in this structure. 981% 982*/ 983MagickExport Image *ExcerptImage(const Image *image, 984 const RectangleInfo *geometry,ExceptionInfo *exception) 985{ 986#define ExcerptImageTag "Excerpt/Image" 987 988 CacheView 989 *excerpt_view, 990 *image_view; 991 992 Image 993 *excerpt_image; 994 995 MagickBooleanType 996 status; 997 998 MagickOffsetType 999 progress; 1000 1001 ssize_t 1002 y; 1003 1004 /* 1005 Allocate excerpt image. 1006 */ 1007 assert(image != (const Image *) NULL); 1008 assert(image->signature == MagickSignature); 1009 if (image->debug != MagickFalse) 1010 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1011 assert(geometry != (const RectangleInfo *) NULL); 1012 assert(exception != (ExceptionInfo *) NULL); 1013 assert(exception->signature == MagickSignature); 1014 excerpt_image=CloneImage(image,geometry->width,geometry->height,MagickTrue, 1015 exception); 1016 if (excerpt_image == (Image *) NULL) 1017 return((Image *) NULL); 1018 /* 1019 Excerpt each row. 1020 */ 1021 status=MagickTrue; 1022 progress=0; 1023 image_view=AcquireVirtualCacheView(image,exception); 1024 excerpt_view=AcquireAuthenticCacheView(excerpt_image,exception); 1025#if defined(MAGICKCORE_OPENMP_SUPPORT) 1026 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 1027 magick_threads(image,excerpt_image,excerpt_image->rows,1) 1028#endif 1029 for (y=0; y < (ssize_t) excerpt_image->rows; y++) 1030 { 1031 register const Quantum 1032 *restrict p; 1033 1034 register Quantum 1035 *restrict q; 1036 1037 register ssize_t 1038 x; 1039 1040 if (status == MagickFalse) 1041 continue; 1042 p=GetCacheViewVirtualPixels(image_view,geometry->x,geometry->y+y, 1043 geometry->width,1,exception); 1044 q=GetCacheViewAuthenticPixels(excerpt_view,0,y,excerpt_image->columns,1, 1045 exception); 1046 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 1047 { 1048 status=MagickFalse; 1049 continue; 1050 } 1051 for (x=0; x < (ssize_t) excerpt_image->columns; x++) 1052 { 1053 register ssize_t 1054 i; 1055 1056 if (GetPixelReadMask(image,p) == 0) 1057 { 1058 SetPixelBackgoundColor(excerpt_image,q); 1059 p+=GetPixelChannels(image); 1060 q+=GetPixelChannels(excerpt_image); 1061 continue; 1062 } 1063 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1064 { 1065 PixelChannel channel=GetPixelChannelChannel(image,i); 1066 PixelTrait traits=GetPixelChannelTraits(image,channel); 1067 PixelTrait excerpt_traits=GetPixelChannelTraits(excerpt_image,channel); 1068 if ((traits == UndefinedPixelTrait) || 1069 (excerpt_traits == UndefinedPixelTrait)) 1070 continue; 1071 SetPixelChannel(excerpt_image,channel,p[i],q); 1072 } 1073 p+=GetPixelChannels(image); 1074 q+=GetPixelChannels(excerpt_image); 1075 } 1076 if (SyncCacheViewAuthenticPixels(excerpt_view,exception) == MagickFalse) 1077 status=MagickFalse; 1078 if (image->progress_monitor != (MagickProgressMonitor) NULL) 1079 { 1080 MagickBooleanType 1081 proceed; 1082 1083#if defined(MAGICKCORE_OPENMP_SUPPORT) 1084 #pragma omp critical (MagickCore_ExcerptImage) 1085#endif 1086 proceed=SetImageProgress(image,ExcerptImageTag,progress++,image->rows); 1087 if (proceed == MagickFalse) 1088 status=MagickFalse; 1089 } 1090 } 1091 excerpt_view=DestroyCacheView(excerpt_view); 1092 image_view=DestroyCacheView(image_view); 1093 excerpt_image->type=image->type; 1094 if (status == MagickFalse) 1095 excerpt_image=DestroyImage(excerpt_image); 1096 return(excerpt_image); 1097} 1098 1099/* 1100%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1101% % 1102% % 1103% % 1104% E x t e n t I m a g e % 1105% % 1106% % 1107% % 1108%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1109% 1110% ExtentImage() extends the image as defined by the geometry, gravity, and 1111% image background color. Set the (x,y) offset of the geometry to move the 1112% original image relative to the extended image. 1113% 1114% The format of the ExtentImage method is: 1115% 1116% Image *ExtentImage(const Image *image,const RectangleInfo *geometry, 1117% ExceptionInfo *exception) 1118% 1119% A description of each parameter follows: 1120% 1121% o image: the image. 1122% 1123% o geometry: Define the region of the image to extend with members 1124% x, y, width, and height. 1125% 1126% o exception: return any errors or warnings in this structure. 1127% 1128*/ 1129MagickExport Image *ExtentImage(const Image *image, 1130 const RectangleInfo *geometry,ExceptionInfo *exception) 1131{ 1132 Image 1133 *extent_image; 1134 1135 /* 1136 Allocate extent image. 1137 */ 1138 assert(image != (const Image *) NULL); 1139 assert(image->signature == MagickSignature); 1140 if (image->debug != MagickFalse) 1141 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1142 assert(geometry != (const RectangleInfo *) NULL); 1143 assert(exception != (ExceptionInfo *) NULL); 1144 assert(exception->signature == MagickSignature); 1145 extent_image=CloneImage(image,geometry->width,geometry->height,MagickTrue, 1146 exception); 1147 if (extent_image == (Image *) NULL) 1148 return((Image *) NULL); 1149 if (SetImageStorageClass(extent_image,DirectClass,exception) == MagickFalse) 1150 { 1151 extent_image=DestroyImage(extent_image); 1152 return((Image *) NULL); 1153 } 1154 if (extent_image->background_color.alpha != OpaqueAlpha) 1155 extent_image->alpha_trait=BlendPixelTrait; 1156 (void) SetImageBackgroundColor(extent_image,exception); 1157 (void) CompositeImage(extent_image,image,image->compose,MagickTrue, 1158 -geometry->x,-geometry->y,exception); 1159 return(extent_image); 1160} 1161 1162/* 1163%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1164% % 1165% % 1166% % 1167% F l i p I m a g e % 1168% % 1169% % 1170% % 1171%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1172% 1173% FlipImage() creates a vertical mirror image by reflecting the pixels 1174% around the central x-axis. 1175% 1176% The format of the FlipImage method is: 1177% 1178% Image *FlipImage(const Image *image,ExceptionInfo *exception) 1179% 1180% A description of each parameter follows: 1181% 1182% o image: the image. 1183% 1184% o exception: return any errors or warnings in this structure. 1185% 1186*/ 1187MagickExport Image *FlipImage(const Image *image,ExceptionInfo *exception) 1188{ 1189#define FlipImageTag "Flip/Image" 1190 1191 CacheView 1192 *flip_view, 1193 *image_view; 1194 1195 Image 1196 *flip_image; 1197 1198 MagickBooleanType 1199 status; 1200 1201 MagickOffsetType 1202 progress; 1203 1204 RectangleInfo 1205 page; 1206 1207 ssize_t 1208 y; 1209 1210 assert(image != (const Image *) NULL); 1211 assert(image->signature == MagickSignature); 1212 if (image->debug != MagickFalse) 1213 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1214 assert(exception != (ExceptionInfo *) NULL); 1215 assert(exception->signature == MagickSignature); 1216 flip_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception); 1217 if (flip_image == (Image *) NULL) 1218 return((Image *) NULL); 1219 /* 1220 Flip image. 1221 */ 1222 status=MagickTrue; 1223 progress=0; 1224 page=image->page; 1225 image_view=AcquireVirtualCacheView(image,exception); 1226 flip_view=AcquireAuthenticCacheView(flip_image,exception); 1227#if defined(MAGICKCORE_OPENMP_SUPPORT) 1228 #pragma omp parallel for schedule(static,4) shared(status) \ 1229 magick_threads(image,flip_image,1,1) 1230#endif 1231 for (y=0; y < (ssize_t) flip_image->rows; y++) 1232 { 1233 register const Quantum 1234 *restrict p; 1235 1236 register Quantum 1237 *restrict q; 1238 1239 register ssize_t 1240 x; 1241 1242 if (status == MagickFalse) 1243 continue; 1244 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 1245 q=QueueCacheViewAuthenticPixels(flip_view,0,(ssize_t) (flip_image->rows-y- 1246 1),flip_image->columns,1,exception); 1247 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 1248 { 1249 status=MagickFalse; 1250 continue; 1251 } 1252 for (x=0; x < (ssize_t) flip_image->columns; x++) 1253 { 1254 register ssize_t 1255 i; 1256 1257 if (GetPixelReadMask(image,p) == 0) 1258 { 1259 SetPixelBackgoundColor(flip_image,q); 1260 p+=GetPixelChannels(image); 1261 q+=GetPixelChannels(flip_image); 1262 continue; 1263 } 1264 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1265 { 1266 PixelChannel channel=GetPixelChannelChannel(image,i); 1267 PixelTrait traits=GetPixelChannelTraits(image,channel); 1268 PixelTrait flip_traits=GetPixelChannelTraits(flip_image,channel); 1269 if ((traits == UndefinedPixelTrait) || 1270 (flip_traits == UndefinedPixelTrait)) 1271 continue; 1272 SetPixelChannel(flip_image,channel,p[i],q); 1273 } 1274 p+=GetPixelChannels(image); 1275 q+=GetPixelChannels(flip_image); 1276 } 1277 if (SyncCacheViewAuthenticPixels(flip_view,exception) == MagickFalse) 1278 status=MagickFalse; 1279 if (image->progress_monitor != (MagickProgressMonitor) NULL) 1280 { 1281 MagickBooleanType 1282 proceed; 1283 1284#if defined(MAGICKCORE_OPENMP_SUPPORT) 1285 #pragma omp critical (MagickCore_FlipImage) 1286#endif 1287 proceed=SetImageProgress(image,FlipImageTag,progress++,image->rows); 1288 if (proceed == MagickFalse) 1289 status=MagickFalse; 1290 } 1291 } 1292 flip_view=DestroyCacheView(flip_view); 1293 image_view=DestroyCacheView(image_view); 1294 flip_image->type=image->type; 1295 if (page.height != 0) 1296 page.y=(ssize_t) (page.height-flip_image->rows-page.y); 1297 flip_image->page=page; 1298 if (status == MagickFalse) 1299 flip_image=DestroyImage(flip_image); 1300 return(flip_image); 1301} 1302 1303/* 1304%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1305% % 1306% % 1307% % 1308% F l o p I m a g e % 1309% % 1310% % 1311% % 1312%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1313% 1314% FlopImage() creates a horizontal mirror image by reflecting the pixels 1315% around the central y-axis. 1316% 1317% The format of the FlopImage method is: 1318% 1319% Image *FlopImage(const Image *image,ExceptionInfo *exception) 1320% 1321% A description of each parameter follows: 1322% 1323% o image: the image. 1324% 1325% o exception: return any errors or warnings in this structure. 1326% 1327*/ 1328MagickExport Image *FlopImage(const Image *image,ExceptionInfo *exception) 1329{ 1330#define FlopImageTag "Flop/Image" 1331 1332 CacheView 1333 *flop_view, 1334 *image_view; 1335 1336 Image 1337 *flop_image; 1338 1339 MagickBooleanType 1340 status; 1341 1342 MagickOffsetType 1343 progress; 1344 1345 RectangleInfo 1346 page; 1347 1348 ssize_t 1349 y; 1350 1351 assert(image != (const Image *) NULL); 1352 assert(image->signature == MagickSignature); 1353 if (image->debug != MagickFalse) 1354 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1355 assert(exception != (ExceptionInfo *) NULL); 1356 assert(exception->signature == MagickSignature); 1357 flop_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception); 1358 if (flop_image == (Image *) NULL) 1359 return((Image *) NULL); 1360 /* 1361 Flop each row. 1362 */ 1363 status=MagickTrue; 1364 progress=0; 1365 page=image->page; 1366 image_view=AcquireVirtualCacheView(image,exception); 1367 flop_view=AcquireAuthenticCacheView(flop_image,exception); 1368#if defined(MAGICKCORE_OPENMP_SUPPORT) 1369 #pragma omp parallel for schedule(static,4) shared(status) \ 1370 magick_threads(image,flop_image,1,1) 1371#endif 1372 for (y=0; y < (ssize_t) flop_image->rows; y++) 1373 { 1374 register const Quantum 1375 *restrict p; 1376 1377 register ssize_t 1378 x; 1379 1380 register Quantum 1381 *restrict q; 1382 1383 if (status == MagickFalse) 1384 continue; 1385 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 1386 q=QueueCacheViewAuthenticPixels(flop_view,0,y,flop_image->columns,1, 1387 exception); 1388 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 1389 { 1390 status=MagickFalse; 1391 continue; 1392 } 1393 q+=GetPixelChannels(flop_image)*flop_image->columns; 1394 for (x=0; x < (ssize_t) flop_image->columns; x++) 1395 { 1396 register ssize_t 1397 i; 1398 1399 q-=GetPixelChannels(flop_image); 1400 if (GetPixelReadMask(image,p) == 0) 1401 { 1402 p+=GetPixelChannels(image); 1403 continue; 1404 } 1405 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1406 { 1407 PixelChannel channel=GetPixelChannelChannel(image,i); 1408 PixelTrait traits=GetPixelChannelTraits(image,channel); 1409 PixelTrait flop_traits=GetPixelChannelTraits(flop_image,channel); 1410 if ((traits == UndefinedPixelTrait) || 1411 (flop_traits == UndefinedPixelTrait)) 1412 continue; 1413 SetPixelChannel(flop_image,channel,p[i],q); 1414 } 1415 p+=GetPixelChannels(image); 1416 } 1417 if (SyncCacheViewAuthenticPixels(flop_view,exception) == MagickFalse) 1418 status=MagickFalse; 1419 if (image->progress_monitor != (MagickProgressMonitor) NULL) 1420 { 1421 MagickBooleanType 1422 proceed; 1423 1424#if defined(MAGICKCORE_OPENMP_SUPPORT) 1425 #pragma omp critical (MagickCore_FlopImage) 1426#endif 1427 proceed=SetImageProgress(image,FlopImageTag,progress++,image->rows); 1428 if (proceed == MagickFalse) 1429 status=MagickFalse; 1430 } 1431 } 1432 flop_view=DestroyCacheView(flop_view); 1433 image_view=DestroyCacheView(image_view); 1434 flop_image->type=image->type; 1435 if (page.width != 0) 1436 page.x=(ssize_t) (page.width-flop_image->columns-page.x); 1437 flop_image->page=page; 1438 if (status == MagickFalse) 1439 flop_image=DestroyImage(flop_image); 1440 return(flop_image); 1441} 1442 1443/* 1444%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1445% % 1446% % 1447% % 1448% R o l l I m a g e % 1449% % 1450% % 1451% % 1452%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1453% 1454% RollImage() offsets an image as defined by x_offset and y_offset. 1455% 1456% The format of the RollImage method is: 1457% 1458% Image *RollImage(const Image *image,const ssize_t x_offset, 1459% const ssize_t y_offset,ExceptionInfo *exception) 1460% 1461% A description of each parameter follows: 1462% 1463% o image: the image. 1464% 1465% o x_offset: the number of columns to roll in the horizontal direction. 1466% 1467% o y_offset: the number of rows to roll in the vertical direction. 1468% 1469% o exception: return any errors or warnings in this structure. 1470% 1471*/ 1472 1473static inline MagickBooleanType CopyImageRegion(Image *destination, 1474 const Image *source,const size_t columns,const size_t rows, 1475 const ssize_t sx,const ssize_t sy,const ssize_t dx,const ssize_t dy, 1476 ExceptionInfo *exception) 1477{ 1478 CacheView 1479 *source_view, 1480 *destination_view; 1481 1482 MagickBooleanType 1483 status; 1484 1485 ssize_t 1486 y; 1487 1488 if (columns == 0) 1489 return(MagickTrue); 1490 status=MagickTrue; 1491 source_view=AcquireVirtualCacheView(source,exception); 1492 destination_view=AcquireAuthenticCacheView(destination,exception); 1493#if defined(MAGICKCORE_OPENMP_SUPPORT) 1494 #pragma omp parallel for schedule(static,4) shared(status) \ 1495 magick_threads(source,destination,rows,1) 1496#endif 1497 for (y=0; y < (ssize_t) rows; y++) 1498 { 1499 MagickBooleanType 1500 sync; 1501 1502 register const Quantum 1503 *restrict p; 1504 1505 register Quantum 1506 *restrict q; 1507 1508 register ssize_t 1509 x; 1510 1511 /* 1512 Transfer scanline. 1513 */ 1514 if (status == MagickFalse) 1515 continue; 1516 p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception); 1517 q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception); 1518 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 1519 { 1520 status=MagickFalse; 1521 continue; 1522 } 1523 for (x=0; x < (ssize_t) columns; x++) 1524 { 1525 register ssize_t 1526 i; 1527 1528 if (GetPixelReadMask(source,p) == 0) 1529 { 1530 SetPixelBackgoundColor(destination,q); 1531 p+=GetPixelChannels(source); 1532 q+=GetPixelChannels(destination); 1533 continue; 1534 } 1535 for (i=0; i < (ssize_t) GetPixelChannels(source); i++) 1536 { 1537 PixelChannel channel=GetPixelChannelChannel(source,i); 1538 PixelTrait source_traits=GetPixelChannelTraits(source,channel); 1539 PixelTrait destination_traits=GetPixelChannelTraits(destination, 1540 channel); 1541 if ((source_traits == UndefinedPixelTrait) || 1542 (destination_traits == UndefinedPixelTrait)) 1543 continue; 1544 SetPixelChannel(destination,channel,p[i],q); 1545 } 1546 p+=GetPixelChannels(source); 1547 q+=GetPixelChannels(destination); 1548 } 1549 sync=SyncCacheViewAuthenticPixels(destination_view,exception); 1550 if (sync == MagickFalse) 1551 status=MagickFalse; 1552 } 1553 destination_view=DestroyCacheView(destination_view); 1554 source_view=DestroyCacheView(source_view); 1555 return(status); 1556} 1557 1558MagickExport Image *RollImage(const Image *image,const ssize_t x_offset, 1559 const ssize_t y_offset,ExceptionInfo *exception) 1560{ 1561#define RollImageTag "Roll/Image" 1562 1563 Image 1564 *roll_image; 1565 1566 MagickStatusType 1567 status; 1568 1569 RectangleInfo 1570 offset; 1571 1572 /* 1573 Initialize roll image attributes. 1574 */ 1575 assert(image != (const Image *) NULL); 1576 assert(image->signature == MagickSignature); 1577 if (image->debug != MagickFalse) 1578 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1579 assert(exception != (ExceptionInfo *) NULL); 1580 assert(exception->signature == MagickSignature); 1581 roll_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception); 1582 if (roll_image == (Image *) NULL) 1583 return((Image *) NULL); 1584 offset.x=x_offset; 1585 offset.y=y_offset; 1586 while (offset.x < 0) 1587 offset.x+=(ssize_t) image->columns; 1588 while (offset.x >= (ssize_t) image->columns) 1589 offset.x-=(ssize_t) image->columns; 1590 while (offset.y < 0) 1591 offset.y+=(ssize_t) image->rows; 1592 while (offset.y >= (ssize_t) image->rows) 1593 offset.y-=(ssize_t) image->rows; 1594 /* 1595 Roll image. 1596 */ 1597 status=CopyImageRegion(roll_image,image,(size_t) offset.x, 1598 (size_t) offset.y,(ssize_t) image->columns-offset.x,(ssize_t) image->rows- 1599 offset.y,0,0,exception); 1600 (void) SetImageProgress(image,RollImageTag,0,3); 1601 status&=CopyImageRegion(roll_image,image,image->columns-offset.x, 1602 (size_t) offset.y,0,(ssize_t) image->rows-offset.y,offset.x,0, 1603 exception); 1604 (void) SetImageProgress(image,RollImageTag,1,3); 1605 status&=CopyImageRegion(roll_image,image,(size_t) offset.x,image->rows- 1606 offset.y,(ssize_t) image->columns-offset.x,0,0,offset.y,exception); 1607 (void) SetImageProgress(image,RollImageTag,2,3); 1608 status&=CopyImageRegion(roll_image,image,image->columns-offset.x,image->rows- 1609 offset.y,0,0,offset.x,offset.y,exception); 1610 (void) SetImageProgress(image,RollImageTag,3,3); 1611 roll_image->type=image->type; 1612 if (status == MagickFalse) 1613 roll_image=DestroyImage(roll_image); 1614 return(roll_image); 1615} 1616 1617/* 1618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1619% % 1620% % 1621% % 1622% S h a v e I m a g e % 1623% % 1624% % 1625% % 1626%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1627% 1628% ShaveImage() shaves pixels from the image edges. It allocates the memory 1629% necessary for the new Image structure and returns a pointer to the new 1630% image. 1631% 1632% The format of the ShaveImage method is: 1633% 1634% Image *ShaveImage(const Image *image,const RectangleInfo *shave_info, 1635% ExceptionInfo *exception) 1636% 1637% A description of each parameter follows: 1638% 1639% o shave_image: Method ShaveImage returns a pointer to the shaved 1640% image. A null image is returned if there is a memory shortage or 1641% if the image width or height is zero. 1642% 1643% o image: the image. 1644% 1645% o shave_info: Specifies a pointer to a RectangleInfo which defines the 1646% region of the image to crop. 1647% 1648% o exception: return any errors or warnings in this structure. 1649% 1650*/ 1651MagickExport Image *ShaveImage(const Image *image, 1652 const RectangleInfo *shave_info,ExceptionInfo *exception) 1653{ 1654 Image 1655 *shave_image; 1656 1657 RectangleInfo 1658 geometry; 1659 1660 assert(image != (const Image *) NULL); 1661 assert(image->signature == MagickSignature); 1662 if (image->debug != MagickFalse) 1663 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1664 if (((2*shave_info->width) >= image->columns) || 1665 ((2*shave_info->height) >= image->rows)) 1666 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage"); 1667 SetGeometry(image,&geometry); 1668 geometry.width-=2*shave_info->width; 1669 geometry.height-=2*shave_info->height; 1670 geometry.x=(ssize_t) shave_info->width+image->page.x; 1671 geometry.y=(ssize_t) shave_info->height+image->page.y; 1672 shave_image=CropImage(image,&geometry,exception); 1673 if (shave_image == (Image *) NULL) 1674 return((Image *) NULL); 1675 shave_image->page.width-=2*shave_info->width; 1676 shave_image->page.height-=2*shave_info->height; 1677 shave_image->page.x-=(ssize_t) shave_info->width; 1678 shave_image->page.y-=(ssize_t) shave_info->height; 1679 return(shave_image); 1680} 1681 1682/* 1683%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1684% % 1685% % 1686% % 1687% S p l i c e I m a g e % 1688% % 1689% % 1690% % 1691%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1692% 1693% SpliceImage() splices a solid color into the image as defined by the 1694% geometry. 1695% 1696% The format of the SpliceImage method is: 1697% 1698% Image *SpliceImage(const Image *image,const RectangleInfo *geometry, 1699% ExceptionInfo *exception) 1700% 1701% A description of each parameter follows: 1702% 1703% o image: the image. 1704% 1705% o geometry: Define the region of the image to splice with members 1706% x, y, width, and height. 1707% 1708% o exception: return any errors or warnings in this structure. 1709% 1710*/ 1711MagickExport Image *SpliceImage(const Image *image, 1712 const RectangleInfo *geometry,ExceptionInfo *exception) 1713{ 1714#define SpliceImageTag "Splice/Image" 1715 1716 CacheView 1717 *image_view, 1718 *splice_view; 1719 1720 Image 1721 *splice_image; 1722 1723 MagickBooleanType 1724 status; 1725 1726 MagickOffsetType 1727 progress; 1728 1729 RectangleInfo 1730 splice_geometry; 1731 1732 ssize_t 1733 y; 1734 1735 /* 1736 Allocate splice image. 1737 */ 1738 assert(image != (const Image *) NULL); 1739 assert(image->signature == MagickSignature); 1740 if (image->debug != MagickFalse) 1741 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1742 assert(geometry != (const RectangleInfo *) NULL); 1743 assert(exception != (ExceptionInfo *) NULL); 1744 assert(exception->signature == MagickSignature); 1745 splice_geometry=(*geometry); 1746 splice_image=CloneImage(image,image->columns+splice_geometry.width, 1747 image->rows+splice_geometry.height,MagickTrue,exception); 1748 if (splice_image == (Image *) NULL) 1749 return((Image *) NULL); 1750 if (SetImageStorageClass(splice_image,DirectClass,exception) == MagickFalse) 1751 { 1752 splice_image=DestroyImage(splice_image); 1753 return((Image *) NULL); 1754 } 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 p+=GetPixelChannels(image); 1865 q+=GetPixelChannels(splice_image); 1866 } 1867 for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++) 1868 q+=GetPixelChannels(splice_image); 1869 for ( ; x < (ssize_t) splice_image->columns; x++) 1870 { 1871 register ssize_t 1872 i; 1873 1874 if (GetPixelReadMask(image,p) == 0) 1875 { 1876 SetPixelBackgoundColor(splice_image,q); 1877 p+=GetPixelChannels(image); 1878 q+=GetPixelChannels(splice_image); 1879 continue; 1880 } 1881 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1882 { 1883 PixelChannel channel=GetPixelChannelChannel(image,i); 1884 PixelTrait traits=GetPixelChannelTraits(image,channel); 1885 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel); 1886 if ((traits == UndefinedPixelTrait) || 1887 (splice_traits == UndefinedPixelTrait)) 1888 continue; 1889 SetPixelChannel(splice_image,channel,p[i],q); 1890 } 1891 p+=GetPixelChannels(image); 1892 q+=GetPixelChannels(splice_image); 1893 } 1894 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse) 1895 status=MagickFalse; 1896 if (image->progress_monitor != (MagickProgressMonitor) NULL) 1897 { 1898 MagickBooleanType 1899 proceed; 1900 1901#if defined(MAGICKCORE_OPENMP_SUPPORT) 1902 #pragma omp critical (MagickCore_TransposeImage) 1903#endif 1904 proceed=SetImageProgress(image,SpliceImageTag,progress++, 1905 splice_image->rows); 1906 if (proceed == MagickFalse) 1907 status=MagickFalse; 1908 } 1909 } 1910#if defined(MAGICKCORE_OPENMP_SUPPORT) 1911 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 1912 magick_threads(image,splice_image,1,1) 1913#endif 1914 for (y=(ssize_t) (splice_geometry.y+splice_geometry.height); 1915 y < (ssize_t) splice_image->rows; y++) 1916 { 1917 register const Quantum 1918 *restrict p; 1919 1920 register ssize_t 1921 x; 1922 1923 register Quantum 1924 *restrict q; 1925 1926 if (status == MagickFalse) 1927 continue; 1928 p=GetCacheViewVirtualPixels(image_view,0,y-(ssize_t) splice_geometry.height, 1929 image->columns,1,exception); 1930 if ((y < 0) || (y >= (ssize_t) splice_image->rows)) 1931 continue; 1932 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1, 1933 exception); 1934 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 1935 { 1936 status=MagickFalse; 1937 continue; 1938 } 1939 for (x=0; x < splice_geometry.x; x++) 1940 { 1941 register ssize_t 1942 i; 1943 1944 if (GetPixelReadMask(image,q) == 0) 1945 { 1946 SetPixelBackgoundColor(splice_image,q); 1947 p+=GetPixelChannels(image); 1948 q+=GetPixelChannels(splice_image); 1949 continue; 1950 } 1951 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1952 { 1953 PixelChannel channel=GetPixelChannelChannel(image,i); 1954 PixelTrait traits=GetPixelChannelTraits(image,channel); 1955 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel); 1956 if ((traits == UndefinedPixelTrait) || 1957 (splice_traits == UndefinedPixelTrait)) 1958 continue; 1959 SetPixelChannel(splice_image,channel,p[i],q); 1960 } 1961 p+=GetPixelChannels(image); 1962 q+=GetPixelChannels(splice_image); 1963 } 1964 for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++) 1965 q+=GetPixelChannels(splice_image); 1966 for ( ; x < (ssize_t) splice_image->columns; x++) 1967 { 1968 register ssize_t 1969 i; 1970 1971 if (GetPixelReadMask(image,q) == 0) 1972 { 1973 SetPixelBackgoundColor(splice_image,q); 1974 p+=GetPixelChannels(image); 1975 q+=GetPixelChannels(splice_image); 1976 continue; 1977 } 1978 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1979 { 1980 PixelChannel channel=GetPixelChannelChannel(image,i); 1981 PixelTrait traits=GetPixelChannelTraits(image,channel); 1982 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel); 1983 if ((traits == UndefinedPixelTrait) || 1984 (splice_traits == UndefinedPixelTrait)) 1985 continue; 1986 SetPixelChannel(splice_image,channel,p[i],q); 1987 } 1988 p+=GetPixelChannels(image); 1989 q+=GetPixelChannels(splice_image); 1990 } 1991 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse) 1992 status=MagickFalse; 1993 if (image->progress_monitor != (MagickProgressMonitor) NULL) 1994 { 1995 MagickBooleanType 1996 proceed; 1997 1998#if defined(MAGICKCORE_OPENMP_SUPPORT) 1999 #pragma omp critical (MagickCore_TransposeImage) 2000#endif 2001 proceed=SetImageProgress(image,SpliceImageTag,progress++, 2002 splice_image->rows); 2003 if (proceed == MagickFalse) 2004 status=MagickFalse; 2005 } 2006 } 2007 splice_view=DestroyCacheView(splice_view); 2008 image_view=DestroyCacheView(image_view); 2009 if (status == MagickFalse) 2010 splice_image=DestroyImage(splice_image); 2011 return(splice_image); 2012} 2013 2014/* 2015%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2016% % 2017% % 2018% % 2019% T r a n s f o r m I m a g e % 2020% % 2021% % 2022% % 2023%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2024% 2025% TransformImage() is a convenience method that behaves like ResizeImage() or 2026% CropImage() but accepts scaling and/or cropping information as a region 2027% geometry specification. If the operation fails, the original image handle 2028% is left as is. 2029% 2030% This should only be used for single images. 2031% 2032% This function destroys what it assumes to be a single image list. 2033% If the input image is part of a larger list, all other images in that list 2034% will be simply 'lost', not destroyed. 2035% 2036% Also if the crop generates a list of images only the first image is resized. 2037% And finally if the crop succeeds and the resize failed, you will get a 2038% cropped image, as well as a 'false' or 'failed' report. 2039% 2040% This function and should probably be depreciated in favor of direct calls 2041% to CropImageToTiles() or ResizeImage(), as appropriate. 2042% 2043% The format of the TransformImage method is: 2044% 2045% MagickBooleanType TransformImage(Image **image,const char *crop_geometry, 2046% const char *image_geometry,ExceptionInfo *exception) 2047% 2048% A description of each parameter follows: 2049% 2050% o image: the image The transformed image is returned as this parameter. 2051% 2052% o crop_geometry: A crop geometry string. This geometry defines a 2053% subregion of the image to crop. 2054% 2055% o image_geometry: An image geometry string. This geometry defines the 2056% final size of the image. 2057% 2058% o exception: return any errors or warnings in this structure. 2059% 2060*/ 2061MagickExport MagickBooleanType TransformImage(Image **image, 2062 const char *crop_geometry,const char *image_geometry,ExceptionInfo *exception) 2063{ 2064 Image 2065 *resize_image, 2066 *transform_image; 2067 2068 MagickStatusType 2069 flags; 2070 2071 RectangleInfo 2072 geometry; 2073 2074 assert(image != (Image **) NULL); 2075 assert((*image)->signature == MagickSignature); 2076 if ((*image)->debug != MagickFalse) 2077 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename); 2078 transform_image=(*image); 2079 if (crop_geometry != (const char *) NULL) 2080 { 2081 Image 2082 *crop_image; 2083 2084 /* 2085 Crop image to a user specified size. 2086 */ 2087 crop_image=CropImageToTiles(*image,crop_geometry,exception); 2088 if (crop_image == (Image *) NULL) 2089 transform_image=CloneImage(*image,0,0,MagickTrue,exception); 2090 else 2091 { 2092 transform_image=DestroyImage(transform_image); 2093 transform_image=GetFirstImageInList(crop_image); 2094 } 2095 *image=transform_image; 2096 } 2097 if (image_geometry == (const char *) NULL) 2098 return(MagickTrue); 2099 2100 /* 2101 Scale image to a user specified size. 2102 */ 2103 flags=ParseRegionGeometry(transform_image,image_geometry,&geometry,exception); 2104 (void) flags; 2105 if ((transform_image->columns == geometry.width) && 2106 (transform_image->rows == geometry.height)) 2107 return(MagickTrue); 2108 resize_image=ResizeImage(transform_image,geometry.width,geometry.height, 2109 transform_image->filter,exception); 2110 if (resize_image == (Image *) NULL) 2111 return(MagickFalse); 2112 transform_image=DestroyImage(transform_image); 2113 transform_image=resize_image; 2114 *image=transform_image; 2115 return(MagickTrue); 2116} 2117 2118/* 2119%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2120% % 2121% % 2122% % 2123% T r a n s f o r m I m a g e s % 2124% % 2125% % 2126% % 2127%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2128% 2129% TransformImages() calls TransformImage() on each image of a sequence. 2130% 2131% The format of the TransformImage method is: 2132% 2133% MagickBooleanType TransformImages(Image **image, 2134% const char *crop_geometry,const char *image_geometry, 2135% ExceptionInfo *exception) 2136% 2137% A description of each parameter follows: 2138% 2139% o image: the image The transformed image is returned as this parameter. 2140% 2141% o crop_geometry: A crop geometry string. This geometry defines a 2142% subregion of the image to crop. 2143% 2144% o image_geometry: An image geometry string. This geometry defines the 2145% final size of the image. 2146% 2147% o exception: return any errors or warnings in this structure. 2148% 2149*/ 2150MagickExport MagickBooleanType TransformImages(Image **images, 2151 const char *crop_geometry,const char *image_geometry,ExceptionInfo *exception) 2152{ 2153 Image 2154 *image, 2155 **image_list, 2156 *transform_images; 2157 2158 MagickStatusType 2159 status; 2160 2161 register ssize_t 2162 i; 2163 2164 assert(images != (Image **) NULL); 2165 assert((*images)->signature == MagickSignature); 2166 if ((*images)->debug != MagickFalse) 2167 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 2168 (*images)->filename); 2169 image_list=ImageListToArray(*images,exception); 2170 if (image_list == (Image **) NULL) 2171 return(MagickFalse); 2172 status=MagickTrue; 2173 transform_images=NewImageList(); 2174 for (i=0; image_list[i] != (Image *) NULL; i++) 2175 { 2176 image=image_list[i]; 2177 status&=TransformImage(&image,crop_geometry,image_geometry,exception); 2178 AppendImageToList(&transform_images,image); 2179 } 2180 *images=transform_images; 2181 image_list=(Image **) RelinquishMagickMemory(image_list); 2182 return(status != 0 ? MagickTrue : MagickFalse); 2183} 2184 2185/* 2186%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2187% % 2188% % 2189% % 2190% T r a n s p o s e I m a g e % 2191% % 2192% % 2193% % 2194%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2195% 2196% TransposeImage() creates a horizontal mirror image by reflecting the pixels 2197% around the central y-axis while rotating them by 90 degrees. 2198% 2199% The format of the TransposeImage method is: 2200% 2201% Image *TransposeImage(const Image *image,ExceptionInfo *exception) 2202% 2203% A description of each parameter follows: 2204% 2205% o image: the image. 2206% 2207% o exception: return any errors or warnings in this structure. 2208% 2209*/ 2210MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception) 2211{ 2212#define TransposeImageTag "Transpose/Image" 2213 2214 CacheView 2215 *image_view, 2216 *transpose_view; 2217 2218 Image 2219 *transpose_image; 2220 2221 MagickBooleanType 2222 status; 2223 2224 MagickOffsetType 2225 progress; 2226 2227 RectangleInfo 2228 page; 2229 2230 ssize_t 2231 y; 2232 2233 assert(image != (const Image *) NULL); 2234 assert(image->signature == MagickSignature); 2235 if (image->debug != MagickFalse) 2236 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2237 assert(exception != (ExceptionInfo *) NULL); 2238 assert(exception->signature == MagickSignature); 2239 transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue, 2240 exception); 2241 if (transpose_image == (Image *) NULL) 2242 return((Image *) NULL); 2243 /* 2244 Transpose image. 2245 */ 2246 status=MagickTrue; 2247 progress=0; 2248 image_view=AcquireVirtualCacheView(image,exception); 2249 transpose_view=AcquireAuthenticCacheView(transpose_image,exception); 2250#if defined(MAGICKCORE_OPENMP_SUPPORT) 2251 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 2252 magick_threads(image,transpose_image,image->rows,1) 2253#endif 2254 for (y=0; y < (ssize_t) image->rows; y++) 2255 { 2256 register const Quantum 2257 *restrict p; 2258 2259 register Quantum 2260 *restrict q; 2261 2262 register ssize_t 2263 x; 2264 2265 if (status == MagickFalse) 2266 continue; 2267 p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-y-1, 2268 image->columns,1,exception); 2269 q=QueueCacheViewAuthenticPixels(transpose_view,(ssize_t) (image->rows-y-1), 2270 0,1,transpose_image->rows,exception); 2271 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 2272 { 2273 status=MagickFalse; 2274 continue; 2275 } 2276 for (x=0; x < (ssize_t) image->columns; x++) 2277 { 2278 register ssize_t 2279 i; 2280 2281 if (GetPixelReadMask(image,q) == 0) 2282 { 2283 SetPixelBackgoundColor(transpose_image,q); 2284 p+=GetPixelChannels(image); 2285 q+=GetPixelChannels(transpose_image); 2286 continue; 2287 } 2288 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 2289 { 2290 PixelChannel channel=GetPixelChannelChannel(image,i); 2291 PixelTrait traits=GetPixelChannelTraits(image,channel); 2292 PixelTrait transpose_traits=GetPixelChannelTraits(transpose_image, 2293 channel); 2294 if ((traits == UndefinedPixelTrait) || 2295 (transpose_traits == UndefinedPixelTrait)) 2296 continue; 2297 SetPixelChannel(transpose_image,channel,p[i],q); 2298 } 2299 p+=GetPixelChannels(image); 2300 q+=GetPixelChannels(transpose_image); 2301 } 2302 if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse) 2303 status=MagickFalse; 2304 if (image->progress_monitor != (MagickProgressMonitor) NULL) 2305 { 2306 MagickBooleanType 2307 proceed; 2308 2309#if defined(MAGICKCORE_OPENMP_SUPPORT) 2310 #pragma omp critical (MagickCore_TransposeImage) 2311#endif 2312 proceed=SetImageProgress(image,TransposeImageTag,progress++, 2313 image->rows); 2314 if (proceed == MagickFalse) 2315 status=MagickFalse; 2316 } 2317 } 2318 transpose_view=DestroyCacheView(transpose_view); 2319 image_view=DestroyCacheView(image_view); 2320 transpose_image->type=image->type; 2321 page=transpose_image->page; 2322 Swap(page.width,page.height); 2323 Swap(page.x,page.y); 2324 transpose_image->page=page; 2325 if (status == MagickFalse) 2326 transpose_image=DestroyImage(transpose_image); 2327 return(transpose_image); 2328} 2329 2330/* 2331%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2332% % 2333% % 2334% % 2335% T r a n s v e r s e I m a g e % 2336% % 2337% % 2338% % 2339%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2340% 2341% TransverseImage() creates a vertical mirror image by reflecting the pixels 2342% around the central x-axis while rotating them by 270 degrees. 2343% 2344% The format of the TransverseImage method is: 2345% 2346% Image *TransverseImage(const Image *image,ExceptionInfo *exception) 2347% 2348% A description of each parameter follows: 2349% 2350% o image: the image. 2351% 2352% o exception: return any errors or warnings in this structure. 2353% 2354*/ 2355MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception) 2356{ 2357#define TransverseImageTag "Transverse/Image" 2358 2359 CacheView 2360 *image_view, 2361 *transverse_view; 2362 2363 Image 2364 *transverse_image; 2365 2366 MagickBooleanType 2367 status; 2368 2369 MagickOffsetType 2370 progress; 2371 2372 RectangleInfo 2373 page; 2374 2375 ssize_t 2376 y; 2377 2378 assert(image != (const Image *) NULL); 2379 assert(image->signature == MagickSignature); 2380 if (image->debug != MagickFalse) 2381 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2382 assert(exception != (ExceptionInfo *) NULL); 2383 assert(exception->signature == MagickSignature); 2384 transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue, 2385 exception); 2386 if (transverse_image == (Image *) NULL) 2387 return((Image *) NULL); 2388 /* 2389 Transverse image. 2390 */ 2391 status=MagickTrue; 2392 progress=0; 2393 image_view=AcquireVirtualCacheView(image,exception); 2394 transverse_view=AcquireAuthenticCacheView(transverse_image,exception); 2395#if defined(MAGICKCORE_OPENMP_SUPPORT) 2396 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 2397 magick_threads(image,transverse_image,image->rows,1) 2398#endif 2399 for (y=0; y < (ssize_t) image->rows; y++) 2400 { 2401 MagickBooleanType 2402 sync; 2403 2404 register const Quantum 2405 *restrict p; 2406 2407 register Quantum 2408 *restrict q; 2409 2410 register ssize_t 2411 x; 2412 2413 if (status == MagickFalse) 2414 continue; 2415 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 2416 q=QueueCacheViewAuthenticPixels(transverse_view,(ssize_t) (image->rows-y-1), 2417 0,1,transverse_image->rows,exception); 2418 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 2419 { 2420 status=MagickFalse; 2421 continue; 2422 } 2423 q+=GetPixelChannels(transverse_image)*image->columns; 2424 for (x=0; x < (ssize_t) image->columns; x++) 2425 { 2426 register ssize_t 2427 i; 2428 2429 q-=GetPixelChannels(transverse_image); 2430 if (GetPixelReadMask(image,p) == 0) 2431 { 2432 p+=GetPixelChannels(image); 2433 continue; 2434 } 2435 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 2436 { 2437 PixelChannel channel=GetPixelChannelChannel(image,i); 2438 PixelTrait traits=GetPixelChannelTraits(image,channel); 2439 PixelTrait transverse_traits=GetPixelChannelTraits(transverse_image, 2440 channel); 2441 if ((traits == UndefinedPixelTrait) || 2442 (transverse_traits == UndefinedPixelTrait)) 2443 continue; 2444 SetPixelChannel(transverse_image,channel,p[i],q); 2445 } 2446 p+=GetPixelChannels(image); 2447 } 2448 sync=SyncCacheViewAuthenticPixels(transverse_view,exception); 2449 if (sync == MagickFalse) 2450 status=MagickFalse; 2451 if (image->progress_monitor != (MagickProgressMonitor) NULL) 2452 { 2453 MagickBooleanType 2454 proceed; 2455 2456#if defined(MAGICKCORE_OPENMP_SUPPORT) 2457 #pragma omp critical (MagickCore_TransverseImage) 2458#endif 2459 proceed=SetImageProgress(image,TransverseImageTag,progress++, 2460 image->rows); 2461 if (proceed == MagickFalse) 2462 status=MagickFalse; 2463 } 2464 } 2465 transverse_view=DestroyCacheView(transverse_view); 2466 image_view=DestroyCacheView(image_view); 2467 transverse_image->type=image->type; 2468 page=transverse_image->page; 2469 Swap(page.width,page.height); 2470 Swap(page.x,page.y); 2471 if (page.width != 0) 2472 page.x=(ssize_t) (page.width-transverse_image->columns-page.x); 2473 if (page.height != 0) 2474 page.y=(ssize_t) (page.height-transverse_image->rows-page.y); 2475 transverse_image->page=page; 2476 if (status == MagickFalse) 2477 transverse_image=DestroyImage(transverse_image); 2478 return(transverse_image); 2479} 2480 2481/* 2482%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2483% % 2484% % 2485% % 2486% T r i m I m a g e % 2487% % 2488% % 2489% % 2490%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2491% 2492% TrimImage() trims pixels from the image edges. It allocates the memory 2493% necessary for the new Image structure and returns a pointer to the new 2494% image. 2495% 2496% The format of the TrimImage method is: 2497% 2498% Image *TrimImage(const Image *image,ExceptionInfo *exception) 2499% 2500% A description of each parameter follows: 2501% 2502% o image: the image. 2503% 2504% o exception: return any errors or warnings in this structure. 2505% 2506*/ 2507MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception) 2508{ 2509 RectangleInfo 2510 geometry; 2511 2512 assert(image != (const Image *) NULL); 2513 assert(image->signature == MagickSignature); 2514 if (image->debug != MagickFalse) 2515 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2516 geometry=GetImageBoundingBox(image,exception); 2517 if ((geometry.width == 0) || (geometry.height == 0)) 2518 { 2519 Image 2520 *crop_image; 2521 2522 crop_image=CloneImage(image,1,1,MagickTrue,exception); 2523 if (crop_image == (Image *) NULL) 2524 return((Image *) NULL); 2525 crop_image->background_color.alpha=(Quantum) TransparentAlpha; 2526 crop_image->alpha_trait=BlendPixelTrait; 2527 (void) SetImageBackgroundColor(crop_image,exception); 2528 crop_image->page=image->page; 2529 crop_image->page.x=(-1); 2530 crop_image->page.y=(-1); 2531 return(crop_image); 2532 } 2533 geometry.x+=image->page.x; 2534 geometry.y+=image->page.y; 2535 return(CropImage(image,&geometry,exception)); 2536} 2537