blob: f01f11d139243a8b849c8cc84cffcfab0aed2916 [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
cristy4ee60402012-03-11 02:01:20 +0000272 status=SetImageStorageClass(destination_image,DirectClass,exception);
273 if (status == MagickFalse)
274 {
cristyfc68ef52012-03-11 23:33:15 +0000275 destination_image=DestroyImageList(destination_image);
276 return(destination_image);
cristy4ee60402012-03-11 02:01:20 +0000277 }
cristyab272ac2012-03-04 22:08:07 +0000278 canvas=CloneImage(source_image,0,0,MagickTrue,exception);
279 if (canvas == (Image *) NULL)
280 {
cristyfc68ef52012-03-11 23:33:15 +0000281 destination_image=DestroyImageList(destination_image);
282 return(destination_image);
cristyab272ac2012-03-04 22:08:07 +0000283 }
cristyfc68ef52012-03-11 23:33:15 +0000284 if (canvas->colorspace == GRAYColorspace)
285 canvas->colorspace=RGBColorspace;
cristyab272ac2012-03-04 22:08:07 +0000286 AppendImageToList(&destination_image,canvas);
287 destination_image=GetLastImageInList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000288 GetMagickToken(p,&p,token);
cristyab272ac2012-03-04 22:08:07 +0000289 channels=0;
290 destination_channel=RedPixelChannel;
cristya15140f2012-03-04 01:21:15 +0000291 }
292 i=ParsePixelChannelOption(token);
293 if (i < 0)
294 {
295 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyabb241b2012-03-06 01:12:58 +0000296 "UnrecognizedChannelType","`%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000297 destination_image=DestroyImageList(destination_image);
cristyfc68ef52012-03-11 23:33:15 +0000298 return(destination_image);
cristya15140f2012-03-04 01:21:15 +0000299 }
cristyab272ac2012-03-04 22:08:07 +0000300 source_channel=(PixelChannel) i;
cristya15140f2012-03-04 01:21:15 +0000301 channel_op=ExtractChannelOp;
302 GetMagickToken(p,&p,token);
303 if (*token == '<')
304 {
305 channel_op=ExchangeChannelOp;
306 GetMagickToken(p,&p,token);
307 }
308 if (*token == '=')
cristy8748f562012-03-08 13:58:34 +0000309 {
310 if (channel_op != ExchangeChannelOp)
311 channel_op=AssignChannelOp;
312 GetMagickToken(p,&p,token);
313 }
cristya15140f2012-03-04 01:21:15 +0000314 if (*token == '>')
315 {
316 if (channel_op != ExchangeChannelOp)
317 channel_op=TransferChannelOp;
318 GetMagickToken(p,&p,token);
319 }
cristy8748f562012-03-08 13:58:34 +0000320 switch (channel_op)
321 {
322 case AssignChannelOp:
323 {
cristy41db1742012-03-09 00:57:52 +0000324 pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
cristy3bb378a2012-03-08 14:38:52 +0000325 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000326 break;
327 }
328 case ExchangeChannelOp:
329 case TransferChannelOp:
cristya15140f2012-03-04 01:21:15 +0000330 {
331 i=ParsePixelChannelOption(token);
332 if (i < 0)
333 {
334 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyabb241b2012-03-06 01:12:58 +0000335 "UnrecognizedChannelType","`%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000336 destination_image=DestroyImageList(destination_image);
cristyfc68ef52012-03-11 23:33:15 +0000337 return(destination_image);
cristya15140f2012-03-04 01:21:15 +0000338 }
cristyab272ac2012-03-04 22:08:07 +0000339 destination_channel=(PixelChannel) i;
cristy4b4a93a2012-03-21 23:15:56 +0000340 if (LocaleCompare(token,"gray") == 0)
341 (void) SetImageAlpha(destination_image,GrayColorspace,exception);
cristy542dd0b2012-03-18 19:10:09 +0000342 if ((LocaleCompare(token,"black") == 0) ||
343 (LocaleCompare(token,"c") == 0) ||
344 (LocaleCompare(token,"cyan") == 0) ||
345 (LocaleCompare(token,"k") == 0) ||
346 (LocaleCompare(token,"m") == 0) ||
347 (LocaleCompare(token,"magenta") == 0) ||
348 (LocaleCompare(token,"y") == 0) ||
349 (LocaleCompare(token,"yellow") == 0))
cristy322d07d2012-03-18 21:17:23 +0000350 (void) SetImageColorspace(destination_image,CMYKColorspace,exception);
cristyeef21762012-03-18 19:12:58 +0000351 if ((LocaleCompare(token,"Cb") == 0) ||
352 (LocaleCompare(token,"Cr") == 0))
cristy322d07d2012-03-18 21:17:23 +0000353 (void) SetImageColorspace(destination_image,YCbCrColorspace,
354 exception);
355 if (LocaleCompare(token,"alpha") == 0)
356 (void) SetImageAlpha(destination_image,OpaqueAlpha,exception);
cristy25d54482012-03-18 22:33:54 +0000357 if (i >= (ssize_t) GetPixelChannels(destination_image))
cristydfdb19e2012-03-21 22:22:24 +0000358 (void) SetPixelMetaChannels(destination_image,(size_t) (i-
359 GetPixelChannels(destination_image)+1),exception);
cristy3bb378a2012-03-08 14:38:52 +0000360 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000361 break;
cristya15140f2012-03-04 01:21:15 +0000362 }
cristy8748f562012-03-08 13:58:34 +0000363 case ExtractChannelOp:
cristy7ea920d2012-03-16 01:08:01 +0000364 {
cristy7ea920d2012-03-16 01:08:01 +0000365 destination_channel++;
cristy3c213612012-03-16 01:14:48 +0000366 break;
cristy7ea920d2012-03-16 01:08:01 +0000367 }
368 }
cristy3bb378a2012-03-08 14:38:52 +0000369 status=ChannelImage(destination_image,destination_channel,channel_op,
370 source_image,source_channel,ClampToQuantum(pixel),exception);
cristya15140f2012-03-04 01:21:15 +0000371 if (status == MagickFalse)
372 {
cristyab272ac2012-03-04 22:08:07 +0000373 destination_image=DestroyImageList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000374 break;
375 }
cristy4ee60402012-03-11 02:01:20 +0000376 channels++;
cristy8748f562012-03-08 13:58:34 +0000377 if (channel_op == ExchangeChannelOp)
378 {
cristy4ee60402012-03-11 02:01:20 +0000379 status=ChannelImage(destination_image,source_channel,channel_op,
380 source_image,destination_channel,ClampToQuantum(pixel),exception);
cristy8748f562012-03-08 13:58:34 +0000381 if (status == MagickFalse)
382 {
383 destination_image=DestroyImageList(destination_image);
384 break;
385 }
cristy4ee60402012-03-11 02:01:20 +0000386 channels++;
cristy8748f562012-03-08 13:58:34 +0000387 }
cristy5f257b22012-03-07 00:27:29 +0000388 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
cristyab272ac2012-03-04 22:08:07 +0000389 strlen(expression));
390 if (status == MagickFalse)
391 break;
cristya15140f2012-03-04 01:21:15 +0000392 }
cristy4ee60402012-03-11 02:01:20 +0000393 status=SetImageStorageClass(destination_image,DirectClass,exception);
394 if (status == MagickFalse)
395 {
396 destination_image=GetLastImageInList(destination_image);
397 return((Image *) NULL);
398 }
cristy09ef5be2012-03-13 13:01:27 +0000399 return(GetFirstImageInList(destination_image));
cristyd04e7bf2012-03-03 19:19:12 +0000400}
cristy0c643722012-03-05 19:18:42 +0000401
402/*
403%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
404% %
405% %
406% %
407% C o m b i n e I m a g e s %
408% %
409% %
410% %
411%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
412%
413% CombineImages() combines one or more images into a single image. The
414% grayscale value of the pixels of each image in the sequence is assigned in
415% order to the specified channels of the combined image. The typical
416% ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
417%
418% The format of the CombineImages method is:
419%
420% Image *CombineImages(const Image *image,ExceptionInfo *exception)
421%
422% A description of each parameter follows:
423%
424% o image: the image.
425%
426% o exception: return any errors or warnings in this structure.
427%
428*/
429MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
430{
431#define CombineImageTag "Combine/Image"
432
433 CacheView
434 *combine_view;
435
436 Image
437 *combine_image;
438
439 MagickBooleanType
440 status;
441
442 MagickOffsetType
443 progress;
444
445 ssize_t
446 y;
447
448 /*
449 Ensure the image are the same size.
450 */
451 assert(image != (const Image *) NULL);
452 assert(image->signature == MagickSignature);
453 if (image->debug != MagickFalse)
454 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
455 assert(exception != (ExceptionInfo *) NULL);
456 assert(exception->signature == MagickSignature);
457 combine_image=CloneImage(image,0,0,MagickTrue,exception);
458 if (combine_image == (Image *) NULL)
459 return((Image *) NULL);
460 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
461 {
462 combine_image=DestroyImage(combine_image);
463 return((Image *) NULL);
464 }
465 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
466 combine_image->matte=MagickTrue;
467 /*
468 Combine images.
469 */
470 status=MagickTrue;
471 progress=0;
472 combine_view=AcquireCacheView(combine_image);
473 for (y=0; y < (ssize_t) combine_image->rows; y++)
474 {
475 CacheView
476 *image_view;
477
478 const Image
479 *next;
480
481 Quantum
482 *pixels;
483
484 register const Quantum
485 *restrict p;
486
487 register Quantum
488 *restrict q;
489
490 register ssize_t
491 i;
492
493 if (status == MagickFalse)
494 continue;
495 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
496 1,exception);
497 if (pixels == (Quantum *) NULL)
498 {
499 status=MagickFalse;
500 continue;
501 }
502 next=image;
503 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
504 {
505 PixelChannel
506 channel;
507
508 PixelTrait
509 combine_traits,
510 traits;
511
512 register ssize_t
513 x;
514
515 if (next == (Image *) NULL)
516 continue;
517 channel=GetPixelChannelMapChannel(image,i);
518 traits=GetPixelChannelMapTraits(image,channel);
519 combine_traits=GetPixelChannelMapTraits(combine_image,channel);
520 if ((traits == UndefinedPixelTrait) ||
521 (combine_traits == UndefinedPixelTrait))
522 continue;
523 image_view=AcquireCacheView(next);
524 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
525 if (p == (const Quantum *) NULL)
526 continue;
527 q=pixels;
528 for (x=0; x < (ssize_t) combine_image->columns; x++)
529 {
530 if (x < (ssize_t) image->columns)
531 {
532 q[i]=GetPixelGray(image,p);
533 p+=GetPixelChannels(image);
534 }
535 q+=GetPixelChannels(combine_image);
536 }
537 image_view=DestroyCacheView(image_view);
538 next=GetNextImageInList(next);
539 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
540 status=MagickFalse;
541 if (image->progress_monitor != (MagickProgressMonitor) NULL)
542 {
543 MagickBooleanType
544 proceed;
545
546 proceed=SetImageProgress(image,CombineImageTag,progress++,
547 combine_image->rows);
548 if (proceed == MagickFalse)
549 status=MagickFalse;
550 }
551 }
552 }
553 combine_view=DestroyCacheView(combine_view);
554 if (status == MagickFalse)
555 combine_image=DestroyImage(combine_image);
556 return(combine_image);
557}
558
559/*
560%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
561% %
562% %
563% %
564% S e p a r a t e I m a g e %
565% %
566% %
567% %
568%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
569%
570% SeparateImage() separates a channel from the image and returns it as a
571% grayscale image.
572%
573% The format of the SeparateImage method is:
574%
575% Image *SeparateImage(const Image *image,const ChannelType channel,
576% ExceptionInfo *exception)
577%
578% A description of each parameter follows:
579%
580% o image: the image.
581%
582% o channel: the image channel.
583%
584% o exception: return any errors or warnings in this structure.
585%
586*/
587MagickExport Image *SeparateImage(const Image *image,
588 const ChannelType channel_type,ExceptionInfo *exception)
589{
590#define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
591#define SeparateImageTag "Separate/Image"
592
593 CacheView
594 *image_view,
595 *separate_view;
596
597 Image
598 *separate_image;
599
600 MagickBooleanType
601 status;
602
603 MagickOffsetType
604 progress;
605
606 ssize_t
607 y;
608
609 /*
610 Initialize spread image attributes.
611 */
612 assert(image != (Image *) NULL);
613 assert(image->signature == MagickSignature);
614 if (image->debug != MagickFalse)
615 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
616 assert(exception != (ExceptionInfo *) NULL);
617 assert(exception->signature == MagickSignature);
618 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
619 exception);
620 if (separate_image == (Image *) NULL)
621 return((Image *) NULL);
622 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
623 {
624 separate_image=DestroyImage(separate_image);
625 return((Image *) NULL);
626 }
627 separate_image->colorspace=GRAYColorspace;
628 /*
629 Separate image.
630 */
631 status=MagickTrue;
632 progress=0;
633 image_view=AcquireCacheView(image);
634 separate_view=AcquireCacheView(separate_image);
635#if defined(MAGICKCORE_OPENMP_SUPPORT)
636 #pragma omp parallel for schedule(static) shared(progress,status)
637#endif
638 for (y=0; y < (ssize_t) image->rows; y++)
639 {
640 register const Quantum
641 *restrict p;
642
643 register Quantum
644 *restrict q;
645
646 register ssize_t
647 x;
648
649 if (status == MagickFalse)
650 continue;
651 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
652 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
653 exception);
654 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
655 {
656 status=MagickFalse;
657 continue;
658 }
659 for (x=0; x < (ssize_t) image->columns; x++)
660 {
661 register ssize_t
662 i;
663
664 if (GetPixelMask(image,p) != 0)
665 {
666 p+=GetPixelChannels(image);
667 q+=GetPixelChannels(separate_image);
668 continue;
669 }
670 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
671 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
672 {
673 PixelChannel
674 channel;
675
676 PixelTrait
677 traits;
678
679 channel=GetPixelChannelMapChannel(image,i);
680 traits=GetPixelChannelMapTraits(image,channel);
681 if ((traits == UndefinedPixelTrait) ||
682 (GetChannelBit(channel_type,channel) == 0))
683 continue;
684 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
685 }
686 p+=GetPixelChannels(image);
687 q+=GetPixelChannels(separate_image);
688 }
689 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
690 status=MagickFalse;
691 if (image->progress_monitor != (MagickProgressMonitor) NULL)
692 {
693 MagickBooleanType
694 proceed;
695
696#if defined(MAGICKCORE_OPENMP_SUPPORT)
697 #pragma omp critical (MagickCore_SeparateImage)
698#endif
699 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
700 if (proceed == MagickFalse)
701 status=MagickFalse;
702 }
703 }
704 separate_view=DestroyCacheView(separate_view);
705 image_view=DestroyCacheView(image_view);
706 return(separate_image);
707}
708
709/*
710%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
711% %
712% %
713% %
714% S e p a r a t e I m a g e s %
715% %
716% %
717% %
718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719%
720% SeparateImages() returns a separate grayscale image for each channel
721% specified.
722%
723% The format of the SeparateImages method is:
724%
cristydfdb19e2012-03-21 22:22:24 +0000725% Image *SeparateImages(const Image *image,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000726%
727% A description of each parameter follows:
728%
729% o image: the image.
730%
731% o exception: return any errors or warnings in this structure.
732%
733*/
cristydfdb19e2012-03-21 22:22:24 +0000734MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000735{
736 Image
737 *images,
738 *separate_image;
739
740 register ssize_t
741 i;
742
743 assert(image != (Image *) NULL);
744 assert(image->signature == MagickSignature);
745 if (image->debug != MagickFalse)
746 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
747 images=NewImageList();
748 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
749 {
750 PixelChannel
751 channel;
752
753 PixelTrait
754 traits;
755
756 channel=GetPixelChannelMapChannel(image,i);
757 traits=GetPixelChannelMapTraits(image,channel);
758 if ((traits == UndefinedPixelTrait) ||
759 ((traits & UpdatePixelTrait) == 0))
760 continue;
761 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
762 if (separate_image != (Image *) NULL)
763 AppendImageToList(&images,separate_image);
764 }
765 return(images);
766}