1 /* 2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 3 % % 4 % % 5 % % 6 % CCCC H H AAA N N N N EEEEE L % 7 % C H H A A NN N NN N E L % 8 % C HHHHH AAAAA N N N N N N EEE L % 9 % C H H A A N NN N NN E L % 10 % CCCC H H A A N N N N EEEEE LLLLL % 11 % % 12 % % 13 % MagickCore Image Channel Methods % 14 % % 15 % Software Design % 16 % Cristy % 17 % December 2003 % 18 % % 19 % % 20 % Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization % 21 % dedicated to making software imaging solutions freely available. % 22 % % 23 % You may not use this file except in compliance with the License. You may % 24 % obtain a copy of the License at % 25 % % 26 % http://www.imagemagick.org/script/license.php % 27 % % 28 % Unless required by applicable law or agreed to in writing, software % 29 % distributed under the License is distributed on an "AS IS" BASIS, % 30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 31 % See the License for the specific language governing permissions and % 32 % limitations under the License. % 33 % % 34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 35 % 36 % 37 % 38 */ 39 40 /* 41 Include declarations. 42 */ 43 #include "MagickCore/studio.h" 44 #include "MagickCore/cache-private.h" 45 #include "MagickCore/channel.h" 46 #include "MagickCore/colorspace-private.h" 47 #include "MagickCore/composite-private.h" 48 #include "MagickCore/enhance.h" 49 #include "MagickCore/image.h" 50 #include "MagickCore/list.h" 51 #include "MagickCore/log.h" 52 #include "MagickCore/monitor.h" 53 #include "MagickCore/monitor-private.h" 54 #include "MagickCore/option.h" 55 #include "MagickCore/pixel-accessor.h" 56 #include "MagickCore/pixel-private.h" 57 #include "MagickCore/resource_.h" 58 #include "MagickCore/string-private.h" 59 #include "MagickCore/thread-private.h" 60 #include "MagickCore/token.h" 61 #include "MagickCore/utility.h" 62 #include "MagickCore/version.h" 63 64 /* 65 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 66 % % 67 % % 68 % % 69 % C h a n n e l F x I m a g e % 70 % % 71 % % 72 % % 73 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 74 % 75 % ChannelFxImage() applies a channel expression to the specified image. The 76 % expression consists of one or more channels, either mnemonic or numeric (e.g. 77 % red, 1), separated by actions as follows: 78 % 79 % <=> exchange two channels (e.g. red<=>blue) 80 % => copy one channel to another channel (e.g. red=>green) 81 % = assign a constant value to a channel (e.g. red=50%) 82 % , write new image channels in the specified order (e.g. red, green) 83 % | add a new output image for the next set of channel operations 84 % ; move to the next input image for the source of channel data 85 % 86 % For example, to create 3 grayscale images from the red, green, and blue 87 % channels of an image, use: 88 % 89 % -channel-fx "red; green; blue" 90 % 91 % A channel without an operation symbol implies separate (i.e, semicolon). 92 % 93 % The format of the ChannelFxImage method is: 94 % 95 % Image *ChannelFxImage(const Image *image,const char *expression, 96 % ExceptionInfo *exception) 97 % 98 % A description of each parameter follows: 99 % 100 % o image: the image. 101 % 102 % o expression: A channel expression. 103 % 104 % o exception: return any errors or warnings in this structure. 105 % 106 */ 107 108 typedef enum 109 { 110 ExtractChannelOp, 111 AssignChannelOp, 112 ExchangeChannelOp, 113 TransferChannelOp 114 } ChannelFx; 115 116 static MagickBooleanType ChannelImage(Image *destination_image, 117 const PixelChannel destination_channel,const ChannelFx channel_op, 118 const Image *source_image,const PixelChannel source_channel, 119 const Quantum pixel,ExceptionInfo *exception) 120 { 121 CacheView 122 *source_view, 123 *destination_view; 124 125 MagickBooleanType 126 status; 127 128 size_t 129 height, 130 width; 131 132 ssize_t 133 y; 134 135 status=MagickTrue; 136 source_view=AcquireVirtualCacheView(source_image,exception); 137 destination_view=AcquireAuthenticCacheView(destination_image,exception); 138 height=MagickMin(source_image->rows,destination_image->rows); 139 width=MagickMin(source_image->columns,destination_image->columns); 140 #if defined(MAGICKCORE_OPENMP_SUPPORT) 141 #pragma omp parallel for schedule(static,4) shared(status) \ 142 magick_threads(source_image,source_image,height,1) 143 #endif 144 for (y=0; y < (ssize_t) height; y++) 145 { 146 PixelTrait 147 destination_traits, 148 source_traits; 149 150 register const Quantum 151 *magick_restrict p; 152 153 register Quantum 154 *magick_restrict q; 155 156 register ssize_t 157 x; 158 159 if (status == MagickFalse) 160 continue; 161 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1, 162 exception); 163 q=GetCacheViewAuthenticPixels(destination_view,0,y, 164 destination_image->columns,1,exception); 165 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 166 { 167 status=MagickFalse; 168 continue; 169 } 170 destination_traits=GetPixelChannelTraits(destination_image, 171 destination_channel); 172 source_traits=GetPixelChannelTraits(source_image,source_channel); 173 if ((destination_traits == UndefinedPixelTrait) || 174 (source_traits == UndefinedPixelTrait)) 175 continue; 176 for (x=0; x < (ssize_t) width; x++) 177 { 178 if (channel_op == AssignChannelOp) 179 SetPixelChannel(destination_image,destination_channel,pixel,q); 180 else 181 SetPixelChannel(destination_image,destination_channel, 182 GetPixelChannel(source_image,source_channel,p),q); 183 p+=GetPixelChannels(source_image); 184 q+=GetPixelChannels(destination_image); 185 } 186 if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse) 187 status=MagickFalse; 188 } 189 destination_view=DestroyCacheView(destination_view); 190 source_view=DestroyCacheView(source_view); 191 return(status); 192 } 193 194 MagickExport Image *ChannelFxImage(const Image *image,const char *expression, 195 ExceptionInfo *exception) 196 { 197 #define ChannelFxImageTag "ChannelFx/Image" 198 199 ChannelFx 200 channel_op; 201 202 ChannelType 203 channel_mask; 204 205 char 206 token[MagickPathExtent]; 207 208 const char 209 *p; 210 211 const Image 212 *source_image; 213 214 double 215 pixel; 216 217 Image 218 *destination_image; 219 220 MagickBooleanType 221 status; 222 223 PixelChannel 224 source_channel, 225 destination_channel; 226 227 ssize_t 228 channels; 229 230 assert(image != (Image *) NULL); 231 assert(image->signature == MagickCoreSignature); 232 if (image->debug != MagickFalse) 233 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 234 assert(exception != (ExceptionInfo *) NULL); 235 assert(exception->signature == MagickCoreSignature); 236 source_image=image; 237 destination_image=CloneImage(source_image,0,0,MagickTrue,exception); 238 if (destination_image == (Image *) NULL) 239 return((Image *) NULL); 240 if (expression == (const char *) NULL) 241 return(destination_image); 242 destination_channel=RedPixelChannel; 243 channel_mask=UndefinedChannel; 244 pixel=0.0; 245 p=(char *) expression; 246 GetNextToken(p,&p,MagickPathExtent,token); 247 channel_op=ExtractChannelOp; 248 for (channels=0; *token != '\0'; ) 249 { 250 ssize_t 251 i; 252 253 /* 254 Interpret channel expression. 255 */ 256 switch (*token) 257 { 258 case ',': 259 { 260 GetNextToken(p,&p,MagickPathExtent,token); 261 break; 262 } 263 case '|': 264 { 265 if (GetNextImageInList(source_image) != (Image *) NULL) 266 source_image=GetNextImageInList(source_image); 267 else 268 source_image=GetFirstImageInList(source_image); 269 GetNextToken(p,&p,MagickPathExtent,token); 270 break; 271 } 272 case ';': 273 { 274 Image 275 *canvas; 276 277 (void) SetPixelChannelMask(destination_image,channel_mask); 278 if ((channel_op == ExtractChannelOp) && (channels == 1)) 279 (void) SetImageColorspace(destination_image,GRAYColorspace,exception); 280 status=SetImageStorageClass(destination_image,DirectClass,exception); 281 if (status == MagickFalse) 282 { 283 destination_image=DestroyImageList(destination_image); 284 return(destination_image); 285 } 286 canvas=CloneImage(source_image,0,0,MagickTrue,exception); 287 if (canvas == (Image *) NULL) 288 { 289 destination_image=DestroyImageList(destination_image); 290 return(destination_image); 291 } 292 AppendImageToList(&destination_image,canvas); 293 destination_image=GetLastImageInList(destination_image); 294 GetNextToken(p,&p,MagickPathExtent,token); 295 channels=0; 296 destination_channel=RedPixelChannel; 297 channel_mask=UndefinedChannel; 298 break; 299 } 300 default: 301 break; 302 } 303 i=ParsePixelChannelOption(token); 304 if (i < 0) 305 { 306 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 307 "UnrecognizedChannelType","`%s'",token); 308 destination_image=DestroyImageList(destination_image); 309 return(destination_image); 310 } 311 source_channel=(PixelChannel) i; 312 channel_op=ExtractChannelOp; 313 GetNextToken(p,&p,MagickPathExtent,token); 314 if (*token == '<') 315 { 316 channel_op=ExchangeChannelOp; 317 GetNextToken(p,&p,MagickPathExtent,token); 318 } 319 if (*token == '=') 320 { 321 if (channel_op != ExchangeChannelOp) 322 channel_op=AssignChannelOp; 323 GetNextToken(p,&p,MagickPathExtent,token); 324 } 325 if (*token == '>') 326 { 327 if (channel_op != ExchangeChannelOp) 328 channel_op=TransferChannelOp; 329 GetNextToken(p,&p,MagickPathExtent,token); 330 } 331 switch (channel_op) 332 { 333 case AssignChannelOp: 334 { 335 pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0); 336 GetNextToken(p,&p,MagickPathExtent,token); 337 break; 338 } 339 case ExchangeChannelOp: 340 case TransferChannelOp: 341 { 342 i=ParsePixelChannelOption(token); 343 if (i < 0) 344 { 345 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 346 "UnrecognizedChannelType","`%s'",token); 347 destination_image=DestroyImageList(destination_image); 348 return(destination_image); 349 } 350 destination_channel=(PixelChannel) i; 351 switch (destination_channel) 352 { 353 case RedPixelChannel: 354 case GreenPixelChannel: 355 case BluePixelChannel: 356 case BlackPixelChannel: 357 case IndexPixelChannel: 358 break; 359 case AlphaPixelChannel: 360 { 361 destination_image->alpha_trait=BlendPixelTrait; 362 break; 363 } 364 case ReadMaskPixelChannel: 365 { 366 destination_image->read_mask=MagickTrue; 367 break; 368 } 369 case WriteMaskPixelChannel: 370 { 371 destination_image->write_mask=MagickTrue; 372 break; 373 } 374 case MetaPixelChannel: 375 default: 376 { 377 (void) SetPixelMetaChannels(destination_image,(size_t) (i- 378 GetPixelChannels(destination_image)+1),exception); 379 break; 380 } 381 } 382 channel_mask=(ChannelType) (channel_mask | ParseChannelOption(token)); 383 if (((channels >= 1) || (destination_channel >= 1)) && 384 (IsGrayColorspace(destination_image->colorspace) != MagickFalse)) 385 (void) SetImageColorspace(destination_image,sRGBColorspace,exception); 386 GetNextToken(p,&p,MagickPathExtent,token); 387 break; 388 } 389 default: 390 break; 391 } 392 status=ChannelImage(destination_image,destination_channel,channel_op, 393 source_image,source_channel,ClampToQuantum(pixel),exception); 394 if (status == MagickFalse) 395 { 396 destination_image=DestroyImageList(destination_image); 397 break; 398 } 399 channels++; 400 if (channel_op == ExchangeChannelOp) 401 { 402 status=ChannelImage(destination_image,source_channel,channel_op, 403 source_image,destination_channel,ClampToQuantum(pixel),exception); 404 if (status == MagickFalse) 405 { 406 destination_image=DestroyImageList(destination_image); 407 break; 408 } 409 channels++; 410 } 411 switch (channel_op) 412 { 413 case ExtractChannelOp: 414 { 415 channel_mask=(ChannelType) (channel_mask | (1 << destination_channel)); 416 destination_channel=(PixelChannel) (destination_channel+1); 417 break; 418 } 419 default: 420 break; 421 } 422 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression, 423 strlen(expression)); 424 if (status == MagickFalse) 425 break; 426 } 427 (void) SetPixelChannelMask(destination_image,channel_mask); 428 if ((channel_op == ExtractChannelOp) && (channels == 1)) 429 (void) SetImageColorspace(destination_image,GRAYColorspace,exception); 430 status=SetImageStorageClass(destination_image,DirectClass,exception); 431 if (status == MagickFalse) 432 { 433 destination_image=GetLastImageInList(destination_image); 434 return((Image *) NULL); 435 } 436 return(GetFirstImageInList(destination_image)); 437 } 438 439 /* 440 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 441 % % 442 % % 443 % % 444 % C o m b i n e I m a g e s % 445 % % 446 % % 447 % % 448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 449 % 450 % CombineImages() combines one or more images into a single image. The 451 % grayscale value of the pixels of each image in the sequence is assigned in 452 % order to the specified channels of the combined image. The typical 453 % ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc. 454 % 455 % The format of the CombineImages method is: 456 % 457 % Image *CombineImages(const Image *images,const ColorspaceType colorspace, 458 % ExceptionInfo *exception) 459 % 460 % A description of each parameter follows: 461 % 462 % o images: the image sequence. 463 % 464 % o colorspace: the image colorspace. 465 % 466 % o exception: return any errors or warnings in this structure. 467 % 468 */ 469 MagickExport Image *CombineImages(const Image *image, 470 const ColorspaceType colorspace,ExceptionInfo *exception) 471 { 472 #define CombineImageTag "Combine/Image" 473 474 CacheView 475 *combine_view; 476 477 Image 478 *combine_image; 479 480 MagickBooleanType 481 status; 482 483 MagickOffsetType 484 progress; 485 486 ssize_t 487 y; 488 489 /* 490 Ensure the image are the same size. 491 */ 492 assert(image != (const Image *) NULL); 493 assert(image->signature == MagickCoreSignature); 494 if (image->debug != MagickFalse) 495 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 496 assert(exception != (ExceptionInfo *) NULL); 497 assert(exception->signature == MagickCoreSignature); 498 combine_image=CloneImage(image,0,0,MagickTrue,exception); 499 if (combine_image == (Image *) NULL) 500 return((Image *) NULL); 501 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse) 502 { 503 combine_image=DestroyImage(combine_image); 504 return((Image *) NULL); 505 } 506 if ((colorspace == UndefinedColorspace) || (image->number_channels == 1)) 507 (void) SetImageColorspace(combine_image,sRGBColorspace,exception); 508 else 509 (void) SetImageColorspace(combine_image,colorspace,exception); 510 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 511 combine_image->alpha_trait=BlendPixelTrait; 512 /* 513 Combine images. 514 */ 515 status=MagickTrue; 516 progress=0; 517 combine_view=AcquireAuthenticCacheView(combine_image,exception); 518 for (y=0; y < (ssize_t) combine_image->rows; y++) 519 { 520 CacheView 521 *image_view; 522 523 const Image 524 *next; 525 526 Quantum 527 *pixels; 528 529 register const Quantum 530 *magick_restrict p; 531 532 register Quantum 533 *magick_restrict q; 534 535 register ssize_t 536 i; 537 538 if (status == MagickFalse) 539 continue; 540 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns, 541 1,exception); 542 if (pixels == (Quantum *) NULL) 543 { 544 status=MagickFalse; 545 continue; 546 } 547 next=image; 548 for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++) 549 { 550 register ssize_t 551 x; 552 553 PixelChannel channel=GetPixelChannelChannel(combine_image,i); 554 PixelTrait traits=GetPixelChannelTraits(combine_image,channel); 555 if (traits == UndefinedPixelTrait) 556 continue; 557 if (next == (Image *) NULL) 558 continue; 559 image_view=AcquireVirtualCacheView(next,exception); 560 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception); 561 if (p == (const Quantum *) NULL) 562 continue; 563 q=pixels; 564 for (x=0; x < (ssize_t) combine_image->columns; x++) 565 { 566 if (x < (ssize_t) next->columns) 567 { 568 q[i]=GetPixelGray(next,p); 569 p+=GetPixelChannels(next); 570 } 571 q+=GetPixelChannels(combine_image); 572 } 573 image_view=DestroyCacheView(image_view); 574 next=GetNextImageInList(next); 575 } 576 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse) 577 status=MagickFalse; 578 if (image->progress_monitor != (MagickProgressMonitor) NULL) 579 { 580 MagickBooleanType 581 proceed; 582 583 proceed=SetImageProgress(image,CombineImageTag,progress++, 584 combine_image->rows); 585 if (proceed == MagickFalse) 586 status=MagickFalse; 587 } 588 } 589 combine_view=DestroyCacheView(combine_view); 590 if (status == MagickFalse) 591 combine_image=DestroyImage(combine_image); 592 return(combine_image); 593 } 594 595 /* 596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 597 % % 598 % % 599 % % 600 % G e t I m a g e A l p h a C h a n n e l % 601 % % 602 % % 603 % % 604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 605 % 606 % GetImageAlphaChannel() returns MagickFalse if the image alpha channel is 607 % not activated. That is, the image is RGB rather than RGBA or CMYK rather 608 % than CMYKA. 609 % 610 % The format of the GetImageAlphaChannel method is: 611 % 612 % MagickBooleanType GetImageAlphaChannel(const Image *image) 613 % 614 % A description of each parameter follows: 615 % 616 % o image: the image. 617 % 618 */ 619 MagickExport MagickBooleanType GetImageAlphaChannel(const Image *image) 620 { 621 assert(image != (const Image *) NULL); 622 if (image->debug != MagickFalse) 623 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); 624 assert(image->signature == MagickCoreSignature); 625 return(image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse); 626 } 627 628 /* 629 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 630 % % 631 % % 632 % % 633 % S e p a r a t e I m a g e % 634 % % 635 % % 636 % % 637 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 638 % 639 % SeparateImage() separates a channel from the image and returns it as a 640 % grayscale image. 641 % 642 % The format of the SeparateImage method is: 643 % 644 % Image *SeparateImage(const Image *image,const ChannelType channel, 645 % ExceptionInfo *exception) 646 % 647 % A description of each parameter follows: 648 % 649 % o image: the image. 650 % 651 % o channel: the image channel. 652 % 653 % o exception: return any errors or warnings in this structure. 654 % 655 */ 656 MagickExport Image *SeparateImage(const Image *image, 657 const ChannelType channel_type,ExceptionInfo *exception) 658 { 659 #define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01) 660 #define SeparateImageTag "Separate/Image" 661 662 CacheView 663 *image_view, 664 *separate_view; 665 666 Image 667 *separate_image; 668 669 MagickBooleanType 670 status; 671 672 MagickOffsetType 673 progress; 674 675 ssize_t 676 y; 677 678 /* 679 Initialize separate image attributes. 680 */ 681 assert(image != (Image *) NULL); 682 assert(image->signature == MagickCoreSignature); 683 if (image->debug != MagickFalse) 684 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 685 assert(exception != (ExceptionInfo *) NULL); 686 assert(exception->signature == MagickCoreSignature); 687 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue, 688 exception); 689 if (separate_image == (Image *) NULL) 690 return((Image *) NULL); 691 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse) 692 { 693 separate_image=DestroyImage(separate_image); 694 return((Image *) NULL); 695 } 696 (void) SetImageColorspace(separate_image,GRAYColorspace,exception); 697 separate_image->alpha_trait=UndefinedPixelTrait; 698 /* 699 Separate image. 700 */ 701 status=MagickTrue; 702 progress=0; 703 image_view=AcquireVirtualCacheView(image,exception); 704 separate_view=AcquireAuthenticCacheView(separate_image,exception); 705 #if defined(MAGICKCORE_OPENMP_SUPPORT) 706 #pragma omp parallel for schedule(static,4) shared(progress,status) \ 707 magick_threads(image,image,image->rows,1) 708 #endif 709 for (y=0; y < (ssize_t) image->rows; y++) 710 { 711 register const Quantum 712 *magick_restrict p; 713 714 register Quantum 715 *magick_restrict q; 716 717 register ssize_t 718 x; 719 720 if (status == MagickFalse) 721 continue; 722 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 723 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1, 724 exception); 725 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 726 { 727 status=MagickFalse; 728 continue; 729 } 730 for (x=0; x < (ssize_t) image->columns; x++) 731 { 732 register ssize_t 733 i; 734 735 if (GetPixelReadMask(image,p) == 0) 736 { 737 SetPixelBackgoundColor(separate_image,q); 738 p+=GetPixelChannels(image); 739 q+=GetPixelChannels(separate_image); 740 continue; 741 } 742 SetPixelChannel(separate_image,GrayPixelChannel,0,q); 743 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 744 { 745 PixelChannel channel=GetPixelChannelChannel(image,i); 746 PixelTrait traits=GetPixelChannelTraits(image,channel); 747 if ((traits == UndefinedPixelTrait) || 748 (GetChannelBit(channel_type,channel) == 0)) 749 continue; 750 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q); 751 } 752 p+=GetPixelChannels(image); 753 q+=GetPixelChannels(separate_image); 754 } 755 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse) 756 status=MagickFalse; 757 if (image->progress_monitor != (MagickProgressMonitor) NULL) 758 { 759 MagickBooleanType 760 proceed; 761 762 #if defined(MAGICKCORE_OPENMP_SUPPORT) 763 #pragma omp critical (MagickCore_SeparateImage) 764 #endif 765 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows); 766 if (proceed == MagickFalse) 767 status=MagickFalse; 768 } 769 } 770 separate_view=DestroyCacheView(separate_view); 771 image_view=DestroyCacheView(image_view); 772 (void) SetImageChannelMask(separate_image,DefaultChannels); 773 if (status == MagickFalse) 774 separate_image=DestroyImage(separate_image); 775 return(separate_image); 776 } 777 778 /* 779 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 780 % % 781 % % 782 % % 783 % S e p a r a t e I m a g e s % 784 % % 785 % % 786 % % 787 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 788 % 789 % SeparateImages() returns a separate grayscale image for each channel 790 % specified. 791 % 792 % The format of the SeparateImages method is: 793 % 794 % Image *SeparateImages(const Image *image,ExceptionInfo *exception) 795 % 796 % A description of each parameter follows: 797 % 798 % o image: the image. 799 % 800 % o exception: return any errors or warnings in this structure. 801 % 802 */ 803 MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception) 804 { 805 Image 806 *images, 807 *separate_image; 808 809 register ssize_t 810 i; 811 812 assert(image != (Image *) NULL); 813 assert(image->signature == MagickCoreSignature); 814 if (image->debug != MagickFalse) 815 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 816 images=NewImageList(); 817 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 818 { 819 PixelChannel channel=GetPixelChannelChannel(image,i); 820 PixelTrait traits=GetPixelChannelTraits(image,channel); 821 if ((traits == UndefinedPixelTrait) || 822 ((traits & UpdatePixelTrait) == 0)) 823 continue; 824 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception); 825 if (separate_image != (Image *) NULL) 826 AppendImageToList(&images,separate_image); 827 } 828 if (images == (Image *) NULL) 829 images=SeparateImage(image,UndefinedChannel,exception); 830 return(images); 831 } 832 833 /* 834 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 835 % % 836 % % 837 % % 838 % S e t I m a g e A l p h a C h a n n e l % 839 % % 840 % % 841 % % 842 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 843 % 844 % SetImageAlphaChannel() activates, deactivates, resets, or sets the alpha 845 % channel. 846 % 847 % The format of the SetImageAlphaChannel method is: 848 % 849 % MagickBooleanType SetImageAlphaChannel(Image *image, 850 % const AlphaChannelOption alpha_type,ExceptionInfo *exception) 851 % 852 % A description of each parameter follows: 853 % 854 % o image: the image. 855 % 856 % o alpha_type: The alpha channel type: ActivateAlphaChannel, 857 % AssociateAlphaChannel, CopyAlphaChannel, DeactivateAlphaChannel, 858 % DisassociateAlphaChannel, ExtractAlphaChannel, OffAlphaChannel, 859 % OnAlphaChannel, OpaqueAlphaChannel, SetAlphaChannel, ShapeAlphaChannel, 860 % and TransparentAlphaChannel. 861 % 862 % o exception: return any errors or warnings in this structure. 863 % 864 */ 865 866 static inline void FlattenPixelInfo(const Image *image,const PixelInfo *p, 867 const double alpha,const Quantum *q,const double beta, 868 Quantum *composite) 869 { 870 double 871 Da, 872 gamma, 873 Sa; 874 875 register ssize_t 876 i; 877 878 /* 879 Compose pixel p over pixel q with the given alpha. 880 */ 881 Sa=QuantumScale*alpha; 882 Da=QuantumScale*beta, 883 gamma=Sa*(-Da)+Sa+Da; 884 gamma=PerceptibleReciprocal(gamma); 885 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 886 { 887 PixelChannel channel=GetPixelChannelChannel(image,i); 888 PixelTrait traits=GetPixelChannelTraits(image,channel); 889 if (traits == UndefinedPixelTrait) 890 continue; 891 switch (channel) 892 { 893 case RedPixelChannel: 894 { 895 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta, 896 (double) p->red,alpha)); 897 break; 898 } 899 case GreenPixelChannel: 900 { 901 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta, 902 (double) p->green,alpha)); 903 break; 904 } 905 case BluePixelChannel: 906 { 907 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta, 908 (double) p->blue,alpha)); 909 break; 910 } 911 case BlackPixelChannel: 912 { 913 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta, 914 (double) p->black,alpha)); 915 break; 916 } 917 case AlphaPixelChannel: 918 { 919 composite[i]=ClampToQuantum(QuantumRange*(Sa*(-Da)+Sa+Da)); 920 break; 921 } 922 default: 923 break; 924 } 925 } 926 } 927 928 MagickExport MagickBooleanType SetImageAlphaChannel(Image *image, 929 const AlphaChannelOption alpha_type,ExceptionInfo *exception) 930 { 931 CacheView 932 *image_view; 933 934 MagickBooleanType 935 status; 936 937 ssize_t 938 y; 939 940 assert(image != (Image *) NULL); 941 if (image->debug != MagickFalse) 942 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); 943 assert(image->signature == MagickCoreSignature); 944 status=MagickTrue; 945 switch (alpha_type) 946 { 947 case ActivateAlphaChannel: 948 { 949 image->alpha_trait=BlendPixelTrait; 950 break; 951 } 952 case AssociateAlphaChannel: 953 { 954 /* 955 Associate alpha. 956 */ 957 status=SetImageStorageClass(image,DirectClass,exception); 958 if (status == MagickFalse) 959 break; 960 image_view=AcquireAuthenticCacheView(image,exception); 961 #if defined(MAGICKCORE_OPENMP_SUPPORT) 962 #pragma omp parallel for schedule(static,4) shared(status) \ 963 magick_threads(image,image,image->rows,1) 964 #endif 965 for (y=0; y < (ssize_t) image->rows; y++) 966 { 967 register Quantum 968 *magick_restrict q; 969 970 register ssize_t 971 x; 972 973 if (status == MagickFalse) 974 continue; 975 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, 976 exception); 977 if (q == (Quantum *) NULL) 978 { 979 status=MagickFalse; 980 continue; 981 } 982 for (x=0; x < (ssize_t) image->columns; x++) 983 { 984 double 985 gamma; 986 987 register ssize_t 988 i; 989 990 if (GetPixelReadMask(image,q) == 0) 991 { 992 q+=GetPixelChannels(image); 993 continue; 994 } 995 gamma=QuantumScale*GetPixelAlpha(image,q); 996 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 997 { 998 PixelChannel channel=GetPixelChannelChannel(image,i); 999 PixelTrait traits=GetPixelChannelTraits(image,channel); 1000 if (channel == AlphaPixelChannel) 1001 continue; 1002 if ((traits & UpdatePixelTrait) == 0) 1003 continue; 1004 q[i]=ClampToQuantum(gamma*q[i]); 1005 } 1006 q+=GetPixelChannels(image); 1007 } 1008 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 1009 status=MagickFalse; 1010 } 1011 image_view=DestroyCacheView(image_view); 1012 image->alpha_trait=CopyPixelTrait; 1013 return(status); 1014 } 1015 case BackgroundAlphaChannel: 1016 { 1017 /* 1018 Set transparent pixels to background color. 1019 */ 1020 if (image->alpha_trait == UndefinedPixelTrait) 1021 break; 1022 status=SetImageStorageClass(image,DirectClass,exception); 1023 if (status == MagickFalse) 1024 break; 1025 image_view=AcquireAuthenticCacheView(image,exception); 1026 #if defined(MAGICKCORE_OPENMP_SUPPORT) 1027 #pragma omp parallel for schedule(static,4) shared(status) \ 1028 magick_threads(image,image,image->rows,1) 1029 #endif 1030 for (y=0; y < (ssize_t) image->rows; y++) 1031 { 1032 register Quantum 1033 *magick_restrict q; 1034 1035 register ssize_t 1036 x; 1037 1038 if (status == MagickFalse) 1039 continue; 1040 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, 1041 exception); 1042 if (q == (Quantum *) NULL) 1043 { 1044 status=MagickFalse; 1045 continue; 1046 } 1047 for (x=0; x < (ssize_t) image->columns; x++) 1048 { 1049 if (GetPixelAlpha(image,q) == TransparentAlpha) 1050 { 1051 SetPixelViaPixelInfo(image,&image->background_color,q); 1052 SetPixelChannel(image,AlphaPixelChannel,TransparentAlpha,q); 1053 } 1054 q+=GetPixelChannels(image); 1055 } 1056 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 1057 status=MagickFalse; 1058 } 1059 image_view=DestroyCacheView(image_view); 1060 return(status); 1061 } 1062 case CopyAlphaChannel: 1063 case ShapeAlphaChannel: 1064 { 1065 /* 1066 Copy pixel intensity to the alpha channel. 1067 */ 1068 image->alpha_trait=UpdatePixelTrait; 1069 status=CompositeImage(image,image,IntensityCompositeOp,MagickTrue,0,0, 1070 exception); 1071 if (alpha_type == ShapeAlphaChannel) 1072 (void) LevelImageColors(image,&image->background_color, 1073 &image->background_color,MagickTrue,exception); 1074 break; 1075 } 1076 case DeactivateAlphaChannel: 1077 { 1078 if (image->alpha_trait == UndefinedPixelTrait) 1079 status=SetImageAlpha(image,OpaqueAlpha,exception); 1080 image->alpha_trait=CopyPixelTrait; 1081 break; 1082 } 1083 case DisassociateAlphaChannel: 1084 { 1085 /* 1086 Disassociate alpha. 1087 */ 1088 status=SetImageStorageClass(image,DirectClass,exception); 1089 if (status == MagickFalse) 1090 break; 1091 image->alpha_trait=BlendPixelTrait; 1092 image_view=AcquireAuthenticCacheView(image,exception); 1093 #if defined(MAGICKCORE_OPENMP_SUPPORT) 1094 #pragma omp parallel for schedule(static,4) shared(status) \ 1095 magick_threads(image,image,image->rows,1) 1096 #endif 1097 for (y=0; y < (ssize_t) image->rows; y++) 1098 { 1099 register Quantum 1100 *magick_restrict q; 1101 1102 register ssize_t 1103 x; 1104 1105 if (status == MagickFalse) 1106 continue; 1107 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, 1108 exception); 1109 if (q == (Quantum *) NULL) 1110 { 1111 status=MagickFalse; 1112 continue; 1113 } 1114 for (x=0; x < (ssize_t) image->columns; x++) 1115 { 1116 double 1117 gamma, 1118 Sa; 1119 1120 register ssize_t 1121 i; 1122 1123 if (GetPixelReadMask(image,q) == 0) 1124 { 1125 q+=GetPixelChannels(image); 1126 continue; 1127 } 1128 Sa=QuantumScale*GetPixelAlpha(image,q); 1129 gamma=PerceptibleReciprocal(Sa); 1130 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 1131 { 1132 PixelChannel channel=GetPixelChannelChannel(image,i); 1133 PixelTrait traits=GetPixelChannelTraits(image,channel); 1134 if (channel == AlphaPixelChannel) 1135 continue; 1136 if ((traits & UpdatePixelTrait) == 0) 1137 continue; 1138 q[i]=ClampToQuantum(gamma*q[i]); 1139 } 1140 q+=GetPixelChannels(image); 1141 } 1142 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 1143 status=MagickFalse; 1144 } 1145 image_view=DestroyCacheView(image_view); 1146 image->alpha_trait=UndefinedPixelTrait; 1147 return(status); 1148 } 1149 case DiscreteAlphaChannel: 1150 { 1151 if (image->alpha_trait == UndefinedPixelTrait) 1152 status=SetImageAlpha(image,OpaqueAlpha,exception); 1153 image->alpha_trait=UpdatePixelTrait; 1154 break; 1155 } 1156 case ExtractAlphaChannel: 1157 { 1158 status=CompositeImage(image,image,AlphaCompositeOp,MagickTrue,0,0, 1159 exception); 1160 image->alpha_trait=UndefinedPixelTrait; 1161 break; 1162 } 1163 case OffAlphaChannel: 1164 { 1165 image->alpha_trait=UndefinedPixelTrait; 1166 break; 1167 } 1168 case OnAlphaChannel: 1169 { 1170 if (image->alpha_trait == UndefinedPixelTrait) 1171 status=SetImageAlpha(image,OpaqueAlpha,exception); 1172 image->alpha_trait=BlendPixelTrait; 1173 break; 1174 } 1175 case OpaqueAlphaChannel: 1176 { 1177 status=SetImageAlpha(image,OpaqueAlpha,exception); 1178 break; 1179 } 1180 case RemoveAlphaChannel: 1181 { 1182 /* 1183 Remove transparency. 1184 */ 1185 if (image->alpha_trait == UndefinedPixelTrait) 1186 break; 1187 status=SetImageStorageClass(image,DirectClass,exception); 1188 if (status == MagickFalse) 1189 break; 1190 image_view=AcquireAuthenticCacheView(image,exception); 1191 #if defined(MAGICKCORE_OPENMP_SUPPORT) 1192 #pragma omp parallel for schedule(static,4) shared(status) \ 1193 magick_threads(image,image,image->rows,1) 1194 #endif 1195 for (y=0; y < (ssize_t) image->rows; y++) 1196 { 1197 register Quantum 1198 *magick_restrict q; 1199 1200 register ssize_t 1201 x; 1202 1203 if (status == MagickFalse) 1204 continue; 1205 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1, 1206 exception); 1207 if (q == (Quantum *) NULL) 1208 { 1209 status=MagickFalse; 1210 continue; 1211 } 1212 for (x=0; x < (ssize_t) image->columns; x++) 1213 { 1214 FlattenPixelInfo(image,&image->background_color, 1215 image->background_color.alpha,q,(double) 1216 GetPixelAlpha(image,q),q); 1217 q+=GetPixelChannels(image); 1218 } 1219 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse) 1220 status=MagickFalse; 1221 } 1222 image_view=DestroyCacheView(image_view); 1223 image->alpha_trait=image->background_color.alpha_trait; 1224 break; 1225 } 1226 case SetAlphaChannel: 1227 { 1228 if (image->alpha_trait == UndefinedPixelTrait) 1229 status=SetImageAlpha(image,OpaqueAlpha,exception); 1230 break; 1231 } 1232 case TransparentAlphaChannel: 1233 { 1234 status=SetImageAlpha(image,TransparentAlpha,exception); 1235 break; 1236 } 1237 case UndefinedAlphaChannel: 1238 break; 1239 } 1240 if (status == MagickFalse) 1241 return(status); 1242 (void) SetPixelChannelMask(image,image->channel_mask); 1243 return(SyncImagePixelCache(image,exception)); 1244 } 1245