2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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 %
13 % MagickCore Image Channel Methods %
20 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "MagickCore/studio.h"
44 #include "MagickCore/image.h"
45 #include "MagickCore/list.h"
46 #include "MagickCore/log.h"
47 #include "MagickCore/monitor.h"
48 #include "MagickCore/monitor-private.h"
49 #include "MagickCore/option.h"
50 #include "MagickCore/pixel-accessor.h"
51 #include "MagickCore/string-private.h"
52 #include "MagickCore/token.h"
53 #include "MagickCore/utility.h"
54 #include "MagickCore/version.h"
57 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
61 % C h a n n e l F x I m a g e %
65 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
67 % ChannelFxImage() applies a channel expression to the specified image. The
68 % expression consists of one or more channels, either mnemonic or numeric (e.g.
69 % red, 1), separated by actions as follows:
71 % <=> exchange two channels (e.g. red<=>blue)
72 % => transfer a channel to another (e.g. red=>green)
73 % = assign a constant (e.g. red=50%)
74 % , separate channel operations (e.g. red, green)
75 % | read channels from next input image (e.g. red | green)
76 % ; write channels to next output image (e.g. red; green; blue)
78 % A channel without a operation symbol implies extract. For example, to create
79 % 3 grayscale images from the red, green, and blue channels of an image, use:
81 % -channel-fx "red; green; blue"
83 % The format of the ChannelFxImage method is:
85 % Image *ChannelFxImage(const Image *image,const char *expression,
86 % ExceptionInfo *exception)
88 % A description of each parameter follows:
92 % o expression: A channel expression.
94 % o exception: return any errors or warnings in this structure.
106 static inline size_t MagickMin(const size_t x,const size_t y)
113 static MagickBooleanType ChannelImage(Image *destination_image,
114 const Image *source_image,const PixelChannel source_channel,
115 const PixelChannel destination_channel,const ChannelFx channel_op,
116 const Quantum pixel,ExceptionInfo *exception)
132 source_view=AcquireCacheView(source_image);
133 destination_view=AcquireCacheView(destination_image);
134 height=MagickMin(source_image->rows,destination_image->rows);
135 #if defined(MAGICKCORE_OPENMP_SUPPORT)
136 #pragma omp parallel for schedule(static) shared(status)
138 for (y=0; y < (ssize_t) height; y++)
140 register const Quantum
152 if (status == MagickFalse)
154 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
156 q=QueueCacheViewAuthenticPixels(destination_view,0,y,
157 destination_image->columns,1,exception);
158 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
163 width=MagickMin(source_image->columns,destination_image->columns);
164 for (x=0; x < (ssize_t) width; x++)
173 source_traits=GetPixelChannelMapTraits(source_image,source_channel);
174 destination_traits=GetPixelChannelMapTraits(destination_image,
175 destination_channel);
176 if ((source_traits == UndefinedPixelTrait) ||
177 (destination_traits == UndefinedPixelTrait))
179 offset=GetPixelChannelMapOffset(source_image,source_channel);
180 if (channel_op == AssignChannelOp)
181 SetPixelChannel(destination_image,destination_channel,pixel,q);
183 SetPixelChannel(destination_image,destination_channel,p[offset],q);
184 p+=GetPixelChannels(source_image);
185 q+=GetPixelChannels(destination_image);
187 if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse)
190 destination_view=DestroyCacheView(destination_view);
191 source_view=DestroyCacheView(source_view);
195 MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
196 ExceptionInfo *exception)
198 #define ChannelFxImageTag "ChannelFx/Image"
201 token[MaxTextExtent];
225 assert(image != (Image *) NULL);
226 assert(image->signature == MagickSignature);
227 if (image->debug != MagickFalse)
228 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
229 assert(exception != (ExceptionInfo *) NULL);
230 assert(exception->signature == MagickSignature);
232 destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
233 if (destination_image == (Image *) NULL)
234 return((Image *) NULL);
235 if (SetImageBackgroundColor(destination_image,exception) == MagickFalse)
237 destination_image=GetLastImageInList(destination_image);
238 return((Image *) NULL);
240 if (expression == (const char *) NULL)
241 return(destination_image);
242 destination_channel=RedPixelChannel;
244 p=(char *) expression;
245 GetMagickToken(p,&p,token);
246 for (channels=0; *p != '\0'; )
255 Interpret channel expression.
259 destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
260 GetMagickToken(p,&p,token);
264 if (GetNextImageInList(source_image) != (Image *) NULL)
265 source_image=GetNextImageInList(source_image);
267 source_image=GetFirstImageInList(source_image);
268 GetMagickToken(p,&p,token);
277 destination_image->colorspace=GRAYColorspace;
278 InitializePixelChannelMap(destination_image);
280 canvas=CloneImage(source_image,0,0,MagickTrue,exception);
281 if (canvas == (Image *) NULL)
283 destination_image=GetLastImageInList(destination_image);
284 return((Image *) NULL);
286 AppendImageToList(&destination_image,canvas);
287 destination_image=GetLastImageInList(destination_image);
288 if (SetImageBackgroundColor(destination_image,exception) == MagickFalse)
290 destination_image=GetLastImageInList(destination_image);
291 return((Image *) NULL);
293 GetMagickToken(p,&p,token);
295 destination_channel=RedPixelChannel;
297 i=ParsePixelChannelOption(token);
300 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
301 "UnrecognizedChannelType","`%s'",token);
302 destination_image=DestroyImageList(destination_image);
305 source_channel=(PixelChannel) i;
306 channel_op=ExtractChannelOp;
307 GetMagickToken(p,&p,token);
310 channel_op=ExchangeChannelOp;
311 GetMagickToken(p,&p,token);
315 if (channel_op != ExchangeChannelOp)
316 channel_op=AssignChannelOp;
317 GetMagickToken(p,&p,token);
321 if (channel_op != ExchangeChannelOp)
322 channel_op=TransferChannelOp;
323 GetMagickToken(p,&p,token);
327 case AssignChannelOp:
329 pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
332 case ExchangeChannelOp:
333 case TransferChannelOp:
335 i=ParsePixelChannelOption(token);
338 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
339 "UnrecognizedChannelType","`%s'",token);
340 destination_image=DestroyImageList(destination_image);
343 destination_channel=(PixelChannel) i;
346 case ExtractChannelOp:
349 status=ChannelImage(destination_image,source_image,source_channel,
350 destination_channel,channel_op,ClampToQuantum(pixel),exception);
351 if (status == MagickFalse)
353 destination_image=DestroyImageList(destination_image);
356 if (channel_op == ExchangeChannelOp)
358 status=ChannelImage(destination_image,source_image,destination_channel,
359 source_channel,channel_op,ClampToQuantum(pixel),exception);
360 if (status == MagickFalse)
362 destination_image=DestroyImageList(destination_image);
367 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
369 if (status == MagickFalse)
374 destination_image->colorspace=GRAYColorspace;
375 InitializePixelChannelMap(destination_image);
377 return(destination_image);
381 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
385 % C o m b i n e I m a g e s %
389 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
391 % CombineImages() combines one or more images into a single image. The
392 % grayscale value of the pixels of each image in the sequence is assigned in
393 % order to the specified channels of the combined image. The typical
394 % ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
396 % The format of the CombineImages method is:
398 % Image *CombineImages(const Image *image,ExceptionInfo *exception)
400 % A description of each parameter follows:
402 % o image: the image.
404 % o exception: return any errors or warnings in this structure.
407 MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
409 #define CombineImageTag "Combine/Image"
427 Ensure the image are the same size.
429 assert(image != (const Image *) NULL);
430 assert(image->signature == MagickSignature);
431 if (image->debug != MagickFalse)
432 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
433 assert(exception != (ExceptionInfo *) NULL);
434 assert(exception->signature == MagickSignature);
435 combine_image=CloneImage(image,0,0,MagickTrue,exception);
436 if (combine_image == (Image *) NULL)
437 return((Image *) NULL);
438 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
440 combine_image=DestroyImage(combine_image);
441 return((Image *) NULL);
443 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
444 combine_image->matte=MagickTrue;
450 combine_view=AcquireCacheView(combine_image);
451 for (y=0; y < (ssize_t) combine_image->rows; y++)
462 register const Quantum
471 if (status == MagickFalse)
473 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
475 if (pixels == (Quantum *) NULL)
481 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
493 if (next == (Image *) NULL)
495 channel=GetPixelChannelMapChannel(image,i);
496 traits=GetPixelChannelMapTraits(image,channel);
497 combine_traits=GetPixelChannelMapTraits(combine_image,channel);
498 if ((traits == UndefinedPixelTrait) ||
499 (combine_traits == UndefinedPixelTrait))
501 image_view=AcquireCacheView(next);
502 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
503 if (p == (const Quantum *) NULL)
506 for (x=0; x < (ssize_t) combine_image->columns; x++)
508 if (x < (ssize_t) image->columns)
510 q[i]=GetPixelGray(image,p);
511 p+=GetPixelChannels(image);
513 q+=GetPixelChannels(combine_image);
515 image_view=DestroyCacheView(image_view);
516 next=GetNextImageInList(next);
517 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
519 if (image->progress_monitor != (MagickProgressMonitor) NULL)
524 proceed=SetImageProgress(image,CombineImageTag,progress++,
525 combine_image->rows);
526 if (proceed == MagickFalse)
531 combine_view=DestroyCacheView(combine_view);
532 if (status == MagickFalse)
533 combine_image=DestroyImage(combine_image);
534 return(combine_image);
538 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
542 % S e p a r a t e I m a g e %
546 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
548 % SeparateImage() separates a channel from the image and returns it as a
551 % The format of the SeparateImage method is:
553 % Image *SeparateImage(const Image *image,const ChannelType channel,
554 % ExceptionInfo *exception)
556 % A description of each parameter follows:
558 % o image: the image.
560 % o channel: the image channel.
562 % o exception: return any errors or warnings in this structure.
565 MagickExport Image *SeparateImage(const Image *image,
566 const ChannelType channel_type,ExceptionInfo *exception)
568 #define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
569 #define SeparateImageTag "Separate/Image"
588 Initialize spread image attributes.
590 assert(image != (Image *) NULL);
591 assert(image->signature == MagickSignature);
592 if (image->debug != MagickFalse)
593 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
594 assert(exception != (ExceptionInfo *) NULL);
595 assert(exception->signature == MagickSignature);
596 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
598 if (separate_image == (Image *) NULL)
599 return((Image *) NULL);
600 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
602 separate_image=DestroyImage(separate_image);
603 return((Image *) NULL);
605 separate_image->colorspace=GRAYColorspace;
611 image_view=AcquireCacheView(image);
612 separate_view=AcquireCacheView(separate_image);
613 #if defined(MAGICKCORE_OPENMP_SUPPORT)
614 #pragma omp parallel for schedule(static) shared(progress,status)
616 for (y=0; y < (ssize_t) image->rows; y++)
618 register const Quantum
627 if (status == MagickFalse)
629 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
630 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
632 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
637 for (x=0; x < (ssize_t) image->columns; x++)
642 if (GetPixelMask(image,p) != 0)
644 p+=GetPixelChannels(image);
645 q+=GetPixelChannels(separate_image);
648 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
649 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
657 channel=GetPixelChannelMapChannel(image,i);
658 traits=GetPixelChannelMapTraits(image,channel);
659 if ((traits == UndefinedPixelTrait) ||
660 (GetChannelBit(channel_type,channel) == 0))
662 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
664 p+=GetPixelChannels(image);
665 q+=GetPixelChannels(separate_image);
667 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
669 if (image->progress_monitor != (MagickProgressMonitor) NULL)
674 #if defined(MAGICKCORE_OPENMP_SUPPORT)
675 #pragma omp critical (MagickCore_SeparateImage)
677 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
678 if (proceed == MagickFalse)
682 separate_view=DestroyCacheView(separate_view);
683 image_view=DestroyCacheView(image_view);
684 return(separate_image);
688 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
692 % S e p a r a t e I m a g e s %
696 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
698 % SeparateImages() returns a separate grayscale image for each channel
701 % The format of the SeparateImages method is:
703 % MagickBooleanType SeparateImages(const Image *image,
704 % ExceptionInfo *exception)
706 % A description of each parameter follows:
708 % o image: the image.
710 % o exception: return any errors or warnings in this structure.
713 MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
722 assert(image != (Image *) NULL);
723 assert(image->signature == MagickSignature);
724 if (image->debug != MagickFalse)
725 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
726 images=NewImageList();
727 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
735 channel=GetPixelChannelMapChannel(image,i);
736 traits=GetPixelChannelMapTraits(image,channel);
737 if ((traits == UndefinedPixelTrait) ||
738 ((traits & UpdatePixelTrait) == 0))
740 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
741 if (separate_image != (Image *) NULL)
742 AppendImageToList(&images,separate_image);