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 % => copy one channel to another channel (e.g. red=>green)
73 % = assign a constant value to a channel (e.g. red=50%)
74 % , write new image channels in the specified order (e.g. red, green)
75 % | add a new output image for the next set of channel operations
76 % ; move to the next input image for the source of channel data
78 % For example, to create 3 grayscale images from the red, green, and blue
79 % channels of an image, use:
81 % -channel-fx "red; green; blue"
83 % A channel without an operation symbol implies separate (i.e, semicolon).
85 % The format of the ChannelFxImage method is:
87 % Image *ChannelFxImage(const Image *image,const char *expression,
88 % ExceptionInfo *exception)
90 % A description of each parameter follows:
94 % o expression: A channel expression.
96 % o exception: return any errors or warnings in this structure.
108 static inline size_t MagickMin(const size_t x,const size_t y)
115 static MagickBooleanType ChannelImage(Image *destination_image,
116 const PixelChannel destination_channel,const ChannelFx channel_op,
117 const Image *source_image,const PixelChannel source_channel,
118 const Quantum pixel,ExceptionInfo *exception)
134 source_view=AcquireCacheView(source_image);
135 destination_view=AcquireCacheView(destination_image);
136 height=MagickMin(source_image->rows,destination_image->rows);
137 #if defined(MAGICKCORE_OPENMP_SUPPORT)
138 #pragma omp parallel for schedule(static) shared(status)
140 for (y=0; y < (ssize_t) height; y++)
146 register const Quantum
158 if (status == MagickFalse)
160 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
162 q=GetCacheViewAuthenticPixels(destination_view,0,y,
163 destination_image->columns,1,exception);
164 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
169 destination_traits=GetPixelChannelMapTraits(destination_image,
170 destination_channel);
171 source_traits=GetPixelChannelMapTraits(source_image,source_channel);
172 if ((destination_traits == UndefinedPixelTrait) ||
173 (source_traits == UndefinedPixelTrait))
175 width=MagickMin(source_image->columns,destination_image->columns);
176 for (x=0; x < (ssize_t) width; x++)
178 if (channel_op == AssignChannelOp)
179 SetPixelChannel(destination_image,destination_channel,pixel,q);
181 SetPixelChannel(destination_image,destination_channel,
182 GetPixelChannel(source_image,source_channel,p),q);
183 p+=GetPixelChannels(source_image);
184 q+=GetPixelChannels(destination_image);
186 if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse)
189 destination_view=DestroyCacheView(destination_view);
190 source_view=DestroyCacheView(source_view);
194 MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
195 ExceptionInfo *exception)
197 #define ChannelFxImageTag "ChannelFx/Image"
203 token[MaxTextExtent];
227 assert(image != (Image *) NULL);
228 assert(image->signature == MagickSignature);
229 if (image->debug != MagickFalse)
230 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
231 assert(exception != (ExceptionInfo *) NULL);
232 assert(exception->signature == MagickSignature);
234 destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
235 if (destination_image == (Image *) NULL)
236 return((Image *) NULL);
237 if (destination_image->colorspace == GRAYColorspace)
238 destination_image->colorspace=RGBColorspace;
239 if (expression == (const char *) NULL)
240 return(destination_image);
241 destination_channel=RedPixelChannel;
243 p=(char *) expression;
244 GetMagickToken(p,&p,token);
245 channel_op=ExtractChannelOp;
246 for (channels=0; *token != '\0'; )
252 Interpret channel expression.
256 destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
257 GetMagickToken(p,&p,token);
261 if (GetNextImageInList(source_image) != (Image *) NULL)
262 source_image=GetNextImageInList(source_image);
264 source_image=GetFirstImageInList(source_image);
265 GetMagickToken(p,&p,token);
272 if ((channel_op == ExtractChannelOp) && (channels == 1))
274 destination_image->colorspace=GRAYColorspace;
275 InitializePixelChannelMap(destination_image);
277 status=SetImageStorageClass(destination_image,DirectClass,exception);
278 if (status == MagickFalse)
280 destination_image=DestroyImageList(destination_image);
281 return(destination_image);
283 canvas=CloneImage(source_image,0,0,MagickTrue,exception);
284 if (canvas == (Image *) NULL)
286 destination_image=DestroyImageList(destination_image);
287 return(destination_image);
289 if (canvas->colorspace == GRAYColorspace)
290 canvas->colorspace=RGBColorspace;
291 AppendImageToList(&destination_image,canvas);
292 destination_image=GetLastImageInList(destination_image);
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);
303 return(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);
330 GetMagickToken(p,&p,token);
333 case ExchangeChannelOp:
334 case TransferChannelOp:
336 i=ParsePixelChannelOption(token);
339 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
340 "UnrecognizedChannelType","`%s'",token);
341 destination_image=DestroyImageList(destination_image);
342 return(destination_image);
344 destination_channel=(PixelChannel) i;
345 if ((LocaleCompare(token,"black") == 0) ||
346 (LocaleCompare(token,"c") == 0) ||
347 (LocaleCompare(token,"cyan") == 0) ||
348 (LocaleCompare(token,"k") == 0) ||
349 (LocaleCompare(token,"m") == 0) ||
350 (LocaleCompare(token,"magenta") == 0) ||
351 (LocaleCompare(token,"y") == 0) ||
352 (LocaleCompare(token,"yellow") == 0))
353 (void) SetImageColorspace(destination_image,CMYKColorspace,exception);
354 if ((LocaleCompare(token,"Cb") == 0) ||
355 (LocaleCompare(token,"Cr") == 0))
356 (void) SetImageColorspace(destination_image,YCbCrColorspace,
358 if (LocaleCompare(token,"alpha") == 0)
359 (void) SetImageAlpha(destination_image,OpaqueAlpha,exception);
360 if (i >= GetPixelChannels(destination_image))
361 SetPixelMetaChannels(destination_image,i-
362 GetPixelChannels(destination_image)+1,exception);
363 GetMagickToken(p,&p,token);
366 case ExtractChannelOp:
368 destination_channel++;
372 status=ChannelImage(destination_image,destination_channel,channel_op,
373 source_image,source_channel,ClampToQuantum(pixel),exception);
374 if (status == MagickFalse)
376 destination_image=DestroyImageList(destination_image);
380 if (channel_op == ExchangeChannelOp)
382 status=ChannelImage(destination_image,source_channel,channel_op,
383 source_image,destination_channel,ClampToQuantum(pixel),exception);
384 if (status == MagickFalse)
386 destination_image=DestroyImageList(destination_image);
391 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
393 if (status == MagickFalse)
396 if ((channel_op == ExtractChannelOp) && (channels == 1))
398 destination_image->colorspace=GRAYColorspace;
399 InitializePixelChannelMap(destination_image);
401 status=SetImageStorageClass(destination_image,DirectClass,exception);
402 if (status == MagickFalse)
404 destination_image=GetLastImageInList(destination_image);
405 return((Image *) NULL);
407 return(GetFirstImageInList(destination_image));
411 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
415 % C o m b i n e I m a g e s %
419 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
421 % CombineImages() combines one or more images into a single image. The
422 % grayscale value of the pixels of each image in the sequence is assigned in
423 % order to the specified channels of the combined image. The typical
424 % ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
426 % The format of the CombineImages method is:
428 % Image *CombineImages(const Image *image,ExceptionInfo *exception)
430 % A description of each parameter follows:
432 % o image: the image.
434 % o exception: return any errors or warnings in this structure.
437 MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
439 #define CombineImageTag "Combine/Image"
457 Ensure the image are the same size.
459 assert(image != (const Image *) NULL);
460 assert(image->signature == MagickSignature);
461 if (image->debug != MagickFalse)
462 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
463 assert(exception != (ExceptionInfo *) NULL);
464 assert(exception->signature == MagickSignature);
465 combine_image=CloneImage(image,0,0,MagickTrue,exception);
466 if (combine_image == (Image *) NULL)
467 return((Image *) NULL);
468 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
470 combine_image=DestroyImage(combine_image);
471 return((Image *) NULL);
473 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
474 combine_image->matte=MagickTrue;
480 combine_view=AcquireCacheView(combine_image);
481 for (y=0; y < (ssize_t) combine_image->rows; y++)
492 register const Quantum
501 if (status == MagickFalse)
503 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
505 if (pixels == (Quantum *) NULL)
511 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
523 if (next == (Image *) NULL)
525 channel=GetPixelChannelMapChannel(image,i);
526 traits=GetPixelChannelMapTraits(image,channel);
527 combine_traits=GetPixelChannelMapTraits(combine_image,channel);
528 if ((traits == UndefinedPixelTrait) ||
529 (combine_traits == UndefinedPixelTrait))
531 image_view=AcquireCacheView(next);
532 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
533 if (p == (const Quantum *) NULL)
536 for (x=0; x < (ssize_t) combine_image->columns; x++)
538 if (x < (ssize_t) image->columns)
540 q[i]=GetPixelGray(image,p);
541 p+=GetPixelChannels(image);
543 q+=GetPixelChannels(combine_image);
545 image_view=DestroyCacheView(image_view);
546 next=GetNextImageInList(next);
547 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
549 if (image->progress_monitor != (MagickProgressMonitor) NULL)
554 proceed=SetImageProgress(image,CombineImageTag,progress++,
555 combine_image->rows);
556 if (proceed == MagickFalse)
561 combine_view=DestroyCacheView(combine_view);
562 if (status == MagickFalse)
563 combine_image=DestroyImage(combine_image);
564 return(combine_image);
568 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
572 % S e p a r a t e I m a g e %
576 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
578 % SeparateImage() separates a channel from the image and returns it as a
581 % The format of the SeparateImage method is:
583 % Image *SeparateImage(const Image *image,const ChannelType channel,
584 % ExceptionInfo *exception)
586 % A description of each parameter follows:
588 % o image: the image.
590 % o channel: the image channel.
592 % o exception: return any errors or warnings in this structure.
595 MagickExport Image *SeparateImage(const Image *image,
596 const ChannelType channel_type,ExceptionInfo *exception)
598 #define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
599 #define SeparateImageTag "Separate/Image"
618 Initialize spread image attributes.
620 assert(image != (Image *) NULL);
621 assert(image->signature == MagickSignature);
622 if (image->debug != MagickFalse)
623 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
624 assert(exception != (ExceptionInfo *) NULL);
625 assert(exception->signature == MagickSignature);
626 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
628 if (separate_image == (Image *) NULL)
629 return((Image *) NULL);
630 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
632 separate_image=DestroyImage(separate_image);
633 return((Image *) NULL);
635 separate_image->colorspace=GRAYColorspace;
641 image_view=AcquireCacheView(image);
642 separate_view=AcquireCacheView(separate_image);
643 #if defined(MAGICKCORE_OPENMP_SUPPORT)
644 #pragma omp parallel for schedule(static) shared(progress,status)
646 for (y=0; y < (ssize_t) image->rows; y++)
648 register const Quantum
657 if (status == MagickFalse)
659 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
660 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
662 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
667 for (x=0; x < (ssize_t) image->columns; x++)
672 if (GetPixelMask(image,p) != 0)
674 p+=GetPixelChannels(image);
675 q+=GetPixelChannels(separate_image);
678 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
679 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
687 channel=GetPixelChannelMapChannel(image,i);
688 traits=GetPixelChannelMapTraits(image,channel);
689 if ((traits == UndefinedPixelTrait) ||
690 (GetChannelBit(channel_type,channel) == 0))
692 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
694 p+=GetPixelChannels(image);
695 q+=GetPixelChannels(separate_image);
697 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
699 if (image->progress_monitor != (MagickProgressMonitor) NULL)
704 #if defined(MAGICKCORE_OPENMP_SUPPORT)
705 #pragma omp critical (MagickCore_SeparateImage)
707 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
708 if (proceed == MagickFalse)
712 separate_view=DestroyCacheView(separate_view);
713 image_view=DestroyCacheView(image_view);
714 return(separate_image);
718 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
722 % S e p a r a t e I m a g e s %
726 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
728 % SeparateImages() returns a separate grayscale image for each channel
731 % The format of the SeparateImages method is:
733 % MagickBooleanType SeparateImages(const Image *image,
734 % ExceptionInfo *exception)
736 % A description of each parameter follows:
738 % o image: the image.
740 % o exception: return any errors or warnings in this structure.
743 MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
752 assert(image != (Image *) NULL);
753 assert(image->signature == MagickSignature);
754 if (image->debug != MagickFalse)
755 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
756 images=NewImageList();
757 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
765 channel=GetPixelChannelMapChannel(image,i);
766 traits=GetPixelChannelMapTraits(image,channel);
767 if ((traits == UndefinedPixelTrait) ||
768 ((traits & UpdatePixelTrait) == 0))
770 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
771 if (separate_image != (Image *) NULL)
772 AppendImageToList(&images,separate_image);