blob: 1ca7f64883b37c025ed5642b9c31a89c221d0ed9 [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"
cristyac245f82012-05-05 17:13:57 +000052#include "MagickCore/resource_.h"
cristy8748f562012-03-08 13:58:34 +000053#include "MagickCore/string-private.h"
cristya15140f2012-03-04 01:21:15 +000054#include "MagickCore/token.h"
cristyd04e7bf2012-03-03 19:19:12 +000055#include "MagickCore/utility.h"
56#include "MagickCore/version.h"
57
58/*
59%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
60% %
61% %
62% %
cristy5f257b22012-03-07 00:27:29 +000063% C h a n n e l F x I m a g e %
cristyd04e7bf2012-03-03 19:19:12 +000064% %
65% %
66% %
67%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
68%
cristy5f257b22012-03-07 00:27:29 +000069% ChannelFxImage() applies a channel expression to the specified image. The
70% expression consists of one or more channels, either mnemonic or numeric (e.g.
71% red, 1), separated by actions as follows:
cristyab272ac2012-03-04 22:08:07 +000072%
73% <=> exchange two channels (e.g. red<=>blue)
cristy7ea920d2012-03-16 01:08:01 +000074% => copy one channel to another channel (e.g. red=>green)
75% = assign a constant value to a channel (e.g. red=50%)
76% , write new image channels in the specified order (e.g. red, green)
cristy3c213612012-03-16 01:14:48 +000077% | add a new output image for the next set of channel operations
78% ; move to the next input image for the source of channel data
cristyab272ac2012-03-04 22:08:07 +000079%
cristy7ea920d2012-03-16 01:08:01 +000080% For example, to create 3 grayscale images from the red, green, and blue
81% channels of an image, use:
cristyab272ac2012-03-04 22:08:07 +000082%
cristy3c213612012-03-16 01:14:48 +000083% -channel-fx "red; green; blue"
cristy7ea920d2012-03-16 01:08:01 +000084%
85% A channel without an operation symbol implies separate (i.e, semicolon).
cristyd04e7bf2012-03-03 19:19:12 +000086%
cristy5f257b22012-03-07 00:27:29 +000087% The format of the ChannelFxImage method is:
cristyd04e7bf2012-03-03 19:19:12 +000088%
cristy5f257b22012-03-07 00:27:29 +000089% Image *ChannelFxImage(const Image *image,const char *expression,
90% ExceptionInfo *exception)
cristyd04e7bf2012-03-03 19:19:12 +000091%
92% A description of each parameter follows:
93%
cristyab272ac2012-03-04 22:08:07 +000094% o image: the image.
cristyd04e7bf2012-03-03 19:19:12 +000095%
96% o expression: A channel expression.
97%
98% o exception: return any errors or warnings in this structure.
99%
100*/
cristya15140f2012-03-04 01:21:15 +0000101
102typedef enum
103{
104 ExtractChannelOp,
cristy8748f562012-03-08 13:58:34 +0000105 AssignChannelOp,
cristya15140f2012-03-04 01:21:15 +0000106 ExchangeChannelOp,
107 TransferChannelOp
cristy5f257b22012-03-07 00:27:29 +0000108} ChannelFx;
cristya15140f2012-03-04 01:21:15 +0000109
cristyab272ac2012-03-04 22:08:07 +0000110static inline size_t MagickMin(const size_t x,const size_t y)
cristya15140f2012-03-04 01:21:15 +0000111{
cristyab272ac2012-03-04 22:08:07 +0000112 if (x < y)
113 return(x);
114 return(y);
cristya15140f2012-03-04 01:21:15 +0000115}
116
cristyab272ac2012-03-04 22:08:07 +0000117static MagickBooleanType ChannelImage(Image *destination_image,
cristy8748f562012-03-08 13:58:34 +0000118 const PixelChannel destination_channel,const ChannelFx channel_op,
cristy3bb378a2012-03-08 14:38:52 +0000119 const Image *source_image,const PixelChannel source_channel,
cristy8748f562012-03-08 13:58:34 +0000120 const Quantum pixel,ExceptionInfo *exception)
cristyab272ac2012-03-04 22:08:07 +0000121{
122 CacheView
123 *source_view,
124 *destination_view;
125
126 MagickBooleanType
127 status;
128
129 size_t
cristyac245f82012-05-05 17:13:57 +0000130 height,
131 width;
cristyab272ac2012-03-04 22:08:07 +0000132
133 ssize_t
134 y;
135
136 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +0000137 source_view=AcquireVirtualCacheView(source_image,exception);
138 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristyab272ac2012-03-04 22:08:07 +0000139 height=MagickMin(source_image->rows,destination_image->rows);
cristyac245f82012-05-05 17:13:57 +0000140 width=MagickMin(source_image->columns,destination_image->columns);
cristyab272ac2012-03-04 22:08:07 +0000141#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000142 #pragma omp parallel for schedule(static) shared(status) \
143 if ((height*width) > 8192) \
144 num_threads(GetMagickResourceLimit(ThreadResource))
cristyab272ac2012-03-04 22:08:07 +0000145#endif
146 for (y=0; y < (ssize_t) height; y++)
147 {
cristy4ee60402012-03-11 02:01:20 +0000148 PixelTrait
149 destination_traits,
150 source_traits;
151
cristyab272ac2012-03-04 22:08:07 +0000152 register const Quantum
153 *restrict p;
154
155 register Quantum
156 *restrict q;
157
158 register ssize_t
159 x;
160
cristyab272ac2012-03-04 22:08:07 +0000161 if (status == MagickFalse)
162 continue;
163 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
164 exception);
cristy4ee60402012-03-11 02:01:20 +0000165 q=GetCacheViewAuthenticPixels(destination_view,0,y,
cristyab272ac2012-03-04 22:08:07 +0000166 destination_image->columns,1,exception);
167 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
168 {
169 status=MagickFalse;
170 continue;
171 }
cristy4ee60402012-03-11 02:01:20 +0000172 destination_traits=GetPixelChannelMapTraits(destination_image,
173 destination_channel);
174 source_traits=GetPixelChannelMapTraits(source_image,source_channel);
175 if ((destination_traits == UndefinedPixelTrait) ||
176 (source_traits == UndefinedPixelTrait))
177 continue;
cristyab272ac2012-03-04 22:08:07 +0000178 for (x=0; x < (ssize_t) width; x++)
179 {
cristy8748f562012-03-08 13:58:34 +0000180 if (channel_op == AssignChannelOp)
181 SetPixelChannel(destination_image,destination_channel,pixel,q);
182 else
cristy4ee60402012-03-11 02:01:20 +0000183 SetPixelChannel(destination_image,destination_channel,
184 GetPixelChannel(source_image,source_channel,p),q);
185 p+=GetPixelChannels(source_image);
cristyab272ac2012-03-04 22:08:07 +0000186 q+=GetPixelChannels(destination_image);
187 }
188 if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse)
189 status=MagickFalse;
190 }
191 destination_view=DestroyCacheView(destination_view);
192 source_view=DestroyCacheView(source_view);
193 return(status);
194}
195
cristy5f257b22012-03-07 00:27:29 +0000196MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
197 ExceptionInfo *exception)
cristyd04e7bf2012-03-03 19:19:12 +0000198{
cristy5f257b22012-03-07 00:27:29 +0000199#define ChannelFxImageTag "ChannelFx/Image"
cristyab272ac2012-03-04 22:08:07 +0000200
cristy5f257b22012-03-07 00:27:29 +0000201 ChannelFx
cristya15140f2012-03-04 01:21:15 +0000202 channel_op;
203
cristyfe88ede2012-03-22 00:34:48 +0000204 ChannelType
205 channel_mask;
206
cristy4ee60402012-03-11 02:01:20 +0000207 char
208 token[MaxTextExtent];
209
cristya15140f2012-03-04 01:21:15 +0000210 const char
211 *p;
212
cristyab272ac2012-03-04 22:08:07 +0000213 const Image
214 *source_image;
215
cristy8748f562012-03-08 13:58:34 +0000216 double
217 pixel;
218
cristya15140f2012-03-04 01:21:15 +0000219 Image
cristyab272ac2012-03-04 22:08:07 +0000220 *destination_image;
cristya15140f2012-03-04 01:21:15 +0000221
cristy4ee60402012-03-11 02:01:20 +0000222 MagickBooleanType
223 status;
224
cristya15140f2012-03-04 01:21:15 +0000225 PixelChannel
cristyab272ac2012-03-04 22:08:07 +0000226 source_channel,
227 destination_channel;
cristya15140f2012-03-04 01:21:15 +0000228
cristyab272ac2012-03-04 22:08:07 +0000229 ssize_t
230 channels;
231
232 assert(image != (Image *) NULL);
233 assert(image->signature == MagickSignature);
234 if (image->debug != MagickFalse)
235 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
236 assert(exception != (ExceptionInfo *) NULL);
237 assert(exception->signature == MagickSignature);
238 source_image=image;
239 destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
240 if (destination_image == (Image *) NULL)
241 return((Image *) NULL);
cristy72a31a92012-05-03 14:21:18 +0000242 if (IsGrayColorspace(image->colorspace) != MagickFalse)
cristy46298092012-05-04 10:27:41 +0000243 (void) TransformImageColorspace((Image *) image,sRGBColorspace,exception);
cristyab272ac2012-03-04 22:08:07 +0000244 if (expression == (const char *) NULL)
245 return(destination_image);
246 destination_channel=RedPixelChannel;
cristyfe88ede2012-03-22 00:34:48 +0000247 channel_mask=UndefinedChannel;
cristy8748f562012-03-08 13:58:34 +0000248 pixel=0.0;
cristya15140f2012-03-04 01:21:15 +0000249 p=(char *) expression;
250 GetMagickToken(p,&p,token);
cristy4ee60402012-03-11 02:01:20 +0000251 channel_op=ExtractChannelOp;
cristy09ef5be2012-03-13 13:01:27 +0000252 for (channels=0; *token != '\0'; )
cristya15140f2012-03-04 01:21:15 +0000253 {
cristya15140f2012-03-04 01:21:15 +0000254 ssize_t
255 i;
256
257 /*
258 Interpret channel expression.
259 */
260 if (*token == ',')
261 {
cristyab272ac2012-03-04 22:08:07 +0000262 destination_channel=(PixelChannel) ((ssize_t) destination_channel+1);
cristya15140f2012-03-04 01:21:15 +0000263 GetMagickToken(p,&p,token);
264 }
265 if (*token == '|')
266 {
cristyab272ac2012-03-04 22:08:07 +0000267 if (GetNextImageInList(source_image) != (Image *) NULL)
268 source_image=GetNextImageInList(source_image);
cristya15140f2012-03-04 01:21:15 +0000269 else
cristyab272ac2012-03-04 22:08:07 +0000270 source_image=GetFirstImageInList(source_image);
cristya15140f2012-03-04 01:21:15 +0000271 GetMagickToken(p,&p,token);
272 }
273 if (*token == ';')
274 {
cristyab272ac2012-03-04 22:08:07 +0000275 Image
276 *canvas;
277
cristyfe88ede2012-03-22 00:34:48 +0000278 SetPixelChannelMapMask(destination_image,channel_mask);
279 if ((channel_op == ExtractChannelOp) && (destination_channel == 1))
280 (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
cristy4ee60402012-03-11 02:01:20 +0000281 status=SetImageStorageClass(destination_image,DirectClass,exception);
282 if (status == MagickFalse)
283 {
cristyfc68ef52012-03-11 23:33:15 +0000284 destination_image=DestroyImageList(destination_image);
285 return(destination_image);
cristy4ee60402012-03-11 02:01:20 +0000286 }
cristyab272ac2012-03-04 22:08:07 +0000287 canvas=CloneImage(source_image,0,0,MagickTrue,exception);
288 if (canvas == (Image *) NULL)
289 {
cristyfc68ef52012-03-11 23:33:15 +0000290 destination_image=DestroyImageList(destination_image);
291 return(destination_image);
cristyab272ac2012-03-04 22:08:07 +0000292 }
cristy72a31a92012-05-03 14:21:18 +0000293 if (IsGrayColorspace(canvas->colorspace) != MagickFalse)
294 (void) TransformImageColorspace(canvas,sRGBColorspace,exception);
cristyab272ac2012-03-04 22:08:07 +0000295 AppendImageToList(&destination_image,canvas);
296 destination_image=GetLastImageInList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000297 GetMagickToken(p,&p,token);
cristyab272ac2012-03-04 22:08:07 +0000298 channels=0;
299 destination_channel=RedPixelChannel;
cristyfe88ede2012-03-22 00:34:48 +0000300 channel_mask=UndefinedChannel;
cristya15140f2012-03-04 01:21:15 +0000301 }
302 i=ParsePixelChannelOption(token);
303 if (i < 0)
304 {
305 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +0000306 "UnrecognizedChannelType","'%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000307 destination_image=DestroyImageList(destination_image);
cristyfc68ef52012-03-11 23:33:15 +0000308 return(destination_image);
cristya15140f2012-03-04 01:21:15 +0000309 }
cristyab272ac2012-03-04 22:08:07 +0000310 source_channel=(PixelChannel) i;
cristya15140f2012-03-04 01:21:15 +0000311 channel_op=ExtractChannelOp;
312 GetMagickToken(p,&p,token);
313 if (*token == '<')
314 {
315 channel_op=ExchangeChannelOp;
316 GetMagickToken(p,&p,token);
317 }
318 if (*token == '=')
cristy8748f562012-03-08 13:58:34 +0000319 {
320 if (channel_op != ExchangeChannelOp)
321 channel_op=AssignChannelOp;
322 GetMagickToken(p,&p,token);
323 }
cristya15140f2012-03-04 01:21:15 +0000324 if (*token == '>')
325 {
326 if (channel_op != ExchangeChannelOp)
327 channel_op=TransferChannelOp;
328 GetMagickToken(p,&p,token);
329 }
cristy8748f562012-03-08 13:58:34 +0000330 switch (channel_op)
331 {
332 case AssignChannelOp:
333 {
cristy41db1742012-03-09 00:57:52 +0000334 pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
cristy3bb378a2012-03-08 14:38:52 +0000335 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000336 break;
337 }
338 case ExchangeChannelOp:
339 case TransferChannelOp:
cristya15140f2012-03-04 01:21:15 +0000340 {
341 i=ParsePixelChannelOption(token);
342 if (i < 0)
343 {
344 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +0000345 "UnrecognizedChannelType","'%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000346 destination_image=DestroyImageList(destination_image);
cristyfc68ef52012-03-11 23:33:15 +0000347 return(destination_image);
cristya15140f2012-03-04 01:21:15 +0000348 }
cristyab272ac2012-03-04 22:08:07 +0000349 destination_channel=(PixelChannel) i;
cristyaa2c16c2012-03-25 22:21:35 +0000350 channel_mask=(ChannelType) (channel_mask | ParseChannelOption(token));
cristy4b4a93a2012-03-21 23:15:56 +0000351 if (LocaleCompare(token,"gray") == 0)
cristyfe88ede2012-03-22 00:34:48 +0000352 (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
cristy542dd0b2012-03-18 19:10:09 +0000353 if ((LocaleCompare(token,"black") == 0) ||
354 (LocaleCompare(token,"c") == 0) ||
355 (LocaleCompare(token,"cyan") == 0) ||
356 (LocaleCompare(token,"k") == 0) ||
357 (LocaleCompare(token,"m") == 0) ||
358 (LocaleCompare(token,"magenta") == 0) ||
359 (LocaleCompare(token,"y") == 0) ||
360 (LocaleCompare(token,"yellow") == 0))
cristy322d07d2012-03-18 21:17:23 +0000361 (void) SetImageColorspace(destination_image,CMYKColorspace,exception);
cristyeef21762012-03-18 19:12:58 +0000362 if ((LocaleCompare(token,"Cb") == 0) ||
363 (LocaleCompare(token,"Cr") == 0))
cristy322d07d2012-03-18 21:17:23 +0000364 (void) SetImageColorspace(destination_image,YCbCrColorspace,
365 exception);
366 if (LocaleCompare(token,"alpha") == 0)
367 (void) SetImageAlpha(destination_image,OpaqueAlpha,exception);
cristy25d54482012-03-18 22:33:54 +0000368 if (i >= (ssize_t) GetPixelChannels(destination_image))
cristydfdb19e2012-03-21 22:22:24 +0000369 (void) SetPixelMetaChannels(destination_image,(size_t) (i-
370 GetPixelChannels(destination_image)+1),exception);
cristy3bb378a2012-03-08 14:38:52 +0000371 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000372 break;
cristya15140f2012-03-04 01:21:15 +0000373 }
cristyfe88ede2012-03-22 00:34:48 +0000374 default:
cristy3c213612012-03-16 01:14:48 +0000375 break;
cristy7ea920d2012-03-16 01:08:01 +0000376 }
cristy3bb378a2012-03-08 14:38:52 +0000377 status=ChannelImage(destination_image,destination_channel,channel_op,
378 source_image,source_channel,ClampToQuantum(pixel),exception);
cristya15140f2012-03-04 01:21:15 +0000379 if (status == MagickFalse)
380 {
cristyab272ac2012-03-04 22:08:07 +0000381 destination_image=DestroyImageList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000382 break;
383 }
cristy4ee60402012-03-11 02:01:20 +0000384 channels++;
cristy8748f562012-03-08 13:58:34 +0000385 if (channel_op == ExchangeChannelOp)
386 {
cristy4ee60402012-03-11 02:01:20 +0000387 status=ChannelImage(destination_image,source_channel,channel_op,
388 source_image,destination_channel,ClampToQuantum(pixel),exception);
cristy8748f562012-03-08 13:58:34 +0000389 if (status == MagickFalse)
390 {
391 destination_image=DestroyImageList(destination_image);
392 break;
393 }
cristy4ee60402012-03-11 02:01:20 +0000394 channels++;
cristy8748f562012-03-08 13:58:34 +0000395 }
cristyfe88ede2012-03-22 00:34:48 +0000396 switch (channel_op)
397 {
398 case ExtractChannelOp:
399 {
cristyaa2c16c2012-03-25 22:21:35 +0000400 channel_mask=(ChannelType) (channel_mask | (1 << destination_channel));
401 destination_channel=(PixelChannel) (destination_channel+1);
cristyfe88ede2012-03-22 00:34:48 +0000402 break;
403 }
404 default:
405 break;
406 }
cristy5f257b22012-03-07 00:27:29 +0000407 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
cristyab272ac2012-03-04 22:08:07 +0000408 strlen(expression));
409 if (status == MagickFalse)
410 break;
cristya15140f2012-03-04 01:21:15 +0000411 }
cristyfe88ede2012-03-22 00:34:48 +0000412 SetPixelChannelMapMask(destination_image,channel_mask);
413 if ((channel_op == ExtractChannelOp) && (destination_channel == 1))
414 (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
cristy4ee60402012-03-11 02:01:20 +0000415 status=SetImageStorageClass(destination_image,DirectClass,exception);
416 if (status == MagickFalse)
417 {
418 destination_image=GetLastImageInList(destination_image);
419 return((Image *) NULL);
420 }
cristy09ef5be2012-03-13 13:01:27 +0000421 return(GetFirstImageInList(destination_image));
cristyd04e7bf2012-03-03 19:19:12 +0000422}
cristy0c643722012-03-05 19:18:42 +0000423
424/*
425%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
426% %
427% %
428% %
429% C o m b i n e I m a g e s %
430% %
431% %
432% %
433%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
434%
435% CombineImages() combines one or more images into a single image. The
436% grayscale value of the pixels of each image in the sequence is assigned in
437% order to the specified channels of the combined image. The typical
438% ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
439%
440% The format of the CombineImages method is:
441%
442% Image *CombineImages(const Image *image,ExceptionInfo *exception)
443%
444% A description of each parameter follows:
445%
446% o image: the image.
447%
448% o exception: return any errors or warnings in this structure.
449%
450*/
451MagickExport Image *CombineImages(const Image *image,ExceptionInfo *exception)
452{
453#define CombineImageTag "Combine/Image"
454
455 CacheView
456 *combine_view;
457
458 Image
459 *combine_image;
460
461 MagickBooleanType
462 status;
463
464 MagickOffsetType
465 progress;
466
467 ssize_t
468 y;
469
470 /*
471 Ensure the image are the same size.
472 */
473 assert(image != (const Image *) NULL);
474 assert(image->signature == MagickSignature);
475 if (image->debug != MagickFalse)
476 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
477 assert(exception != (ExceptionInfo *) NULL);
478 assert(exception->signature == MagickSignature);
479 combine_image=CloneImage(image,0,0,MagickTrue,exception);
480 if (combine_image == (Image *) NULL)
481 return((Image *) NULL);
482 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
483 {
484 combine_image=DestroyImage(combine_image);
485 return((Image *) NULL);
486 }
cristyc1119af2012-04-16 15:28:23 +0000487 if (IsGrayColorspace(image->colorspace) != MagickFalse)
488 (void) SetImageColorspace(combine_image,sRGBColorspace,exception);
cristy0c643722012-03-05 19:18:42 +0000489 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
490 combine_image->matte=MagickTrue;
491 /*
492 Combine images.
493 */
494 status=MagickTrue;
495 progress=0;
cristydb070952012-04-20 14:33:00 +0000496 combine_view=AcquireAuthenticCacheView(combine_image,exception);
cristy0c643722012-03-05 19:18:42 +0000497 for (y=0; y < (ssize_t) combine_image->rows; y++)
498 {
499 CacheView
500 *image_view;
501
502 const Image
503 *next;
504
505 Quantum
506 *pixels;
507
508 register const Quantum
509 *restrict p;
510
511 register Quantum
512 *restrict q;
513
514 register ssize_t
515 i;
516
517 if (status == MagickFalse)
518 continue;
519 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
520 1,exception);
521 if (pixels == (Quantum *) NULL)
522 {
523 status=MagickFalse;
524 continue;
525 }
526 next=image;
cristyc1119af2012-04-16 15:28:23 +0000527 for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
cristy0c643722012-03-05 19:18:42 +0000528 {
529 PixelChannel
530 channel;
531
532 PixelTrait
cristy0c643722012-03-05 19:18:42 +0000533 traits;
534
535 register ssize_t
536 x;
537
538 if (next == (Image *) NULL)
539 continue;
cristyc1119af2012-04-16 15:28:23 +0000540 channel=GetPixelChannelMapChannel(combine_image,i);
541 traits=GetPixelChannelMapTraits(combine_image,channel);
542 if (traits == UndefinedPixelTrait)
cristy0c643722012-03-05 19:18:42 +0000543 continue;
cristydb070952012-04-20 14:33:00 +0000544 image_view=AcquireVirtualCacheView(next,exception);
cristy0c643722012-03-05 19:18:42 +0000545 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
546 if (p == (const Quantum *) NULL)
547 continue;
548 q=pixels;
549 for (x=0; x < (ssize_t) combine_image->columns; x++)
550 {
551 if (x < (ssize_t) image->columns)
552 {
553 q[i]=GetPixelGray(image,p);
554 p+=GetPixelChannels(image);
555 }
556 q+=GetPixelChannels(combine_image);
557 }
558 image_view=DestroyCacheView(image_view);
559 next=GetNextImageInList(next);
560 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
561 status=MagickFalse;
562 if (image->progress_monitor != (MagickProgressMonitor) NULL)
563 {
564 MagickBooleanType
565 proceed;
566
567 proceed=SetImageProgress(image,CombineImageTag,progress++,
568 combine_image->rows);
569 if (proceed == MagickFalse)
570 status=MagickFalse;
571 }
572 }
573 }
574 combine_view=DestroyCacheView(combine_view);
575 if (status == MagickFalse)
576 combine_image=DestroyImage(combine_image);
577 return(combine_image);
578}
579
580/*
581%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
582% %
583% %
584% %
585% S e p a r a t e I m a g e %
586% %
587% %
588% %
589%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
590%
591% SeparateImage() separates a channel from the image and returns it as a
592% grayscale image.
593%
594% The format of the SeparateImage method is:
595%
596% Image *SeparateImage(const Image *image,const ChannelType channel,
597% ExceptionInfo *exception)
598%
599% A description of each parameter follows:
600%
601% o image: the image.
602%
603% o channel: the image channel.
604%
605% o exception: return any errors or warnings in this structure.
606%
607*/
608MagickExport Image *SeparateImage(const Image *image,
609 const ChannelType channel_type,ExceptionInfo *exception)
610{
611#define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
612#define SeparateImageTag "Separate/Image"
613
614 CacheView
615 *image_view,
616 *separate_view;
617
618 Image
619 *separate_image;
620
621 MagickBooleanType
622 status;
623
624 MagickOffsetType
625 progress;
626
627 ssize_t
628 y;
629
630 /*
631 Initialize spread image attributes.
632 */
633 assert(image != (Image *) NULL);
634 assert(image->signature == MagickSignature);
635 if (image->debug != MagickFalse)
636 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
637 assert(exception != (ExceptionInfo *) NULL);
638 assert(exception->signature == MagickSignature);
639 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
640 exception);
641 if (separate_image == (Image *) NULL)
642 return((Image *) NULL);
643 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
644 {
645 separate_image=DestroyImage(separate_image);
646 return((Image *) NULL);
647 }
648 separate_image->colorspace=GRAYColorspace;
cristya06ee302012-04-29 20:56:04 +0000649 separate_image->matte=MagickFalse;
cristy0c643722012-03-05 19:18:42 +0000650 /*
651 Separate image.
652 */
653 status=MagickTrue;
654 progress=0;
cristydb070952012-04-20 14:33:00 +0000655 image_view=AcquireVirtualCacheView(image,exception);
656 separate_view=AcquireAuthenticCacheView(separate_image,exception);
cristy0c643722012-03-05 19:18:42 +0000657#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000658 #pragma omp parallel for schedule(static) shared(progress,status) \
659 if ((image->rows*image->columns) > 8192) \
660 num_threads(GetMagickResourceLimit(ThreadResource))
cristy0c643722012-03-05 19:18:42 +0000661#endif
662 for (y=0; y < (ssize_t) image->rows; y++)
663 {
664 register const Quantum
665 *restrict p;
666
667 register Quantum
668 *restrict q;
669
670 register ssize_t
671 x;
672
673 if (status == MagickFalse)
674 continue;
675 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
676 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
677 exception);
678 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
679 {
680 status=MagickFalse;
681 continue;
682 }
683 for (x=0; x < (ssize_t) image->columns; x++)
684 {
685 register ssize_t
686 i;
687
688 if (GetPixelMask(image,p) != 0)
689 {
690 p+=GetPixelChannels(image);
691 q+=GetPixelChannels(separate_image);
692 continue;
693 }
694 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
695 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
696 {
697 PixelChannel
698 channel;
699
700 PixelTrait
701 traits;
702
703 channel=GetPixelChannelMapChannel(image,i);
704 traits=GetPixelChannelMapTraits(image,channel);
705 if ((traits == UndefinedPixelTrait) ||
706 (GetChannelBit(channel_type,channel) == 0))
707 continue;
708 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
709 }
710 p+=GetPixelChannels(image);
711 q+=GetPixelChannels(separate_image);
712 }
713 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
714 status=MagickFalse;
715 if (image->progress_monitor != (MagickProgressMonitor) NULL)
716 {
717 MagickBooleanType
718 proceed;
719
720#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000721 #pragma omp critical (MagickCore_SeparateImage)
cristy0c643722012-03-05 19:18:42 +0000722#endif
723 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
724 if (proceed == MagickFalse)
725 status=MagickFalse;
726 }
727 }
728 separate_view=DestroyCacheView(separate_view);
729 image_view=DestroyCacheView(image_view);
730 return(separate_image);
731}
732
733/*
734%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
735% %
736% %
737% %
738% S e p a r a t e I m a g e s %
739% %
740% %
741% %
742%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
743%
744% SeparateImages() returns a separate grayscale image for each channel
745% specified.
746%
747% The format of the SeparateImages method is:
748%
cristydfdb19e2012-03-21 22:22:24 +0000749% Image *SeparateImages(const Image *image,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000750%
751% A description of each parameter follows:
752%
753% o image: the image.
754%
755% o exception: return any errors or warnings in this structure.
756%
757*/
cristydfdb19e2012-03-21 22:22:24 +0000758MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000759{
760 Image
761 *images,
762 *separate_image;
763
764 register ssize_t
765 i;
766
767 assert(image != (Image *) NULL);
768 assert(image->signature == MagickSignature);
769 if (image->debug != MagickFalse)
770 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
771 images=NewImageList();
772 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
773 {
774 PixelChannel
775 channel;
776
777 PixelTrait
778 traits;
779
780 channel=GetPixelChannelMapChannel(image,i);
781 traits=GetPixelChannelMapTraits(image,channel);
782 if ((traits == UndefinedPixelTrait) ||
783 ((traits & UpdatePixelTrait) == 0))
784 continue;
785 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
786 if (separate_image != (Image *) NULL)
787 AppendImageToList(&images,separate_image);
788 }
cristye5ef2ce2012-04-29 21:14:32 +0000789 if (images == (Image *) NULL)
790 images=SeparateImage(image,UndefinedChannel,exception);
cristy0c643722012-03-05 19:18:42 +0000791 return(images);
792}