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