blob: 7cae8df0642dfc6b311560b5a84875f3fb595286 [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);
cristy3bb378a2012-03-08 14:38:52 +0000354 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000355 break;
cristya15140f2012-03-04 01:21:15 +0000356 }
cristy8748f562012-03-08 13:58:34 +0000357 case ExtractChannelOp:
cristy7ea920d2012-03-16 01:08:01 +0000358 {
cristy7ea920d2012-03-16 01:08:01 +0000359 destination_channel++;
cristy3c213612012-03-16 01:14:48 +0000360 break;
cristy7ea920d2012-03-16 01:08:01 +0000361 }
362 }
cristy3bb378a2012-03-08 14:38:52 +0000363 status=ChannelImage(destination_image,destination_channel,channel_op,
364 source_image,source_channel,ClampToQuantum(pixel),exception);
cristya15140f2012-03-04 01:21:15 +0000365 if (status == MagickFalse)
366 {
cristyab272ac2012-03-04 22:08:07 +0000367 destination_image=DestroyImageList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000368 break;
369 }
cristy4ee60402012-03-11 02:01:20 +0000370 channels++;
cristy8748f562012-03-08 13:58:34 +0000371 if (channel_op == ExchangeChannelOp)
372 {
cristy4ee60402012-03-11 02:01:20 +0000373 status=ChannelImage(destination_image,source_channel,channel_op,
374 source_image,destination_channel,ClampToQuantum(pixel),exception);
cristy8748f562012-03-08 13:58:34 +0000375 if (status == MagickFalse)
376 {
377 destination_image=DestroyImageList(destination_image);
378 break;
379 }
cristy4ee60402012-03-11 02:01:20 +0000380 channels++;
cristy8748f562012-03-08 13:58:34 +0000381 }
cristy5f257b22012-03-07 00:27:29 +0000382 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
cristyab272ac2012-03-04 22:08:07 +0000383 strlen(expression));
384 if (status == MagickFalse)
385 break;
cristya15140f2012-03-04 01:21:15 +0000386 }
cristy09ef5be2012-03-13 13:01:27 +0000387 if ((channel_op == ExtractChannelOp) && (channels == 1))
388 {
389 destination_image->colorspace=GRAYColorspace;
390 InitializePixelChannelMap(destination_image);
391 }
cristy4ee60402012-03-11 02:01:20 +0000392 status=SetImageStorageClass(destination_image,DirectClass,exception);
393 if (status == MagickFalse)
394 {
395 destination_image=GetLastImageInList(destination_image);
396 return((Image *) NULL);
397 }
cristy09ef5be2012-03-13 13:01:27 +0000398 return(GetFirstImageInList(destination_image));
cristyd04e7bf2012-03-03 19:19:12 +0000399}
cristy0c643722012-03-05 19:18:42 +0000400
401/*
402%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
403% %
404% %
405% %
406% C o m b i n e I m a g e s %
407% %
408% %
409% %
410%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
411%
412% CombineImages() combines one or more images into a single image. The
413% grayscale value of the pixels of each image in the sequence is assigned in
414% order to the specified channels of the combined image. The typical
415% ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
416%
417% The format of the CombineImages method is:
418%
419% Image *CombineImages(const Image *image,ExceptionInfo *exception)
420%
421% A description of each parameter follows:
422%
423% o image: the image.
424%
425% o exception: return any errors or warnings in this structure.
426%
427*/
428MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
429{
430#define CombineImageTag "Combine/Image"
431
432 CacheView
433 *combine_view;
434
435 Image
436 *combine_image;
437
438 MagickBooleanType
439 status;
440
441 MagickOffsetType
442 progress;
443
444 ssize_t
445 y;
446
447 /*
448 Ensure the image are the same size.
449 */
450 assert(image != (const Image *) NULL);
451 assert(image->signature == MagickSignature);
452 if (image->debug != MagickFalse)
453 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
454 assert(exception != (ExceptionInfo *) NULL);
455 assert(exception->signature == MagickSignature);
456 combine_image=CloneImage(image,0,0,MagickTrue,exception);
457 if (combine_image == (Image *) NULL)
458 return((Image *) NULL);
459 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
460 {
461 combine_image=DestroyImage(combine_image);
462 return((Image *) NULL);
463 }
464 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
465 combine_image->matte=MagickTrue;
466 /*
467 Combine images.
468 */
469 status=MagickTrue;
470 progress=0;
471 combine_view=AcquireCacheView(combine_image);
472 for (y=0; y < (ssize_t) combine_image->rows; y++)
473 {
474 CacheView
475 *image_view;
476
477 const Image
478 *next;
479
480 Quantum
481 *pixels;
482
483 register const Quantum
484 *restrict p;
485
486 register Quantum
487 *restrict q;
488
489 register ssize_t
490 i;
491
492 if (status == MagickFalse)
493 continue;
494 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
495 1,exception);
496 if (pixels == (Quantum *) NULL)
497 {
498 status=MagickFalse;
499 continue;
500 }
501 next=image;
502 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
503 {
504 PixelChannel
505 channel;
506
507 PixelTrait
508 combine_traits,
509 traits;
510
511 register ssize_t
512 x;
513
514 if (next == (Image *) NULL)
515 continue;
516 channel=GetPixelChannelMapChannel(image,i);
517 traits=GetPixelChannelMapTraits(image,channel);
518 combine_traits=GetPixelChannelMapTraits(combine_image,channel);
519 if ((traits == UndefinedPixelTrait) ||
520 (combine_traits == UndefinedPixelTrait))
521 continue;
522 image_view=AcquireCacheView(next);
523 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
524 if (p == (const Quantum *) NULL)
525 continue;
526 q=pixels;
527 for (x=0; x < (ssize_t) combine_image->columns; x++)
528 {
529 if (x < (ssize_t) image->columns)
530 {
531 q[i]=GetPixelGray(image,p);
532 p+=GetPixelChannels(image);
533 }
534 q+=GetPixelChannels(combine_image);
535 }
536 image_view=DestroyCacheView(image_view);
537 next=GetNextImageInList(next);
538 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
539 status=MagickFalse;
540 if (image->progress_monitor != (MagickProgressMonitor) NULL)
541 {
542 MagickBooleanType
543 proceed;
544
545 proceed=SetImageProgress(image,CombineImageTag,progress++,
546 combine_image->rows);
547 if (proceed == MagickFalse)
548 status=MagickFalse;
549 }
550 }
551 }
552 combine_view=DestroyCacheView(combine_view);
553 if (status == MagickFalse)
554 combine_image=DestroyImage(combine_image);
555 return(combine_image);
556}
557
558/*
559%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
560% %
561% %
562% %
563% S e p a r a t e I m a g e %
564% %
565% %
566% %
567%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
568%
569% SeparateImage() separates a channel from the image and returns it as a
570% grayscale image.
571%
572% The format of the SeparateImage method is:
573%
574% Image *SeparateImage(const Image *image,const ChannelType channel,
575% ExceptionInfo *exception)
576%
577% A description of each parameter follows:
578%
579% o image: the image.
580%
581% o channel: the image channel.
582%
583% o exception: return any errors or warnings in this structure.
584%
585*/
586MagickExport Image *SeparateImage(const Image *image,
587 const ChannelType channel_type,ExceptionInfo *exception)
588{
589#define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
590#define SeparateImageTag "Separate/Image"
591
592 CacheView
593 *image_view,
594 *separate_view;
595
596 Image
597 *separate_image;
598
599 MagickBooleanType
600 status;
601
602 MagickOffsetType
603 progress;
604
605 ssize_t
606 y;
607
608 /*
609 Initialize spread image attributes.
610 */
611 assert(image != (Image *) NULL);
612 assert(image->signature == MagickSignature);
613 if (image->debug != MagickFalse)
614 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
615 assert(exception != (ExceptionInfo *) NULL);
616 assert(exception->signature == MagickSignature);
617 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
618 exception);
619 if (separate_image == (Image *) NULL)
620 return((Image *) NULL);
621 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
622 {
623 separate_image=DestroyImage(separate_image);
624 return((Image *) NULL);
625 }
626 separate_image->colorspace=GRAYColorspace;
627 /*
628 Separate image.
629 */
630 status=MagickTrue;
631 progress=0;
632 image_view=AcquireCacheView(image);
633 separate_view=AcquireCacheView(separate_image);
634#if defined(MAGICKCORE_OPENMP_SUPPORT)
635 #pragma omp parallel for schedule(static) shared(progress,status)
636#endif
637 for (y=0; y < (ssize_t) image->rows; y++)
638 {
639 register const Quantum
640 *restrict p;
641
642 register Quantum
643 *restrict q;
644
645 register ssize_t
646 x;
647
648 if (status == MagickFalse)
649 continue;
650 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
651 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
652 exception);
653 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
654 {
655 status=MagickFalse;
656 continue;
657 }
658 for (x=0; x < (ssize_t) image->columns; x++)
659 {
660 register ssize_t
661 i;
662
663 if (GetPixelMask(image,p) != 0)
664 {
665 p+=GetPixelChannels(image);
666 q+=GetPixelChannels(separate_image);
667 continue;
668 }
669 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
670 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
671 {
672 PixelChannel
673 channel;
674
675 PixelTrait
676 traits;
677
678 channel=GetPixelChannelMapChannel(image,i);
679 traits=GetPixelChannelMapTraits(image,channel);
680 if ((traits == UndefinedPixelTrait) ||
681 (GetChannelBit(channel_type,channel) == 0))
682 continue;
683 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
684 }
685 p+=GetPixelChannels(image);
686 q+=GetPixelChannels(separate_image);
687 }
688 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
689 status=MagickFalse;
690 if (image->progress_monitor != (MagickProgressMonitor) NULL)
691 {
692 MagickBooleanType
693 proceed;
694
695#if defined(MAGICKCORE_OPENMP_SUPPORT)
696 #pragma omp critical (MagickCore_SeparateImage)
697#endif
698 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
699 if (proceed == MagickFalse)
700 status=MagickFalse;
701 }
702 }
703 separate_view=DestroyCacheView(separate_view);
704 image_view=DestroyCacheView(image_view);
705 return(separate_image);
706}
707
708/*
709%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
710% %
711% %
712% %
713% S e p a r a t e I m a g e s %
714% %
715% %
716% %
717%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
718%
719% SeparateImages() returns a separate grayscale image for each channel
720% specified.
721%
722% The format of the SeparateImages method is:
723%
724% MagickBooleanType SeparateImages(const Image *image,
725% ExceptionInfo *exception)
726%
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*/
734MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
735{
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}