blob: 17adb85cb0a1bb41c29ccff4a284ad6439c38b08 [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"
cristy16881e62012-05-06 14:41:29 +000054#include "MagickCore/thread-private.h"
cristya15140f2012-03-04 01:21:15 +000055#include "MagickCore/token.h"
cristyd04e7bf2012-03-03 19:19:12 +000056#include "MagickCore/utility.h"
57#include "MagickCore/version.h"
58
59/*
60%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
61% %
62% %
63% %
cristy5f257b22012-03-07 00:27:29 +000064% C h a n n e l F x I m a g e %
cristyd04e7bf2012-03-03 19:19:12 +000065% %
66% %
67% %
68%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
69%
cristy5f257b22012-03-07 00:27:29 +000070% ChannelFxImage() applies a channel expression to the specified image. The
71% expression consists of one or more channels, either mnemonic or numeric (e.g.
72% red, 1), separated by actions as follows:
cristyab272ac2012-03-04 22:08:07 +000073%
74% <=> exchange two channels (e.g. red<=>blue)
cristy7ea920d2012-03-16 01:08:01 +000075% => copy one channel to another channel (e.g. red=>green)
76% = assign a constant value to a channel (e.g. red=50%)
77% , write new image channels in the specified order (e.g. red, green)
cristy3c213612012-03-16 01:14:48 +000078% | add a new output image for the next set of channel operations
79% ; move to the next input image for the source of channel data
cristyab272ac2012-03-04 22:08:07 +000080%
cristy7ea920d2012-03-16 01:08:01 +000081% For example, to create 3 grayscale images from the red, green, and blue
82% channels of an image, use:
cristyab272ac2012-03-04 22:08:07 +000083%
cristy3c213612012-03-16 01:14:48 +000084% -channel-fx "red; green; blue"
cristy7ea920d2012-03-16 01:08:01 +000085%
86% A channel without an operation symbol implies separate (i.e, semicolon).
cristyd04e7bf2012-03-03 19:19:12 +000087%
cristy5f257b22012-03-07 00:27:29 +000088% The format of the ChannelFxImage method is:
cristyd04e7bf2012-03-03 19:19:12 +000089%
cristy5f257b22012-03-07 00:27:29 +000090% Image *ChannelFxImage(const Image *image,const char *expression,
91% ExceptionInfo *exception)
cristyd04e7bf2012-03-03 19:19:12 +000092%
93% A description of each parameter follows:
94%
cristyab272ac2012-03-04 22:08:07 +000095% o image: the image.
cristyd04e7bf2012-03-03 19:19:12 +000096%
97% o expression: A channel expression.
98%
99% o exception: return any errors or warnings in this structure.
100%
101*/
cristya15140f2012-03-04 01:21:15 +0000102
103typedef enum
104{
105 ExtractChannelOp,
cristy8748f562012-03-08 13:58:34 +0000106 AssignChannelOp,
cristya15140f2012-03-04 01:21:15 +0000107 ExchangeChannelOp,
108 TransferChannelOp
cristy5f257b22012-03-07 00:27:29 +0000109} ChannelFx;
cristya15140f2012-03-04 01:21:15 +0000110
cristyab272ac2012-03-04 22:08:07 +0000111static inline size_t MagickMin(const size_t x,const size_t y)
cristya15140f2012-03-04 01:21:15 +0000112{
cristyab272ac2012-03-04 22:08:07 +0000113 if (x < y)
114 return(x);
115 return(y);
cristya15140f2012-03-04 01:21:15 +0000116}
117
cristyab272ac2012-03-04 22:08:07 +0000118static MagickBooleanType ChannelImage(Image *destination_image,
cristy8748f562012-03-08 13:58:34 +0000119 const PixelChannel destination_channel,const ChannelFx channel_op,
cristy3bb378a2012-03-08 14:38:52 +0000120 const Image *source_image,const PixelChannel source_channel,
cristy8748f562012-03-08 13:58:34 +0000121 const Quantum pixel,ExceptionInfo *exception)
cristyab272ac2012-03-04 22:08:07 +0000122{
123 CacheView
124 *source_view,
125 *destination_view;
126
127 MagickBooleanType
128 status;
129
130 size_t
cristyac245f82012-05-05 17:13:57 +0000131 height,
132 width;
cristyab272ac2012-03-04 22:08:07 +0000133
134 ssize_t
135 y;
136
137 status=MagickTrue;
cristydb070952012-04-20 14:33:00 +0000138 source_view=AcquireVirtualCacheView(source_image,exception);
139 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristyab272ac2012-03-04 22:08:07 +0000140 height=MagickMin(source_image->rows,destination_image->rows);
cristyac245f82012-05-05 17:13:57 +0000141 width=MagickMin(source_image->columns,destination_image->columns);
cristyab272ac2012-03-04 22:08:07 +0000142#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000143 #pragma omp parallel for schedule(static) shared(status) \
cristy4ee2b0c2012-05-15 00:30:35 +0000144 dynamic_number_threads(source_image,width,height,1)
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 }
cristya06ee302012-04-29 20:56:04 +0000648 separate_image->matte=MagickFalse;
cristy079c78d2012-07-03 11:53:48 +0000649 (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
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) \
cristy4ee2b0c2012-05-15 00:30:35 +0000659 dynamic_number_threads(image,image->columns,image->rows,1)
cristy0c643722012-03-05 19:18:42 +0000660#endif
661 for (y=0; y < (ssize_t) image->rows; y++)
662 {
663 register const Quantum
664 *restrict p;
665
666 register Quantum
667 *restrict q;
668
669 register ssize_t
670 x;
671
672 if (status == MagickFalse)
673 continue;
674 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
675 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
676 exception);
677 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
678 {
679 status=MagickFalse;
680 continue;
681 }
682 for (x=0; x < (ssize_t) image->columns; x++)
683 {
684 register ssize_t
685 i;
686
687 if (GetPixelMask(image,p) != 0)
688 {
689 p+=GetPixelChannels(image);
690 q+=GetPixelChannels(separate_image);
691 continue;
692 }
693 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
694 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
695 {
696 PixelChannel
697 channel;
698
699 PixelTrait
700 traits;
701
702 channel=GetPixelChannelMapChannel(image,i);
703 traits=GetPixelChannelMapTraits(image,channel);
704 if ((traits == UndefinedPixelTrait) ||
705 (GetChannelBit(channel_type,channel) == 0))
706 continue;
707 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
708 }
709 p+=GetPixelChannels(image);
710 q+=GetPixelChannels(separate_image);
711 }
712 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
713 status=MagickFalse;
714 if (image->progress_monitor != (MagickProgressMonitor) NULL)
715 {
716 MagickBooleanType
717 proceed;
718
719#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000720 #pragma omp critical (MagickCore_SeparateImage)
cristy0c643722012-03-05 19:18:42 +0000721#endif
722 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
723 if (proceed == MagickFalse)
724 status=MagickFalse;
725 }
726 }
727 separate_view=DestroyCacheView(separate_view);
728 image_view=DestroyCacheView(image_view);
cristy079c78d2012-07-03 11:53:48 +0000729 (void) SetImageColorspace(separate_image,image->colorspace,exception);
730 (void) TransformImageColorspace(separate_image,GRAYColorspace,exception);
cristy0c643722012-03-05 19:18:42 +0000731 return(separate_image);
732}
733
734/*
735%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
736% %
737% %
738% %
739% S e p a r a t e I m a g e s %
740% %
741% %
742% %
743%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
744%
745% SeparateImages() returns a separate grayscale image for each channel
746% specified.
747%
748% The format of the SeparateImages method is:
749%
cristydfdb19e2012-03-21 22:22:24 +0000750% Image *SeparateImages(const Image *image,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000751%
752% A description of each parameter follows:
753%
754% o image: the image.
755%
756% o exception: return any errors or warnings in this structure.
757%
758*/
cristydfdb19e2012-03-21 22:22:24 +0000759MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000760{
761 Image
762 *images,
763 *separate_image;
764
765 register ssize_t
766 i;
767
768 assert(image != (Image *) NULL);
769 assert(image->signature == MagickSignature);
770 if (image->debug != MagickFalse)
771 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
772 images=NewImageList();
773 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
774 {
775 PixelChannel
776 channel;
777
778 PixelTrait
779 traits;
780
781 channel=GetPixelChannelMapChannel(image,i);
782 traits=GetPixelChannelMapTraits(image,channel);
783 if ((traits == UndefinedPixelTrait) ||
784 ((traits & UpdatePixelTrait) == 0))
785 continue;
786 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
787 if (separate_image != (Image *) NULL)
788 AppendImageToList(&images,separate_image);
789 }
cristye5ef2ce2012-04-29 21:14:32 +0000790 if (images == (Image *) NULL)
791 images=SeparateImage(image,UndefinedChannel,exception);
cristy0c643722012-03-05 19:18:42 +0000792 return(images);
793}