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-2016 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 % http://www.imagemagick.org/script/license.php %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 %
38 */
39
40 /*
41 Include declarations.
42 */
43 #include "MagickCore/studio.h"
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,4) shared(status) \
142 magick_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 destination_channel=RedPixelChannel;
243 channel_mask=UndefinedChannel;
244 pixel=0.0;
245 p=(char *) expression;
246 GetNextToken(p,&p,MagickPathExtent,token);
247 channel_op=ExtractChannelOp;
248 for (channels=0; *token != '\0'; )
249 {
250 ssize_t
251 i;
252
253 /*
254 Interpret channel expression.
255 */
256 switch (*token)
257 {
258 case ',':
259 {
260 GetNextToken(p,&p,MagickPathExtent,token);
261 break;
262 }
263 case '|':
264 {
265 if (GetNextImageInList(source_image) != (Image *) NULL)
266 source_image=GetNextImageInList(source_image);
267 else
268 source_image=GetFirstImageInList(source_image);
269 GetNextToken(p,&p,MagickPathExtent,token);
270 break;
271 }
272 case ';':
273 {
274 Image
275 *canvas;
276
277 (void) SetPixelChannelMask(destination_image,channel_mask);
278 if ((channel_op == ExtractChannelOp) && (channels == 1))
279 (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
280 status=SetImageStorageClass(destination_image,DirectClass,exception);
281 if (status == MagickFalse)
282 {
283 destination_image=DestroyImageList(destination_image);
284 return(destination_image);
285 }
286 canvas=CloneImage(source_image,0,0,MagickTrue,exception);
287 if (canvas == (Image *) NULL)
288 {
289 destination_image=DestroyImageList(destination_image);
290 return(destination_image);
291 }
292 AppendImageToList(&destination_image,canvas);
293 destination_image=GetLastImageInList(destination_image);
294 GetNextToken(p,&p,MagickPathExtent,token);
295 channels=0;
296 destination_channel=RedPixelChannel;
297 channel_mask=UndefinedChannel;
298 break;
299 }
300 default:
301 break;
302 }
303 i=ParsePixelChannelOption(token);
304 if (i < 0)
305 {
306 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
307 "UnrecognizedChannelType","`%s'",token);
308 destination_image=DestroyImageList(destination_image);
309 return(destination_image);
310 }
311 source_channel=(PixelChannel) i;
312 channel_op=ExtractChannelOp;
313 GetNextToken(p,&p,MagickPathExtent,token);
314 if (*token == '<')
315 {
316 channel_op=ExchangeChannelOp;
317 GetNextToken(p,&p,MagickPathExtent,token);
318 }
319 if (*token == '=')
320 {
321 if (channel_op != ExchangeChannelOp)
322 channel_op=AssignChannelOp;
323 GetNextToken(p,&p,MagickPathExtent,token);
324 }
325 if (*token == '>')
326 {
327 if (channel_op != ExchangeChannelOp)
328 channel_op=TransferChannelOp;
329 GetNextToken(p,&p,MagickPathExtent,token);
330 }
331 switch (channel_op)
332 {
333 case AssignChannelOp:
334 {
335 pixel=StringToDoubleInterval(token,(double) QuantumRange+1.0);
336 GetNextToken(p,&p,MagickPathExtent,token);
337 break;
338 }
339 case ExchangeChannelOp:
340 case TransferChannelOp:
341 {
342 i=ParsePixelChannelOption(token);
343 if (i < 0)
344 {
345 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
346 "UnrecognizedChannelType","`%s'",token);
347 destination_image=DestroyImageList(destination_image);
348 return(destination_image);
349 }
350 destination_channel=(PixelChannel) i;
351 switch (destination_channel)
352 {
353 case RedPixelChannel:
354 case GreenPixelChannel:
355 case BluePixelChannel:
356 case BlackPixelChannel:
357 case IndexPixelChannel:
358 break;
359 case AlphaPixelChannel:
360 {
361 destination_image->alpha_trait=BlendPixelTrait;
362 break;
363 }
364 case ReadMaskPixelChannel:
365 {
366 destination_image->read_mask=MagickTrue;
367 break;
368 }
369 case WriteMaskPixelChannel:
370 {
371 destination_image->write_mask=MagickTrue;
372 break;
373 }
374 case MetaPixelChannel:
375 default:
376 {
377 (void) SetPixelMetaChannels(destination_image,(size_t) (i-
378 GetPixelChannels(destination_image)+1),exception);
379 break;
380 }
381 }
382 channel_mask=(ChannelType) (channel_mask | ParseChannelOption(token));
383 if (((channels >= 1) || (destination_channel >= 1)) &&
384 (IsGrayColorspace(destination_image->colorspace) != MagickFalse))
385 (void) SetImageColorspace(destination_image,sRGBColorspace,exception);
386 GetNextToken(p,&p,MagickPathExtent,token);
387 break;
388 }
389 default:
390 break;
391 }
392 status=ChannelImage(destination_image,destination_channel,channel_op,
393 source_image,source_channel,ClampToQuantum(pixel),exception);
394 if (status == MagickFalse)
395 {
396 destination_image=DestroyImageList(destination_image);
397 break;
398 }
399 channels++;
400 if (channel_op == ExchangeChannelOp)
401 {
402 status=ChannelImage(destination_image,source_channel,channel_op,
403 source_image,destination_channel,ClampToQuantum(pixel),exception);
404 if (status == MagickFalse)
405 {
406 destination_image=DestroyImageList(destination_image);
407 break;
408 }
409 channels++;
410 }
411 switch (channel_op)
412 {
413 case ExtractChannelOp:
414 {
415 channel_mask=(ChannelType) (channel_mask | (1 << destination_channel));
416 destination_channel=(PixelChannel) (destination_channel+1);
417 break;
418 }
419 default:
420 break;
421 }
422 status=SetImageProgress(source_image,ChannelFxImageTag,p-expression,
423 strlen(expression));
424 if (status == MagickFalse)
425 break;
426 }
427 (void) SetPixelChannelMask(destination_image,channel_mask);
428 if ((channel_op == ExtractChannelOp) && (channels == 1))
429 (void) SetImageColorspace(destination_image,GRAYColorspace,exception);
430 status=SetImageStorageClass(destination_image,DirectClass,exception);
431 if (status == MagickFalse)
432 {
433 destination_image=GetLastImageInList(destination_image);
434 return((Image *) NULL);
435 }
436 return(GetFirstImageInList(destination_image));
437 }
438
439 /*
440 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441 % %
442 % %
443 % %
444 % C o m b i n e I m a g e s %
445 % %
446 % %
447 % %
448 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
449 %
450 % CombineImages() combines one or more images into a single image. The
451 % grayscale value of the pixels of each image in the sequence is assigned in
452 % order to the specified channels of the combined image. The typical
453 % ordering would be image 1 => Red, 2 => Green, 3 => Blue, etc.
454 %
455 % The format of the CombineImages method is:
456 %
457 % Image *CombineImages(const Image *images,const ColorspaceType colorspace,
458 % ExceptionInfo *exception)
459 %
460 % A description of each parameter follows:
461 %
462 % o images: the image sequence.
463 %
464 % o colorspace: the image colorspace.
465 %
466 % o exception: return any errors or warnings in this structure.
467 %
468 */
CombineImages(const Image * image,const ColorspaceType colorspace,ExceptionInfo * exception)469 MagickExport Image *CombineImages(const Image *image,
470 const ColorspaceType colorspace,ExceptionInfo *exception)
471 {
472 #define CombineImageTag "Combine/Image"
473
474 CacheView
475 *combine_view;
476
477 Image
478 *combine_image;
479
480 MagickBooleanType
481 status;
482
483 MagickOffsetType
484 progress;
485
486 ssize_t
487 y;
488
489 /*
490 Ensure the image are the same size.
491 */
492 assert(image != (const Image *) NULL);
493 assert(image->signature == MagickCoreSignature);
494 if (image->debug != MagickFalse)
495 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
496 assert(exception != (ExceptionInfo *) NULL);
497 assert(exception->signature == MagickCoreSignature);
498 combine_image=CloneImage(image,0,0,MagickTrue,exception);
499 if (combine_image == (Image *) NULL)
500 return((Image *) NULL);
501 if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
502 {
503 combine_image=DestroyImage(combine_image);
504 return((Image *) NULL);
505 }
506 if ((colorspace == UndefinedColorspace) || (image->number_channels == 1))
507 (void) SetImageColorspace(combine_image,sRGBColorspace,exception);
508 else
509 (void) SetImageColorspace(combine_image,colorspace,exception);
510 if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0)
511 combine_image->alpha_trait=BlendPixelTrait;
512 /*
513 Combine images.
514 */
515 status=MagickTrue;
516 progress=0;
517 combine_view=AcquireAuthenticCacheView(combine_image,exception);
518 for (y=0; y < (ssize_t) combine_image->rows; y++)
519 {
520 CacheView
521 *image_view;
522
523 const Image
524 *next;
525
526 Quantum
527 *pixels;
528
529 register const Quantum
530 *magick_restrict p;
531
532 register Quantum
533 *magick_restrict q;
534
535 register ssize_t
536 i;
537
538 if (status == MagickFalse)
539 continue;
540 pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
541 1,exception);
542 if (pixels == (Quantum *) NULL)
543 {
544 status=MagickFalse;
545 continue;
546 }
547 next=image;
548 for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
549 {
550 register ssize_t
551 x;
552
553 PixelChannel channel=GetPixelChannelChannel(combine_image,i);
554 PixelTrait traits=GetPixelChannelTraits(combine_image,channel);
555 if (traits == UndefinedPixelTrait)
556 continue;
557 if (next == (Image *) NULL)
558 continue;
559 image_view=AcquireVirtualCacheView(next,exception);
560 p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
561 if (p == (const Quantum *) NULL)
562 continue;
563 q=pixels;
564 for (x=0; x < (ssize_t) combine_image->columns; x++)
565 {
566 if (x < (ssize_t) next->columns)
567 {
568 q[i]=GetPixelGray(next,p);
569 p+=GetPixelChannels(next);
570 }
571 q+=GetPixelChannels(combine_image);
572 }
573 image_view=DestroyCacheView(image_view);
574 next=GetNextImageInList(next);
575 }
576 if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
577 status=MagickFalse;
578 if (image->progress_monitor != (MagickProgressMonitor) NULL)
579 {
580 MagickBooleanType
581 proceed;
582
583 proceed=SetImageProgress(image,CombineImageTag,progress++,
584 combine_image->rows);
585 if (proceed == MagickFalse)
586 status=MagickFalse;
587 }
588 }
589 combine_view=DestroyCacheView(combine_view);
590 if (status == MagickFalse)
591 combine_image=DestroyImage(combine_image);
592 return(combine_image);
593 }
594
595 /*
596 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
597 % %
598 % %
599 % %
600 % G e t I m a g e A l p h a C h a n n e l %
601 % %
602 % %
603 % %
604 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
605 %
606 % GetImageAlphaChannel() returns MagickFalse if the image alpha channel is
607 % not activated. That is, the image is RGB rather than RGBA or CMYK rather
608 % than CMYKA.
609 %
610 % The format of the GetImageAlphaChannel method is:
611 %
612 % MagickBooleanType GetImageAlphaChannel(const Image *image)
613 %
614 % A description of each parameter follows:
615 %
616 % o image: the image.
617 %
618 */
GetImageAlphaChannel(const Image * image)619 MagickExport MagickBooleanType GetImageAlphaChannel(const Image *image)
620 {
621 assert(image != (const Image *) NULL);
622 if (image->debug != MagickFalse)
623 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
624 assert(image->signature == MagickCoreSignature);
625 return(image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse);
626 }
627
628 /*
629 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
630 % %
631 % %
632 % %
633 % S e p a r a t e I m a g e %
634 % %
635 % %
636 % %
637 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
638 %
639 % SeparateImage() separates a channel from the image and returns it as a
640 % grayscale image.
641 %
642 % The format of the SeparateImage method is:
643 %
644 % Image *SeparateImage(const Image *image,const ChannelType channel,
645 % ExceptionInfo *exception)
646 %
647 % A description of each parameter follows:
648 %
649 % o image: the image.
650 %
651 % o channel: the image channel.
652 %
653 % o exception: return any errors or warnings in this structure.
654 %
655 */
SeparateImage(const Image * image,const ChannelType channel_type,ExceptionInfo * exception)656 MagickExport Image *SeparateImage(const Image *image,
657 const ChannelType channel_type,ExceptionInfo *exception)
658 {
659 #define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
660 #define SeparateImageTag "Separate/Image"
661
662 CacheView
663 *image_view,
664 *separate_view;
665
666 Image
667 *separate_image;
668
669 MagickBooleanType
670 status;
671
672 MagickOffsetType
673 progress;
674
675 ssize_t
676 y;
677
678 /*
679 Initialize separate image attributes.
680 */
681 assert(image != (Image *) NULL);
682 assert(image->signature == MagickCoreSignature);
683 if (image->debug != MagickFalse)
684 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
685 assert(exception != (ExceptionInfo *) NULL);
686 assert(exception->signature == MagickCoreSignature);
687 separate_image=CloneImage(image,image->columns,image->rows,MagickTrue,
688 exception);
689 if (separate_image == (Image *) NULL)
690 return((Image *) NULL);
691 if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
692 {
693 separate_image=DestroyImage(separate_image);
694 return((Image *) NULL);
695 }
696 (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
697 separate_image->alpha_trait=UndefinedPixelTrait;
698 /*
699 Separate image.
700 */
701 status=MagickTrue;
702 progress=0;
703 image_view=AcquireVirtualCacheView(image,exception);
704 separate_view=AcquireAuthenticCacheView(separate_image,exception);
705 #if defined(MAGICKCORE_OPENMP_SUPPORT)
706 #pragma omp parallel for schedule(static,4) shared(progress,status) \
707 magick_threads(image,image,image->rows,1)
708 #endif
709 for (y=0; y < (ssize_t) image->rows; y++)
710 {
711 register const Quantum
712 *magick_restrict p;
713
714 register Quantum
715 *magick_restrict q;
716
717 register ssize_t
718 x;
719
720 if (status == MagickFalse)
721 continue;
722 p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
723 q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
724 exception);
725 if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
726 {
727 status=MagickFalse;
728 continue;
729 }
730 for (x=0; x < (ssize_t) image->columns; x++)
731 {
732 register ssize_t
733 i;
734
735 if (GetPixelReadMask(image,p) == 0)
736 {
737 SetPixelBackgoundColor(separate_image,q);
738 p+=GetPixelChannels(image);
739 q+=GetPixelChannels(separate_image);
740 continue;
741 }
742 SetPixelChannel(separate_image,GrayPixelChannel,0,q);
743 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
744 {
745 PixelChannel channel=GetPixelChannelChannel(image,i);
746 PixelTrait traits=GetPixelChannelTraits(image,channel);
747 if ((traits == UndefinedPixelTrait) ||
748 (GetChannelBit(channel_type,channel) == 0))
749 continue;
750 SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
751 }
752 p+=GetPixelChannels(image);
753 q+=GetPixelChannels(separate_image);
754 }
755 if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
756 status=MagickFalse;
757 if (image->progress_monitor != (MagickProgressMonitor) NULL)
758 {
759 MagickBooleanType
760 proceed;
761
762 #if defined(MAGICKCORE_OPENMP_SUPPORT)
763 #pragma omp critical (MagickCore_SeparateImage)
764 #endif
765 proceed=SetImageProgress(image,SeparateImageTag,progress++,image->rows);
766 if (proceed == MagickFalse)
767 status=MagickFalse;
768 }
769 }
770 separate_view=DestroyCacheView(separate_view);
771 image_view=DestroyCacheView(image_view);
772 (void) SetImageChannelMask(separate_image,DefaultChannels);
773 if (status == MagickFalse)
774 separate_image=DestroyImage(separate_image);
775 return(separate_image);
776 }
777
778 /*
779 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
780 % %
781 % %
782 % %
783 % S e p a r a t e I m a g e s %
784 % %
785 % %
786 % %
787 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
788 %
789 % SeparateImages() returns a separate grayscale image for each channel
790 % specified.
791 %
792 % The format of the SeparateImages method is:
793 %
794 % Image *SeparateImages(const Image *image,ExceptionInfo *exception)
795 %
796 % A description of each parameter follows:
797 %
798 % o image: the image.
799 %
800 % o exception: return any errors or warnings in this structure.
801 %
802 */
SeparateImages(const Image * image,ExceptionInfo * exception)803 MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
804 {
805 Image
806 *images,
807 *separate_image;
808
809 register ssize_t
810 i;
811
812 assert(image != (Image *) NULL);
813 assert(image->signature == MagickCoreSignature);
814 if (image->debug != MagickFalse)
815 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
816 images=NewImageList();
817 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
818 {
819 PixelChannel channel=GetPixelChannelChannel(image,i);
820 PixelTrait traits=GetPixelChannelTraits(image,channel);
821 if ((traits == UndefinedPixelTrait) ||
822 ((traits & UpdatePixelTrait) == 0))
823 continue;
824 separate_image=SeparateImage(image,(ChannelType) (1 << channel),exception);
825 if (separate_image != (Image *) NULL)
826 AppendImageToList(&images,separate_image);
827 }
828 if (images == (Image *) NULL)
829 images=SeparateImage(image,UndefinedChannel,exception);
830 return(images);
831 }
832
833 /*
834 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
835 % %
836 % %
837 % %
838 % S e t I m a g e A l p h a C h a n n e l %
839 % %
840 % %
841 % %
842 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
843 %
844 % SetImageAlphaChannel() activates, deactivates, resets, or sets the alpha
845 % channel.
846 %
847 % The format of the SetImageAlphaChannel method is:
848 %
849 % MagickBooleanType SetImageAlphaChannel(Image *image,
850 % const AlphaChannelOption alpha_type,ExceptionInfo *exception)
851 %
852 % A description of each parameter follows:
853 %
854 % o image: the image.
855 %
856 % o alpha_type: The alpha channel type: ActivateAlphaChannel,
857 % AssociateAlphaChannel, CopyAlphaChannel, DeactivateAlphaChannel,
858 % DisassociateAlphaChannel, ExtractAlphaChannel, OffAlphaChannel,
859 % OnAlphaChannel, OpaqueAlphaChannel, SetAlphaChannel, ShapeAlphaChannel,
860 % and TransparentAlphaChannel.
861 %
862 % o exception: return any errors or warnings in this structure.
863 %
864 */
865
FlattenPixelInfo(const Image * image,const PixelInfo * p,const double alpha,const Quantum * q,const double beta,Quantum * composite)866 static inline void FlattenPixelInfo(const Image *image,const PixelInfo *p,
867 const double alpha,const Quantum *q,const double beta,
868 Quantum *composite)
869 {
870 double
871 Da,
872 gamma,
873 Sa;
874
875 register ssize_t
876 i;
877
878 /*
879 Compose pixel p over pixel q with the given alpha.
880 */
881 Sa=QuantumScale*alpha;
882 Da=QuantumScale*beta,
883 gamma=Sa*(-Da)+Sa+Da;
884 gamma=PerceptibleReciprocal(gamma);
885 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
886 {
887 PixelChannel channel=GetPixelChannelChannel(image,i);
888 PixelTrait traits=GetPixelChannelTraits(image,channel);
889 if (traits == UndefinedPixelTrait)
890 continue;
891 switch (channel)
892 {
893 case RedPixelChannel:
894 {
895 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
896 (double) p->red,alpha));
897 break;
898 }
899 case GreenPixelChannel:
900 {
901 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
902 (double) p->green,alpha));
903 break;
904 }
905 case BluePixelChannel:
906 {
907 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
908 (double) p->blue,alpha));
909 break;
910 }
911 case BlackPixelChannel:
912 {
913 composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
914 (double) p->black,alpha));
915 break;
916 }
917 case AlphaPixelChannel:
918 {
919 composite[i]=ClampToQuantum(QuantumRange*(Sa*(-Da)+Sa+Da));
920 break;
921 }
922 default:
923 break;
924 }
925 }
926 }
927
SetImageAlphaChannel(Image * image,const AlphaChannelOption alpha_type,ExceptionInfo * exception)928 MagickExport MagickBooleanType SetImageAlphaChannel(Image *image,
929 const AlphaChannelOption alpha_type,ExceptionInfo *exception)
930 {
931 CacheView
932 *image_view;
933
934 MagickBooleanType
935 status;
936
937 ssize_t
938 y;
939
940 assert(image != (Image *) NULL);
941 if (image->debug != MagickFalse)
942 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
943 assert(image->signature == MagickCoreSignature);
944 status=MagickTrue;
945 switch (alpha_type)
946 {
947 case ActivateAlphaChannel:
948 {
949 image->alpha_trait=BlendPixelTrait;
950 break;
951 }
952 case AssociateAlphaChannel:
953 {
954 /*
955 Associate alpha.
956 */
957 status=SetImageStorageClass(image,DirectClass,exception);
958 if (status == MagickFalse)
959 break;
960 image_view=AcquireAuthenticCacheView(image,exception);
961 #if defined(MAGICKCORE_OPENMP_SUPPORT)
962 #pragma omp parallel for schedule(static,4) shared(status) \
963 magick_threads(image,image,image->rows,1)
964 #endif
965 for (y=0; y < (ssize_t) image->rows; y++)
966 {
967 register Quantum
968 *magick_restrict q;
969
970 register ssize_t
971 x;
972
973 if (status == MagickFalse)
974 continue;
975 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
976 exception);
977 if (q == (Quantum *) NULL)
978 {
979 status=MagickFalse;
980 continue;
981 }
982 for (x=0; x < (ssize_t) image->columns; x++)
983 {
984 double
985 gamma;
986
987 register ssize_t
988 i;
989
990 if (GetPixelReadMask(image,q) == 0)
991 {
992 q+=GetPixelChannels(image);
993 continue;
994 }
995 gamma=QuantumScale*GetPixelAlpha(image,q);
996 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
997 {
998 PixelChannel channel=GetPixelChannelChannel(image,i);
999 PixelTrait traits=GetPixelChannelTraits(image,channel);
1000 if (channel == AlphaPixelChannel)
1001 continue;
1002 if ((traits & UpdatePixelTrait) == 0)
1003 continue;
1004 q[i]=ClampToQuantum(gamma*q[i]);
1005 }
1006 q+=GetPixelChannels(image);
1007 }
1008 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1009 status=MagickFalse;
1010 }
1011 image_view=DestroyCacheView(image_view);
1012 image->alpha_trait=CopyPixelTrait;
1013 return(status);
1014 }
1015 case BackgroundAlphaChannel:
1016 {
1017 /*
1018 Set transparent pixels to background color.
1019 */
1020 if (image->alpha_trait == UndefinedPixelTrait)
1021 break;
1022 status=SetImageStorageClass(image,DirectClass,exception);
1023 if (status == MagickFalse)
1024 break;
1025 image_view=AcquireAuthenticCacheView(image,exception);
1026 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1027 #pragma omp parallel for schedule(static,4) shared(status) \
1028 magick_threads(image,image,image->rows,1)
1029 #endif
1030 for (y=0; y < (ssize_t) image->rows; y++)
1031 {
1032 register Quantum
1033 *magick_restrict q;
1034
1035 register ssize_t
1036 x;
1037
1038 if (status == MagickFalse)
1039 continue;
1040 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1041 exception);
1042 if (q == (Quantum *) NULL)
1043 {
1044 status=MagickFalse;
1045 continue;
1046 }
1047 for (x=0; x < (ssize_t) image->columns; x++)
1048 {
1049 if (GetPixelAlpha(image,q) == TransparentAlpha)
1050 {
1051 SetPixelViaPixelInfo(image,&image->background_color,q);
1052 SetPixelChannel(image,AlphaPixelChannel,TransparentAlpha,q);
1053 }
1054 q+=GetPixelChannels(image);
1055 }
1056 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1057 status=MagickFalse;
1058 }
1059 image_view=DestroyCacheView(image_view);
1060 return(status);
1061 }
1062 case CopyAlphaChannel:
1063 case ShapeAlphaChannel:
1064 {
1065 /*
1066 Copy pixel intensity to the alpha channel.
1067 */
1068 image->alpha_trait=UpdatePixelTrait;
1069 status=CompositeImage(image,image,IntensityCompositeOp,MagickTrue,0,0,
1070 exception);
1071 if (alpha_type == ShapeAlphaChannel)
1072 (void) LevelImageColors(image,&image->background_color,
1073 &image->background_color,MagickTrue,exception);
1074 break;
1075 }
1076 case DeactivateAlphaChannel:
1077 {
1078 if (image->alpha_trait == UndefinedPixelTrait)
1079 status=SetImageAlpha(image,OpaqueAlpha,exception);
1080 image->alpha_trait=CopyPixelTrait;
1081 break;
1082 }
1083 case DisassociateAlphaChannel:
1084 {
1085 /*
1086 Disassociate alpha.
1087 */
1088 status=SetImageStorageClass(image,DirectClass,exception);
1089 if (status == MagickFalse)
1090 break;
1091 image->alpha_trait=BlendPixelTrait;
1092 image_view=AcquireAuthenticCacheView(image,exception);
1093 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1094 #pragma omp parallel for schedule(static,4) shared(status) \
1095 magick_threads(image,image,image->rows,1)
1096 #endif
1097 for (y=0; y < (ssize_t) image->rows; y++)
1098 {
1099 register Quantum
1100 *magick_restrict q;
1101
1102 register ssize_t
1103 x;
1104
1105 if (status == MagickFalse)
1106 continue;
1107 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1108 exception);
1109 if (q == (Quantum *) NULL)
1110 {
1111 status=MagickFalse;
1112 continue;
1113 }
1114 for (x=0; x < (ssize_t) image->columns; x++)
1115 {
1116 double
1117 gamma,
1118 Sa;
1119
1120 register ssize_t
1121 i;
1122
1123 if (GetPixelReadMask(image,q) == 0)
1124 {
1125 q+=GetPixelChannels(image);
1126 continue;
1127 }
1128 Sa=QuantumScale*GetPixelAlpha(image,q);
1129 gamma=PerceptibleReciprocal(Sa);
1130 for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1131 {
1132 PixelChannel channel=GetPixelChannelChannel(image,i);
1133 PixelTrait traits=GetPixelChannelTraits(image,channel);
1134 if (channel == AlphaPixelChannel)
1135 continue;
1136 if ((traits & UpdatePixelTrait) == 0)
1137 continue;
1138 q[i]=ClampToQuantum(gamma*q[i]);
1139 }
1140 q+=GetPixelChannels(image);
1141 }
1142 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1143 status=MagickFalse;
1144 }
1145 image_view=DestroyCacheView(image_view);
1146 image->alpha_trait=UndefinedPixelTrait;
1147 return(status);
1148 }
1149 case DiscreteAlphaChannel:
1150 {
1151 if (image->alpha_trait == UndefinedPixelTrait)
1152 status=SetImageAlpha(image,OpaqueAlpha,exception);
1153 image->alpha_trait=UpdatePixelTrait;
1154 break;
1155 }
1156 case ExtractAlphaChannel:
1157 {
1158 status=CompositeImage(image,image,AlphaCompositeOp,MagickTrue,0,0,
1159 exception);
1160 image->alpha_trait=UndefinedPixelTrait;
1161 break;
1162 }
1163 case OffAlphaChannel:
1164 {
1165 image->alpha_trait=UndefinedPixelTrait;
1166 break;
1167 }
1168 case OnAlphaChannel:
1169 {
1170 if (image->alpha_trait == UndefinedPixelTrait)
1171 status=SetImageAlpha(image,OpaqueAlpha,exception);
1172 image->alpha_trait=BlendPixelTrait;
1173 break;
1174 }
1175 case OpaqueAlphaChannel:
1176 {
1177 status=SetImageAlpha(image,OpaqueAlpha,exception);
1178 break;
1179 }
1180 case RemoveAlphaChannel:
1181 {
1182 /*
1183 Remove transparency.
1184 */
1185 if (image->alpha_trait == UndefinedPixelTrait)
1186 break;
1187 status=SetImageStorageClass(image,DirectClass,exception);
1188 if (status == MagickFalse)
1189 break;
1190 image_view=AcquireAuthenticCacheView(image,exception);
1191 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1192 #pragma omp parallel for schedule(static,4) shared(status) \
1193 magick_threads(image,image,image->rows,1)
1194 #endif
1195 for (y=0; y < (ssize_t) image->rows; y++)
1196 {
1197 register Quantum
1198 *magick_restrict q;
1199
1200 register ssize_t
1201 x;
1202
1203 if (status == MagickFalse)
1204 continue;
1205 q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1206 exception);
1207 if (q == (Quantum *) NULL)
1208 {
1209 status=MagickFalse;
1210 continue;
1211 }
1212 for (x=0; x < (ssize_t) image->columns; x++)
1213 {
1214 FlattenPixelInfo(image,&image->background_color,
1215 image->background_color.alpha,q,(double)
1216 GetPixelAlpha(image,q),q);
1217 q+=GetPixelChannels(image);
1218 }
1219 if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1220 status=MagickFalse;
1221 }
1222 image_view=DestroyCacheView(image_view);
1223 image->alpha_trait=image->background_color.alpha_trait;
1224 break;
1225 }
1226 case SetAlphaChannel:
1227 {
1228 if (image->alpha_trait == UndefinedPixelTrait)
1229 status=SetImageAlpha(image,OpaqueAlpha,exception);
1230 break;
1231 }
1232 case TransparentAlphaChannel:
1233 {
1234 status=SetImageAlpha(image,TransparentAlpha,exception);
1235 break;
1236 }
1237 case UndefinedAlphaChannel:
1238 break;
1239 }
1240 if (status == MagickFalse)
1241 return(status);
1242 (void) SetPixelChannelMask(image,image->channel_mask);
1243 return(SyncImagePixelCache(image,exception));
1244 }
1245