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 PixelChannel destination_channel,const ChannelFx channel_op,
115 const Image *source_image,const PixelChannel source_channel,
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 destination_traits=GetPixelChannelMapTraits(destination_image,
174 destination_channel);
175 if (destination_traits == UndefinedPixelTrait)
177 if (channel_op == AssignChannelOp)
178 SetPixelChannel(destination_image,destination_channel,pixel,q);
181 source_traits=GetPixelChannelMapTraits(source_image,source_channel);
182 if (source_traits == UndefinedPixelTrait)
184 offset=GetPixelChannelMapOffset(source_image,source_channel);
185 SetPixelChannel(destination_image,destination_channel,p[offset],q);
186 p+=GetPixelChannels(source_image);
188 q+=GetPixelChannels(destination_image);
190 if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse)
193 destination_view=DestroyCacheView(destination_view);
194 source_view=DestroyCacheView(source_view);
198 MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
199 ExceptionInfo *exception)
201 #define ChannelFxImageTag "ChannelFx/Image"
204 token[MaxTextExtent];
228 assert(image != (Image *) NULL);
229 assert(image->signature == MagickSignature);
230 if (image->debug != MagickFalse)
231 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
232 assert(exception != (ExceptionInfo *) NULL);
233 assert(exception->signature == MagickSignature);
235 destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
236 if (destination_image == (Image *) NULL)
237 return((Image *) NULL);
238 if (SetImageBackgroundColor(destination_image,exception) == MagickFalse)
240 destination_image=GetLastImageInList(destination_image);
241 return((Image *) NULL);
243 if (expression == (const char *) NULL)
244 return(destination_image);
245 destination_channel=RedPixelChannel;
247 p=(char *) expression;
248 GetMagickToken(p,&p,token);
249 for (channels=0; *p != '\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);
280 destination_image->colorspace=GRAYColorspace;
281 InitializePixelChannelMap(destination_image);
283 canvas=CloneImage(source_image,0,0,MagickTrue,exception);
284 if (canvas == (Image *) NULL)
286 destination_image=GetLastImageInList(destination_image);
287 return((Image *) NULL);
289 AppendImageToList(&destination_image,canvas);
290 destination_image=GetLastImageInList(destination_image);
291 if (SetImageBackgroundColor(destination_image,exception) == MagickFalse)
293 destination_image=GetLastImageInList(destination_image);
294 return((Image *) NULL);
296 GetMagickToken(p,&p,token);
298 destination_channel=RedPixelChannel;
300 i=ParsePixelChannelOption(token);
303 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
304 "UnrecognizedChannelType","`%s'",token);
305 destination_image=DestroyImageList(destination_image);
308 source_channel=(PixelChannel) i;
309 channel_op=ExtractChannelOp;
310 GetMagickToken(p,&p,token);
313 channel_op=ExchangeChannelOp;
314 GetMagickToken(p,&p,token);
318 if (channel_op != ExchangeChannelOp)
319 channel_op=AssignChannelOp;
320 GetMagickToken(p,&p,token);
324 if (channel_op != ExchangeChannelOp)
325 channel_op=TransferChannelOp;
326 GetMagickToken(p,&p,token);
330 case AssignChannelOp:
332 pixel=StringToDouble(token,(char **) NULL);
333 GetMagickToken(p,&p,token);
336 case ExchangeChannelOp:
337 case TransferChannelOp:
339 i=ParsePixelChannelOption(token);
342 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
343 "UnrecognizedChannelType","`%s'",token);
344 destination_image=DestroyImageList(destination_image);
347 destination_channel=(PixelChannel) i;
348 GetMagickToken(p,&p,token);
351 case ExtractChannelOp:
354 status=ChannelImage(destination_image,destination_channel,channel_op,
355 source_image,source_channel,ClampToQuantum(pixel),exception);
356 if (status == MagickFalse)
358 destination_image=DestroyImageList(destination_image);
361 if (channel_op == ExchangeChannelOp)
363 status=ChannelImage(destination_image,destination_channel,channel_op,
364 source_image,source_channel,ClampToQuantum(pixel),exception);
365 if (status == MagickFalse)
367 destination_image=DestroyImageList(destination_image);
372 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
374 if (status == MagickFalse)
379 destination_image->colorspace=GRAYColorspace;
380 InitializePixelChannelMap(destination_image);
382 return(destination_image);
386 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
390 % C o m b i n e I m a g e s %
394 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
396 % CombineImages() combines one or more images into a single image. The
397 % grayscale value of the pixels of each image in the sequence is assigned in
398 % order to the specified channels of the combined image. The typical
399 % ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
401 % The format of the CombineImages method is:
403 % Image *CombineImages(const Image *image,ExceptionInfo *exception)
405 % A description of each parameter follows:
407 % o image: the image.
409 % o exception: return any errors or warnings in this structure.
412 MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
414 #define CombineImageTag "Combine/Image"
432 Ensure the image are the same size.
434 assert(image != (const Image *) NULL);
435 assert(image->signature == MagickSignature);
436 if (image->debug != MagickFalse)
437 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
438 assert(exception != (ExceptionInfo *) NULL);
439 assert(exception->signature == MagickSignature);
440 combine_image=CloneImage(image,0,0,MagickTrue,exception);
441 if (combine_image == (Image *) NULL)
442 return((Image *) NULL);
443 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
445 combine_image=DestroyImage(combine_image);
446 return((Image *) NULL);
448 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
449 combine_image->matte=MagickTrue;
455 combine_view=AcquireCacheView(combine_image);
456 for (y=0; y < (ssize_t) combine_image->rows; y++)
467 register const Quantum
476 if (status == MagickFalse)
478 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
480 if (pixels == (Quantum *) NULL)
486 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
498 if (next == (Image *) NULL)
500 channel=GetPixelChannelMapChannel(image,i);
501 traits=GetPixelChannelMapTraits(image,channel);
502 combine_traits=GetPixelChannelMapTraits(combine_image,channel);
503 if ((traits == UndefinedPixelTrait) ||
504 (combine_traits == UndefinedPixelTrait))
506 image_view=AcquireCacheView(next);
507 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
508 if (p == (const Quantum *) NULL)
511 for (x=0; x < (ssize_t) combine_image->columns; x++)
513 if (x < (ssize_t) image->columns)
515 q[i]=GetPixelGray(image,p);
516 p+=GetPixelChannels(image);
518 q+=GetPixelChannels(combine_image);
520 image_view=DestroyCacheView(image_view);
521 next=GetNextImageInList(next);
522 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
524 if (image->progress_monitor != (MagickProgressMonitor) NULL)
529 proceed=SetImageProgress(image,CombineImageTag,progress++,
530 combine_image->rows);
531 if (proceed == MagickFalse)
536 combine_view=DestroyCacheView(combine_view);
537 if (status == MagickFalse)
538 combine_image=DestroyImage(combine_image);
539 return(combine_image);
543 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
547 % S e p a r a t e I m a g e %
551 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
553 % SeparateImage() separates a channel from the image and returns it as a
556 % The format of the SeparateImage method is:
558 % Image *SeparateImage(const Image *image,const ChannelType channel,
559 % ExceptionInfo *exception)
561 % A description of each parameter follows:
563 % o image: the image.
565 % o channel: the image channel.
567 % o exception: return any errors or warnings in this structure.
570 MagickExport Image *SeparateImage(const Image *image,
571 const ChannelType channel_type,ExceptionInfo *exception)
573 #define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
574 #define SeparateImageTag "Separate/Image"
593 Initialize spread image attributes.
595 assert(image != (Image *) NULL);
596 assert(image->signature == MagickSignature);
597 if (image->debug != MagickFalse)
598 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
599 assert(exception != (ExceptionInfo *) NULL);
600 assert(exception->signature == MagickSignature);
601 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
603 if (separate_image == (Image *) NULL)
604 return((Image *) NULL);
605 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
607 separate_image=DestroyImage(separate_image);
608 return((Image *) NULL);
610 separate_image->colorspace=GRAYColorspace;
616 image_view=AcquireCacheView(image);
617 separate_view=AcquireCacheView(separate_image);
618 #if defined(MAGICKCORE_OPENMP_SUPPORT)
619 #pragma omp parallel for schedule(static) shared(progress,status)
621 for (y=0; y < (ssize_t) image->rows; y++)
623 register const Quantum
632 if (status == MagickFalse)
634 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
635 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
637 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
642 for (x=0; x < (ssize_t) image->columns; x++)
647 if (GetPixelMask(image,p) != 0)
649 p+=GetPixelChannels(image);
650 q+=GetPixelChannels(separate_image);
653 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
654 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
662 channel=GetPixelChannelMapChannel(image,i);
663 traits=GetPixelChannelMapTraits(image,channel);
664 if ((traits == UndefinedPixelTrait) ||
665 (GetChannelBit(channel_type,channel) == 0))
667 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
669 p+=GetPixelChannels(image);
670 q+=GetPixelChannels(separate_image);
672 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
674 if (image->progress_monitor != (MagickProgressMonitor) NULL)
679 #if defined(MAGICKCORE_OPENMP_SUPPORT)
680 #pragma omp critical (MagickCore_SeparateImage)
682 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
683 if (proceed == MagickFalse)
687 separate_view=DestroyCacheView(separate_view);
688 image_view=DestroyCacheView(image_view);
689 return(separate_image);
693 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
697 % S e p a r a t e I m a g e s %
701 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
703 % SeparateImages() returns a separate grayscale image for each channel
706 % The format of the SeparateImages method is:
708 % MagickBooleanType SeparateImages(const Image *image,
709 % ExceptionInfo *exception)
711 % A description of each parameter follows:
713 % o image: the image.
715 % o exception: return any errors or warnings in this structure.
718 MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
727 assert(image != (Image *) NULL);
728 assert(image->signature == MagickSignature);
729 if (image->debug != MagickFalse)
730 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
731 images=NewImageList();
732 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
740 channel=GetPixelChannelMapChannel(image,i);
741 traits=GetPixelChannelMapTraits(image,channel);
742 if ((traits == UndefinedPixelTrait) ||
743 ((traits & UpdatePixelTrait) == 0))
745 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
746 if (separate_image != (Image *) NULL)
747 AppendImageToList(&images,separate_image);