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