blob: b0f13a685a1fca66cc22cd81c723f48836de24f9 [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);
cristyfc68ef52012-03-11 23:33:15 +0000235 if (destination_image->colorspace == GRAYColorspace)
236 destination_image->colorspace=RGBColorspace;
cristyab272ac2012-03-04 22:08:07 +0000237 if (expression == (const char *) NULL)
238 return(destination_image);
239 destination_channel=RedPixelChannel;
cristy8748f562012-03-08 13:58:34 +0000240 pixel=0.0;
cristya15140f2012-03-04 01:21:15 +0000241 p=(char *) expression;
242 GetMagickToken(p,&p,token);
cristy4ee60402012-03-11 02:01:20 +0000243 channel_op=ExtractChannelOp;
cristyab272ac2012-03-04 22:08:07 +0000244 for (channels=0; *p != '\0'; )
cristya15140f2012-03-04 01:21:15 +0000245 {
cristya15140f2012-03-04 01:21:15 +0000246 ssize_t
247 i;
248
249 /*
250 Interpret channel expression.
251 */
252 if (*token == ',')
253 {
cristyab272ac2012-03-04 22:08:07 +0000254 destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
cristya15140f2012-03-04 01:21:15 +0000255 GetMagickToken(p,&p,token);
256 }
257 if (*token == '|')
258 {
cristyab272ac2012-03-04 22:08:07 +0000259 if (GetNextImageInList(source_image) != (Image *) NULL)
260 source_image=GetNextImageInList(source_image);
cristya15140f2012-03-04 01:21:15 +0000261 else
cristyab272ac2012-03-04 22:08:07 +0000262 source_image=GetFirstImageInList(source_image);
cristya15140f2012-03-04 01:21:15 +0000263 GetMagickToken(p,&p,token);
264 }
265 if (*token == ';')
266 {
cristyab272ac2012-03-04 22:08:07 +0000267 Image
268 *canvas;
269
cristy4ee60402012-03-11 02:01:20 +0000270 status=SetImageStorageClass(destination_image,DirectClass,exception);
271 if (status == MagickFalse)
272 {
cristyfc68ef52012-03-11 23:33:15 +0000273 destination_image=DestroyImageList(destination_image);
274 return(destination_image);
cristy4ee60402012-03-11 02:01:20 +0000275 }
276 if ((channel_op == ExtractChannelOp) && (channels == 1))
cristye21ffdf2012-03-05 16:33:39 +0000277 {
278 destination_image->colorspace=GRAYColorspace;
279 InitializePixelChannelMap(destination_image);
280 }
cristyab272ac2012-03-04 22:08:07 +0000281 canvas=CloneImage(source_image,0,0,MagickTrue,exception);
282 if (canvas == (Image *) NULL)
283 {
cristyfc68ef52012-03-11 23:33:15 +0000284 destination_image=DestroyImageList(destination_image);
285 return(destination_image);
cristyab272ac2012-03-04 22:08:07 +0000286 }
cristyfc68ef52012-03-11 23:33:15 +0000287 if (canvas->colorspace == GRAYColorspace)
288 canvas->colorspace=RGBColorspace;
cristyab272ac2012-03-04 22:08:07 +0000289 AppendImageToList(&destination_image,canvas);
290 destination_image=GetLastImageInList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000291 GetMagickToken(p,&p,token);
cristyab272ac2012-03-04 22:08:07 +0000292 channels=0;
293 destination_channel=RedPixelChannel;
cristya15140f2012-03-04 01:21:15 +0000294 }
295 i=ParsePixelChannelOption(token);
296 if (i < 0)
297 {
298 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyabb241b2012-03-06 01:12:58 +0000299 "UnrecognizedChannelType","`%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000300 destination_image=DestroyImageList(destination_image);
cristyfc68ef52012-03-11 23:33:15 +0000301 return(destination_image);
cristya15140f2012-03-04 01:21:15 +0000302 }
cristyab272ac2012-03-04 22:08:07 +0000303 source_channel=(PixelChannel) i;
cristya15140f2012-03-04 01:21:15 +0000304 channel_op=ExtractChannelOp;
305 GetMagickToken(p,&p,token);
306 if (*token == '<')
307 {
308 channel_op=ExchangeChannelOp;
309 GetMagickToken(p,&p,token);
310 }
311 if (*token == '=')
cristy8748f562012-03-08 13:58:34 +0000312 {
313 if (channel_op != ExchangeChannelOp)
314 channel_op=AssignChannelOp;
315 GetMagickToken(p,&p,token);
316 }
cristya15140f2012-03-04 01:21:15 +0000317 if (*token == '>')
318 {
319 if (channel_op != ExchangeChannelOp)
320 channel_op=TransferChannelOp;
321 GetMagickToken(p,&p,token);
322 }
cristy8748f562012-03-08 13:58:34 +0000323 switch (channel_op)
324 {
325 case AssignChannelOp:
326 {
cristy41db1742012-03-09 00:57:52 +0000327 pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
cristy3bb378a2012-03-08 14:38:52 +0000328 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000329 break;
330 }
331 case ExchangeChannelOp:
332 case TransferChannelOp:
cristya15140f2012-03-04 01:21:15 +0000333 {
334 i=ParsePixelChannelOption(token);
335 if (i < 0)
336 {
337 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyabb241b2012-03-06 01:12:58 +0000338 "UnrecognizedChannelType","`%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000339 destination_image=DestroyImageList(destination_image);
cristyfc68ef52012-03-11 23:33:15 +0000340 return(destination_image);
cristya15140f2012-03-04 01:21:15 +0000341 }
cristyab272ac2012-03-04 22:08:07 +0000342 destination_channel=(PixelChannel) i;
cristy3bb378a2012-03-08 14:38:52 +0000343 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000344 break;
cristya15140f2012-03-04 01:21:15 +0000345 }
cristy8748f562012-03-08 13:58:34 +0000346 case ExtractChannelOp:
347 break;
348 }
cristy3bb378a2012-03-08 14:38:52 +0000349 status=ChannelImage(destination_image,destination_channel,channel_op,
350 source_image,source_channel,ClampToQuantum(pixel),exception);
cristya15140f2012-03-04 01:21:15 +0000351 if (status == MagickFalse)
352 {
cristyab272ac2012-03-04 22:08:07 +0000353 destination_image=DestroyImageList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000354 break;
355 }
cristy4ee60402012-03-11 02:01:20 +0000356 channels++;
cristy8748f562012-03-08 13:58:34 +0000357 if (channel_op == ExchangeChannelOp)
358 {
cristy4ee60402012-03-11 02:01:20 +0000359 status=ChannelImage(destination_image,source_channel,channel_op,
360 source_image,destination_channel,ClampToQuantum(pixel),exception);
cristy8748f562012-03-08 13:58:34 +0000361 if (status == MagickFalse)
362 {
363 destination_image=DestroyImageList(destination_image);
364 break;
365 }
cristy4ee60402012-03-11 02:01:20 +0000366 channels++;
cristy8748f562012-03-08 13:58:34 +0000367 }
cristy5f257b22012-03-07 00:27:29 +0000368 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
cristyab272ac2012-03-04 22:08:07 +0000369 strlen(expression));
370 if (status == MagickFalse)
371 break;
cristya15140f2012-03-04 01:21:15 +0000372 }
cristy4ee60402012-03-11 02:01:20 +0000373 status=SetImageStorageClass(destination_image,DirectClass,exception);
374 if (status == MagickFalse)
375 {
376 destination_image=GetLastImageInList(destination_image);
377 return((Image *) NULL);
378 }
379 if ((channel_op == ExtractChannelOp) && (channels == 1))
cristye21ffdf2012-03-05 16:33:39 +0000380 {
381 destination_image->colorspace=GRAYColorspace;
382 InitializePixelChannelMap(destination_image);
383 }
cristyab272ac2012-03-04 22:08:07 +0000384 return(destination_image);
cristyd04e7bf2012-03-03 19:19:12 +0000385}
cristy0c643722012-03-05 19:18:42 +0000386
387/*
388%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
389% %
390% %
391% %
392% C o m b i n e I m a g e s %
393% %
394% %
395% %
396%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
397%
398% CombineImages() combines one or more images into a single image. The
399% grayscale value of the pixels of each image in the sequence is assigned in
400% order to the specified channels of the combined image. The typical
401% ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
402%
403% The format of the CombineImages method is:
404%
405% Image *CombineImages(const Image *image,ExceptionInfo *exception)
406%
407% A description of each parameter follows:
408%
409% o image: the image.
410%
411% o exception: return any errors or warnings in this structure.
412%
413*/
414MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
415{
416#define CombineImageTag "Combine/Image"
417
418 CacheView
419 *combine_view;
420
421 Image
422 *combine_image;
423
424 MagickBooleanType
425 status;
426
427 MagickOffsetType
428 progress;
429
430 ssize_t
431 y;
432
433 /*
434 Ensure the image are the same size.
435 */
436 assert(image != (const Image *) NULL);
437 assert(image->signature == MagickSignature);
438 if (image->debug != MagickFalse)
439 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
440 assert(exception != (ExceptionInfo *) NULL);
441 assert(exception->signature == MagickSignature);
442 combine_image=CloneImage(image,0,0,MagickTrue,exception);
443 if (combine_image == (Image *) NULL)
444 return((Image *) NULL);
445 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
446 {
447 combine_image=DestroyImage(combine_image);
448 return((Image *) NULL);
449 }
450 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
451 combine_image->matte=MagickTrue;
452 /*
453 Combine images.
454 */
455 status=MagickTrue;
456 progress=0;
457 combine_view=AcquireCacheView(combine_image);
458 for (y=0; y < (ssize_t) combine_image->rows; y++)
459 {
460 CacheView
461 *image_view;
462
463 const Image
464 *next;
465
466 Quantum
467 *pixels;
468
469 register const Quantum
470 *restrict p;
471
472 register Quantum
473 *restrict q;
474
475 register ssize_t
476 i;
477
478 if (status == MagickFalse)
479 continue;
480 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
481 1,exception);
482 if (pixels == (Quantum *) NULL)
483 {
484 status=MagickFalse;
485 continue;
486 }
487 next=image;
488 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
489 {
490 PixelChannel
491 channel;
492
493 PixelTrait
494 combine_traits,
495 traits;
496
497 register ssize_t
498 x;
499
500 if (next == (Image *) NULL)
501 continue;
502 channel=GetPixelChannelMapChannel(image,i);
503 traits=GetPixelChannelMapTraits(image,channel);
504 combine_traits=GetPixelChannelMapTraits(combine_image,channel);
505 if ((traits == UndefinedPixelTrait) ||
506 (combine_traits == UndefinedPixelTrait))
507 continue;
508 image_view=AcquireCacheView(next);
509 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
510 if (p == (const Quantum *) NULL)
511 continue;
512 q=pixels;
513 for (x=0; x < (ssize_t) combine_image->columns; x++)
514 {
515 if (x < (ssize_t) image->columns)
516 {
517 q[i]=GetPixelGray(image,p);
518 p+=GetPixelChannels(image);
519 }
520 q+=GetPixelChannels(combine_image);
521 }
522 image_view=DestroyCacheView(image_view);
523 next=GetNextImageInList(next);
524 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
525 status=MagickFalse;
526 if (image->progress_monitor != (MagickProgressMonitor) NULL)
527 {
528 MagickBooleanType
529 proceed;
530
531 proceed=SetImageProgress(image,CombineImageTag,progress++,
532 combine_image->rows);
533 if (proceed == MagickFalse)
534 status=MagickFalse;
535 }
536 }
537 }
538 combine_view=DestroyCacheView(combine_view);
539 if (status == MagickFalse)
540 combine_image=DestroyImage(combine_image);
541 return(combine_image);
542}
543
544/*
545%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
546% %
547% %
548% %
549% S e p a r a t e I m a g e %
550% %
551% %
552% %
553%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
554%
555% SeparateImage() separates a channel from the image and returns it as a
556% grayscale image.
557%
558% The format of the SeparateImage method is:
559%
560% Image *SeparateImage(const Image *image,const ChannelType channel,
561% ExceptionInfo *exception)
562%
563% A description of each parameter follows:
564%
565% o image: the image.
566%
567% o channel: the image channel.
568%
569% o exception: return any errors or warnings in this structure.
570%
571*/
572MagickExport Image *SeparateImage(const Image *image,
573 const ChannelType channel_type,ExceptionInfo *exception)
574{
575#define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
576#define SeparateImageTag "Separate/Image"
577
578 CacheView
579 *image_view,
580 *separate_view;
581
582 Image
583 *separate_image;
584
585 MagickBooleanType
586 status;
587
588 MagickOffsetType
589 progress;
590
591 ssize_t
592 y;
593
594 /*
595 Initialize spread image attributes.
596 */
597 assert(image != (Image *) NULL);
598 assert(image->signature == MagickSignature);
599 if (image->debug != MagickFalse)
600 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
601 assert(exception != (ExceptionInfo *) NULL);
602 assert(exception->signature == MagickSignature);
603 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
604 exception);
605 if (separate_image == (Image *) NULL)
606 return((Image *) NULL);
607 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
608 {
609 separate_image=DestroyImage(separate_image);
610 return((Image *) NULL);
611 }
612 separate_image->colorspace=GRAYColorspace;
613 /*
614 Separate image.
615 */
616 status=MagickTrue;
617 progress=0;
618 image_view=AcquireCacheView(image);
619 separate_view=AcquireCacheView(separate_image);
620#if defined(MAGICKCORE_OPENMP_SUPPORT)
621 #pragma omp parallel for schedule(static) shared(progress,status)
622#endif
623 for (y=0; y < (ssize_t) image->rows; y++)
624 {
625 register const Quantum
626 *restrict p;
627
628 register Quantum
629 *restrict q;
630
631 register ssize_t
632 x;
633
634 if (status == MagickFalse)
635 continue;
636 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
637 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
638 exception);
639 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
640 {
641 status=MagickFalse;
642 continue;
643 }
644 for (x=0; x < (ssize_t) image->columns; x++)
645 {
646 register ssize_t
647 i;
648
649 if (GetPixelMask(image,p) != 0)
650 {
651 p+=GetPixelChannels(image);
652 q+=GetPixelChannels(separate_image);
653 continue;
654 }
655 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
656 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
657 {
658 PixelChannel
659 channel;
660
661 PixelTrait
662 traits;
663
664 channel=GetPixelChannelMapChannel(image,i);
665 traits=GetPixelChannelMapTraits(image,channel);
666 if ((traits == UndefinedPixelTrait) ||
667 (GetChannelBit(channel_type,channel) == 0))
668 continue;
669 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
670 }
671 p+=GetPixelChannels(image);
672 q+=GetPixelChannels(separate_image);
673 }
674 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
675 status=MagickFalse;
676 if (image->progress_monitor != (MagickProgressMonitor) NULL)
677 {
678 MagickBooleanType
679 proceed;
680
681#if defined(MAGICKCORE_OPENMP_SUPPORT)
682 #pragma omp critical (MagickCore_SeparateImage)
683#endif
684 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
685 if (proceed == MagickFalse)
686 status=MagickFalse;
687 }
688 }
689 separate_view=DestroyCacheView(separate_view);
690 image_view=DestroyCacheView(image_view);
691 return(separate_image);
692}
693
694/*
695%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
696% %
697% %
698% %
699% S e p a r a t e I m a g e s %
700% %
701% %
702% %
703%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
704%
705% SeparateImages() returns a separate grayscale image for each channel
706% specified.
707%
708% The format of the SeparateImages method is:
709%
710% MagickBooleanType SeparateImages(const Image *image,
711% ExceptionInfo *exception)
712%
713% A description of each parameter follows:
714%
715% o image: the image.
716%
717% o exception: return any errors or warnings in this structure.
718%
719*/
720MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
721{
722 Image
723 *images,
724 *separate_image;
725
726 register ssize_t
727 i;
728
729 assert(image != (Image *) NULL);
730 assert(image->signature == MagickSignature);
731 if (image->debug != MagickFalse)
732 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
733 images=NewImageList();
734 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
735 {
736 PixelChannel
737 channel;
738
739 PixelTrait
740 traits;
741
742 channel=GetPixelChannelMapChannel(image,i);
743 traits=GetPixelChannelMapTraits(image,channel);
744 if ((traits == UndefinedPixelTrait) ||
745 ((traits & UpdatePixelTrait) == 0))
746 continue;
747 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
748 if (separate_image != (Image *) NULL)
749 AppendImageToList(&images,separate_image);
750 }
751 return(images);
752}