blob: 04546841d8c253c2a545985232cf4d67b67ca79a [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%
cristy46f354c2012-07-04 13:31:29 +0000442% Image *CombineImages(const Image *images,const ColorspaceType colorspace,
443% ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000444%
445% A description of each parameter follows:
446%
cristy46f354c2012-07-04 13:31:29 +0000447% o images: the image sequence.
448%
449% o colorspace: the image colorspace.
cristy0c643722012-03-05 19:18:42 +0000450%
451% o exception: return any errors or warnings in this structure.
452%
453*/
cristy46f354c2012-07-04 13:31:29 +0000454MagickExport Image *CombineImages(const Image *image,
455 const ColorspaceType colorspace,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000456{
457#define CombineImageTag "Combine/Image"
458
459 CacheView
460 *combine_view;
461
462 Image
463 *combine_image;
464
465 MagickBooleanType
466 status;
467
468 MagickOffsetType
469 progress;
470
471 ssize_t
472 y;
473
474 /*
475 Ensure the image are the same size.
476 */
477 assert(image != (const Image *) NULL);
478 assert(image->signature == MagickSignature);
479 if (image->debug != MagickFalse)
480 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
481 assert(exception != (ExceptionInfo *) NULL);
482 assert(exception->signature == MagickSignature);
483 combine_image=CloneImage(image,0,0,MagickTrue,exception);
484 if (combine_image == (Image *) NULL)
485 return((Image *) NULL);
486 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
487 {
488 combine_image=DestroyImage(combine_image);
489 return((Image *) NULL);
490 }
cristyc1119af2012-04-16 15:28:23 +0000491 if (IsGrayColorspace(image->colorspace) != MagickFalse)
cristy46f354c2012-07-04 13:31:29 +0000492 (void) SetImageColorspace(combine_image,RGBColorspace,exception);
cristy0c643722012-03-05 19:18:42 +0000493 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
494 combine_image->matte=MagickTrue;
495 /*
496 Combine images.
497 */
498 status=MagickTrue;
499 progress=0;
cristydb070952012-04-20 14:33:00 +0000500 combine_view=AcquireAuthenticCacheView(combine_image,exception);
cristy0c643722012-03-05 19:18:42 +0000501 for (y=0; y < (ssize_t) combine_image->rows; y++)
502 {
503 CacheView
504 *image_view;
505
506 const Image
507 *next;
508
509 Quantum
510 *pixels;
511
512 register const Quantum
513 *restrict p;
514
515 register Quantum
516 *restrict q;
517
518 register ssize_t
519 i;
520
521 if (status == MagickFalse)
522 continue;
523 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
524 1,exception);
525 if (pixels == (Quantum *) NULL)
526 {
527 status=MagickFalse;
528 continue;
529 }
530 next=image;
cristyc1119af2012-04-16 15:28:23 +0000531 for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
cristy0c643722012-03-05 19:18:42 +0000532 {
533 PixelChannel
534 channel;
535
536 PixelTrait
cristy0c643722012-03-05 19:18:42 +0000537 traits;
538
539 register ssize_t
540 x;
541
542 if (next == (Image *) NULL)
543 continue;
cristyc1119af2012-04-16 15:28:23 +0000544 channel=GetPixelChannelMapChannel(combine_image,i);
545 traits=GetPixelChannelMapTraits(combine_image,channel);
546 if (traits == UndefinedPixelTrait)
cristy0c643722012-03-05 19:18:42 +0000547 continue;
cristydb070952012-04-20 14:33:00 +0000548 image_view=AcquireVirtualCacheView(next,exception);
cristy0c643722012-03-05 19:18:42 +0000549 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
550 if (p == (const Quantum *) NULL)
551 continue;
552 q=pixels;
553 for (x=0; x < (ssize_t) combine_image->columns; x++)
554 {
555 if (x < (ssize_t) image->columns)
556 {
557 q[i]=GetPixelGray(image,p);
558 p+=GetPixelChannels(image);
559 }
560 q+=GetPixelChannels(combine_image);
561 }
562 image_view=DestroyCacheView(image_view);
563 next=GetNextImageInList(next);
564 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
565 status=MagickFalse;
566 if (image->progress_monitor != (MagickProgressMonitor) NULL)
567 {
568 MagickBooleanType
569 proceed;
570
571 proceed=SetImageProgress(image,CombineImageTag,progress++,
572 combine_image->rows);
573 if (proceed == MagickFalse)
574 status=MagickFalse;
575 }
576 }
577 }
578 combine_view=DestroyCacheView(combine_view);
579 if (status == MagickFalse)
580 combine_image=DestroyImage(combine_image);
cristy46f354c2012-07-04 13:31:29 +0000581 (void) TransformImageColorspace(combine_image,colorspace,exception);
cristy0c643722012-03-05 19:18:42 +0000582 return(combine_image);
583}
584
585/*
586%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
587% %
588% %
589% %
590% S e p a r a t e I m a g e %
591% %
592% %
593% %
594%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
595%
596% SeparateImage() separates a channel from the image and returns it as a
597% grayscale image.
598%
599% The format of the SeparateImage method is:
600%
601% Image *SeparateImage(const Image *image,const ChannelType channel,
602% ExceptionInfo *exception)
603%
604% A description of each parameter follows:
605%
606% o image: the image.
607%
608% o channel: the image channel.
609%
610% o exception: return any errors or warnings in this structure.
611%
612*/
613MagickExport Image *SeparateImage(const Image *image,
614 const ChannelType channel_type,ExceptionInfo *exception)
615{
616#define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
617#define SeparateImageTag "Separate/Image"
618
619 CacheView
620 *image_view,
621 *separate_view;
622
623 Image
624 *separate_image;
625
626 MagickBooleanType
627 status;
628
629 MagickOffsetType
630 progress;
631
632 ssize_t
633 y;
634
635 /*
636 Initialize spread image attributes.
637 */
638 assert(image != (Image *) NULL);
639 assert(image->signature == MagickSignature);
640 if (image->debug != MagickFalse)
641 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
642 assert(exception != (ExceptionInfo *) NULL);
643 assert(exception->signature == MagickSignature);
644 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
645 exception);
646 if (separate_image == (Image *) NULL)
647 return((Image *) NULL);
648 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
649 {
650 separate_image=DestroyImage(separate_image);
651 return((Image *) NULL);
652 }
cristya06ee302012-04-29 20:56:04 +0000653 separate_image->matte=MagickFalse;
cristy079c78d2012-07-03 11:53:48 +0000654 (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
cristy0c643722012-03-05 19:18:42 +0000655 /*
656 Separate image.
657 */
658 status=MagickTrue;
659 progress=0;
cristydb070952012-04-20 14:33:00 +0000660 image_view=AcquireVirtualCacheView(image,exception);
661 separate_view=AcquireAuthenticCacheView(separate_image,exception);
cristy0c643722012-03-05 19:18:42 +0000662#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000663 #pragma omp parallel for schedule(static) shared(progress,status) \
cristy4ee2b0c2012-05-15 00:30:35 +0000664 dynamic_number_threads(image,image->columns,image->rows,1)
cristy0c643722012-03-05 19:18:42 +0000665#endif
666 for (y=0; y < (ssize_t) image->rows; y++)
667 {
668 register const Quantum
669 *restrict p;
670
671 register Quantum
672 *restrict q;
673
674 register ssize_t
675 x;
676
677 if (status == MagickFalse)
678 continue;
679 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
680 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
681 exception);
682 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
683 {
684 status=MagickFalse;
685 continue;
686 }
687 for (x=0; x < (ssize_t) image->columns; x++)
688 {
689 register ssize_t
690 i;
691
692 if (GetPixelMask(image,p) != 0)
693 {
694 p+=GetPixelChannels(image);
695 q+=GetPixelChannels(separate_image);
696 continue;
697 }
698 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
699 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
700 {
701 PixelChannel
702 channel;
703
704 PixelTrait
705 traits;
706
707 channel=GetPixelChannelMapChannel(image,i);
708 traits=GetPixelChannelMapTraits(image,channel);
709 if ((traits == UndefinedPixelTrait) ||
710 (GetChannelBit(channel_type,channel) == 0))
711 continue;
712 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
713 }
714 p+=GetPixelChannels(image);
715 q+=GetPixelChannels(separate_image);
716 }
717 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
718 status=MagickFalse;
719 if (image->progress_monitor != (MagickProgressMonitor) NULL)
720 {
721 MagickBooleanType
722 proceed;
723
724#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000725 #pragma omp critical (MagickCore_SeparateImage)
cristy0c643722012-03-05 19:18:42 +0000726#endif
727 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
728 if (proceed == MagickFalse)
729 status=MagickFalse;
730 }
731 }
732 separate_view=DestroyCacheView(separate_view);
733 image_view=DestroyCacheView(image_view);
cristy079c78d2012-07-03 11:53:48 +0000734 (void) SetImageColorspace(separate_image,image->colorspace,exception);
735 (void) TransformImageColorspace(separate_image,GRAYColorspace,exception);
cristy0c643722012-03-05 19:18:42 +0000736 return(separate_image);
737}
738
739/*
740%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
741% %
742% %
743% %
744% S e p a r a t e I m a g e s %
745% %
746% %
747% %
748%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
749%
750% SeparateImages() returns a separate grayscale image for each channel
751% specified.
752%
753% The format of the SeparateImages method is:
754%
cristydfdb19e2012-03-21 22:22:24 +0000755% Image *SeparateImages(const Image *image,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000756%
757% A description of each parameter follows:
758%
759% o image: the image.
760%
761% o exception: return any errors or warnings in this structure.
762%
763*/
cristydfdb19e2012-03-21 22:22:24 +0000764MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000765{
766 Image
767 *images,
768 *separate_image;
769
770 register ssize_t
771 i;
772
773 assert(image != (Image *) NULL);
774 assert(image->signature == MagickSignature);
775 if (image->debug != MagickFalse)
776 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
777 images=NewImageList();
778 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
779 {
780 PixelChannel
781 channel;
782
783 PixelTrait
784 traits;
785
786 channel=GetPixelChannelMapChannel(image,i);
787 traits=GetPixelChannelMapTraits(image,channel);
788 if ((traits == UndefinedPixelTrait) ||
789 ((traits & UpdatePixelTrait) == 0))
790 continue;
791 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
792 if (separate_image != (Image *) NULL)
793 AppendImageToList(&images,separate_image);
794 }
cristye5ef2ce2012-04-29 21:14:32 +0000795 if (images == (Image *) NULL)
796 images=SeparateImage(image,UndefinedChannel,exception);
cristy0c643722012-03-05 19:18:42 +0000797 return(images);
798}