blob: 2b137eaef46dd7f483f66187ac72d8460d2f6e42 [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"
cristyc1119af2012-04-16 15:28:23 +000044#include "MagickCore/colorspace-private.h"
cristya15140f2012-03-04 01:21:15 +000045#include "MagickCore/image.h"
46#include "MagickCore/list.h"
47#include "MagickCore/log.h"
cristyab272ac2012-03-04 22:08:07 +000048#include "MagickCore/monitor.h"
49#include "MagickCore/monitor-private.h"
cristya15140f2012-03-04 01:21:15 +000050#include "MagickCore/option.h"
cristyab272ac2012-03-04 22:08:07 +000051#include "MagickCore/pixel-accessor.h"
cristy8748f562012-03-08 13:58:34 +000052#include "MagickCore/string-private.h"
cristya15140f2012-03-04 01:21:15 +000053#include "MagickCore/token.h"
cristyd04e7bf2012-03-03 19:19:12 +000054#include "MagickCore/utility.h"
55#include "MagickCore/version.h"
56
57/*
58%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
59% %
60% %
61% %
cristy5f257b22012-03-07 00:27:29 +000062% C h a n n e l F x I m a g e %
cristyd04e7bf2012-03-03 19:19:12 +000063% %
64% %
65% %
66%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
67%
cristy5f257b22012-03-07 00:27:29 +000068% ChannelFxImage() applies a channel expression to the specified image. The
69% expression consists of one or more channels, either mnemonic or numeric (e.g.
70% red, 1), separated by actions as follows:
cristyab272ac2012-03-04 22:08:07 +000071%
72% <=> exchange two channels (e.g. red<=>blue)
cristy7ea920d2012-03-16 01:08:01 +000073% => copy one channel to another channel (e.g. red=>green)
74% = assign a constant value to a channel (e.g. red=50%)
75% , write new image channels in the specified order (e.g. red, green)
cristy3c213612012-03-16 01:14:48 +000076% | add a new output image for the next set of channel operations
77% ; move to the next input image for the source of channel data
cristyab272ac2012-03-04 22:08:07 +000078%
cristy7ea920d2012-03-16 01:08:01 +000079% For example, to create 3 grayscale images from the red, green, and blue
80% channels of an image, use:
cristyab272ac2012-03-04 22:08:07 +000081%
cristy3c213612012-03-16 01:14:48 +000082% -channel-fx "red; green; blue"
cristy7ea920d2012-03-16 01:08:01 +000083%
84% A channel without an operation symbol implies separate (i.e, semicolon).
cristyd04e7bf2012-03-03 19:19:12 +000085%
cristy5f257b22012-03-07 00:27:29 +000086% The format of the ChannelFxImage method is:
cristyd04e7bf2012-03-03 19:19:12 +000087%
cristy5f257b22012-03-07 00:27:29 +000088% Image *ChannelFxImage(const Image *image,const char *expression,
89% ExceptionInfo *exception)
cristyd04e7bf2012-03-03 19:19:12 +000090%
91% A description of each parameter follows:
92%
cristyab272ac2012-03-04 22:08:07 +000093% o image: the image.
cristyd04e7bf2012-03-03 19:19:12 +000094%
95% o expression: A channel expression.
96%
97% o exception: return any errors or warnings in this structure.
98%
99*/
cristya15140f2012-03-04 01:21:15 +0000100
101typedef enum
102{
103 ExtractChannelOp,
cristy8748f562012-03-08 13:58:34 +0000104 AssignChannelOp,
cristya15140f2012-03-04 01:21:15 +0000105 ExchangeChannelOp,
106 TransferChannelOp
cristy5f257b22012-03-07 00:27:29 +0000107} ChannelFx;
cristya15140f2012-03-04 01:21:15 +0000108
cristyab272ac2012-03-04 22:08:07 +0000109static inline size_t MagickMin(const size_t x,const size_t y)
cristya15140f2012-03-04 01:21:15 +0000110{
cristyab272ac2012-03-04 22:08:07 +0000111 if (x < y)
112 return(x);
113 return(y);
cristya15140f2012-03-04 01:21:15 +0000114}
115
cristyab272ac2012-03-04 22:08:07 +0000116static MagickBooleanType ChannelImage(Image *destination_image,
cristy8748f562012-03-08 13:58:34 +0000117 const PixelChannel destination_channel,const ChannelFx channel_op,
cristy3bb378a2012-03-08 14:38:52 +0000118 const Image *source_image,const PixelChannel source_channel,
cristy8748f562012-03-08 13:58:34 +0000119 const Quantum pixel,ExceptionInfo *exception)
cristyab272ac2012-03-04 22:08:07 +0000120{
121 CacheView
122 *source_view,
123 *destination_view;
124
125 MagickBooleanType
126 status;
127
128 size_t
129 height;
130
131 ssize_t
132 y;
133
134 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +0000135 source_view=AcquireVirtualCacheView(source_image,exception);
136 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristyab272ac2012-03-04 22:08:07 +0000137 height=MagickMin(source_image->rows,destination_image->rows);
138#if defined(MAGICKCORE_OPENMP_SUPPORT)
139 #pragma omp parallel for schedule(static) shared(status)
140#endif
141 for (y=0; y < (ssize_t) height; y++)
142 {
cristy4ee60402012-03-11 02:01:20 +0000143 PixelTrait
144 destination_traits,
145 source_traits;
146
cristyab272ac2012-03-04 22:08:07 +0000147 register const Quantum
148 *restrict p;
149
150 register Quantum
151 *restrict q;
152
153 register ssize_t
154 x;
155
156 size_t
157 width;
158
159 if (status == MagickFalse)
160 continue;
161 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
162 exception);
cristy4ee60402012-03-11 02:01:20 +0000163 q=GetCacheViewAuthenticPixels(destination_view,0,y,
cristyab272ac2012-03-04 22:08:07 +0000164 destination_image->columns,1,exception);
165 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
166 {
167 status=MagickFalse;
168 continue;
169 }
cristy4ee60402012-03-11 02:01:20 +0000170 destination_traits=GetPixelChannelMapTraits(destination_image,
171 destination_channel);
172 source_traits=GetPixelChannelMapTraits(source_image,source_channel);
173 if ((destination_traits == UndefinedPixelTrait) ||
174 (source_traits == UndefinedPixelTrait))
175 continue;
cristyab272ac2012-03-04 22:08:07 +0000176 width=MagickMin(source_image->columns,destination_image->columns);
177 for (x=0; x < (ssize_t) width; x++)
178 {
cristy8748f562012-03-08 13:58:34 +0000179 if (channel_op == AssignChannelOp)
180 SetPixelChannel(destination_image,destination_channel,pixel,q);
181 else
cristy4ee60402012-03-11 02:01:20 +0000182 SetPixelChannel(destination_image,destination_channel,
183 GetPixelChannel(source_image,source_channel,p),q);
184 p+=GetPixelChannels(source_image);
cristyab272ac2012-03-04 22:08:07 +0000185 q+=GetPixelChannels(destination_image);
186 }
187 if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse)
188 status=MagickFalse;
189 }
190 destination_view=DestroyCacheView(destination_view);
191 source_view=DestroyCacheView(source_view);
192 return(status);
193}
194
cristy5f257b22012-03-07 00:27:29 +0000195MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
196 ExceptionInfo *exception)
cristyd04e7bf2012-03-03 19:19:12 +0000197{
cristy5f257b22012-03-07 00:27:29 +0000198#define ChannelFxImageTag "ChannelFx/Image"
cristyab272ac2012-03-04 22:08:07 +0000199
cristy5f257b22012-03-07 00:27:29 +0000200 ChannelFx
cristya15140f2012-03-04 01:21:15 +0000201 channel_op;
202
cristyfe88ede2012-03-22 00:34:48 +0000203 ChannelType
204 channel_mask;
205
cristy4ee60402012-03-11 02:01:20 +0000206 char
207 token[MaxTextExtent];
208
cristya15140f2012-03-04 01:21:15 +0000209 const char
210 *p;
211
cristyab272ac2012-03-04 22:08:07 +0000212 const Image
213 *source_image;
214
cristy8748f562012-03-08 13:58:34 +0000215 double
216 pixel;
217
cristya15140f2012-03-04 01:21:15 +0000218 Image
cristyab272ac2012-03-04 22:08:07 +0000219 *destination_image;
cristya15140f2012-03-04 01:21:15 +0000220
cristy4ee60402012-03-11 02:01:20 +0000221 MagickBooleanType
222 status;
223
cristya15140f2012-03-04 01:21:15 +0000224 PixelChannel
cristyab272ac2012-03-04 22:08:07 +0000225 source_channel,
226 destination_channel;
cristya15140f2012-03-04 01:21:15 +0000227
cristyab272ac2012-03-04 22:08:07 +0000228 ssize_t
229 channels;
230
231 assert(image != (Image *) NULL);
232 assert(image->signature == MagickSignature);
233 if (image->debug != MagickFalse)
234 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
235 assert(exception != (ExceptionInfo *) NULL);
236 assert(exception->signature == MagickSignature);
237 source_image=image;
238 destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
239 if (destination_image == (Image *) NULL)
240 return((Image *) NULL);
cristyfc68ef52012-03-11 23:33:15 +0000241 if (destination_image->colorspace == GRAYColorspace)
cristy7020ae62012-04-18 12:58:34 +0000242 destination_image->colorspace=sRGBColorspace;
cristyab272ac2012-03-04 22:08:07 +0000243 if (expression == (const char *) NULL)
244 return(destination_image);
245 destination_channel=RedPixelChannel;
cristyfe88ede2012-03-22 00:34:48 +0000246 channel_mask=UndefinedChannel;
cristy8748f562012-03-08 13:58:34 +0000247 pixel=0.0;
cristya15140f2012-03-04 01:21:15 +0000248 p=(char *) expression;
249 GetMagickToken(p,&p,token);
cristy4ee60402012-03-11 02:01:20 +0000250 channel_op=ExtractChannelOp;
cristy09ef5be2012-03-13 13:01:27 +0000251 for (channels=0; *token != '\0'; )
cristya15140f2012-03-04 01:21:15 +0000252 {
cristya15140f2012-03-04 01:21:15 +0000253 ssize_t
254 i;
255
256 /*
257 Interpret channel expression.
258 */
259 if (*token == ',')
260 {
cristyab272ac2012-03-04 22:08:07 +0000261 destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
cristya15140f2012-03-04 01:21:15 +0000262 GetMagickToken(p,&p,token);
263 }
264 if (*token == '|')
265 {
cristyab272ac2012-03-04 22:08:07 +0000266 if (GetNextImageInList(source_image) != (Image *) NULL)
267 source_image=GetNextImageInList(source_image);
cristya15140f2012-03-04 01:21:15 +0000268 else
cristyab272ac2012-03-04 22:08:07 +0000269 source_image=GetFirstImageInList(source_image);
cristya15140f2012-03-04 01:21:15 +0000270 GetMagickToken(p,&p,token);
271 }
272 if (*token == ';')
273 {
cristyab272ac2012-03-04 22:08:07 +0000274 Image
275 *canvas;
276
cristyfe88ede2012-03-22 00:34:48 +0000277 SetPixelChannelMapMask(destination_image,channel_mask);
278 if ((channel_op == ExtractChannelOp) && (destination_channel == 1))
279 (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
cristy4ee60402012-03-11 02:01:20 +0000280 status=SetImageStorageClass(destination_image,DirectClass,exception);
281 if (status == MagickFalse)
282 {
cristyfc68ef52012-03-11 23:33:15 +0000283 destination_image=DestroyImageList(destination_image);
284 return(destination_image);
cristy4ee60402012-03-11 02:01:20 +0000285 }
cristyab272ac2012-03-04 22:08:07 +0000286 canvas=CloneImage(source_image,0,0,MagickTrue,exception);
287 if (canvas == (Image *) NULL)
288 {
cristyfc68ef52012-03-11 23:33:15 +0000289 destination_image=DestroyImageList(destination_image);
290 return(destination_image);
cristyab272ac2012-03-04 22:08:07 +0000291 }
cristyfc68ef52012-03-11 23:33:15 +0000292 if (canvas->colorspace == GRAYColorspace)
cristy7020ae62012-04-18 12:58:34 +0000293 canvas->colorspace=sRGBColorspace;
cristyab272ac2012-03-04 22:08:07 +0000294 AppendImageToList(&destination_image,canvas);
295 destination_image=GetLastImageInList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000296 GetMagickToken(p,&p,token);
cristyab272ac2012-03-04 22:08:07 +0000297 channels=0;
298 destination_channel=RedPixelChannel;
cristyfe88ede2012-03-22 00:34:48 +0000299 channel_mask=UndefinedChannel;
cristya15140f2012-03-04 01:21:15 +0000300 }
301 i=ParsePixelChannelOption(token);
302 if (i < 0)
303 {
304 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +0000305 "UnrecognizedChannelType","'%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000306 destination_image=DestroyImageList(destination_image);
cristyfc68ef52012-03-11 23:33:15 +0000307 return(destination_image);
cristya15140f2012-03-04 01:21:15 +0000308 }
cristyab272ac2012-03-04 22:08:07 +0000309 source_channel=(PixelChannel) i;
cristya15140f2012-03-04 01:21:15 +0000310 channel_op=ExtractChannelOp;
311 GetMagickToken(p,&p,token);
312 if (*token == '<')
313 {
314 channel_op=ExchangeChannelOp;
315 GetMagickToken(p,&p,token);
316 }
317 if (*token == '=')
cristy8748f562012-03-08 13:58:34 +0000318 {
319 if (channel_op != ExchangeChannelOp)
320 channel_op=AssignChannelOp;
321 GetMagickToken(p,&p,token);
322 }
cristya15140f2012-03-04 01:21:15 +0000323 if (*token == '>')
324 {
325 if (channel_op != ExchangeChannelOp)
326 channel_op=TransferChannelOp;
327 GetMagickToken(p,&p,token);
328 }
cristy8748f562012-03-08 13:58:34 +0000329 switch (channel_op)
330 {
331 case AssignChannelOp:
332 {
cristy41db1742012-03-09 00:57:52 +0000333 pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
cristy3bb378a2012-03-08 14:38:52 +0000334 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000335 break;
336 }
337 case ExchangeChannelOp:
338 case TransferChannelOp:
cristya15140f2012-03-04 01:21:15 +0000339 {
340 i=ParsePixelChannelOption(token);
341 if (i < 0)
342 {
343 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +0000344 "UnrecognizedChannelType","'%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000345 destination_image=DestroyImageList(destination_image);
cristyfc68ef52012-03-11 23:33:15 +0000346 return(destination_image);
cristya15140f2012-03-04 01:21:15 +0000347 }
cristyab272ac2012-03-04 22:08:07 +0000348 destination_channel=(PixelChannel) i;
cristyaa2c16c2012-03-25 22:21:35 +0000349 channel_mask=(ChannelType) (channel_mask | ParseChannelOption(token));
cristy4b4a93a2012-03-21 23:15:56 +0000350 if (LocaleCompare(token,"gray") == 0)
cristyfe88ede2012-03-22 00:34:48 +0000351 (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
cristy542dd0b2012-03-18 19:10:09 +0000352 if ((LocaleCompare(token,"black") == 0) ||
353 (LocaleCompare(token,"c") == 0) ||
354 (LocaleCompare(token,"cyan") == 0) ||
355 (LocaleCompare(token,"k") == 0) ||
356 (LocaleCompare(token,"m") == 0) ||
357 (LocaleCompare(token,"magenta") == 0) ||
358 (LocaleCompare(token,"y") == 0) ||
359 (LocaleCompare(token,"yellow") == 0))
cristy322d07d2012-03-18 21:17:23 +0000360 (void) SetImageColorspace(destination_image,CMYKColorspace,exception);
cristyeef21762012-03-18 19:12:58 +0000361 if ((LocaleCompare(token,"Cb") == 0) ||
362 (LocaleCompare(token,"Cr") == 0))
cristy322d07d2012-03-18 21:17:23 +0000363 (void) SetImageColorspace(destination_image,YCbCrColorspace,
364 exception);
365 if (LocaleCompare(token,"alpha") == 0)
366 (void) SetImageAlpha(destination_image,OpaqueAlpha,exception);
cristy25d54482012-03-18 22:33:54 +0000367 if (i >= (ssize_t) GetPixelChannels(destination_image))
cristydfdb19e2012-03-21 22:22:24 +0000368 (void) SetPixelMetaChannels(destination_image,(size_t) (i-
369 GetPixelChannels(destination_image)+1),exception);
cristy3bb378a2012-03-08 14:38:52 +0000370 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000371 break;
cristya15140f2012-03-04 01:21:15 +0000372 }
cristyfe88ede2012-03-22 00:34:48 +0000373 default:
cristy3c213612012-03-16 01:14:48 +0000374 break;
cristy7ea920d2012-03-16 01:08:01 +0000375 }
cristy3bb378a2012-03-08 14:38:52 +0000376 status=ChannelImage(destination_image,destination_channel,channel_op,
377 source_image,source_channel,ClampToQuantum(pixel),exception);
cristya15140f2012-03-04 01:21:15 +0000378 if (status == MagickFalse)
379 {
cristyab272ac2012-03-04 22:08:07 +0000380 destination_image=DestroyImageList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000381 break;
382 }
cristy4ee60402012-03-11 02:01:20 +0000383 channels++;
cristy8748f562012-03-08 13:58:34 +0000384 if (channel_op == ExchangeChannelOp)
385 {
cristy4ee60402012-03-11 02:01:20 +0000386 status=ChannelImage(destination_image,source_channel,channel_op,
387 source_image,destination_channel,ClampToQuantum(pixel),exception);
cristy8748f562012-03-08 13:58:34 +0000388 if (status == MagickFalse)
389 {
390 destination_image=DestroyImageList(destination_image);
391 break;
392 }
cristy4ee60402012-03-11 02:01:20 +0000393 channels++;
cristy8748f562012-03-08 13:58:34 +0000394 }
cristyfe88ede2012-03-22 00:34:48 +0000395 switch (channel_op)
396 {
397 case ExtractChannelOp:
398 {
cristyaa2c16c2012-03-25 22:21:35 +0000399 channel_mask=(ChannelType) (channel_mask | (1 << destination_channel));
400 destination_channel=(PixelChannel) (destination_channel+1);
cristyfe88ede2012-03-22 00:34:48 +0000401 break;
402 }
403 default:
404 break;
405 }
cristy5f257b22012-03-07 00:27:29 +0000406 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
cristyab272ac2012-03-04 22:08:07 +0000407 strlen(expression));
408 if (status == MagickFalse)
409 break;
cristya15140f2012-03-04 01:21:15 +0000410 }
cristyfe88ede2012-03-22 00:34:48 +0000411 SetPixelChannelMapMask(destination_image,channel_mask);
412 if ((channel_op == ExtractChannelOp) && (destination_channel == 1))
413 (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
cristy4ee60402012-03-11 02:01:20 +0000414 status=SetImageStorageClass(destination_image,DirectClass,exception);
415 if (status == MagickFalse)
416 {
417 destination_image=GetLastImageInList(destination_image);
418 return((Image *) NULL);
419 }
cristy09ef5be2012-03-13 13:01:27 +0000420 return(GetFirstImageInList(destination_image));
cristyd04e7bf2012-03-03 19:19:12 +0000421}
cristy0c643722012-03-05 19:18:42 +0000422
423/*
424%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
425% %
426% %
427% %
428% C o m b i n e I m a g e s %
429% %
430% %
431% %
432%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
433%
434% CombineImages() combines one or more images into a single image. The
435% grayscale value of the pixels of each image in the sequence is assigned in
436% order to the specified channels of the combined image. The typical
437% ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
438%
439% The format of the CombineImages method is:
440%
441% Image *CombineImages(const Image *image,ExceptionInfo *exception)
442%
443% A description of each parameter follows:
444%
445% o image: the image.
446%
447% o exception: return any errors or warnings in this structure.
448%
449*/
450MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
451{
452#define CombineImageTag "Combine/Image"
453
454 CacheView
455 *combine_view;
456
457 Image
458 *combine_image;
459
460 MagickBooleanType
461 status;
462
463 MagickOffsetType
464 progress;
465
466 ssize_t
467 y;
468
469 /*
470 Ensure the image are the same size.
471 */
472 assert(image != (const Image *) NULL);
473 assert(image->signature == MagickSignature);
474 if (image->debug != MagickFalse)
475 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
476 assert(exception != (ExceptionInfo *) NULL);
477 assert(exception->signature == MagickSignature);
478 combine_image=CloneImage(image,0,0,MagickTrue,exception);
479 if (combine_image == (Image *) NULL)
480 return((Image *) NULL);
481 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
482 {
483 combine_image=DestroyImage(combine_image);
484 return((Image *) NULL);
485 }
cristyc1119af2012-04-16 15:28:23 +0000486 if (IsGrayColorspace(image->colorspace) != MagickFalse)
487 (void) SetImageColorspace(combine_image,sRGBColorspace,exception);
cristy0c643722012-03-05 19:18:42 +0000488 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
489 combine_image->matte=MagickTrue;
490 /*
491 Combine images.
492 */
493 status=MagickTrue;
494 progress=0;
cristydb070952012-04-20 14:33:00 +0000495 combine_view=AcquireAuthenticCacheView(combine_image,exception);
cristy0c643722012-03-05 19:18:42 +0000496 for (y=0; y < (ssize_t) combine_image->rows; y++)
497 {
498 CacheView
499 *image_view;
500
501 const Image
502 *next;
503
504 Quantum
505 *pixels;
506
507 register const Quantum
508 *restrict p;
509
510 register Quantum
511 *restrict q;
512
513 register ssize_t
514 i;
515
516 if (status == MagickFalse)
517 continue;
518 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
519 1,exception);
520 if (pixels == (Quantum *) NULL)
521 {
522 status=MagickFalse;
523 continue;
524 }
525 next=image;
cristyc1119af2012-04-16 15:28:23 +0000526 for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
cristy0c643722012-03-05 19:18:42 +0000527 {
528 PixelChannel
529 channel;
530
531 PixelTrait
cristy0c643722012-03-05 19:18:42 +0000532 traits;
533
534 register ssize_t
535 x;
536
537 if (next == (Image *) NULL)
538 continue;
cristyc1119af2012-04-16 15:28:23 +0000539 channel=GetPixelChannelMapChannel(combine_image,i);
540 traits=GetPixelChannelMapTraits(combine_image,channel);
541 if (traits == UndefinedPixelTrait)
cristy0c643722012-03-05 19:18:42 +0000542 continue;
cristydb070952012-04-20 14:33:00 +0000543 image_view=AcquireVirtualCacheView(next,exception);
cristy0c643722012-03-05 19:18:42 +0000544 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
545 if (p == (const Quantum *) NULL)
546 continue;
547 q=pixels;
548 for (x=0; x < (ssize_t) combine_image->columns; x++)
549 {
550 if (x < (ssize_t) image->columns)
551 {
552 q[i]=GetPixelGray(image,p);
553 p+=GetPixelChannels(image);
554 }
555 q+=GetPixelChannels(combine_image);
556 }
557 image_view=DestroyCacheView(image_view);
558 next=GetNextImageInList(next);
559 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
560 status=MagickFalse;
561 if (image->progress_monitor != (MagickProgressMonitor) NULL)
562 {
563 MagickBooleanType
564 proceed;
565
566 proceed=SetImageProgress(image,CombineImageTag,progress++,
567 combine_image->rows);
568 if (proceed == MagickFalse)
569 status=MagickFalse;
570 }
571 }
572 }
573 combine_view=DestroyCacheView(combine_view);
574 if (status == MagickFalse)
575 combine_image=DestroyImage(combine_image);
576 return(combine_image);
577}
578
579/*
580%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
581% %
582% %
583% %
584% S e p a r a t e I m a g e %
585% %
586% %
587% %
588%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
589%
590% SeparateImage() separates a channel from the image and returns it as a
591% grayscale image.
592%
593% The format of the SeparateImage method is:
594%
595% Image *SeparateImage(const Image *image,const ChannelType channel,
596% ExceptionInfo *exception)
597%
598% A description of each parameter follows:
599%
600% o image: the image.
601%
602% o channel: the image channel.
603%
604% o exception: return any errors or warnings in this structure.
605%
606*/
607MagickExport Image *SeparateImage(const Image *image,
608 const ChannelType channel_type,ExceptionInfo *exception)
609{
610#define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
611#define SeparateImageTag "Separate/Image"
612
613 CacheView
614 *image_view,
615 *separate_view;
616
617 Image
618 *separate_image;
619
620 MagickBooleanType
621 status;
622
623 MagickOffsetType
624 progress;
625
626 ssize_t
627 y;
628
629 /*
630 Initialize spread image attributes.
631 */
632 assert(image != (Image *) NULL);
633 assert(image->signature == MagickSignature);
634 if (image->debug != MagickFalse)
635 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
636 assert(exception != (ExceptionInfo *) NULL);
637 assert(exception->signature == MagickSignature);
638 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
639 exception);
640 if (separate_image == (Image *) NULL)
641 return((Image *) NULL);
642 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
643 {
644 separate_image=DestroyImage(separate_image);
645 return((Image *) NULL);
646 }
647 separate_image->colorspace=GRAYColorspace;
cristya06ee302012-04-29 20:56:04 +0000648 separate_image->matte=MagickFalse;
cristy0c643722012-03-05 19:18:42 +0000649 /*
650 Separate image.
651 */
652 status=MagickTrue;
653 progress=0;
cristydb070952012-04-20 14:33:00 +0000654 image_view=AcquireVirtualCacheView(image,exception);
655 separate_view=AcquireAuthenticCacheView(separate_image,exception);
cristy0c643722012-03-05 19:18:42 +0000656#if defined(MAGICKCORE_OPENMP_SUPPORT)
657 #pragma omp parallel for schedule(static) shared(progress,status)
658#endif
659 for (y=0; y < (ssize_t) image->rows; y++)
660 {
661 register const Quantum
662 *restrict p;
663
664 register Quantum
665 *restrict q;
666
667 register ssize_t
668 x;
669
670 if (status == MagickFalse)
671 continue;
672 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
673 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
674 exception);
675 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
676 {
677 status=MagickFalse;
678 continue;
679 }
680 for (x=0; x < (ssize_t) image->columns; x++)
681 {
682 register ssize_t
683 i;
684
685 if (GetPixelMask(image,p) != 0)
686 {
687 p+=GetPixelChannels(image);
688 q+=GetPixelChannels(separate_image);
689 continue;
690 }
691 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
692 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
693 {
694 PixelChannel
695 channel;
696
697 PixelTrait
698 traits;
699
700 channel=GetPixelChannelMapChannel(image,i);
701 traits=GetPixelChannelMapTraits(image,channel);
702 if ((traits == UndefinedPixelTrait) ||
703 (GetChannelBit(channel_type,channel) == 0))
704 continue;
705 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
706 }
707 p+=GetPixelChannels(image);
708 q+=GetPixelChannels(separate_image);
709 }
710 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
711 status=MagickFalse;
712 if (image->progress_monitor != (MagickProgressMonitor) NULL)
713 {
714 MagickBooleanType
715 proceed;
716
717#if defined(MAGICKCORE_OPENMP_SUPPORT)
718 #pragma omp critical (MagickCore_SeparateImage)
719#endif
720 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
721 if (proceed == MagickFalse)
722 status=MagickFalse;
723 }
724 }
725 separate_view=DestroyCacheView(separate_view);
726 image_view=DestroyCacheView(image_view);
727 return(separate_image);
728}
729
730/*
731%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
732% %
733% %
734% %
735% S e p a r a t e I m a g e s %
736% %
737% %
738% %
739%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
740%
741% SeparateImages() returns a separate grayscale image for each channel
742% specified.
743%
744% The format of the SeparateImages method is:
745%
cristydfdb19e2012-03-21 22:22:24 +0000746% Image *SeparateImages(const Image *image,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000747%
748% A description of each parameter follows:
749%
750% o image: the image.
751%
752% o exception: return any errors or warnings in this structure.
753%
754*/
cristydfdb19e2012-03-21 22:22:24 +0000755MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000756{
757 Image
758 *images,
759 *separate_image;
760
761 register ssize_t
762 i;
763
764 assert(image != (Image *) NULL);
765 assert(image->signature == MagickSignature);
766 if (image->debug != MagickFalse)
767 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
768 images=NewImageList();
769 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
770 {
771 PixelChannel
772 channel;
773
774 PixelTrait
775 traits;
776
777 channel=GetPixelChannelMapChannel(image,i);
778 traits=GetPixelChannelMapTraits(image,channel);
779 if ((traits == UndefinedPixelTrait) ||
780 ((traits & UpdatePixelTrait) == 0))
781 continue;
782 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
783 if (separate_image != (Image *) NULL)
784 AppendImageToList(&images,separate_image);
785 }
786 return(images);
787}