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