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-2016 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 == MagickCoreSignature); 106 assert(exception != (ExceptionInfo *) NULL); 107 assert(exception->signature == MagickCoreSignature); 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 == MagickCoreSignature); 216 if (image->debug != MagickFalse) 217 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 218 assert(exception != (ExceptionInfo *) NULL); 219 assert(exception->signature == MagickCoreSignature); 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 *magick_restrict p; 260 261 register ssize_t 262 x; 263 264 register Quantum 265 *magick_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 *magick_restrict p; 324 325 register ssize_t 326 x; 327 328 register Quantum 329 *magick_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 == MagickCoreSignature); 433 if (images->debug != MagickFalse) 434 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename); 435 assert(exception != (ExceptionInfo *) NULL); 436 assert(exception->signature == MagickCoreSignature); 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 *magick_restrict p; 459 460 register ssize_t 461 x; 462 463 register Quantum 464 *magick_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 == MagickCoreSignature); 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 == MagickCoreSignature); 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 *magick_restrict p; 677 678 register Quantum 679 *magick_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 == MagickCoreSignature); 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 == MagickCoreSignature); 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 == MagickCoreSignature); 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 *magick_restrict p; 1030 1031 register Quantum 1032 *magick_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 == MagickCoreSignature); 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 == MagickCoreSignature); 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 == MagickCoreSignature); 1206 if (image->debug != MagickFalse) 1207 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1208 assert(exception != (ExceptionInfo *) NULL); 1209 assert(exception->signature == MagickCoreSignature); 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 *magick_restrict p; 1229 1230 register Quantum 1231 *magick_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 == MagickCoreSignature); 1347 if (image->debug != MagickFalse) 1348 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1349 assert(exception != (ExceptionInfo *) NULL); 1350 assert(exception->signature == MagickCoreSignature); 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 *magick_restrict p; 1370 1371 register ssize_t 1372 x; 1373 1374 register Quantum 1375 *magick_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 MagickBooleanType CopyImageRegion(Image *destination,const Image *source, const size_t columns,const size_t rows,const ssize_t sx,const ssize_t sy, 1468 const ssize_t dx,const ssize_t dy,ExceptionInfo *exception) 1469{ 1470 CacheView 1471 *source_view, 1472 *destination_view; 1473 1474 MagickBooleanType 1475 status; 1476 1477 ssize_t 1478 y; 1479 1480 if (columns == 0) 1481 return(MagickTrue); 1482 status=MagickTrue; 1483 source_view=AcquireVirtualCacheView(source,exception); 1484 destination_view=AcquireAuthenticCacheView(destination,exception); 1485#if defined(MAGICKCORE_OPENMP_SUPPORT) 1486 #pragma omp parallel for schedule(static,4) shared(status) \ 1487 magick_threads(source,destination,rows,1) 1488#endif 1489 for (y=0; y < (ssize_t) rows; y++) 1490 { 1491 MagickBooleanType 1492 sync; 1493 1494 register const Quantum 1495 *magick_restrict p; 1496 1497 register Quantum 1498 *magick_restrict q; 1499 1500 register ssize_t 1501 x; 1502 1503 /* 1504 Transfer scanline. 1505 */ 1506 if (status == MagickFalse) 1507 continue; 1508 p=GetCacheViewVirtualPixels(source_view,sx,sy+y,columns,1,exception); 1509 q=GetCacheViewAuthenticPixels(destination_view,dx,dy+y,columns,1,exception); 1510 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 1511 { 1512 status=MagickFalse; 1513 continue; 1514 } 1515 for (x=0; x < (ssize_t) columns; x++) 1516 { 1517 register ssize_t 1518 i; 1519 1520 if (GetPixelReadMask(source,p) == 0) 1521 { 1522 SetPixelBackgoundColor(destination,q); 1523 p+=GetPixelChannels(source); 1524 q+=GetPixelChannels(destination); 1525 continue; 1526 } 1527 for (i=0; i < (ssize_t) GetPixelChannels(source); i++) 1528 { 1529 PixelChannel channel=GetPixelChannelChannel(source,i); 1530 PixelTrait source_traits=GetPixelChannelTraits(source,channel); 1531 PixelTrait destination_traits=GetPixelChannelTraits(destination, 1532 channel); 1533 if ((source_traits == UndefinedPixelTrait) || 1534 (destination_traits == UndefinedPixelTrait)) 1535 continue; 1536 SetPixelChannel(destination,channel,p[i],q); 1537 } 1538 p+=GetPixelChannels(source); 1539 q+=GetPixelChannels(destination); 1540 } 1541 sync=SyncCacheViewAuthenticPixels(destination_view,exception); 1542 if (sync == MagickFalse) 1543 status=MagickFalse; 1544 } 1545 destination_view=DestroyCacheView(destination_view); 1546 source_view=DestroyCacheView(source_view); 1547 return(status); 1548} 1549 1550MagickExport Image *RollImage(const Image *image,const ssize_t x_offset, 1551 const ssize_t y_offset,ExceptionInfo *exception) 1552{ 1553#define RollImageTag "Roll/Image" 1554 1555 Image 1556 *roll_image; 1557 1558 MagickStatusType 1559 status; 1560 1561 RectangleInfo 1562 offset; 1563 1564 /* 1565 Initialize roll image attributes. 1566 */ 1567 assert(image != (const Image *) NULL); 1568 assert(image->signature == MagickCoreSignature); 1569 if (image->debug != MagickFalse) 1570 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1571 assert(exception != (ExceptionInfo *) NULL); 1572 assert(exception->signature == MagickCoreSignature); 1573 roll_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception); 1574 if (roll_image == (Image *) NULL) 1575 return((Image *) NULL); 1576 offset.x=x_offset; 1577 offset.y=y_offset; 1578 while (offset.x < 0) 1579 offset.x+=(ssize_t) image->columns; 1580 while (offset.x >= (ssize_t) image->columns) 1581 offset.x-=(ssize_t) image->columns; 1582 while (offset.y < 0) 1583 offset.y+=(ssize_t) image->rows; 1584 while (offset.y >= (ssize_t) image->rows) 1585 offset.y-=(ssize_t) image->rows; 1586 /* 1587 Roll image. 1588 */ 1589 status=CopyImageRegion(roll_image,image,(size_t) offset.x, 1590 (size_t) offset.y,(ssize_t) image->columns-offset.x,(ssize_t) image->rows- 1591 offset.y,0,0,exception); 1592 (void) SetImageProgress(image,RollImageTag,0,3); 1593 status&=CopyImageRegion(roll_image,image,image->columns-offset.x, 1594 (size_t) offset.y,0,(ssize_t) image->rows-offset.y,offset.x,0, 1595 exception); 1596 (void) SetImageProgress(image,RollImageTag,1,3); 1597 status&=CopyImageRegion(roll_image,image,(size_t) offset.x,image->rows- 1598 offset.y,(ssize_t) image->columns-offset.x,0,0,offset.y,exception); 1599 (void) SetImageProgress(image,RollImageTag,2,3); 1600 status&=CopyImageRegion(roll_image,image,image->columns-offset.x,image->rows- 1601 offset.y,0,0,offset.x,offset.y,exception); 1602 (void) SetImageProgress(image,RollImageTag,3,3); 1603 roll_image->type=image->type; 1604 if (status == MagickFalse) 1605 roll_image=DestroyImage(roll_image); 1606 return(roll_image); 1607} 1608 1609/* 1610%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1611% % 1612% % 1613% % 1614% S h a v e I m a g e % 1615% % 1616% % 1617% % 1618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1619% 1620% ShaveImage() shaves pixels from the image edges. It allocates the memory 1621% necessary for the new Image structure and returns a pointer to the new 1622% image. 1623% 1624% The format of the ShaveImage method is: 1625% 1626% Image *ShaveImage(const Image *image,const RectangleInfo *shave_info, 1627% ExceptionInfo *exception) 1628% 1629% A description of each parameter follows: 1630% 1631% o shave_image: Method ShaveImage returns a pointer to the shaved 1632% image. A null image is returned if there is a memory shortage or 1633% if the image width or height is zero. 1634% 1635% o image: the image. 1636% 1637% o shave_info: Specifies a pointer to a RectangleInfo which defines the 1638% region of the image to crop. 1639% 1640% o exception: return any errors or warnings in this structure. 1641% 1642*/ 1643MagickExport Image *ShaveImage(const Image *image, 1644 const RectangleInfo *shave_info,ExceptionInfo *exception) 1645{ 1646 Image 1647 *shave_image; 1648 1649 RectangleInfo 1650 geometry; 1651 1652 assert(image != (const Image *) NULL); 1653 assert(image->signature == MagickCoreSignature); 1654 if (image->debug != MagickFalse) 1655 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1656 if (((2*shave_info->width) >= image->columns) || 1657 ((2*shave_info->height) >= image->rows)) 1658 ThrowImageException(OptionWarning,"GeometryDoesNotContainImage"); 1659 SetGeometry(image,&geometry); 1660 geometry.width-=2*shave_info->width; 1661 geometry.height-=2*shave_info->height; 1662 geometry.x=(ssize_t) shave_info->width+image->page.x; 1663 geometry.y=(ssize_t) shave_info->height+image->page.y; 1664 shave_image=CropImage(image,&geometry,exception); 1665 if (shave_image == (Image *) NULL) 1666 return((Image *) NULL); 1667 shave_image->page.width-=2*shave_info->width; 1668 shave_image->page.height-=2*shave_info->height; 1669 shave_image->page.x-=(ssize_t) shave_info->width; 1670 shave_image->page.y-=(ssize_t) shave_info->height; 1671 return(shave_image); 1672} 1673 1674/* 1675%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1676% % 1677% % 1678% % 1679% S p l i c e I m a g e % 1680% % 1681% % 1682% % 1683%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1684% 1685% SpliceImage() splices a solid color into the image as defined by the 1686% geometry. 1687% 1688% The format of the SpliceImage method is: 1689% 1690% Image *SpliceImage(const Image *image,const RectangleInfo *geometry, 1691% ExceptionInfo *exception) 1692% 1693% A description of each parameter follows: 1694% 1695% o image: the image. 1696% 1697% o geometry: Define the region of the image to splice with members 1698% x, y, width, and height. 1699% 1700% o exception: return any errors or warnings in this structure. 1701% 1702*/ 1703MagickExport Image *SpliceImage(const Image *image, 1704 const RectangleInfo *geometry,ExceptionInfo *exception) 1705{ 1706#define SpliceImageTag "Splice/Image" 1707 1708 CacheView 1709 *image_view, 1710 *splice_view; 1711 1712 Image 1713 *splice_image; 1714 1715 MagickBooleanType 1716 status; 1717 1718 MagickOffsetType 1719 progress; 1720 1721 RectangleInfo 1722 splice_geometry; 1723 1724 ssize_t 1725 columns, 1726 y; 1727 1728 /* 1729 Allocate splice image. 1730 */ 1731 assert(image != (const Image *) NULL); 1732 assert(image->signature == MagickCoreSignature); 1733 if (image->debug != MagickFalse) 1734 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 1735 assert(geometry != (const RectangleInfo *) NULL); 1736 assert(exception != (ExceptionInfo *) NULL); 1737 assert(exception->signature == MagickCoreSignature); 1738 splice_geometry=(*geometry); 1739 splice_image=CloneImage(image,image->columns+splice_geometry.width, 1740 image->rows+splice_geometry.height,MagickTrue,exception); 1741 if (splice_image == (Image *) NULL) 1742 return((Image *) NULL); 1743 if (SetImageStorageClass(splice_image,DirectClass,exception) == MagickFalse) 1744 { 1745 splice_image=DestroyImage(splice_image); 1746 return((Image *) NULL); 1747 } 1748 if ((IsPixelInfoGray(&splice_image->background_color) == MagickFalse) && 1749 (IsGrayColorspace(splice_image->colorspace) != MagickFalse)) 1750 (void) SetImageColorspace(splice_image,sRGBColorspace,exception); 1751 if ((splice_image->background_color.alpha_trait != UndefinedPixelTrait) && 1752 (splice_image->alpha_trait == UndefinedPixelTrait)) 1753 (void) SetImageAlpha(splice_image,OpaqueAlpha,exception); 1754 (void) SetImageBackgroundColor(splice_image,exception); 1755 /* 1756 Respect image geometry. 1757 */ 1758 switch (image->gravity) 1759 { 1760 default: 1761 case UndefinedGravity: 1762 case NorthWestGravity: 1763 break; 1764 case NorthGravity: 1765 { 1766 splice_geometry.x+=(ssize_t) splice_geometry.width/2; 1767 break; 1768 } 1769 case NorthEastGravity: 1770 { 1771 splice_geometry.x+=(ssize_t) splice_geometry.width; 1772 break; 1773 } 1774 case WestGravity: 1775 { 1776 splice_geometry.y+=(ssize_t) splice_geometry.width/2; 1777 break; 1778 } 1779 case CenterGravity: 1780 { 1781 splice_geometry.x+=(ssize_t) splice_geometry.width/2; 1782 splice_geometry.y+=(ssize_t) splice_geometry.height/2; 1783 break; 1784 } 1785 case EastGravity: 1786 { 1787 splice_geometry.x+=(ssize_t) splice_geometry.width; 1788 splice_geometry.y+=(ssize_t) splice_geometry.height/2; 1789 break; 1790 } 1791 case SouthWestGravity: 1792 { 1793 splice_geometry.y+=(ssize_t) splice_geometry.height; 1794 break; 1795 } 1796 case SouthGravity: 1797 { 1798 splice_geometry.x+=(ssize_t) splice_geometry.width/2; 1799 splice_geometry.y+=(ssize_t) splice_geometry.height; 1800 break; 1801 } 1802 case SouthEastGravity: 1803 { 1804 splice_geometry.x+=(ssize_t) splice_geometry.width; 1805 splice_geometry.y+=(ssize_t) splice_geometry.height; 1806 break; 1807 } 1808 } 1809 /* 1810 Splice image. 1811 */ 1812 status=MagickTrue; 1813 progress=0; 1814 columns=MagickMin(splice_geometry.x,(ssize_t) splice_image->columns); 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 *magick_restrict p; 1825 1826 register ssize_t 1827 x; 1828 1829 register Quantum 1830 *magick_restrict q; 1831 1832 if (status == MagickFalse) 1833 continue; 1834 p=GetCacheViewVirtualPixels(image_view,0,y,splice_image->columns,1, 1835 exception); 1836 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1, 1837 exception); 1838 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 1839 { 1840 status=MagickFalse; 1841 continue; 1842 } 1843 for (x=0; x < columns; x++) 1844 { 1845 register ssize_t 1846 i; 1847 1848 if (GetPixelReadMask(image,p) == 0) 1849 { 1850 SetPixelBackgoundColor(splice_image,q); 1851 p+=GetPixelChannels(image); 1852 q+=GetPixelChannels(splice_image); 1853 continue; 1854 } 1855 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1856 { 1857 PixelChannel channel=GetPixelChannelChannel(image,i); 1858 PixelTrait traits=GetPixelChannelTraits(image,channel); 1859 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel); 1860 if ((traits == UndefinedPixelTrait) || 1861 (splice_traits == UndefinedPixelTrait)) 1862 continue; 1863 SetPixelChannel(splice_image,channel,p[i],q); 1864 } 1865 SetPixelRed(splice_image,GetPixelRed(image,p),q); 1866 SetPixelGreen(splice_image,GetPixelGreen(image,p),q); 1867 SetPixelBlue(splice_image,GetPixelBlue(image,p),q); 1868 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q); 1869 p+=GetPixelChannels(image); 1870 q+=GetPixelChannels(splice_image); 1871 } 1872 for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++) 1873 q+=GetPixelChannels(splice_image); 1874 for ( ; x < (ssize_t) splice_image->columns; x++) 1875 { 1876 register ssize_t 1877 i; 1878 1879 if (GetPixelReadMask(image,p) == 0) 1880 { 1881 SetPixelBackgoundColor(splice_image,q); 1882 p+=GetPixelChannels(image); 1883 q+=GetPixelChannels(splice_image); 1884 continue; 1885 } 1886 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1887 { 1888 PixelChannel channel=GetPixelChannelChannel(image,i); 1889 PixelTrait traits=GetPixelChannelTraits(image,channel); 1890 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel); 1891 if ((traits == UndefinedPixelTrait) || 1892 (splice_traits == UndefinedPixelTrait)) 1893 continue; 1894 SetPixelChannel(splice_image,channel,p[i],q); 1895 } 1896 SetPixelRed(splice_image,GetPixelRed(image,p),q); 1897 SetPixelGreen(splice_image,GetPixelGreen(image,p),q); 1898 SetPixelBlue(splice_image,GetPixelBlue(image,p),q); 1899 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q); 1900 p+=GetPixelChannels(image); 1901 q+=GetPixelChannels(splice_image); 1902 } 1903 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse) 1904 status=MagickFalse; 1905 if (image->progress_monitor != (MagickProgressMonitor) NULL) 1906 { 1907 MagickBooleanType 1908 proceed; 1909 1910#if defined(MAGICKCORE_OPENMP_SUPPORT) 1911 #pragma omp critical (MagickCore_TransposeImage) 1912#endif 1913 proceed=SetImageProgress(image,SpliceImageTag,progress++, 1914 splice_image->rows); 1915 if (proceed == MagickFalse) 1916 status=MagickFalse; 1917 } 1918 } 1919#if defined(MAGICKCORE_OPENMP_SUPPORT) 1920 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 1921 magick_threads(image,splice_image,1,1) 1922#endif 1923 for (y=(ssize_t) (splice_geometry.y+splice_geometry.height); 1924 y < (ssize_t) splice_image->rows; y++) 1925 { 1926 register const Quantum 1927 *magick_restrict p; 1928 1929 register ssize_t 1930 x; 1931 1932 register Quantum 1933 *magick_restrict q; 1934 1935 if (status == MagickFalse) 1936 continue; 1937 if ((y < 0) || (y >= (ssize_t)splice_image->rows)) 1938 continue; 1939 p=GetCacheViewVirtualPixels(image_view,0,y-(ssize_t) splice_geometry.height, 1940 splice_image->columns,1,exception); 1941 q=QueueCacheViewAuthenticPixels(splice_view,0,y,splice_image->columns,1, 1942 exception); 1943 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 1944 { 1945 status=MagickFalse; 1946 continue; 1947 } 1948 for (x=0; x < columns; x++) 1949 { 1950 register ssize_t 1951 i; 1952 1953 if (GetPixelReadMask(image,q) == 0) 1954 { 1955 SetPixelBackgoundColor(splice_image,q); 1956 p+=GetPixelChannels(image); 1957 q+=GetPixelChannels(splice_image); 1958 continue; 1959 } 1960 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1961 { 1962 PixelChannel channel=GetPixelChannelChannel(image,i); 1963 PixelTrait traits=GetPixelChannelTraits(image,channel); 1964 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel); 1965 if ((traits == UndefinedPixelTrait) || 1966 (splice_traits == UndefinedPixelTrait)) 1967 continue; 1968 SetPixelChannel(splice_image,channel,p[i],q); 1969 } 1970 SetPixelRed(splice_image,GetPixelRed(image,p),q); 1971 SetPixelGreen(splice_image,GetPixelGreen(image,p),q); 1972 SetPixelBlue(splice_image,GetPixelBlue(image,p),q); 1973 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q); 1974 p+=GetPixelChannels(image); 1975 q+=GetPixelChannels(splice_image); 1976 } 1977 for ( ; x < (ssize_t) (splice_geometry.x+splice_geometry.width); x++) 1978 q+=GetPixelChannels(splice_image); 1979 for ( ; x < (ssize_t) splice_image->columns; x++) 1980 { 1981 register ssize_t 1982 i; 1983 1984 if (GetPixelReadMask(image,q) == 0) 1985 { 1986 SetPixelBackgoundColor(splice_image,q); 1987 p+=GetPixelChannels(image); 1988 q+=GetPixelChannels(splice_image); 1989 continue; 1990 } 1991 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1992 { 1993 PixelChannel channel=GetPixelChannelChannel(image,i); 1994 PixelTrait traits=GetPixelChannelTraits(image,channel); 1995 PixelTrait splice_traits=GetPixelChannelTraits(splice_image,channel); 1996 if ((traits == UndefinedPixelTrait) || 1997 (splice_traits == UndefinedPixelTrait)) 1998 continue; 1999 SetPixelChannel(splice_image,channel,p[i],q); 2000 } 2001 SetPixelRed(splice_image,GetPixelRed(image,p),q); 2002 SetPixelGreen(splice_image,GetPixelGreen(image,p),q); 2003 SetPixelBlue(splice_image,GetPixelBlue(image,p),q); 2004 SetPixelAlpha(splice_image,GetPixelAlpha(image,p),q); 2005 p+=GetPixelChannels(image); 2006 q+=GetPixelChannels(splice_image); 2007 } 2008 if (SyncCacheViewAuthenticPixels(splice_view,exception) == MagickFalse) 2009 status=MagickFalse; 2010 if (image->progress_monitor != (MagickProgressMonitor) NULL) 2011 { 2012 MagickBooleanType 2013 proceed; 2014 2015#if defined(MAGICKCORE_OPENMP_SUPPORT) 2016 #pragma omp critical (MagickCore_TransposeImage) 2017#endif 2018 proceed=SetImageProgress(image,SpliceImageTag,progress++, 2019 splice_image->rows); 2020 if (proceed == MagickFalse) 2021 status=MagickFalse; 2022 } 2023 } 2024 splice_view=DestroyCacheView(splice_view); 2025 image_view=DestroyCacheView(image_view); 2026 if (status == MagickFalse) 2027 splice_image=DestroyImage(splice_image); 2028 return(splice_image); 2029} 2030 2031/* 2032%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2033% % 2034% % 2035% % 2036% T r a n s f o r m I m a g e % 2037% % 2038% % 2039% % 2040%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2041% 2042% TransformImage() is a convenience method that behaves like ResizeImage() or 2043% CropImage() but accepts scaling and/or cropping information as a region 2044% geometry specification. If the operation fails, the original image handle 2045% is left as is. 2046% 2047% This should only be used for single images. 2048% 2049% This function destroys what it assumes to be a single image list. 2050% If the input image is part of a larger list, all other images in that list 2051% will be simply 'lost', not destroyed. 2052% 2053% Also if the crop generates a list of images only the first image is resized. 2054% And finally if the crop succeeds and the resize failed, you will get a 2055% cropped image, as well as a 'false' or 'failed' report. 2056% 2057% This function and should probably be deprecated in favor of direct calls 2058% to CropImageToTiles() or ResizeImage(), as appropriate. 2059% 2060% The format of the TransformImage method is: 2061% 2062% MagickBooleanType TransformImage(Image **image,const char *crop_geometry, 2063% const char *image_geometry,ExceptionInfo *exception) 2064% 2065% A description of each parameter follows: 2066% 2067% o image: the image The transformed image is returned as this parameter. 2068% 2069% o crop_geometry: A crop geometry string. This geometry defines a 2070% subregion of the image to crop. 2071% 2072% o image_geometry: An image geometry string. This geometry defines the 2073% final size of the image. 2074% 2075% o exception: return any errors or warnings in this structure. 2076% 2077*/ 2078MagickPrivate MagickBooleanType TransformImage(Image **image, 2079 const char *crop_geometry,const char *image_geometry,ExceptionInfo *exception) 2080{ 2081 Image 2082 *resize_image, 2083 *transform_image; 2084 2085 RectangleInfo 2086 geometry; 2087 2088 assert(image != (Image **) NULL); 2089 assert((*image)->signature == MagickCoreSignature); 2090 if ((*image)->debug != MagickFalse) 2091 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",(*image)->filename); 2092 transform_image=(*image); 2093 if (crop_geometry != (const char *) NULL) 2094 { 2095 Image 2096 *crop_image; 2097 2098 /* 2099 Crop image to a user specified size. 2100 */ 2101 crop_image=CropImageToTiles(*image,crop_geometry,exception); 2102 if (crop_image == (Image *) NULL) 2103 transform_image=CloneImage(*image,0,0,MagickTrue,exception); 2104 else 2105 { 2106 transform_image=DestroyImage(transform_image); 2107 transform_image=GetFirstImageInList(crop_image); 2108 } 2109 *image=transform_image; 2110 } 2111 if (image_geometry == (const char *) NULL) 2112 return(MagickTrue); 2113 2114 /* 2115 Scale image to a user specified size. 2116 */ 2117 (void) ParseRegionGeometry(transform_image,image_geometry,&geometry,exception); 2118 if ((transform_image->columns == geometry.width) && 2119 (transform_image->rows == geometry.height)) 2120 return(MagickTrue); 2121 resize_image=ResizeImage(transform_image,geometry.width,geometry.height, 2122 transform_image->filter,exception); 2123 if (resize_image == (Image *) NULL) 2124 return(MagickFalse); 2125 transform_image=DestroyImage(transform_image); 2126 transform_image=resize_image; 2127 *image=transform_image; 2128 return(MagickTrue); 2129} 2130 2131/* 2132%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2133% % 2134% % 2135% % 2136% T r a n s p o s e I m a g e % 2137% % 2138% % 2139% % 2140%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2141% 2142% TransposeImage() creates a horizontal mirror image by reflecting the pixels 2143% around the central y-axis while rotating them by 90 degrees. 2144% 2145% The format of the TransposeImage method is: 2146% 2147% Image *TransposeImage(const Image *image,ExceptionInfo *exception) 2148% 2149% A description of each parameter follows: 2150% 2151% o image: the image. 2152% 2153% o exception: return any errors or warnings in this structure. 2154% 2155*/ 2156MagickExport Image *TransposeImage(const Image *image,ExceptionInfo *exception) 2157{ 2158#define TransposeImageTag "Transpose/Image" 2159 2160 CacheView 2161 *image_view, 2162 *transpose_view; 2163 2164 Image 2165 *transpose_image; 2166 2167 MagickBooleanType 2168 status; 2169 2170 MagickOffsetType 2171 progress; 2172 2173 RectangleInfo 2174 page; 2175 2176 ssize_t 2177 y; 2178 2179 assert(image != (const Image *) NULL); 2180 assert(image->signature == MagickCoreSignature); 2181 if (image->debug != MagickFalse) 2182 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2183 assert(exception != (ExceptionInfo *) NULL); 2184 assert(exception->signature == MagickCoreSignature); 2185 transpose_image=CloneImage(image,image->rows,image->columns,MagickTrue, 2186 exception); 2187 if (transpose_image == (Image *) NULL) 2188 return((Image *) NULL); 2189 /* 2190 Transpose image. 2191 */ 2192 status=MagickTrue; 2193 progress=0; 2194 image_view=AcquireVirtualCacheView(image,exception); 2195 transpose_view=AcquireAuthenticCacheView(transpose_image,exception); 2196#if defined(MAGICKCORE_OPENMP_SUPPORT) 2197 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 2198 magick_threads(image,transpose_image,image->rows,1) 2199#endif 2200 for (y=0; y < (ssize_t) image->rows; y++) 2201 { 2202 register const Quantum 2203 *magick_restrict p; 2204 2205 register Quantum 2206 *magick_restrict q; 2207 2208 register ssize_t 2209 x; 2210 2211 if (status == MagickFalse) 2212 continue; 2213 p=GetCacheViewVirtualPixels(image_view,0,(ssize_t) image->rows-y-1, 2214 image->columns,1,exception); 2215 q=QueueCacheViewAuthenticPixels(transpose_view,(ssize_t) (image->rows-y-1), 2216 0,1,transpose_image->rows,exception); 2217 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 2218 { 2219 status=MagickFalse; 2220 continue; 2221 } 2222 for (x=0; x < (ssize_t) image->columns; x++) 2223 { 2224 register ssize_t 2225 i; 2226 2227 if (GetPixelReadMask(image,q) == 0) 2228 { 2229 SetPixelBackgoundColor(transpose_image,q); 2230 p+=GetPixelChannels(image); 2231 q+=GetPixelChannels(transpose_image); 2232 continue; 2233 } 2234 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 2235 { 2236 PixelChannel channel=GetPixelChannelChannel(image,i); 2237 PixelTrait traits=GetPixelChannelTraits(image,channel); 2238 PixelTrait transpose_traits=GetPixelChannelTraits(transpose_image, 2239 channel); 2240 if ((traits == UndefinedPixelTrait) || 2241 (transpose_traits == UndefinedPixelTrait)) 2242 continue; 2243 SetPixelChannel(transpose_image,channel,p[i],q); 2244 } 2245 p+=GetPixelChannels(image); 2246 q+=GetPixelChannels(transpose_image); 2247 } 2248 if (SyncCacheViewAuthenticPixels(transpose_view,exception) == MagickFalse) 2249 status=MagickFalse; 2250 if (image->progress_monitor != (MagickProgressMonitor) NULL) 2251 { 2252 MagickBooleanType 2253 proceed; 2254 2255#if defined(MAGICKCORE_OPENMP_SUPPORT) 2256 #pragma omp critical (MagickCore_TransposeImage) 2257#endif 2258 proceed=SetImageProgress(image,TransposeImageTag,progress++, 2259 image->rows); 2260 if (proceed == MagickFalse) 2261 status=MagickFalse; 2262 } 2263 } 2264 transpose_view=DestroyCacheView(transpose_view); 2265 image_view=DestroyCacheView(image_view); 2266 transpose_image->type=image->type; 2267 page=transpose_image->page; 2268 Swap(page.width,page.height); 2269 Swap(page.x,page.y); 2270 transpose_image->page=page; 2271 if (status == MagickFalse) 2272 transpose_image=DestroyImage(transpose_image); 2273 return(transpose_image); 2274} 2275 2276/* 2277%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2278% % 2279% % 2280% % 2281% T r a n s v e r s e I m a g e % 2282% % 2283% % 2284% % 2285%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2286% 2287% TransverseImage() creates a vertical mirror image by reflecting the pixels 2288% around the central x-axis while rotating them by 270 degrees. 2289% 2290% The format of the TransverseImage method is: 2291% 2292% Image *TransverseImage(const Image *image,ExceptionInfo *exception) 2293% 2294% A description of each parameter follows: 2295% 2296% o image: the image. 2297% 2298% o exception: return any errors or warnings in this structure. 2299% 2300*/ 2301MagickExport Image *TransverseImage(const Image *image,ExceptionInfo *exception) 2302{ 2303#define TransverseImageTag "Transverse/Image" 2304 2305 CacheView 2306 *image_view, 2307 *transverse_view; 2308 2309 Image 2310 *transverse_image; 2311 2312 MagickBooleanType 2313 status; 2314 2315 MagickOffsetType 2316 progress; 2317 2318 RectangleInfo 2319 page; 2320 2321 ssize_t 2322 y; 2323 2324 assert(image != (const Image *) NULL); 2325 assert(image->signature == MagickCoreSignature); 2326 if (image->debug != MagickFalse) 2327 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2328 assert(exception != (ExceptionInfo *) NULL); 2329 assert(exception->signature == MagickCoreSignature); 2330 transverse_image=CloneImage(image,image->rows,image->columns,MagickTrue, 2331 exception); 2332 if (transverse_image == (Image *) NULL) 2333 return((Image *) NULL); 2334 /* 2335 Transverse image. 2336 */ 2337 status=MagickTrue; 2338 progress=0; 2339 image_view=AcquireVirtualCacheView(image,exception); 2340 transverse_view=AcquireAuthenticCacheView(transverse_image,exception); 2341#if defined(MAGICKCORE_OPENMP_SUPPORT) 2342 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 2343 magick_threads(image,transverse_image,image->rows,1) 2344#endif 2345 for (y=0; y < (ssize_t) image->rows; y++) 2346 { 2347 MagickBooleanType 2348 sync; 2349 2350 register const Quantum 2351 *magick_restrict p; 2352 2353 register Quantum 2354 *magick_restrict q; 2355 2356 register ssize_t 2357 x; 2358 2359 if (status == MagickFalse) 2360 continue; 2361 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 2362 q=QueueCacheViewAuthenticPixels(transverse_view,(ssize_t) (image->rows-y-1), 2363 0,1,transverse_image->rows,exception); 2364 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 2365 { 2366 status=MagickFalse; 2367 continue; 2368 } 2369 q+=GetPixelChannels(transverse_image)*image->columns; 2370 for (x=0; x < (ssize_t) image->columns; x++) 2371 { 2372 register ssize_t 2373 i; 2374 2375 q-=GetPixelChannels(transverse_image); 2376 if (GetPixelReadMask(image,p) == 0) 2377 { 2378 p+=GetPixelChannels(image); 2379 continue; 2380 } 2381 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 2382 { 2383 PixelChannel channel=GetPixelChannelChannel(image,i); 2384 PixelTrait traits=GetPixelChannelTraits(image,channel); 2385 PixelTrait transverse_traits=GetPixelChannelTraits(transverse_image, 2386 channel); 2387 if ((traits == UndefinedPixelTrait) || 2388 (transverse_traits == UndefinedPixelTrait)) 2389 continue; 2390 SetPixelChannel(transverse_image,channel,p[i],q); 2391 } 2392 p+=GetPixelChannels(image); 2393 } 2394 sync=SyncCacheViewAuthenticPixels(transverse_view,exception); 2395 if (sync == MagickFalse) 2396 status=MagickFalse; 2397 if (image->progress_monitor != (MagickProgressMonitor) NULL) 2398 { 2399 MagickBooleanType 2400 proceed; 2401 2402#if defined(MAGICKCORE_OPENMP_SUPPORT) 2403 #pragma omp critical (MagickCore_TransverseImage) 2404#endif 2405 proceed=SetImageProgress(image,TransverseImageTag,progress++, 2406 image->rows); 2407 if (proceed == MagickFalse) 2408 status=MagickFalse; 2409 } 2410 } 2411 transverse_view=DestroyCacheView(transverse_view); 2412 image_view=DestroyCacheView(image_view); 2413 transverse_image->type=image->type; 2414 page=transverse_image->page; 2415 Swap(page.width,page.height); 2416 Swap(page.x,page.y); 2417 if (page.width != 0) 2418 page.x=(ssize_t) (page.width-transverse_image->columns-page.x); 2419 if (page.height != 0) 2420 page.y=(ssize_t) (page.height-transverse_image->rows-page.y); 2421 transverse_image->page=page; 2422 if (status == MagickFalse) 2423 transverse_image=DestroyImage(transverse_image); 2424 return(transverse_image); 2425} 2426 2427/* 2428%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2429% % 2430% % 2431% % 2432% T r i m I m a g e % 2433% % 2434% % 2435% % 2436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 2437% 2438% TrimImage() trims pixels from the image edges. It allocates the memory 2439% necessary for the new Image structure and returns a pointer to the new 2440% image. 2441% 2442% The format of the TrimImage method is: 2443% 2444% Image *TrimImage(const Image *image,ExceptionInfo *exception) 2445% 2446% A description of each parameter follows: 2447% 2448% o image: the image. 2449% 2450% o exception: return any errors or warnings in this structure. 2451% 2452*/ 2453MagickExport Image *TrimImage(const Image *image,ExceptionInfo *exception) 2454{ 2455 RectangleInfo 2456 geometry; 2457 2458 assert(image != (const Image *) NULL); 2459 assert(image->signature == MagickCoreSignature); 2460 if (image->debug != MagickFalse) 2461 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 2462 geometry=GetImageBoundingBox(image,exception); 2463 if ((geometry.width == 0) || (geometry.height == 0)) 2464 { 2465 Image 2466 *crop_image; 2467 2468 crop_image=CloneImage(image,1,1,MagickTrue,exception); 2469 if (crop_image == (Image *) NULL) 2470 return((Image *) NULL); 2471 crop_image->background_color.alpha=(Quantum) TransparentAlpha; 2472 crop_image->alpha_trait=BlendPixelTrait; 2473 (void) SetImageBackgroundColor(crop_image,exception); 2474 crop_image->page=image->page; 2475 crop_image->page.x=(-1); 2476 crop_image->page.y=(-1); 2477 return(crop_image); 2478 } 2479 geometry.x+=image->page.x; 2480 geometry.y+=image->page.y; 2481 return(CropImage(image,&geometry,exception)); 2482} 2483