blob: 9f4dda7dd1935e4c60fcbdfa6c0f1cbe9d6cf229 [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)
cristy7ea920d2012-03-16 01:08:01 +000072% => copy one channel to another channel (e.g. red=>green)
73% = assign a constant value to a channel (e.g. red=50%)
74% , write new image channels in the specified order (e.g. red, green)
cristy3c213612012-03-16 01:14:48 +000075% | add a new output image for the next set of channel operations
76% ; move to the next input image for the source of channel data
cristyab272ac2012-03-04 22:08:07 +000077%
cristy7ea920d2012-03-16 01:08:01 +000078% For example, to create 3 grayscale images from the red, green, and blue
79% channels of an image, use:
cristyab272ac2012-03-04 22:08:07 +000080%
cristy3c213612012-03-16 01:14:48 +000081% -channel-fx "red; green; blue"
cristy7ea920d2012-03-16 01:08:01 +000082%
83% A channel without an operation symbol implies separate (i.e, semicolon).
cristyd04e7bf2012-03-03 19:19:12 +000084%
cristy5f257b22012-03-07 00:27:29 +000085% The format of the ChannelFxImage method is:
cristyd04e7bf2012-03-03 19:19:12 +000086%
cristy5f257b22012-03-07 00:27:29 +000087% Image *ChannelFxImage(const Image *image,const char *expression,
88% ExceptionInfo *exception)
cristyd04e7bf2012-03-03 19:19:12 +000089%
90% A description of each parameter follows:
91%
cristyab272ac2012-03-04 22:08:07 +000092% o image: the image.
cristyd04e7bf2012-03-03 19:19:12 +000093%
94% o expression: A channel expression.
95%
96% o exception: return any errors or warnings in this structure.
97%
98*/
cristya15140f2012-03-04 01:21:15 +000099
100typedef enum
101{
102 ExtractChannelOp,
cristy8748f562012-03-08 13:58:34 +0000103 AssignChannelOp,
cristya15140f2012-03-04 01:21:15 +0000104 ExchangeChannelOp,
105 TransferChannelOp
cristy5f257b22012-03-07 00:27:29 +0000106} ChannelFx;
cristya15140f2012-03-04 01:21:15 +0000107
cristyab272ac2012-03-04 22:08:07 +0000108static inline size_t MagickMin(const size_t x,const size_t y)
cristya15140f2012-03-04 01:21:15 +0000109{
cristyab272ac2012-03-04 22:08:07 +0000110 if (x < y)
111 return(x);
112 return(y);
cristya15140f2012-03-04 01:21:15 +0000113}
114
cristyab272ac2012-03-04 22:08:07 +0000115static MagickBooleanType ChannelImage(Image *destination_image,
cristy8748f562012-03-08 13:58:34 +0000116 const PixelChannel destination_channel,const ChannelFx channel_op,
cristy3bb378a2012-03-08 14:38:52 +0000117 const Image *source_image,const PixelChannel source_channel,
cristy8748f562012-03-08 13:58:34 +0000118 const Quantum pixel,ExceptionInfo *exception)
cristyab272ac2012-03-04 22:08:07 +0000119{
120 CacheView
121 *source_view,
122 *destination_view;
123
124 MagickBooleanType
125 status;
126
127 size_t
128 height;
129
130 ssize_t
131 y;
132
133 status=MagickTrue;
134 source_view=AcquireCacheView(source_image);
135 destination_view=AcquireCacheView(destination_image);
136 height=MagickMin(source_image->rows,destination_image->rows);
137#if defined(MAGICKCORE_OPENMP_SUPPORT)
138 #pragma omp parallel for schedule(static) shared(status)
139#endif
140 for (y=0; y < (ssize_t) height; y++)
141 {
cristy4ee60402012-03-11 02:01:20 +0000142 PixelTrait
143 destination_traits,
144 source_traits;
145
cristyab272ac2012-03-04 22:08:07 +0000146 register const Quantum
147 *restrict p;
148
149 register Quantum
150 *restrict q;
151
152 register ssize_t
153 x;
154
155 size_t
156 width;
157
158 if (status == MagickFalse)
159 continue;
160 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
161 exception);
cristy4ee60402012-03-11 02:01:20 +0000162 q=GetCacheViewAuthenticPixels(destination_view,0,y,
cristyab272ac2012-03-04 22:08:07 +0000163 destination_image->columns,1,exception);
164 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
165 {
166 status=MagickFalse;
167 continue;
168 }
cristy4ee60402012-03-11 02:01:20 +0000169 destination_traits=GetPixelChannelMapTraits(destination_image,
170 destination_channel);
171 source_traits=GetPixelChannelMapTraits(source_image,source_channel);
172 if ((destination_traits == UndefinedPixelTrait) ||
173 (source_traits == UndefinedPixelTrait))
174 continue;
cristyab272ac2012-03-04 22:08:07 +0000175 width=MagickMin(source_image->columns,destination_image->columns);
176 for (x=0; x < (ssize_t) width; x++)
177 {
cristy8748f562012-03-08 13:58:34 +0000178 if (channel_op == AssignChannelOp)
179 SetPixelChannel(destination_image,destination_channel,pixel,q);
180 else
cristy4ee60402012-03-11 02:01:20 +0000181 SetPixelChannel(destination_image,destination_channel,
182 GetPixelChannel(source_image,source_channel,p),q);
183 p+=GetPixelChannels(source_image);
cristyab272ac2012-03-04 22:08:07 +0000184 q+=GetPixelChannels(destination_image);
185 }
186 if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse)
187 status=MagickFalse;
188 }
189 destination_view=DestroyCacheView(destination_view);
190 source_view=DestroyCacheView(source_view);
191 return(status);
192}
193
cristy5f257b22012-03-07 00:27:29 +0000194MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
195 ExceptionInfo *exception)
cristyd04e7bf2012-03-03 19:19:12 +0000196{
cristy5f257b22012-03-07 00:27:29 +0000197#define ChannelFxImageTag "ChannelFx/Image"
cristyab272ac2012-03-04 22:08:07 +0000198
cristy5f257b22012-03-07 00:27:29 +0000199 ChannelFx
cristya15140f2012-03-04 01:21:15 +0000200 channel_op;
201
cristy4ee60402012-03-11 02:01:20 +0000202 char
203 token[MaxTextExtent];
204
cristya15140f2012-03-04 01:21:15 +0000205 const char
206 *p;
207
cristyab272ac2012-03-04 22:08:07 +0000208 const Image
209 *source_image;
210
cristy8748f562012-03-08 13:58:34 +0000211 double
212 pixel;
213
cristya15140f2012-03-04 01:21:15 +0000214 Image
cristyab272ac2012-03-04 22:08:07 +0000215 *destination_image;
cristya15140f2012-03-04 01:21:15 +0000216
cristy4ee60402012-03-11 02:01:20 +0000217 MagickBooleanType
218 status;
219
cristya15140f2012-03-04 01:21:15 +0000220 PixelChannel
cristyab272ac2012-03-04 22:08:07 +0000221 source_channel,
222 destination_channel;
cristya15140f2012-03-04 01:21:15 +0000223
cristyab272ac2012-03-04 22:08:07 +0000224 ssize_t
225 channels;
226
227 assert(image != (Image *) NULL);
228 assert(image->signature == MagickSignature);
229 if (image->debug != MagickFalse)
230 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
231 assert(exception != (ExceptionInfo *) NULL);
232 assert(exception->signature == MagickSignature);
233 source_image=image;
234 destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
235 if (destination_image == (Image *) NULL)
236 return((Image *) NULL);
cristyfc68ef52012-03-11 23:33:15 +0000237 if (destination_image->colorspace == GRAYColorspace)
238 destination_image->colorspace=RGBColorspace;
cristyab272ac2012-03-04 22:08:07 +0000239 if (expression == (const char *) NULL)
240 return(destination_image);
241 destination_channel=RedPixelChannel;
cristy8748f562012-03-08 13:58:34 +0000242 pixel=0.0;
cristya15140f2012-03-04 01:21:15 +0000243 p=(char *) expression;
244 GetMagickToken(p,&p,token);
cristy4ee60402012-03-11 02:01:20 +0000245 channel_op=ExtractChannelOp;
cristy09ef5be2012-03-13 13:01:27 +0000246 for (channels=0; *token != '\0'; )
cristya15140f2012-03-04 01:21:15 +0000247 {
cristya15140f2012-03-04 01:21:15 +0000248 ssize_t
249 i;
250
251 /*
252 Interpret channel expression.
253 */
254 if (*token == ',')
255 {
cristyab272ac2012-03-04 22:08:07 +0000256 destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
cristya15140f2012-03-04 01:21:15 +0000257 GetMagickToken(p,&p,token);
258 }
259 if (*token == '|')
260 {
cristyab272ac2012-03-04 22:08:07 +0000261 if (GetNextImageInList(source_image) != (Image *) NULL)
262 source_image=GetNextImageInList(source_image);
cristya15140f2012-03-04 01:21:15 +0000263 else
cristyab272ac2012-03-04 22:08:07 +0000264 source_image=GetFirstImageInList(source_image);
cristya15140f2012-03-04 01:21:15 +0000265 GetMagickToken(p,&p,token);
266 }
267 if (*token == ';')
268 {
cristyab272ac2012-03-04 22:08:07 +0000269 Image
270 *canvas;
271
cristy09ef5be2012-03-13 13:01:27 +0000272 if ((channel_op == ExtractChannelOp) && (channels == 1))
273 {
274 destination_image->colorspace=GRAYColorspace;
275 InitializePixelChannelMap(destination_image);
276 }
cristy4ee60402012-03-11 02:01:20 +0000277 status=SetImageStorageClass(destination_image,DirectClass,exception);
278 if (status == MagickFalse)
279 {
cristyfc68ef52012-03-11 23:33:15 +0000280 destination_image=DestroyImageList(destination_image);
281 return(destination_image);
cristy4ee60402012-03-11 02:01:20 +0000282 }
cristyab272ac2012-03-04 22:08:07 +0000283 canvas=CloneImage(source_image,0,0,MagickTrue,exception);
284 if (canvas == (Image *) NULL)
285 {
cristyfc68ef52012-03-11 23:33:15 +0000286 destination_image=DestroyImageList(destination_image);
287 return(destination_image);
cristyab272ac2012-03-04 22:08:07 +0000288 }
cristyfc68ef52012-03-11 23:33:15 +0000289 if (canvas->colorspace == GRAYColorspace)
290 canvas->colorspace=RGBColorspace;
cristyab272ac2012-03-04 22:08:07 +0000291 AppendImageToList(&destination_image,canvas);
292 destination_image=GetLastImageInList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000293 GetMagickToken(p,&p,token);
cristyab272ac2012-03-04 22:08:07 +0000294 channels=0;
295 destination_channel=RedPixelChannel;
cristya15140f2012-03-04 01:21:15 +0000296 }
297 i=ParsePixelChannelOption(token);
298 if (i < 0)
299 {
300 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyabb241b2012-03-06 01:12:58 +0000301 "UnrecognizedChannelType","`%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000302 destination_image=DestroyImageList(destination_image);
cristyfc68ef52012-03-11 23:33:15 +0000303 return(destination_image);
cristya15140f2012-03-04 01:21:15 +0000304 }
cristyab272ac2012-03-04 22:08:07 +0000305 source_channel=(PixelChannel) i;
cristya15140f2012-03-04 01:21:15 +0000306 channel_op=ExtractChannelOp;
307 GetMagickToken(p,&p,token);
308 if (*token == '<')
309 {
310 channel_op=ExchangeChannelOp;
311 GetMagickToken(p,&p,token);
312 }
313 if (*token == '=')
cristy8748f562012-03-08 13:58:34 +0000314 {
315 if (channel_op != ExchangeChannelOp)
316 channel_op=AssignChannelOp;
317 GetMagickToken(p,&p,token);
318 }
cristya15140f2012-03-04 01:21:15 +0000319 if (*token == '>')
320 {
321 if (channel_op != ExchangeChannelOp)
322 channel_op=TransferChannelOp;
323 GetMagickToken(p,&p,token);
324 }
cristy8748f562012-03-08 13:58:34 +0000325 switch (channel_op)
326 {
327 case AssignChannelOp:
328 {
cristy41db1742012-03-09 00:57:52 +0000329 pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
cristy3bb378a2012-03-08 14:38:52 +0000330 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000331 break;
332 }
333 case ExchangeChannelOp:
334 case TransferChannelOp:
cristya15140f2012-03-04 01:21:15 +0000335 {
336 i=ParsePixelChannelOption(token);
337 if (i < 0)
338 {
339 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyabb241b2012-03-06 01:12:58 +0000340 "UnrecognizedChannelType","`%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000341 destination_image=DestroyImageList(destination_image);
cristyfc68ef52012-03-11 23:33:15 +0000342 return(destination_image);
cristya15140f2012-03-04 01:21:15 +0000343 }
cristyab272ac2012-03-04 22:08:07 +0000344 destination_channel=(PixelChannel) i;
cristy542dd0b2012-03-18 19:10:09 +0000345 if ((LocaleCompare(token,"black") == 0) ||
346 (LocaleCompare(token,"c") == 0) ||
347 (LocaleCompare(token,"cyan") == 0) ||
348 (LocaleCompare(token,"k") == 0) ||
349 (LocaleCompare(token,"m") == 0) ||
350 (LocaleCompare(token,"magenta") == 0) ||
351 (LocaleCompare(token,"y") == 0) ||
352 (LocaleCompare(token,"yellow") == 0))
353 SetImageColorspace(destination_image,CMYKColorspace,exception);
cristyeef21762012-03-18 19:12:58 +0000354 if ((LocaleCompare(token,"Cb") == 0) ||
355 (LocaleCompare(token,"Cr") == 0))
356 SetImageColorspace(destination_image,YCbCrColorspace,exception);
cristy3bb378a2012-03-08 14:38:52 +0000357 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000358 break;
cristya15140f2012-03-04 01:21:15 +0000359 }
cristy8748f562012-03-08 13:58:34 +0000360 case ExtractChannelOp:
cristy7ea920d2012-03-16 01:08:01 +0000361 {
cristy7ea920d2012-03-16 01:08:01 +0000362 destination_channel++;
cristy3c213612012-03-16 01:14:48 +0000363 break;
cristy7ea920d2012-03-16 01:08:01 +0000364 }
365 }
cristy3bb378a2012-03-08 14:38:52 +0000366 status=ChannelImage(destination_image,destination_channel,channel_op,
367 source_image,source_channel,ClampToQuantum(pixel),exception);
cristya15140f2012-03-04 01:21:15 +0000368 if (status == MagickFalse)
369 {
cristyab272ac2012-03-04 22:08:07 +0000370 destination_image=DestroyImageList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000371 break;
372 }
cristy4ee60402012-03-11 02:01:20 +0000373 channels++;
cristy8748f562012-03-08 13:58:34 +0000374 if (channel_op == ExchangeChannelOp)
375 {
cristy4ee60402012-03-11 02:01:20 +0000376 status=ChannelImage(destination_image,source_channel,channel_op,
377 source_image,destination_channel,ClampToQuantum(pixel),exception);
cristy8748f562012-03-08 13:58:34 +0000378 if (status == MagickFalse)
379 {
380 destination_image=DestroyImageList(destination_image);
381 break;
382 }
cristy4ee60402012-03-11 02:01:20 +0000383 channels++;
cristy8748f562012-03-08 13:58:34 +0000384 }
cristy5f257b22012-03-07 00:27:29 +0000385 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
cristyab272ac2012-03-04 22:08:07 +0000386 strlen(expression));
387 if (status == MagickFalse)
388 break;
cristya15140f2012-03-04 01:21:15 +0000389 }
cristy09ef5be2012-03-13 13:01:27 +0000390 if ((channel_op == ExtractChannelOp) && (channels == 1))
391 {
392 destination_image->colorspace=GRAYColorspace;
393 InitializePixelChannelMap(destination_image);
394 }
cristy4ee60402012-03-11 02:01:20 +0000395 status=SetImageStorageClass(destination_image,DirectClass,exception);
396 if (status == MagickFalse)
397 {
398 destination_image=GetLastImageInList(destination_image);
399 return((Image *) NULL);
400 }
cristy09ef5be2012-03-13 13:01:27 +0000401 return(GetFirstImageInList(destination_image));
cristyd04e7bf2012-03-03 19:19:12 +0000402}
cristy0c643722012-03-05 19:18:42 +0000403
404/*
405%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
406% %
407% %
408% %
409% C o m b i n e I m a g e s %
410% %
411% %
412% %
413%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
414%
415% CombineImages() combines one or more images into a single image. The
416% grayscale value of the pixels of each image in the sequence is assigned in
417% order to the specified channels of the combined image. The typical
418% ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
419%
420% The format of the CombineImages method is:
421%
422% Image *CombineImages(const Image *image,ExceptionInfo *exception)
423%
424% A description of each parameter follows:
425%
426% o image: the image.
427%
428% o exception: return any errors or warnings in this structure.
429%
430*/
431MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
432{
433#define CombineImageTag "Combine/Image"
434
435 CacheView
436 *combine_view;
437
438 Image
439 *combine_image;
440
441 MagickBooleanType
442 status;
443
444 MagickOffsetType
445 progress;
446
447 ssize_t
448 y;
449
450 /*
451 Ensure the image are the same size.
452 */
453 assert(image != (const Image *) NULL);
454 assert(image->signature == MagickSignature);
455 if (image->debug != MagickFalse)
456 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
457 assert(exception != (ExceptionInfo *) NULL);
458 assert(exception->signature == MagickSignature);
459 combine_image=CloneImage(image,0,0,MagickTrue,exception);
460 if (combine_image == (Image *) NULL)
461 return((Image *) NULL);
462 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
463 {
464 combine_image=DestroyImage(combine_image);
465 return((Image *) NULL);
466 }
467 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
468 combine_image->matte=MagickTrue;
469 /*
470 Combine images.
471 */
472 status=MagickTrue;
473 progress=0;
474 combine_view=AcquireCacheView(combine_image);
475 for (y=0; y < (ssize_t) combine_image->rows; y++)
476 {
477 CacheView
478 *image_view;
479
480 const Image
481 *next;
482
483 Quantum
484 *pixels;
485
486 register const Quantum
487 *restrict p;
488
489 register Quantum
490 *restrict q;
491
492 register ssize_t
493 i;
494
495 if (status == MagickFalse)
496 continue;
497 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
498 1,exception);
499 if (pixels == (Quantum *) NULL)
500 {
501 status=MagickFalse;
502 continue;
503 }
504 next=image;
505 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
506 {
507 PixelChannel
508 channel;
509
510 PixelTrait
511 combine_traits,
512 traits;
513
514 register ssize_t
515 x;
516
517 if (next == (Image *) NULL)
518 continue;
519 channel=GetPixelChannelMapChannel(image,i);
520 traits=GetPixelChannelMapTraits(image,channel);
521 combine_traits=GetPixelChannelMapTraits(combine_image,channel);
522 if ((traits == UndefinedPixelTrait) ||
523 (combine_traits == UndefinedPixelTrait))
524 continue;
525 image_view=AcquireCacheView(next);
526 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
527 if (p == (const Quantum *) NULL)
528 continue;
529 q=pixels;
530 for (x=0; x < (ssize_t) combine_image->columns; x++)
531 {
532 if (x < (ssize_t) image->columns)
533 {
534 q[i]=GetPixelGray(image,p);
535 p+=GetPixelChannels(image);
536 }
537 q+=GetPixelChannels(combine_image);
538 }
539 image_view=DestroyCacheView(image_view);
540 next=GetNextImageInList(next);
541 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
542 status=MagickFalse;
543 if (image->progress_monitor != (MagickProgressMonitor) NULL)
544 {
545 MagickBooleanType
546 proceed;
547
548 proceed=SetImageProgress(image,CombineImageTag,progress++,
549 combine_image->rows);
550 if (proceed == MagickFalse)
551 status=MagickFalse;
552 }
553 }
554 }
555 combine_view=DestroyCacheView(combine_view);
556 if (status == MagickFalse)
557 combine_image=DestroyImage(combine_image);
558 return(combine_image);
559}
560
561/*
562%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
563% %
564% %
565% %
566% S e p a r a t e I m a g e %
567% %
568% %
569% %
570%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
571%
572% SeparateImage() separates a channel from the image and returns it as a
573% grayscale image.
574%
575% The format of the SeparateImage method is:
576%
577% Image *SeparateImage(const Image *image,const ChannelType channel,
578% ExceptionInfo *exception)
579%
580% A description of each parameter follows:
581%
582% o image: the image.
583%
584% o channel: the image channel.
585%
586% o exception: return any errors or warnings in this structure.
587%
588*/
589MagickExport Image *SeparateImage(const Image *image,
590 const ChannelType channel_type,ExceptionInfo *exception)
591{
592#define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
593#define SeparateImageTag "Separate/Image"
594
595 CacheView
596 *image_view,
597 *separate_view;
598
599 Image
600 *separate_image;
601
602 MagickBooleanType
603 status;
604
605 MagickOffsetType
606 progress;
607
608 ssize_t
609 y;
610
611 /*
612 Initialize spread image attributes.
613 */
614 assert(image != (Image *) NULL);
615 assert(image->signature == MagickSignature);
616 if (image->debug != MagickFalse)
617 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
618 assert(exception != (ExceptionInfo *) NULL);
619 assert(exception->signature == MagickSignature);
620 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
621 exception);
622 if (separate_image == (Image *) NULL)
623 return((Image *) NULL);
624 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
625 {
626 separate_image=DestroyImage(separate_image);
627 return((Image *) NULL);
628 }
629 separate_image->colorspace=GRAYColorspace;
630 /*
631 Separate image.
632 */
633 status=MagickTrue;
634 progress=0;
635 image_view=AcquireCacheView(image);
636 separate_view=AcquireCacheView(separate_image);
637#if defined(MAGICKCORE_OPENMP_SUPPORT)
638 #pragma omp parallel for schedule(static) shared(progress,status)
639#endif
640 for (y=0; y < (ssize_t) image->rows; y++)
641 {
642 register const Quantum
643 *restrict p;
644
645 register Quantum
646 *restrict q;
647
648 register ssize_t
649 x;
650
651 if (status == MagickFalse)
652 continue;
653 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
654 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
655 exception);
656 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
657 {
658 status=MagickFalse;
659 continue;
660 }
661 for (x=0; x < (ssize_t) image->columns; x++)
662 {
663 register ssize_t
664 i;
665
666 if (GetPixelMask(image,p) != 0)
667 {
668 p+=GetPixelChannels(image);
669 q+=GetPixelChannels(separate_image);
670 continue;
671 }
672 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
673 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
674 {
675 PixelChannel
676 channel;
677
678 PixelTrait
679 traits;
680
681 channel=GetPixelChannelMapChannel(image,i);
682 traits=GetPixelChannelMapTraits(image,channel);
683 if ((traits == UndefinedPixelTrait) ||
684 (GetChannelBit(channel_type,channel) == 0))
685 continue;
686 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
687 }
688 p+=GetPixelChannels(image);
689 q+=GetPixelChannels(separate_image);
690 }
691 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
692 status=MagickFalse;
693 if (image->progress_monitor != (MagickProgressMonitor) NULL)
694 {
695 MagickBooleanType
696 proceed;
697
698#if defined(MAGICKCORE_OPENMP_SUPPORT)
699 #pragma omp critical (MagickCore_SeparateImage)
700#endif
701 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
702 if (proceed == MagickFalse)
703 status=MagickFalse;
704 }
705 }
706 separate_view=DestroyCacheView(separate_view);
707 image_view=DestroyCacheView(image_view);
708 return(separate_image);
709}
710
711/*
712%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
713% %
714% %
715% %
716% S e p a r a t e I m a g e s %
717% %
718% %
719% %
720%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
721%
722% SeparateImages() returns a separate grayscale image for each channel
723% specified.
724%
725% The format of the SeparateImages method is:
726%
727% MagickBooleanType SeparateImages(const Image *image,
728% ExceptionInfo *exception)
729%
730% A description of each parameter follows:
731%
732% o image: the image.
733%
734% o exception: return any errors or warnings in this structure.
735%
736*/
737MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
738{
739 Image
740 *images,
741 *separate_image;
742
743 register ssize_t
744 i;
745
746 assert(image != (Image *) NULL);
747 assert(image->signature == MagickSignature);
748 if (image->debug != MagickFalse)
749 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
750 images=NewImageList();
751 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
752 {
753 PixelChannel
754 channel;
755
756 PixelTrait
757 traits;
758
759 channel=GetPixelChannelMapChannel(image,i);
760 traits=GetPixelChannelMapTraits(image,channel);
761 if ((traits == UndefinedPixelTrait) ||
762 ((traits & UpdatePixelTrait) == 0))
763 continue;
764 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
765 if (separate_image != (Image *) NULL)
766 AppendImageToList(&images,separate_image);
767 }
768 return(images);
769}