blob: 28ae9b27c04c08fd44399d5594ebc705d781b8ca [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"
cristy8748f562012-03-08 13:58:34 +000051#include "MagickCore/string-private.h"
cristya15140f2012-03-04 01:21:15 +000052#include "MagickCore/token.h"
cristyd04e7bf2012-03-03 19:19:12 +000053#include "MagickCore/utility.h"
54#include "MagickCore/version.h"
55
56/*
57%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
58% %
59% %
60% %
cristy5f257b22012-03-07 00:27:29 +000061% C h a n n e l F x I m a g e %
cristyd04e7bf2012-03-03 19:19:12 +000062% %
63% %
64% %
65%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66%
cristy5f257b22012-03-07 00:27:29 +000067% 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:
cristyab272ac2012-03-04 22:08:07 +000070%
71% <=> exchange two channels (e.g. red<=>blue)
72% => transfer a channel to another (e.g. red=>green)
cristy979b0f22012-03-08 14:17:33 +000073% = assign a constant (e.g. red=50%)
cristyab272ac2012-03-04 22:08:07 +000074% , 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)
77%
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:
80%
cristy5f257b22012-03-07 00:27:29 +000081% -channel-fx "red; green; blue"
cristyd04e7bf2012-03-03 19:19:12 +000082%
cristy5f257b22012-03-07 00:27:29 +000083% The format of the ChannelFxImage method is:
cristyd04e7bf2012-03-03 19:19:12 +000084%
cristy5f257b22012-03-07 00:27:29 +000085% Image *ChannelFxImage(const Image *image,const char *expression,
86% ExceptionInfo *exception)
cristyd04e7bf2012-03-03 19:19:12 +000087%
88% A description of each parameter follows:
89%
cristyab272ac2012-03-04 22:08:07 +000090% o image: the image.
cristyd04e7bf2012-03-03 19:19:12 +000091%
92% o expression: A channel expression.
93%
94% o exception: return any errors or warnings in this structure.
95%
96*/
cristya15140f2012-03-04 01:21:15 +000097
98typedef enum
99{
100 ExtractChannelOp,
cristy8748f562012-03-08 13:58:34 +0000101 AssignChannelOp,
cristya15140f2012-03-04 01:21:15 +0000102 ExchangeChannelOp,
103 TransferChannelOp
cristy5f257b22012-03-07 00:27:29 +0000104} ChannelFx;
cristya15140f2012-03-04 01:21:15 +0000105
cristyab272ac2012-03-04 22:08:07 +0000106static inline size_t MagickMin(const size_t x,const size_t y)
cristya15140f2012-03-04 01:21:15 +0000107{
cristyab272ac2012-03-04 22:08:07 +0000108 if (x < y)
109 return(x);
110 return(y);
cristya15140f2012-03-04 01:21:15 +0000111}
112
cristyab272ac2012-03-04 22:08:07 +0000113static MagickBooleanType ChannelImage(Image *destination_image,
cristy8748f562012-03-08 13:58:34 +0000114 const PixelChannel destination_channel,const ChannelFx channel_op,
cristy3bb378a2012-03-08 14:38:52 +0000115 const Image *source_image,const PixelChannel source_channel,
cristy8748f562012-03-08 13:58:34 +0000116 const Quantum pixel,ExceptionInfo *exception)
cristyab272ac2012-03-04 22:08:07 +0000117{
118 CacheView
119 *source_view,
120 *destination_view;
121
122 MagickBooleanType
123 status;
124
125 size_t
126 height;
127
128 ssize_t
129 y;
130
131 status=MagickTrue;
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)
137#endif
138 for (y=0; y < (ssize_t) height; y++)
139 {
cristy4ee60402012-03-11 02:01:20 +0000140 PixelTrait
141 destination_traits,
142 source_traits;
143
cristyab272ac2012-03-04 22:08:07 +0000144 register const Quantum
145 *restrict p;
146
147 register Quantum
148 *restrict q;
149
150 register ssize_t
151 x;
152
153 size_t
154 width;
155
156 if (status == MagickFalse)
157 continue;
158 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
159 exception);
cristy4ee60402012-03-11 02:01:20 +0000160 q=GetCacheViewAuthenticPixels(destination_view,0,y,
cristyab272ac2012-03-04 22:08:07 +0000161 destination_image->columns,1,exception);
162 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
163 {
164 status=MagickFalse;
165 continue;
166 }
cristy4ee60402012-03-11 02:01:20 +0000167 destination_traits=GetPixelChannelMapTraits(destination_image,
168 destination_channel);
169 source_traits=GetPixelChannelMapTraits(source_image,source_channel);
170 if ((destination_traits == UndefinedPixelTrait) ||
171 (source_traits == UndefinedPixelTrait))
172 continue;
cristyab272ac2012-03-04 22:08:07 +0000173 width=MagickMin(source_image->columns,destination_image->columns);
174 for (x=0; x < (ssize_t) width; x++)
175 {
cristy8748f562012-03-08 13:58:34 +0000176 if (channel_op == AssignChannelOp)
177 SetPixelChannel(destination_image,destination_channel,pixel,q);
178 else
cristy4ee60402012-03-11 02:01:20 +0000179 SetPixelChannel(destination_image,destination_channel,
180 GetPixelChannel(source_image,source_channel,p),q);
181 p+=GetPixelChannels(source_image);
cristyab272ac2012-03-04 22:08:07 +0000182 q+=GetPixelChannels(destination_image);
183 }
184 if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse)
185 status=MagickFalse;
186 }
187 destination_view=DestroyCacheView(destination_view);
188 source_view=DestroyCacheView(source_view);
189 return(status);
190}
191
cristy5f257b22012-03-07 00:27:29 +0000192MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
193 ExceptionInfo *exception)
cristyd04e7bf2012-03-03 19:19:12 +0000194{
cristy5f257b22012-03-07 00:27:29 +0000195#define ChannelFxImageTag "ChannelFx/Image"
cristyab272ac2012-03-04 22:08:07 +0000196
cristy5f257b22012-03-07 00:27:29 +0000197 ChannelFx
cristya15140f2012-03-04 01:21:15 +0000198 channel_op;
199
cristy4ee60402012-03-11 02:01:20 +0000200 char
201 token[MaxTextExtent];
202
cristya15140f2012-03-04 01:21:15 +0000203 const char
204 *p;
205
cristyab272ac2012-03-04 22:08:07 +0000206 const Image
207 *source_image;
208
cristy8748f562012-03-08 13:58:34 +0000209 double
210 pixel;
211
cristya15140f2012-03-04 01:21:15 +0000212 Image
cristyab272ac2012-03-04 22:08:07 +0000213 *destination_image;
cristya15140f2012-03-04 01:21:15 +0000214
cristy4ee60402012-03-11 02:01:20 +0000215 MagickBooleanType
216 status;
217
cristya15140f2012-03-04 01:21:15 +0000218 PixelChannel
cristyab272ac2012-03-04 22:08:07 +0000219 source_channel,
220 destination_channel;
cristya15140f2012-03-04 01:21:15 +0000221
cristyab272ac2012-03-04 22:08:07 +0000222 ssize_t
223 channels;
224
225 assert(image != (Image *) NULL);
226 assert(image->signature == MagickSignature);
227 if (image->debug != MagickFalse)
228 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
229 assert(exception != (ExceptionInfo *) NULL);
230 assert(exception->signature == MagickSignature);
231 source_image=image;
232 destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
233 if (destination_image == (Image *) NULL)
234 return((Image *) NULL);
cristyab272ac2012-03-04 22:08:07 +0000235 if (expression == (const char *) NULL)
236 return(destination_image);
237 destination_channel=RedPixelChannel;
cristy8748f562012-03-08 13:58:34 +0000238 pixel=0.0;
cristya15140f2012-03-04 01:21:15 +0000239 p=(char *) expression;
240 GetMagickToken(p,&p,token);
cristy4ee60402012-03-11 02:01:20 +0000241 channel_op=ExtractChannelOp;
cristyab272ac2012-03-04 22:08:07 +0000242 for (channels=0; *p != '\0'; )
cristya15140f2012-03-04 01:21:15 +0000243 {
cristya15140f2012-03-04 01:21:15 +0000244 ssize_t
245 i;
246
247 /*
248 Interpret channel expression.
249 */
250 if (*token == ',')
251 {
cristyab272ac2012-03-04 22:08:07 +0000252 destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
cristya15140f2012-03-04 01:21:15 +0000253 GetMagickToken(p,&p,token);
254 }
255 if (*token == '|')
256 {
cristyab272ac2012-03-04 22:08:07 +0000257 if (GetNextImageInList(source_image) != (Image *) NULL)
258 source_image=GetNextImageInList(source_image);
cristya15140f2012-03-04 01:21:15 +0000259 else
cristyab272ac2012-03-04 22:08:07 +0000260 source_image=GetFirstImageInList(source_image);
cristya15140f2012-03-04 01:21:15 +0000261 GetMagickToken(p,&p,token);
262 }
263 if (*token == ';')
264 {
cristyab272ac2012-03-04 22:08:07 +0000265 Image
266 *canvas;
267
cristy4ee60402012-03-11 02:01:20 +0000268 status=SetImageStorageClass(destination_image,DirectClass,exception);
269 if (status == MagickFalse)
270 {
271 destination_image=GetLastImageInList(destination_image);
272 return((Image *) NULL);
273 }
274 if ((channel_op == ExtractChannelOp) && (channels == 1))
cristye21ffdf2012-03-05 16:33:39 +0000275 {
276 destination_image->colorspace=GRAYColorspace;
277 InitializePixelChannelMap(destination_image);
278 }
cristyab272ac2012-03-04 22:08:07 +0000279 canvas=CloneImage(source_image,0,0,MagickTrue,exception);
280 if (canvas == (Image *) NULL)
281 {
282 destination_image=GetLastImageInList(destination_image);
283 return((Image *) NULL);
284 }
285 AppendImageToList(&destination_image,canvas);
286 destination_image=GetLastImageInList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000287 GetMagickToken(p,&p,token);
cristyab272ac2012-03-04 22:08:07 +0000288 channels=0;
289 destination_channel=RedPixelChannel;
cristya15140f2012-03-04 01:21:15 +0000290 }
291 i=ParsePixelChannelOption(token);
292 if (i < 0)
293 {
294 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyabb241b2012-03-06 01:12:58 +0000295 "UnrecognizedChannelType","`%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000296 destination_image=DestroyImageList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000297 break;
298 }
cristyab272ac2012-03-04 22:08:07 +0000299 source_channel=(PixelChannel) i;
cristya15140f2012-03-04 01:21:15 +0000300 channel_op=ExtractChannelOp;
301 GetMagickToken(p,&p,token);
302 if (*token == '<')
303 {
304 channel_op=ExchangeChannelOp;
305 GetMagickToken(p,&p,token);
306 }
307 if (*token == '=')
cristy8748f562012-03-08 13:58:34 +0000308 {
309 if (channel_op != ExchangeChannelOp)
310 channel_op=AssignChannelOp;
311 GetMagickToken(p,&p,token);
312 }
cristya15140f2012-03-04 01:21:15 +0000313 if (*token == '>')
314 {
315 if (channel_op != ExchangeChannelOp)
316 channel_op=TransferChannelOp;
317 GetMagickToken(p,&p,token);
318 }
cristy8748f562012-03-08 13:58:34 +0000319 switch (channel_op)
320 {
321 case AssignChannelOp:
322 {
cristy41db1742012-03-09 00:57:52 +0000323 pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
cristy3bb378a2012-03-08 14:38:52 +0000324 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000325 break;
326 }
327 case ExchangeChannelOp:
328 case TransferChannelOp:
cristya15140f2012-03-04 01:21:15 +0000329 {
330 i=ParsePixelChannelOption(token);
331 if (i < 0)
332 {
333 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyabb241b2012-03-06 01:12:58 +0000334 "UnrecognizedChannelType","`%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000335 destination_image=DestroyImageList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000336 break;
337 }
cristyab272ac2012-03-04 22:08:07 +0000338 destination_channel=(PixelChannel) i;
cristy3bb378a2012-03-08 14:38:52 +0000339 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000340 break;
cristya15140f2012-03-04 01:21:15 +0000341 }
cristy8748f562012-03-08 13:58:34 +0000342 case ExtractChannelOp:
343 break;
344 }
cristy3bb378a2012-03-08 14:38:52 +0000345 status=ChannelImage(destination_image,destination_channel,channel_op,
346 source_image,source_channel,ClampToQuantum(pixel),exception);
cristya15140f2012-03-04 01:21:15 +0000347 if (status == MagickFalse)
348 {
cristyab272ac2012-03-04 22:08:07 +0000349 destination_image=DestroyImageList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000350 break;
351 }
cristy4ee60402012-03-11 02:01:20 +0000352 channels++;
cristy8748f562012-03-08 13:58:34 +0000353 if (channel_op == ExchangeChannelOp)
354 {
cristy4ee60402012-03-11 02:01:20 +0000355 status=ChannelImage(destination_image,source_channel,channel_op,
356 source_image,destination_channel,ClampToQuantum(pixel),exception);
cristy8748f562012-03-08 13:58:34 +0000357 if (status == MagickFalse)
358 {
359 destination_image=DestroyImageList(destination_image);
360 break;
361 }
cristy4ee60402012-03-11 02:01:20 +0000362 channels++;
cristy8748f562012-03-08 13:58:34 +0000363 }
cristy5f257b22012-03-07 00:27:29 +0000364 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
cristyab272ac2012-03-04 22:08:07 +0000365 strlen(expression));
366 if (status == MagickFalse)
367 break;
cristya15140f2012-03-04 01:21:15 +0000368 }
cristy4ee60402012-03-11 02:01:20 +0000369 status=SetImageStorageClass(destination_image,DirectClass,exception);
370 if (status == MagickFalse)
371 {
372 destination_image=GetLastImageInList(destination_image);
373 return((Image *) NULL);
374 }
375 if ((channel_op == ExtractChannelOp) && (channels == 1))
cristye21ffdf2012-03-05 16:33:39 +0000376 {
377 destination_image->colorspace=GRAYColorspace;
378 InitializePixelChannelMap(destination_image);
379 }
cristyab272ac2012-03-04 22:08:07 +0000380 return(destination_image);
cristyd04e7bf2012-03-03 19:19:12 +0000381}
cristy0c643722012-03-05 19:18:42 +0000382
383/*
384%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
385% %
386% %
387% %
388% C o m b i n e I m a g e s %
389% %
390% %
391% %
392%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
393%
394% CombineImages() combines one or more images into a single image. The
395% grayscale value of the pixels of each image in the sequence is assigned in
396% order to the specified channels of the combined image. The typical
397% ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
398%
399% The format of the CombineImages method is:
400%
401% Image *CombineImages(const Image *image,ExceptionInfo *exception)
402%
403% A description of each parameter follows:
404%
405% o image: the image.
406%
407% o exception: return any errors or warnings in this structure.
408%
409*/
410MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
411{
412#define CombineImageTag "Combine/Image"
413
414 CacheView
415 *combine_view;
416
417 Image
418 *combine_image;
419
420 MagickBooleanType
421 status;
422
423 MagickOffsetType
424 progress;
425
426 ssize_t
427 y;
428
429 /*
430 Ensure the image are the same size.
431 */
432 assert(image != (const Image *) NULL);
433 assert(image->signature == MagickSignature);
434 if (image->debug != MagickFalse)
435 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
436 assert(exception != (ExceptionInfo *) NULL);
437 assert(exception->signature == MagickSignature);
438 combine_image=CloneImage(image,0,0,MagickTrue,exception);
439 if (combine_image == (Image *) NULL)
440 return((Image *) NULL);
441 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
442 {
443 combine_image=DestroyImage(combine_image);
444 return((Image *) NULL);
445 }
446 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
447 combine_image->matte=MagickTrue;
448 /*
449 Combine images.
450 */
451 status=MagickTrue;
452 progress=0;
453 combine_view=AcquireCacheView(combine_image);
454 for (y=0; y < (ssize_t) combine_image->rows; y++)
455 {
456 CacheView
457 *image_view;
458
459 const Image
460 *next;
461
462 Quantum
463 *pixels;
464
465 register const Quantum
466 *restrict p;
467
468 register Quantum
469 *restrict q;
470
471 register ssize_t
472 i;
473
474 if (status == MagickFalse)
475 continue;
476 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
477 1,exception);
478 if (pixels == (Quantum *) NULL)
479 {
480 status=MagickFalse;
481 continue;
482 }
483 next=image;
484 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
485 {
486 PixelChannel
487 channel;
488
489 PixelTrait
490 combine_traits,
491 traits;
492
493 register ssize_t
494 x;
495
496 if (next == (Image *) NULL)
497 continue;
498 channel=GetPixelChannelMapChannel(image,i);
499 traits=GetPixelChannelMapTraits(image,channel);
500 combine_traits=GetPixelChannelMapTraits(combine_image,channel);
501 if ((traits == UndefinedPixelTrait) ||
502 (combine_traits == UndefinedPixelTrait))
503 continue;
504 image_view=AcquireCacheView(next);
505 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
506 if (p == (const Quantum *) NULL)
507 continue;
508 q=pixels;
509 for (x=0; x < (ssize_t) combine_image->columns; x++)
510 {
511 if (x < (ssize_t) image->columns)
512 {
513 q[i]=GetPixelGray(image,p);
514 p+=GetPixelChannels(image);
515 }
516 q+=GetPixelChannels(combine_image);
517 }
518 image_view=DestroyCacheView(image_view);
519 next=GetNextImageInList(next);
520 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
521 status=MagickFalse;
522 if (image->progress_monitor != (MagickProgressMonitor) NULL)
523 {
524 MagickBooleanType
525 proceed;
526
527 proceed=SetImageProgress(image,CombineImageTag,progress++,
528 combine_image->rows);
529 if (proceed == MagickFalse)
530 status=MagickFalse;
531 }
532 }
533 }
534 combine_view=DestroyCacheView(combine_view);
535 if (status == MagickFalse)
536 combine_image=DestroyImage(combine_image);
537 return(combine_image);
538}
539
540/*
541%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
542% %
543% %
544% %
545% S e p a r a t e I m a g e %
546% %
547% %
548% %
549%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
550%
551% SeparateImage() separates a channel from the image and returns it as a
552% grayscale image.
553%
554% The format of the SeparateImage method is:
555%
556% Image *SeparateImage(const Image *image,const ChannelType channel,
557% ExceptionInfo *exception)
558%
559% A description of each parameter follows:
560%
561% o image: the image.
562%
563% o channel: the image channel.
564%
565% o exception: return any errors or warnings in this structure.
566%
567*/
568MagickExport Image *SeparateImage(const Image *image,
569 const ChannelType channel_type,ExceptionInfo *exception)
570{
571#define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
572#define SeparateImageTag "Separate/Image"
573
574 CacheView
575 *image_view,
576 *separate_view;
577
578 Image
579 *separate_image;
580
581 MagickBooleanType
582 status;
583
584 MagickOffsetType
585 progress;
586
587 ssize_t
588 y;
589
590 /*
591 Initialize spread image attributes.
592 */
593 assert(image != (Image *) NULL);
594 assert(image->signature == MagickSignature);
595 if (image->debug != MagickFalse)
596 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
597 assert(exception != (ExceptionInfo *) NULL);
598 assert(exception->signature == MagickSignature);
599 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
600 exception);
601 if (separate_image == (Image *) NULL)
602 return((Image *) NULL);
603 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
604 {
605 separate_image=DestroyImage(separate_image);
606 return((Image *) NULL);
607 }
608 separate_image->colorspace=GRAYColorspace;
609 /*
610 Separate image.
611 */
612 status=MagickTrue;
613 progress=0;
614 image_view=AcquireCacheView(image);
615 separate_view=AcquireCacheView(separate_image);
616#if defined(MAGICKCORE_OPENMP_SUPPORT)
617 #pragma omp parallel for schedule(static) shared(progress,status)
618#endif
619 for (y=0; y < (ssize_t) image->rows; y++)
620 {
621 register const Quantum
622 *restrict p;
623
624 register Quantum
625 *restrict q;
626
627 register ssize_t
628 x;
629
630 if (status == MagickFalse)
631 continue;
632 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
633 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
634 exception);
635 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
636 {
637 status=MagickFalse;
638 continue;
639 }
640 for (x=0; x < (ssize_t) image->columns; x++)
641 {
642 register ssize_t
643 i;
644
645 if (GetPixelMask(image,p) != 0)
646 {
647 p+=GetPixelChannels(image);
648 q+=GetPixelChannels(separate_image);
649 continue;
650 }
651 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
652 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
653 {
654 PixelChannel
655 channel;
656
657 PixelTrait
658 traits;
659
660 channel=GetPixelChannelMapChannel(image,i);
661 traits=GetPixelChannelMapTraits(image,channel);
662 if ((traits == UndefinedPixelTrait) ||
663 (GetChannelBit(channel_type,channel) == 0))
664 continue;
665 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
666 }
667 p+=GetPixelChannels(image);
668 q+=GetPixelChannels(separate_image);
669 }
670 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
671 status=MagickFalse;
672 if (image->progress_monitor != (MagickProgressMonitor) NULL)
673 {
674 MagickBooleanType
675 proceed;
676
677#if defined(MAGICKCORE_OPENMP_SUPPORT)
678 #pragma omp critical (MagickCore_SeparateImage)
679#endif
680 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
681 if (proceed == MagickFalse)
682 status=MagickFalse;
683 }
684 }
685 separate_view=DestroyCacheView(separate_view);
686 image_view=DestroyCacheView(image_view);
687 return(separate_image);
688}
689
690/*
691%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
692% %
693% %
694% %
695% S e p a r a t e I m a g e s %
696% %
697% %
698% %
699%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
700%
701% SeparateImages() returns a separate grayscale image for each channel
702% specified.
703%
704% The format of the SeparateImages method is:
705%
706% MagickBooleanType SeparateImages(const Image *image,
707% ExceptionInfo *exception)
708%
709% A description of each parameter follows:
710%
711% o image: the image.
712%
713% o exception: return any errors or warnings in this structure.
714%
715*/
716MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
717{
718 Image
719 *images,
720 *separate_image;
721
722 register ssize_t
723 i;
724
725 assert(image != (Image *) NULL);
726 assert(image->signature == MagickSignature);
727 if (image->debug != MagickFalse)
728 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
729 images=NewImageList();
730 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
731 {
732 PixelChannel
733 channel;
734
735 PixelTrait
736 traits;
737
738 channel=GetPixelChannelMapChannel(image,i);
739 traits=GetPixelChannelMapTraits(image,channel);
740 if ((traits == UndefinedPixelTrait) ||
741 ((traits & UpdatePixelTrait) == 0))
742 continue;
743 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
744 if (separate_image != (Image *) NULL)
745 AppendImageToList(&images,separate_image);
746 }
747 return(images);
748}