MagickCore  7.1.2-29
Convert, Edit, Or Compose Bitmap Images
channel.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % CCCC H H AAA N N N N EEEEE L %
7 % C H H A A NN N NN N E L %
8 % C HHHHH AAAAA N N N N N N EEE L %
9 % C H H A A N NN N NN E L %
10 % CCCC H H A A N N N N EEEEE LLLLL %
11 % %
12 % %
13 % MagickCore Image Channel Methods %
14 % %
15 % Software Design %
16 % Cristy %
17 % December 2003 %
18 % %
19 % %
20 % Copyright @ 1999 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/license/ %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 %
38 */
39 
40 /*
41  Include declarations.
42 */
43 #include "MagickCore/studio.h"
44 #include "MagickCore/cache-private.h"
45 #include "MagickCore/channel.h"
46 #include "MagickCore/colorspace-private.h"
47 #include "MagickCore/composite-private.h"
48 #include "MagickCore/enhance.h"
49 #include "MagickCore/image.h"
50 #include "MagickCore/list.h"
51 #include "MagickCore/log.h"
52 #include "MagickCore/monitor.h"
53 #include "MagickCore/monitor-private.h"
54 #include "MagickCore/option.h"
55 #include "MagickCore/pixel-accessor.h"
56 #include "MagickCore/resource_.h"
57 #include "MagickCore/string-private.h"
58 #include "MagickCore/thread-private.h"
59 #include "MagickCore/token.h"
60 #include "MagickCore/utility.h"
61 #include "MagickCore/version.h"
62 ␌
63 /*
64 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
65 % %
66 % %
67 % %
68 % C h a n n e l F x I m a g e %
69 % %
70 % %
71 % %
72 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
73 %
74 % ChannelFxImage() applies a channel expression to the specified image. The
75 % expression consists of one or more channels, either mnemonic or numeric (e.g.
76 % r, red, 0), separated by actions as follows:
77 %
78 % <=> exchange two channels (e.g. red<=>blue)
79 % => copy one channel to another channel (e.g. red=>green)
80 % = assign a constant value to a channel (e.g. red=50%)
81 % , write new image channels in the specified order (e.g. red, green)
82 % ; add a new output image for the next set of channel operations
83 % | move to the next input image for the source of channel data
84 % If there are no more images in the list, | has no effect.
85 %
86 % For example, to create 3 grayscale images from the red, green, and blue
87 % channels of an image, use:
88 %
89 % -channel-fx "red; green; blue"
90 %
91 % A channel without an operation symbol implies separate (i.e, semicolon).
92 %
93 % The format of the ChannelFxImage method is:
94 %
95 % Image *ChannelFxImage(const Image *image,const char *expression,
96 % ExceptionInfo *exception)
97 %
98 % A description of each parameter follows:
99 %
100 % o image: the image.
101 %
102 % o expression: A channel expression.
103 %
104 % o exception: return any errors or warnings in this structure.
105 %
106 */
107 
108 typedef enum
109 {
110  ExtractChannelOp,
111  AssignChannelOp,
112  ExchangeChannelOp,
113  TransferChannelOp
114 } ChannelFx;
115 
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 = 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 
196 MagickExport 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 */
498 MagickExport Image *CombineImages(const Image *image,
499  const ColorspaceType colorspace,ExceptionInfo *exception)
500 {
501 #define CombineImageTag "Combine/Image"
502 
503  CacheView
504  *combine_view;
505 
506  Image
507  *combine_image;
508 
509  MagickBooleanType
510  status;
511 
512  MagickOffsetType
513  progress;
514 
515  size_t
516  number_channels;
517 
518  ssize_t
519  y;
520 
521  /*
522  Ensure the image are the same size.
523  */
524  assert(image != (const Image *) NULL);
525  assert(image->signature == MagickCoreSignature);
526  if (IsEventLogging() != MagickFalse)
527  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
528  assert(exception != (ExceptionInfo *) NULL);
529  assert(exception->signature == MagickCoreSignature);
530  combine_image=CloneImage(image,0,0,MagickTrue,exception);
531  if (combine_image == (Image *) NULL)
532  return((Image *) NULL);
533  if (SetImageStorageClass(combine_image,DirectClass,exception) == MagickFalse)
534  {
535  combine_image=DestroyImage(combine_image);
536  return((Image *) NULL);
537  }
538  if (colorspace != UndefinedColorspace)
539  (void) SetImageColorspace(combine_image,colorspace,exception);
540  else
541  if (fabs(image->gamma-1.0) <= MagickEpsilon)
542  (void) SetImageColorspace(combine_image,RGBColorspace,exception);
543  else
544  (void) SetImageColorspace(combine_image,sRGBColorspace,exception);
545  number_channels=GetImageListLength(image);
546  switch (combine_image->colorspace)
547  {
548  case UndefinedColorspace:
549  case sRGBColorspace:
550  {
551  if (number_channels > 3)
552  combine_image->alpha_trait=BlendPixelTrait;
553  if (number_channels > 4)
554  SetPixelMetaChannels(combine_image,number_channels-4,exception);
555  break;
556  }
557  case LinearGRAYColorspace:
558  case GRAYColorspace:
559  {
560  if (number_channels > 1)
561  combine_image->alpha_trait=BlendPixelTrait;
562  if (number_channels > 2)
563  SetPixelMetaChannels(combine_image,number_channels-2,exception);
564  break;
565  }
566  case CMYKColorspace:
567  {
568  if (number_channels > 4)
569  combine_image->alpha_trait=BlendPixelTrait;
570  if (number_channels > 5)
571  SetPixelMetaChannels(combine_image,number_channels-5,exception);
572  break;
573  }
574  default:
575  break;
576  }
577  /*
578  Combine images.
579  */
580  status=MagickTrue;
581  progress=0;
582  combine_view=AcquireAuthenticCacheView(combine_image,exception);
583 #if defined(MAGICKCORE_OPENMP_SUPPORT)
584  #pragma omp parallel for schedule(static) shared(progress,status) \
585  magick_number_threads(combine_image,combine_image,combine_image->rows,4)
586 #endif
587  for (y=0; y < (ssize_t) combine_image->rows; y++)
588  {
589  CacheView
590  *image_view;
591 
592  const Image
593  *next;
594 
595  const Quantum
596  *magick_restrict p;
597 
598  Quantum
599  *pixels,
600  *magick_restrict q;
601 
602  ssize_t
603  i;
604 
605  if (status == MagickFalse)
606  continue;
607  pixels=GetCacheViewAuthenticPixels(combine_view,0,y,combine_image->columns,
608  1,exception);
609  if (pixels == (Quantum *) NULL)
610  {
611  status=MagickFalse;
612  continue;
613  }
614  next=image;
615  for (i=0; i < (ssize_t) GetPixelChannels(combine_image); i++)
616  {
617  ssize_t
618  x;
619 
620  PixelChannel channel = GetPixelChannelChannel(combine_image,i);
621  PixelTrait traits = GetPixelChannelTraits(combine_image,channel);
622  if (traits == UndefinedPixelTrait)
623  continue;
624  if (next == (Image *) NULL)
625  continue;
626  image_view=AcquireVirtualCacheView(next,exception);
627  p=GetCacheViewVirtualPixels(image_view,0,y,next->columns,1,exception);
628  if (p == (const Quantum *) NULL)
629  continue;
630  q=pixels;
631  for (x=0; x < (ssize_t) combine_image->columns; x++)
632  {
633  if (x < (ssize_t) next->columns)
634  {
635  q[i]=(Quantum) GetPixelIntensity(next,p);
636  p+=(ptrdiff_t) GetPixelChannels(next);
637  }
638  q+=(ptrdiff_t) GetPixelChannels(combine_image);
639  }
640  image_view=DestroyCacheView(image_view);
641  next=GetNextImageInList(next);
642  }
643  if (SyncCacheViewAuthenticPixels(combine_view,exception) == MagickFalse)
644  status=MagickFalse;
645  if (image->progress_monitor != (MagickProgressMonitor) NULL)
646  {
647  MagickBooleanType
648  proceed;
649 
650 #if defined(MAGICKCORE_OPENMP_SUPPORT)
651  #pragma omp atomic
652 #endif
653  progress++;
654  proceed=SetImageProgress(image,CombineImageTag,progress,
655  combine_image->rows);
656  if (proceed == MagickFalse)
657  status=MagickFalse;
658  }
659  }
660  combine_view=DestroyCacheView(combine_view);
661  if (status == MagickFalse)
662  combine_image=DestroyImage(combine_image);
663  else
664  combine_image->type=UndefinedType;
665  return(combine_image);
666 }
667 ␌
668 /*
669 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
670 % %
671 % %
672 % %
673 % G e t I m a g e A l p h a C h a n n e l %
674 % %
675 % %
676 % %
677 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
678 %
679 % GetImageAlphaChannel() returns MagickFalse if the image alpha channel is
680 % not activated. That is, the image is RGB rather than RGBA or CMYK rather
681 % than CMYKA.
682 %
683 % The format of the GetImageAlphaChannel method is:
684 %
685 % MagickBooleanType GetImageAlphaChannel(const Image *image)
686 %
687 % A description of each parameter follows:
688 %
689 % o image: the image.
690 %
691 */
692 MagickExport MagickBooleanType GetImageAlphaChannel(const Image *image)
693 {
694  assert(image != (const Image *) NULL);
695  if (IsEventLogging() != MagickFalse)
696  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
697  assert(image->signature == MagickCoreSignature);
698  return(image->alpha_trait != UndefinedPixelTrait ? MagickTrue : MagickFalse);
699 }
700 ␌
701 /*
702 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
703 % %
704 % %
705 % %
706 % S e p a r a t e I m a g e %
707 % %
708 % %
709 % %
710 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
711 %
712 % SeparateImage() separates a channel from the image and returns it as a
713 % grayscale image.
714 %
715 % The format of the SeparateImage method is:
716 %
717 % Image *SeparateImage(const Image *image,const ChannelType channel,
718 % ExceptionInfo *exception)
719 %
720 % A description of each parameter follows:
721 %
722 % o image: the image.
723 %
724 % o channel: the image channel.
725 %
726 % o exception: return any errors or warnings in this structure.
727 %
728 */
729 MagickExport Image *SeparateImage(const Image *image,
730  const ChannelType channel_type,ExceptionInfo *exception)
731 {
732 #define GetChannelBit(mask,bit) (((size_t) (mask) >> (size_t) (bit)) & 0x01)
733 #define SeparateImageTag "Separate/Image"
734 
735  CacheView
736  *image_view,
737  *separate_view;
738 
739  Image
740  *separate_image;
741 
742  MagickBooleanType
743  status;
744 
745  MagickOffsetType
746  progress;
747 
748  ssize_t
749  y;
750 
751  /*
752  Initialize separate image attributes.
753  */
754  assert(image != (Image *) NULL);
755  assert(image->signature == MagickCoreSignature);
756  if (IsEventLogging() != MagickFalse)
757  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
758  assert(exception != (ExceptionInfo *) NULL);
759  assert(exception->signature == MagickCoreSignature);
760  separate_image=CloneImage(image,0,0,MagickTrue,exception);
761  if (separate_image == (Image *) NULL)
762  return((Image *) NULL);
763  if (SetImageStorageClass(separate_image,DirectClass,exception) == MagickFalse)
764  {
765  separate_image=DestroyImage(separate_image);
766  return((Image *) NULL);
767  }
768  separate_image->alpha_trait=UndefinedPixelTrait;
769  (void) SetImageColorspace(separate_image,GRAYColorspace,exception);
770  separate_image->gamma=image->gamma;
771  /*
772  Separate image.
773  */
774  status=MagickTrue;
775  progress=0;
776  image_view=AcquireVirtualCacheView(image,exception);
777  separate_view=AcquireAuthenticCacheView(separate_image,exception);
778 #if defined(MAGICKCORE_OPENMP_SUPPORT)
779  #pragma omp parallel for schedule(static) shared(progress,status) \
780  magick_number_threads(image,image,image->rows,2)
781 #endif
782  for (y=0; y < (ssize_t) image->rows; y++)
783  {
784  const Quantum
785  *magick_restrict p;
786 
787  Quantum
788  *magick_restrict q;
789 
790  ssize_t
791  x;
792 
793  if (status == MagickFalse)
794  continue;
795  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
796  q=QueueCacheViewAuthenticPixels(separate_view,0,y,separate_image->columns,1,
797  exception);
798  if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
799  {
800  status=MagickFalse;
801  continue;
802  }
803  for (x=0; x < (ssize_t) image->columns; x++)
804  {
805  ssize_t
806  i;
807 
808  SetPixelChannel(separate_image,GrayPixelChannel,(Quantum) 0,q);
809  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
810  {
811  PixelChannel channel = GetPixelChannelChannel(image,i);
812  PixelTrait traits = GetPixelChannelTraits(image,channel);
813  if ((traits == UndefinedPixelTrait) ||
814  (GetChannelBit(channel_type,channel) == 0))
815  continue;
816  SetPixelChannel(separate_image,GrayPixelChannel,p[i],q);
817  }
818  p+=(ptrdiff_t) GetPixelChannels(image);
819  q+=(ptrdiff_t) GetPixelChannels(separate_image);
820  }
821  if (SyncCacheViewAuthenticPixels(separate_view,exception) == MagickFalse)
822  status=MagickFalse;
823  if (image->progress_monitor != (MagickProgressMonitor) NULL)
824  {
825  MagickBooleanType
826  proceed;
827 
828 #if defined(MAGICKCORE_OPENMP_SUPPORT)
829  #pragma omp atomic
830 #endif
831  progress++;
832  proceed=SetImageProgress(image,SeparateImageTag,progress,image->rows);
833  if (proceed == MagickFalse)
834  status=MagickFalse;
835  }
836  }
837  separate_view=DestroyCacheView(separate_view);
838  image_view=DestroyCacheView(image_view);
839  (void) SetImageChannelMask(separate_image,AllChannels);
840  if (status == MagickFalse)
841  separate_image=DestroyImage(separate_image);
842  return(separate_image);
843 }
844 ␌
845 /*
846 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
847 % %
848 % %
849 % %
850 % S e p a r a t e I m a g e s %
851 % %
852 % %
853 % %
854 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
855 %
856 % SeparateImages() returns a separate grayscale image for each channel
857 % specified.
858 %
859 % The format of the SeparateImages method is:
860 %
861 % Image *SeparateImages(const Image *image,ExceptionInfo *exception)
862 %
863 % A description of each parameter follows:
864 %
865 % o image: the image.
866 %
867 % o exception: return any errors or warnings in this structure.
868 %
869 */
870 MagickExport Image *SeparateImages(const Image *image,ExceptionInfo *exception)
871 {
872  Image
873  *images,
874  *separate_image;
875 
876  ssize_t
877  i;
878 
879  assert(image != (Image *) NULL);
880  assert(image->signature == MagickCoreSignature);
881  if (IsEventLogging() != MagickFalse)
882  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
883  images=NewImageList();
884  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
885  {
886  PixelChannel channel = GetPixelChannelChannel(image,i);
887  PixelTrait traits = GetPixelChannelTraits(image,channel);
888  if ((traits == UndefinedPixelTrait) || ((traits & UpdatePixelTrait) == 0))
889  continue;
890  separate_image=SeparateImage(image,(ChannelType)
891  (MagickLLConstant(1) << channel),exception);
892  if (separate_image != (Image *) NULL)
893  AppendImageToList(&images,separate_image);
894  }
895  if (images == (Image *) NULL)
896  images=SeparateImage(image,UndefinedChannel,exception);
897  return(images);
898 }
899 ␌
900 /*
901 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
902 % %
903 % %
904 % %
905 % S e t I m a g e A l p h a C h a n n e l %
906 % %
907 % %
908 % %
909 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
910 %
911 % SetImageAlphaChannel() activates, deactivates, resets, or sets the alpha
912 % channel.
913 %
914 % The format of the SetImageAlphaChannel method is:
915 %
916 % MagickBooleanType SetImageAlphaChannel(Image *image,
917 % const AlphaChannelOption alpha_type,ExceptionInfo *exception)
918 %
919 % A description of each parameter follows:
920 %
921 % o image: the image.
922 %
923 % o alpha_type: The alpha channel type: ActivateAlphaChannel,
924 % AssociateAlphaChannel, CopyAlphaChannel, DeactivateAlphaChannel,
925 % DisassociateAlphaChannel, ExtractAlphaChannel, OffAlphaChannel,
926 % OnAlphaChannel, OpaqueAlphaChannel, SetAlphaChannel, ShapeAlphaChannel,
927 % and TransparentAlphaChannel.
928 %
929 % o exception: return any errors or warnings in this structure.
930 %
931 */
932 
933 static inline void FlattenPixelInfo(const Image *image,const PixelInfo *p,
934  const double alpha,const Quantum *q,const double beta,Quantum *composite)
935 {
936  double
937  Da,
938  gamma,
939  Sa;
940 
941  ssize_t
942  i;
943 
944  /*
945  Compose pixel p over pixel q with the given alpha.
946  */
947  Sa=QuantumScale*alpha;
948  Da=QuantumScale*beta,
949  gamma=Sa*(-Da)+Sa+Da;
950  gamma=MagickSafeReciprocal(gamma);
951  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
952  {
953  PixelChannel channel = GetPixelChannelChannel(image,i);
954  PixelTrait traits = GetPixelChannelTraits(image,channel);
955  if (traits == UndefinedPixelTrait)
956  continue;
957  switch (channel)
958  {
959  case RedPixelChannel:
960  {
961  composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
962  (double) p->red,alpha));
963  break;
964  }
965  case GreenPixelChannel:
966  {
967  composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
968  (double) p->green,alpha));
969  break;
970  }
971  case BluePixelChannel:
972  {
973  composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
974  (double) p->blue,alpha));
975  break;
976  }
977  case BlackPixelChannel:
978  {
979  composite[i]=ClampToQuantum(gamma*MagickOver_((double) q[i],beta,
980  (double) p->black,alpha));
981  break;
982  }
983  case AlphaPixelChannel:
984  {
985  composite[i]=ClampToQuantum((double) QuantumRange*(Sa*(-Da)+Sa+Da));
986  break;
987  }
988  default:
989  break;
990  }
991  }
992 }
993 
994 MagickExport MagickBooleanType SetImageAlphaChannel(Image *image,
995  const AlphaChannelOption alpha_type,ExceptionInfo *exception)
996 {
997  CacheView
998  *image_view;
999 
1000  MagickBooleanType
1001  status;
1002 
1003  ssize_t
1004  y;
1005 
1006  assert(image != (Image *) NULL);
1007  if (IsEventLogging() != MagickFalse)
1008  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1009  assert(image->signature == MagickCoreSignature);
1010  status=MagickTrue;
1011  switch (alpha_type)
1012  {
1013  case ActivateAlphaChannel:
1014  {
1015  if ((image->alpha_trait & BlendPixelTrait) != 0)
1016  return(status);
1017  image->alpha_trait=BlendPixelTrait;
1018  break;
1019  }
1020  case AssociateAlphaChannel:
1021  {
1022  /*
1023  Associate alpha.
1024  */
1025  status=SetImageStorageClass(image,DirectClass,exception);
1026  if (status == MagickFalse)
1027  break;
1028  image_view=AcquireAuthenticCacheView(image,exception);
1029 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1030  #pragma omp parallel for schedule(static) shared(status) \
1031  magick_number_threads(image,image,image->rows,2)
1032 #endif
1033  for (y=0; y < (ssize_t) image->rows; y++)
1034  {
1035  Quantum
1036  *magick_restrict q;
1037 
1038  ssize_t
1039  x;
1040 
1041  if (status == MagickFalse)
1042  continue;
1043  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1044  exception);
1045  if (q == (Quantum *) NULL)
1046  {
1047  status=MagickFalse;
1048  continue;
1049  }
1050  for (x=0; x < (ssize_t) image->columns; x++)
1051  {
1052  double
1053  gamma;
1054 
1055  ssize_t
1056  i;
1057 
1058  gamma=QuantumScale*(double) GetPixelAlpha(image,q);
1059  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1060  {
1061  PixelChannel channel = GetPixelChannelChannel(image,i);
1062  PixelTrait traits = GetPixelChannelTraits(image,channel);
1063  if (channel == AlphaPixelChannel)
1064  continue;
1065  if ((traits & UpdatePixelTrait) == 0)
1066  continue;
1067  q[i]=ClampToQuantum(gamma*(double) q[i]);
1068  }
1069  q+=(ptrdiff_t) GetPixelChannels(image);
1070  }
1071  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1072  status=MagickFalse;
1073  }
1074  image_view=DestroyCacheView(image_view);
1075  image->alpha_trait=CopyPixelTrait;
1076  return(status);
1077  }
1078  case BackgroundAlphaChannel:
1079  {
1080  /*
1081  Set transparent pixels to background color.
1082  */
1083  if ((image->alpha_trait & BlendPixelTrait) == 0)
1084  break;
1085  status=SetImageStorageClass(image,DirectClass,exception);
1086  if (status == MagickFalse)
1087  break;
1088  image_view=AcquireAuthenticCacheView(image,exception);
1089 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1090  #pragma omp parallel for schedule(static) shared(status) \
1091  magick_number_threads(image,image,image->rows,2)
1092 #endif
1093  for (y=0; y < (ssize_t) image->rows; y++)
1094  {
1095  Quantum
1096  *magick_restrict q;
1097 
1098  ssize_t
1099  x;
1100 
1101  if (status == MagickFalse)
1102  continue;
1103  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1104  exception);
1105  if (q == (Quantum *) NULL)
1106  {
1107  status=MagickFalse;
1108  continue;
1109  }
1110  for (x=0; x < (ssize_t) image->columns; x++)
1111  {
1112  if (GetPixelAlpha(image,q) == TransparentAlpha)
1113  {
1114  SetPixelViaPixelInfo(image,&image->background_color,q);
1115  SetPixelChannel(image,AlphaPixelChannel,TransparentAlpha,q);
1116  }
1117  q+=(ptrdiff_t) GetPixelChannels(image);
1118  }
1119  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1120  status=MagickFalse;
1121  }
1122  image_view=DestroyCacheView(image_view);
1123  return(status);
1124  }
1125  case CopyAlphaChannel:
1126  {
1127  image->alpha_trait=UpdatePixelTrait;
1128  status=CompositeImage(image,image,IntensityCompositeOp,MagickTrue,0,0,
1129  exception);
1130  break;
1131  }
1132  case DeactivateAlphaChannel:
1133  {
1134  if ((image->alpha_trait & BlendPixelTrait) == 0)
1135  status=SetImageAlpha(image,OpaqueAlpha,exception);
1136  image->alpha_trait=CopyPixelTrait;
1137  break;
1138  }
1139  case DisassociateAlphaChannel:
1140  {
1141  /*
1142  Disassociate alpha.
1143  */
1144  status=SetImageStorageClass(image,DirectClass,exception);
1145  if (status == MagickFalse)
1146  break;
1147  image->alpha_trait=BlendPixelTrait;
1148  image_view=AcquireAuthenticCacheView(image,exception);
1149 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1150  #pragma omp parallel for schedule(static) shared(status) \
1151  magick_number_threads(image,image,image->rows,2)
1152 #endif
1153  for (y=0; y < (ssize_t) image->rows; y++)
1154  {
1155  Quantum
1156  *magick_restrict q;
1157 
1158  ssize_t
1159  x;
1160 
1161  if (status == MagickFalse)
1162  continue;
1163  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1164  exception);
1165  if (q == (Quantum *) NULL)
1166  {
1167  status=MagickFalse;
1168  continue;
1169  }
1170  for (x=0; x < (ssize_t) image->columns; x++)
1171  {
1172  double
1173  gamma,
1174  Sa;
1175 
1176  ssize_t
1177  i;
1178 
1179  Sa=QuantumScale*(double) GetPixelAlpha(image,q);
1180  gamma=MagickSafeReciprocal(Sa);
1181  for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
1182  {
1183  PixelChannel channel = GetPixelChannelChannel(image,i);
1184  PixelTrait traits = GetPixelChannelTraits(image,channel);
1185  if (channel == AlphaPixelChannel)
1186  continue;
1187  if ((traits & UpdatePixelTrait) == 0)
1188  continue;
1189  q[i]=ClampToQuantum(gamma*(double) q[i]);
1190  }
1191  q+=(ptrdiff_t) GetPixelChannels(image);
1192  }
1193  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1194  status=MagickFalse;
1195  }
1196  image_view=DestroyCacheView(image_view);
1197  image->alpha_trait=UndefinedPixelTrait;
1198  return(status);
1199  }
1200  case DiscreteAlphaChannel:
1201  {
1202  if ((image->alpha_trait & BlendPixelTrait) == 0)
1203  status=SetImageAlpha(image,OpaqueAlpha,exception);
1204  image->alpha_trait=UpdatePixelTrait;
1205  break;
1206  }
1207  case ExtractAlphaChannel:
1208  {
1209  status=CompositeImage(image,image,AlphaCompositeOp,MagickTrue,0,0,
1210  exception);
1211  image->alpha_trait=UndefinedPixelTrait;
1212  break;
1213  }
1214  case OffAlphaChannel:
1215  {
1216  if ((image->alpha_trait & BlendPixelTrait) == 0)
1217  return(status);
1218  image->alpha_trait=UndefinedPixelTrait;
1219  break;
1220  }
1221  case OffIfOpaqueAlphaChannel:
1222  {
1223  MagickBooleanType
1224  opaque = MagickTrue;
1225 
1226  /*
1227  Remove opaque alpha channel.
1228  */
1229  if ((image->alpha_trait & BlendPixelTrait) == 0)
1230  break;
1231  image_view=AcquireVirtualCacheView(image,exception);
1232 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1233  #pragma omp parallel for schedule(static) shared(opaque,status) \
1234  magick_number_threads(image,image,image->rows,2)
1235 #endif
1236  for (y=0; y < (ssize_t) image->rows; y++)
1237  {
1238  const Quantum
1239  *magick_restrict p;
1240 
1241  ssize_t
1242  x;
1243 
1244  if ((status == MagickFalse) || (opaque == MagickFalse))
1245  continue;
1246  p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
1247  if (p == (const Quantum *) NULL)
1248  {
1249  status=MagickFalse;
1250  continue;
1251  }
1252  for (x=0; x < (ssize_t) image->columns; x++)
1253  {
1254  if (GetPixelAlpha(image,p) != OpaqueAlpha)
1255  {
1256  opaque=MagickFalse;
1257  break;
1258  }
1259  p+=(ptrdiff_t) GetPixelChannels(image);
1260  }
1261  }
1262  image_view=DestroyCacheView(image_view);
1263  if (opaque != MagickFalse)
1264  image->alpha_trait=UndefinedPixelTrait;
1265  break;
1266  }
1267  case OnAlphaChannel:
1268  {
1269  if ((image->alpha_trait & BlendPixelTrait) == 0)
1270  status=SetImageAlpha(image,OpaqueAlpha,exception);
1271  image->alpha_trait=BlendPixelTrait;
1272  break;
1273  }
1274  case OpaqueAlphaChannel:
1275  {
1276  status=SetImageAlpha(image,OpaqueAlpha,exception);
1277  break;
1278  }
1279  case RemoveAlphaChannel:
1280  {
1281  /*
1282  Remove transparency.
1283  */
1284  if ((image->alpha_trait & BlendPixelTrait) == 0)
1285  break;
1286  status=SetImageStorageClass(image,DirectClass,exception);
1287  if (status == MagickFalse)
1288  break;
1289  image_view=AcquireAuthenticCacheView(image,exception);
1290 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1291  #pragma omp parallel for schedule(static) shared(status) \
1292  magick_number_threads(image,image,image->rows,2)
1293 #endif
1294  for (y=0; y < (ssize_t) image->rows; y++)
1295  {
1296  Quantum
1297  *magick_restrict q;
1298 
1299  ssize_t
1300  x;
1301 
1302  if (status == MagickFalse)
1303  continue;
1304  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1305  exception);
1306  if (q == (Quantum *) NULL)
1307  {
1308  status=MagickFalse;
1309  continue;
1310  }
1311  for (x=0; x < (ssize_t) image->columns; x++)
1312  {
1313  FlattenPixelInfo(image,&image->background_color,
1314  image->background_color.alpha,q,(double) GetPixelAlpha(image,q),q);
1315  q+=(ptrdiff_t) GetPixelChannels(image);
1316  }
1317  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1318  status=MagickFalse;
1319  }
1320  image_view=DestroyCacheView(image_view);
1321  image->alpha_trait=image->background_color.alpha_trait;
1322  break;
1323  }
1324  case SetAlphaChannel:
1325  {
1326  if ((image->alpha_trait & BlendPixelTrait) == 0)
1327  status=SetImageAlpha(image,OpaqueAlpha,exception);
1328  break;
1329  }
1330  case ShapeAlphaChannel:
1331  {
1332  PixelInfo
1333  background;
1334 
1335  /*
1336  Remove transparency.
1337  */
1338  ConformPixelInfo(image,&image->background_color,&background,exception);
1339  background.alpha_trait=BlendPixelTrait;
1340  image->alpha_trait=BlendPixelTrait;
1341  status=SetImageStorageClass(image,DirectClass,exception);
1342  if (status == MagickFalse)
1343  break;
1344  image_view=AcquireAuthenticCacheView(image,exception);
1345 #if defined(MAGICKCORE_OPENMP_SUPPORT)
1346  #pragma omp parallel for schedule(static) shared(status) \
1347  magick_number_threads(image,image,image->rows,2)
1348 #endif
1349  for (y=0; y < (ssize_t) image->rows; y++)
1350  {
1351  PixelInfo
1352  pixel;
1353 
1354  Quantum
1355  *magick_restrict q;
1356 
1357  ssize_t
1358  x;
1359 
1360  if (status == MagickFalse)
1361  continue;
1362  q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,
1363  exception);
1364  if (q == (Quantum *) NULL)
1365  {
1366  status=MagickFalse;
1367  continue;
1368  }
1369  pixel=background;
1370  for (x=0; x < (ssize_t) image->columns; x++)
1371  {
1372  pixel.alpha=GetPixelIntensity(image,q);
1373  SetPixelViaPixelInfo(image,&pixel,q);
1374  q+=(ptrdiff_t) GetPixelChannels(image);
1375  }
1376  if (SyncCacheViewAuthenticPixels(image_view,exception) == MagickFalse)
1377  status=MagickFalse;
1378  }
1379  image_view=DestroyCacheView(image_view);
1380  break;
1381  }
1382  case TransparentAlphaChannel:
1383  {
1384  status=SetImageAlpha(image,TransparentAlpha,exception);
1385  break;
1386  }
1387  case UndefinedAlphaChannel:
1388  break;
1389  }
1390  if (status == MagickFalse)
1391  return(status);
1392  (void) SetPixelChannelMask(image,image->channel_mask);
1393  return(SyncImagePixelCache(image,exception));
1394 }
Definition: image.h:132