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