blob: 2430bf1461cd6a7705703228e910fbacdec7b652 [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% %
cristyfe676ee2013-11-18 13:03:38 +000020% Copyright 1999-2014 ImageMagick Studio LLC, a non-profit organization %
cristyd04e7bf2012-03-03 19:19:12 +000021% 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"
cristy8c8c9162013-05-27 10:14:21 +000044#include "MagickCore/cache-private.h"
cristyc1119af2012-04-16 15:28:23 +000045#include "MagickCore/colorspace-private.h"
cristy8c8c9162013-05-27 10:14:21 +000046#include "MagickCore/composite-private.h"
47#include "MagickCore/enhance.h"
cristya15140f2012-03-04 01:21:15 +000048#include "MagickCore/image.h"
49#include "MagickCore/list.h"
50#include "MagickCore/log.h"
cristyab272ac2012-03-04 22:08:07 +000051#include "MagickCore/monitor.h"
52#include "MagickCore/monitor-private.h"
cristya15140f2012-03-04 01:21:15 +000053#include "MagickCore/option.h"
cristyab272ac2012-03-04 22:08:07 +000054#include "MagickCore/pixel-accessor.h"
cristy8c8c9162013-05-27 10:14:21 +000055#include "MagickCore/pixel-private.h"
cristyac245f82012-05-05 17:13:57 +000056#include "MagickCore/resource_.h"
cristy8748f562012-03-08 13:58:34 +000057#include "MagickCore/string-private.h"
cristy16881e62012-05-06 14:41:29 +000058#include "MagickCore/thread-private.h"
cristya15140f2012-03-04 01:21:15 +000059#include "MagickCore/token.h"
cristyd04e7bf2012-03-03 19:19:12 +000060#include "MagickCore/utility.h"
61#include "MagickCore/version.h"
62
63/*
64%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
65% %
66% %
67% %
cristy5f257b22012-03-07 00:27:29 +000068% C h a n n e l F x I m a g e %
cristyd04e7bf2012-03-03 19:19:12 +000069% %
70% %
71% %
72%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
73%
cristy5f257b22012-03-07 00:27:29 +000074% ChannelFxImage() applies a channel expression to the specified image. The
75% expression consists of one or more channels, either mnemonic or numeric (e.g.
76% red, 1), separated by actions as follows:
cristyab272ac2012-03-04 22:08:07 +000077%
78% <=> exchange two channels (e.g. red<=>blue)
cristy7ea920d2012-03-16 01:08:01 +000079% => copy one channel to another channel (e.g. red=>green)
80% = assign a constant value to a channel (e.g. red=50%)
81% , write new image channels in the specified order (e.g. red, green)
cristy3c213612012-03-16 01:14:48 +000082% | add a new output image for the next set of channel operations
83% ; move to the next input image for the source of channel data
cristyab272ac2012-03-04 22:08:07 +000084%
cristy7ea920d2012-03-16 01:08:01 +000085% For example, to create 3 grayscale images from the red, green, and blue
86% channels of an image, use:
cristyab272ac2012-03-04 22:08:07 +000087%
cristy3c213612012-03-16 01:14:48 +000088% -channel-fx "red; green; blue"
cristy7ea920d2012-03-16 01:08:01 +000089%
90% A channel without an operation symbol implies separate (i.e, semicolon).
cristyd04e7bf2012-03-03 19:19:12 +000091%
cristy5f257b22012-03-07 00:27:29 +000092% The format of the ChannelFxImage method is:
cristyd04e7bf2012-03-03 19:19:12 +000093%
cristy5f257b22012-03-07 00:27:29 +000094% Image *ChannelFxImage(const Image *image,const char *expression,
95% ExceptionInfo *exception)
cristyd04e7bf2012-03-03 19:19:12 +000096%
97% A description of each parameter follows:
98%
cristyab272ac2012-03-04 22:08:07 +000099% o image: the image.
cristyd04e7bf2012-03-03 19:19:12 +0000100%
101% o expression: A channel expression.
102%
103% o exception: return any errors or warnings in this structure.
104%
105*/
cristya15140f2012-03-04 01:21:15 +0000106
107typedef enum
108{
109 ExtractChannelOp,
cristy8748f562012-03-08 13:58:34 +0000110 AssignChannelOp,
cristya15140f2012-03-04 01:21:15 +0000111 ExchangeChannelOp,
112 TransferChannelOp
cristy5f257b22012-03-07 00:27:29 +0000113} ChannelFx;
cristya15140f2012-03-04 01:21:15 +0000114
cristyab272ac2012-03-04 22:08:07 +0000115static inline size_t MagickMin(const size_t x,const size_t y)
cristya15140f2012-03-04 01:21:15 +0000116{
cristyab272ac2012-03-04 22:08:07 +0000117 if (x < y)
118 return(x);
119 return(y);
cristya15140f2012-03-04 01:21:15 +0000120}
121
cristyab272ac2012-03-04 22:08:07 +0000122static MagickBooleanType ChannelImage(Image *destination_image,
cristy8748f562012-03-08 13:58:34 +0000123 const PixelChannel destination_channel,const ChannelFx channel_op,
cristy3bb378a2012-03-08 14:38:52 +0000124 const Image *source_image,const PixelChannel source_channel,
cristy8748f562012-03-08 13:58:34 +0000125 const Quantum pixel,ExceptionInfo *exception)
cristyab272ac2012-03-04 22:08:07 +0000126{
127 CacheView
128 *source_view,
129 *destination_view;
130
131 MagickBooleanType
132 status;
133
134 size_t
cristyac245f82012-05-05 17:13:57 +0000135 height,
136 width;
cristyab272ac2012-03-04 22:08:07 +0000137
138 ssize_t
139 y;
140
141 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +0000142 source_view=AcquireVirtualCacheView(source_image,exception);
143 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristyab272ac2012-03-04 22:08:07 +0000144 height=MagickMin(source_image->rows,destination_image->rows);
cristyac245f82012-05-05 17:13:57 +0000145 width=MagickMin(source_image->columns,destination_image->columns);
cristyab272ac2012-03-04 22:08:07 +0000146#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy9a5a52f2012-10-09 14:40:31 +0000147 #pragma omp parallel for schedule(static,4) shared(status) \
cristy5e6b2592012-12-19 14:08:11 +0000148 magick_threads(source_image,source_image,height,1)
cristyab272ac2012-03-04 22:08:07 +0000149#endif
150 for (y=0; y < (ssize_t) height; y++)
151 {
cristy4ee60402012-03-11 02:01:20 +0000152 PixelTrait
153 destination_traits,
154 source_traits;
155
cristyab272ac2012-03-04 22:08:07 +0000156 register const Quantum
157 *restrict p;
158
159 register Quantum
160 *restrict q;
161
162 register ssize_t
163 x;
164
cristyab272ac2012-03-04 22:08:07 +0000165 if (status == MagickFalse)
166 continue;
167 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
168 exception);
cristy4ee60402012-03-11 02:01:20 +0000169 q=GetCacheViewAuthenticPixels(destination_view,0,y,
cristyab272ac2012-03-04 22:08:07 +0000170 destination_image->columns,1,exception);
171 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
172 {
173 status=MagickFalse;
174 continue;
175 }
cristycf1296e2012-08-26 23:40:49 +0000176 destination_traits=GetPixelChannelTraits(destination_image,
cristy4ee60402012-03-11 02:01:20 +0000177 destination_channel);
cristycf1296e2012-08-26 23:40:49 +0000178 source_traits=GetPixelChannelTraits(source_image,source_channel);
cristy4ee60402012-03-11 02:01:20 +0000179 if ((destination_traits == UndefinedPixelTrait) ||
180 (source_traits == UndefinedPixelTrait))
181 continue;
cristyab272ac2012-03-04 22:08:07 +0000182 for (x=0; x < (ssize_t) width; x++)
183 {
cristy8748f562012-03-08 13:58:34 +0000184 if (channel_op == AssignChannelOp)
185 SetPixelChannel(destination_image,destination_channel,pixel,q);
186 else
cristy4ee60402012-03-11 02:01:20 +0000187 SetPixelChannel(destination_image,destination_channel,
188 GetPixelChannel(source_image,source_channel,p),q);
189 p+=GetPixelChannels(source_image);
cristyab272ac2012-03-04 22:08:07 +0000190 q+=GetPixelChannels(destination_image);
191 }
192 if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse)
193 status=MagickFalse;
194 }
195 destination_view=DestroyCacheView(destination_view);
196 source_view=DestroyCacheView(source_view);
197 return(status);
198}
199
cristy6398ec72013-11-28 02:00:27 +0000200#if defined(__cplusplus) || defined(c_plusplus)
201extern "C" {
202#endif
203
cristy5f257b22012-03-07 00:27:29 +0000204MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
205 ExceptionInfo *exception)
cristyd04e7bf2012-03-03 19:19:12 +0000206{
cristy5f257b22012-03-07 00:27:29 +0000207#define ChannelFxImageTag "ChannelFx/Image"
cristyab272ac2012-03-04 22:08:07 +0000208
cristy5f257b22012-03-07 00:27:29 +0000209 ChannelFx
cristya15140f2012-03-04 01:21:15 +0000210 channel_op;
211
cristyfe88ede2012-03-22 00:34:48 +0000212 ChannelType
213 channel_mask;
214
cristy4ee60402012-03-11 02:01:20 +0000215 char
216 token[MaxTextExtent];
217
cristya15140f2012-03-04 01:21:15 +0000218 const char
219 *p;
220
cristyab272ac2012-03-04 22:08:07 +0000221 const Image
222 *source_image;
223
cristy8748f562012-03-08 13:58:34 +0000224 double
225 pixel;
226
cristya15140f2012-03-04 01:21:15 +0000227 Image
cristyab272ac2012-03-04 22:08:07 +0000228 *destination_image;
cristya15140f2012-03-04 01:21:15 +0000229
cristy4ee60402012-03-11 02:01:20 +0000230 MagickBooleanType
231 status;
232
cristya15140f2012-03-04 01:21:15 +0000233 PixelChannel
cristyab272ac2012-03-04 22:08:07 +0000234 source_channel,
235 destination_channel;
cristya15140f2012-03-04 01:21:15 +0000236
cristyab272ac2012-03-04 22:08:07 +0000237 ssize_t
238 channels;
239
240 assert(image != (Image *) NULL);
241 assert(image->signature == MagickSignature);
242 if (image->debug != MagickFalse)
243 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
244 assert(exception != (ExceptionInfo *) NULL);
245 assert(exception->signature == MagickSignature);
246 source_image=image;
247 destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
248 if (destination_image == (Image *) NULL)
249 return((Image *) NULL);
cristyab272ac2012-03-04 22:08:07 +0000250 if (expression == (const char *) NULL)
251 return(destination_image);
252 destination_channel=RedPixelChannel;
cristyfe88ede2012-03-22 00:34:48 +0000253 channel_mask=UndefinedChannel;
cristy8748f562012-03-08 13:58:34 +0000254 pixel=0.0;
cristya15140f2012-03-04 01:21:15 +0000255 p=(char *) expression;
256 GetMagickToken(p,&p,token);
cristy4ee60402012-03-11 02:01:20 +0000257 channel_op=ExtractChannelOp;
cristy09ef5be2012-03-13 13:01:27 +0000258 for (channels=0; *token != '\0'; )
cristya15140f2012-03-04 01:21:15 +0000259 {
cristya15140f2012-03-04 01:21:15 +0000260 ssize_t
261 i;
262
263 /*
264 Interpret channel expression.
265 */
cristy68bc2572013-05-16 01:09:13 +0000266 switch (*token)
267 {
cristy68bc2572013-05-16 01:09:13 +0000268 case ',':
cristya15140f2012-03-04 01:21:15 +0000269 {
cristya15140f2012-03-04 01:21:15 +0000270 GetMagickToken(p,&p,token);
cristy68bc2572013-05-16 01:09:13 +0000271 break;
cristya15140f2012-03-04 01:21:15 +0000272 }
cristy68bc2572013-05-16 01:09:13 +0000273 case '|':
cristya15140f2012-03-04 01:21:15 +0000274 {
cristyab272ac2012-03-04 22:08:07 +0000275 if (GetNextImageInList(source_image) != (Image *) NULL)
276 source_image=GetNextImageInList(source_image);
cristya15140f2012-03-04 01:21:15 +0000277 else
cristyab272ac2012-03-04 22:08:07 +0000278 source_image=GetFirstImageInList(source_image);
cristya15140f2012-03-04 01:21:15 +0000279 GetMagickToken(p,&p,token);
cristy68bc2572013-05-16 01:09:13 +0000280 break;
cristya15140f2012-03-04 01:21:15 +0000281 }
cristy68bc2572013-05-16 01:09:13 +0000282 case ';':
cristya15140f2012-03-04 01:21:15 +0000283 {
cristyab272ac2012-03-04 22:08:07 +0000284 Image
285 *canvas;
286
cristycf1296e2012-08-26 23:40:49 +0000287 SetPixelChannelMask(destination_image,channel_mask);
cristy68bc2572013-05-16 01:09:13 +0000288 if ((channel_op == ExtractChannelOp) && (channels == 1))
cristyfe88ede2012-03-22 00:34:48 +0000289 (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
cristy4ee60402012-03-11 02:01:20 +0000290 status=SetImageStorageClass(destination_image,DirectClass,exception);
291 if (status == MagickFalse)
292 {
cristyfc68ef52012-03-11 23:33:15 +0000293 destination_image=DestroyImageList(destination_image);
294 return(destination_image);
cristy4ee60402012-03-11 02:01:20 +0000295 }
cristyab272ac2012-03-04 22:08:07 +0000296 canvas=CloneImage(source_image,0,0,MagickTrue,exception);
297 if (canvas == (Image *) NULL)
298 {
cristyfc68ef52012-03-11 23:33:15 +0000299 destination_image=DestroyImageList(destination_image);
300 return(destination_image);
cristyab272ac2012-03-04 22:08:07 +0000301 }
302 AppendImageToList(&destination_image,canvas);
303 destination_image=GetLastImageInList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000304 GetMagickToken(p,&p,token);
cristyab272ac2012-03-04 22:08:07 +0000305 channels=0;
306 destination_channel=RedPixelChannel;
cristyfe88ede2012-03-22 00:34:48 +0000307 channel_mask=UndefinedChannel;
cristy68bc2572013-05-16 01:09:13 +0000308 break;
cristya15140f2012-03-04 01:21:15 +0000309 }
cristy68bc2572013-05-16 01:09:13 +0000310 default:
311 break;
312 }
cristya15140f2012-03-04 01:21:15 +0000313 i=ParsePixelChannelOption(token);
314 if (i < 0)
315 {
316 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyefe601c2013-01-05 17:51:12 +0000317 "UnrecognizedChannelType","`%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000318 destination_image=DestroyImageList(destination_image);
cristyfc68ef52012-03-11 23:33:15 +0000319 return(destination_image);
cristya15140f2012-03-04 01:21:15 +0000320 }
cristyab272ac2012-03-04 22:08:07 +0000321 source_channel=(PixelChannel) i;
cristya15140f2012-03-04 01:21:15 +0000322 channel_op=ExtractChannelOp;
323 GetMagickToken(p,&p,token);
324 if (*token == '<')
325 {
326 channel_op=ExchangeChannelOp;
327 GetMagickToken(p,&p,token);
328 }
329 if (*token == '=')
cristy8748f562012-03-08 13:58:34 +0000330 {
331 if (channel_op != ExchangeChannelOp)
332 channel_op=AssignChannelOp;
333 GetMagickToken(p,&p,token);
334 }
cristya15140f2012-03-04 01:21:15 +0000335 if (*token == '>')
336 {
337 if (channel_op != ExchangeChannelOp)
338 channel_op=TransferChannelOp;
339 GetMagickToken(p,&p,token);
340 }
cristy8748f562012-03-08 13:58:34 +0000341 switch (channel_op)
342 {
343 case AssignChannelOp:
344 {
cristy41db1742012-03-09 00:57:52 +0000345 pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
cristy3bb378a2012-03-08 14:38:52 +0000346 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000347 break;
348 }
349 case ExchangeChannelOp:
350 case TransferChannelOp:
cristya15140f2012-03-04 01:21:15 +0000351 {
352 i=ParsePixelChannelOption(token);
353 if (i < 0)
354 {
355 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyefe601c2013-01-05 17:51:12 +0000356 "UnrecognizedChannelType","`%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000357 destination_image=DestroyImageList(destination_image);
cristyfc68ef52012-03-11 23:33:15 +0000358 return(destination_image);
cristya15140f2012-03-04 01:21:15 +0000359 }
cristyab272ac2012-03-04 22:08:07 +0000360 destination_channel=(PixelChannel) i;
cristy04817142013-05-20 17:14:03 +0000361 switch (destination_channel)
362 {
cristyed27bb72013-05-20 17:24:07 +0000363 case RedPixelChannel:
364 case GreenPixelChannel:
365 case BluePixelChannel:
366 case BlackPixelChannel:
367 case IndexPixelChannel:
368 break;
cristy04817142013-05-20 17:14:03 +0000369 case AlphaPixelChannel:
370 {
371 destination_image->alpha_trait=BlendPixelTrait;
372 break;
373 }
374 case ReadMaskPixelChannel:
375 {
376 destination_image->read_mask=MagickTrue;
377 break;
378 }
379 case WriteMaskPixelChannel:
380 {
381 destination_image->write_mask=MagickTrue;
382 break;
383 }
cristyed27bb72013-05-20 17:24:07 +0000384 case MetaPixelChannel:
cristy04817142013-05-20 17:14:03 +0000385 default:
cristyed27bb72013-05-20 17:24:07 +0000386 {
387 (void) SetPixelMetaChannels(destination_image,(size_t) (i-
388 GetPixelChannels(destination_image)+1),exception);
cristy04817142013-05-20 17:14:03 +0000389 break;
cristyed27bb72013-05-20 17:24:07 +0000390 }
cristy04817142013-05-20 17:14:03 +0000391 }
cristyaa2c16c2012-03-25 22:21:35 +0000392 channel_mask=(ChannelType) (channel_mask | ParseChannelOption(token));
cristy68bc2572013-05-16 01:09:13 +0000393 if (((channels >= 1) || (destination_channel >= 1)) &&
394 (IsGrayColorspace(destination_image->colorspace) != MagickFalse))
395 (void) SetImageColorspace(destination_image,sRGBColorspace,exception);
cristy3bb378a2012-03-08 14:38:52 +0000396 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000397 break;
cristya15140f2012-03-04 01:21:15 +0000398 }
cristyfe88ede2012-03-22 00:34:48 +0000399 default:
cristy3c213612012-03-16 01:14:48 +0000400 break;
cristy7ea920d2012-03-16 01:08:01 +0000401 }
cristy3bb378a2012-03-08 14:38:52 +0000402 status=ChannelImage(destination_image,destination_channel,channel_op,
403 source_image,source_channel,ClampToQuantum(pixel),exception);
cristya15140f2012-03-04 01:21:15 +0000404 if (status == MagickFalse)
405 {
cristyab272ac2012-03-04 22:08:07 +0000406 destination_image=DestroyImageList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000407 break;
408 }
cristy4ee60402012-03-11 02:01:20 +0000409 channels++;
cristy8748f562012-03-08 13:58:34 +0000410 if (channel_op == ExchangeChannelOp)
411 {
cristy4ee60402012-03-11 02:01:20 +0000412 status=ChannelImage(destination_image,source_channel,channel_op,
413 source_image,destination_channel,ClampToQuantum(pixel),exception);
cristy8748f562012-03-08 13:58:34 +0000414 if (status == MagickFalse)
415 {
416 destination_image=DestroyImageList(destination_image);
417 break;
418 }
cristy4ee60402012-03-11 02:01:20 +0000419 channels++;
cristy8748f562012-03-08 13:58:34 +0000420 }
cristyfe88ede2012-03-22 00:34:48 +0000421 switch (channel_op)
422 {
423 case ExtractChannelOp:
424 {
cristy9dbae2c2013-05-27 20:12:53 +0000425 channel_mask=(ChannelType) (channel_mask | (1 << destination_channel));
cristyaa2c16c2012-03-25 22:21:35 +0000426 destination_channel=(PixelChannel) (destination_channel+1);
cristyfe88ede2012-03-22 00:34:48 +0000427 break;
428 }
429 default:
430 break;
431 }
cristy5f257b22012-03-07 00:27:29 +0000432 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
cristyab272ac2012-03-04 22:08:07 +0000433 strlen(expression));
434 if (status == MagickFalse)
435 break;
cristya15140f2012-03-04 01:21:15 +0000436 }
cristycf1296e2012-08-26 23:40:49 +0000437 SetPixelChannelMask(destination_image,channel_mask);
cristy68bc2572013-05-16 01:09:13 +0000438 if ((channel_op == ExtractChannelOp) && (channels == 1))
cristyfe88ede2012-03-22 00:34:48 +0000439 (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
cristy4ee60402012-03-11 02:01:20 +0000440 status=SetImageStorageClass(destination_image,DirectClass,exception);
441 if (status == MagickFalse)
442 {
443 destination_image=GetLastImageInList(destination_image);
444 return((Image *) NULL);
445 }
cristy09ef5be2012-03-13 13:01:27 +0000446 return(GetFirstImageInList(destination_image));
cristyd04e7bf2012-03-03 19:19:12 +0000447}
cristy6398ec72013-11-28 02:00:27 +0000448
449#if defined(__cplusplus) || defined(c_plusplus)
450}
451#endif
cristy0c643722012-03-05 19:18:42 +0000452
453/*
454%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
455% %
456% %
457% %
458% C o m b i n e I m a g e s %
459% %
460% %
461% %
462%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
463%
464% CombineImages() combines one or more images into a single image. The
465% grayscale value of the pixels of each image in the sequence is assigned in
466% order to the specified channels of the combined image. The typical
467% ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
468%
469% The format of the CombineImages method is:
470%
cristy46f354c2012-07-04 13:31:29 +0000471% Image *CombineImages(const Image *images,const ColorspaceType colorspace,
472% ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000473%
474% A description of each parameter follows:
475%
cristy46f354c2012-07-04 13:31:29 +0000476% o images: the image sequence.
477%
478% o colorspace: the image colorspace.
cristy0c643722012-03-05 19:18:42 +0000479%
480% o exception: return any errors or warnings in this structure.
481%
482*/
cristy6398ec72013-11-28 02:00:27 +0000483#if defined(__cplusplus) || defined(c_plusplus)
484extern "C" {
485#endif
486
cristy46f354c2012-07-04 13:31:29 +0000487MagickExport Image *CombineImages(const Image *image,
488 const ColorspaceType colorspace,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000489{
490#define CombineImageTag "Combine/Image"
491
492 CacheView
493 *combine_view;
494
495 Image
496 *combine_image;
497
498 MagickBooleanType
499 status;
500
501 MagickOffsetType
502 progress;
503
504 ssize_t
505 y;
506
507 /*
508 Ensure the image are the same size.
509 */
510 assert(image != (const Image *) NULL);
511 assert(image->signature == MagickSignature);
512 if (image->debug != MagickFalse)
513 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
514 assert(exception != (ExceptionInfo *) NULL);
515 assert(exception->signature == MagickSignature);
516 combine_image=CloneImage(image,0,0,MagickTrue,exception);
517 if (combine_image == (Image *) NULL)
518 return((Image *) NULL);
519 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
520 {
521 combine_image=DestroyImage(combine_image);
522 return((Image *) NULL);
523 }
cristy74f3f942013-05-01 11:11:28 +0000524 (void) SetImageColorspace(combine_image,colorspace,exception);
cristy0c643722012-03-05 19:18:42 +0000525 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy8a46d822012-08-28 23:32:39 +0000526 combine_image->alpha_trait=BlendPixelTrait;
cristy0c643722012-03-05 19:18:42 +0000527 /*
528 Combine images.
529 */
530 status=MagickTrue;
531 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000532 combine_view=AcquireAuthenticCacheView(combine_image,exception);
cristy0c643722012-03-05 19:18:42 +0000533 for (y=0; y < (ssize_t) combine_image->rows; y++)
534 {
535 CacheView
536 *image_view;
537
538 const Image
539 *next;
540
541 Quantum
542 *pixels;
543
544 register const Quantum
545 *restrict p;
546
547 register Quantum
548 *restrict q;
549
550 register ssize_t
551 i;
552
553 if (status == MagickFalse)
554 continue;
555 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
556 1,exception);
557 if (pixels == (Quantum *) NULL)
558 {
559 status=MagickFalse;
560 continue;
561 }
562 next=image;
cristyc1119af2012-04-16 15:28:23 +0000563 for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
cristy0c643722012-03-05 19:18:42 +0000564 {
cristy0c643722012-03-05 19:18:42 +0000565 register ssize_t
566 x;
567
cristy5a23c552013-02-13 14:34:28 +0000568 PixelChannel channel=GetPixelChannelChannel(combine_image,i);
569 PixelTrait traits=GetPixelChannelTraits(combine_image,channel);
cristyc1119af2012-04-16 15:28:23 +0000570 if (traits == UndefinedPixelTrait)
cristy0c643722012-03-05 19:18:42 +0000571 continue;
cristy5a23c552013-02-13 14:34:28 +0000572 if (next == (Image *) NULL)
573 continue;
cristy46ff2672012-12-14 15:32:26 +0000574 image_view=AcquireVirtualCacheView(next,exception);
cristy0c643722012-03-05 19:18:42 +0000575 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
576 if (p == (const Quantum *) NULL)
577 continue;
578 q=pixels;
579 for (x=0; x < (ssize_t) combine_image->columns; x++)
580 {
cristyf3381ae2012-07-13 16:30:09 +0000581 if (x < (ssize_t) next->columns)
cristy0c643722012-03-05 19:18:42 +0000582 {
cristyf3381ae2012-07-13 16:30:09 +0000583 q[i]=GetPixelGray(next,p);
584 p+=GetPixelChannels(next);
cristy0c643722012-03-05 19:18:42 +0000585 }
586 q+=GetPixelChannels(combine_image);
587 }
588 image_view=DestroyCacheView(image_view);
589 next=GetNextImageInList(next);
cristy0c643722012-03-05 19:18:42 +0000590 }
cristyf3381ae2012-07-13 16:30:09 +0000591 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
592 status=MagickFalse;
593 if (image->progress_monitor != (MagickProgressMonitor) NULL)
594 {
595 MagickBooleanType
596 proceed;
597
598 proceed=SetImageProgress(image,CombineImageTag,progress++,
599 combine_image->rows);
600 if (proceed == MagickFalse)
601 status=MagickFalse;
602 }
cristy0c643722012-03-05 19:18:42 +0000603 }
604 combine_view=DestroyCacheView(combine_view);
605 if (status == MagickFalse)
606 combine_image=DestroyImage(combine_image);
607 return(combine_image);
608}
cristy6398ec72013-11-28 02:00:27 +0000609
610#if defined(__cplusplus) || defined(c_plusplus)
611}
612#endif
cristy0c643722012-03-05 19:18:42 +0000613
614/*
615%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
616% %
617% %
618% %
cristy8c8c9162013-05-27 10:14:21 +0000619% G e t I m a g e A l p h a C h a n n e l %
620% %
621% %
622% %
623%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
624%
625% GetImageAlphaChannel() returns MagickFalse if the image alpha channel is
626% not activated. That is, the image is RGB rather than RGBA or CMYK rather
627% than CMYKA.
628%
629% The format of the GetImageAlphaChannel method is:
630%
631% MagickBooleanType GetImageAlphaChannel(const Image *image)
632%
633% A description of each parameter follows:
634%
635% o image: the image.
636%
637*/
638MagickExport MagickBooleanType GetImageAlphaChannel(const Image *image)
639{
640 assert(image != (const Image *) NULL);
641 if (image->debug != MagickFalse)
642 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
643 assert(image->signature == MagickSignature);
644 return(image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse);
645}
646
647/*
648%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
649% %
650% %
651% %
cristy0c643722012-03-05 19:18:42 +0000652% S e p a r a t e I m a g e %
653% %
654% %
655% %
656%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
657%
658% SeparateImage() separates a channel from the image and returns it as a
659% grayscale image.
660%
661% The format of the SeparateImage method is:
662%
663% Image *SeparateImage(const Image *image,const ChannelType channel,
664% ExceptionInfo *exception)
665%
666% A description of each parameter follows:
667%
668% o image: the image.
669%
670% o channel: the image channel.
671%
672% o exception: return any errors or warnings in this structure.
673%
674*/
cristy6398ec72013-11-28 02:00:27 +0000675#if defined(__cplusplus) || defined(c_plusplus)
676extern "C" {
677#endif
678
cristy0c643722012-03-05 19:18:42 +0000679MagickExport Image *SeparateImage(const Image *image,
680 const ChannelType channel_type,ExceptionInfo *exception)
681{
682#define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
683#define SeparateImageTag "Separate/Image"
684
685 CacheView
686 *image_view,
687 *separate_view;
688
689 Image
690 *separate_image;
691
692 MagickBooleanType
693 status;
694
695 MagickOffsetType
696 progress;
697
698 ssize_t
699 y;
700
701 /*
cristy8fdeeb32013-03-31 21:15:31 +0000702 Initialize separate image attributes.
cristy0c643722012-03-05 19:18:42 +0000703 */
704 assert(image != (Image *) NULL);
705 assert(image->signature == MagickSignature);
706 if (image->debug != MagickFalse)
707 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
708 assert(exception != (ExceptionInfo *) NULL);
709 assert(exception->signature == MagickSignature);
710 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
711 exception);
712 if (separate_image == (Image *) NULL)
713 return((Image *) NULL);
714 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
715 {
716 separate_image=DestroyImage(separate_image);
717 return((Image *) NULL);
718 }
cristyaa7a2252012-08-15 12:19:00 +0000719 (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
cristy9b8205c2013-04-16 11:16:19 +0000720 separate_image->alpha_trait=UndefinedPixelTrait;
cristy0c643722012-03-05 19:18:42 +0000721 /*
722 Separate image.
723 */
724 status=MagickTrue;
725 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000726 image_view=AcquireVirtualCacheView(image,exception);
727 separate_view=AcquireAuthenticCacheView(separate_image,exception);
cristy0c643722012-03-05 19:18:42 +0000728#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy9a5a52f2012-10-09 14:40:31 +0000729 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000730 magick_threads(image,image,image->rows,1)
cristy0c643722012-03-05 19:18:42 +0000731#endif
732 for (y=0; y < (ssize_t) image->rows; y++)
733 {
734 register const Quantum
735 *restrict p;
736
737 register Quantum
738 *restrict q;
739
740 register ssize_t
741 x;
742
743 if (status == MagickFalse)
744 continue;
745 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
746 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
747 exception);
748 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
749 {
750 status=MagickFalse;
751 continue;
752 }
753 for (x=0; x < (ssize_t) image->columns; x++)
754 {
755 register ssize_t
756 i;
757
cristy883fde12013-04-08 00:50:13 +0000758 if (GetPixelReadMask(image,p) == 0)
cristy0c643722012-03-05 19:18:42 +0000759 {
cristyc3a58022013-10-09 23:22:42 +0000760 SetPixelBackgoundColor(separate_image,q);
cristy0c643722012-03-05 19:18:42 +0000761 p+=GetPixelChannels(image);
762 q+=GetPixelChannels(separate_image);
763 continue;
764 }
765 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
766 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
767 {
cristy5a23c552013-02-13 14:34:28 +0000768 PixelChannel channel=GetPixelChannelChannel(image,i);
769 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristy0c643722012-03-05 19:18:42 +0000770 if ((traits == UndefinedPixelTrait) ||
771 (GetChannelBit(channel_type,channel) == 0))
772 continue;
cristy9b8205c2013-04-16 11:16:19 +0000773 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
cristy0c643722012-03-05 19:18:42 +0000774 }
775 p+=GetPixelChannels(image);
776 q+=GetPixelChannels(separate_image);
777 }
778 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
779 status=MagickFalse;
780 if (image->progress_monitor != (MagickProgressMonitor) NULL)
781 {
782 MagickBooleanType
783 proceed;
784
785#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000786 #pragma omp critical (MagickCore_SeparateImage)
cristy0c643722012-03-05 19:18:42 +0000787#endif
788 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
789 if (proceed == MagickFalse)
790 status=MagickFalse;
791 }
792 }
793 separate_view=DestroyCacheView(separate_view);
794 image_view=DestroyCacheView(image_view);
cristy1c2f48d2012-12-14 01:20:55 +0000795 if (status == MagickFalse)
796 separate_image=DestroyImage(separate_image);
cristy0c643722012-03-05 19:18:42 +0000797 return(separate_image);
798}
cristy6398ec72013-11-28 02:00:27 +0000799
800#if defined(__cplusplus) || defined(c_plusplus)
801}
802#endif
cristy0c643722012-03-05 19:18:42 +0000803
804/*
805%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
806% %
807% %
808% %
809% S e p a r a t e I m a g e s %
810% %
811% %
812% %
813%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
814%
815% SeparateImages() returns a separate grayscale image for each channel
816% specified.
817%
818% The format of the SeparateImages method is:
819%
cristydfdb19e2012-03-21 22:22:24 +0000820% Image *SeparateImages(const Image *image,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000821%
822% A description of each parameter follows:
823%
824% o image: the image.
825%
826% o exception: return any errors or warnings in this structure.
827%
828*/
cristy6398ec72013-11-28 02:00:27 +0000829#if defined(__cplusplus) || defined(c_plusplus)
830extern "C" {
831#endif
832
cristydfdb19e2012-03-21 22:22:24 +0000833MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000834{
835 Image
836 *images,
837 *separate_image;
838
839 register ssize_t
840 i;
841
842 assert(image != (Image *) NULL);
843 assert(image->signature == MagickSignature);
844 if (image->debug != MagickFalse)
845 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
846 images=NewImageList();
847 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
848 {
cristy5a23c552013-02-13 14:34:28 +0000849 PixelChannel channel=GetPixelChannelChannel(image,i);
850 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristy0c643722012-03-05 19:18:42 +0000851 if ((traits == UndefinedPixelTrait) ||
852 ((traits & UpdatePixelTrait) == 0))
853 continue;
854 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
855 if (separate_image != (Image *) NULL)
856 AppendImageToList(&images,separate_image);
857 }
cristye5ef2ce2012-04-29 21:14:32 +0000858 if (images == (Image *) NULL)
859 images=SeparateImage(image,UndefinedChannel,exception);
cristy0c643722012-03-05 19:18:42 +0000860 return(images);
861}
cristy6398ec72013-11-28 02:00:27 +0000862
863#if defined(__cplusplus) || defined(c_plusplus)
864}
865#endif
cristy8c8c9162013-05-27 10:14:21 +0000866
867/*
868%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
869% %
870% %
871% %
872% S e t I m a g e A l p h a C h a n n e l %
873% %
874% %
875% %
876%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
877%
878% SetImageAlphaChannel() activates, deactivates, resets, or sets the alpha
879% channel.
880%
881% The format of the SetImageAlphaChannel method is:
882%
883% MagickBooleanType SetImageAlphaChannel(Image *image,
884% const AlphaChannelOption alpha_type,ExceptionInfo *exception)
885%
886% A description of each parameter follows:
887%
888% o image: the image.
889%
890% o alpha_type: The alpha channel type: ActivateAlphaChannel,
891% CopyAlphaChannel, DeactivateAlphaChannel, ExtractAlphaChannel,
892% OpaqueAlphaChannel, SetAlphaChannel, ShapeAlphaChannel, and
893% TransparentAlphaChannel.
894%
895% o exception: return any errors or warnings in this structure.
896%
897*/
898
899static inline void FlattenPixelInfo(const Image *image,const PixelInfo *p,
900 const double alpha,const Quantum *q,const double beta,
901 Quantum *composite)
902{
903 double
904 Da,
905 gamma,
906 Sa;
907
908 register ssize_t
909 i;
910
911 /*
912 Compose pixel p over pixel q with the given alpha.
913 */
914 Sa=QuantumScale*alpha;
915 Da=QuantumScale*beta,
916 gamma=Sa*(-Da)+Sa+Da;
917 gamma=PerceptibleReciprocal(gamma);
918 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
919 {
920 PixelChannel channel=GetPixelChannelChannel(image,i);
921 PixelTrait traits=GetPixelChannelTraits(image,channel);
922 if (traits == UndefinedPixelTrait)
923 continue;
924 switch (channel)
925 {
926 case RedPixelChannel:
927 {
928 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
929 (double) p->red,alpha));
930 break;
931 }
932 case GreenPixelChannel:
933 {
934 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
935 (double) p->green,alpha));
936 break;
937 }
938 case BluePixelChannel:
939 {
940 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
941 (double) p->blue,alpha));
942 break;
943 }
944 case BlackPixelChannel:
945 {
946 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
947 (double) p->black,alpha));
948 break;
949 }
950 case AlphaPixelChannel:
951 {
952 composite[i]=ClampToQuantum(QuantumRange*(Sa*(-Da)+Sa+Da));
953 break;
954 }
955 default:
956 break;
957 }
958 }
959}
960
cristy6398ec72013-11-28 02:00:27 +0000961#if defined(__cplusplus) || defined(c_plusplus)
962extern "C" {
963#endif
964
cristy8c8c9162013-05-27 10:14:21 +0000965MagickExport MagickBooleanType SetImageAlphaChannel(Image *image,
966 const AlphaChannelOption alpha_type,ExceptionInfo *exception)
967{
968 MagickBooleanType
969 status;
970
971 assert(image != (Image *) NULL);
972 if (image->debug != MagickFalse)
973 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
974 assert(image->signature == MagickSignature);
975 status=MagickTrue;
976 switch (alpha_type)
977 {
978 case ActivateAlphaChannel:
979 {
980 image->alpha_trait=BlendPixelTrait;
981 break;
982 }
983 case BackgroundAlphaChannel:
984 {
985 CacheView
986 *image_view;
987
988 ssize_t
989 y;
990
991 /*
992 Set transparent pixels to background color.
993 */
994 if (image->alpha_trait != BlendPixelTrait)
995 break;
996 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
997 break;
998 image_view=AcquireAuthenticCacheView(image,exception);
999#if defined(MAGICKCORE_OPENMP_SUPPORT)
1000 #pragma omp parallel for schedule(static,4) shared(status) \
1001 magick_threads(image,image,image->rows,1)
1002#endif
1003 for (y=0; y < (ssize_t) image->rows; y++)
1004 {
1005 register Quantum
1006 *restrict q;
1007
1008 register ssize_t
1009 x;
1010
1011 if (status == MagickFalse)
1012 continue;
1013 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1014 exception);
1015 if (q == (Quantum *) NULL)
1016 {
1017 status=MagickFalse;
1018 continue;
1019 }
1020 for (x=0; x < (ssize_t) image->columns; x++)
1021 {
1022 if (GetPixelAlpha(image,q) == TransparentAlpha)
1023 {
1024 SetPixelInfoPixel(image,&image->background_color,q);
1025 SetPixelChannel(image,AlphaPixelChannel,TransparentAlpha,q);
1026 }
1027 q+=GetPixelChannels(image);
1028 }
1029 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1030 status=MagickFalse;
1031 }
1032 image_view=DestroyCacheView(image_view);
1033 return(status);
1034 }
1035 case CopyAlphaChannel:
1036 case ShapeAlphaChannel:
1037 {
1038 /*
1039 Copy pixel intensity to the alpha channel.
1040 */
1041 status=CompositeImage(image,image,IntensityCompositeOp,MagickTrue,0,0,
1042 exception);
1043 if (alpha_type == ShapeAlphaChannel)
1044 (void) LevelImageColors(image,&image->background_color,
1045 &image->background_color,MagickTrue,exception);
1046 break;
1047 }
1048 case DeactivateAlphaChannel:
1049 {
1050 image->alpha_trait=CopyPixelTrait;
1051 break;
1052 }
1053 case ExtractAlphaChannel:
1054 {
1055 status=CompositeImage(image,image,AlphaCompositeOp,MagickTrue,0,0,
1056 exception);
1057 image->alpha_trait=CopyPixelTrait;
1058 break;
1059 }
1060 case OpaqueAlphaChannel:
1061 {
1062 status=SetImageAlpha(image,OpaqueAlpha,exception);
1063 break;
1064 }
1065 case RemoveAlphaChannel:
1066 {
1067 CacheView
1068 *image_view;
1069
1070 ssize_t
1071 y;
1072
1073 /*
1074 Remove transparency.
1075 */
1076 if (image->alpha_trait != BlendPixelTrait)
1077 break;
1078 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1079 break;
1080 image_view=AcquireAuthenticCacheView(image,exception);
1081#if defined(MAGICKCORE_OPENMP_SUPPORT)
1082 #pragma omp parallel for schedule(static,4) shared(status) \
1083 magick_threads(image,image,image->rows,1)
1084#endif
1085 for (y=0; y < (ssize_t) image->rows; y++)
1086 {
1087 register Quantum
1088 *restrict q;
1089
1090 register ssize_t
1091 x;
1092
1093 if (status == MagickFalse)
1094 continue;
1095 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1096 exception);
1097 if (q == (Quantum *) NULL)
1098 {
1099 status=MagickFalse;
1100 continue;
1101 }
1102 for (x=0; x < (ssize_t) image->columns; x++)
1103 {
1104 FlattenPixelInfo(image,&image->background_color,
1105 image->background_color.alpha,q,(double)
1106 GetPixelAlpha(image,q),q);
1107 q+=GetPixelChannels(image);
1108 }
1109 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1110 status=MagickFalse;
1111 }
1112 image_view=DestroyCacheView(image_view);
1113 image->alpha_trait=image->background_color.alpha_trait;
1114 return(status);
1115 }
1116 case SetAlphaChannel:
1117 {
1118 if (image->alpha_trait != BlendPixelTrait)
1119 status=SetImageAlpha(image,OpaqueAlpha,exception);
1120 break;
1121 }
1122 case TransparentAlphaChannel:
1123 {
1124 status=SetImageAlpha(image,TransparentAlpha,exception);
1125 break;
1126 }
1127 case UndefinedAlphaChannel:
1128 break;
1129 }
1130 if (status == MagickFalse)
1131 return(status);
1132 return(SyncImagePixelCache(image,exception));
1133}
cristy6398ec72013-11-28 02:00:27 +00001134
1135#if defined(__cplusplus) || defined(c_plusplus)
1136}
1137#endif