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