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