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