MagickCore 7.1.2
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/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"
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 return(combine_image);
664}
665
666/*
667%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
668% %
669% %
670% %
671% G e t I m a g e A l p h a C h a n n e l %
672% %
673% %
674% %
675%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
676%
677% GetImageAlphaChannel() returns MagickFalse if the image alpha channel is
678% not activated. That is, the image is RGB rather than RGBA or CMYK rather
679% than CMYKA.
680%
681% The format of the GetImageAlphaChannel method is:
682%
683% MagickBooleanType GetImageAlphaChannel(const Image *image)
684%
685% A description of each parameter follows:
686%
687% o image: the image.
688%
689*/
690MagickExport MagickBooleanType GetImageAlphaChannel(const Image *image)
691{
692 assert(image != (const Image *) NULL);
693 if (IsEventLogging() != MagickFalse)
694 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
695 assert(image->signature == MagickCoreSignature);
696 return(image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse);
697}
698
699/*
700%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
701% %
702% %
703% %
704% S e p a r a t e I m a g e %
705% %
706% %
707% %
708%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
709%
710% SeparateImage() separates a channel from the image and returns it as a
711% grayscale image.
712%
713% The format of the SeparateImage method is:
714%
715% Image *SeparateImage(const Image *image,const ChannelType channel,
716% ExceptionInfo *exception)
717%
718% A description of each parameter follows:
719%
720% o image: the image.
721%
722% o channel: the image channel.
723%
724% o exception: return any errors or warnings in this structure.
725%
726*/
727MagickExport Image *SeparateImage(const Image *image,
728 const ChannelType channel_type,ExceptionInfo *exception)
729{
730#define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
731#define SeparateImageTag "Separate/Image"
732
733 CacheView
734 *image_view,
735 *separate_view;
736
737 Image
738 *separate_image;
739
740 MagickBooleanType
741 status;
742
743 MagickOffsetType
744 progress;
745
746 ssize_t
747 y;
748
749 /*
750 Initialize separate image attributes.
751 */
752 assert(image != (Image *) NULL);
753 assert(image->signature == MagickCoreSignature);
754 if (IsEventLogging() != MagickFalse)
755 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
756 assert(exception != (ExceptionInfo *) NULL);
757 assert(exception->signature == MagickCoreSignature);
758 separate_image=CloneImage(image,0,0,MagickTrue,exception);
759 if (separate_image == (Image *) NULL)
760 return((Image *) NULL);
761 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
762 {
763 separate_image=DestroyImage(separate_image);
764 return((Image *) NULL);
765 }
766 separate_image->alpha_trait=UndefinedPixelTrait;
767 (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
768 separate_image->gamma=image->gamma;
769 /*
770 Separate image.
771 */
772 status=MagickTrue;
773 progress=0;
774 image_view=AcquireVirtualCacheView(image,exception);
775 separate_view=AcquireAuthenticCacheView(separate_image,exception);
776#if defined(MAGICKCORE_OPENMP_SUPPORT)
777 #pragma omp parallel for schedule(static) shared(progress,status) \
778 magick_number_threads(image,image,image->rows,2)
779#endif
780 for (y=0; y < (ssize_t) image->rows; y++)
781 {
782 const Quantum
783 *magick_restrict p;
784
785 Quantum
786 *magick_restrict q;
787
788 ssize_t
789 x;
790
791 if (status == MagickFalse)
792 continue;
793 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
794 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
795 exception);
796 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
797 {
798 status=MagickFalse;
799 continue;
800 }
801 for (x=0; x < (ssize_t) image->columns; x++)
802 {
803 ssize_t
804 i;
805
806 SetPixelChannel(separate_image,GrayPixelChannel,(Quantum) 0,q);
807 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
808 {
809 PixelChannel channel = GetPixelChannelChannel(image,i);
810 PixelTrait traits = GetPixelChannelTraits(image,channel);
811 if ((traits == UndefinedPixelTrait) ||
812 (GetChannelBit(channel_type,channel) == 0))
813 continue;
814 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
815 }
816 p+=(ptrdiff_t) GetPixelChannels(image);
817 q+=(ptrdiff_t) GetPixelChannels(separate_image);
818 }
819 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
820 status=MagickFalse;
821 if (image->progress_monitor != (MagickProgressMonitor) NULL)
822 {
823 MagickBooleanType
824 proceed;
825
826#if defined(MAGICKCORE_OPENMP_SUPPORT)
827 #pragma omp atomic
828#endif
829 progress++;
830 proceed=SetImageProgress(image,SeparateImageTag,progress,image->rows);
831 if (proceed == MagickFalse)
832 status=MagickFalse;
833 }
834 }
835 separate_view=DestroyCacheView(separate_view);
836 image_view=DestroyCacheView(image_view);
837 (void) SetImageChannelMask(separate_image,AllChannels);
838 if (status == MagickFalse)
839 separate_image=DestroyImage(separate_image);
840 return(separate_image);
841}
842
843/*
844%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
845% %
846% %
847% %
848% S e p a r a t e I m a g e s %
849% %
850% %
851% %
852%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
853%
854% SeparateImages() returns a separate grayscale image for each channel
855% specified.
856%
857% The format of the SeparateImages method is:
858%
859% Image *SeparateImages(const Image *image,ExceptionInfo *exception)
860%
861% A description of each parameter follows:
862%
863% o image: the image.
864%
865% o exception: return any errors or warnings in this structure.
866%
867*/
868MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
869{
870 Image
871 *images,
872 *separate_image;
873
874 ssize_t
875 i;
876
877 assert(image != (Image *) NULL);
878 assert(image->signature == MagickCoreSignature);
879 if (IsEventLogging() != MagickFalse)
880 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
881 images=NewImageList();
882 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
883 {
884 PixelChannel channel = GetPixelChannelChannel(image,i);
885 PixelTrait traits = GetPixelChannelTraits(image,channel);
886 if ((traits == UndefinedPixelTrait) || ((traits & UpdatePixelTrait) == 0))
887 continue;
888 separate_image=SeparateImage(image,(ChannelType)
889 (MagickLLConstant(1) << channel),exception);
890 if (separate_image != (Image *) NULL)
891 AppendImageToList(&images,separate_image);
892 }
893 if (images == (Image *) NULL)
894 images=SeparateImage(image,UndefinedChannel,exception);
895 return(images);
896}
897
898/*
899%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
900% %
901% %
902% %
903% S e t I m a g e A l p h a C h a n n e l %
904% %
905% %
906% %
907%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
908%
909% SetImageAlphaChannel() activates, deactivates, resets, or sets the alpha
910% channel.
911%
912% The format of the SetImageAlphaChannel method is:
913%
914% MagickBooleanType SetImageAlphaChannel(Image *image,
915% const AlphaChannelOption alpha_type,ExceptionInfo *exception)
916%
917% A description of each parameter follows:
918%
919% o image: the image.
920%
921% o alpha_type: The alpha channel type: ActivateAlphaChannel,
922% AssociateAlphaChannel, CopyAlphaChannel, DeactivateAlphaChannel,
923% DisassociateAlphaChannel, ExtractAlphaChannel, OffAlphaChannel,
924% OnAlphaChannel, OpaqueAlphaChannel, SetAlphaChannel, ShapeAlphaChannel,
925% and TransparentAlphaChannel.
926%
927% o exception: return any errors or warnings in this structure.
928%
929*/
930
931static inline void FlattenPixelInfo(const Image *image,const PixelInfo *p,
932 const double alpha,const Quantum *q,const double beta,Quantum *composite)
933{
934 double
935 Da,
936 gamma,
937 Sa;
938
939 ssize_t
940 i;
941
942 /*
943 Compose pixel p over pixel q with the given alpha.
944 */
945 Sa=QuantumScale*alpha;
946 Da=QuantumScale*beta,
947 gamma=Sa*(-Da)+Sa+Da;
948 gamma=MagickSafeReciprocal(gamma);
949 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
950 {
951 PixelChannel channel = GetPixelChannelChannel(image,i);
952 PixelTrait traits = GetPixelChannelTraits(image,channel);
953 if (traits == UndefinedPixelTrait)
954 continue;
955 switch (channel)
956 {
957 case RedPixelChannel:
958 {
959 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
960 (double) p->red,alpha));
961 break;
962 }
963 case GreenPixelChannel:
964 {
965 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
966 (double) p->green,alpha));
967 break;
968 }
969 case BluePixelChannel:
970 {
971 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
972 (double) p->blue,alpha));
973 break;
974 }
975 case BlackPixelChannel:
976 {
977 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
978 (double) p->black,alpha));
979 break;
980 }
981 case AlphaPixelChannel:
982 {
983 composite[i]=ClampToQuantum((double) QuantumRange*(Sa*(-Da)+Sa+Da));
984 break;
985 }
986 default:
987 break;
988 }
989 }
990}
991
992MagickExport MagickBooleanType SetImageAlphaChannel(Image *image,
993 const AlphaChannelOption alpha_type,ExceptionInfo *exception)
994{
995 CacheView
996 *image_view;
997
998 MagickBooleanType
999 status;
1000
1001 ssize_t
1002 y;
1003
1004 assert(image != (Image *) NULL);
1005 if (IsEventLogging() != MagickFalse)
1006 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1007 assert(image->signature == MagickCoreSignature);
1008 status=MagickTrue;
1009 switch (alpha_type)
1010 {
1011 case ActivateAlphaChannel:
1012 {
1013 if ((image->alpha_trait & BlendPixelTrait) != 0)
1014 return(status);
1015 image->alpha_trait=BlendPixelTrait;
1016 break;
1017 }
1018 case AssociateAlphaChannel:
1019 {
1020 /*
1021 Associate alpha.
1022 */
1023 status=SetImageStorageClass(image,DirectClass,exception);
1024 if (status == MagickFalse)
1025 break;
1026 image_view=AcquireAuthenticCacheView(image,exception);
1027#if defined(MAGICKCORE_OPENMP_SUPPORT)
1028 #pragma omp parallel for schedule(static) shared(status) \
1029 magick_number_threads(image,image,image->rows,2)
1030#endif
1031 for (y=0; y < (ssize_t) image->rows; y++)
1032 {
1033 Quantum
1034 *magick_restrict q;
1035
1036 ssize_t
1037 x;
1038
1039 if (status == MagickFalse)
1040 continue;
1041 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1042 exception);
1043 if (q == (Quantum *) NULL)
1044 {
1045 status=MagickFalse;
1046 continue;
1047 }
1048 for (x=0; x < (ssize_t) image->columns; x++)
1049 {
1050 double
1051 gamma;
1052
1053 ssize_t
1054 i;
1055
1056 gamma=QuantumScale*(double) GetPixelAlpha(image,q);
1057 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1058 {
1059 PixelChannel channel = GetPixelChannelChannel(image,i);
1060 PixelTrait traits = GetPixelChannelTraits(image,channel);
1061 if (channel == AlphaPixelChannel)
1062 continue;
1063 if ((traits & UpdatePixelTrait) == 0)
1064 continue;
1065 q[i]=ClampToQuantum(gamma*(double) q[i]);
1066 }
1067 q+=(ptrdiff_t) GetPixelChannels(image);
1068 }
1069 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1070 status=MagickFalse;
1071 }
1072 image_view=DestroyCacheView(image_view);
1073 image->alpha_trait=CopyPixelTrait;
1074 return(status);
1075 }
1076 case BackgroundAlphaChannel:
1077 {
1078 /*
1079 Set transparent pixels to background color.
1080 */
1081 if ((image->alpha_trait & BlendPixelTrait) == 0)
1082 break;
1083 status=SetImageStorageClass(image,DirectClass,exception);
1084 if (status == MagickFalse)
1085 break;
1086 image_view=AcquireAuthenticCacheView(image,exception);
1087#if defined(MAGICKCORE_OPENMP_SUPPORT)
1088 #pragma omp parallel for schedule(static) shared(status) \
1089 magick_number_threads(image,image,image->rows,2)
1090#endif
1091 for (y=0; y < (ssize_t) image->rows; y++)
1092 {
1093 Quantum
1094 *magick_restrict q;
1095
1096 ssize_t
1097 x;
1098
1099 if (status == MagickFalse)
1100 continue;
1101 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1102 exception);
1103 if (q == (Quantum *) NULL)
1104 {
1105 status=MagickFalse;
1106 continue;
1107 }
1108 for (x=0; x < (ssize_t) image->columns; x++)
1109 {
1110 if (GetPixelAlpha(image,q) == TransparentAlpha)
1111 {
1112 SetPixelViaPixelInfo(image,&image->background_color,q);
1113 SetPixelChannel(image,AlphaPixelChannel,TransparentAlpha,q);
1114 }
1115 q+=(ptrdiff_t) GetPixelChannels(image);
1116 }
1117 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1118 status=MagickFalse;
1119 }
1120 image_view=DestroyCacheView(image_view);
1121 return(status);
1122 }
1123 case CopyAlphaChannel:
1124 {
1125 image->alpha_trait=UpdatePixelTrait;
1126 status=CompositeImage(image,image,IntensityCompositeOp,MagickTrue,0,0,
1127 exception);
1128 break;
1129 }
1130 case DeactivateAlphaChannel:
1131 {
1132 if ((image->alpha_trait & BlendPixelTrait) == 0)
1133 status=SetImageAlpha(image,OpaqueAlpha,exception);
1134 image->alpha_trait=CopyPixelTrait;
1135 break;
1136 }
1137 case DisassociateAlphaChannel:
1138 {
1139 /*
1140 Disassociate alpha.
1141 */
1142 status=SetImageStorageClass(image,DirectClass,exception);
1143 if (status == MagickFalse)
1144 break;
1145 image->alpha_trait=BlendPixelTrait;
1146 image_view=AcquireAuthenticCacheView(image,exception);
1147#if defined(MAGICKCORE_OPENMP_SUPPORT)
1148 #pragma omp parallel for schedule(static) shared(status) \
1149 magick_number_threads(image,image,image->rows,2)
1150#endif
1151 for (y=0; y < (ssize_t) image->rows; y++)
1152 {
1153 Quantum
1154 *magick_restrict q;
1155
1156 ssize_t
1157 x;
1158
1159 if (status == MagickFalse)
1160 continue;
1161 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1162 exception);
1163 if (q == (Quantum *) NULL)
1164 {
1165 status=MagickFalse;
1166 continue;
1167 }
1168 for (x=0; x < (ssize_t) image->columns; x++)
1169 {
1170 double
1171 gamma,
1172 Sa;
1173
1174 ssize_t
1175 i;
1176
1177 Sa=QuantumScale*(double) GetPixelAlpha(image,q);
1178 gamma=MagickSafeReciprocal(Sa);
1179 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1180 {
1181 PixelChannel channel = GetPixelChannelChannel(image,i);
1182 PixelTrait traits = GetPixelChannelTraits(image,channel);
1183 if (channel == AlphaPixelChannel)
1184 continue;
1185 if ((traits & UpdatePixelTrait) == 0)
1186 continue;
1187 q[i]=ClampToQuantum(gamma*(double) q[i]);
1188 }
1189 q+=(ptrdiff_t) GetPixelChannels(image);
1190 }
1191 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1192 status=MagickFalse;
1193 }
1194 image_view=DestroyCacheView(image_view);
1195 image->alpha_trait=UndefinedPixelTrait;
1196 return(status);
1197 }
1198 case DiscreteAlphaChannel:
1199 {
1200 if ((image->alpha_trait & BlendPixelTrait) == 0)
1201 status=SetImageAlpha(image,OpaqueAlpha,exception);
1202 image->alpha_trait=UpdatePixelTrait;
1203 break;
1204 }
1205 case ExtractAlphaChannel:
1206 {
1207 status=CompositeImage(image,image,AlphaCompositeOp,MagickTrue,0,0,
1208 exception);
1209 image->alpha_trait=UndefinedPixelTrait;
1210 break;
1211 }
1212 case OffAlphaChannel:
1213 {
1214 if ((image->alpha_trait & BlendPixelTrait) == 0)
1215 return(status);
1216 image->alpha_trait=UndefinedPixelTrait;
1217 break;
1218 }
1219 case OffIfOpaqueAlphaChannel:
1220 {
1221 MagickBooleanType
1222 opaque = MagickTrue;
1223
1224 /*
1225 Remove opaque alpha channel.
1226 */
1227 if ((image->alpha_trait & BlendPixelTrait) == 0)
1228 break;
1229 image_view=AcquireVirtualCacheView(image,exception);
1230#if defined(MAGICKCORE_OPENMP_SUPPORT)
1231 #pragma omp parallel for schedule(static) shared(opaque,status) \
1232 magick_number_threads(image,image,image->rows,2)
1233#endif
1234 for (y=0; y < (ssize_t) image->rows; y++)
1235 {
1236 const Quantum
1237 *magick_restrict p;
1238
1239 ssize_t
1240 x;
1241
1242 if ((status == MagickFalse) || (opaque == MagickFalse))
1243 continue;
1244 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1245 if (p == (const Quantum *) NULL)
1246 {
1247 status=MagickFalse;
1248 continue;
1249 }
1250 for (x=0; x < (ssize_t) image->columns; x++)
1251 {
1252 if (GetPixelAlpha(image,p) != OpaqueAlpha)
1253 {
1254 opaque=MagickFalse;
1255 break;
1256 }
1257 p+=(ptrdiff_t) GetPixelChannels(image);
1258 }
1259 }
1260 image_view=DestroyCacheView(image_view);
1261 if (opaque != MagickFalse)
1262 image->alpha_trait=UndefinedPixelTrait;
1263 break;
1264 }
1265 case OnAlphaChannel:
1266 {
1267 if ((image->alpha_trait & BlendPixelTrait) == 0)
1268 status=SetImageAlpha(image,OpaqueAlpha,exception);
1269 image->alpha_trait=BlendPixelTrait;
1270 break;
1271 }
1272 case OpaqueAlphaChannel:
1273 {
1274 status=SetImageAlpha(image,OpaqueAlpha,exception);
1275 break;
1276 }
1277 case RemoveAlphaChannel:
1278 {
1279 /*
1280 Remove transparency.
1281 */
1282 if ((image->alpha_trait & BlendPixelTrait) == 0)
1283 break;
1284 status=SetImageStorageClass(image,DirectClass,exception);
1285 if (status == MagickFalse)
1286 break;
1287 image_view=AcquireAuthenticCacheView(image,exception);
1288#if defined(MAGICKCORE_OPENMP_SUPPORT)
1289 #pragma omp parallel for schedule(static) shared(status) \
1290 magick_number_threads(image,image,image->rows,2)
1291#endif
1292 for (y=0; y < (ssize_t) image->rows; y++)
1293 {
1294 Quantum
1295 *magick_restrict q;
1296
1297 ssize_t
1298 x;
1299
1300 if (status == MagickFalse)
1301 continue;
1302 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1303 exception);
1304 if (q == (Quantum *) NULL)
1305 {
1306 status=MagickFalse;
1307 continue;
1308 }
1309 for (x=0; x < (ssize_t) image->columns; x++)
1310 {
1311 FlattenPixelInfo(image,&image->background_color,
1312 image->background_color.alpha,q,(double) GetPixelAlpha(image,q),q);
1313 q+=(ptrdiff_t) GetPixelChannels(image);
1314 }
1315 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1316 status=MagickFalse;
1317 }
1318 image_view=DestroyCacheView(image_view);
1319 image->alpha_trait=image->background_color.alpha_trait;
1320 break;
1321 }
1322 case SetAlphaChannel:
1323 {
1324 if ((image->alpha_trait & BlendPixelTrait) == 0)
1325 status=SetImageAlpha(image,OpaqueAlpha,exception);
1326 break;
1327 }
1328 case ShapeAlphaChannel:
1329 {
1330 PixelInfo
1331 background;
1332
1333 /*
1334 Remove transparency.
1335 */
1336 ConformPixelInfo(image,&image->background_color,&background,exception);
1337 background.alpha_trait=BlendPixelTrait;
1338 image->alpha_trait=BlendPixelTrait;
1339 status=SetImageStorageClass(image,DirectClass,exception);
1340 if (status == MagickFalse)
1341 break;
1342 image_view=AcquireAuthenticCacheView(image,exception);
1343#if defined(MAGICKCORE_OPENMP_SUPPORT)
1344 #pragma omp parallel for schedule(static) shared(status) \
1345 magick_number_threads(image,image,image->rows,2)
1346#endif
1347 for (y=0; y < (ssize_t) image->rows; y++)
1348 {
1349 PixelInfo
1350 pixel;
1351
1352 Quantum
1353 *magick_restrict q;
1354
1355 ssize_t
1356 x;
1357
1358 if (status == MagickFalse)
1359 continue;
1360 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1361 exception);
1362 if (q == (Quantum *) NULL)
1363 {
1364 status=MagickFalse;
1365 continue;
1366 }
1367 pixel=background;
1368 for (x=0; x < (ssize_t) image->columns; x++)
1369 {
1370 pixel.alpha=GetPixelIntensity(image,q);
1371 SetPixelViaPixelInfo(image,&pixel,q);
1372 q+=(ptrdiff_t) GetPixelChannels(image);
1373 }
1374 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1375 status=MagickFalse;
1376 }
1377 image_view=DestroyCacheView(image_view);
1378 break;
1379 }
1380 case TransparentAlphaChannel:
1381 {
1382 status=SetImageAlpha(image,TransparentAlpha,exception);
1383 break;
1384 }
1385 case UndefinedAlphaChannel:
1386 break;
1387 }
1388 if (status == MagickFalse)
1389 return(status);
1390 (void) SetPixelChannelMask(image,image->channel_mask);
1391 return(SyncImagePixelCache(image,exception));
1392}