blob: 57cde8f3d0b3915aa75bce27d60ee9135156dfe9 [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 %
cristyde984cd2013-12-01 14:49:27 +000016% Cristy %
cristyd04e7bf2012-03-03 19:19:12 +000017% 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"
cristya8816142013-11-28 10:21:59 +000045#include "MagickCore/channel.h"
cristyc1119af2012-04-16 15:28:23 +000046#include "MagickCore/colorspace-private.h"
cristy8c8c9162013-05-27 10:14:21 +000047#include "MagickCore/composite-private.h"
48#include "MagickCore/enhance.h"
cristya15140f2012-03-04 01:21:15 +000049#include "MagickCore/image.h"
50#include "MagickCore/list.h"
51#include "MagickCore/log.h"
cristyab272ac2012-03-04 22:08:07 +000052#include "MagickCore/monitor.h"
53#include "MagickCore/monitor-private.h"
cristya15140f2012-03-04 01:21:15 +000054#include "MagickCore/option.h"
cristyab272ac2012-03-04 22:08:07 +000055#include "MagickCore/pixel-accessor.h"
cristy8c8c9162013-05-27 10:14:21 +000056#include "MagickCore/pixel-private.h"
cristyac245f82012-05-05 17:13:57 +000057#include "MagickCore/resource_.h"
cristy8748f562012-03-08 13:58:34 +000058#include "MagickCore/string-private.h"
cristy16881e62012-05-06 14:41:29 +000059#include "MagickCore/thread-private.h"
cristya15140f2012-03-04 01:21:15 +000060#include "MagickCore/token.h"
cristyd04e7bf2012-03-03 19:19:12 +000061#include "MagickCore/utility.h"
62#include "MagickCore/version.h"
63
64/*
65%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66% %
67% %
68% %
cristy5f257b22012-03-07 00:27:29 +000069% C h a n n e l F x I m a g e %
cristyd04e7bf2012-03-03 19:19:12 +000070% %
71% %
72% %
73%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
74%
cristy5f257b22012-03-07 00:27:29 +000075% ChannelFxImage() applies a channel expression to the specified image. The
76% expression consists of one or more channels, either mnemonic or numeric (e.g.
77% red, 1), separated by actions as follows:
cristyab272ac2012-03-04 22:08:07 +000078%
79% <=> exchange two channels (e.g. red<=>blue)
cristy7ea920d2012-03-16 01:08:01 +000080% => copy one channel to another channel (e.g. red=>green)
81% = assign a constant value to a channel (e.g. red=50%)
82% , write new image channels in the specified order (e.g. red, green)
cristy3c213612012-03-16 01:14:48 +000083% | add a new output image for the next set of channel operations
84% ; move to the next input image for the source of channel data
cristyab272ac2012-03-04 22:08:07 +000085%
cristy7ea920d2012-03-16 01:08:01 +000086% For example, to create 3 grayscale images from the red, green, and blue
87% channels of an image, use:
cristyab272ac2012-03-04 22:08:07 +000088%
cristy3c213612012-03-16 01:14:48 +000089% -channel-fx "red; green; blue"
cristy7ea920d2012-03-16 01:08:01 +000090%
91% A channel without an operation symbol implies separate (i.e, semicolon).
cristyd04e7bf2012-03-03 19:19:12 +000092%
cristy5f257b22012-03-07 00:27:29 +000093% The format of the ChannelFxImage method is:
cristyd04e7bf2012-03-03 19:19:12 +000094%
cristy5f257b22012-03-07 00:27:29 +000095% Image *ChannelFxImage(const Image *image,const char *expression,
96% ExceptionInfo *exception)
cristyd04e7bf2012-03-03 19:19:12 +000097%
98% A description of each parameter follows:
99%
cristyab272ac2012-03-04 22:08:07 +0000100% o image: the image.
cristyd04e7bf2012-03-03 19:19:12 +0000101%
102% o expression: A channel expression.
103%
104% o exception: return any errors or warnings in this structure.
105%
106*/
cristya15140f2012-03-04 01:21:15 +0000107
108typedef enum
109{
110 ExtractChannelOp,
cristy8748f562012-03-08 13:58:34 +0000111 AssignChannelOp,
cristya15140f2012-03-04 01:21:15 +0000112 ExchangeChannelOp,
113 TransferChannelOp
cristy5f257b22012-03-07 00:27:29 +0000114} ChannelFx;
cristya15140f2012-03-04 01:21:15 +0000115
cristyab272ac2012-03-04 22:08:07 +0000116static inline size_t MagickMin(const size_t x,const size_t y)
cristya15140f2012-03-04 01:21:15 +0000117{
cristyab272ac2012-03-04 22:08:07 +0000118 if (x < y)
119 return(x);
120 return(y);
cristya15140f2012-03-04 01:21:15 +0000121}
122
cristyab272ac2012-03-04 22:08:07 +0000123static MagickBooleanType ChannelImage(Image *destination_image,
cristy8748f562012-03-08 13:58:34 +0000124 const PixelChannel destination_channel,const ChannelFx channel_op,
cristy3bb378a2012-03-08 14:38:52 +0000125 const Image *source_image,const PixelChannel source_channel,
cristy8748f562012-03-08 13:58:34 +0000126 const Quantum pixel,ExceptionInfo *exception)
cristyab272ac2012-03-04 22:08:07 +0000127{
128 CacheView
129 *source_view,
130 *destination_view;
131
132 MagickBooleanType
133 status;
134
135 size_t
cristyac245f82012-05-05 17:13:57 +0000136 height,
137 width;
cristyab272ac2012-03-04 22:08:07 +0000138
139 ssize_t
140 y;
141
142 status=MagickTrue;
cristy46ff2672012-12-14 15:32:26 +0000143 source_view=AcquireVirtualCacheView(source_image,exception);
144 destination_view=AcquireAuthenticCacheView(destination_image,exception);
cristyab272ac2012-03-04 22:08:07 +0000145 height=MagickMin(source_image->rows,destination_image->rows);
cristyac245f82012-05-05 17:13:57 +0000146 width=MagickMin(source_image->columns,destination_image->columns);
cristyab272ac2012-03-04 22:08:07 +0000147#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy9a5a52f2012-10-09 14:40:31 +0000148 #pragma omp parallel for schedule(static,4) shared(status) \
cristy5e6b2592012-12-19 14:08:11 +0000149 magick_threads(source_image,source_image,height,1)
cristyab272ac2012-03-04 22:08:07 +0000150#endif
151 for (y=0; y < (ssize_t) height; y++)
152 {
cristy4ee60402012-03-11 02:01:20 +0000153 PixelTrait
154 destination_traits,
155 source_traits;
156
cristyab272ac2012-03-04 22:08:07 +0000157 register const Quantum
158 *restrict p;
159
160 register Quantum
161 *restrict q;
162
163 register ssize_t
164 x;
165
cristyab272ac2012-03-04 22:08:07 +0000166 if (status == MagickFalse)
167 continue;
168 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
169 exception);
cristy4ee60402012-03-11 02:01:20 +0000170 q=GetCacheViewAuthenticPixels(destination_view,0,y,
cristyab272ac2012-03-04 22:08:07 +0000171 destination_image->columns,1,exception);
172 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
173 {
174 status=MagickFalse;
175 continue;
176 }
cristycf1296e2012-08-26 23:40:49 +0000177 destination_traits=GetPixelChannelTraits(destination_image,
cristy4ee60402012-03-11 02:01:20 +0000178 destination_channel);
cristycf1296e2012-08-26 23:40:49 +0000179 source_traits=GetPixelChannelTraits(source_image,source_channel);
cristy4ee60402012-03-11 02:01:20 +0000180 if ((destination_traits == UndefinedPixelTrait) ||
181 (source_traits == UndefinedPixelTrait))
182 continue;
cristyab272ac2012-03-04 22:08:07 +0000183 for (x=0; x < (ssize_t) width; x++)
184 {
cristy8748f562012-03-08 13:58:34 +0000185 if (channel_op == AssignChannelOp)
186 SetPixelChannel(destination_image,destination_channel,pixel,q);
187 else
cristy4ee60402012-03-11 02:01:20 +0000188 SetPixelChannel(destination_image,destination_channel,
189 GetPixelChannel(source_image,source_channel,p),q);
190 p+=GetPixelChannels(source_image);
cristyab272ac2012-03-04 22:08:07 +0000191 q+=GetPixelChannels(destination_image);
192 }
193 if (SyncCacheViewAuthenticPixels(destination_view,exception) == MagickFalse)
194 status=MagickFalse;
195 }
196 destination_view=DestroyCacheView(destination_view);
197 source_view=DestroyCacheView(source_view);
198 return(status);
199}
200
cristy5f257b22012-03-07 00:27:29 +0000201MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
202 ExceptionInfo *exception)
cristyd04e7bf2012-03-03 19:19:12 +0000203{
cristy5f257b22012-03-07 00:27:29 +0000204#define ChannelFxImageTag "ChannelFx/Image"
cristyab272ac2012-03-04 22:08:07 +0000205
cristy5f257b22012-03-07 00:27:29 +0000206 ChannelFx
cristya15140f2012-03-04 01:21:15 +0000207 channel_op;
208
cristyfe88ede2012-03-22 00:34:48 +0000209 ChannelType
210 channel_mask;
211
cristy4ee60402012-03-11 02:01:20 +0000212 char
213 token[MaxTextExtent];
214
cristya15140f2012-03-04 01:21:15 +0000215 const char
216 *p;
217
cristyab272ac2012-03-04 22:08:07 +0000218 const Image
219 *source_image;
220
cristy8748f562012-03-08 13:58:34 +0000221 double
222 pixel;
223
cristya15140f2012-03-04 01:21:15 +0000224 Image
cristyab272ac2012-03-04 22:08:07 +0000225 *destination_image;
cristya15140f2012-03-04 01:21:15 +0000226
cristy4ee60402012-03-11 02:01:20 +0000227 MagickBooleanType
228 status;
229
cristya15140f2012-03-04 01:21:15 +0000230 PixelChannel
cristyab272ac2012-03-04 22:08:07 +0000231 source_channel,
232 destination_channel;
cristya15140f2012-03-04 01:21:15 +0000233
cristyab272ac2012-03-04 22:08:07 +0000234 ssize_t
235 channels;
236
237 assert(image != (Image *) NULL);
238 assert(image->signature == MagickSignature);
239 if (image->debug != MagickFalse)
240 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
241 assert(exception != (ExceptionInfo *) NULL);
242 assert(exception->signature == MagickSignature);
243 source_image=image;
244 destination_image=CloneImage(source_image,0,0,MagickTrue,exception);
245 if (destination_image == (Image *) NULL)
246 return((Image *) NULL);
cristyab272ac2012-03-04 22:08:07 +0000247 if (expression == (const char *) NULL)
248 return(destination_image);
249 destination_channel=RedPixelChannel;
cristyfe88ede2012-03-22 00:34:48 +0000250 channel_mask=UndefinedChannel;
cristy8748f562012-03-08 13:58:34 +0000251 pixel=0.0;
cristya15140f2012-03-04 01:21:15 +0000252 p=(char *) expression;
253 GetMagickToken(p,&p,token);
cristy4ee60402012-03-11 02:01:20 +0000254 channel_op=ExtractChannelOp;
cristy09ef5be2012-03-13 13:01:27 +0000255 for (channels=0; *token != '\0'; )
cristya15140f2012-03-04 01:21:15 +0000256 {
cristya15140f2012-03-04 01:21:15 +0000257 ssize_t
258 i;
259
260 /*
261 Interpret channel expression.
262 */
cristy68bc2572013-05-16 01:09:13 +0000263 switch (*token)
264 {
cristy68bc2572013-05-16 01:09:13 +0000265 case ',':
cristya15140f2012-03-04 01:21:15 +0000266 {
cristya15140f2012-03-04 01:21:15 +0000267 GetMagickToken(p,&p,token);
cristy68bc2572013-05-16 01:09:13 +0000268 break;
cristya15140f2012-03-04 01:21:15 +0000269 }
cristy68bc2572013-05-16 01:09:13 +0000270 case '|':
cristya15140f2012-03-04 01:21:15 +0000271 {
cristyab272ac2012-03-04 22:08:07 +0000272 if (GetNextImageInList(source_image) != (Image *) NULL)
273 source_image=GetNextImageInList(source_image);
cristya15140f2012-03-04 01:21:15 +0000274 else
cristyab272ac2012-03-04 22:08:07 +0000275 source_image=GetFirstImageInList(source_image);
cristya15140f2012-03-04 01:21:15 +0000276 GetMagickToken(p,&p,token);
cristy68bc2572013-05-16 01:09:13 +0000277 break;
cristya15140f2012-03-04 01:21:15 +0000278 }
cristy68bc2572013-05-16 01:09:13 +0000279 case ';':
cristya15140f2012-03-04 01:21:15 +0000280 {
cristyab272ac2012-03-04 22:08:07 +0000281 Image
282 *canvas;
283
cristycf1296e2012-08-26 23:40:49 +0000284 SetPixelChannelMask(destination_image,channel_mask);
cristy68bc2572013-05-16 01:09:13 +0000285 if ((channel_op == ExtractChannelOp) && (channels == 1))
cristyfe88ede2012-03-22 00:34:48 +0000286 (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
cristy4ee60402012-03-11 02:01:20 +0000287 status=SetImageStorageClass(destination_image,DirectClass,exception);
288 if (status == MagickFalse)
289 {
cristyfc68ef52012-03-11 23:33:15 +0000290 destination_image=DestroyImageList(destination_image);
291 return(destination_image);
cristy4ee60402012-03-11 02:01:20 +0000292 }
cristyab272ac2012-03-04 22:08:07 +0000293 canvas=CloneImage(source_image,0,0,MagickTrue,exception);
294 if (canvas == (Image *) NULL)
295 {
cristyfc68ef52012-03-11 23:33:15 +0000296 destination_image=DestroyImageList(destination_image);
297 return(destination_image);
cristyab272ac2012-03-04 22:08:07 +0000298 }
299 AppendImageToList(&destination_image,canvas);
300 destination_image=GetLastImageInList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000301 GetMagickToken(p,&p,token);
cristyab272ac2012-03-04 22:08:07 +0000302 channels=0;
303 destination_channel=RedPixelChannel;
cristyfe88ede2012-03-22 00:34:48 +0000304 channel_mask=UndefinedChannel;
cristy68bc2572013-05-16 01:09:13 +0000305 break;
cristya15140f2012-03-04 01:21:15 +0000306 }
cristy68bc2572013-05-16 01:09:13 +0000307 default:
308 break;
309 }
cristya15140f2012-03-04 01:21:15 +0000310 i=ParsePixelChannelOption(token);
311 if (i < 0)
312 {
313 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyefe601c2013-01-05 17:51:12 +0000314 "UnrecognizedChannelType","`%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000315 destination_image=DestroyImageList(destination_image);
cristyfc68ef52012-03-11 23:33:15 +0000316 return(destination_image);
cristya15140f2012-03-04 01:21:15 +0000317 }
cristyab272ac2012-03-04 22:08:07 +0000318 source_channel=(PixelChannel) i;
cristya15140f2012-03-04 01:21:15 +0000319 channel_op=ExtractChannelOp;
320 GetMagickToken(p,&p,token);
321 if (*token == '<')
322 {
323 channel_op=ExchangeChannelOp;
324 GetMagickToken(p,&p,token);
325 }
326 if (*token == '=')
cristy8748f562012-03-08 13:58:34 +0000327 {
328 if (channel_op != ExchangeChannelOp)
329 channel_op=AssignChannelOp;
330 GetMagickToken(p,&p,token);
331 }
cristya15140f2012-03-04 01:21:15 +0000332 if (*token == '>')
333 {
334 if (channel_op != ExchangeChannelOp)
335 channel_op=TransferChannelOp;
336 GetMagickToken(p,&p,token);
337 }
cristy8748f562012-03-08 13:58:34 +0000338 switch (channel_op)
339 {
340 case AssignChannelOp:
341 {
cristy41db1742012-03-09 00:57:52 +0000342 pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
cristy3bb378a2012-03-08 14:38:52 +0000343 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000344 break;
345 }
346 case ExchangeChannelOp:
347 case TransferChannelOp:
cristya15140f2012-03-04 01:21:15 +0000348 {
349 i=ParsePixelChannelOption(token);
350 if (i < 0)
351 {
352 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
cristyefe601c2013-01-05 17:51:12 +0000353 "UnrecognizedChannelType","`%s'",token);
cristyab272ac2012-03-04 22:08:07 +0000354 destination_image=DestroyImageList(destination_image);
cristyfc68ef52012-03-11 23:33:15 +0000355 return(destination_image);
cristya15140f2012-03-04 01:21:15 +0000356 }
cristyab272ac2012-03-04 22:08:07 +0000357 destination_channel=(PixelChannel) i;
cristy04817142013-05-20 17:14:03 +0000358 switch (destination_channel)
359 {
cristyed27bb72013-05-20 17:24:07 +0000360 case RedPixelChannel:
361 case GreenPixelChannel:
362 case BluePixelChannel:
363 case BlackPixelChannel:
364 case IndexPixelChannel:
365 break;
cristy04817142013-05-20 17:14:03 +0000366 case AlphaPixelChannel:
367 {
368 destination_image->alpha_trait=BlendPixelTrait;
369 break;
370 }
371 case ReadMaskPixelChannel:
372 {
373 destination_image->read_mask=MagickTrue;
374 break;
375 }
376 case WriteMaskPixelChannel:
377 {
378 destination_image->write_mask=MagickTrue;
379 break;
380 }
cristyed27bb72013-05-20 17:24:07 +0000381 case MetaPixelChannel:
cristy04817142013-05-20 17:14:03 +0000382 default:
cristyed27bb72013-05-20 17:24:07 +0000383 {
384 (void) SetPixelMetaChannels(destination_image,(size_t) (i-
385 GetPixelChannels(destination_image)+1),exception);
cristy04817142013-05-20 17:14:03 +0000386 break;
cristyed27bb72013-05-20 17:24:07 +0000387 }
cristy04817142013-05-20 17:14:03 +0000388 }
cristyaa2c16c2012-03-25 22:21:35 +0000389 channel_mask=(ChannelType) (channel_mask | ParseChannelOption(token));
cristy68bc2572013-05-16 01:09:13 +0000390 if (((channels >= 1) || (destination_channel >= 1)) &&
391 (IsGrayColorspace(destination_image->colorspace) != MagickFalse))
392 (void) SetImageColorspace(destination_image,sRGBColorspace,exception);
cristy3bb378a2012-03-08 14:38:52 +0000393 GetMagickToken(p,&p,token);
cristy8748f562012-03-08 13:58:34 +0000394 break;
cristya15140f2012-03-04 01:21:15 +0000395 }
cristyfe88ede2012-03-22 00:34:48 +0000396 default:
cristy3c213612012-03-16 01:14:48 +0000397 break;
cristy7ea920d2012-03-16 01:08:01 +0000398 }
cristy3bb378a2012-03-08 14:38:52 +0000399 status=ChannelImage(destination_image,destination_channel,channel_op,
400 source_image,source_channel,ClampToQuantum(pixel),exception);
cristya15140f2012-03-04 01:21:15 +0000401 if (status == MagickFalse)
402 {
cristyab272ac2012-03-04 22:08:07 +0000403 destination_image=DestroyImageList(destination_image);
cristya15140f2012-03-04 01:21:15 +0000404 break;
405 }
cristy4ee60402012-03-11 02:01:20 +0000406 channels++;
cristy8748f562012-03-08 13:58:34 +0000407 if (channel_op == ExchangeChannelOp)
408 {
cristy4ee60402012-03-11 02:01:20 +0000409 status=ChannelImage(destination_image,source_channel,channel_op,
410 source_image,destination_channel,ClampToQuantum(pixel),exception);
cristy8748f562012-03-08 13:58:34 +0000411 if (status == MagickFalse)
412 {
413 destination_image=DestroyImageList(destination_image);
414 break;
415 }
cristy4ee60402012-03-11 02:01:20 +0000416 channels++;
cristy8748f562012-03-08 13:58:34 +0000417 }
cristyfe88ede2012-03-22 00:34:48 +0000418 switch (channel_op)
419 {
420 case ExtractChannelOp:
421 {
cristy9dbae2c2013-05-27 20:12:53 +0000422 channel_mask=(ChannelType) (channel_mask | (1 << destination_channel));
cristyaa2c16c2012-03-25 22:21:35 +0000423 destination_channel=(PixelChannel) (destination_channel+1);
cristyfe88ede2012-03-22 00:34:48 +0000424 break;
425 }
426 default:
427 break;
428 }
cristy5f257b22012-03-07 00:27:29 +0000429 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
cristyab272ac2012-03-04 22:08:07 +0000430 strlen(expression));
431 if (status == MagickFalse)
432 break;
cristya15140f2012-03-04 01:21:15 +0000433 }
cristycf1296e2012-08-26 23:40:49 +0000434 SetPixelChannelMask(destination_image,channel_mask);
cristy68bc2572013-05-16 01:09:13 +0000435 if ((channel_op == ExtractChannelOp) && (channels == 1))
cristyfe88ede2012-03-22 00:34:48 +0000436 (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
cristy4ee60402012-03-11 02:01:20 +0000437 status=SetImageStorageClass(destination_image,DirectClass,exception);
438 if (status == MagickFalse)
439 {
440 destination_image=GetLastImageInList(destination_image);
441 return((Image *) NULL);
442 }
cristy09ef5be2012-03-13 13:01:27 +0000443 return(GetFirstImageInList(destination_image));
cristyd04e7bf2012-03-03 19:19:12 +0000444}
cristy0c643722012-03-05 19:18:42 +0000445
446/*
447%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
448% %
449% %
450% %
451% C o m b i n e I m a g e s %
452% %
453% %
454% %
455%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
456%
457% CombineImages() combines one or more images into a single image. The
458% grayscale value of the pixels of each image in the sequence is assigned in
459% order to the specified channels of the combined image. The typical
460% ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
461%
462% The format of the CombineImages method is:
463%
cristy46f354c2012-07-04 13:31:29 +0000464% Image *CombineImages(const Image *images,const ColorspaceType colorspace,
465% ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000466%
467% A description of each parameter follows:
468%
cristy46f354c2012-07-04 13:31:29 +0000469% o images: the image sequence.
470%
471% o colorspace: the image colorspace.
cristy0c643722012-03-05 19:18:42 +0000472%
473% o exception: return any errors or warnings in this structure.
474%
475*/
cristy46f354c2012-07-04 13:31:29 +0000476MagickExport Image *CombineImages(const Image *image,
477 const ColorspaceType colorspace,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000478{
479#define CombineImageTag "Combine/Image"
480
481 CacheView
482 *combine_view;
483
484 Image
485 *combine_image;
486
487 MagickBooleanType
488 status;
489
490 MagickOffsetType
491 progress;
492
493 ssize_t
494 y;
495
496 /*
497 Ensure the image are the same size.
498 */
499 assert(image != (const Image *) NULL);
500 assert(image->signature == MagickSignature);
501 if (image->debug != MagickFalse)
502 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
503 assert(exception != (ExceptionInfo *) NULL);
504 assert(exception->signature == MagickSignature);
505 combine_image=CloneImage(image,0,0,MagickTrue,exception);
506 if (combine_image == (Image *) NULL)
507 return((Image *) NULL);
508 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
509 {
510 combine_image=DestroyImage(combine_image);
511 return((Image *) NULL);
512 }
cristy74f3f942013-05-01 11:11:28 +0000513 (void) SetImageColorspace(combine_image,colorspace,exception);
cristy0c643722012-03-05 19:18:42 +0000514 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
cristy8a46d822012-08-28 23:32:39 +0000515 combine_image->alpha_trait=BlendPixelTrait;
cristy0c643722012-03-05 19:18:42 +0000516 /*
517 Combine images.
518 */
519 status=MagickTrue;
520 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000521 combine_view=AcquireAuthenticCacheView(combine_image,exception);
cristy0c643722012-03-05 19:18:42 +0000522 for (y=0; y < (ssize_t) combine_image->rows; y++)
523 {
524 CacheView
525 *image_view;
526
527 const Image
528 *next;
529
530 Quantum
531 *pixels;
532
533 register const Quantum
534 *restrict p;
535
536 register Quantum
537 *restrict q;
538
539 register ssize_t
540 i;
541
542 if (status == MagickFalse)
543 continue;
544 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
545 1,exception);
546 if (pixels == (Quantum *) NULL)
547 {
548 status=MagickFalse;
549 continue;
550 }
551 next=image;
cristyc1119af2012-04-16 15:28:23 +0000552 for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
cristy0c643722012-03-05 19:18:42 +0000553 {
cristy0c643722012-03-05 19:18:42 +0000554 register ssize_t
555 x;
556
cristy5a23c552013-02-13 14:34:28 +0000557 PixelChannel channel=GetPixelChannelChannel(combine_image,i);
558 PixelTrait traits=GetPixelChannelTraits(combine_image,channel);
cristyc1119af2012-04-16 15:28:23 +0000559 if (traits == UndefinedPixelTrait)
cristy0c643722012-03-05 19:18:42 +0000560 continue;
cristy5a23c552013-02-13 14:34:28 +0000561 if (next == (Image *) NULL)
562 continue;
cristy46ff2672012-12-14 15:32:26 +0000563 image_view=AcquireVirtualCacheView(next,exception);
cristy0c643722012-03-05 19:18:42 +0000564 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
565 if (p == (const Quantum *) NULL)
566 continue;
567 q=pixels;
568 for (x=0; x < (ssize_t) combine_image->columns; x++)
569 {
cristyf3381ae2012-07-13 16:30:09 +0000570 if (x < (ssize_t) next->columns)
cristy0c643722012-03-05 19:18:42 +0000571 {
cristyf3381ae2012-07-13 16:30:09 +0000572 q[i]=GetPixelGray(next,p);
573 p+=GetPixelChannels(next);
cristy0c643722012-03-05 19:18:42 +0000574 }
575 q+=GetPixelChannels(combine_image);
576 }
577 image_view=DestroyCacheView(image_view);
578 next=GetNextImageInList(next);
cristy0c643722012-03-05 19:18:42 +0000579 }
cristyf3381ae2012-07-13 16:30:09 +0000580 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
581 status=MagickFalse;
582 if (image->progress_monitor != (MagickProgressMonitor) NULL)
583 {
584 MagickBooleanType
585 proceed;
586
587 proceed=SetImageProgress(image,CombineImageTag,progress++,
588 combine_image->rows);
589 if (proceed == MagickFalse)
590 status=MagickFalse;
591 }
cristy0c643722012-03-05 19:18:42 +0000592 }
593 combine_view=DestroyCacheView(combine_view);
594 if (status == MagickFalse)
595 combine_image=DestroyImage(combine_image);
596 return(combine_image);
597}
598
599/*
600%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
601% %
602% %
603% %
cristy8c8c9162013-05-27 10:14:21 +0000604% G e t I m a g e A l p h a C h a n n e l %
605% %
606% %
607% %
608%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
609%
610% GetImageAlphaChannel() returns MagickFalse if the image alpha channel is
611% not activated. That is, the image is RGB rather than RGBA or CMYK rather
612% than CMYKA.
613%
614% The format of the GetImageAlphaChannel method is:
615%
616% MagickBooleanType GetImageAlphaChannel(const Image *image)
617%
618% A description of each parameter follows:
619%
620% o image: the image.
621%
622*/
623MagickExport MagickBooleanType GetImageAlphaChannel(const Image *image)
624{
625 assert(image != (const Image *) NULL);
626 if (image->debug != MagickFalse)
627 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
628 assert(image->signature == MagickSignature);
629 return(image->alpha_trait == BlendPixelTrait ? MagickTrue : MagickFalse);
630}
631
632/*
633%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
634% %
635% %
636% %
cristy0c643722012-03-05 19:18:42 +0000637% S e p a r a t e I m a g e %
638% %
639% %
640% %
641%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
642%
643% SeparateImage() separates a channel from the image and returns it as a
644% grayscale image.
645%
646% The format of the SeparateImage method is:
647%
648% Image *SeparateImage(const Image *image,const ChannelType channel,
649% ExceptionInfo *exception)
650%
651% A description of each parameter follows:
652%
653% o image: the image.
654%
655% o channel: the image channel.
656%
657% o exception: return any errors or warnings in this structure.
658%
659*/
660MagickExport Image *SeparateImage(const Image *image,
661 const ChannelType channel_type,ExceptionInfo *exception)
662{
663#define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
664#define SeparateImageTag "Separate/Image"
665
666 CacheView
667 *image_view,
668 *separate_view;
669
670 Image
671 *separate_image;
672
673 MagickBooleanType
674 status;
675
676 MagickOffsetType
677 progress;
678
679 ssize_t
680 y;
681
682 /*
cristy8fdeeb32013-03-31 21:15:31 +0000683 Initialize separate image attributes.
cristy0c643722012-03-05 19:18:42 +0000684 */
685 assert(image != (Image *) NULL);
686 assert(image->signature == MagickSignature);
687 if (image->debug != MagickFalse)
688 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
689 assert(exception != (ExceptionInfo *) NULL);
690 assert(exception->signature == MagickSignature);
691 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
692 exception);
693 if (separate_image == (Image *) NULL)
694 return((Image *) NULL);
695 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
696 {
697 separate_image=DestroyImage(separate_image);
698 return((Image *) NULL);
699 }
cristyaa7a2252012-08-15 12:19:00 +0000700 (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
cristy9b8205c2013-04-16 11:16:19 +0000701 separate_image->alpha_trait=UndefinedPixelTrait;
cristy0c643722012-03-05 19:18:42 +0000702 /*
703 Separate image.
704 */
705 status=MagickTrue;
706 progress=0;
cristy46ff2672012-12-14 15:32:26 +0000707 image_view=AcquireVirtualCacheView(image,exception);
708 separate_view=AcquireAuthenticCacheView(separate_image,exception);
cristy0c643722012-03-05 19:18:42 +0000709#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristy9a5a52f2012-10-09 14:40:31 +0000710 #pragma omp parallel for schedule(static,4) shared(progress,status) \
cristy5e6b2592012-12-19 14:08:11 +0000711 magick_threads(image,image,image->rows,1)
cristy0c643722012-03-05 19:18:42 +0000712#endif
713 for (y=0; y < (ssize_t) image->rows; y++)
714 {
715 register const Quantum
716 *restrict p;
717
718 register Quantum
719 *restrict q;
720
721 register ssize_t
722 x;
723
724 if (status == MagickFalse)
725 continue;
726 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
727 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
728 exception);
729 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
730 {
731 status=MagickFalse;
732 continue;
733 }
734 for (x=0; x < (ssize_t) image->columns; x++)
735 {
736 register ssize_t
737 i;
738
cristy883fde12013-04-08 00:50:13 +0000739 if (GetPixelReadMask(image,p) == 0)
cristy0c643722012-03-05 19:18:42 +0000740 {
cristyc3a58022013-10-09 23:22:42 +0000741 SetPixelBackgoundColor(separate_image,q);
cristy0c643722012-03-05 19:18:42 +0000742 p+=GetPixelChannels(image);
743 q+=GetPixelChannels(separate_image);
744 continue;
745 }
746 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
747 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
748 {
cristy5a23c552013-02-13 14:34:28 +0000749 PixelChannel channel=GetPixelChannelChannel(image,i);
750 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristy0c643722012-03-05 19:18:42 +0000751 if ((traits == UndefinedPixelTrait) ||
752 (GetChannelBit(channel_type,channel) == 0))
753 continue;
cristy9b8205c2013-04-16 11:16:19 +0000754 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
cristy0c643722012-03-05 19:18:42 +0000755 }
756 p+=GetPixelChannels(image);
757 q+=GetPixelChannels(separate_image);
758 }
759 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
760 status=MagickFalse;
761 if (image->progress_monitor != (MagickProgressMonitor) NULL)
762 {
763 MagickBooleanType
764 proceed;
765
766#if defined(MAGICKCORE_OPENMP_SUPPORT)
cristyac245f82012-05-05 17:13:57 +0000767 #pragma omp critical (MagickCore_SeparateImage)
cristy0c643722012-03-05 19:18:42 +0000768#endif
769 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
770 if (proceed == MagickFalse)
771 status=MagickFalse;
772 }
773 }
774 separate_view=DestroyCacheView(separate_view);
775 image_view=DestroyCacheView(image_view);
cristy1c2f48d2012-12-14 01:20:55 +0000776 if (status == MagickFalse)
777 separate_image=DestroyImage(separate_image);
cristy0c643722012-03-05 19:18:42 +0000778 return(separate_image);
779}
780
781/*
782%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
783% %
784% %
785% %
786% S e p a r a t e I m a g e s %
787% %
788% %
789% %
790%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
791%
792% SeparateImages() returns a separate grayscale image for each channel
793% specified.
794%
795% The format of the SeparateImages method is:
796%
cristydfdb19e2012-03-21 22:22:24 +0000797% Image *SeparateImages(const Image *image,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000798%
799% A description of each parameter follows:
800%
801% o image: the image.
802%
803% o exception: return any errors or warnings in this structure.
804%
805*/
cristydfdb19e2012-03-21 22:22:24 +0000806MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
cristy0c643722012-03-05 19:18:42 +0000807{
808 Image
809 *images,
810 *separate_image;
811
812 register ssize_t
813 i;
814
815 assert(image != (Image *) NULL);
816 assert(image->signature == MagickSignature);
817 if (image->debug != MagickFalse)
818 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
819 images=NewImageList();
820 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
821 {
cristy5a23c552013-02-13 14:34:28 +0000822 PixelChannel channel=GetPixelChannelChannel(image,i);
823 PixelTrait traits=GetPixelChannelTraits(image,channel);
cristy0c643722012-03-05 19:18:42 +0000824 if ((traits == UndefinedPixelTrait) ||
825 ((traits & UpdatePixelTrait) == 0))
826 continue;
827 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
828 if (separate_image != (Image *) NULL)
829 AppendImageToList(&images,separate_image);
830 }
cristye5ef2ce2012-04-29 21:14:32 +0000831 if (images == (Image *) NULL)
832 images=SeparateImage(image,UndefinedChannel,exception);
cristy0c643722012-03-05 19:18:42 +0000833 return(images);
834}
cristy8c8c9162013-05-27 10:14:21 +0000835
836/*
837%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
838% %
839% %
840% %
841% S e t I m a g e A l p h a C h a n n e l %
842% %
843% %
844% %
845%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
846%
847% SetImageAlphaChannel() activates, deactivates, resets, or sets the alpha
848% channel.
849%
850% The format of the SetImageAlphaChannel method is:
851%
852% MagickBooleanType SetImageAlphaChannel(Image *image,
853% const AlphaChannelOption alpha_type,ExceptionInfo *exception)
854%
855% A description of each parameter follows:
856%
857% o image: the image.
858%
859% o alpha_type: The alpha channel type: ActivateAlphaChannel,
860% CopyAlphaChannel, DeactivateAlphaChannel, ExtractAlphaChannel,
861% OpaqueAlphaChannel, SetAlphaChannel, ShapeAlphaChannel, and
862% TransparentAlphaChannel.
863%
864% o exception: return any errors or warnings in this structure.
865%
866*/
867
868static inline void FlattenPixelInfo(const Image *image,const PixelInfo *p,
869 const double alpha,const Quantum *q,const double beta,
870 Quantum *composite)
871{
872 double
873 Da,
874 gamma,
875 Sa;
876
877 register ssize_t
878 i;
879
880 /*
881 Compose pixel p over pixel q with the given alpha.
882 */
883 Sa=QuantumScale*alpha;
884 Da=QuantumScale*beta,
885 gamma=Sa*(-Da)+Sa+Da;
886 gamma=PerceptibleReciprocal(gamma);
887 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
888 {
889 PixelChannel channel=GetPixelChannelChannel(image,i);
890 PixelTrait traits=GetPixelChannelTraits(image,channel);
891 if (traits == UndefinedPixelTrait)
892 continue;
893 switch (channel)
894 {
895 case RedPixelChannel:
896 {
897 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
898 (double) p->red,alpha));
899 break;
900 }
901 case GreenPixelChannel:
902 {
903 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
904 (double) p->green,alpha));
905 break;
906 }
907 case BluePixelChannel:
908 {
909 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
910 (double) p->blue,alpha));
911 break;
912 }
913 case BlackPixelChannel:
914 {
915 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
916 (double) p->black,alpha));
917 break;
918 }
919 case AlphaPixelChannel:
920 {
921 composite[i]=ClampToQuantum(QuantumRange*(Sa*(-Da)+Sa+Da));
922 break;
923 }
924 default:
925 break;
926 }
927 }
928}
929
930MagickExport MagickBooleanType SetImageAlphaChannel(Image *image,
931 const AlphaChannelOption alpha_type,ExceptionInfo *exception)
932{
933 MagickBooleanType
934 status;
935
936 assert(image != (Image *) NULL);
937 if (image->debug != MagickFalse)
938 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
939 assert(image->signature == MagickSignature);
940 status=MagickTrue;
941 switch (alpha_type)
942 {
943 case ActivateAlphaChannel:
944 {
945 image->alpha_trait=BlendPixelTrait;
946 break;
947 }
948 case BackgroundAlphaChannel:
949 {
950 CacheView
951 *image_view;
952
953 ssize_t
954 y;
955
956 /*
957 Set transparent pixels to background color.
958 */
959 if (image->alpha_trait != BlendPixelTrait)
960 break;
961 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
962 break;
963 image_view=AcquireAuthenticCacheView(image,exception);
964#if defined(MAGICKCORE_OPENMP_SUPPORT)
965 #pragma omp parallel for schedule(static,4) shared(status) \
966 magick_threads(image,image,image->rows,1)
967#endif
968 for (y=0; y < (ssize_t) image->rows; y++)
969 {
970 register Quantum
971 *restrict q;
972
973 register ssize_t
974 x;
975
976 if (status == MagickFalse)
977 continue;
978 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
979 exception);
980 if (q == (Quantum *) NULL)
981 {
982 status=MagickFalse;
983 continue;
984 }
985 for (x=0; x < (ssize_t) image->columns; x++)
986 {
987 if (GetPixelAlpha(image,q) == TransparentAlpha)
988 {
989 SetPixelInfoPixel(image,&image->background_color,q);
990 SetPixelChannel(image,AlphaPixelChannel,TransparentAlpha,q);
991 }
992 q+=GetPixelChannels(image);
993 }
994 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
995 status=MagickFalse;
996 }
997 image_view=DestroyCacheView(image_view);
998 return(status);
999 }
1000 case CopyAlphaChannel:
1001 case ShapeAlphaChannel:
1002 {
1003 /*
1004 Copy pixel intensity to the alpha channel.
1005 */
1006 status=CompositeImage(image,image,IntensityCompositeOp,MagickTrue,0,0,
1007 exception);
1008 if (alpha_type == ShapeAlphaChannel)
1009 (void) LevelImageColors(image,&image->background_color,
1010 &image->background_color,MagickTrue,exception);
1011 break;
1012 }
1013 case DeactivateAlphaChannel:
1014 {
1015 image->alpha_trait=CopyPixelTrait;
1016 break;
1017 }
1018 case ExtractAlphaChannel:
1019 {
1020 status=CompositeImage(image,image,AlphaCompositeOp,MagickTrue,0,0,
1021 exception);
1022 image->alpha_trait=CopyPixelTrait;
1023 break;
1024 }
1025 case OpaqueAlphaChannel:
1026 {
1027 status=SetImageAlpha(image,OpaqueAlpha,exception);
1028 break;
1029 }
1030 case RemoveAlphaChannel:
1031 {
1032 CacheView
1033 *image_view;
1034
1035 ssize_t
1036 y;
1037
1038 /*
1039 Remove transparency.
1040 */
1041 if (image->alpha_trait != BlendPixelTrait)
1042 break;
1043 if (SetImageStorageClass(image,DirectClass,exception) == MagickFalse)
1044 break;
1045 image_view=AcquireAuthenticCacheView(image,exception);
1046#if defined(MAGICKCORE_OPENMP_SUPPORT)
1047 #pragma omp parallel for schedule(static,4) shared(status) \
1048 magick_threads(image,image,image->rows,1)
1049#endif
1050 for (y=0; y < (ssize_t) image->rows; y++)
1051 {
1052 register Quantum
1053 *restrict q;
1054
1055 register ssize_t
1056 x;
1057
1058 if (status == MagickFalse)
1059 continue;
1060 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1061 exception);
1062 if (q == (Quantum *) NULL)
1063 {
1064 status=MagickFalse;
1065 continue;
1066 }
1067 for (x=0; x < (ssize_t) image->columns; x++)
1068 {
1069 FlattenPixelInfo(image,&image->background_color,
1070 image->background_color.alpha,q,(double)
1071 GetPixelAlpha(image,q),q);
1072 q+=GetPixelChannels(image);
1073 }
1074 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1075 status=MagickFalse;
1076 }
1077 image_view=DestroyCacheView(image_view);
1078 image->alpha_trait=image->background_color.alpha_trait;
1079 return(status);
1080 }
1081 case SetAlphaChannel:
1082 {
1083 if (image->alpha_trait != BlendPixelTrait)
1084 status=SetImageAlpha(image,OpaqueAlpha,exception);
1085 break;
1086 }
1087 case TransparentAlphaChannel:
1088 {
1089 status=SetImageAlpha(image,TransparentAlpha,exception);
1090 break;
1091 }
1092 case UndefinedAlphaChannel:
1093 break;
1094 }
1095 if (status == MagickFalse)
1096 return(status);
1097 return(SyncImagePixelCache(image,exception));
1098}