channel.c revision 8a46d827a124555f0c48fb2368ec1bba8e079ab6
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 RRR 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% John Cristy % 17% December 2003 % 18% % 19% % 20% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization % 21% dedicated to making software imaging solutions freely available. % 22% % 23% You may not use this file except in compliance with the License. You may % 24% obtain a copy of the License at % 25% % 26% http://www.imagemagick.org/script/license.php % 27% % 28% Unless required by applicable law or agreed to in writing, software % 29% distributed under the License is distributed on an "AS IS" BASIS, % 30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 31% See the License for the specific language governing permissions and % 32% limitations under the License. % 33% % 34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 35% 36% 37% 38*/ 39 40/* 41 Include declarations. 42*/ 43#include "MagickCore/studio.h" 44#include "MagickCore/colorspace-private.h" 45#include "MagickCore/image.h" 46#include "MagickCore/list.h" 47#include "MagickCore/log.h" 48#include "MagickCore/monitor.h" 49#include "MagickCore/monitor-private.h" 50#include "MagickCore/option.h" 51#include "MagickCore/pixel-accessor.h" 52#include "MagickCore/resource_.h" 53#include "MagickCore/string-private.h" 54#include "MagickCore/thread-private.h" 55#include "MagickCore/token.h" 56#include "MagickCore/utility.h" 57#include "MagickCore/version.h" 58 59/* 60%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 61% % 62% % 63% % 64% C h a n n e l F x I m a g e % 65% % 66% % 67% % 68%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 69% 70% ChannelFxImage() applies a channel expression to the specified image. The 71% expression consists of one or more channels, either mnemonic or numeric (e.g. 72% red, 1), separated by actions as follows: 73% 74% <=> exchange two channels (e.g. red<=>blue) 75% => copy one channel to another channel (e.g. red=>green) 76% = assign a constant value to a channel (e.g. red=50%) 77% , write new image channels in the specified order (e.g. red, green) 78% | add a new output image for the next set of channel operations 79% ; move to the next input image for the source of channel data 80% 81% For example, to create 3 grayscale images from the red, green, and blue 82% channels of an image, use: 83% 84% -channel-fx "red; green; blue" 85% 86% A channel without an operation symbol implies separate (i.e, semicolon). 87% 88% The format of the ChannelFxImage method is: 89% 90% Image *ChannelFxImage(const Image *image,const char *expression, 91% ExceptionInfo *exception) 92% 93% A description of each parameter follows: 94% 95% o image: the image. 96% 97% o expression: A channel expression. 98% 99% o exception: return any errors or warnings in this structure. 100% 101*/ 102 103typedef enum 104{ 105 ExtractChannelOp, 106 AssignChannelOp, 107 ExchangeChannelOp, 108 TransferChannelOp 109} ChannelFx; 110 111static inline size_t MagickMin(const size_t x,const size_t y) 112{ 113 if (x < y) 114 return(x); 115 return(y); 116} 117 118static MagickBooleanType ChannelImage(Image *destination_image, 119 const PixelChannel destination_channel,const ChannelFx channel_op, 120 const Image *source_image,const PixelChannel source_channel, 121 const Quantum pixel,ExceptionInfo *exception) 122{ 123 CacheView 124 *source_view, 125 *destination_view; 126 127 MagickBooleanType 128 status; 129 130 size_t 131 height, 132 width; 133 134 ssize_t 135 y; 136 137 status=MagickTrue; 138 source_view=AcquireVirtualCacheView(source_image,exception); 139 destination_view=AcquireAuthenticCacheView(destination_image,exception); 140 height=MagickMin(source_image->rows,destination_image->rows); 141 width=MagickMin(source_image->columns,destination_image->columns); 142#if defined(MAGICKCORE_OPENMP_SUPPORT) 143 #pragma omp parallel for schedule(static) shared(status) \ 144 dynamic_number_threads(source_image,width,height,1) 145#endif 146 for (y=0; y < (ssize_t) height; y++) 147 { 148 PixelTrait 149 destination_traits, 150 source_traits; 151 152 register const Quantum 153 *restrict p; 154 155 register Quantum 156 *restrict q; 157 158 register ssize_t 159 x; 160 161 if (status == MagickFalse) 162 continue; 163 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1, 164 exception); 165 q=GetCacheViewAuthenticPixels(destination_view,0,y, 166 destination_image->columns,1,exception); 167 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 168 { 169 status=MagickFalse; 170 continue; 171 } 172 destination_traits=GetPixelChannelTraits(destination_image, 173 destination_channel); 174 source_traits=GetPixelChannelTraits(source_image,source_channel); 175 if ((destination_traits == UndefinedPixelTrait) || 176 (source_traits == UndefinedPixelTrait)) 177 continue; 178 for (x=0; x < (ssize_t) width; x++) 179 { 180 if (channel_op == AssignChannelOp) 181 SetPixelChannel(destination_image,destination_channel,pixel,q); 182 else 183 SetPixelChannel(destination_image,destination_channel, 184 GetPixelChannel(source_image,source_channel,p),q); 185 p+=GetPixelChannels(source_image); 186 q+=GetPixelChannels(destination_image); 187 } 188 if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse) 189 status=MagickFalse; 190 } 191 destination_view=DestroyCacheView(destination_view); 192 source_view=DestroyCacheView(source_view); 193 return(status); 194} 195 196MagickExport Image *ChannelFxImage(const Image *image,const char *expression, 197 ExceptionInfo *exception) 198{ 199#define ChannelFxImageTag "ChannelFx/Image" 200 201 ChannelFx 202 channel_op; 203 204 ChannelType 205 channel_mask; 206 207 char 208 token[MaxTextExtent]; 209 210 const char 211 *p; 212 213 const Image 214 *source_image; 215 216 double 217 pixel; 218 219 Image 220 *destination_image; 221 222 MagickBooleanType 223 status; 224 225 PixelChannel 226 source_channel, 227 destination_channel; 228 229 ssize_t 230 channels; 231 232 assert(image != (Image *) NULL); 233 assert(image->signature == MagickSignature); 234 if (image->debug != MagickFalse) 235 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 236 assert(exception != (ExceptionInfo *) NULL); 237 assert(exception->signature == MagickSignature); 238 source_image=image; 239 destination_image=CloneImage(source_image,0,0,MagickTrue,exception); 240 if (destination_image == (Image *) NULL) 241 return((Image *) NULL); 242 if (IsGrayColorspace(image->colorspace) != MagickFalse) 243 (void) TransformImageColorspace((Image *) image,RGBColorspace,exception); 244 if (expression == (const char *) NULL) 245 return(destination_image); 246 destination_channel=RedPixelChannel; 247 channel_mask=UndefinedChannel; 248 pixel=0.0; 249 p=(char *) expression; 250 GetMagickToken(p,&p,token); 251 channel_op=ExtractChannelOp; 252 for (channels=0; *token != '\0'; ) 253 { 254 ssize_t 255 i; 256 257 /* 258 Interpret channel expression. 259 */ 260 if (*token == ',') 261 { 262 destination_channel=(PixelChannel) ((ssize_t) destination_channel+1); 263 GetMagickToken(p,&p,token); 264 } 265 if (*token == '|') 266 { 267 if (GetNextImageInList(source_image) != (Image *) NULL) 268 source_image=GetNextImageInList(source_image); 269 else 270 source_image=GetFirstImageInList(source_image); 271 GetMagickToken(p,&p,token); 272 } 273 if (*token == ';') 274 { 275 Image 276 *canvas; 277 278 SetPixelChannelMask(destination_image,channel_mask); 279 if ((channel_op == ExtractChannelOp) && (destination_channel == 1)) 280 (void) SetImageColorspace(destination_image,GRAYColorspace,exception); 281 status=SetImageStorageClass(destination_image,DirectClass,exception); 282 if (status == MagickFalse) 283 { 284 destination_image=DestroyImageList(destination_image); 285 return(destination_image); 286 } 287 canvas=CloneImage(source_image,0,0,MagickTrue,exception); 288 if (canvas == (Image *) NULL) 289 { 290 destination_image=DestroyImageList(destination_image); 291 return(destination_image); 292 } 293 if (IsGrayColorspace(canvas->colorspace) != MagickFalse) 294 (void) TransformImageColorspace(canvas,RGBColorspace,exception); 295 AppendImageToList(&destination_image,canvas); 296 destination_image=GetLastImageInList(destination_image); 297 GetMagickToken(p,&p,token); 298 channels=0; 299 destination_channel=RedPixelChannel; 300 channel_mask=UndefinedChannel; 301 } 302 i=ParsePixelChannelOption(token); 303 if (i < 0) 304 { 305 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 306 "UnrecognizedChannelType","'%s'",token); 307 destination_image=DestroyImageList(destination_image); 308 return(destination_image); 309 } 310 source_channel=(PixelChannel) i; 311 channel_op=ExtractChannelOp; 312 GetMagickToken(p,&p,token); 313 if (*token == '<') 314 { 315 channel_op=ExchangeChannelOp; 316 GetMagickToken(p,&p,token); 317 } 318 if (*token == '=') 319 { 320 if (channel_op != ExchangeChannelOp) 321 channel_op=AssignChannelOp; 322 GetMagickToken(p,&p,token); 323 } 324 if (*token == '>') 325 { 326 if (channel_op != ExchangeChannelOp) 327 channel_op=TransferChannelOp; 328 GetMagickToken(p,&p,token); 329 } 330 switch (channel_op) 331 { 332 case AssignChannelOp: 333 { 334 pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0); 335 GetMagickToken(p,&p,token); 336 break; 337 } 338 case ExchangeChannelOp: 339 case TransferChannelOp: 340 { 341 i=ParsePixelChannelOption(token); 342 if (i < 0) 343 { 344 (void) ThrowMagickException(exception,GetMagickModule(),OptionError, 345 "UnrecognizedChannelType","'%s'",token); 346 destination_image=DestroyImageList(destination_image); 347 return(destination_image); 348 } 349 destination_channel=(PixelChannel) i; 350 channel_mask=(ChannelType) (channel_mask | ParseChannelOption(token)); 351 if (LocaleCompare(token,"gray") == 0) 352 (void) SetImageColorspace(destination_image,GRAYColorspace,exception); 353 if ((LocaleCompare(token,"black") == 0) || 354 (LocaleCompare(token,"c") == 0) || 355 (LocaleCompare(token,"cyan") == 0) || 356 (LocaleCompare(token,"k") == 0) || 357 (LocaleCompare(token,"m") == 0) || 358 (LocaleCompare(token,"magenta") == 0) || 359 (LocaleCompare(token,"y") == 0) || 360 (LocaleCompare(token,"yellow") == 0)) 361 (void) SetImageColorspace(destination_image,CMYKColorspace,exception); 362 if ((LocaleCompare(token,"Cb") == 0) || 363 (LocaleCompare(token,"Cr") == 0)) 364 (void) SetImageColorspace(destination_image,YCbCrColorspace, 365 exception); 366 if (LocaleCompare(token,"alpha") == 0) 367 (void) SetImageAlpha(destination_image,OpaqueAlpha,exception); 368 if (i >= (ssize_t) GetPixelChannels(destination_image)) 369 (void) SetPixelMetaChannels(destination_image,(size_t) (i- 370 GetPixelChannels(destination_image)+1),exception); 371 GetMagickToken(p,&p,token); 372 break; 373 } 374 default: 375 break; 376 } 377 status=ChannelImage(destination_image,destination_channel,channel_op, 378 source_image,source_channel,ClampToQuantum(pixel),exception); 379 if (status == MagickFalse) 380 { 381 destination_image=DestroyImageList(destination_image); 382 break; 383 } 384 channels++; 385 if (channel_op == ExchangeChannelOp) 386 { 387 status=ChannelImage(destination_image,source_channel,channel_op, 388 source_image,destination_channel,ClampToQuantum(pixel),exception); 389 if (status == MagickFalse) 390 { 391 destination_image=DestroyImageList(destination_image); 392 break; 393 } 394 channels++; 395 } 396 switch (channel_op) 397 { 398 case ExtractChannelOp: 399 { 400 channel_mask=(ChannelType) (channel_mask | (1 << destination_channel)); 401 destination_channel=(PixelChannel) (destination_channel+1); 402 break; 403 } 404 default: 405 break; 406 } 407 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression, 408 strlen(expression)); 409 if (status == MagickFalse) 410 break; 411 } 412 SetPixelChannelMask(destination_image,channel_mask); 413 if ((channel_op == ExtractChannelOp) && (destination_channel == 1)) 414 (void) SetImageColorspace(destination_image,GRAYColorspace,exception); 415 status=SetImageStorageClass(destination_image,DirectClass,exception); 416 if (status == MagickFalse) 417 { 418 destination_image=GetLastImageInList(destination_image); 419 return((Image *) NULL); 420 } 421 return(GetFirstImageInList(destination_image)); 422} 423 424/* 425%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 426% % 427% % 428% % 429% C o m b i n e I m a g e s % 430% % 431% % 432% % 433%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 434% 435% CombineImages() combines one or more images into a single image. The 436% grayscale value of the pixels of each image in the sequence is assigned in 437% order to the specified channels of the combined image. The typical 438% ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc. 439% 440% The format of the CombineImages method is: 441% 442% Image *CombineImages(const Image *images,const ColorspaceType colorspace, 443% ExceptionInfo *exception) 444% 445% A description of each parameter follows: 446% 447% o images: the image sequence. 448% 449% o colorspace: the image colorspace. 450% 451% o exception: return any errors or warnings in this structure. 452% 453*/ 454MagickExport Image *CombineImages(const Image *image, 455 const ColorspaceType colorspace,ExceptionInfo *exception) 456{ 457#define CombineImageTag "Combine/Image" 458 459 CacheView 460 *combine_view; 461 462 Image 463 *combine_image; 464 465 MagickBooleanType 466 status; 467 468 MagickOffsetType 469 progress; 470 471 ssize_t 472 y; 473 474 /* 475 Ensure the image are the same size. 476 */ 477 assert(image != (const Image *) NULL); 478 assert(image->signature == MagickSignature); 479 if (image->debug != MagickFalse) 480 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 481 assert(exception != (ExceptionInfo *) NULL); 482 assert(exception->signature == MagickSignature); 483 combine_image=CloneImage(image,0,0,MagickTrue,exception); 484 if (combine_image == (Image *) NULL) 485 return((Image *) NULL); 486 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse) 487 { 488 combine_image=DestroyImage(combine_image); 489 return((Image *) NULL); 490 } 491 if (IsGrayColorspace(image->colorspace) != MagickFalse) 492 (void) SetImageColorspace(combine_image,RGBColorspace,exception); 493 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) 494 combine_image->alpha_trait=BlendPixelTrait; 495 /* 496 Combine images. 497 */ 498 status=MagickTrue; 499 progress=0; 500 combine_view=AcquireAuthenticCacheView(combine_image,exception); 501 for (y=0; y < (ssize_t) combine_image->rows; y++) 502 { 503 CacheView 504 *image_view; 505 506 const Image 507 *next; 508 509 Quantum 510 *pixels; 511 512 register const Quantum 513 *restrict p; 514 515 register Quantum 516 *restrict q; 517 518 register ssize_t 519 i; 520 521 if (status == MagickFalse) 522 continue; 523 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns, 524 1,exception); 525 if (pixels == (Quantum *) NULL) 526 { 527 status=MagickFalse; 528 continue; 529 } 530 next=image; 531 for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++) 532 { 533 PixelChannel 534 channel; 535 536 PixelTrait 537 traits; 538 539 register ssize_t 540 x; 541 542 if (next == (Image *) NULL) 543 continue; 544 channel=GetPixelChannelChannel(combine_image,i); 545 traits=GetPixelChannelTraits(combine_image,channel); 546 if (traits == UndefinedPixelTrait) 547 continue; 548 image_view=AcquireVirtualCacheView(next,exception); 549 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception); 550 if (p == (const Quantum *) NULL) 551 continue; 552 q=pixels; 553 for (x=0; x < (ssize_t) combine_image->columns; x++) 554 { 555 if (x < (ssize_t) next->columns) 556 { 557 q[i]=GetPixelGray(next,p); 558 p+=GetPixelChannels(next); 559 } 560 q+=GetPixelChannels(combine_image); 561 } 562 image_view=DestroyCacheView(image_view); 563 next=GetNextImageInList(next); 564 } 565 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse) 566 status=MagickFalse; 567 if (image->progress_monitor != (MagickProgressMonitor) NULL) 568 { 569 MagickBooleanType 570 proceed; 571 572 proceed=SetImageProgress(image,CombineImageTag,progress++, 573 combine_image->rows); 574 if (proceed == MagickFalse) 575 status=MagickFalse; 576 } 577 } 578 combine_view=DestroyCacheView(combine_view); 579 if (status == MagickFalse) 580 combine_image=DestroyImage(combine_image); 581 (void) TransformImageColorspace(combine_image,colorspace,exception); 582 return(combine_image); 583} 584 585/* 586%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 587% % 588% % 589% % 590% S e p a r a t e I m a g e % 591% % 592% % 593% % 594%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 595% 596% SeparateImage() separates a channel from the image and returns it as a 597% grayscale image. 598% 599% The format of the SeparateImage method is: 600% 601% Image *SeparateImage(const Image *image,const ChannelType channel, 602% ExceptionInfo *exception) 603% 604% A description of each parameter follows: 605% 606% o image: the image. 607% 608% o channel: the image channel. 609% 610% o exception: return any errors or warnings in this structure. 611% 612*/ 613MagickExport Image *SeparateImage(const Image *image, 614 const ChannelType channel_type,ExceptionInfo *exception) 615{ 616#define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01) 617#define SeparateImageTag "Separate/Image" 618 619 CacheView 620 *image_view, 621 *separate_view; 622 623 Image 624 *separate_image; 625 626 MagickBooleanType 627 status; 628 629 MagickOffsetType 630 progress; 631 632 ssize_t 633 y; 634 635 /* 636 Initialize spread image attributes. 637 */ 638 assert(image != (Image *) NULL); 639 assert(image->signature == MagickSignature); 640 if (image->debug != MagickFalse) 641 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 642 assert(exception != (ExceptionInfo *) NULL); 643 assert(exception->signature == MagickSignature); 644 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue, 645 exception); 646 if (separate_image == (Image *) NULL) 647 return((Image *) NULL); 648 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse) 649 { 650 separate_image=DestroyImage(separate_image); 651 return((Image *) NULL); 652 } 653 separate_image->alpha_trait=UndefinedPixelTrait; 654 (void) SetImageColorspace(separate_image,GRAYColorspace,exception); 655 /* 656 Separate image. 657 */ 658 status=MagickTrue; 659 progress=0; 660 image_view=AcquireVirtualCacheView(image,exception); 661 separate_view=AcquireAuthenticCacheView(separate_image,exception); 662#if defined(MAGICKCORE_OPENMP_SUPPORT) 663 #pragma omp parallel for schedule(static) shared(progress,status) \ 664 dynamic_number_threads(image,image->columns,image->rows,1) 665#endif 666 for (y=0; y < (ssize_t) image->rows; y++) 667 { 668 register const Quantum 669 *restrict p; 670 671 register Quantum 672 *restrict q; 673 674 register ssize_t 675 x; 676 677 if (status == MagickFalse) 678 continue; 679 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); 680 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1, 681 exception); 682 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL)) 683 { 684 status=MagickFalse; 685 continue; 686 } 687 for (x=0; x < (ssize_t) image->columns; x++) 688 { 689 register ssize_t 690 i; 691 692 if (GetPixelMask(image,p) != 0) 693 { 694 p+=GetPixelChannels(image); 695 q+=GetPixelChannels(separate_image); 696 continue; 697 } 698 SetPixelChannel(separate_image,GrayPixelChannel,0,q); 699 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 700 { 701 double 702 pixel; 703 704 PixelChannel 705 channel; 706 707 PixelTrait 708 traits; 709 710 channel=GetPixelChannelChannel(image,i); 711 traits=GetPixelChannelTraits(image,channel); 712 if ((traits == UndefinedPixelTrait) || 713 (GetChannelBit(channel_type,channel) == 0)) 714 continue; 715 pixel=p[i]; 716 if (IssRGBColorspace(image->colorspace) != MagickFalse) 717 pixel=InversesRGBCompandor(pixel); 718 SetPixelChannel(separate_image,GrayPixelChannel,ClampToQuantum(pixel), 719 q); 720 } 721 p+=GetPixelChannels(image); 722 q+=GetPixelChannels(separate_image); 723 } 724 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse) 725 status=MagickFalse; 726 if (image->progress_monitor != (MagickProgressMonitor) NULL) 727 { 728 MagickBooleanType 729 proceed; 730 731#if defined(MAGICKCORE_OPENMP_SUPPORT) 732 #pragma omp critical (MagickCore_SeparateImage) 733#endif 734 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows); 735 if (proceed == MagickFalse) 736 status=MagickFalse; 737 } 738 } 739 separate_view=DestroyCacheView(separate_view); 740 image_view=DestroyCacheView(image_view); 741 return(separate_image); 742} 743 744/* 745%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 746% % 747% % 748% % 749% S e p a r a t e I m a g e s % 750% % 751% % 752% % 753%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 754% 755% SeparateImages() returns a separate grayscale image for each channel 756% specified. 757% 758% The format of the SeparateImages method is: 759% 760% Image *SeparateImages(const Image *image,ExceptionInfo *exception) 761% 762% A description of each parameter follows: 763% 764% o image: the image. 765% 766% o exception: return any errors or warnings in this structure. 767% 768*/ 769MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception) 770{ 771 Image 772 *images, 773 *separate_image; 774 775 register ssize_t 776 i; 777 778 assert(image != (Image *) NULL); 779 assert(image->signature == MagickSignature); 780 if (image->debug != MagickFalse) 781 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); 782 images=NewImageList(); 783 for (i=0; i < (ssize_t) GetPixelChannels(image); i++) 784 { 785 PixelChannel 786 channel; 787 788 PixelTrait 789 traits; 790 791 channel=GetPixelChannelChannel(image,i); 792 traits=GetPixelChannelTraits(image,channel); 793 if ((traits == UndefinedPixelTrait) || 794 ((traits & UpdatePixelTrait) == 0)) 795 continue; 796 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception); 797 if (separate_image != (Image *) NULL) 798 AppendImageToList(&images,separate_image); 799 } 800 if (images == (Image *) NULL) 801 images=SeparateImage(image,UndefinedChannel,exception); 802 return(images); 803} 804