MagickCore 7.1.2-22
Convert, Edit, Or Compose Bitmap Images
Loading...
Searching...
No Matches
channel.c
1/*
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 EEE 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% Cristy %
17% December 2003 %
18% %
19% %
20% Copyright @ 1999 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% https://imagemagick.org/license/ %
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"
44#include "MagickCore/cache-private.h"
45#include "MagickCore/channel.h"
46#include "MagickCore/colorspace-private.h"
47#include "MagickCore/composite-private.h"
48#include "MagickCore/enhance.h"
49#include "MagickCore/image.h"
50#include "MagickCore/list.h"
51#include "MagickCore/log.h"
52#include "MagickCore/monitor.h"
53#include "MagickCore/monitor-private.h"
54#include "MagickCore/option.h"
55#include "MagickCore/pixel-accessor.h"
56#include "MagickCore/resource_.h"
57#include "MagickCore/string-private.h"
58#include "MagickCore/thread-private.h"
59#include "MagickCore/token.h"
60#include "MagickCore/utility.h"
61#include "MagickCore/version.h"
62
63/*
64%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
65% %
66% %
67% %
68% C h a n n e l F x I m a g e %
69% %
70% %
71% %
72%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
73%
74% 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% r, red, 0), separated by actions as follows:
77%
78% <=> exchange two channels (e.g. red<=>blue)
79% => 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)
82% ; 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
84% If there are no more images in the list, | has no effect.
85%
86% For example, to create 3 grayscale images from the red, green, and blue
87% channels of an image, use:
88%
89% -channel-fx "red; green; blue"
90%
91% A channel without an operation symbol implies separate (i.e, semicolon).
92%
93% The format of the ChannelFxImage method is:
94%
95% Image *ChannelFxImage(const Image *image,const char *expression,
96% ExceptionInfo *exception)
97%
98% A description of each parameter follows:
99%
100% o image: the image.
101%
102% o expression: A channel expression.
103%
104% o exception: return any errors or warnings in this structure.
105%
106*/
107
108typedef enum
109{
110 ExtractChannelOp,
111 AssignChannelOp,
112 ExchangeChannelOp,
113 TransferChannelOp
114} ChannelFx;
115
116static MagickBooleanType ChannelImage(Image *destination_image,
117 const PixelChannel destination_channel,const ChannelFx channel_op,
118 const Image *source_image,const PixelChannel source_channel,
119 const Quantum pixel,ExceptionInfo *exception)
120{
121 CacheView
122 *source_view,
123 *destination_view;
124
125 MagickBooleanType
126 status = MagickTrue;
127
128 size_t
129 height,
130 width;
131
132 ssize_t
133 y;
134
135 /*
136 Copy source channel to destination.
137 */
138 height=MagickMin(source_image->rows,destination_image->rows);
139 width=MagickMin(source_image->columns,destination_image->columns);
140 source_view=AcquireVirtualCacheView(source_image,exception);
141 destination_view=AcquireAuthenticCacheView(destination_image,exception);
142#if defined(MAGICKCORE_OPENMP_SUPPORT)
143 #pragma omp parallel for schedule(static) shared(status) \
144 magick_number_threads(source_image,source_image,height,4)
145#endif
146 for (y=0; y < (ssize_t) height; y++)
147 {
148 PixelTrait
149 destination_traits,
150 source_traits;
151
152 const Quantum
153 *magick_restrict p;
154
155 Quantum
156 *magick_restrict q;
157
158 ssize_t
159 x;
160
161 if (status == MagickFalse)
162 continue;
163 p=GetCacheViewVirtualPixels(source_view,0,y,source_image->columns,1,
164 exception);
165 q=GetCacheViewAuthenticPixels(destination_view,0,y,
166 destination_image->columns,1,exception);
167 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
168 {
169 status=MagickFalse;
170 continue;
171 }
172 destination_traits=GetPixelChannelTraits(destination_image,
173 destination_channel);
174 source_traits=GetPixelChannelTraits(source_image,source_channel);
175 if ((destination_traits == UndefinedPixelTrait) ||
176 (source_traits == UndefinedPixelTrait))
177 continue;
178 for (x=0; x < (ssize_t) width; x++)
179 {
180 if (channel_op == AssignChannelOp)
181 SetPixelChannel(destination_image,destination_channel,pixel,q);
182 else
183 SetPixelChannel(destination_image,destination_channel,
184 GetPixelChannel(source_image,source_channel,p),q);
185 p+=(ptrdiff_t) GetPixelChannels(source_image);
186 q+=(ptrdiff_t) 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
196MagickExport Image *ChannelFxImage(const Image *image,const char *expression,
197 ExceptionInfo *exception)
198{
199#define ChannelFxImageTag "ChannelFx/Image"
200
201 ChannelFx
202 channel_op = ExtractChannelOp;
203
204 ChannelType
205 channel_mask;
206
207 char
208 token[MagickPathExtent] = "";
209
210 const char
211 *p;
212
213 const Image
214 *source_image;
215
216 double
217 pixel = 0.0;
218
219 Image
220 *destination_image;
221
222 MagickBooleanType
223 status = MagickTrue;
224
225 PixelChannel
226 source_channel,
227 destination_channel = RedPixelChannel;
228
229 ssize_t
230 channels = 0;
231
232 assert(image != (Image *) NULL);
233 assert(image->signature == MagickCoreSignature);
234 if (IsEventLogging() != MagickFalse)
235 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
236 assert(exception != (ExceptionInfo *) NULL);
237 assert(exception->signature == MagickCoreSignature);
238 p=expression;
239 source_image=image;
240 destination_image=CloneImage(image,0,0,MagickTrue,exception);
241 if (destination_image == (Image *) NULL)
242 return((Image *) NULL);
243 if (expression == (const char *) NULL)
244 return(destination_image);
245 status=SetImageStorageClass(destination_image,DirectClass,exception);
246 if (status == MagickFalse)
247 {
248 destination_image=GetLastImageInList(destination_image);
249 return((Image *) NULL);
250 }
251 channel_mask=destination_image->channel_mask;
252 (void) GetNextToken(p,&p,MagickPathExtent,token);
253 while (*token != '\0')
254 {
255 PixelTrait
256 traits;
257
258 ssize_t
259 i;
260
261 /*
262 Interpret channel expression.
263 */
264 switch (*token)
265 {
266 case ',':
267 {
268 (void) GetNextToken(p,&p,MagickPathExtent,token);
269 break;
270 }
271 case '|':
272 {
273 if (GetNextImageInList(source_image) != (Image *) NULL)
274 source_image=GetNextImageInList(source_image);
275 (void) GetNextToken(p,&p,MagickPathExtent,token);
276 break;
277 }
278 case ';':
279 {
280 Image
281 *canvas;
282
283 (void) SetPixelChannelMask(destination_image,channel_mask);
284 if ((channel_op == ExtractChannelOp) && (channels == 1))
285 {
286 (void) SetPixelMetaChannels(destination_image,0,exception);
287 (void) SetImageColorspace(destination_image,GRAYColorspace,
288 exception);
289 }
290 canvas=CloneImage(source_image,0,0,MagickTrue,exception);
291 if (canvas == (Image *) NULL)
292 {
293 destination_image=DestroyImageList(destination_image);
294 return(destination_image);
295 }
296 AppendImageToList(&destination_image,canvas);
297 destination_image=GetLastImageInList(destination_image);
298 status=SetImageStorageClass(destination_image,DirectClass,exception);
299 if (status == MagickFalse)
300 {
301 destination_image=GetLastImageInList(destination_image);
302 return((Image *) NULL);
303 }
304 (void) GetNextToken(p,&p,MagickPathExtent,token);
305 channels=0;
306 destination_channel=RedPixelChannel;
307 channel_mask=destination_image->channel_mask;
308 break;
309 }
310 default:
311 break;
312 }
313 i=ParsePixelChannelOption(token);
314 source_channel=(PixelChannel) i;
315 traits=GetPixelChannelTraits(source_image,source_channel);
316 if (traits == UndefinedPixelTrait)
317 {
318 (void) ThrowMagickException(exception,GetMagickModule(),
319 CorruptImageError,"MissingImageChannel","`%s'",token);
320 destination_image=DestroyImageList(destination_image);
321 return(destination_image);
322 }
323 channel_op=ExtractChannelOp;
324 (void) GetNextToken(p,&p,MagickPathExtent,token);
325 if (*token == '<')
326 {
327 channel_op=ExchangeChannelOp;
328 (void) GetNextToken(p,&p,MagickPathExtent,token);
329 }
330 if (*token == '=')
331 {
332 if (channel_op != ExchangeChannelOp)
333 channel_op=AssignChannelOp;
334 (void) GetNextToken(p,&p,MagickPathExtent,token);
335 }
336 if (*token == '>')
337 {
338 if (channel_op != ExchangeChannelOp)
339 channel_op=TransferChannelOp;
340 (void) GetNextToken(p,&p,MagickPathExtent,token);
341 }
342 switch (channel_op)
343 {
344 case AssignChannelOp:
345 case ExchangeChannelOp:
346 case TransferChannelOp:
347 {
348 if (channel_op == AssignChannelOp)
349 pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
350 else
351 {
352 i=ParsePixelChannelOption(token);
353 if (LocaleCompare(token,"alpha") == 0)
354 destination_image->alpha_trait=BlendPixelTrait;
355 if (i < 0)
356 {
357 (void) ThrowMagickException(exception,GetMagickModule(),
358 OptionError,"UnrecognizedChannelType","`%s'",token);
359 destination_image=DestroyImageList(destination_image);
360 return(destination_image);
361 }
362 }
363 destination_channel=(PixelChannel) i;
364 if (image->colorspace != UndefinedColorspace)
365 switch (destination_channel)
366 {
367 case RedPixelChannel:
368 case GreenPixelChannel:
369 case BluePixelChannel:
370 case BlackPixelChannel:
371 case AlphaPixelChannel:
372 case IndexPixelChannel:
373 break;
374 case CompositeMaskPixelChannel:
375 {
376 destination_image->channels=(ChannelType)
377 (destination_image->channels | CompositeMaskChannel);
378 break;
379 }
380 case ReadMaskPixelChannel:
381 {
382 destination_image->channels=(ChannelType)
383 (destination_image->channels | ReadMaskChannel);
384 break;
385 }
386 case WriteMaskPixelChannel:
387 {
388 destination_image->channels=(ChannelType)
389 (destination_image->channels | WriteMaskChannel);
390 break;
391 }
392 case MetaPixelChannels:
393 default:
394 {
395 traits=GetPixelChannelTraits(destination_image,
396 destination_channel);
397 if (traits != UndefinedPixelTrait)
398 break;
399 (void) SetPixelMetaChannels(destination_image,
400 GetPixelMetaChannels(destination_image)+1,exception);
401 traits=GetPixelChannelTraits(destination_image,
402 destination_channel);
403 if (traits == UndefinedPixelTrait)
404 {
405 (void) ThrowMagickException(exception,GetMagickModule(),
406 CorruptImageError,"MissingImageChannel","`%s'",token);
407 destination_image=DestroyImageList(destination_image);
408 return(destination_image);
409 }
410 break;
411 }
412 }
413 channel_mask=(ChannelType) (channel_mask |
414 (MagickLLConstant(1) << ParseChannelOption(token)));
415 (void) GetNextToken(p,&p,MagickPathExtent,token);
416 break;
417 }
418 default:
419 break;
420 }
421 status=ChannelImage(destination_image,destination_channel,channel_op,
422 source_image,source_channel,ClampToQuantum(pixel),exception);
423 if (status == MagickFalse)
424 {
425 destination_image=DestroyImageList(destination_image);
426 break;
427 }
428 channels++;
429 if (channel_op == ExchangeChannelOp)
430 {
431 status=ChannelImage(destination_image,source_channel,channel_op,
432 source_image,destination_channel,ClampToQuantum(pixel),exception);
433 if (status == MagickFalse)
434 {
435 destination_image=DestroyImageList(destination_image);
436 break;
437 }
438 channels++;
439 }
440 switch (channel_op)
441 {
442 case ExtractChannelOp:
443 {
444 channel_mask=(ChannelType) (channel_mask |
445 (MagickLLConstant(1) << destination_channel));
446 destination_channel=(PixelChannel) (destination_channel+1);
447 break;
448 }
449 default:
450 break;
451 }
452 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
453 strlen(expression));
454 if (status == MagickFalse)
455 break;
456 }
457 if (destination_image == (Image *) NULL)
458 return(destination_image);
459 (void) SetPixelChannelMask(destination_image,channel_mask);
460 if ((channel_op == ExtractChannelOp) && (channels == 1))
461 {
462 (void) SetPixelMetaChannels(destination_image,0,exception);
463 (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
464 }
465 return(GetFirstImageInList(destination_image));
466}
467
468/*
469%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
470% %
471% %
472% %
473% C o m b i n e I m a g e s %
474% %
475% %
476% %
477%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
478%
479% CombineImages() combines one or more images into a single image. The
480% grayscale value of the pixels of each image in the sequence is assigned in
481% order to the specified channels of the combined image. The typical
482% ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
483%
484% The format of the CombineImages method is:
485%
486% Image *CombineImages(const Image *images,const ColorspaceType colorspace,
487% ExceptionInfo *exception)
488%
489% A description of each parameter follows:
490%
491% o images: the image sequence.
492%
493% o colorspace: the image colorspace.
494%
495% o exception: return any errors or warnings in this structure.
496%
497*/
498MagickExport Image *CombineImages(const Image *image,
499 const ColorspaceType colorspace,ExceptionInfo *exception)
500{
501#define CombineImageTag "Combine/Image"
502
503 CacheView
504 *combine_view;
505
506 Image
507 *combine_image;
508
509 MagickBooleanType
510 status;
511
512 MagickOffsetType
513 progress;
514
515 size_t
516 number_channels;
517
518 ssize_t
519 y;
520
521 /*
522 Ensure the image are the same size.
523 */
524 assert(image != (const Image *) NULL);
525 assert(image->signature == MagickCoreSignature);
526 if (IsEventLogging() != MagickFalse)
527 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
528 assert(exception != (ExceptionInfo *) NULL);
529 assert(exception->signature == MagickCoreSignature);
530 combine_image=CloneImage(image,0,0,MagickTrue,exception);
531 if (combine_image == (Image *) NULL)
532 return((Image *) NULL);
533 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
534 {
535 combine_image=DestroyImage(combine_image);
536 return((Image *) NULL);
537 }
538 if (colorspace != UndefinedColorspace)
539 (void) SetImageColorspace(combine_image,colorspace,exception);
540 else
541 if (fabs(image->gamma-1.0) <= MagickEpsilon)
542 (void) SetImageColorspace(combine_image,RGBColorspace,exception);
543 else
544 (void) SetImageColorspace(combine_image,sRGBColorspace,exception);
545 number_channels=GetImageListLength(image);
546 switch (combine_image->colorspace)
547 {
548 case UndefinedColorspace:
549 case sRGBColorspace:
550 {
551 if (number_channels > 3)
552 combine_image->alpha_trait=BlendPixelTrait;
553 if (number_channels > 4)
554 SetPixelMetaChannels(combine_image,number_channels-4,exception);
555 break;
556 }
557 case LinearGRAYColorspace:
558 case GRAYColorspace:
559 {
560 if (number_channels > 1)
561 combine_image->alpha_trait=BlendPixelTrait;
562 if (number_channels > 2)
563 SetPixelMetaChannels(combine_image,number_channels-2,exception);
564 break;
565 }
566 case CMYKColorspace:
567 {
568 if (number_channels > 4)
569 combine_image->alpha_trait=BlendPixelTrait;
570 if (number_channels > 5)
571 SetPixelMetaChannels(combine_image,number_channels-5,exception);
572 break;
573 }
574 default:
575 break;
576 }
577 /*
578 Combine images.
579 */
580 status=MagickTrue;
581 progress=0;
582 combine_view=AcquireAuthenticCacheView(combine_image,exception);
583#if defined(MAGICKCORE_OPENMP_SUPPORT)
584 #pragma omp parallel for schedule(static) shared(progress,status) \
585 magick_number_threads(combine_image,combine_image,combine_image->rows,4)
586#endif
587 for (y=0; y < (ssize_t) combine_image->rows; y++)
588 {
589 CacheView
590 *image_view;
591
592 const Image
593 *next;
594
595 const Quantum
596 *magick_restrict p;
597
598 Quantum
599 *pixels,
600 *magick_restrict q;
601
602 ssize_t
603 i;
604
605 if (status == MagickFalse)
606 continue;
607 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
608 1,exception);
609 if (pixels == (Quantum *) NULL)
610 {
611 status=MagickFalse;
612 continue;
613 }
614 next=image;
615 for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
616 {
617 ssize_t
618 x;
619
620 PixelChannel channel = GetPixelChannelChannel(combine_image,i);
621 PixelTrait traits = GetPixelChannelTraits(combine_image,channel);
622 if (traits == UndefinedPixelTrait)
623 continue;
624 if (next == (Image *) NULL)
625 continue;
626 image_view=AcquireVirtualCacheView(next,exception);
627 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
628 if (p == (const Quantum *) NULL)
629 continue;
630 q=pixels;
631 for (x=0; x < (ssize_t) combine_image->columns; x++)
632 {
633 if (x < (ssize_t) next->columns)
634 {
635 q[i]=(Quantum) GetPixelIntensity(next,p);
636 p+=(ptrdiff_t) GetPixelChannels(next);
637 }
638 q+=(ptrdiff_t) GetPixelChannels(combine_image);
639 }
640 image_view=DestroyCacheView(image_view);
641 next=GetNextImageInList(next);
642 }
643 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
644 status=MagickFalse;
645 if (image->progress_monitor != (MagickProgressMonitor) NULL)
646 {
647 MagickBooleanType
648 proceed;
649
650#if defined(MAGICKCORE_OPENMP_SUPPORT)
651 #pragma omp atomic
652#endif
653 progress++;
654 proceed=SetImageProgress(image,CombineImageTag,progress,
655 combine_image->rows);
656 if (proceed == MagickFalse)
657 status=MagickFalse;
658 }
659 }
660 combine_view=DestroyCacheView(combine_view);
661 if (status == MagickFalse)
662 combine_image=DestroyImage(combine_image);
663 else
664 combine_image->type=UndefinedType;
665 return(combine_image);
666}
667
668/*
669%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
670% %
671% %
672% %
673% G e t I m a g e A l p h a C h a n n e l %
674% %
675% %
676% %
677%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
678%
679% GetImageAlphaChannel() returns MagickFalse if the image alpha channel is
680% not activated. That is, the image is RGB rather than RGBA or CMYK rather
681% than CMYKA.
682%
683% The format of the GetImageAlphaChannel method is:
684%
685% MagickBooleanType GetImageAlphaChannel(const Image *image)
686%
687% A description of each parameter follows:
688%
689% o image: the image.
690%
691*/
692MagickExport MagickBooleanType GetImageAlphaChannel(const Image *image)
693{
694 assert(image != (const Image *) NULL);
695 if (IsEventLogging() != MagickFalse)
696 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
697 assert(image->signature == MagickCoreSignature);
698 return(image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse);
699}
700
701/*
702%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
703% %
704% %
705% %
706% S e p a r a t e I m a g e %
707% %
708% %
709% %
710%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
711%
712% SeparateImage() separates a channel from the image and returns it as a
713% grayscale image.
714%
715% The format of the SeparateImage method is:
716%
717% Image *SeparateImage(const Image *image,const ChannelType channel,
718% ExceptionInfo *exception)
719%
720% A description of each parameter follows:
721%
722% o image: the image.
723%
724% o channel: the image channel.
725%
726% o exception: return any errors or warnings in this structure.
727%
728*/
729MagickExport Image *SeparateImage(const Image *image,
730 const ChannelType channel_type,ExceptionInfo *exception)
731{
732#define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
733#define SeparateImageTag "Separate/Image"
734
735 CacheView
736 *image_view,
737 *separate_view;
738
739 Image
740 *separate_image;
741
742 MagickBooleanType
743 status;
744
745 MagickOffsetType
746 progress;
747
748 ssize_t
749 y;
750
751 /*
752 Initialize separate image attributes.
753 */
754 assert(image != (Image *) NULL);
755 assert(image->signature == MagickCoreSignature);
756 if (IsEventLogging() != MagickFalse)
757 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
758 assert(exception != (ExceptionInfo *) NULL);
759 assert(exception->signature == MagickCoreSignature);
760 separate_image=CloneImage(image,0,0,MagickTrue,exception);
761 if (separate_image == (Image *) NULL)
762 return((Image *) NULL);
763 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
764 {
765 separate_image=DestroyImage(separate_image);
766 return((Image *) NULL);
767 }
768 separate_image->alpha_trait=UndefinedPixelTrait;
769 (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
770 separate_image->gamma=image->gamma;
771 /*
772 Separate image.
773 */
774 status=MagickTrue;
775 progress=0;
776 image_view=AcquireVirtualCacheView(image,exception);
777 separate_view=AcquireAuthenticCacheView(separate_image,exception);
778#if defined(MAGICKCORE_OPENMP_SUPPORT)
779 #pragma omp parallel for schedule(static) shared(progress,status) \
780 magick_number_threads(image,image,image->rows,2)
781#endif
782 for (y=0; y < (ssize_t) image->rows; y++)
783 {
784 const Quantum
785 *magick_restrict p;
786
787 Quantum
788 *magick_restrict q;
789
790 ssize_t
791 x;
792
793 if (status == MagickFalse)
794 continue;
795 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
796 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
797 exception);
798 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
799 {
800 status=MagickFalse;
801 continue;
802 }
803 for (x=0; x < (ssize_t) image->columns; x++)
804 {
805 ssize_t
806 i;
807
808 SetPixelChannel(separate_image,GrayPixelChannel,(Quantum) 0,q);
809 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
810 {
811 PixelChannel channel = GetPixelChannelChannel(image,i);
812 PixelTrait traits = GetPixelChannelTraits(image,channel);
813 if ((traits == UndefinedPixelTrait) ||
814 (GetChannelBit(channel_type,channel) == 0))
815 continue;
816 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
817 }
818 p+=(ptrdiff_t) GetPixelChannels(image);
819 q+=(ptrdiff_t) GetPixelChannels(separate_image);
820 }
821 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
822 status=MagickFalse;
823 if (image->progress_monitor != (MagickProgressMonitor) NULL)
824 {
825 MagickBooleanType
826 proceed;
827
828#if defined(MAGICKCORE_OPENMP_SUPPORT)
829 #pragma omp atomic
830#endif
831 progress++;
832 proceed=SetImageProgress(image,SeparateImageTag,progress,image->rows);
833 if (proceed == MagickFalse)
834 status=MagickFalse;
835 }
836 }
837 separate_view=DestroyCacheView(separate_view);
838 image_view=DestroyCacheView(image_view);
839 (void) SetImageChannelMask(separate_image,AllChannels);
840 if (status == MagickFalse)
841 separate_image=DestroyImage(separate_image);
842 return(separate_image);
843}
844
845/*
846%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
847% %
848% %
849% %
850% S e p a r a t e I m a g e s %
851% %
852% %
853% %
854%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855%
856% SeparateImages() returns a separate grayscale image for each channel
857% specified.
858%
859% The format of the SeparateImages method is:
860%
861% Image *SeparateImages(const Image *image,ExceptionInfo *exception)
862%
863% A description of each parameter follows:
864%
865% o image: the image.
866%
867% o exception: return any errors or warnings in this structure.
868%
869*/
870MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
871{
872 Image
873 *images,
874 *separate_image;
875
876 ssize_t
877 i;
878
879 assert(image != (Image *) NULL);
880 assert(image->signature == MagickCoreSignature);
881 if (IsEventLogging() != MagickFalse)
882 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
883 images=NewImageList();
884 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
885 {
886 PixelChannel channel = GetPixelChannelChannel(image,i);
887 PixelTrait traits = GetPixelChannelTraits(image,channel);
888 if ((traits == UndefinedPixelTrait) || ((traits & UpdatePixelTrait) == 0))
889 continue;
890 separate_image=SeparateImage(image,(ChannelType)
891 (MagickLLConstant(1) << channel),exception);
892 if (separate_image != (Image *) NULL)
893 AppendImageToList(&images,separate_image);
894 }
895 if (images == (Image *) NULL)
896 images=SeparateImage(image,UndefinedChannel,exception);
897 return(images);
898}
899
900/*
901%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
902% %
903% %
904% %
905% S e t I m a g e A l p h a C h a n n e l %
906% %
907% %
908% %
909%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
910%
911% SetImageAlphaChannel() activates, deactivates, resets, or sets the alpha
912% channel.
913%
914% The format of the SetImageAlphaChannel method is:
915%
916% MagickBooleanType SetImageAlphaChannel(Image *image,
917% const AlphaChannelOption alpha_type,ExceptionInfo *exception)
918%
919% A description of each parameter follows:
920%
921% o image: the image.
922%
923% o alpha_type: The alpha channel type: ActivateAlphaChannel,
924% AssociateAlphaChannel, CopyAlphaChannel, DeactivateAlphaChannel,
925% DisassociateAlphaChannel, ExtractAlphaChannel, OffAlphaChannel,
926% OnAlphaChannel, OpaqueAlphaChannel, SetAlphaChannel, ShapeAlphaChannel,
927% and TransparentAlphaChannel.
928%
929% o exception: return any errors or warnings in this structure.
930%
931*/
932
933static inline void FlattenPixelInfo(const Image *image,const PixelInfo *p,
934 const double alpha,const Quantum *q,const double beta,Quantum *composite)
935{
936 double
937 Da,
938 gamma,
939 Sa;
940
941 ssize_t
942 i;
943
944 /*
945 Compose pixel p over pixel q with the given alpha.
946 */
947 Sa=QuantumScale*alpha;
948 Da=QuantumScale*beta,
949 gamma=Sa*(-Da)+Sa+Da;
950 gamma=MagickSafeReciprocal(gamma);
951 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
952 {
953 PixelChannel channel = GetPixelChannelChannel(image,i);
954 PixelTrait traits = GetPixelChannelTraits(image,channel);
955 if (traits == UndefinedPixelTrait)
956 continue;
957 switch (channel)
958 {
959 case RedPixelChannel:
960 {
961 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
962 (double) p->red,alpha));
963 break;
964 }
965 case GreenPixelChannel:
966 {
967 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
968 (double) p->green,alpha));
969 break;
970 }
971 case BluePixelChannel:
972 {
973 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
974 (double) p->blue,alpha));
975 break;
976 }
977 case BlackPixelChannel:
978 {
979 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
980 (double) p->black,alpha));
981 break;
982 }
983 case AlphaPixelChannel:
984 {
985 composite[i]=ClampToQuantum((double) QuantumRange*(Sa*(-Da)+Sa+Da));
986 break;
987 }
988 default:
989 break;
990 }
991 }
992}
993
994MagickExport MagickBooleanType SetImageAlphaChannel(Image *image,
995 const AlphaChannelOption alpha_type,ExceptionInfo *exception)
996{
997 CacheView
998 *image_view;
999
1000 MagickBooleanType
1001 status;
1002
1003 ssize_t
1004 y;
1005
1006 assert(image != (Image *) NULL);
1007 if (IsEventLogging() != MagickFalse)
1008 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1009 assert(image->signature == MagickCoreSignature);
1010 status=MagickTrue;
1011 switch (alpha_type)
1012 {
1013 case ActivateAlphaChannel:
1014 {
1015 if ((image->alpha_trait & BlendPixelTrait) != 0)
1016 return(status);
1017 image->alpha_trait=BlendPixelTrait;
1018 break;
1019 }
1020 case AssociateAlphaChannel:
1021 {
1022 /*
1023 Associate alpha.
1024 */
1025 status=SetImageStorageClass(image,DirectClass,exception);
1026 if (status == MagickFalse)
1027 break;
1028 image_view=AcquireAuthenticCacheView(image,exception);
1029#if defined(MAGICKCORE_OPENMP_SUPPORT)
1030 #pragma omp parallel for schedule(static) shared(status) \
1031 magick_number_threads(image,image,image->rows,2)
1032#endif
1033 for (y=0; y < (ssize_t) image->rows; y++)
1034 {
1035 Quantum
1036 *magick_restrict q;
1037
1038 ssize_t
1039 x;
1040
1041 if (status == MagickFalse)
1042 continue;
1043 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1044 exception);
1045 if (q == (Quantum *) NULL)
1046 {
1047 status=MagickFalse;
1048 continue;
1049 }
1050 for (x=0; x < (ssize_t) image->columns; x++)
1051 {
1052 double
1053 gamma;
1054
1055 ssize_t
1056 i;
1057
1058 gamma=QuantumScale*(double) GetPixelAlpha(image,q);
1059 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1060 {
1061 PixelChannel channel = GetPixelChannelChannel(image,i);
1062 PixelTrait traits = GetPixelChannelTraits(image,channel);
1063 if (channel == AlphaPixelChannel)
1064 continue;
1065 if ((traits & UpdatePixelTrait) == 0)
1066 continue;
1067 q[i]=ClampToQuantum(gamma*(double) q[i]);
1068 }
1069 q+=(ptrdiff_t) GetPixelChannels(image);
1070 }
1071 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1072 status=MagickFalse;
1073 }
1074 image_view=DestroyCacheView(image_view);
1075 image->alpha_trait=CopyPixelTrait;
1076 return(status);
1077 }
1078 case BackgroundAlphaChannel:
1079 {
1080 /*
1081 Set transparent pixels to background color.
1082 */
1083 if ((image->alpha_trait & BlendPixelTrait) == 0)
1084 break;
1085 status=SetImageStorageClass(image,DirectClass,exception);
1086 if (status == MagickFalse)
1087 break;
1088 image_view=AcquireAuthenticCacheView(image,exception);
1089#if defined(MAGICKCORE_OPENMP_SUPPORT)
1090 #pragma omp parallel for schedule(static) shared(status) \
1091 magick_number_threads(image,image,image->rows,2)
1092#endif
1093 for (y=0; y < (ssize_t) image->rows; y++)
1094 {
1095 Quantum
1096 *magick_restrict q;
1097
1098 ssize_t
1099 x;
1100
1101 if (status == MagickFalse)
1102 continue;
1103 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1104 exception);
1105 if (q == (Quantum *) NULL)
1106 {
1107 status=MagickFalse;
1108 continue;
1109 }
1110 for (x=0; x < (ssize_t) image->columns; x++)
1111 {
1112 if (GetPixelAlpha(image,q) == TransparentAlpha)
1113 {
1114 SetPixelViaPixelInfo(image,&image->background_color,q);
1115 SetPixelChannel(image,AlphaPixelChannel,TransparentAlpha,q);
1116 }
1117 q+=(ptrdiff_t) GetPixelChannels(image);
1118 }
1119 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1120 status=MagickFalse;
1121 }
1122 image_view=DestroyCacheView(image_view);
1123 return(status);
1124 }
1125 case CopyAlphaChannel:
1126 {
1127 image->alpha_trait=UpdatePixelTrait;
1128 status=CompositeImage(image,image,IntensityCompositeOp,MagickTrue,0,0,
1129 exception);
1130 break;
1131 }
1132 case DeactivateAlphaChannel:
1133 {
1134 if ((image->alpha_trait & BlendPixelTrait) == 0)
1135 status=SetImageAlpha(image,OpaqueAlpha,exception);
1136 image->alpha_trait=CopyPixelTrait;
1137 break;
1138 }
1139 case DisassociateAlphaChannel:
1140 {
1141 /*
1142 Disassociate alpha.
1143 */
1144 status=SetImageStorageClass(image,DirectClass,exception);
1145 if (status == MagickFalse)
1146 break;
1147 image->alpha_trait=BlendPixelTrait;
1148 image_view=AcquireAuthenticCacheView(image,exception);
1149#if defined(MAGICKCORE_OPENMP_SUPPORT)
1150 #pragma omp parallel for schedule(static) shared(status) \
1151 magick_number_threads(image,image,image->rows,2)
1152#endif
1153 for (y=0; y < (ssize_t) image->rows; y++)
1154 {
1155 Quantum
1156 *magick_restrict q;
1157
1158 ssize_t
1159 x;
1160
1161 if (status == MagickFalse)
1162 continue;
1163 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1164 exception);
1165 if (q == (Quantum *) NULL)
1166 {
1167 status=MagickFalse;
1168 continue;
1169 }
1170 for (x=0; x < (ssize_t) image->columns; x++)
1171 {
1172 double
1173 gamma,
1174 Sa;
1175
1176 ssize_t
1177 i;
1178
1179 Sa=QuantumScale*(double) GetPixelAlpha(image,q);
1180 gamma=MagickSafeReciprocal(Sa);
1181 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1182 {
1183 PixelChannel channel = GetPixelChannelChannel(image,i);
1184 PixelTrait traits = GetPixelChannelTraits(image,channel);
1185 if (channel == AlphaPixelChannel)
1186 continue;
1187 if ((traits & UpdatePixelTrait) == 0)
1188 continue;
1189 q[i]=ClampToQuantum(gamma*(double) q[i]);
1190 }
1191 q+=(ptrdiff_t) GetPixelChannels(image);
1192 }
1193 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1194 status=MagickFalse;
1195 }
1196 image_view=DestroyCacheView(image_view);
1197 image->alpha_trait=UndefinedPixelTrait;
1198 return(status);
1199 }
1200 case DiscreteAlphaChannel:
1201 {
1202 if ((image->alpha_trait & BlendPixelTrait) == 0)
1203 status=SetImageAlpha(image,OpaqueAlpha,exception);
1204 image->alpha_trait=UpdatePixelTrait;
1205 break;
1206 }
1207 case ExtractAlphaChannel:
1208 {
1209 status=CompositeImage(image,image,AlphaCompositeOp,MagickTrue,0,0,
1210 exception);
1211 image->alpha_trait=UndefinedPixelTrait;
1212 break;
1213 }
1214 case OffAlphaChannel:
1215 {
1216 if ((image->alpha_trait & BlendPixelTrait) == 0)
1217 return(status);
1218 image->alpha_trait=UndefinedPixelTrait;
1219 break;
1220 }
1221 case OffIfOpaqueAlphaChannel:
1222 {
1223 MagickBooleanType
1224 opaque = MagickTrue;
1225
1226 /*
1227 Remove opaque alpha channel.
1228 */
1229 if ((image->alpha_trait & BlendPixelTrait) == 0)
1230 break;
1231 image_view=AcquireVirtualCacheView(image,exception);
1232#if defined(MAGICKCORE_OPENMP_SUPPORT)
1233 #pragma omp parallel for schedule(static) shared(opaque,status) \
1234 magick_number_threads(image,image,image->rows,2)
1235#endif
1236 for (y=0; y < (ssize_t) image->rows; y++)
1237 {
1238 const Quantum
1239 *magick_restrict p;
1240
1241 ssize_t
1242 x;
1243
1244 if ((status == MagickFalse) || (opaque == MagickFalse))
1245 continue;
1246 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1247 if (p == (const Quantum *) NULL)
1248 {
1249 status=MagickFalse;
1250 continue;
1251 }
1252 for (x=0; x < (ssize_t) image->columns; x++)
1253 {
1254 if (GetPixelAlpha(image,p) != OpaqueAlpha)
1255 {
1256 opaque=MagickFalse;
1257 break;
1258 }
1259 p+=(ptrdiff_t) GetPixelChannels(image);
1260 }
1261 }
1262 image_view=DestroyCacheView(image_view);
1263 if (opaque != MagickFalse)
1264 image->alpha_trait=UndefinedPixelTrait;
1265 break;
1266 }
1267 case OnAlphaChannel:
1268 {
1269 if ((image->alpha_trait & BlendPixelTrait) == 0)
1270 status=SetImageAlpha(image,OpaqueAlpha,exception);
1271 image->alpha_trait=BlendPixelTrait;
1272 break;
1273 }
1274 case OpaqueAlphaChannel:
1275 {
1276 status=SetImageAlpha(image,OpaqueAlpha,exception);
1277 break;
1278 }
1279 case RemoveAlphaChannel:
1280 {
1281 /*
1282 Remove transparency.
1283 */
1284 if ((image->alpha_trait & BlendPixelTrait) == 0)
1285 break;
1286 status=SetImageStorageClass(image,DirectClass,exception);
1287 if (status == MagickFalse)
1288 break;
1289 image_view=AcquireAuthenticCacheView(image,exception);
1290#if defined(MAGICKCORE_OPENMP_SUPPORT)
1291 #pragma omp parallel for schedule(static) shared(status) \
1292 magick_number_threads(image,image,image->rows,2)
1293#endif
1294 for (y=0; y < (ssize_t) image->rows; y++)
1295 {
1296 Quantum
1297 *magick_restrict q;
1298
1299 ssize_t
1300 x;
1301
1302 if (status == MagickFalse)
1303 continue;
1304 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1305 exception);
1306 if (q == (Quantum *) NULL)
1307 {
1308 status=MagickFalse;
1309 continue;
1310 }
1311 for (x=0; x < (ssize_t) image->columns; x++)
1312 {
1313 FlattenPixelInfo(image,&image->background_color,
1314 image->background_color.alpha,q,(double) GetPixelAlpha(image,q),q);
1315 q+=(ptrdiff_t) GetPixelChannels(image);
1316 }
1317 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1318 status=MagickFalse;
1319 }
1320 image_view=DestroyCacheView(image_view);
1321 image->alpha_trait=image->background_color.alpha_trait;
1322 break;
1323 }
1324 case SetAlphaChannel:
1325 {
1326 if ((image->alpha_trait & BlendPixelTrait) == 0)
1327 status=SetImageAlpha(image,OpaqueAlpha,exception);
1328 break;
1329 }
1330 case ShapeAlphaChannel:
1331 {
1332 PixelInfo
1333 background;
1334
1335 /*
1336 Remove transparency.
1337 */
1338 ConformPixelInfo(image,&image->background_color,&background,exception);
1339 background.alpha_trait=BlendPixelTrait;
1340 image->alpha_trait=BlendPixelTrait;
1341 status=SetImageStorageClass(image,DirectClass,exception);
1342 if (status == MagickFalse)
1343 break;
1344 image_view=AcquireAuthenticCacheView(image,exception);
1345#if defined(MAGICKCORE_OPENMP_SUPPORT)
1346 #pragma omp parallel for schedule(static) shared(status) \
1347 magick_number_threads(image,image,image->rows,2)
1348#endif
1349 for (y=0; y < (ssize_t) image->rows; y++)
1350 {
1351 PixelInfo
1352 pixel;
1353
1354 Quantum
1355 *magick_restrict q;
1356
1357 ssize_t
1358 x;
1359
1360 if (status == MagickFalse)
1361 continue;
1362 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1363 exception);
1364 if (q == (Quantum *) NULL)
1365 {
1366 status=MagickFalse;
1367 continue;
1368 }
1369 pixel=background;
1370 for (x=0; x < (ssize_t) image->columns; x++)
1371 {
1372 pixel.alpha=GetPixelIntensity(image,q);
1373 SetPixelViaPixelInfo(image,&pixel,q);
1374 q+=(ptrdiff_t) GetPixelChannels(image);
1375 }
1376 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1377 status=MagickFalse;
1378 }
1379 image_view=DestroyCacheView(image_view);
1380 break;
1381 }
1382 case TransparentAlphaChannel:
1383 {
1384 status=SetImageAlpha(image,TransparentAlpha,exception);
1385 break;
1386 }
1387 case UndefinedAlphaChannel:
1388 break;
1389 }
1390 if (status == MagickFalse)
1391 return(status);
1392 (void) SetPixelChannelMask(image,image->channel_mask);
1393 return(SyncImagePixelCache(image,exception));
1394}