blob: 7fd7cd69a172a210f915a22718c907eaaf0b433c [file] [log] [blame]
cristyd04e7bf2012-03-03 19:19:12 +00001/*
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
40/*
41 Include declarations.
42*/
43#include "MagickCore/studio.h"
cristya15140f2012-03-04 01:21:15 +000044#include "MagickCore/image.h"
45#include "MagickCore/list.h"
46#include "MagickCore/log.h"
cristyab272ac2012-03-04 22:08:07 +000047#include "MagickCore/monitor.h"
48#include "MagickCore/monitor-private.h"
cristya15140f2012-03-04 01:21:15 +000049#include "MagickCore/option.h"
cristyab272ac2012-03-04 22:08:07 +000050#include "MagickCore/pixel-accessor.h"
cristya15140f2012-03-04 01:21:15 +000051#include "MagickCore/token.h"
cristyd04e7bf2012-03-03 19:19:12 +000052#include "MagickCore/utility.h"
53#include "MagickCore/version.h"
54
55/*
56%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
57% %
58% %
59% %
cristy5f257b22012-03-07 00:27:29 +000060% C h a n n e l F x I m a g e %
cristyd04e7bf2012-03-03 19:19:12 +000061% %
62% %
63% %
64%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
65%
cristy5f257b22012-03-07 00:27:29 +000066% 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:
cristyab272ac2012-03-04 22:08:07 +000069%
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%
cristy5f257b22012-03-07 00:27:29 +000079% -channel-fx "red; green; blue"
cristyd04e7bf2012-03-03 19:19:12 +000080%
cristy5f257b22012-03-07 00:27:29 +000081% The format of the ChannelFxImage method is:
cristyd04e7bf2012-03-03 19:19:12 +000082%
cristy5f257b22012-03-07 00:27:29 +000083% Image *ChannelFxImage(const Image *image,const char *expression,
84% ExceptionInfo *exception)
cristyd04e7bf2012-03-03 19:19:12 +000085%
86% A description of each parameter follows:
87%
cristyab272ac2012-03-04 22:08:07 +000088% o image: the image.
cristyd04e7bf2012-03-03 19:19:12 +000089%
90% o expression: A channel expression.
91%
92% o exception: return any errors or warnings in this structure.
93%
94*/
cristya15140f2012-03-04 01:21:15 +000095
96typedef enum
97{
98 ExtractChannelOp,
99 ExchangeChannelOp,
100 TransferChannelOp
cristy5f257b22012-03-07 00:27:29 +0000101} ChannelFx;
cristya15140f2012-03-04 01:21:15 +0000102
cristyab272ac2012-03-04 22:08:07 +0000103static inline size_t MagickMin(const size_t x,const size_t y)
cristya15140f2012-03-04 01:21:15 +0000104{
cristyab272ac2012-03-04 22:08:07 +0000105 if (x < y)
106 return(x);
107 return(y);
cristya15140f2012-03-04 01:21:15 +0000108}
109
cristyab272ac2012-03-04 22:08:07 +0000110static MagickBooleanType ChannelImage(Image *destination_image,
cristy5f257b22012-03-07 00:27:29 +0000111 const Image *source_image,const ChannelFx channel_op,
cristyab272ac2012-03-04 22:08:07 +0000112 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
cristy5f257b22012-03-07 00:27:29 +0000189MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
190 ExceptionInfo *exception)
cristyd04e7bf2012-03-03 19:19:12 +0000191{
cristy5f257b22012-03-07 00:27:29 +0000192#define ChannelFxImageTag "ChannelFx/Image"
cristyab272ac2012-03-04 22:08:07 +0000193
cristya15140f2012-03-04 01:21:15 +0000194 char
195 token[MaxTextExtent];
196
cristy5f257b22012-03-07 00:27:29 +0000197 ChannelFx
cristya15140f2012-03-04 01:21:15 +0000198 channel_op;
199
200 const char
201 *p;
202
cristyab272ac2012-03-04 22:08:07 +0000203 const Image
204 *source_image;
205
cristya15140f2012-03-04 01:21:15 +0000206 Image
cristyab272ac2012-03-04 22:08:07 +0000207 *destination_image;
cristya15140f2012-03-04 01:21:15 +0000208
209 PixelChannel
cristyab272ac2012-03-04 22:08:07 +0000210 source_channel,
211 destination_channel;
cristya15140f2012-03-04 01:21:15 +0000212
cristyab272ac2012-03-04 22:08:07 +0000213 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;
cristya15140f2012-03-04 01:21:15 +0000234 p=(char *) expression;
235 GetMagickToken(p,&p,token);
cristyab272ac2012-03-04 22:08:07 +0000236 for (channels=0; *p != '\0'; )
cristya15140f2012-03-04 01:21:15 +0000237 {
238 MagickBooleanType
239 status;
240
241 ssize_t
242 i;
243
244 /*
245 Interpret channel expression.
246 */
247 if (*token == ',')
248 {
cristyab272ac2012-03-04 22:08:07 +0000249 destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
cristya15140f2012-03-04 01:21:15 +0000250 GetMagickToken(p,&p,token);
251 }
252 if (*token == '|')
253 {
cristyab272ac2012-03-04 22:08:07 +0000254 if (GetNextImageInList(source_image) != (Image *) NULL)
255 source_image=GetNextImageInList(source_image);
cristya15140f2012-03-04 01:21:15 +0000256 else
cristyab272ac2012-03-04 22:08:07 +0000257 source_image=GetFirstImageInList(source_image);
cristya15140f2012-03-04 01:21:15 +0000258 GetMagickToken(p,&p,token);
259 }
260 if (*token == ';')
261 {
cristyab272ac2012-03-04 22:08:07 +0000262 Image
263 *canvas;
264
265 if (channels == 1)
cristye21ffdf2012-03-05 16:33:39 +0000266 {
267 destination_image->colorspace=GRAYColorspace;
268 InitializePixelChannelMap(destination_image);
269 }
cristyab272ac2012-03-04 22:08:07 +0000270 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 }
cristya15140f2012-03-04 01:21:15 +0000283 GetMagickToken(p,&p,token);
cristyab272ac2012-03-04 22:08:07 +0000284 channels=0;
285 destination_channel=RedPixelChannel;
cristya15140f2012-03-04 01:21:15 +0000286 }
287 i=ParsePixelChannelOption(token);
288 if (i < 0)
289 {
290 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyabb241b2012-03-06 01:12:58 +0000291 "UnrecognizedChannelType","`%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000292 destination_image=DestroyImageList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000293 break;
294 }
cristyab272ac2012-03-04 22:08:07 +0000295 source_channel=(PixelChannel) i;
cristya15140f2012-03-04 01:21:15 +0000296 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,
cristyabb241b2012-03-06 01:12:58 +0000317 "UnrecognizedChannelType","`%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000318 destination_image=DestroyImageList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000319 break;
320 }
cristyab272ac2012-03-04 22:08:07 +0000321 destination_channel=(PixelChannel) i;
cristya15140f2012-03-04 01:21:15 +0000322 }
cristyab272ac2012-03-04 22:08:07 +0000323 status=ChannelImage(destination_image,source_image,channel_op,
324 source_channel,destination_channel,exception);
cristya15140f2012-03-04 01:21:15 +0000325 if (status == MagickFalse)
326 {
cristyab272ac2012-03-04 22:08:07 +0000327 destination_image=DestroyImageList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000328 break;
329 }
cristyab272ac2012-03-04 22:08:07 +0000330 channels++;
cristy5f257b22012-03-07 00:27:29 +0000331 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
cristyab272ac2012-03-04 22:08:07 +0000332 strlen(expression));
333 if (status == MagickFalse)
334 break;
cristya15140f2012-03-04 01:21:15 +0000335 }
cristyab272ac2012-03-04 22:08:07 +0000336 if (channels == 1)
cristye21ffdf2012-03-05 16:33:39 +0000337 {
338 destination_image->colorspace=GRAYColorspace;
339 InitializePixelChannelMap(destination_image);
340 }
cristyab272ac2012-03-04 22:08:07 +0000341 return(destination_image);
cristyd04e7bf2012-03-03 19:19:12 +0000342}
cristy0c643722012-03-05 19:18:42 +0000343
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*/
371MagickExport 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
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*/
529MagickExport 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
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*/
677MagickExport 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}