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-2013 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/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"
60 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
64 % C h a n n e l F x I m a g e %
68 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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:
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
81 % For example, to create 3 grayscale images from the red, green, and blue
82 % channels of an image, use:
84 % -channel-fx "red; green; blue"
86 % A channel without an operation symbol implies separate (i.e, semicolon).
88 % The format of the ChannelFxImage method is:
90 % Image *ChannelFxImage(const Image *image,const char *expression,
91 % ExceptionInfo *exception)
93 % A description of each parameter follows:
97 % o expression: A channel expression.
99 % o exception: return any errors or warnings in this structure.
111 static inline size_t MagickMin(const size_t x,const size_t y)
118 static 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)
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,4) shared(status) \
144 magick_threads(source_image,source_image,height,1)
146 for (y=0; y < (ssize_t) height; y++)
152 register const Quantum
161 if (status == MagickFalse)
163 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
165 q=GetCacheViewAuthenticPixels(destination_view,0,y,
166 destination_image->columns,1,exception);
167 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
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))
178 for (x=0; x < (ssize_t) width; x++)
180 if (channel_op == AssignChannelOp)
181 SetPixelChannel(destination_image,destination_channel,pixel,q);
183 SetPixelChannel(destination_image,destination_channel,
184 GetPixelChannel(source_image,source_channel,p),q);
185 p+=GetPixelChannels(source_image);
186 q+=GetPixelChannels(destination_image);
188 if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse)
191 destination_view=DestroyCacheView(destination_view);
192 source_view=DestroyCacheView(source_view);
196 MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
197 ExceptionInfo *exception)
199 #define ChannelFxImageTag "ChannelFx/Image"
208 token[MaxTextExtent];
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);
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;
249 p=(char *) expression;
250 GetMagickToken(p,&p,token);
251 channel_op=ExtractChannelOp;
252 for (channels=0; *token != '\0'; )
258 Interpret channel expression.
262 destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
263 GetMagickToken(p,&p,token);
267 if (GetNextImageInList(source_image) != (Image *) NULL)
268 source_image=GetNextImageInList(source_image);
270 source_image=GetFirstImageInList(source_image);
271 GetMagickToken(p,&p,token);
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)
284 destination_image=DestroyImageList(destination_image);
285 return(destination_image);
287 canvas=CloneImage(source_image,0,0,MagickTrue,exception);
288 if (canvas == (Image *) NULL)
290 destination_image=DestroyImageList(destination_image);
291 return(destination_image);
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);
299 destination_channel=RedPixelChannel;
300 channel_mask=UndefinedChannel;
302 i=ParsePixelChannelOption(token);
305 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
306 "UnrecognizedChannelType","'%s'",token);
307 destination_image=DestroyImageList(destination_image);
308 return(destination_image);
310 source_channel=(PixelChannel) i;
311 channel_op=ExtractChannelOp;
312 GetMagickToken(p,&p,token);
315 channel_op=ExchangeChannelOp;
316 GetMagickToken(p,&p,token);
320 if (channel_op != ExchangeChannelOp)
321 channel_op=AssignChannelOp;
322 GetMagickToken(p,&p,token);
326 if (channel_op != ExchangeChannelOp)
327 channel_op=TransferChannelOp;
328 GetMagickToken(p,&p,token);
332 case AssignChannelOp:
334 pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
335 GetMagickToken(p,&p,token);
338 case ExchangeChannelOp:
339 case TransferChannelOp:
341 i=ParsePixelChannelOption(token);
344 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
345 "UnrecognizedChannelType","'%s'",token);
346 destination_image=DestroyImageList(destination_image);
347 return(destination_image);
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,
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);
377 status=ChannelImage(destination_image,destination_channel,channel_op,
378 source_image,source_channel,ClampToQuantum(pixel),exception);
379 if (status == MagickFalse)
381 destination_image=DestroyImageList(destination_image);
385 if (channel_op == ExchangeChannelOp)
387 status=ChannelImage(destination_image,source_channel,channel_op,
388 source_image,destination_channel,ClampToQuantum(pixel),exception);
389 if (status == MagickFalse)
391 destination_image=DestroyImageList(destination_image);
398 case ExtractChannelOp:
400 channel_mask=(ChannelType) (channel_mask | (1 << destination_channel));
401 destination_channel=(PixelChannel) (destination_channel+1);
407 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
409 if (status == MagickFalse)
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)
418 destination_image=GetLastImageInList(destination_image);
419 return((Image *) NULL);
421 return(GetFirstImageInList(destination_image));
425 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
429 % C o m b i n e I m a g e s %
433 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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.
440 % The format of the CombineImages method is:
442 % Image *CombineImages(const Image *images,const ColorspaceType colorspace,
443 % ExceptionInfo *exception)
445 % A description of each parameter follows:
447 % o images: the image sequence.
449 % o colorspace: the image colorspace.
451 % o exception: return any errors or warnings in this structure.
454 MagickExport Image *CombineImages(const Image *image,
455 const ColorspaceType colorspace,ExceptionInfo *exception)
457 #define CombineImageTag "Combine/Image"
475 Ensure the image are the same size.
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)
488 combine_image=DestroyImage(combine_image);
489 return((Image *) NULL);
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;
500 combine_view=AcquireAuthenticCacheView(combine_image,exception);
501 for (y=0; y < (ssize_t) combine_image->rows; y++)
512 register const Quantum
521 if (status == MagickFalse)
523 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
525 if (pixels == (Quantum *) NULL)
531 for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
542 if (next == (Image *) NULL)
544 channel=GetPixelChannelChannel(combine_image,i);
545 traits=GetPixelChannelTraits(combine_image,channel);
546 if (traits == UndefinedPixelTrait)
548 image_view=AcquireVirtualCacheView(next,exception);
549 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
550 if (p == (const Quantum *) NULL)
553 for (x=0; x < (ssize_t) combine_image->columns; x++)
555 if (x < (ssize_t) next->columns)
557 q[i]=GetPixelGray(next,p);
558 p+=GetPixelChannels(next);
560 q+=GetPixelChannels(combine_image);
562 image_view=DestroyCacheView(image_view);
563 next=GetNextImageInList(next);
565 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
567 if (image->progress_monitor != (MagickProgressMonitor) NULL)
572 proceed=SetImageProgress(image,CombineImageTag,progress++,
573 combine_image->rows);
574 if (proceed == MagickFalse)
578 combine_view=DestroyCacheView(combine_view);
579 (void) TransformImageColorspace(combine_image,colorspace,exception);
580 if (status == MagickFalse)
581 combine_image=DestroyImage(combine_image);
582 return(combine_image);
586 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
590 % S e p a r a t e I m a g e %
594 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
596 % SeparateImage() separates a channel from the image and returns it as a
599 % The format of the SeparateImage method is:
601 % Image *SeparateImage(const Image *image,const ChannelType channel,
602 % ExceptionInfo *exception)
604 % A description of each parameter follows:
606 % o image: the image.
608 % o channel: the image channel.
610 % o exception: return any errors or warnings in this structure.
613 MagickExport Image *SeparateImage(const Image *image,
614 const ChannelType channel_type,ExceptionInfo *exception)
616 #define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
617 #define SeparateImageTag "Separate/Image"
636 Initialize spread image attributes.
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,
646 if (separate_image == (Image *) NULL)
647 return((Image *) NULL);
648 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
650 separate_image=DestroyImage(separate_image);
651 return((Image *) NULL);
653 separate_image->alpha_trait=UndefinedPixelTrait;
654 (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
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,4) shared(progress,status) \
664 magick_threads(image,image,image->rows,1)
666 for (y=0; y < (ssize_t) image->rows; y++)
668 register const Quantum
677 if (status == MagickFalse)
679 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
680 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
682 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
687 for (x=0; x < (ssize_t) image->columns; x++)
692 if (GetPixelMask(image,p) != 0)
694 p+=GetPixelChannels(image);
695 q+=GetPixelChannels(separate_image);
698 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
699 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
710 channel=GetPixelChannelChannel(image,i);
711 traits=GetPixelChannelTraits(image,channel);
712 if ((traits == UndefinedPixelTrait) ||
713 (GetChannelBit(channel_type,channel) == 0))
716 if (IssRGBColorspace(image->colorspace) != MagickFalse)
717 pixel=DecodePixelGamma(pixel);
718 SetPixelChannel(separate_image,GrayPixelChannel,ClampToQuantum(pixel),
721 p+=GetPixelChannels(image);
722 q+=GetPixelChannels(separate_image);
724 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
726 if (image->progress_monitor != (MagickProgressMonitor) NULL)
731 #if defined(MAGICKCORE_OPENMP_SUPPORT)
732 #pragma omp critical (MagickCore_SeparateImage)
734 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
735 if (proceed == MagickFalse)
739 separate_view=DestroyCacheView(separate_view);
740 image_view=DestroyCacheView(image_view);
741 if (status == MagickFalse)
742 separate_image=DestroyImage(separate_image);
743 return(separate_image);
747 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
751 % S e p a r a t e I m a g e s %
755 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
757 % SeparateImages() returns a separate grayscale image for each channel
760 % The format of the SeparateImages method is:
762 % Image *SeparateImages(const Image *image,ExceptionInfo *exception)
764 % A description of each parameter follows:
766 % o image: the image.
768 % o exception: return any errors or warnings in this structure.
771 MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
780 assert(image != (Image *) NULL);
781 assert(image->signature == MagickSignature);
782 if (image->debug != MagickFalse)
783 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
784 images=NewImageList();
785 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
793 channel=GetPixelChannelChannel(image,i);
794 traits=GetPixelChannelTraits(image,channel);
795 if ((traits == UndefinedPixelTrait) ||
796 ((traits & UpdatePixelTrait) == 0))
798 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
799 if (separate_image != (Image *) NULL)
800 AppendImageToList(&images,separate_image);
802 if (images == (Image *) NULL)
803 images=SeparateImage(image,UndefinedChannel,exception);