]> granicus.if.org Git - imagemagick/blob - MagickCore/channel.c
(no commit message)
[imagemagick] / MagickCore / channel.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 %                                                                             %
4 %                                                                             %
5 %                                                                             %
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               %
11 %                                                                             %
12 %                                                                             %
13 %                      MagickCore Image Channel Methods                       %
14 %                                                                             %
15 %                              Software Design                                %
16 %                                John Cristy                                  %
17 %                               December 2003                                 %
18 %                                                                             %
19 %                                                                             %
20 %  Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization      %
21 %  dedicated to making software imaging solutions freely available.           %
22 %                                                                             %
23 %  You may not use this file except in compliance with the License.  You may  %
24 %  obtain a copy of the License at                                            %
25 %                                                                             %
26 %    http://www.imagemagick.org/script/license.php                            %
27 %                                                                             %
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.                                             %
33 %                                                                             %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 %
38 */
39 \f
40 /*
41   Include declarations.
42 */
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/token.h"
52 #include "MagickCore/utility.h"
53 #include "MagickCore/version.h"
54 \f
55 /*
56 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
57 %                                                                             %
58 %                                                                             %
59 %                                                                             %
60 %     C h a n n e l F x I m a g e                                             %
61 %                                                                             %
62 %                                                                             %
63 %                                                                             %
64 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
65 %
66 %  ChannelFxImage() applies a channel expression to the specified image.  The
67 %  expression consists of one or more channels, either mnemonic or numeric (e.g.
68 %  red, 1), separated by actions as follows:
69 %
70 %    <=>     exchange two channels (e.g. red<=>blue)
71 %    =>      transfer a channel to another (e.g. red=>green)
72 %    ,       separate channel operations (e.g. red, green)
73 %    |       read channels from next input image (e.g. red | green)
74 %    ;       write channels to next output image (e.g. red; green; blue)
75 %
76 %  A channel without a operation symbol implies extract. For example, to create
77 %  3 grayscale images from the red, green, and blue channels of an image, use:
78 %
79 %    -channel-fx "red; green; blue"
80 %
81 %  The format of the ChannelFxImage method is:
82 %
83 %      Image *ChannelFxImage(const Image *image,const char *expression,
84 %        ExceptionInfo *exception)
85 %
86 %  A description of each parameter follows:
87 %
88 %    o image: the image.
89 %
90 %    o expression: A channel expression.
91 %
92 %    o exception: return any errors or warnings in this structure.
93 %
94 */
95
96 typedef enum
97 {
98   ExtractChannelOp,
99   ExchangeChannelOp,
100   TransferChannelOp
101 } ChannelFx;
102
103 static inline size_t MagickMin(const size_t x,const size_t y)
104 {
105   if (x < y)
106     return(x);
107   return(y);
108 }
109
110 static MagickBooleanType ChannelImage(Image *destination_image,
111   const Image *source_image,const ChannelFx channel_op,
112   const PixelChannel source_channel,const PixelChannel destination_channel,
113   ExceptionInfo *exception)
114 {
115   CacheView
116     *source_view,
117     *destination_view;
118
119   MagickBooleanType
120     status;
121
122   size_t
123     height;
124
125   ssize_t
126     y;
127
128   status=MagickTrue;
129   source_view=AcquireCacheView(source_image);
130   destination_view=AcquireCacheView(destination_image);
131   height=MagickMin(source_image->rows,destination_image->rows);
132 #if defined(MAGICKCORE_OPENMP_SUPPORT)
133   #pragma omp parallel for schedule(static) shared(status)
134 #endif
135   for (y=0; y < (ssize_t) height; y++)
136   {
137     register const Quantum
138       *restrict p;
139
140     register Quantum
141       *restrict q;
142
143     register ssize_t
144       x;
145
146     size_t
147       width;
148
149     if (status == MagickFalse)
150       continue;
151     p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
152       exception);
153     q=QueueCacheViewAuthenticPixels(destination_view,0,y,
154       destination_image->columns,1,exception);
155     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
156       {
157         status=MagickFalse;
158         continue;
159       }
160     width=MagickMin(source_image->columns,destination_image->columns);
161     for (x=0; x < (ssize_t) width; x++)
162     {
163       PixelTrait
164         destination_traits,
165         source_traits;
166
167       ssize_t
168         offset;
169
170       source_traits=GetPixelChannelMapTraits(source_image,source_channel);
171       destination_traits=GetPixelChannelMapTraits(destination_image,
172         destination_channel);
173       if ((source_traits == UndefinedPixelTrait) ||
174           (destination_traits == UndefinedPixelTrait))
175         continue;
176       offset=GetPixelChannelMapOffset(source_image,source_channel);
177       SetPixelChannel(destination_image,destination_channel,p[offset],q);
178       p+=GetPixelChannels(source_image);
179       q+=GetPixelChannels(destination_image);
180     }
181     if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse)
182       status=MagickFalse;
183   }
184   destination_view=DestroyCacheView(destination_view);
185   source_view=DestroyCacheView(source_view);
186   return(status);
187 }
188
189 MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
190   ExceptionInfo *exception)
191 {
192 #define ChannelFxImageTag  "ChannelFx/Image"
193
194   char
195     token[MaxTextExtent];
196
197   ChannelFx
198     channel_op;
199
200   const char
201     *p;
202
203   const Image
204     *source_image;
205
206   Image
207     *destination_image;
208
209   PixelChannel
210     source_channel,
211     destination_channel;
212
213   ssize_t
214     channels;
215
216   assert(image != (Image *) NULL);
217   assert(image->signature == MagickSignature);
218   if (image->debug != MagickFalse)
219     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
220   assert(exception != (ExceptionInfo *) NULL);
221   assert(exception->signature == MagickSignature);
222   source_image=image;
223   destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
224   if (destination_image == (Image *) NULL)
225     return((Image *) NULL);
226   if (SetImageBackgroundColor(destination_image,exception) == MagickFalse)
227     {
228       destination_image=GetLastImageInList(destination_image);
229       return((Image *) NULL);
230     }
231   if (expression == (const char *) NULL)
232     return(destination_image);
233   destination_channel=RedPixelChannel;
234   p=(char *) expression;
235   GetMagickToken(p,&p,token);
236   for (channels=0; *p != '\0'; )
237   {
238     MagickBooleanType
239       status;
240
241     ssize_t
242       i;
243
244     /*
245       Interpret channel expression.
246     */
247     if (*token == ',')
248       {
249         destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
250         GetMagickToken(p,&p,token);
251       }
252     if (*token == '|')
253       {
254         if (GetNextImageInList(source_image) != (Image *) NULL)
255           source_image=GetNextImageInList(source_image);
256         else
257           source_image=GetFirstImageInList(source_image);
258         GetMagickToken(p,&p,token);
259       }
260     if (*token == ';')
261       {
262         Image
263           *canvas;
264
265         if (channels == 1)
266           {
267             destination_image->colorspace=GRAYColorspace;
268             InitializePixelChannelMap(destination_image);
269           }
270         canvas=CloneImage(source_image,0,0,MagickTrue,exception);
271         if (canvas == (Image *) NULL)
272           {
273             destination_image=GetLastImageInList(destination_image);
274             return((Image *) NULL);
275           }
276         AppendImageToList(&destination_image,canvas);
277         destination_image=GetLastImageInList(destination_image);
278         if (SetImageBackgroundColor(destination_image,exception) == MagickFalse)
279           {
280             destination_image=GetLastImageInList(destination_image);
281             return((Image *) NULL);
282           }
283         GetMagickToken(p,&p,token);
284         channels=0;
285         destination_channel=RedPixelChannel;
286       }
287     i=ParsePixelChannelOption(token);
288     if (i < 0)
289       {
290         (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
291           "UnrecognizedChannelType","`%s'",token);
292         destination_image=DestroyImageList(destination_image);
293         break;
294       }
295     source_channel=(PixelChannel) i;
296     channel_op=ExtractChannelOp;
297     GetMagickToken(p,&p,token);
298     if (*token == '<')
299       {
300         channel_op=ExchangeChannelOp;
301         GetMagickToken(p,&p,token);
302       }
303     if (*token == '=')
304       GetMagickToken(p,&p,token);
305     if (*token == '>')
306       {
307         if (channel_op != ExchangeChannelOp)
308           channel_op=TransferChannelOp;
309         GetMagickToken(p,&p,token);
310       }
311     if (channel_op != ExtractChannelOp)
312       {
313         i=ParsePixelChannelOption(token);
314         if (i < 0)
315           {
316             (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
317               "UnrecognizedChannelType","`%s'",token);
318             destination_image=DestroyImageList(destination_image);
319             break;
320           }
321         destination_channel=(PixelChannel) i;
322       }
323     status=ChannelImage(destination_image,source_image,channel_op,
324       source_channel,destination_channel,exception);
325     if (status == MagickFalse)
326       {
327         destination_image=DestroyImageList(destination_image);
328         break;
329       }
330     channels++;
331     status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
332       strlen(expression));
333     if (status == MagickFalse)
334       break;
335   }
336   if (channels == 1)
337     {
338       destination_image->colorspace=GRAYColorspace;
339       InitializePixelChannelMap(destination_image);
340     }
341   return(destination_image);
342 }
343 \f
344 /*
345 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
346 %                                                                             %
347 %                                                                             %
348 %                                                                             %
349 %     C o m b i n e I m a g e s                                               %
350 %                                                                             %
351 %                                                                             %
352 %                                                                             %
353 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
354 %
355 %  CombineImages() combines one or more images into a single image.  The
356 %  grayscale value of the pixels of each image in the sequence is assigned in
357 %  order to the specified channels of the combined image.   The typical
358 %  ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
359 %
360 %  The format of the CombineImages method is:
361 %
362 %      Image *CombineImages(const Image *image,ExceptionInfo *exception)
363 %
364 %  A description of each parameter follows:
365 %
366 %    o image: the image.
367 %
368 %    o exception: return any errors or warnings in this structure.
369 %
370 */
371 MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
372 {
373 #define CombineImageTag  "Combine/Image"
374
375   CacheView
376     *combine_view;
377
378   Image
379     *combine_image;
380
381   MagickBooleanType
382     status;
383
384   MagickOffsetType
385     progress;
386
387   ssize_t
388     y;
389
390   /*
391     Ensure the image are the same size.
392   */
393   assert(image != (const Image *) NULL);
394   assert(image->signature == MagickSignature);
395   if (image->debug != MagickFalse)
396     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
397   assert(exception != (ExceptionInfo *) NULL);
398   assert(exception->signature == MagickSignature);
399   combine_image=CloneImage(image,0,0,MagickTrue,exception);
400   if (combine_image == (Image *) NULL)
401     return((Image *) NULL);
402   if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
403     {
404       combine_image=DestroyImage(combine_image);
405       return((Image *) NULL);
406     }
407   if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
408     combine_image->matte=MagickTrue;
409   /*
410     Combine images.
411   */
412   status=MagickTrue;
413   progress=0;
414   combine_view=AcquireCacheView(combine_image);
415   for (y=0; y < (ssize_t) combine_image->rows; y++)
416   {
417     CacheView
418       *image_view;
419
420     const Image
421       *next;
422
423     Quantum
424       *pixels;
425
426     register const Quantum
427       *restrict p;
428
429     register Quantum
430       *restrict q;
431
432     register ssize_t
433       i;
434
435     if (status == MagickFalse)
436       continue;
437     pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
438       1,exception);
439     if (pixels == (Quantum *) NULL)
440       {
441         status=MagickFalse;
442         continue;
443       }
444     next=image;
445     for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
446     {
447       PixelChannel
448         channel;
449
450       PixelTrait
451         combine_traits,
452         traits;
453
454       register ssize_t
455         x;
456
457       if (next == (Image *) NULL)
458         continue;
459       channel=GetPixelChannelMapChannel(image,i);
460       traits=GetPixelChannelMapTraits(image,channel);
461       combine_traits=GetPixelChannelMapTraits(combine_image,channel);
462       if ((traits == UndefinedPixelTrait) ||
463           (combine_traits == UndefinedPixelTrait))
464         continue;
465       image_view=AcquireCacheView(next);
466       p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
467       if (p == (const Quantum *) NULL)
468         continue;
469       q=pixels;
470       for (x=0; x < (ssize_t) combine_image->columns; x++)
471       {
472         if (x < (ssize_t) image->columns)
473           {
474             q[i]=GetPixelGray(image,p);
475             p+=GetPixelChannels(image);
476           }
477         q+=GetPixelChannels(combine_image);
478       }
479       image_view=DestroyCacheView(image_view);
480       next=GetNextImageInList(next);
481       if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
482         status=MagickFalse;
483       if (image->progress_monitor != (MagickProgressMonitor) NULL)
484         {
485           MagickBooleanType
486             proceed;
487
488           proceed=SetImageProgress(image,CombineImageTag,progress++,
489             combine_image->rows);
490           if (proceed == MagickFalse)
491             status=MagickFalse;
492         }
493     }
494   }
495   combine_view=DestroyCacheView(combine_view);
496   if (status == MagickFalse)
497     combine_image=DestroyImage(combine_image);
498   return(combine_image);
499 }
500 \f
501 /*
502 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
503 %                                                                             %
504 %                                                                             %
505 %                                                                             %
506 %     S e p a r a t e I m a g e                                               %
507 %                                                                             %
508 %                                                                             %
509 %                                                                             %
510 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
511 %
512 %  SeparateImage() separates a channel from the image and returns it as a
513 %  grayscale image.
514 %
515 %  The format of the SeparateImage method is:
516 %
517 %      Image *SeparateImage(const Image *image,const ChannelType channel,
518 %        ExceptionInfo *exception)
519 %
520 %  A description of each parameter follows:
521 %
522 %    o image: the image.
523 %
524 %    o channel: the image channel.
525 %
526 %    o exception: return any errors or warnings in this structure.
527 %
528 */
529 MagickExport Image *SeparateImage(const Image *image,
530   const ChannelType channel_type,ExceptionInfo *exception)
531 {
532 #define GetChannelBit(mask,bit)  (((size_t) (mask) >> (size_t) (bit)) & 0x01)
533 #define SeparateImageTag  "Separate/Image"
534
535   CacheView
536     *image_view,
537     *separate_view;
538
539   Image
540     *separate_image;
541
542   MagickBooleanType
543     status;
544
545   MagickOffsetType
546     progress;
547
548   ssize_t
549     y;
550
551   /*
552     Initialize spread image attributes.
553   */
554   assert(image != (Image *) NULL);
555   assert(image->signature == MagickSignature);
556   if (image->debug != MagickFalse)
557     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
558   assert(exception != (ExceptionInfo *) NULL);
559   assert(exception->signature == MagickSignature);
560   separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
561     exception);
562   if (separate_image == (Image *) NULL)
563     return((Image *) NULL);
564   if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
565     {
566       separate_image=DestroyImage(separate_image);
567       return((Image *) NULL);
568     }
569   separate_image->colorspace=GRAYColorspace;
570   /*
571     Separate image.
572   */
573   status=MagickTrue;
574   progress=0;
575   image_view=AcquireCacheView(image);
576   separate_view=AcquireCacheView(separate_image);
577 #if defined(MAGICKCORE_OPENMP_SUPPORT)
578   #pragma omp parallel for schedule(static) shared(progress,status)
579 #endif
580   for (y=0; y < (ssize_t) image->rows; y++)
581   {
582     register const Quantum
583       *restrict p;
584
585     register Quantum
586       *restrict q;
587
588     register ssize_t
589       x;
590
591     if (status == MagickFalse)
592       continue;
593     p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
594     q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
595       exception);
596     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
597       {
598         status=MagickFalse;
599         continue;
600       }
601     for (x=0; x < (ssize_t) image->columns; x++)
602     {
603       register ssize_t
604         i;
605
606       if (GetPixelMask(image,p) != 0)
607         {
608           p+=GetPixelChannels(image);
609           q+=GetPixelChannels(separate_image);
610           continue;
611         }
612       SetPixelChannel(separate_image,GrayPixelChannel,0,q);
613       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
614       {
615         PixelChannel
616           channel;
617
618         PixelTrait
619           traits;
620
621         channel=GetPixelChannelMapChannel(image,i);
622         traits=GetPixelChannelMapTraits(image,channel);
623         if ((traits == UndefinedPixelTrait) ||
624             (GetChannelBit(channel_type,channel) == 0))
625           continue;
626         SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
627       }
628       p+=GetPixelChannels(image);
629       q+=GetPixelChannels(separate_image);
630     }
631     if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
632       status=MagickFalse;
633     if (image->progress_monitor != (MagickProgressMonitor) NULL)
634       {
635         MagickBooleanType
636           proceed;
637
638 #if defined(MAGICKCORE_OPENMP_SUPPORT)
639   #pragma omp critical (MagickCore_SeparateImage)
640 #endif
641         proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
642         if (proceed == MagickFalse)
643           status=MagickFalse;
644       }
645   }
646   separate_view=DestroyCacheView(separate_view);
647   image_view=DestroyCacheView(image_view);
648   return(separate_image);
649 }
650 \f
651 /*
652 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
653 %                                                                             %
654 %                                                                             %
655 %                                                                             %
656 %     S e p a r a t e I m a g e s                                             %
657 %                                                                             %
658 %                                                                             %
659 %                                                                             %
660 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
661 %
662 %  SeparateImages() returns a separate grayscale image for each channel
663 %  specified.
664 %
665 %  The format of the SeparateImages method is:
666 %
667 %      MagickBooleanType SeparateImages(const Image *image,
668 %        ExceptionInfo *exception)
669 %
670 %  A description of each parameter follows:
671 %
672 %    o image: the image.
673 %
674 %    o exception: return any errors or warnings in this structure.
675 %
676 */
677 MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
678 {
679   Image
680     *images,
681     *separate_image;
682
683   register ssize_t
684     i;
685
686   assert(image != (Image *) NULL);
687   assert(image->signature == MagickSignature);
688   if (image->debug != MagickFalse)
689     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
690   images=NewImageList();
691   for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
692   {
693     PixelChannel
694       channel;
695
696     PixelTrait
697       traits;
698
699     channel=GetPixelChannelMapChannel(image,i);
700     traits=GetPixelChannelMapTraits(image,channel);
701     if ((traits == UndefinedPixelTrait) ||
702         ((traits & UpdatePixelTrait) == 0))
703       continue;
704     separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
705     if (separate_image != (Image *) NULL)
706       AppendImageToList(&images,separate_image);
707   }
708   return(images);
709 }