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/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/string-private.h"
53 #include "MagickCore/token.h"
54 #include "MagickCore/utility.h"
55 #include "MagickCore/version.h"
58 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
62 % C h a n n e l F x I m a g e %
66 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
68 % ChannelFxImage() applies a channel expression to the specified image. The
69 % expression consists of one or more channels, either mnemonic or numeric (e.g.
70 % red, 1), separated by actions as follows:
72 % <=> exchange two channels (e.g. red<=>blue)
73 % => copy one channel to another channel (e.g. red=>green)
74 % = assign a constant value to a channel (e.g. red=50%)
75 % , write new image channels in the specified order (e.g. red, green)
76 % | add a new output image for the next set of channel operations
77 % ; move to the next input image for the source of channel data
79 % For example, to create 3 grayscale images from the red, green, and blue
80 % channels of an image, use:
82 % -channel-fx "red; green; blue"
84 % A channel without an operation symbol implies separate (i.e, semicolon).
86 % The format of the ChannelFxImage method is:
88 % Image *ChannelFxImage(const Image *image,const char *expression,
89 % ExceptionInfo *exception)
91 % A description of each parameter follows:
95 % o expression: A channel expression.
97 % o exception: return any errors or warnings in this structure.
109 static inline size_t MagickMin(const size_t x,const size_t y)
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)
135 source_view=AcquireVirtualCacheView(source_image,exception);
136 destination_view=AcquireAuthenticCacheView(destination_image,exception);
137 height=MagickMin(source_image->rows,destination_image->rows);
138 #if defined(MAGICKCORE_OPENMP_SUPPORT)
139 #pragma omp parallel for schedule(static) shared(status)
141 for (y=0; y < (ssize_t) height; y++)
147 register const Quantum
159 if (status == MagickFalse)
161 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
163 q=GetCacheViewAuthenticPixels(destination_view,0,y,
164 destination_image->columns,1,exception);
165 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
170 destination_traits=GetPixelChannelMapTraits(destination_image,
171 destination_channel);
172 source_traits=GetPixelChannelMapTraits(source_image,source_channel);
173 if ((destination_traits == UndefinedPixelTrait) ||
174 (source_traits == UndefinedPixelTrait))
176 width=MagickMin(source_image->columns,destination_image->columns);
177 for (x=0; x < (ssize_t) width; x++)
179 if (channel_op == AssignChannelOp)
180 SetPixelChannel(destination_image,destination_channel,pixel,q);
182 SetPixelChannel(destination_image,destination_channel,
183 GetPixelChannel(source_image,source_channel,p),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"
207 token[MaxTextExtent];
231 assert(image != (Image *) NULL);
232 assert(image->signature == MagickSignature);
233 if (image->debug != MagickFalse)
234 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
235 assert(exception != (ExceptionInfo *) NULL);
236 assert(exception->signature == MagickSignature);
238 destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
239 if (destination_image == (Image *) NULL)
240 return((Image *) NULL);
241 if (destination_image->colorspace == GRAYColorspace)
242 destination_image->colorspace=sRGBColorspace;
243 if (expression == (const char *) NULL)
244 return(destination_image);
245 destination_channel=RedPixelChannel;
246 channel_mask=UndefinedChannel;
248 p=(char *) expression;
249 GetMagickToken(p,&p,token);
250 channel_op=ExtractChannelOp;
251 for (channels=0; *token != '\0'; )
257 Interpret channel expression.
261 destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
262 GetMagickToken(p,&p,token);
266 if (GetNextImageInList(source_image) != (Image *) NULL)
267 source_image=GetNextImageInList(source_image);
269 source_image=GetFirstImageInList(source_image);
270 GetMagickToken(p,&p,token);
277 SetPixelChannelMapMask(destination_image,channel_mask);
278 if ((channel_op == ExtractChannelOp) && (destination_channel == 1))
279 (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
280 status=SetImageStorageClass(destination_image,DirectClass,exception);
281 if (status == MagickFalse)
283 destination_image=DestroyImageList(destination_image);
284 return(destination_image);
286 canvas=CloneImage(source_image,0,0,MagickTrue,exception);
287 if (canvas == (Image *) NULL)
289 destination_image=DestroyImageList(destination_image);
290 return(destination_image);
292 if (canvas->colorspace == GRAYColorspace)
293 canvas->colorspace=sRGBColorspace;
294 AppendImageToList(&destination_image,canvas);
295 destination_image=GetLastImageInList(destination_image);
296 GetMagickToken(p,&p,token);
298 destination_channel=RedPixelChannel;
299 channel_mask=UndefinedChannel;
301 i=ParsePixelChannelOption(token);
304 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
305 "UnrecognizedChannelType","'%s'",token);
306 destination_image=DestroyImageList(destination_image);
307 return(destination_image);
309 source_channel=(PixelChannel) i;
310 channel_op=ExtractChannelOp;
311 GetMagickToken(p,&p,token);
314 channel_op=ExchangeChannelOp;
315 GetMagickToken(p,&p,token);
319 if (channel_op != ExchangeChannelOp)
320 channel_op=AssignChannelOp;
321 GetMagickToken(p,&p,token);
325 if (channel_op != ExchangeChannelOp)
326 channel_op=TransferChannelOp;
327 GetMagickToken(p,&p,token);
331 case AssignChannelOp:
333 pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
334 GetMagickToken(p,&p,token);
337 case ExchangeChannelOp:
338 case TransferChannelOp:
340 i=ParsePixelChannelOption(token);
343 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
344 "UnrecognizedChannelType","'%s'",token);
345 destination_image=DestroyImageList(destination_image);
346 return(destination_image);
348 destination_channel=(PixelChannel) i;
349 channel_mask=(ChannelType) (channel_mask | ParseChannelOption(token));
350 if (LocaleCompare(token,"gray") == 0)
351 (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
352 if ((LocaleCompare(token,"black") == 0) ||
353 (LocaleCompare(token,"c") == 0) ||
354 (LocaleCompare(token,"cyan") == 0) ||
355 (LocaleCompare(token,"k") == 0) ||
356 (LocaleCompare(token,"m") == 0) ||
357 (LocaleCompare(token,"magenta") == 0) ||
358 (LocaleCompare(token,"y") == 0) ||
359 (LocaleCompare(token,"yellow") == 0))
360 (void) SetImageColorspace(destination_image,CMYKColorspace,exception);
361 if ((LocaleCompare(token,"Cb") == 0) ||
362 (LocaleCompare(token,"Cr") == 0))
363 (void) SetImageColorspace(destination_image,YCbCrColorspace,
365 if (LocaleCompare(token,"alpha") == 0)
366 (void) SetImageAlpha(destination_image,OpaqueAlpha,exception);
367 if (i >= (ssize_t) GetPixelChannels(destination_image))
368 (void) SetPixelMetaChannels(destination_image,(size_t) (i-
369 GetPixelChannels(destination_image)+1),exception);
370 GetMagickToken(p,&p,token);
376 status=ChannelImage(destination_image,destination_channel,channel_op,
377 source_image,source_channel,ClampToQuantum(pixel),exception);
378 if (status == MagickFalse)
380 destination_image=DestroyImageList(destination_image);
384 if (channel_op == ExchangeChannelOp)
386 status=ChannelImage(destination_image,source_channel,channel_op,
387 source_image,destination_channel,ClampToQuantum(pixel),exception);
388 if (status == MagickFalse)
390 destination_image=DestroyImageList(destination_image);
397 case ExtractChannelOp:
399 channel_mask=(ChannelType) (channel_mask | (1 << destination_channel));
400 destination_channel=(PixelChannel) (destination_channel+1);
406 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
408 if (status == MagickFalse)
411 SetPixelChannelMapMask(destination_image,channel_mask);
412 if ((channel_op == ExtractChannelOp) && (destination_channel == 1))
413 (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
414 status=SetImageStorageClass(destination_image,DirectClass,exception);
415 if (status == MagickFalse)
417 destination_image=GetLastImageInList(destination_image);
418 return((Image *) NULL);
420 return(GetFirstImageInList(destination_image));
424 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
428 % C o m b i n e I m a g e s %
432 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
434 % CombineImages() combines one or more images into a single image. The
435 % grayscale value of the pixels of each image in the sequence is assigned in
436 % order to the specified channels of the combined image. The typical
437 % ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
439 % The format of the CombineImages method is:
441 % Image *CombineImages(const Image *image,ExceptionInfo *exception)
443 % A description of each parameter follows:
445 % o image: the image.
447 % o exception: return any errors or warnings in this structure.
450 MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
452 #define CombineImageTag "Combine/Image"
470 Ensure the image are the same size.
472 assert(image != (const Image *) NULL);
473 assert(image->signature == MagickSignature);
474 if (image->debug != MagickFalse)
475 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
476 assert(exception != (ExceptionInfo *) NULL);
477 assert(exception->signature == MagickSignature);
478 combine_image=CloneImage(image,0,0,MagickTrue,exception);
479 if (combine_image == (Image *) NULL)
480 return((Image *) NULL);
481 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
483 combine_image=DestroyImage(combine_image);
484 return((Image *) NULL);
486 if (IsGrayColorspace(image->colorspace) != MagickFalse)
487 (void) SetImageColorspace(combine_image,sRGBColorspace,exception);
488 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
489 combine_image->matte=MagickTrue;
495 combine_view=AcquireAuthenticCacheView(combine_image,exception);
496 for (y=0; y < (ssize_t) combine_image->rows; y++)
507 register const Quantum
516 if (status == MagickFalse)
518 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
520 if (pixels == (Quantum *) NULL)
526 for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
537 if (next == (Image *) NULL)
539 channel=GetPixelChannelMapChannel(combine_image,i);
540 traits=GetPixelChannelMapTraits(combine_image,channel);
541 if (traits == UndefinedPixelTrait)
543 image_view=AcquireVirtualCacheView(next,exception);
544 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
545 if (p == (const Quantum *) NULL)
548 for (x=0; x < (ssize_t) combine_image->columns; x++)
550 if (x < (ssize_t) image->columns)
552 q[i]=GetPixelGray(image,p);
553 p+=GetPixelChannels(image);
555 q+=GetPixelChannels(combine_image);
557 image_view=DestroyCacheView(image_view);
558 next=GetNextImageInList(next);
559 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
561 if (image->progress_monitor != (MagickProgressMonitor) NULL)
566 proceed=SetImageProgress(image,CombineImageTag,progress++,
567 combine_image->rows);
568 if (proceed == MagickFalse)
573 combine_view=DestroyCacheView(combine_view);
574 if (status == MagickFalse)
575 combine_image=DestroyImage(combine_image);
576 return(combine_image);
580 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
584 % S e p a r a t e I m a g e %
588 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
590 % SeparateImage() separates a channel from the image and returns it as a
593 % The format of the SeparateImage method is:
595 % Image *SeparateImage(const Image *image,const ChannelType channel,
596 % ExceptionInfo *exception)
598 % A description of each parameter follows:
600 % o image: the image.
602 % o channel: the image channel.
604 % o exception: return any errors or warnings in this structure.
607 MagickExport Image *SeparateImage(const Image *image,
608 const ChannelType channel_type,ExceptionInfo *exception)
610 #define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
611 #define SeparateImageTag "Separate/Image"
630 Initialize spread image attributes.
632 assert(image != (Image *) NULL);
633 assert(image->signature == MagickSignature);
634 if (image->debug != MagickFalse)
635 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
636 assert(exception != (ExceptionInfo *) NULL);
637 assert(exception->signature == MagickSignature);
638 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
640 if (separate_image == (Image *) NULL)
641 return((Image *) NULL);
642 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
644 separate_image=DestroyImage(separate_image);
645 return((Image *) NULL);
647 separate_image->colorspace=GRAYColorspace;
653 image_view=AcquireVirtualCacheView(image,exception);
654 separate_view=AcquireAuthenticCacheView(separate_image,exception);
655 #if defined(MAGICKCORE_OPENMP_SUPPORT)
656 #pragma omp parallel for schedule(static) shared(progress,status)
658 for (y=0; y < (ssize_t) image->rows; y++)
660 register const Quantum
669 if (status == MagickFalse)
671 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
672 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
674 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
679 for (x=0; x < (ssize_t) image->columns; x++)
684 if (GetPixelMask(image,p) != 0)
686 p+=GetPixelChannels(image);
687 q+=GetPixelChannels(separate_image);
690 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
691 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
699 channel=GetPixelChannelMapChannel(image,i);
700 traits=GetPixelChannelMapTraits(image,channel);
701 if ((traits == UndefinedPixelTrait) ||
702 (GetChannelBit(channel_type,channel) == 0))
704 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
706 p+=GetPixelChannels(image);
707 q+=GetPixelChannels(separate_image);
709 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
711 if (image->progress_monitor != (MagickProgressMonitor) NULL)
716 #if defined(MAGICKCORE_OPENMP_SUPPORT)
717 #pragma omp critical (MagickCore_SeparateImage)
719 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
720 if (proceed == MagickFalse)
724 separate_view=DestroyCacheView(separate_view);
725 image_view=DestroyCacheView(image_view);
726 return(separate_image);
730 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
734 % S e p a r a t e I m a g e s %
738 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
740 % SeparateImages() returns a separate grayscale image for each channel
743 % The format of the SeparateImages method is:
745 % Image *SeparateImages(const Image *image,ExceptionInfo *exception)
747 % A description of each parameter follows:
749 % o image: the image.
751 % o exception: return any errors or warnings in this structure.
754 MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
763 assert(image != (Image *) NULL);
764 assert(image->signature == MagickSignature);
765 if (image->debug != MagickFalse)
766 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
767 images=NewImageList();
768 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
776 channel=GetPixelChannelMapChannel(image,i);
777 traits=GetPixelChannelMapTraits(image,channel);
778 if ((traits == UndefinedPixelTrait) ||
779 ((traits & UpdatePixelTrait) == 0))
781 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
782 if (separate_image != (Image *) NULL)
783 AppendImageToList(&images,separate_image);