Home | History | Annotate | Download | only in MagickCore
      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 
    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 
    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 */
    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 */
    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 */
    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 */
    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 
    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 
    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