Home | History | Annotate | Download | only in MagickCore
      1 /*
      2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      3 %                                                                             %
      4 %                                                                             %
      5 %                                                                             %
      6 %    M   M    OOO    RRRR   PPPP   H   H   OOO   L       OOO    GGGG  Y   Y   %
      7 %    MM MM   O   O   R   R  P   P  H   H  O   O  L      O   O  G       Y Y    %
      8 %    M M M   O   O   RRRR   PPPP   HHHHH  O   O  L      O   O  G GGG    Y     %
      9 %    M   M   O   O   R R    P      H   H  O   O  L      O   O  G   G    Y     %
     10 %    M   M    OOO    R  R   P      H   H   OOO   LLLLL   OOO    GGG     Y     %
     11 %                                                                             %
     12 %                                                                             %
     13 %                        MagickCore Morphology Methods                        %
     14 %                                                                             %
     15 %                              Software Design                                %
     16 %                              Anthony Thyssen                                %
     17 %                               January 2010                                  %
     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 % Morphology is the application of various kernels, of any size or shape, to an
     37 % image in various ways (typically binary, but not always).
     38 %
     39 % Convolution (weighted sum or average) is just one specific type of
     40 % morphology. Just one that is very common for image bluring and sharpening
     41 % effects.  Not only 2D Gaussian blurring, but also 2-pass 1D Blurring.
     42 %
     43 % This module provides not only a general morphology function, and the ability
     44 % to apply more advanced or iterative morphologies, but also functions for the
     45 % generation of many different types of kernel arrays from user supplied
     46 % arguments. Prehaps even the generation of a kernel from a small image.
     47 */
     48 
     49 /*
     51   Include declarations.
     52 */
     53 #include "MagickCore/studio.h"
     54 #include "MagickCore/artifact.h"
     55 #include "MagickCore/cache-view.h"
     56 #include "MagickCore/channel.h"
     57 #include "MagickCore/color-private.h"
     58 #include "MagickCore/enhance.h"
     59 #include "MagickCore/exception.h"
     60 #include "MagickCore/exception-private.h"
     61 #include "MagickCore/gem.h"
     62 #include "MagickCore/gem-private.h"
     63 #include "MagickCore/image.h"
     64 #include "MagickCore/image-private.h"
     65 #include "MagickCore/linked-list.h"
     66 #include "MagickCore/list.h"
     67 #include "MagickCore/magick.h"
     68 #include "MagickCore/memory_.h"
     69 #include "MagickCore/memory-private.h"
     70 #include "MagickCore/monitor-private.h"
     71 #include "MagickCore/morphology.h"
     72 #include "MagickCore/morphology-private.h"
     73 #include "MagickCore/option.h"
     74 #include "MagickCore/pixel-accessor.h"
     75 #include "MagickCore/pixel-private.h"
     76 #include "MagickCore/prepress.h"
     77 #include "MagickCore/quantize.h"
     78 #include "MagickCore/resource_.h"
     79 #include "MagickCore/registry.h"
     80 #include "MagickCore/semaphore.h"
     81 #include "MagickCore/splay-tree.h"
     82 #include "MagickCore/statistic.h"
     83 #include "MagickCore/string_.h"
     84 #include "MagickCore/string-private.h"
     85 #include "MagickCore/thread-private.h"
     86 #include "MagickCore/token.h"
     87 #include "MagickCore/utility.h"
     88 #include "MagickCore/utility-private.h"
     89 
     90 /*
     92   Other global definitions used by module.
     93 */
     94 #define Minimize(assign,value) assign=MagickMin(assign,value)
     95 #define Maximize(assign,value) assign=MagickMax(assign,value)
     96 
     97 /* Integer Factorial Function - for a Binomial kernel */
     98 #if 1
     99 static inline size_t fact(size_t n)
    100 {
    101   size_t f,l;
    102   for(f=1, l=2; l <= n; f=f*l, l++);
    103   return(f);
    104 }
    105 #elif 1 /* glibc floating point alternatives */
    106 #define fact(n) ((size_t)tgamma((double)n+1))
    107 #else
    108 #define fact(n) ((size_t)lgamma((double)n+1))
    109 #endif
    110 
    111 
    112 /* Currently these are only internal to this module */
    113 static void
    114   CalcKernelMetaData(KernelInfo *),
    115   ExpandMirrorKernelInfo(KernelInfo *),
    116   ExpandRotateKernelInfo(KernelInfo *, const double),
    117   RotateKernelInfo(KernelInfo *, double);
    118 
    119 
    121 /* Quick function to find last kernel in a kernel list */
    122 static inline KernelInfo *LastKernelInfo(KernelInfo *kernel)
    123 {
    124   while (kernel->next != (KernelInfo *) NULL)
    125     kernel=kernel->next;
    126   return(kernel);
    127 }
    128 
    129 /*
    130 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    131 %                                                                             %
    132 %                                                                             %
    133 %                                                                             %
    134 %     A c q u i r e K e r n e l I n f o                                       %
    135 %                                                                             %
    136 %                                                                             %
    137 %                                                                             %
    138 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    139 %
    140 %  AcquireKernelInfo() takes the given string (generally supplied by the
    141 %  user) and converts it into a Morphology/Convolution Kernel.  This allows
    142 %  users to specify a kernel from a number of pre-defined kernels, or to fully
    143 %  specify their own kernel for a specific Convolution or Morphology
    144 %  Operation.
    145 %
    146 %  The kernel so generated can be any rectangular array of floating point
    147 %  values (doubles) with the 'control point' or 'pixel being affected'
    148 %  anywhere within that array of values.
    149 %
    150 %  Previously IM was restricted to a square of odd size using the exact
    151 %  center as origin, this is no longer the case, and any rectangular kernel
    152 %  with any value being declared the origin. This in turn allows the use of
    153 %  highly asymmetrical kernels.
    154 %
    155 %  The floating point values in the kernel can also include a special value
    156 %  known as 'nan' or 'not a number' to indicate that this value is not part
    157 %  of the kernel array. This allows you to shaped the kernel within its
    158 %  rectangular area. That is 'nan' values provide a 'mask' for the kernel
    159 %  shape.  However at least one non-nan value must be provided for correct
    160 %  working of a kernel.
    161 %
    162 %  The returned kernel should be freed using the DestroyKernelInfo() when you
    163 %  are finished with it.  Do not free this memory yourself.
    164 %
    165 %  Input kernel defintion strings can consist of any of three types.
    166 %
    167 %    "name:args[[@><]"
    168 %         Select from one of the built in kernels, using the name and
    169 %         geometry arguments supplied.  See AcquireKernelBuiltIn()
    170 %
    171 %    "WxH[+X+Y][@><]:num, num, num ..."
    172 %         a kernel of size W by H, with W*H floating point numbers following.
    173 %         the 'center' can be optionally be defined at +X+Y (such that +0+0
    174 %         is top left corner). If not defined the pixel in the center, for
    175 %         odd sizes, or to the immediate top or left of center for even sizes
    176 %         is automatically selected.
    177 %
    178 %    "num, num, num, num, ..."
    179 %         list of floating point numbers defining an 'old style' odd sized
    180 %         square kernel.  At least 9 values should be provided for a 3x3
    181 %         square kernel, 25 for a 5x5 square kernel, 49 for 7x7, etc.
    182 %         Values can be space or comma separated.  This is not recommended.
    183 %
    184 %  You can define a 'list of kernels' which can be used by some morphology
    185 %  operators A list is defined as a semi-colon separated list kernels.
    186 %
    187 %     " kernel ; kernel ; kernel ; "
    188 %
    189 %  Any extra ';' characters, at start, end or between kernel defintions are
    190 %  simply ignored.
    191 %
    192 %  The special flags will expand a single kernel, into a list of rotated
    193 %  kernels. A '@' flag will expand a 3x3 kernel into a list of 45-degree
    194 %  cyclic rotations, while a '>' will generate a list of 90-degree rotations.
    195 %  The '<' also exands using 90-degree rotates, but giving a 180-degree
    196 %  reflected kernel before the +/- 90-degree rotations, which can be important
    197 %  for Thinning operations.
    198 %
    199 %  Note that 'name' kernels will start with an alphabetic character while the
    200 %  new kernel specification has a ':' character in its specification string.
    201 %  If neither is the case, it is assumed an old style of a simple list of
    202 %  numbers generating a odd-sized square kernel has been given.
    203 %
    204 %  The format of the AcquireKernal method is:
    205 %
    206 %      KernelInfo *AcquireKernelInfo(const char *kernel_string)
    207 %
    208 %  A description of each parameter follows:
    209 %
    210 %    o kernel_string: the Morphology/Convolution kernel wanted.
    211 %
    212 */
    213 
    214 /* This was separated so that it could be used as a separate
    215 ** array input handling function, such as for -color-matrix
    216 */
    217 static KernelInfo *ParseKernelArray(const char *kernel_string)
    218 {
    219   KernelInfo
    220     *kernel;
    221 
    222   char
    223     token[MagickPathExtent];
    224 
    225   const char
    226     *p,
    227     *end;
    228 
    229   register ssize_t
    230     i;
    231 
    232   double
    233     nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
    234 
    235   MagickStatusType
    236     flags;
    237 
    238   GeometryInfo
    239     args;
    240 
    241   kernel=(KernelInfo *) AcquireQuantumMemory(1,sizeof(*kernel));
    242   if (kernel == (KernelInfo *) NULL)
    243     return(kernel);
    244   (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
    245   kernel->minimum = kernel->maximum = kernel->angle = 0.0;
    246   kernel->negative_range = kernel->positive_range = 0.0;
    247   kernel->type = UserDefinedKernel;
    248   kernel->next = (KernelInfo *) NULL;
    249   kernel->signature=MagickCoreSignature;
    250   if (kernel_string == (const char *) NULL)
    251     return(kernel);
    252 
    253   /* find end of this specific kernel definition string */
    254   end = strchr(kernel_string, ';');
    255   if ( end == (char *) NULL )
    256     end = strchr(kernel_string, '\0');
    257 
    258   /* clear flags - for Expanding kernel lists thorugh rotations */
    259    flags = NoValue;
    260 
    261   /* Has a ':' in argument - New user kernel specification
    262      FUTURE: this split on ':' could be done by StringToken()
    263    */
    264   p = strchr(kernel_string, ':');
    265   if ( p != (char *) NULL && p < end)
    266     {
    267       /* ParseGeometry() needs the geometry separated! -- Arrgghh */
    268       memcpy(token, kernel_string, (size_t) (p-kernel_string));
    269       token[p-kernel_string] = '\0';
    270       SetGeometryInfo(&args);
    271       flags = ParseGeometry(token, &args);
    272 
    273       /* Size handling and checks of geometry settings */
    274       if ( (flags & WidthValue) == 0 ) /* if no width then */
    275         args.rho = args.sigma;         /* then  width = height */
    276       if ( args.rho < 1.0 )            /* if width too small */
    277          args.rho = 1.0;               /* then  width = 1 */
    278       if ( args.sigma < 1.0 )          /* if height too small */
    279         args.sigma = args.rho;         /* then  height = width */
    280       kernel->width = (size_t)args.rho;
    281       kernel->height = (size_t)args.sigma;
    282 
    283       /* Offset Handling and Checks */
    284       if ( args.xi  < 0.0 || args.psi < 0.0 )
    285         return(DestroyKernelInfo(kernel));
    286       kernel->x = ((flags & XValue)!=0) ? (ssize_t)args.xi
    287                                         : (ssize_t) (kernel->width-1)/2;
    288       kernel->y = ((flags & YValue)!=0) ? (ssize_t)args.psi
    289                                         : (ssize_t) (kernel->height-1)/2;
    290       if ( kernel->x >= (ssize_t) kernel->width ||
    291            kernel->y >= (ssize_t) kernel->height )
    292         return(DestroyKernelInfo(kernel));
    293 
    294       p++; /* advance beyond the ':' */
    295     }
    296   else
    297     { /* ELSE - Old old specification, forming odd-square kernel */
    298       /* count up number of values given */
    299       p=(const char *) kernel_string;
    300       while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
    301         p++;  /* ignore "'" chars for convolve filter usage - Cristy */
    302       for (i=0; p < end; i++)
    303       {
    304         GetNextToken(p,&p,MagickPathExtent,token);
    305         if (*token == ',')
    306           GetNextToken(p,&p,MagickPathExtent,token);
    307       }
    308       /* set the size of the kernel - old sized square */
    309       kernel->width = kernel->height= (size_t) sqrt((double) i+1.0);
    310       kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
    311       p=(const char *) kernel_string;
    312       while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
    313         p++;  /* ignore "'" chars for convolve filter usage - Cristy */
    314     }
    315 
    316   /* Read in the kernel values from rest of input string argument */
    317   kernel->values=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
    318     kernel->width,kernel->height*sizeof(*kernel->values)));
    319   if (kernel->values == (MagickRealType *) NULL)
    320     return(DestroyKernelInfo(kernel));
    321   kernel->minimum=MagickMaximumValue;
    322   kernel->maximum=(-MagickMaximumValue);
    323   kernel->negative_range = kernel->positive_range = 0.0;
    324   for (i=0; (i < (ssize_t) (kernel->width*kernel->height)) && (p < end); i++)
    325   {
    326     GetNextToken(p,&p,MagickPathExtent,token);
    327     if (*token == ',')
    328       GetNextToken(p,&p,MagickPathExtent,token);
    329     if (    LocaleCompare("nan",token) == 0
    330         || LocaleCompare("-",token) == 0 ) {
    331       kernel->values[i] = nan; /* this value is not part of neighbourhood */
    332     }
    333     else {
    334       kernel->values[i] = StringToDouble(token,(char **) NULL);
    335       ( kernel->values[i] < 0)
    336           ?  ( kernel->negative_range += kernel->values[i] )
    337           :  ( kernel->positive_range += kernel->values[i] );
    338       Minimize(kernel->minimum, kernel->values[i]);
    339       Maximize(kernel->maximum, kernel->values[i]);
    340     }
    341   }
    342 
    343   /* sanity check -- no more values in kernel definition */
    344   GetNextToken(p,&p,MagickPathExtent,token);
    345   if ( *token != '\0' && *token != ';' && *token != '\'' )
    346     return(DestroyKernelInfo(kernel));
    347 
    348 #if 0
    349   /* this was the old method of handling a incomplete kernel */
    350   if ( i < (ssize_t) (kernel->width*kernel->height) ) {
    351     Minimize(kernel->minimum, kernel->values[i]);
    352     Maximize(kernel->maximum, kernel->values[i]);
    353     for ( ; i < (ssize_t) (kernel->width*kernel->height); i++)
    354       kernel->values[i]=0.0;
    355   }
    356 #else
    357   /* Number of values for kernel was not enough - Report Error */
    358   if ( i < (ssize_t) (kernel->width*kernel->height) )
    359     return(DestroyKernelInfo(kernel));
    360 #endif
    361 
    362   /* check that we recieved at least one real (non-nan) value! */
    363   if (kernel->minimum == MagickMaximumValue)
    364     return(DestroyKernelInfo(kernel));
    365 
    366   if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel size */
    367     ExpandRotateKernelInfo(kernel, 45.0); /* cyclic rotate 3x3 kernels */
    368   else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
    369     ExpandRotateKernelInfo(kernel, 90.0); /* 90 degree rotate of kernel */
    370   else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
    371     ExpandMirrorKernelInfo(kernel);       /* 90 degree mirror rotate */
    372 
    373   return(kernel);
    374 }
    375 
    376 static KernelInfo *ParseKernelName(const char *kernel_string,
    377   ExceptionInfo *exception)
    378 {
    379   char
    380     token[MagickPathExtent];
    381 
    382   const char
    383     *p,
    384     *end;
    385 
    386   GeometryInfo
    387     args;
    388 
    389   KernelInfo
    390     *kernel;
    391 
    392   MagickStatusType
    393     flags;
    394 
    395   ssize_t
    396     type;
    397 
    398   /* Parse special 'named' kernel */
    399   GetNextToken(kernel_string,&p,MagickPathExtent,token);
    400   type=ParseCommandOption(MagickKernelOptions,MagickFalse,token);
    401   if ( type < 0 || type == UserDefinedKernel )
    402     return((KernelInfo *) NULL);  /* not a valid named kernel */
    403 
    404   while (((isspace((int) ((unsigned char) *p)) != 0) ||
    405           (*p == ',') || (*p == ':' )) && (*p != '\0') && (*p != ';'))
    406     p++;
    407 
    408   end = strchr(p, ';'); /* end of this kernel defintion */
    409   if ( end == (char *) NULL )
    410     end = strchr(p, '\0');
    411 
    412   /* ParseGeometry() needs the geometry separated! -- Arrgghh */
    413   memcpy(token, p, (size_t) (end-p));
    414   token[end-p] = '\0';
    415   SetGeometryInfo(&args);
    416   flags = ParseGeometry(token, &args);
    417 
    418 #if 0
    419   /* For Debugging Geometry Input */
    420   (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
    421     flags, args.rho, args.sigma, args.xi, args.psi );
    422 #endif
    423 
    424   /* special handling of missing values in input string */
    425   switch( type ) {
    426     /* Shape Kernel Defaults */
    427     case UnityKernel:
    428       if ( (flags & WidthValue) == 0 )
    429         args.rho = 1.0;    /* Default scale = 1.0, zero is valid */
    430       break;
    431     case SquareKernel:
    432     case DiamondKernel:
    433     case OctagonKernel:
    434     case DiskKernel:
    435     case PlusKernel:
    436     case CrossKernel:
    437       if ( (flags & HeightValue) == 0 )
    438         args.sigma = 1.0;    /* Default scale = 1.0, zero is valid */
    439       break;
    440     case RingKernel:
    441       if ( (flags & XValue) == 0 )
    442         args.xi = 1.0;       /* Default scale = 1.0, zero is valid */
    443       break;
    444     case RectangleKernel:    /* Rectangle - set size defaults */
    445       if ( (flags & WidthValue) == 0 ) /* if no width then */
    446         args.rho = args.sigma;         /* then  width = height */
    447       if ( args.rho < 1.0 )            /* if width too small */
    448           args.rho = 3;                /* then  width = 3 */
    449       if ( args.sigma < 1.0 )          /* if height too small */
    450         args.sigma = args.rho;         /* then  height = width */
    451       if ( (flags & XValue) == 0 )     /* center offset if not defined */
    452         args.xi = (double)(((ssize_t)args.rho-1)/2);
    453       if ( (flags & YValue) == 0 )
    454         args.psi = (double)(((ssize_t)args.sigma-1)/2);
    455       break;
    456     /* Distance Kernel Defaults */
    457     case ChebyshevKernel:
    458     case ManhattanKernel:
    459     case OctagonalKernel:
    460     case EuclideanKernel:
    461       if ( (flags & HeightValue) == 0 )           /* no distance scale */
    462         args.sigma = 100.0;                       /* default distance scaling */
    463       else if ( (flags & AspectValue ) != 0 )     /* '!' flag */
    464         args.sigma = QuantumRange/(args.sigma+1); /* maximum pixel distance */
    465       else if ( (flags & PercentValue ) != 0 )    /* '%' flag */
    466         args.sigma *= QuantumRange/100.0;         /* percentage of color range */
    467       break;
    468     default:
    469       break;
    470   }
    471 
    472   kernel = AcquireKernelBuiltIn((KernelInfoType)type, &args, exception);
    473   if ( kernel == (KernelInfo *) NULL )
    474     return(kernel);
    475 
    476   /* global expand to rotated kernel list - only for single kernels */
    477   if ( kernel->next == (KernelInfo *) NULL ) {
    478     if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel args */
    479       ExpandRotateKernelInfo(kernel, 45.0);
    480     else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
    481       ExpandRotateKernelInfo(kernel, 90.0);
    482     else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
    483       ExpandMirrorKernelInfo(kernel);
    484   }
    485 
    486   return(kernel);
    487 }
    488 
    489 MagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string,
    490   ExceptionInfo *exception)
    491 {
    492   KernelInfo
    493     *kernel,
    494     *new_kernel;
    495 
    496   char
    497     *kernel_cache,
    498     token[MagickPathExtent];
    499 
    500   const char
    501     *p;
    502 
    503   if (kernel_string == (const char *) NULL)
    504     return(ParseKernelArray(kernel_string));
    505   p=kernel_string;
    506   kernel_cache=(char *) NULL;
    507   if (*kernel_string == '@')
    508     {
    509       kernel_cache=FileToString(kernel_string+1,~0UL,exception);
    510       if (kernel_cache == (char *) NULL)
    511         return((KernelInfo *) NULL);
    512       p=(const char *) kernel_cache;
    513     }
    514   kernel=NULL;
    515   while (GetNextToken(p,(const char **) NULL,MagickPathExtent,token), *token != '\0')
    516   {
    517     /* ignore extra or multiple ';' kernel separators */
    518     if (*token != ';')
    519       {
    520         /* tokens starting with alpha is a Named kernel */
    521         if (isalpha((int) ((unsigned char) *token)) != 0)
    522           new_kernel=ParseKernelName(p,exception);
    523         else /* otherwise a user defined kernel array */
    524           new_kernel=ParseKernelArray(p);
    525 
    526         /* Error handling -- this is not proper error handling! */
    527         if (new_kernel == (KernelInfo *) NULL)
    528           {
    529             if (kernel != (KernelInfo *) NULL)
    530               kernel=DestroyKernelInfo(kernel);
    531             return((KernelInfo *) NULL);
    532           }
    533 
    534         /* initialise or append the kernel list */
    535         if (kernel == (KernelInfo *) NULL)
    536           kernel=new_kernel;
    537         else
    538           LastKernelInfo(kernel)->next=new_kernel;
    539       }
    540 
    541     /* look for the next kernel in list */
    542     p=strchr(p,';');
    543     if (p == (char *) NULL)
    544       break;
    545     p++;
    546   }
    547   if (kernel_cache != (char *) NULL)
    548     kernel_cache=DestroyString(kernel_cache);
    549   return(kernel);
    550 }
    551 
    552 /*
    554 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    555 %                                                                             %
    556 %                                                                             %
    557 %                                                                             %
    558 %     A c q u i r e K e r n e l B u i l t I n                                 %
    559 %                                                                             %
    560 %                                                                             %
    561 %                                                                             %
    562 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    563 %
    564 %  AcquireKernelBuiltIn() returned one of the 'named' built-in types of
    565 %  kernels used for special purposes such as gaussian blurring, skeleton
    566 %  pruning, and edge distance determination.
    567 %
    568 %  They take a KernelType, and a set of geometry style arguments, which were
    569 %  typically decoded from a user supplied string, or from a more complex
    570 %  Morphology Method that was requested.
    571 %
    572 %  The format of the AcquireKernalBuiltIn method is:
    573 %
    574 %      KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
    575 %           const GeometryInfo args)
    576 %
    577 %  A description of each parameter follows:
    578 %
    579 %    o type: the pre-defined type of kernel wanted
    580 %
    581 %    o args: arguments defining or modifying the kernel
    582 %
    583 %  Convolution Kernels
    584 %
    585 %    Unity
    586 %       The a No-Op or Scaling single element kernel.
    587 %
    588 %    Gaussian:{radius},{sigma}
    589 %       Generate a two-dimensional gaussian kernel, as used by -gaussian.
    590 %       The sigma for the curve is required.  The resulting kernel is
    591 %       normalized,
    592 %
    593 %       If 'sigma' is zero, you get a single pixel on a field of zeros.
    594 %
    595 %       NOTE: that the 'radius' is optional, but if provided can limit (clip)
    596 %       the final size of the resulting kernel to a square 2*radius+1 in size.
    597 %       The radius should be at least 2 times that of the sigma value, or
    598 %       sever clipping and aliasing may result.  If not given or set to 0 the
    599 %       radius will be determined so as to produce the best minimal error
    600 %       result, which is usally much larger than is normally needed.
    601 %
    602 %    LoG:{radius},{sigma}
    603 %        "Laplacian of a Gaussian" or "Mexician Hat" Kernel.
    604 %        The supposed ideal edge detection, zero-summing kernel.
    605 %
    606 %        An alturnative to this kernel is to use a "DoG" with a sigma ratio of
    607 %        approx 1.6 (according to wikipedia).
    608 %
    609 %    DoG:{radius},{sigma1},{sigma2}
    610 %        "Difference of Gaussians" Kernel.
    611 %        As "Gaussian" but with a gaussian produced by 'sigma2' subtracted
    612 %        from the gaussian produced by 'sigma1'. Typically sigma2 > sigma1.
    613 %        The result is a zero-summing kernel.
    614 %
    615 %    Blur:{radius},{sigma}[,{angle}]
    616 %       Generates a 1 dimensional or linear gaussian blur, at the angle given
    617 %       (current restricted to orthogonal angles).  If a 'radius' is given the
    618 %       kernel is clipped to a width of 2*radius+1.  Kernel can be rotated
    619 %       by a 90 degree angle.
    620 %
    621 %       If 'sigma' is zero, you get a single pixel on a field of zeros.
    622 %
    623 %       Note that two convolutions with two "Blur" kernels perpendicular to
    624 %       each other, is equivalent to a far larger "Gaussian" kernel with the
    625 %       same sigma value, However it is much faster to apply. This is how the
    626 %       "-blur" operator actually works.
    627 %
    628 %    Comet:{width},{sigma},{angle}
    629 %       Blur in one direction only, much like how a bright object leaves
    630 %       a comet like trail.  The Kernel is actually half a gaussian curve,
    631 %       Adding two such blurs in opposite directions produces a Blur Kernel.
    632 %       Angle can be rotated in multiples of 90 degrees.
    633 %
    634 %       Note that the first argument is the width of the kernel and not the
    635 %       radius of the kernel.
    636 %
    637 %    Binomial:[{radius}]
    638 %       Generate a discrete kernel using a 2 dimentional Pascel's Triangle
    639 %       of values. Used for special forma of image filters.
    640 %
    641 %    # Still to be implemented...
    642 %    #
    643 %    # Filter2D
    644 %    # Filter1D
    645 %    #    Set kernel values using a resize filter, and given scale (sigma)
    646 %    #    Cylindrical or Linear.   Is this possible with an image?
    647 %    #
    648 %
    649 %  Named Constant Convolution Kernels
    650 %
    651 %  All these are unscaled, zero-summing kernels by default. As such for
    652 %  non-HDRI version of ImageMagick some form of normalization, user scaling,
    653 %  and biasing the results is recommended, to prevent the resulting image
    654 %  being 'clipped'.
    655 %
    656 %  The 3x3 kernels (most of these) can be circularly rotated in multiples of
    657 %  45 degrees to generate the 8 angled varients of each of the kernels.
    658 %
    659 %    Laplacian:{type}
    660 %      Discrete Lapacian Kernels, (without normalization)
    661 %        Type 0 :  3x3 with center:8 surounded by -1  (8 neighbourhood)
    662 %        Type 1 :  3x3 with center:4 edge:-1 corner:0 (4 neighbourhood)
    663 %        Type 2 :  3x3 with center:4 edge:1 corner:-2
    664 %        Type 3 :  3x3 with center:4 edge:-2 corner:1
    665 %        Type 5 :  5x5 laplacian
    666 %        Type 7 :  7x7 laplacian
    667 %        Type 15 : 5x5 LoG (sigma approx 1.4)
    668 %        Type 19 : 9x9 LoG (sigma approx 1.4)
    669 %
    670 %    Sobel:{angle}
    671 %      Sobel 'Edge' convolution kernel (3x3)
    672 %          | -1, 0, 1 |
    673 %          | -2, 0,-2 |
    674 %          | -1, 0, 1 |
    675 %
    676 %    Roberts:{angle}
    677 %      Roberts convolution kernel (3x3)
    678 %          |  0, 0, 0 |
    679 %          | -1, 1, 0 |
    680 %          |  0, 0, 0 |
    681 %
    682 %    Prewitt:{angle}
    683 %      Prewitt Edge convolution kernel (3x3)
    684 %          | -1, 0, 1 |
    685 %          | -1, 0, 1 |
    686 %          | -1, 0, 1 |
    687 %
    688 %    Compass:{angle}
    689 %      Prewitt's "Compass" convolution kernel (3x3)
    690 %          | -1, 1, 1 |
    691 %          | -1,-2, 1 |
    692 %          | -1, 1, 1 |
    693 %
    694 %    Kirsch:{angle}
    695 %      Kirsch's "Compass" convolution kernel (3x3)
    696 %          | -3,-3, 5 |
    697 %          | -3, 0, 5 |
    698 %          | -3,-3, 5 |
    699 %
    700 %    FreiChen:{angle}
    701 %      Frei-Chen Edge Detector is based on a kernel that is similar to
    702 %      the Sobel Kernel, but is designed to be isotropic. That is it takes
    703 %      into account the distance of the diagonal in the kernel.
    704 %
    705 %          |   1,     0,   -1     |
    706 %          | sqrt(2), 0, -sqrt(2) |
    707 %          |   1,     0,   -1     |
    708 %
    709 %    FreiChen:{type},{angle}
    710 %
    711 %      Frei-Chen Pre-weighted kernels...
    712 %
    713 %        Type 0:  default un-nomalized version shown above.
    714 %
    715 %        Type 1: Orthogonal Kernel (same as type 11 below)
    716 %          |   1,     0,   -1     |
    717 %          | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
    718 %          |   1,     0,   -1     |
    719 %
    720 %        Type 2: Diagonal form of Kernel...
    721 %          |   1,     sqrt(2),    0     |
    722 %          | sqrt(2),   0,     -sqrt(2) | / 2*sqrt(2)
    723 %          |   0,    -sqrt(2)    -1     |
    724 %
    725 %      However this kernel is als at the heart of the FreiChen Edge Detection
    726 %      Process which uses a set of 9 specially weighted kernel.  These 9
    727 %      kernels not be normalized, but directly applied to the image. The
    728 %      results is then added together, to produce the intensity of an edge in
    729 %      a specific direction.  The square root of the pixel value can then be
    730 %      taken as the cosine of the edge, and at least 2 such runs at 90 degrees
    731 %      from each other, both the direction and the strength of the edge can be
    732 %      determined.
    733 %
    734 %        Type 10: All 9 of the following pre-weighted kernels...
    735 %
    736 %        Type 11: |   1,     0,   -1     |
    737 %                 | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
    738 %                 |   1,     0,   -1     |
    739 %
    740 %        Type 12: | 1, sqrt(2), 1 |
    741 %                 | 0,   0,     0 | / 2*sqrt(2)
    742 %                 | 1, sqrt(2), 1 |
    743 %
    744 %        Type 13: | sqrt(2), -1,    0     |
    745 %                 |  -1,      0,    1     | / 2*sqrt(2)
    746 %                 |   0,      1, -sqrt(2) |
    747 %
    748 %        Type 14: |    0,     1, -sqrt(2) |
    749 %                 |   -1,     0,     1    | / 2*sqrt(2)
    750 %                 | sqrt(2), -1,     0    |
    751 %
    752 %        Type 15: | 0, -1, 0 |
    753 %                 | 1,  0, 1 | / 2
    754 %                 | 0, -1, 0 |
    755 %
    756 %        Type 16: |  1, 0, -1 |
    757 %                 |  0, 0,  0 | / 2
    758 %                 | -1, 0,  1 |
    759 %
    760 %        Type 17: |  1, -2,  1 |
    761 %                 | -2,  4, -2 | / 6
    762 %                 | -1, -2,  1 |
    763 %
    764 %        Type 18: | -2, 1, -2 |
    765 %                 |  1, 4,  1 | / 6
    766 %                 | -2, 1, -2 |
    767 %
    768 %        Type 19: | 1, 1, 1 |
    769 %                 | 1, 1, 1 | / 3
    770 %                 | 1, 1, 1 |
    771 %
    772 %      The first 4 are for edge detection, the next 4 are for line detection
    773 %      and the last is to add a average component to the results.
    774 %
    775 %      Using a special type of '-1' will return all 9 pre-weighted kernels
    776 %      as a multi-kernel list, so that you can use them directly (without
    777 %      normalization) with the special "-set option:morphology:compose Plus"
    778 %      setting to apply the full FreiChen Edge Detection Technique.
    779 %
    780 %      If 'type' is large it will be taken to be an actual rotation angle for
    781 %      the default FreiChen (type 0) kernel.  As such  FreiChen:45  will look
    782 %      like a  Sobel:45  but with 'sqrt(2)' instead of '2' values.
    783 %
    784 %      WARNING: The above was layed out as per
    785 %          http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf
    786 %      But rotated 90 degrees so direction is from left rather than the top.
    787 %      I have yet to find any secondary confirmation of the above. The only
    788 %      other source found was actual source code at
    789 %          http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf
    790 %      Neigher paper defineds the kernels in a way that looks locical or
    791 %      correct when taken as a whole.
    792 %
    793 %  Boolean Kernels
    794 %
    795 %    Diamond:[{radius}[,{scale}]]
    796 %       Generate a diamond shaped kernel with given radius to the points.
    797 %       Kernel size will again be radius*2+1 square and defaults to radius 1,
    798 %       generating a 3x3 kernel that is slightly larger than a square.
    799 %
    800 %    Square:[{radius}[,{scale}]]
    801 %       Generate a square shaped kernel of size radius*2+1, and defaulting
    802 %       to a 3x3 (radius 1).
    803 %
    804 %    Octagon:[{radius}[,{scale}]]
    805 %       Generate octagonal shaped kernel of given radius and constant scale.
    806 %       Default radius is 3 producing a 7x7 kernel. A radius of 1 will result
    807 %       in "Diamond" kernel.
    808 %
    809 %    Disk:[{radius}[,{scale}]]
    810 %       Generate a binary disk, thresholded at the radius given, the radius
    811 %       may be a float-point value. Final Kernel size is floor(radius)*2+1
    812 %       square. A radius of 5.3 is the default.
    813 %
    814 %       NOTE: That a low radii Disk kernels produce the same results as
    815 %       many of the previously defined kernels, but differ greatly at larger
    816 %       radii.  Here is a table of equivalences...
    817 %          "Disk:1"    => "Diamond", "Octagon:1", or "Cross:1"
    818 %          "Disk:1.5"  => "Square"
    819 %          "Disk:2"    => "Diamond:2"
    820 %          "Disk:2.5"  => "Octagon"
    821 %          "Disk:2.9"  => "Square:2"
    822 %          "Disk:3.5"  => "Octagon:3"
    823 %          "Disk:4.5"  => "Octagon:4"
    824 %          "Disk:5.4"  => "Octagon:5"
    825 %          "Disk:6.4"  => "Octagon:6"
    826 %       All other Disk shapes are unique to this kernel, but because a "Disk"
    827 %       is more circular when using a larger radius, using a larger radius is
    828 %       preferred over iterating the morphological operation.
    829 %
    830 %    Rectangle:{geometry}
    831 %       Simply generate a rectangle of 1's with the size given. You can also
    832 %       specify the location of the 'control point', otherwise the closest
    833 %       pixel to the center of the rectangle is selected.
    834 %
    835 %       Properly centered and odd sized rectangles work the best.
    836 %
    837 %  Symbol Dilation Kernels
    838 %
    839 %    These kernel is not a good general morphological kernel, but is used
    840 %    more for highlighting and marking any single pixels in an image using,
    841 %    a "Dilate" method as appropriate.
    842 %
    843 %    For the same reasons iterating these kernels does not produce the
    844 %    same result as using a larger radius for the symbol.
    845 %
    846 %    Plus:[{radius}[,{scale}]]
    847 %    Cross:[{radius}[,{scale}]]
    848 %       Generate a kernel in the shape of a 'plus' or a 'cross' with
    849 %       a each arm the length of the given radius (default 2).
    850 %
    851 %       NOTE: "plus:1" is equivalent to a "Diamond" kernel.
    852 %
    853 %    Ring:{radius1},{radius2}[,{scale}]
    854 %       A ring of the values given that falls between the two radii.
    855 %       Defaults to a ring of approximataly 3 radius in a 7x7 kernel.
    856 %       This is the 'edge' pixels of the default "Disk" kernel,
    857 %       More specifically, "Ring" -> "Ring:2.5,3.5,1.0"
    858 %
    859 %  Hit and Miss Kernels
    860 %
    861 %    Peak:radius1,radius2
    862 %       Find any peak larger than the pixels the fall between the two radii.
    863 %       The default ring of pixels is as per "Ring".
    864 %    Edges
    865 %       Find flat orthogonal edges of a binary shape
    866 %    Corners
    867 %       Find 90 degree corners of a binary shape
    868 %    Diagonals:type
    869 %       A special kernel to thin the 'outside' of diagonals
    870 %    LineEnds:type
    871 %       Find end points of lines (for pruning a skeletion)
    872 %       Two types of lines ends (default to both) can be searched for
    873 %         Type 0: All line ends
    874 %         Type 1: single kernel for 4-conneected line ends
    875 %         Type 2: single kernel for simple line ends
    876 %    LineJunctions
    877 %       Find three line junctions (within a skeletion)
    878 %         Type 0: all line junctions
    879 %         Type 1: Y Junction kernel
    880 %         Type 2: Diagonal T Junction kernel
    881 %         Type 3: Orthogonal T Junction kernel
    882 %         Type 4: Diagonal X Junction kernel
    883 %         Type 5: Orthogonal + Junction kernel
    884 %    Ridges:type
    885 %       Find single pixel ridges or thin lines
    886 %         Type 1: Fine single pixel thick lines and ridges
    887 %         Type 2: Find two pixel thick lines and ridges
    888 %    ConvexHull
    889 %       Octagonal Thickening Kernel, to generate convex hulls of 45 degrees
    890 %    Skeleton:type
    891 %       Traditional skeleton generating kernels.
    892 %         Type 1: Tradional Skeleton kernel (4 connected skeleton)
    893 %         Type 2: HIPR2 Skeleton kernel (8 connected skeleton)
    894 %         Type 3: Thinning skeleton based on a ressearch paper by
    895 %                 Dan S. Bloomberg (Default Type)
    896 %    ThinSE:type
    897 %       A huge variety of Thinning Kernels designed to preserve conectivity.
    898 %       many other kernel sets use these kernels as source definitions.
    899 %       Type numbers are 41-49, 81-89, 481, and 482 which are based on
    900 %       the super and sub notations used in the source research paper.
    901 %
    902 %  Distance Measuring Kernels
    903 %
    904 %    Different types of distance measuring methods, which are used with the
    905 %    a 'Distance' morphology method for generating a gradient based on
    906 %    distance from an edge of a binary shape, though there is a technique
    907 %    for handling a anti-aliased shape.
    908 %
    909 %    See the 'Distance' Morphological Method, for information of how it is
    910 %    applied.
    911 %
    912 %    Chebyshev:[{radius}][x{scale}[%!]]
    913 %       Chebyshev Distance (also known as Tchebychev or Chessboard distance)
    914 %       is a value of one to any neighbour, orthogonal or diagonal. One why
    915 %       of thinking of it is the number of squares a 'King' or 'Queen' in
    916 %       chess needs to traverse reach any other position on a chess board.
    917 %       It results in a 'square' like distance function, but one where
    918 %       diagonals are given a value that is closer than expected.
    919 %
    920 %    Manhattan:[{radius}][x{scale}[%!]]
    921 %       Manhattan Distance (also known as Rectilinear, City Block, or the Taxi
    922 %       Cab distance metric), it is the distance needed when you can only
    923 %       travel in horizontal or vertical directions only.  It is the
    924 %       distance a 'Rook' in chess would have to travel, and results in a
    925 %       diamond like distances, where diagonals are further than expected.
    926 %
    927 %    Octagonal:[{radius}][x{scale}[%!]]
    928 %       An interleving of Manhatten and Chebyshev metrics producing an
    929 %       increasing octagonally shaped distance.  Distances matches those of
    930 %       the "Octagon" shaped kernel of the same radius.  The minimum radius
    931 %       and default is 2, producing a 5x5 kernel.
    932 %
    933 %    Euclidean:[{radius}][x{scale}[%!]]
    934 %       Euclidean distance is the 'direct' or 'as the crow flys' distance.
    935 %       However by default the kernel size only has a radius of 1, which
    936 %       limits the distance to 'Knight' like moves, with only orthogonal and
    937 %       diagonal measurements being correct.  As such for the default kernel
    938 %       you will get octagonal like distance function.
    939 %
    940 %       However using a larger radius such as "Euclidean:4" you will get a
    941 %       much smoother distance gradient from the edge of the shape. Especially
    942 %       if the image is pre-processed to include any anti-aliasing pixels.
    943 %       Of course a larger kernel is slower to use, and not always needed.
    944 %
    945 %    The first three Distance Measuring Kernels will only generate distances
    946 %    of exact multiples of {scale} in binary images. As such you can use a
    947 %    scale of 1 without loosing any information.  However you also need some
    948 %    scaling when handling non-binary anti-aliased shapes.
    949 %
    950 %    The "Euclidean" Distance Kernel however does generate a non-integer
    951 %    fractional results, and as such scaling is vital even for binary shapes.
    952 %
    953 */
    954 
    955 MagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
    956   const GeometryInfo *args,ExceptionInfo *exception)
    957 {
    958   KernelInfo
    959     *kernel;
    960 
    961   register ssize_t
    962     i;
    963 
    964   register ssize_t
    965     u,
    966     v;
    967 
    968   double
    969     nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
    970 
    971   /* Generate a new empty kernel if needed */
    972   kernel=(KernelInfo *) NULL;
    973   switch(type) {
    974     case UndefinedKernel:    /* These should not call this function */
    975     case UserDefinedKernel:
    976       assert("Should not call this function" != (char *) NULL);
    977       break;
    978     case LaplacianKernel:   /* Named Descrete Convolution Kernels */
    979     case SobelKernel:       /* these are defined using other kernels */
    980     case RobertsKernel:
    981     case PrewittKernel:
    982     case CompassKernel:
    983     case KirschKernel:
    984     case FreiChenKernel:
    985     case EdgesKernel:       /* Hit and Miss kernels */
    986     case CornersKernel:
    987     case DiagonalsKernel:
    988     case LineEndsKernel:
    989     case LineJunctionsKernel:
    990     case RidgesKernel:
    991     case ConvexHullKernel:
    992     case SkeletonKernel:
    993     case ThinSEKernel:
    994       break;               /* A pre-generated kernel is not needed */
    995 #if 0
    996     /* set to 1 to do a compile-time check that we haven't missed anything */
    997     case UnityKernel:
    998     case GaussianKernel:
    999     case DoGKernel:
   1000     case LoGKernel:
   1001     case BlurKernel:
   1002     case CometKernel:
   1003     case BinomialKernel:
   1004     case DiamondKernel:
   1005     case SquareKernel:
   1006     case RectangleKernel:
   1007     case OctagonKernel:
   1008     case DiskKernel:
   1009     case PlusKernel:
   1010     case CrossKernel:
   1011     case RingKernel:
   1012     case PeaksKernel:
   1013     case ChebyshevKernel:
   1014     case ManhattanKernel:
   1015     case OctangonalKernel:
   1016     case EuclideanKernel:
   1017 #else
   1018     default:
   1019 #endif
   1020       /* Generate the base Kernel Structure */
   1021       kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
   1022       if (kernel == (KernelInfo *) NULL)
   1023         return(kernel);
   1024       (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
   1025       kernel->minimum = kernel->maximum = kernel->angle = 0.0;
   1026       kernel->negative_range = kernel->positive_range = 0.0;
   1027       kernel->type = type;
   1028       kernel->next = (KernelInfo *) NULL;
   1029       kernel->signature=MagickCoreSignature;
   1030       break;
   1031   }
   1032 
   1033   switch(type) {
   1034     /*
   1035       Convolution Kernels
   1036     */
   1037     case UnityKernel:
   1038       {
   1039         kernel->height = kernel->width = (size_t) 1;
   1040         kernel->x = kernel->y = (ssize_t) 0;
   1041         kernel->values=(MagickRealType *) MagickAssumeAligned(
   1042           AcquireAlignedMemory(1,sizeof(*kernel->values)));
   1043         if (kernel->values == (MagickRealType *) NULL)
   1044           return(DestroyKernelInfo(kernel));
   1045         kernel->maximum = kernel->values[0] = args->rho;
   1046         break;
   1047       }
   1048       break;
   1049     case GaussianKernel:
   1050     case DoGKernel:
   1051     case LoGKernel:
   1052       { double
   1053           sigma = fabs(args->sigma),
   1054           sigma2 = fabs(args->xi),
   1055           A, B, R;
   1056 
   1057         if ( args->rho >= 1.0 )
   1058           kernel->width = (size_t)args->rho*2+1;
   1059         else if ( (type != DoGKernel) || (sigma >= sigma2) )
   1060           kernel->width = GetOptimalKernelWidth2D(args->rho,sigma);
   1061         else
   1062           kernel->width = GetOptimalKernelWidth2D(args->rho,sigma2);
   1063         kernel->height = kernel->width;
   1064         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
   1065         kernel->values=(MagickRealType *) MagickAssumeAligned(
   1066           AcquireAlignedMemory(kernel->width,kernel->height*
   1067           sizeof(*kernel->values)));
   1068         if (kernel->values == (MagickRealType *) NULL)
   1069           return(DestroyKernelInfo(kernel));
   1070 
   1071         /* WARNING: The following generates a 'sampled gaussian' kernel.
   1072          * What we really want is a 'discrete gaussian' kernel.
   1073          *
   1074          * How to do this is I don't know, but appears to be basied on the
   1075          * Error Function 'erf()' (intergral of a gaussian)
   1076          */
   1077 
   1078         if ( type == GaussianKernel || type == DoGKernel )
   1079           { /* Calculate a Gaussian,  OR positive half of a DoG */
   1080             if ( sigma > MagickEpsilon )
   1081               { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
   1082                 B = (double) (1.0/(Magick2PI*sigma*sigma));
   1083                 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
   1084                   for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
   1085                       kernel->values[i] = exp(-((double)(u*u+v*v))*A)*B;
   1086               }
   1087             else /* limiting case - a unity (normalized Dirac) kernel */
   1088               { (void) ResetMagickMemory(kernel->values,0, (size_t)
   1089                   kernel->width*kernel->height*sizeof(*kernel->values));
   1090                 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
   1091               }
   1092           }
   1093 
   1094         if ( type == DoGKernel )
   1095           { /* Subtract a Negative Gaussian for "Difference of Gaussian" */
   1096             if ( sigma2 > MagickEpsilon )
   1097               { sigma = sigma2;                /* simplify loop expressions */
   1098                 A = 1.0/(2.0*sigma*sigma);
   1099                 B = (double) (1.0/(Magick2PI*sigma*sigma));
   1100                 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
   1101                   for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
   1102                     kernel->values[i] -= exp(-((double)(u*u+v*v))*A)*B;
   1103               }
   1104             else /* limiting case - a unity (normalized Dirac) kernel */
   1105               kernel->values[kernel->x+kernel->y*kernel->width] -= 1.0;
   1106           }
   1107 
   1108         if ( type == LoGKernel )
   1109           { /* Calculate a Laplacian of a Gaussian - Or Mexician Hat */
   1110             if ( sigma > MagickEpsilon )
   1111               { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
   1112                 B = (double) (1.0/(MagickPI*sigma*sigma*sigma*sigma));
   1113                 for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
   1114                   for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
   1115                     { R = ((double)(u*u+v*v))*A;
   1116                       kernel->values[i] = (1-R)*exp(-R)*B;
   1117                     }
   1118               }
   1119             else /* special case - generate a unity kernel */
   1120               { (void) ResetMagickMemory(kernel->values,0, (size_t)
   1121                   kernel->width*kernel->height*sizeof(*kernel->values));
   1122                 kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
   1123               }
   1124           }
   1125 
   1126         /* Note the above kernels may have been 'clipped' by a user defined
   1127         ** radius, producing a smaller (darker) kernel.  Also for very small
   1128         ** sigma's (> 0.1) the central value becomes larger than one, and thus
   1129         ** producing a very bright kernel.
   1130         **
   1131         ** Normalization will still be needed.
   1132         */
   1133 
   1134         /* Normalize the 2D Gaussian Kernel
   1135         **
   1136         ** NB: a CorrelateNormalize performs a normal Normalize if
   1137         ** there are no negative values.
   1138         */
   1139         CalcKernelMetaData(kernel);  /* the other kernel meta-data */
   1140         ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
   1141 
   1142         break;
   1143       }
   1144     case BlurKernel:
   1145       { double
   1146           sigma = fabs(args->sigma),
   1147           alpha, beta;
   1148 
   1149         if ( args->rho >= 1.0 )
   1150           kernel->width = (size_t)args->rho*2+1;
   1151         else
   1152           kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
   1153         kernel->height = 1;
   1154         kernel->x = (ssize_t) (kernel->width-1)/2;
   1155         kernel->y = 0;
   1156         kernel->negative_range = kernel->positive_range = 0.0;
   1157         kernel->values=(MagickRealType *) MagickAssumeAligned(
   1158           AcquireAlignedMemory(kernel->width,kernel->height*
   1159           sizeof(*kernel->values)));
   1160         if (kernel->values == (MagickRealType *) NULL)
   1161           return(DestroyKernelInfo(kernel));
   1162 
   1163 #if 1
   1164 #define KernelRank 3
   1165         /* Formula derived from GetBlurKernel() in "effect.c" (plus bug fix).
   1166         ** It generates a gaussian 3 times the width, and compresses it into
   1167         ** the expected range.  This produces a closer normalization of the
   1168         ** resulting kernel, especially for very low sigma values.
   1169         ** As such while wierd it is prefered.
   1170         **
   1171         ** I am told this method originally came from Photoshop.
   1172         **
   1173         ** A properly normalized curve is generated (apart from edge clipping)
   1174         ** even though we later normalize the result (for edge clipping)
   1175         ** to allow the correct generation of a "Difference of Blurs".
   1176         */
   1177 
   1178         /* initialize */
   1179         v = (ssize_t) (kernel->width*KernelRank-1)/2; /* start/end points to fit range */
   1180         (void) ResetMagickMemory(kernel->values,0, (size_t)
   1181           kernel->width*kernel->height*sizeof(*kernel->values));
   1182         /* Calculate a Positive 1D Gaussian */
   1183         if ( sigma > MagickEpsilon )
   1184           { sigma *= KernelRank;               /* simplify loop expressions */
   1185             alpha = 1.0/(2.0*sigma*sigma);
   1186             beta= (double) (1.0/(MagickSQ2PI*sigma ));
   1187             for ( u=-v; u <= v; u++) {
   1188               kernel->values[(u+v)/KernelRank] +=
   1189                               exp(-((double)(u*u))*alpha)*beta;
   1190             }
   1191           }
   1192         else /* special case - generate a unity kernel */
   1193           kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
   1194 #else
   1195         /* Direct calculation without curve averaging
   1196            This is equivelent to a KernelRank of 1 */
   1197 
   1198         /* Calculate a Positive Gaussian */
   1199         if ( sigma > MagickEpsilon )
   1200           { alpha = 1.0/(2.0*sigma*sigma);    /* simplify loop expressions */
   1201             beta = 1.0/(MagickSQ2PI*sigma);
   1202             for ( i=0, u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
   1203               kernel->values[i] = exp(-((double)(u*u))*alpha)*beta;
   1204           }
   1205         else /* special case - generate a unity kernel */
   1206           { (void) ResetMagickMemory(kernel->values,0, (size_t)
   1207               kernel->width*kernel->height*sizeof(*kernel->values));
   1208             kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
   1209           }
   1210 #endif
   1211         /* Note the above kernel may have been 'clipped' by a user defined
   1212         ** radius, producing a smaller (darker) kernel.  Also for very small
   1213         ** sigma's (> 0.1) the central value becomes larger than one, as a
   1214         ** result of not generating a actual 'discrete' kernel, and thus
   1215         ** producing a very bright 'impulse'.
   1216         **
   1217         ** Becuase of these two factors Normalization is required!
   1218         */
   1219 
   1220         /* Normalize the 1D Gaussian Kernel
   1221         **
   1222         ** NB: a CorrelateNormalize performs a normal Normalize if
   1223         ** there are no negative values.
   1224         */
   1225         CalcKernelMetaData(kernel);  /* the other kernel meta-data */
   1226         ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
   1227 
   1228         /* rotate the 1D kernel by given angle */
   1229         RotateKernelInfo(kernel, args->xi );
   1230         break;
   1231       }
   1232     case CometKernel:
   1233       { double
   1234           sigma = fabs(args->sigma),
   1235           A;
   1236 
   1237         if ( args->rho < 1.0 )
   1238           kernel->width = (GetOptimalKernelWidth1D(args->rho,sigma)-1)/2+1;
   1239         else
   1240           kernel->width = (size_t)args->rho;
   1241         kernel->x = kernel->y = 0;
   1242         kernel->height = 1;
   1243         kernel->negative_range = kernel->positive_range = 0.0;
   1244         kernel->values=(MagickRealType *) MagickAssumeAligned(
   1245           AcquireAlignedMemory(kernel->width,kernel->height*
   1246           sizeof(*kernel->values)));
   1247         if (kernel->values == (MagickRealType *) NULL)
   1248           return(DestroyKernelInfo(kernel));
   1249 
   1250         /* A comet blur is half a 1D gaussian curve, so that the object is
   1251         ** blurred in one direction only.  This may not be quite the right
   1252         ** curve to use so may change in the future. The function must be
   1253         ** normalised after generation, which also resolves any clipping.
   1254         **
   1255         ** As we are normalizing and not subtracting gaussians,
   1256         ** there is no need for a divisor in the gaussian formula
   1257         **
   1258         ** It is less comples
   1259         */
   1260         if ( sigma > MagickEpsilon )
   1261           {
   1262 #if 1
   1263 #define KernelRank 3
   1264             v = (ssize_t) kernel->width*KernelRank; /* start/end points */
   1265             (void) ResetMagickMemory(kernel->values,0, (size_t)
   1266               kernel->width*sizeof(*kernel->values));
   1267             sigma *= KernelRank;            /* simplify the loop expression */
   1268             A = 1.0/(2.0*sigma*sigma);
   1269             /* B = 1.0/(MagickSQ2PI*sigma); */
   1270             for ( u=0; u < v; u++) {
   1271               kernel->values[u/KernelRank] +=
   1272                   exp(-((double)(u*u))*A);
   1273               /*  exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
   1274             }
   1275             for (i=0; i < (ssize_t) kernel->width; i++)
   1276               kernel->positive_range += kernel->values[i];
   1277 #else
   1278             A = 1.0/(2.0*sigma*sigma);     /* simplify the loop expression */
   1279             /* B = 1.0/(MagickSQ2PI*sigma); */
   1280             for ( i=0; i < (ssize_t) kernel->width; i++)
   1281               kernel->positive_range +=
   1282                 kernel->values[i] = exp(-((double)(i*i))*A);
   1283                 /* exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
   1284 #endif
   1285           }
   1286         else /* special case - generate a unity kernel */
   1287           { (void) ResetMagickMemory(kernel->values,0, (size_t)
   1288               kernel->width*kernel->height*sizeof(*kernel->values));
   1289             kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
   1290             kernel->positive_range = 1.0;
   1291           }
   1292 
   1293         kernel->minimum = 0.0;
   1294         kernel->maximum = kernel->values[0];
   1295         kernel->negative_range = 0.0;
   1296 
   1297         ScaleKernelInfo(kernel, 1.0, NormalizeValue); /* Normalize */
   1298         RotateKernelInfo(kernel, args->xi); /* Rotate by angle */
   1299         break;
   1300       }
   1301     case BinomialKernel:
   1302       {
   1303         size_t
   1304           order_f;
   1305 
   1306         if (args->rho < 1.0)
   1307           kernel->width = kernel->height = 3;  /* default radius = 1 */
   1308         else
   1309           kernel->width = kernel->height = ((size_t)args->rho)*2+1;
   1310         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
   1311 
   1312         order_f = fact(kernel->width-1);
   1313 
   1314         kernel->values=(MagickRealType *) MagickAssumeAligned(
   1315           AcquireAlignedMemory(kernel->width,kernel->height*
   1316           sizeof(*kernel->values)));
   1317         if (kernel->values == (MagickRealType *) NULL)
   1318           return(DestroyKernelInfo(kernel));
   1319 
   1320         /* set all kernel values within diamond area to scale given */
   1321         for ( i=0, v=0; v < (ssize_t)kernel->height; v++)
   1322           { size_t
   1323               alpha = order_f / ( fact((size_t) v) * fact(kernel->height-v-1) );
   1324             for ( u=0; u < (ssize_t)kernel->width; u++, i++)
   1325               kernel->positive_range += kernel->values[i] = (double)
   1326                 (alpha * order_f / ( fact((size_t) u) * fact(kernel->height-u-1) ));
   1327           }
   1328         kernel->minimum = 1.0;
   1329         kernel->maximum = kernel->values[kernel->x+kernel->y*kernel->width];
   1330         kernel->negative_range = 0.0;
   1331         break;
   1332       }
   1333 
   1334     /*
   1335       Convolution Kernels - Well Known Named Constant Kernels
   1336     */
   1337     case LaplacianKernel:
   1338       { switch ( (int) args->rho ) {
   1339           case 0:
   1340           default: /* laplacian square filter -- default */
   1341             kernel=ParseKernelArray("3: -1,-1,-1  -1,8,-1  -1,-1,-1");
   1342             break;
   1343           case 1:  /* laplacian diamond filter */
   1344             kernel=ParseKernelArray("3: 0,-1,0  -1,4,-1  0,-1,0");
   1345             break;
   1346           case 2:
   1347             kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
   1348             break;
   1349           case 3:
   1350             kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  1,-2,1");
   1351             break;
   1352           case 5:   /* a 5x5 laplacian */
   1353             kernel=ParseKernelArray(
   1354               "5: -4,-1,0,-1,-4  -1,2,3,2,-1  0,3,4,3,0  -1,2,3,2,-1  -4,-1,0,-1,-4");
   1355             break;
   1356           case 7:   /* a 7x7 laplacian */
   1357             kernel=ParseKernelArray(
   1358               "7:-10,-5,-2,-1,-2,-5,-10 -5,0,3,4,3,0,-5 -2,3,6,7,6,3,-2 -1,4,7,8,7,4,-1 -2,3,6,7,6,3,-2 -5,0,3,4,3,0,-5 -10,-5,-2,-1,-2,-5,-10" );
   1359             break;
   1360           case 15:  /* a 5x5 LoG (sigma approx 1.4) */
   1361             kernel=ParseKernelArray(
   1362               "5: 0,0,-1,0,0  0,-1,-2,-1,0  -1,-2,16,-2,-1  0,-1,-2,-1,0  0,0,-1,0,0");
   1363             break;
   1364           case 19:  /* a 9x9 LoG (sigma approx 1.4) */
   1365             /* http://www.cscjournals.org/csc/manuscript/Journals/IJIP/volume3/Issue1/IJIP-15.pdf */
   1366             kernel=ParseKernelArray(
   1367               "9: 0,-1,-1,-2,-2,-2,-1,-1,0  -1,-2,-4,-5,-5,-5,-4,-2,-1  -1,-4,-5,-3,-0,-3,-5,-4,-1  -2,-5,-3,12,24,12,-3,-5,-2  -2,-5,-0,24,40,24,-0,-5,-2  -2,-5,-3,12,24,12,-3,-5,-2  -1,-4,-5,-3,-0,-3,-5,-4,-1  -1,-2,-4,-5,-5,-5,-4,-2,-1  0,-1,-1,-2,-2,-2,-1,-1,0");
   1368             break;
   1369         }
   1370         if (kernel == (KernelInfo *) NULL)
   1371           return(kernel);
   1372         kernel->type = type;
   1373         break;
   1374       }
   1375     case SobelKernel:
   1376       { /* Simple Sobel Kernel */
   1377         kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
   1378         if (kernel == (KernelInfo *) NULL)
   1379           return(kernel);
   1380         kernel->type = type;
   1381         RotateKernelInfo(kernel, args->rho);
   1382         break;
   1383       }
   1384     case RobertsKernel:
   1385       {
   1386         kernel=ParseKernelArray("3: 0,0,0  1,-1,0  0,0,0");
   1387         if (kernel == (KernelInfo *) NULL)
   1388           return(kernel);
   1389         kernel->type = type;
   1390         RotateKernelInfo(kernel, args->rho);
   1391         break;
   1392       }
   1393     case PrewittKernel:
   1394       {
   1395         kernel=ParseKernelArray("3: 1,0,-1  1,0,-1  1,0,-1");
   1396         if (kernel == (KernelInfo *) NULL)
   1397           return(kernel);
   1398         kernel->type = type;
   1399         RotateKernelInfo(kernel, args->rho);
   1400         break;
   1401       }
   1402     case CompassKernel:
   1403       {
   1404         kernel=ParseKernelArray("3: 1,1,-1  1,-2,-1  1,1,-1");
   1405         if (kernel == (KernelInfo *) NULL)
   1406           return(kernel);
   1407         kernel->type = type;
   1408         RotateKernelInfo(kernel, args->rho);
   1409         break;
   1410       }
   1411     case KirschKernel:
   1412       {
   1413         kernel=ParseKernelArray("3: 5,-3,-3  5,0,-3  5,-3,-3");
   1414         if (kernel == (KernelInfo *) NULL)
   1415           return(kernel);
   1416         kernel->type = type;
   1417         RotateKernelInfo(kernel, args->rho);
   1418         break;
   1419       }
   1420     case FreiChenKernel:
   1421       /* Direction is set to be left to right positive */
   1422       /* http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf -- RIGHT? */
   1423       /* http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf -- WRONG? */
   1424       { switch ( (int) args->rho ) {
   1425           default:
   1426           case 0:
   1427             kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
   1428             if (kernel == (KernelInfo *) NULL)
   1429               return(kernel);
   1430             kernel->type = type;
   1431             kernel->values[3] = +(MagickRealType) MagickSQ2;
   1432             kernel->values[5] = -(MagickRealType) MagickSQ2;
   1433             CalcKernelMetaData(kernel);     /* recalculate meta-data */
   1434             break;
   1435           case 2:
   1436             kernel=ParseKernelArray("3: 1,2,0  2,0,-2  0,-2,-1");
   1437             if (kernel == (KernelInfo *) NULL)
   1438               return(kernel);
   1439             kernel->type = type;
   1440             kernel->values[1] = kernel->values[3]= +(MagickRealType) MagickSQ2;
   1441             kernel->values[5] = kernel->values[7]= -(MagickRealType) MagickSQ2;
   1442             CalcKernelMetaData(kernel);     /* recalculate meta-data */
   1443             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
   1444             break;
   1445           case 10:
   1446           {
   1447             kernel=AcquireKernelInfo("FreiChen:11;FreiChen:12;FreiChen:13;FreiChen:14;FreiChen:15;FreiChen:16;FreiChen:17;FreiChen:18;FreiChen:19",exception);
   1448             if (kernel == (KernelInfo *) NULL)
   1449               return(kernel);
   1450             break;
   1451           }
   1452           case 1:
   1453           case 11:
   1454             kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
   1455             if (kernel == (KernelInfo *) NULL)
   1456               return(kernel);
   1457             kernel->type = type;
   1458             kernel->values[3] = +(MagickRealType) MagickSQ2;
   1459             kernel->values[5] = -(MagickRealType) MagickSQ2;
   1460             CalcKernelMetaData(kernel);     /* recalculate meta-data */
   1461             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
   1462             break;
   1463           case 12:
   1464             kernel=ParseKernelArray("3: 1,2,1  0,0,0  1,2,1");
   1465             if (kernel == (KernelInfo *) NULL)
   1466               return(kernel);
   1467             kernel->type = type;
   1468             kernel->values[1] = +(MagickRealType) MagickSQ2;
   1469             kernel->values[7] = +(MagickRealType) MagickSQ2;
   1470             CalcKernelMetaData(kernel);
   1471             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
   1472             break;
   1473           case 13:
   1474             kernel=ParseKernelArray("3: 2,-1,0  -1,0,1  0,1,-2");
   1475             if (kernel == (KernelInfo *) NULL)
   1476               return(kernel);
   1477             kernel->type = type;
   1478             kernel->values[0] = +(MagickRealType) MagickSQ2;
   1479             kernel->values[8] = -(MagickRealType) MagickSQ2;
   1480             CalcKernelMetaData(kernel);
   1481             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
   1482             break;
   1483           case 14:
   1484             kernel=ParseKernelArray("3: 0,1,-2  -1,0,1  2,-1,0");
   1485             if (kernel == (KernelInfo *) NULL)
   1486               return(kernel);
   1487             kernel->type = type;
   1488             kernel->values[2] = -(MagickRealType) MagickSQ2;
   1489             kernel->values[6] = +(MagickRealType) MagickSQ2;
   1490             CalcKernelMetaData(kernel);
   1491             ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
   1492             break;
   1493           case 15:
   1494             kernel=ParseKernelArray("3: 0,-1,0  1,0,1  0,-1,0");
   1495             if (kernel == (KernelInfo *) NULL)
   1496               return(kernel);
   1497             kernel->type = type;
   1498             ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
   1499             break;
   1500           case 16:
   1501             kernel=ParseKernelArray("3: 1,0,-1  0,0,0  -1,0,1");
   1502             if (kernel == (KernelInfo *) NULL)
   1503               return(kernel);
   1504             kernel->type = type;
   1505             ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
   1506             break;
   1507           case 17:
   1508             kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  -1,-2,1");
   1509             if (kernel == (KernelInfo *) NULL)
   1510               return(kernel);
   1511             kernel->type = type;
   1512             ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
   1513             break;
   1514           case 18:
   1515             kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
   1516             if (kernel == (KernelInfo *) NULL)
   1517               return(kernel);
   1518             kernel->type = type;
   1519             ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
   1520             break;
   1521           case 19:
   1522             kernel=ParseKernelArray("3: 1,1,1  1,1,1  1,1,1");
   1523             if (kernel == (KernelInfo *) NULL)
   1524               return(kernel);
   1525             kernel->type = type;
   1526             ScaleKernelInfo(kernel, 1.0/3.0, NoValue);
   1527             break;
   1528         }
   1529         if ( fabs(args->sigma) >= MagickEpsilon )
   1530           /* Rotate by correctly supplied 'angle' */
   1531           RotateKernelInfo(kernel, args->sigma);
   1532         else if ( args->rho > 30.0 || args->rho < -30.0 )
   1533           /* Rotate by out of bounds 'type' */
   1534           RotateKernelInfo(kernel, args->rho);
   1535         break;
   1536       }
   1537 
   1538     /*
   1539       Boolean or Shaped Kernels
   1540     */
   1541     case DiamondKernel:
   1542       {
   1543         if (args->rho < 1.0)
   1544           kernel->width = kernel->height = 3;  /* default radius = 1 */
   1545         else
   1546           kernel->width = kernel->height = ((size_t)args->rho)*2+1;
   1547         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
   1548 
   1549         kernel->values=(MagickRealType *) MagickAssumeAligned(
   1550           AcquireAlignedMemory(kernel->width,kernel->height*
   1551           sizeof(*kernel->values)));
   1552         if (kernel->values == (MagickRealType *) NULL)
   1553           return(DestroyKernelInfo(kernel));
   1554 
   1555         /* set all kernel values within diamond area to scale given */
   1556         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
   1557           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
   1558             if ( (labs((long) u)+labs((long) v)) <= (long) kernel->x)
   1559               kernel->positive_range += kernel->values[i] = args->sigma;
   1560             else
   1561               kernel->values[i] = nan;
   1562         kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
   1563         break;
   1564       }
   1565     case SquareKernel:
   1566     case RectangleKernel:
   1567       { double
   1568           scale;
   1569         if ( type == SquareKernel )
   1570           {
   1571             if (args->rho < 1.0)
   1572               kernel->width = kernel->height = 3;  /* default radius = 1 */
   1573             else
   1574               kernel->width = kernel->height = (size_t) (2*args->rho+1);
   1575             kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
   1576             scale = args->sigma;
   1577           }
   1578         else {
   1579             /* NOTE: user defaults set in "AcquireKernelInfo()" */
   1580             if ( args->rho < 1.0 || args->sigma < 1.0 )
   1581               return(DestroyKernelInfo(kernel));    /* invalid args given */
   1582             kernel->width = (size_t)args->rho;
   1583             kernel->height = (size_t)args->sigma;
   1584             if ( args->xi  < 0.0 || args->xi  > (double)kernel->width ||
   1585                  args->psi < 0.0 || args->psi > (double)kernel->height )
   1586               return(DestroyKernelInfo(kernel));    /* invalid args given */
   1587             kernel->x = (ssize_t) args->xi;
   1588             kernel->y = (ssize_t) args->psi;
   1589             scale = 1.0;
   1590           }
   1591         kernel->values=(MagickRealType *) MagickAssumeAligned(
   1592           AcquireAlignedMemory(kernel->width,kernel->height*
   1593           sizeof(*kernel->values)));
   1594         if (kernel->values == (MagickRealType *) NULL)
   1595           return(DestroyKernelInfo(kernel));
   1596 
   1597         /* set all kernel values to scale given */
   1598         u=(ssize_t) (kernel->width*kernel->height);
   1599         for ( i=0; i < u; i++)
   1600             kernel->values[i] = scale;
   1601         kernel->minimum = kernel->maximum = scale;   /* a flat shape */
   1602         kernel->positive_range = scale*u;
   1603         break;
   1604       }
   1605       case OctagonKernel:
   1606         {
   1607           if (args->rho < 1.0)
   1608             kernel->width = kernel->height = 5;  /* default radius = 2 */
   1609           else
   1610             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
   1611           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
   1612 
   1613           kernel->values=(MagickRealType *) MagickAssumeAligned(
   1614             AcquireAlignedMemory(kernel->width,kernel->height*
   1615             sizeof(*kernel->values)));
   1616           if (kernel->values == (MagickRealType *) NULL)
   1617             return(DestroyKernelInfo(kernel));
   1618 
   1619           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
   1620             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
   1621               if ( (labs((long) u)+labs((long) v)) <=
   1622                         ((long)kernel->x + (long)(kernel->x/2)) )
   1623                 kernel->positive_range += kernel->values[i] = args->sigma;
   1624               else
   1625                 kernel->values[i] = nan;
   1626           kernel->minimum = kernel->maximum = args->sigma;  /* a flat shape */
   1627           break;
   1628         }
   1629       case DiskKernel:
   1630         {
   1631           ssize_t
   1632             limit = (ssize_t)(args->rho*args->rho);
   1633 
   1634           if (args->rho < 0.4)           /* default radius approx 4.3 */
   1635             kernel->width = kernel->height = 9L, limit = 18L;
   1636           else
   1637             kernel->width = kernel->height = (size_t)fabs(args->rho)*2+1;
   1638           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
   1639 
   1640           kernel->values=(MagickRealType *) MagickAssumeAligned(
   1641             AcquireAlignedMemory(kernel->width,kernel->height*
   1642             sizeof(*kernel->values)));
   1643           if (kernel->values == (MagickRealType *) NULL)
   1644             return(DestroyKernelInfo(kernel));
   1645 
   1646           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
   1647             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
   1648               if ((u*u+v*v) <= limit)
   1649                 kernel->positive_range += kernel->values[i] = args->sigma;
   1650               else
   1651                 kernel->values[i] = nan;
   1652           kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
   1653           break;
   1654         }
   1655       case PlusKernel:
   1656         {
   1657           if (args->rho < 1.0)
   1658             kernel->width = kernel->height = 5;  /* default radius 2 */
   1659           else
   1660             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
   1661           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
   1662 
   1663           kernel->values=(MagickRealType *) MagickAssumeAligned(
   1664             AcquireAlignedMemory(kernel->width,kernel->height*
   1665             sizeof(*kernel->values)));
   1666           if (kernel->values == (MagickRealType *) NULL)
   1667             return(DestroyKernelInfo(kernel));
   1668 
   1669           /* set all kernel values along axises to given scale */
   1670           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
   1671             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
   1672               kernel->values[i] = (u == 0 || v == 0) ? args->sigma : nan;
   1673           kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
   1674           kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
   1675           break;
   1676         }
   1677       case CrossKernel:
   1678         {
   1679           if (args->rho < 1.0)
   1680             kernel->width = kernel->height = 5;  /* default radius 2 */
   1681           else
   1682             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
   1683           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
   1684 
   1685           kernel->values=(MagickRealType *) MagickAssumeAligned(
   1686             AcquireAlignedMemory(kernel->width,kernel->height*
   1687             sizeof(*kernel->values)));
   1688           if (kernel->values == (MagickRealType *) NULL)
   1689             return(DestroyKernelInfo(kernel));
   1690 
   1691           /* set all kernel values along axises to given scale */
   1692           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
   1693             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
   1694               kernel->values[i] = (u == v || u == -v) ? args->sigma : nan;
   1695           kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
   1696           kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
   1697           break;
   1698         }
   1699       /*
   1700         HitAndMiss Kernels
   1701       */
   1702       case RingKernel:
   1703       case PeaksKernel:
   1704         {
   1705           ssize_t
   1706             limit1,
   1707             limit2,
   1708             scale;
   1709 
   1710           if (args->rho < args->sigma)
   1711             {
   1712               kernel->width = ((size_t)args->sigma)*2+1;
   1713               limit1 = (ssize_t)(args->rho*args->rho);
   1714               limit2 = (ssize_t)(args->sigma*args->sigma);
   1715             }
   1716           else
   1717             {
   1718               kernel->width = ((size_t)args->rho)*2+1;
   1719               limit1 = (ssize_t)(args->sigma*args->sigma);
   1720               limit2 = (ssize_t)(args->rho*args->rho);
   1721             }
   1722           if ( limit2 <= 0 )
   1723             kernel->width = 7L, limit1 = 7L, limit2 = 11L;
   1724 
   1725           kernel->height = kernel->width;
   1726           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
   1727           kernel->values=(MagickRealType *) MagickAssumeAligned(
   1728             AcquireAlignedMemory(kernel->width,kernel->height*
   1729             sizeof(*kernel->values)));
   1730           if (kernel->values == (MagickRealType *) NULL)
   1731             return(DestroyKernelInfo(kernel));
   1732 
   1733           /* set a ring of points of 'scale' ( 0.0 for PeaksKernel ) */
   1734           scale = (ssize_t) (( type == PeaksKernel) ? 0.0 : args->xi);
   1735           for ( i=0, v= -kernel->y; v <= (ssize_t)kernel->y; v++)
   1736             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
   1737               { ssize_t radius=u*u+v*v;
   1738                 if (limit1 < radius && radius <= limit2)
   1739                   kernel->positive_range += kernel->values[i] = (double) scale;
   1740                 else
   1741                   kernel->values[i] = nan;
   1742               }
   1743           kernel->minimum = kernel->maximum = (double) scale;
   1744           if ( type == PeaksKernel ) {
   1745             /* set the central point in the middle */
   1746             kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
   1747             kernel->positive_range = 1.0;
   1748             kernel->maximum = 1.0;
   1749           }
   1750           break;
   1751         }
   1752       case EdgesKernel:
   1753         {
   1754           kernel=AcquireKernelInfo("ThinSE:482",exception);
   1755           if (kernel == (KernelInfo *) NULL)
   1756             return(kernel);
   1757           kernel->type = type;
   1758           ExpandMirrorKernelInfo(kernel); /* mirror expansion of kernels */
   1759           break;
   1760         }
   1761       case CornersKernel:
   1762         {
   1763           kernel=AcquireKernelInfo("ThinSE:87",exception);
   1764           if (kernel == (KernelInfo *) NULL)
   1765             return(kernel);
   1766           kernel->type = type;
   1767           ExpandRotateKernelInfo(kernel, 90.0); /* Expand 90 degree rotations */
   1768           break;
   1769         }
   1770       case DiagonalsKernel:
   1771         {
   1772           switch ( (int) args->rho ) {
   1773             case 0:
   1774             default:
   1775               { KernelInfo
   1776                   *new_kernel;
   1777                 kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
   1778                 if (kernel == (KernelInfo *) NULL)
   1779                   return(kernel);
   1780                 kernel->type = type;
   1781                 new_kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
   1782                 if (new_kernel == (KernelInfo *) NULL)
   1783                   return(DestroyKernelInfo(kernel));
   1784                 new_kernel->type = type;
   1785                 LastKernelInfo(kernel)->next = new_kernel;
   1786                 ExpandMirrorKernelInfo(kernel);
   1787                 return(kernel);
   1788               }
   1789             case 1:
   1790               kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
   1791               break;
   1792             case 2:
   1793               kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
   1794               break;
   1795           }
   1796           if (kernel == (KernelInfo *) NULL)
   1797             return(kernel);
   1798           kernel->type = type;
   1799           RotateKernelInfo(kernel, args->sigma);
   1800           break;
   1801         }
   1802       case LineEndsKernel:
   1803         { /* Kernels for finding the end of thin lines */
   1804           switch ( (int) args->rho ) {
   1805             case 0:
   1806             default:
   1807               /* set of kernels to find all end of lines */
   1808               return(AcquireKernelInfo("LineEnds:1>;LineEnds:2>",exception));
   1809             case 1:
   1810               /* kernel for 4-connected line ends - no rotation */
   1811               kernel=ParseKernelArray("3: 0,0,-  0,1,1  0,0,-");
   1812               break;
   1813           case 2:
   1814               /* kernel to add for 8-connected lines - no rotation */
   1815               kernel=ParseKernelArray("3: 0,0,0  0,1,0  0,0,1");
   1816               break;
   1817           case 3:
   1818               /* kernel to add for orthogonal line ends - does not find corners */
   1819               kernel=ParseKernelArray("3: 0,0,0  0,1,1  0,0,0");
   1820               break;
   1821           case 4:
   1822               /* traditional line end - fails on last T end */
   1823               kernel=ParseKernelArray("3: 0,0,0  0,1,-  0,0,-");
   1824               break;
   1825           }
   1826           if (kernel == (KernelInfo *) NULL)
   1827             return(kernel);
   1828           kernel->type = type;
   1829           RotateKernelInfo(kernel, args->sigma);
   1830           break;
   1831         }
   1832       case LineJunctionsKernel:
   1833         { /* kernels for finding the junctions of multiple lines */
   1834           switch ( (int) args->rho ) {
   1835             case 0:
   1836             default:
   1837               /* set of kernels to find all line junctions */
   1838               return(AcquireKernelInfo("LineJunctions:1@;LineJunctions:2>",exception));
   1839             case 1:
   1840               /* Y Junction */
   1841               kernel=ParseKernelArray("3: 1,-,1  -,1,-  -,1,-");
   1842               break;
   1843             case 2:
   1844               /* Diagonal T Junctions */
   1845               kernel=ParseKernelArray("3: 1,-,-  -,1,-  1,-,1");
   1846               break;
   1847             case 3:
   1848               /* Orthogonal T Junctions */
   1849               kernel=ParseKernelArray("3: -,-,-  1,1,1  -,1,-");
   1850               break;
   1851             case 4:
   1852               /* Diagonal X Junctions */
   1853               kernel=ParseKernelArray("3: 1,-,1  -,1,-  1,-,1");
   1854               break;
   1855             case 5:
   1856               /* Orthogonal X Junctions - minimal diamond kernel */
   1857               kernel=ParseKernelArray("3: -,1,-  1,1,1  -,1,-");
   1858               break;
   1859           }
   1860           if (kernel == (KernelInfo *) NULL)
   1861             return(kernel);
   1862           kernel->type = type;
   1863           RotateKernelInfo(kernel, args->sigma);
   1864           break;
   1865         }
   1866       case RidgesKernel:
   1867         { /* Ridges - Ridge finding kernels */
   1868           KernelInfo
   1869             *new_kernel;
   1870           switch ( (int) args->rho ) {
   1871             case 1:
   1872             default:
   1873               kernel=ParseKernelArray("3x1:0,1,0");
   1874               if (kernel == (KernelInfo *) NULL)
   1875                 return(kernel);
   1876               kernel->type = type;
   1877               ExpandRotateKernelInfo(kernel, 90.0); /* 2 rotated kernels (symmetrical) */
   1878               break;
   1879             case 2:
   1880               kernel=ParseKernelArray("4x1:0,1,1,0");
   1881               if (kernel == (KernelInfo *) NULL)
   1882                 return(kernel);
   1883               kernel->type = type;
   1884               ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotated kernels */
   1885 
   1886               /* Kernels to find a stepped 'thick' line, 4 rotates + mirrors */
   1887               /* Unfortunatally we can not yet rotate a non-square kernel */
   1888               /* But then we can't flip a non-symetrical kernel either */
   1889               new_kernel=ParseKernelArray("4x3+1+1:0,1,1,- -,1,1,- -,1,1,0");
   1890               if (new_kernel == (KernelInfo *) NULL)
   1891                 return(DestroyKernelInfo(kernel));
   1892               new_kernel->type = type;
   1893               LastKernelInfo(kernel)->next = new_kernel;
   1894               new_kernel=ParseKernelArray("4x3+2+1:0,1,1,- -,1,1,- -,1,1,0");
   1895               if (new_kernel == (KernelInfo *) NULL)
   1896                 return(DestroyKernelInfo(kernel));
   1897               new_kernel->type = type;
   1898               LastKernelInfo(kernel)->next = new_kernel;
   1899               new_kernel=ParseKernelArray("4x3+1+1:-,1,1,0 -,1,1,- 0,1,1,-");
   1900               if (new_kernel == (KernelInfo *) NULL)
   1901                 return(DestroyKernelInfo(kernel));
   1902               new_kernel->type = type;
   1903               LastKernelInfo(kernel)->next = new_kernel;
   1904               new_kernel=ParseKernelArray("4x3+2+1:-,1,1,0 -,1,1,- 0,1,1,-");
   1905               if (new_kernel == (KernelInfo *) NULL)
   1906                 return(DestroyKernelInfo(kernel));
   1907               new_kernel->type = type;
   1908               LastKernelInfo(kernel)->next = new_kernel;
   1909               new_kernel=ParseKernelArray("3x4+1+1:0,-,- 1,1,1 1,1,1 -,-,0");
   1910               if (new_kernel == (KernelInfo *) NULL)
   1911                 return(DestroyKernelInfo(kernel));
   1912               new_kernel->type = type;
   1913               LastKernelInfo(kernel)->next = new_kernel;
   1914               new_kernel=ParseKernelArray("3x4+1+2:0,-,- 1,1,1 1,1,1 -,-,0");
   1915               if (new_kernel == (KernelInfo *) NULL)
   1916                 return(DestroyKernelInfo(kernel));
   1917               new_kernel->type = type;
   1918               LastKernelInfo(kernel)->next = new_kernel;
   1919               new_kernel=ParseKernelArray("3x4+1+1:-,-,0 1,1,1 1,1,1 0,-,-");
   1920               if (new_kernel == (KernelInfo *) NULL)
   1921                 return(DestroyKernelInfo(kernel));
   1922               new_kernel->type = type;
   1923               LastKernelInfo(kernel)->next = new_kernel;
   1924               new_kernel=ParseKernelArray("3x4+1+2:-,-,0 1,1,1 1,1,1 0,-,-");
   1925               if (new_kernel == (KernelInfo *) NULL)
   1926                 return(DestroyKernelInfo(kernel));
   1927               new_kernel->type = type;
   1928               LastKernelInfo(kernel)->next = new_kernel;
   1929               break;
   1930           }
   1931           break;
   1932         }
   1933       case ConvexHullKernel:
   1934         {
   1935           KernelInfo
   1936             *new_kernel;
   1937           /* first set of 8 kernels */
   1938           kernel=ParseKernelArray("3: 1,1,-  1,0,-  1,-,0");
   1939           if (kernel == (KernelInfo *) NULL)
   1940             return(kernel);
   1941           kernel->type = type;
   1942           ExpandRotateKernelInfo(kernel, 90.0);
   1943           /* append the mirror versions too - no flip function yet */
   1944           new_kernel=ParseKernelArray("3: 1,1,1  1,0,-  -,-,0");
   1945           if (new_kernel == (KernelInfo *) NULL)
   1946             return(DestroyKernelInfo(kernel));
   1947           new_kernel->type = type;
   1948           ExpandRotateKernelInfo(new_kernel, 90.0);
   1949           LastKernelInfo(kernel)->next = new_kernel;
   1950           break;
   1951         }
   1952       case SkeletonKernel:
   1953         {
   1954           switch ( (int) args->rho ) {
   1955             case 1:
   1956             default:
   1957               /* Traditional Skeleton...
   1958               ** A cyclically rotated single kernel
   1959               */
   1960               kernel=AcquireKernelInfo("ThinSE:482",exception);
   1961               if (kernel == (KernelInfo *) NULL)
   1962                 return(kernel);
   1963               kernel->type = type;
   1964               ExpandRotateKernelInfo(kernel, 45.0); /* 8 rotations */
   1965               break;
   1966             case 2:
   1967               /* HIPR Variation of the cyclic skeleton
   1968               ** Corners of the traditional method made more forgiving,
   1969               ** but the retain the same cyclic order.
   1970               */
   1971               kernel=AcquireKernelInfo("ThinSE:482; ThinSE:87x90;",exception);
   1972               if (kernel == (KernelInfo *) NULL)
   1973                 return(kernel);
   1974               if (kernel->next == (KernelInfo *) NULL)
   1975                 return(DestroyKernelInfo(kernel));
   1976               kernel->type = type;
   1977               kernel->next->type = type;
   1978               ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotations of the 2 kernels */
   1979               break;
   1980             case 3:
   1981               /* Dan Bloomberg Skeleton, from his paper on 3x3 thinning SE's
   1982               ** "Connectivity-Preserving Morphological Image Thransformations"
   1983               ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
   1984               **   http://www.leptonica.com/papers/conn.pdf
   1985               */
   1986               kernel=AcquireKernelInfo("ThinSE:41; ThinSE:42; ThinSE:43",
   1987                 exception);
   1988               if (kernel == (KernelInfo *) NULL)
   1989                 return(kernel);
   1990               kernel->type = type;
   1991               kernel->next->type = type;
   1992               kernel->next->next->type = type;
   1993               ExpandMirrorKernelInfo(kernel); /* 12 kernels total */
   1994               break;
   1995            }
   1996           break;
   1997         }
   1998       case ThinSEKernel:
   1999         { /* Special kernels for general thinning, while preserving connections
   2000           ** "Connectivity-Preserving Morphological Image Thransformations"
   2001           ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
   2002           **   http://www.leptonica.com/papers/conn.pdf
   2003           ** And
   2004           **   http://tpgit.github.com/Leptonica/ccthin_8c_source.html
   2005           **
   2006           ** Note kernels do not specify the origin pixel, allowing them
   2007           ** to be used for both thickening and thinning operations.
   2008           */
   2009           switch ( (int) args->rho ) {
   2010             /* SE for 4-connected thinning */
   2011             case 41: /* SE_4_1 */
   2012               kernel=ParseKernelArray("3: -,-,1  0,-,1  -,-,1");
   2013               break;
   2014             case 42: /* SE_4_2 */
   2015               kernel=ParseKernelArray("3: -,-,1  0,-,1  -,0,-");
   2016               break;
   2017             case 43: /* SE_4_3 */
   2018               kernel=ParseKernelArray("3: -,0,-  0,-,1  -,-,1");
   2019               break;
   2020             case 44: /* SE_4_4 */
   2021               kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,-");
   2022               break;
   2023             case 45: /* SE_4_5 */
   2024               kernel=ParseKernelArray("3: -,0,1  0,-,1  -,0,-");
   2025               break;
   2026             case 46: /* SE_4_6 */
   2027               kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,1");
   2028               break;
   2029             case 47: /* SE_4_7 */
   2030               kernel=ParseKernelArray("3: -,1,1  0,-,1  -,0,-");
   2031               break;
   2032             case 48: /* SE_4_8 */
   2033               kernel=ParseKernelArray("3: -,-,1  0,-,1  0,-,1");
   2034               break;
   2035             case 49: /* SE_4_9 */
   2036               kernel=ParseKernelArray("3: 0,-,1  0,-,1  -,-,1");
   2037               break;
   2038             /* SE for 8-connected thinning - negatives of the above */
   2039             case 81: /* SE_8_0 */
   2040               kernel=ParseKernelArray("3: -,1,-  0,-,1  -,1,-");
   2041               break;
   2042             case 82: /* SE_8_2 */
   2043               kernel=ParseKernelArray("3: -,1,-  0,-,1  0,-,-");
   2044               break;
   2045             case 83: /* SE_8_3 */
   2046               kernel=ParseKernelArray("3: 0,-,-  0,-,1  -,1,-");
   2047               break;
   2048             case 84: /* SE_8_4 */
   2049               kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,-");
   2050               break;
   2051             case 85: /* SE_8_5 */
   2052               kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,-");
   2053               break;
   2054             case 86: /* SE_8_6 */
   2055               kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,1");
   2056               break;
   2057             case 87: /* SE_8_7 */
   2058               kernel=ParseKernelArray("3: -,1,-  0,-,1  0,0,-");
   2059               break;
   2060             case 88: /* SE_8_8 */
   2061               kernel=ParseKernelArray("3: -,1,-  0,-,1  0,1,-");
   2062               break;
   2063             case 89: /* SE_8_9 */
   2064               kernel=ParseKernelArray("3: 0,1,-  0,-,1  -,1,-");
   2065               break;
   2066             /* Special combined SE kernels */
   2067             case 423: /* SE_4_2 , SE_4_3 Combined Kernel */
   2068               kernel=ParseKernelArray("3: -,-,1  0,-,-  -,0,-");
   2069               break;
   2070             case 823: /* SE_8_2 , SE_8_3 Combined Kernel */
   2071               kernel=ParseKernelArray("3: -,1,-  -,-,1  0,-,-");
   2072               break;
   2073             case 481: /* SE_48_1 - General Connected Corner Kernel */
   2074               kernel=ParseKernelArray("3: -,1,1  0,-,1  0,0,-");
   2075               break;
   2076             default:
   2077             case 482: /* SE_48_2 - General Edge Kernel */
   2078               kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,1");
   2079               break;
   2080           }
   2081           if (kernel == (KernelInfo *) NULL)
   2082             return(kernel);
   2083           kernel->type = type;
   2084           RotateKernelInfo(kernel, args->sigma);
   2085           break;
   2086         }
   2087       /*
   2088         Distance Measuring Kernels
   2089       */
   2090       case ChebyshevKernel:
   2091         {
   2092           if (args->rho < 1.0)
   2093             kernel->width = kernel->height = 3;  /* default radius = 1 */
   2094           else
   2095             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
   2096           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
   2097 
   2098           kernel->values=(MagickRealType *) MagickAssumeAligned(
   2099             AcquireAlignedMemory(kernel->width,kernel->height*
   2100             sizeof(*kernel->values)));
   2101           if (kernel->values == (MagickRealType *) NULL)
   2102             return(DestroyKernelInfo(kernel));
   2103 
   2104           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
   2105             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
   2106               kernel->positive_range += ( kernel->values[i] =
   2107                   args->sigma*MagickMax(fabs((double)u),fabs((double)v)) );
   2108           kernel->maximum = kernel->values[0];
   2109           break;
   2110         }
   2111       case ManhattanKernel:
   2112         {
   2113           if (args->rho < 1.0)
   2114             kernel->width = kernel->height = 3;  /* default radius = 1 */
   2115           else
   2116             kernel->width = kernel->height = ((size_t)args->rho)*2+1;
   2117           kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
   2118 
   2119           kernel->values=(MagickRealType *) MagickAssumeAligned(
   2120             AcquireAlignedMemory(kernel->width,kernel->height*
   2121             sizeof(*kernel->values)));
   2122           if (kernel->values == (MagickRealType *) NULL)
   2123             return(DestroyKernelInfo(kernel));
   2124 
   2125           for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
   2126             for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
   2127               kernel->positive_range += ( kernel->values[i] =
   2128                   args->sigma*(labs((long) u)+labs((long) v)) );
   2129           kernel->maximum = kernel->values[0];
   2130           break;
   2131         }
   2132       case OctagonalKernel:
   2133       {
   2134         if (args->rho < 2.0)
   2135           kernel->width = kernel->height = 5;  /* default/minimum radius = 2 */
   2136         else
   2137           kernel->width = kernel->height = ((size_t)args->rho)*2+1;
   2138         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
   2139 
   2140         kernel->values=(MagickRealType *) MagickAssumeAligned(
   2141           AcquireAlignedMemory(kernel->width,kernel->height*
   2142           sizeof(*kernel->values)));
   2143         if (kernel->values == (MagickRealType *) NULL)
   2144           return(DestroyKernelInfo(kernel));
   2145 
   2146         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
   2147           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
   2148             {
   2149               double
   2150                 r1 = MagickMax(fabs((double)u),fabs((double)v)),
   2151                 r2 = floor((double)(labs((long)u)+labs((long)v)+1)/1.5);
   2152               kernel->positive_range += kernel->values[i] =
   2153                         args->sigma*MagickMax(r1,r2);
   2154             }
   2155         kernel->maximum = kernel->values[0];
   2156         break;
   2157       }
   2158     case EuclideanKernel:
   2159       {
   2160         if (args->rho < 1.0)
   2161           kernel->width = kernel->height = 3;  /* default radius = 1 */
   2162         else
   2163           kernel->width = kernel->height = ((size_t)args->rho)*2+1;
   2164         kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
   2165 
   2166         kernel->values=(MagickRealType *) MagickAssumeAligned(
   2167           AcquireAlignedMemory(kernel->width,kernel->height*
   2168           sizeof(*kernel->values)));
   2169         if (kernel->values == (MagickRealType *) NULL)
   2170           return(DestroyKernelInfo(kernel));
   2171 
   2172         for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
   2173           for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
   2174             kernel->positive_range += ( kernel->values[i] =
   2175               args->sigma*sqrt((double)(u*u+v*v)) );
   2176         kernel->maximum = kernel->values[0];
   2177         break;
   2178       }
   2179     default:
   2180       {
   2181         /* No-Op Kernel - Basically just a single pixel on its own */
   2182         kernel=ParseKernelArray("1:1");
   2183         if (kernel == (KernelInfo *) NULL)
   2184           return(kernel);
   2185         kernel->type = UndefinedKernel;
   2186         break;
   2187       }
   2188       break;
   2189   }
   2190   return(kernel);
   2191 }
   2192 
   2193 /*
   2195 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2196 %                                                                             %
   2197 %                                                                             %
   2198 %                                                                             %
   2199 %     C l o n e K e r n e l I n f o                                           %
   2200 %                                                                             %
   2201 %                                                                             %
   2202 %                                                                             %
   2203 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2204 %
   2205 %  CloneKernelInfo() creates a new clone of the given Kernel List so that its
   2206 %  can be modified without effecting the original.  The cloned kernel should
   2207 %  be destroyed using DestoryKernelInfo() when no longer needed.
   2208 %
   2209 %  The format of the CloneKernelInfo method is:
   2210 %
   2211 %      KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
   2212 %
   2213 %  A description of each parameter follows:
   2214 %
   2215 %    o kernel: the Morphology/Convolution kernel to be cloned
   2216 %
   2217 */
   2218 MagickExport KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
   2219 {
   2220   register ssize_t
   2221     i;
   2222 
   2223   KernelInfo
   2224     *new_kernel;
   2225 
   2226   assert(kernel != (KernelInfo *) NULL);
   2227   new_kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
   2228   if (new_kernel == (KernelInfo *) NULL)
   2229     return(new_kernel);
   2230   *new_kernel=(*kernel); /* copy values in structure */
   2231 
   2232   /* replace the values with a copy of the values */
   2233   new_kernel->values=(MagickRealType *) MagickAssumeAligned(
   2234     AcquireAlignedMemory(kernel->width,kernel->height*sizeof(*kernel->values)));
   2235   if (new_kernel->values == (MagickRealType *) NULL)
   2236     return(DestroyKernelInfo(new_kernel));
   2237   for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
   2238     new_kernel->values[i]=kernel->values[i];
   2239 
   2240   /* Also clone the next kernel in the kernel list */
   2241   if ( kernel->next != (KernelInfo *) NULL ) {
   2242     new_kernel->next = CloneKernelInfo(kernel->next);
   2243     if ( new_kernel->next == (KernelInfo *) NULL )
   2244       return(DestroyKernelInfo(new_kernel));
   2245   }
   2246 
   2247   return(new_kernel);
   2248 }
   2249 
   2250 /*
   2252 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2253 %                                                                             %
   2254 %                                                                             %
   2255 %                                                                             %
   2256 %     D e s t r o y K e r n e l I n f o                                       %
   2257 %                                                                             %
   2258 %                                                                             %
   2259 %                                                                             %
   2260 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2261 %
   2262 %  DestroyKernelInfo() frees the memory used by a Convolution/Morphology
   2263 %  kernel.
   2264 %
   2265 %  The format of the DestroyKernelInfo method is:
   2266 %
   2267 %      KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
   2268 %
   2269 %  A description of each parameter follows:
   2270 %
   2271 %    o kernel: the Morphology/Convolution kernel to be destroyed
   2272 %
   2273 */
   2274 MagickExport KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
   2275 {
   2276   assert(kernel != (KernelInfo *) NULL);
   2277   if (kernel->next != (KernelInfo *) NULL)
   2278     kernel->next=DestroyKernelInfo(kernel->next);
   2279   kernel->values=(MagickRealType *) RelinquishAlignedMemory(kernel->values);
   2280   kernel=(KernelInfo *) RelinquishMagickMemory(kernel);
   2281   return(kernel);
   2282 }
   2283 
   2284 /*
   2286 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2287 %                                                                             %
   2288 %                                                                             %
   2289 %                                                                             %
   2290 +     E x p a n d M i r r o r K e r n e l I n f o                             %
   2291 %                                                                             %
   2292 %                                                                             %
   2293 %                                                                             %
   2294 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2295 %
   2296 %  ExpandMirrorKernelInfo() takes a single kernel, and expands it into a
   2297 %  sequence of 90-degree rotated kernels but providing a reflected 180
   2298 %  rotatation, before the -/+ 90-degree rotations.
   2299 %
   2300 %  This special rotation order produces a better, more symetrical thinning of
   2301 %  objects.
   2302 %
   2303 %  The format of the ExpandMirrorKernelInfo method is:
   2304 %
   2305 %      void ExpandMirrorKernelInfo(KernelInfo *kernel)
   2306 %
   2307 %  A description of each parameter follows:
   2308 %
   2309 %    o kernel: the Morphology/Convolution kernel
   2310 %
   2311 % This function is only internel to this module, as it is not finalized,
   2312 % especially with regard to non-orthogonal angles, and rotation of larger
   2313 % 2D kernels.
   2314 */
   2315 
   2316 #if 0
   2317 static void FlopKernelInfo(KernelInfo *kernel)
   2318     { /* Do a Flop by reversing each row. */
   2319       size_t
   2320         y;
   2321       register ssize_t
   2322         x,r;
   2323       register double
   2324         *k,t;
   2325 
   2326       for ( y=0, k=kernel->values; y < kernel->height; y++, k+=kernel->width)
   2327         for ( x=0, r=kernel->width-1; x<kernel->width/2; x++, r--)
   2328           t=k[x],  k[x]=k[r],  k[r]=t;
   2329 
   2330       kernel->x = kernel->width - kernel->x - 1;
   2331       angle = fmod(angle+180.0, 360.0);
   2332     }
   2333 #endif
   2334 
   2335 static void ExpandMirrorKernelInfo(KernelInfo *kernel)
   2336 {
   2337   KernelInfo
   2338     *clone,
   2339     *last;
   2340 
   2341   last = kernel;
   2342 
   2343   clone = CloneKernelInfo(last);
   2344   RotateKernelInfo(clone, 180);   /* flip */
   2345   LastKernelInfo(last)->next = clone;
   2346   last = clone;
   2347 
   2348   clone = CloneKernelInfo(last);
   2349   RotateKernelInfo(clone, 90);   /* transpose */
   2350   LastKernelInfo(last)->next = clone;
   2351   last = clone;
   2352 
   2353   clone = CloneKernelInfo(last);
   2354   RotateKernelInfo(clone, 180);  /* flop */
   2355   LastKernelInfo(last)->next = clone;
   2356 
   2357   return;
   2358 }
   2359 
   2360 /*
   2362 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2363 %                                                                             %
   2364 %                                                                             %
   2365 %                                                                             %
   2366 +     E x p a n d R o t a t e K e r n e l I n f o                             %
   2367 %                                                                             %
   2368 %                                                                             %
   2369 %                                                                             %
   2370 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2371 %
   2372 %  ExpandRotateKernelInfo() takes a kernel list, and expands it by rotating
   2373 %  incrementally by the angle given, until the kernel repeats.
   2374 %
   2375 %  WARNING: 45 degree rotations only works for 3x3 kernels.
   2376 %  While 90 degree roatations only works for linear and square kernels
   2377 %
   2378 %  The format of the ExpandRotateKernelInfo method is:
   2379 %
   2380 %      void ExpandRotateKernelInfo(KernelInfo *kernel, double angle)
   2381 %
   2382 %  A description of each parameter follows:
   2383 %
   2384 %    o kernel: the Morphology/Convolution kernel
   2385 %
   2386 %    o angle: angle to rotate in degrees
   2387 %
   2388 % This function is only internel to this module, as it is not finalized,
   2389 % especially with regard to non-orthogonal angles, and rotation of larger
   2390 % 2D kernels.
   2391 */
   2392 
   2393 /* Internal Routine - Return true if two kernels are the same */
   2394 static MagickBooleanType SameKernelInfo(const KernelInfo *kernel1,
   2395      const KernelInfo *kernel2)
   2396 {
   2397   register size_t
   2398     i;
   2399 
   2400   /* check size and origin location */
   2401   if (    kernel1->width != kernel2->width
   2402        || kernel1->height != kernel2->height
   2403        || kernel1->x != kernel2->x
   2404        || kernel1->y != kernel2->y )
   2405     return MagickFalse;
   2406 
   2407   /* check actual kernel values */
   2408   for (i=0; i < (kernel1->width*kernel1->height); i++) {
   2409     /* Test for Nan equivalence */
   2410     if ( IsNaN(kernel1->values[i]) && !IsNaN(kernel2->values[i]) )
   2411       return MagickFalse;
   2412     if ( IsNaN(kernel2->values[i]) && !IsNaN(kernel1->values[i]) )
   2413       return MagickFalse;
   2414     /* Test actual values are equivalent */
   2415     if ( fabs(kernel1->values[i] - kernel2->values[i]) >= MagickEpsilon )
   2416       return MagickFalse;
   2417   }
   2418 
   2419   return MagickTrue;
   2420 }
   2421 
   2422 static void ExpandRotateKernelInfo(KernelInfo *kernel, const double angle)
   2423 {
   2424   KernelInfo
   2425     *clone,
   2426     *last;
   2427 
   2428   last = kernel;
   2429 DisableMSCWarning(4127)
   2430   while(1) {
   2431 RestoreMSCWarning
   2432     clone = CloneKernelInfo(last);
   2433     RotateKernelInfo(clone, angle);
   2434     if ( SameKernelInfo(kernel, clone) != MagickFalse )
   2435       break;
   2436     LastKernelInfo(last)->next = clone;
   2437     last = clone;
   2438   }
   2439   clone = DestroyKernelInfo(clone); /* kernel has repeated - junk the clone */
   2440   return;
   2441 }
   2442 
   2443 /*
   2445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2446 %                                                                             %
   2447 %                                                                             %
   2448 %                                                                             %
   2449 +     C a l c M e t a K e r n a l I n f o                                     %
   2450 %                                                                             %
   2451 %                                                                             %
   2452 %                                                                             %
   2453 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2454 %
   2455 %  CalcKernelMetaData() recalculate the KernelInfo meta-data of this kernel only,
   2456 %  using the kernel values.  This should only ne used if it is not possible to
   2457 %  calculate that meta-data in some easier way.
   2458 %
   2459 %  It is important that the meta-data is correct before ScaleKernelInfo() is
   2460 %  used to perform kernel normalization.
   2461 %
   2462 %  The format of the CalcKernelMetaData method is:
   2463 %
   2464 %      void CalcKernelMetaData(KernelInfo *kernel, const double scale )
   2465 %
   2466 %  A description of each parameter follows:
   2467 %
   2468 %    o kernel: the Morphology/Convolution kernel to modify
   2469 %
   2470 %  WARNING: Minimum and Maximum values are assumed to include zero, even if
   2471 %  zero is not part of the kernel (as in Gaussian Derived kernels). This
   2472 %  however is not true for flat-shaped morphological kernels.
   2473 %
   2474 %  WARNING: Only the specific kernel pointed to is modified, not a list of
   2475 %  multiple kernels.
   2476 %
   2477 % This is an internal function and not expected to be useful outside this
   2478 % module.  This could change however.
   2479 */
   2480 static void CalcKernelMetaData(KernelInfo *kernel)
   2481 {
   2482   register size_t
   2483     i;
   2484 
   2485   kernel->minimum = kernel->maximum = 0.0;
   2486   kernel->negative_range = kernel->positive_range = 0.0;
   2487   for (i=0; i < (kernel->width*kernel->height); i++)
   2488     {
   2489       if ( fabs(kernel->values[i]) < MagickEpsilon )
   2490         kernel->values[i] = 0.0;
   2491       ( kernel->values[i] < 0)
   2492           ?  ( kernel->negative_range += kernel->values[i] )
   2493           :  ( kernel->positive_range += kernel->values[i] );
   2494       Minimize(kernel->minimum, kernel->values[i]);
   2495       Maximize(kernel->maximum, kernel->values[i]);
   2496     }
   2497 
   2498   return;
   2499 }
   2500 
   2501 /*
   2503 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2504 %                                                                             %
   2505 %                                                                             %
   2506 %                                                                             %
   2507 %     M o r p h o l o g y A p p l y                                           %
   2508 %                                                                             %
   2509 %                                                                             %
   2510 %                                                                             %
   2511 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   2512 %
   2513 %  MorphologyApply() applies a morphological method, multiple times using
   2514 %  a list of multiple kernels.  This is the method that should be called by
   2515 %  other 'operators' that internally use morphology operations as part of
   2516 %  their processing.
   2517 %
   2518 %  It is basically equivalent to as MorphologyImage() (see below) but without
   2519 %  any user controls.  This allows internel programs to use this method to
   2520 %  perform a specific task without possible interference by any API user
   2521 %  supplied settings.
   2522 %
   2523 %  It is MorphologyImage() task to extract any such user controls, and
   2524 %  pass them to this function for processing.
   2525 %
   2526 %  More specifically all given kernels should already be scaled, normalised,
   2527 %  and blended appropriatally before being parred to this routine. The
   2528 %  appropriate bias, and compose (typically 'UndefinedComposeOp') given.
   2529 %
   2530 %  The format of the MorphologyApply method is:
   2531 %
   2532 %      Image *MorphologyApply(const Image *image,MorphologyMethod method,
   2533 %        const ssize_t iterations,const KernelInfo *kernel,
   2534 %        const CompositeMethod compose,const double bias,
   2535 %        ExceptionInfo *exception)
   2536 %
   2537 %  A description of each parameter follows:
   2538 %
   2539 %    o image: the source image
   2540 %
   2541 %    o method: the morphology method to be applied.
   2542 %
   2543 %    o iterations: apply the operation this many times (or no change).
   2544 %                  A value of -1 means loop until no change found.
   2545 %                  How this is applied may depend on the morphology method.
   2546 %                  Typically this is a value of 1.
   2547 %
   2548 %    o channel: the channel type.
   2549 %
   2550 %    o kernel: An array of double representing the morphology kernel.
   2551 %
   2552 %    o compose: How to handle or merge multi-kernel results.
   2553 %          If 'UndefinedCompositeOp' use default for the Morphology method.
   2554 %          If 'NoCompositeOp' force image to be re-iterated by each kernel.
   2555 %          Otherwise merge the results using the compose method given.
   2556 %
   2557 %    o bias: Convolution Output Bias.
   2558 %
   2559 %    o exception: return any errors or warnings in this structure.
   2560 %
   2561 */
   2562 static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
   2563   const MorphologyMethod method,const KernelInfo *kernel,const double bias,
   2564   ExceptionInfo *exception)
   2565 {
   2566 #define MorphologyTag  "Morphology/Image"
   2567 
   2568   CacheView
   2569     *image_view,
   2570     *morphology_view;
   2571 
   2572   OffsetInfo
   2573     offset;
   2574 
   2575   register ssize_t
   2576     j,
   2577     y;
   2578 
   2579   size_t
   2580     *changes,
   2581     changed,
   2582     width;
   2583 
   2584   MagickBooleanType
   2585     status;
   2586 
   2587   MagickOffsetType
   2588     progress;
   2589 
   2590   assert(image != (Image *) NULL);
   2591   assert(image->signature == MagickCoreSignature);
   2592   assert(morphology_image != (Image *) NULL);
   2593   assert(morphology_image->signature == MagickCoreSignature);
   2594   assert(kernel != (KernelInfo *) NULL);
   2595   assert(kernel->signature == MagickCoreSignature);
   2596   assert(exception != (ExceptionInfo *) NULL);
   2597   assert(exception->signature == MagickCoreSignature);
   2598   status=MagickTrue;
   2599   progress=0;
   2600   image_view=AcquireVirtualCacheView(image,exception);
   2601   morphology_view=AcquireAuthenticCacheView(morphology_image,exception);
   2602   width=image->columns+kernel->width-1;
   2603   offset.x=0;
   2604   offset.y=0;
   2605   switch (method)
   2606   {
   2607     case ConvolveMorphology:
   2608     case DilateMorphology:
   2609     case DilateIntensityMorphology:
   2610     case IterativeDistanceMorphology:
   2611     {
   2612       /*
   2613         Kernel needs to used with reflection about origin.
   2614       */
   2615       offset.x=(ssize_t) kernel->width-kernel->x-1;
   2616       offset.y=(ssize_t) kernel->height-kernel->y-1;
   2617       break;
   2618     }
   2619     case ErodeMorphology:
   2620     case ErodeIntensityMorphology:
   2621     case HitAndMissMorphology:
   2622     case ThinningMorphology:
   2623     case ThickenMorphology:
   2624     {
   2625       offset.x=kernel->x;
   2626       offset.y=kernel->y;
   2627       break;
   2628     }
   2629     default:
   2630     {
   2631       assert("Not a Primitive Morphology Method" != (char *) NULL);
   2632       break;
   2633     }
   2634   }
   2635   changed=0;
   2636   changes=(size_t *) AcquireQuantumMemory(GetOpenMPMaximumThreads(),
   2637     sizeof(*changes));
   2638   if (changes == (size_t *) NULL)
   2639     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
   2640   for (j=0; j < (ssize_t) GetOpenMPMaximumThreads(); j++)
   2641     changes[j]=0;
   2642 
   2643   if ((method == ConvolveMorphology) && (kernel->width == 1))
   2644     {
   2645       register ssize_t
   2646         x;
   2647 
   2648       /*
   2649         Special handling (for speed) of vertical (blur) kernels.  This performs
   2650         its handling in columns rather than in rows.  This is only done
   2651         for convolve as it is the only method that generates very large 1-D
   2652         vertical kernels (such as a 'BlurKernel')
   2653      */
   2654 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2655      #pragma omp parallel for schedule(static,4) shared(progress,status) \
   2656        magick_threads(image,morphology_image,image->columns,1)
   2657 #endif
   2658       for (x=0; x < (ssize_t) image->columns; x++)
   2659       {
   2660         const int
   2661           id = GetOpenMPThreadId();
   2662 
   2663         register const Quantum
   2664           *magick_restrict p;
   2665 
   2666         register Quantum
   2667           *magick_restrict q;
   2668 
   2669         register ssize_t
   2670           r;
   2671 
   2672         ssize_t
   2673           center;
   2674 
   2675         if (status == MagickFalse)
   2676           continue;
   2677         p=GetCacheViewVirtualPixels(image_view,x,-offset.y,1,image->rows+
   2678           kernel->height-1,exception);
   2679         q=GetCacheViewAuthenticPixels(morphology_view,x,0,1,
   2680           morphology_image->rows,exception);
   2681         if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   2682           {
   2683             status=MagickFalse;
   2684             continue;
   2685           }
   2686         center=(ssize_t) GetPixelChannels(image)*offset.y;
   2687         for (r=0; r < (ssize_t) image->rows; r++)
   2688         {
   2689           register ssize_t
   2690             i;
   2691 
   2692           for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   2693           {
   2694             double
   2695               alpha,
   2696               gamma,
   2697               pixel;
   2698 
   2699             PixelChannel
   2700               channel;
   2701 
   2702             PixelTrait
   2703               morphology_traits,
   2704               traits;
   2705 
   2706             register const MagickRealType
   2707               *magick_restrict k;
   2708 
   2709             register const Quantum
   2710               *magick_restrict pixels;
   2711 
   2712             register ssize_t
   2713               v;
   2714 
   2715             size_t
   2716               count;
   2717 
   2718             channel=GetPixelChannelChannel(image,i);
   2719             traits=GetPixelChannelTraits(image,channel);
   2720             morphology_traits=GetPixelChannelTraits(morphology_image,channel);
   2721             if ((traits == UndefinedPixelTrait) ||
   2722                 (morphology_traits == UndefinedPixelTrait))
   2723               continue;
   2724             if (((traits & CopyPixelTrait) != 0) ||
   2725                 (GetPixelReadMask(image,p+center) == 0))
   2726               {
   2727                 SetPixelChannel(morphology_image,channel,p[center+i],q);
   2728                 continue;
   2729               }
   2730             k=(&kernel->values[kernel->height-1]);
   2731             pixels=p;
   2732             pixel=bias;
   2733             gamma=0.0;
   2734             count=0;
   2735             if ((morphology_traits & BlendPixelTrait) == 0)
   2736               for (v=0; v < (ssize_t) kernel->height; v++)
   2737               {
   2738                 if (!IsNaN(*k))
   2739                   {
   2740                     pixel+=(*k)*pixels[i];
   2741                     gamma+=(*k);
   2742                     count++;
   2743                   }
   2744                 k--;
   2745                 pixels+=GetPixelChannels(image);
   2746               }
   2747             else
   2748               for (v=0; v < (ssize_t) kernel->height; v++)
   2749               {
   2750                 if (!IsNaN(*k))
   2751                   {
   2752                     alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
   2753                     pixel+=alpha*(*k)*pixels[i];
   2754                     gamma+=alpha*(*k);
   2755                     count++;
   2756                   }
   2757                 k--;
   2758                 pixels+=GetPixelChannels(image);
   2759               }
   2760             if (fabs(pixel-p[center+i]) > MagickEpsilon)
   2761               changes[id]++;
   2762             gamma=PerceptibleReciprocal(gamma);
   2763             if (count != 0)
   2764               gamma*=(double) kernel->height/count;
   2765             SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*
   2766               pixel),q);
   2767           }
   2768           p+=GetPixelChannels(image);
   2769           q+=GetPixelChannels(morphology_image);
   2770         }
   2771         if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
   2772           status=MagickFalse;
   2773         if (image->progress_monitor != (MagickProgressMonitor) NULL)
   2774           {
   2775             MagickBooleanType
   2776               proceed;
   2777 
   2778 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2779             #pragma omp critical (MagickCore_MorphologyPrimitive)
   2780 #endif
   2781             proceed=SetImageProgress(image,MorphologyTag,progress++,
   2782               image->rows);
   2783             if (proceed == MagickFalse)
   2784               status=MagickFalse;
   2785           }
   2786       }
   2787       morphology_image->type=image->type;
   2788       morphology_view=DestroyCacheView(morphology_view);
   2789       image_view=DestroyCacheView(image_view);
   2790       for (j=0; j < (ssize_t) GetOpenMPMaximumThreads(); j++)
   2791         changed+=changes[j];
   2792       changes=(size_t *) RelinquishMagickMemory(changes);
   2793       return(status ? (ssize_t) changed : 0);
   2794     }
   2795   /*
   2796     Normal handling of horizontal or rectangular kernels (row by row).
   2797   */
   2798 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   2799   #pragma omp parallel for schedule(static,4) shared(progress,status) \
   2800     magick_threads(image,morphology_image,image->rows,1)
   2801 #endif
   2802   for (y=0; y < (ssize_t) image->rows; y++)
   2803   {
   2804     const int
   2805       id = GetOpenMPThreadId();
   2806 
   2807     register const Quantum
   2808       *magick_restrict p;
   2809 
   2810     register Quantum
   2811       *magick_restrict q;
   2812 
   2813     register ssize_t
   2814       x;
   2815 
   2816     ssize_t
   2817       center;
   2818 
   2819     if (status == MagickFalse)
   2820       continue;
   2821     p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,
   2822       kernel->height,exception);
   2823     q=GetCacheViewAuthenticPixels(morphology_view,0,y,morphology_image->columns,
   2824       1,exception);
   2825     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   2826       {
   2827         status=MagickFalse;
   2828         continue;
   2829       }
   2830     center=(ssize_t) (GetPixelChannels(image)*width*offset.y+
   2831       GetPixelChannels(image)*offset.x);
   2832     for (x=0; x < (ssize_t) image->columns; x++)
   2833     {
   2834       register ssize_t
   2835         i;
   2836 
   2837       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   2838       {
   2839         double
   2840           alpha,
   2841           gamma,
   2842           intensity,
   2843           maximum,
   2844           minimum,
   2845           pixel;
   2846 
   2847         PixelChannel
   2848           channel;
   2849 
   2850         PixelTrait
   2851           morphology_traits,
   2852           traits;
   2853 
   2854         register const MagickRealType
   2855           *magick_restrict k;
   2856 
   2857         register const Quantum
   2858           *magick_restrict pixels;
   2859 
   2860         register ssize_t
   2861           u;
   2862 
   2863         size_t
   2864           count;
   2865 
   2866         ssize_t
   2867           v;
   2868 
   2869         channel=GetPixelChannelChannel(image,i);
   2870         traits=GetPixelChannelTraits(image,channel);
   2871         morphology_traits=GetPixelChannelTraits(morphology_image,channel);
   2872         if ((traits == UndefinedPixelTrait) ||
   2873             (morphology_traits == UndefinedPixelTrait))
   2874           continue;
   2875         if (((traits & CopyPixelTrait) != 0) ||
   2876             (GetPixelReadMask(image,p+center) == 0))
   2877           {
   2878             SetPixelChannel(morphology_image,channel,p[center+i],q);
   2879             continue;
   2880           }
   2881         pixels=p;
   2882         maximum=0.0;
   2883         minimum=(double) QuantumRange;
   2884         count=kernel->width*kernel->height;
   2885         switch (method)
   2886         {
   2887           case ConvolveMorphology: pixel=bias; break;
   2888           case HitAndMissMorphology: pixel=(double) QuantumRange; break;
   2889           case ThinningMorphology: pixel=(double) QuantumRange; break;
   2890           case ThickenMorphology: pixel=(double) QuantumRange; break;
   2891           case ErodeMorphology: pixel=(double) QuantumRange; break;
   2892           case DilateMorphology: pixel=0.0; break;
   2893           case ErodeIntensityMorphology:
   2894           case DilateIntensityMorphology:
   2895           case IterativeDistanceMorphology:
   2896           {
   2897             pixel=(double) p[center+i];
   2898             break;
   2899           }
   2900           default: pixel=0; break;
   2901         }
   2902         gamma=1.0;
   2903         switch (method)
   2904         {
   2905           case ConvolveMorphology:
   2906           {
   2907             /*
   2908                Weighted Average of pixels using reflected kernel
   2909 
   2910                For correct working of this operation for asymetrical kernels,
   2911                the kernel needs to be applied in its reflected form.  That is
   2912                its values needs to be reversed.
   2913 
   2914                Correlation is actually the same as this but without reflecting
   2915                the kernel, and thus 'lower-level' that Convolution.  However as
   2916                Convolution is the more common method used, and it does not
   2917                really cost us much in terms of processing to use a reflected
   2918                kernel, so it is Convolution that is implemented.
   2919 
   2920                Correlation will have its kernel reflected before calling this
   2921                function to do a Convolve.
   2922 
   2923                For more details of Correlation vs Convolution see
   2924                  http://www.cs.umd.edu/~djacobs/CMSC426/Convolution.pdf
   2925             */
   2926             k=(&kernel->values[kernel->width*kernel->height-1]);
   2927             count=0;
   2928             if ((morphology_traits & BlendPixelTrait) == 0)
   2929               {
   2930                 /*
   2931                   No alpha blending.
   2932                 */
   2933                 for (v=0; v < (ssize_t) kernel->height; v++)
   2934                 {
   2935                   for (u=0; u < (ssize_t) kernel->width; u++)
   2936                   {
   2937                     if (!IsNaN(*k))
   2938                       {
   2939                         pixel+=(*k)*pixels[i];
   2940                         count++;
   2941                       }
   2942                     k--;
   2943                     pixels+=GetPixelChannels(image);
   2944                   }
   2945                   pixels+=(image->columns-1)*GetPixelChannels(image);
   2946                 }
   2947                 break;
   2948               }
   2949             /*
   2950               Alpha blending.
   2951             */
   2952             gamma=0.0;
   2953             for (v=0; v < (ssize_t) kernel->height; v++)
   2954             {
   2955               for (u=0; u < (ssize_t) kernel->width; u++)
   2956               {
   2957                 if (!IsNaN(*k))
   2958                   {
   2959                     alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
   2960                     pixel+=alpha*(*k)*pixels[i];
   2961                     gamma+=alpha*(*k);
   2962                     count++;
   2963                   }
   2964                 k--;
   2965                 pixels+=GetPixelChannels(image);
   2966               }
   2967               pixels+=(image->columns-1)*GetPixelChannels(image);
   2968             }
   2969             break;
   2970           }
   2971           case ErodeMorphology:
   2972           {
   2973             /*
   2974               Minimum value within kernel neighbourhood.
   2975 
   2976               The kernel is not reflected for this operation.  In normal
   2977               Greyscale Morphology, the kernel value should be added
   2978               to the real value, this is currently not done, due to the
   2979               nature of the boolean kernels being used.
   2980             */
   2981             k=kernel->values;
   2982             for (v=0; v < (ssize_t) kernel->height; v++)
   2983             {
   2984               for (u=0; u < (ssize_t) kernel->width; u++)
   2985               {
   2986                 if (!IsNaN(*k) && (*k >= 0.5))
   2987                   {
   2988                     if ((double) pixels[i] < pixel)
   2989                       pixel=(double) pixels[i];
   2990                   }
   2991                 k++;
   2992                 pixels+=GetPixelChannels(image);
   2993               }
   2994               pixels+=(image->columns-1)*GetPixelChannels(image);
   2995             }
   2996             break;
   2997           }
   2998           case DilateMorphology:
   2999           {
   3000             /*
   3001                Maximum value within kernel neighbourhood.
   3002 
   3003                For correct working of this operation for asymetrical kernels,
   3004                the kernel needs to be applied in its reflected form.  That is
   3005                its values needs to be reversed.
   3006 
   3007                In normal Greyscale Morphology, the kernel value should be
   3008                added to the real value, this is currently not done, due to the
   3009                nature of the boolean kernels being used.
   3010             */
   3011             count=0;
   3012             k=(&kernel->values[kernel->width*kernel->height-1]);
   3013             for (v=0; v < (ssize_t) kernel->height; v++)
   3014             {
   3015               for (u=0; u < (ssize_t) kernel->width; u++)
   3016               {
   3017                 if (!IsNaN(*k) && (*k > 0.5))
   3018                   {
   3019                     if ((double) pixels[i] > pixel)
   3020                       pixel=(double) pixels[i];
   3021                   }
   3022                 k--;
   3023                 pixels+=GetPixelChannels(image);
   3024               }
   3025               pixels+=(image->columns-1)*GetPixelChannels(image);
   3026             }
   3027             break;
   3028           }
   3029           case HitAndMissMorphology:
   3030           case ThinningMorphology:
   3031           case ThickenMorphology:
   3032           {
   3033             /*
   3034                Minimum of foreground pixel minus maxumum of background pixels.
   3035 
   3036                The kernel is not reflected for this operation, and consists
   3037                of both foreground and background pixel neighbourhoods, 0.0 for
   3038                background, and 1.0 for foreground with either Nan or 0.5 values
   3039                for don't care.
   3040 
   3041                This never produces a meaningless negative result.  Such results
   3042                cause Thinning/Thicken to not work correctly when used against a
   3043                greyscale image.
   3044             */
   3045             count=0;
   3046             k=kernel->values;
   3047             for (v=0; v < (ssize_t) kernel->height; v++)
   3048             {
   3049               for (u=0; u < (ssize_t) kernel->width; u++)
   3050               {
   3051                 if (!IsNaN(*k))
   3052                   {
   3053                     if (*k > 0.7)
   3054                       {
   3055                         if ((double) pixels[i] < pixel)
   3056                           pixel=(double) pixels[i];
   3057                       }
   3058                     else
   3059                       if (*k < 0.3)
   3060                         {
   3061                           if ((double) pixels[i] > maximum)
   3062                             maximum=(double) pixels[i];
   3063                         }
   3064                     count++;
   3065                   }
   3066                 k++;
   3067                 pixels+=GetPixelChannels(image);
   3068               }
   3069               pixels+=(image->columns-1)*GetPixelChannels(image);
   3070             }
   3071             pixel-=maximum;
   3072             if (pixel < 0.0)
   3073               pixel=0.0;
   3074             if (method ==  ThinningMorphology)
   3075               pixel=(double) p[center+i]-pixel;
   3076             else
   3077               if (method ==  ThickenMorphology)
   3078                 pixel+=(double) p[center+i]+pixel;
   3079             break;
   3080           }
   3081           case ErodeIntensityMorphology:
   3082           {
   3083             /*
   3084               Select pixel with minimum intensity within kernel neighbourhood.
   3085 
   3086               The kernel is not reflected for this operation.
   3087             */
   3088             count=0;
   3089             k=kernel->values;
   3090             for (v=0; v < (ssize_t) kernel->height; v++)
   3091             {
   3092               for (u=0; u < (ssize_t) kernel->width; u++)
   3093               {
   3094                 if (!IsNaN(*k) && (*k >= 0.5))
   3095                   {
   3096                     intensity=(double) GetPixelIntensity(image,pixels);
   3097                     if (intensity < minimum)
   3098                       {
   3099                         pixel=(double) pixels[i];
   3100                         minimum=intensity;
   3101                       }
   3102                     count++;
   3103                   }
   3104                 k++;
   3105                 pixels+=GetPixelChannels(image);
   3106               }
   3107               pixels+=(image->columns-1)*GetPixelChannels(image);
   3108             }
   3109             break;
   3110           }
   3111           case DilateIntensityMorphology:
   3112           {
   3113             /*
   3114               Select pixel with maximum intensity within kernel neighbourhood.
   3115 
   3116               The kernel is not reflected for this operation.
   3117             */
   3118             count=0;
   3119             k=(&kernel->values[kernel->width*kernel->height-1]);
   3120             for (v=0; v < (ssize_t) kernel->height; v++)
   3121             {
   3122               for (u=0; u < (ssize_t) kernel->width; u++)
   3123               {
   3124                 if (!IsNaN(*k) && (*k >= 0.5))
   3125                   {
   3126                     intensity=(double) GetPixelIntensity(image,pixels);
   3127                     if (intensity > maximum)
   3128                       {
   3129                         pixel=(double) pixels[i];
   3130                         maximum=intensity;
   3131                       }
   3132                     count++;
   3133                   }
   3134                 k--;
   3135                 pixels+=GetPixelChannels(image);
   3136               }
   3137               pixels+=(image->columns-1)*GetPixelChannels(image);
   3138             }
   3139             break;
   3140           }
   3141           case IterativeDistanceMorphology:
   3142           {
   3143             /*
   3144                Compute th iterative distance from black edge of a white image
   3145                shape.  Essentually white values are decreased to the smallest
   3146                'distance from edge' it can find.
   3147 
   3148                It works by adding kernel values to the neighbourhood, and and
   3149                select the minimum value found. The kernel is rotated before
   3150                use, so kernel distances match resulting distances, when a user
   3151                provided asymmetric kernel is applied.
   3152 
   3153                This code is nearly identical to True GrayScale Morphology but
   3154                not quite.
   3155 
   3156                GreyDilate Kernel values added, maximum value found Kernel is
   3157                rotated before use.
   3158 
   3159                GrayErode:  Kernel values subtracted and minimum value found No
   3160                kernel rotation used.
   3161 
   3162                Note the the Iterative Distance method is essentially a
   3163                GrayErode, but with negative kernel values, and kernel rotation
   3164                applied.
   3165             */
   3166             count=0;
   3167             k=(&kernel->values[kernel->width*kernel->height-1]);
   3168             for (v=0; v < (ssize_t) kernel->height; v++)
   3169             {
   3170               for (u=0; u < (ssize_t) kernel->width; u++)
   3171               {
   3172                 if (!IsNaN(*k))
   3173                   {
   3174                     if ((pixels[i]+(*k)) < pixel)
   3175                       pixel=(double) pixels[i]+(*k);
   3176                     count++;
   3177                   }
   3178                 k--;
   3179                 pixels+=GetPixelChannels(image);
   3180               }
   3181               pixels+=(image->columns-1)*GetPixelChannels(image);
   3182             }
   3183             break;
   3184           }
   3185           case UndefinedMorphology:
   3186           default:
   3187             break;
   3188         }
   3189         if (fabs(pixel-p[center+i]) > MagickEpsilon)
   3190           changes[id]++;
   3191         gamma=PerceptibleReciprocal(gamma);
   3192         if (count != 0)
   3193           gamma*=(double) kernel->height*kernel->width/count;
   3194         SetPixelChannel(morphology_image,channel,ClampToQuantum(gamma*pixel),q);
   3195       }
   3196       p+=GetPixelChannels(image);
   3197       q+=GetPixelChannels(morphology_image);
   3198     }
   3199     if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
   3200       status=MagickFalse;
   3201     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   3202       {
   3203         MagickBooleanType
   3204           proceed;
   3205 
   3206 #if defined(MAGICKCORE_OPENMP_SUPPORT)
   3207         #pragma omp critical (MagickCore_MorphologyPrimitive)
   3208 #endif
   3209         proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
   3210         if (proceed == MagickFalse)
   3211           status=MagickFalse;
   3212       }
   3213   }
   3214   morphology_view=DestroyCacheView(morphology_view);
   3215   image_view=DestroyCacheView(image_view);
   3216   for (j=0; j < (ssize_t) GetOpenMPMaximumThreads(); j++)
   3217     changed+=changes[j];
   3218   changes=(size_t *) RelinquishMagickMemory(changes);
   3219   return(status ? (ssize_t) changed : -1);
   3220 }
   3221 
   3222 /*
   3223   This is almost identical to the MorphologyPrimative() function above, but
   3224   applies the primitive directly to the actual image using two passes, once in
   3225   each direction, with the results of the previous (and current) row being
   3226   re-used.
   3227 
   3228   That is after each row is 'Sync'ed' into the image, the next row makes use of
   3229   those values as part of the calculation of the next row.  It repeats, but
   3230   going in the oppisite (bottom-up) direction.
   3231 
   3232   Because of this 're-use of results' this function can not make use of multi-
   3233   threaded, parellel processing.
   3234 */
   3235 static ssize_t MorphologyPrimitiveDirect(Image *image,
   3236   const MorphologyMethod method,const KernelInfo *kernel,
   3237   ExceptionInfo *exception)
   3238 {
   3239   CacheView
   3240     *morphology_view,
   3241     *image_view;
   3242 
   3243   MagickBooleanType
   3244     status;
   3245 
   3246   MagickOffsetType
   3247     progress;
   3248 
   3249   OffsetInfo
   3250     offset;
   3251 
   3252   size_t
   3253     width,
   3254     changed;
   3255 
   3256   ssize_t
   3257     y;
   3258 
   3259   assert(image != (Image *) NULL);
   3260   assert(image->signature == MagickCoreSignature);
   3261   assert(kernel != (KernelInfo *) NULL);
   3262   assert(kernel->signature == MagickCoreSignature);
   3263   assert(exception != (ExceptionInfo *) NULL);
   3264   assert(exception->signature == MagickCoreSignature);
   3265   status=MagickTrue;
   3266   changed=0;
   3267   progress=0;
   3268   switch(method)
   3269   {
   3270     case DistanceMorphology:
   3271     case VoronoiMorphology:
   3272     {
   3273       /*
   3274         Kernel reflected about origin.
   3275       */
   3276       offset.x=(ssize_t) kernel->width-kernel->x-1;
   3277       offset.y=(ssize_t) kernel->height-kernel->y-1;
   3278       break;
   3279     }
   3280     default:
   3281     {
   3282       offset.x=kernel->x;
   3283       offset.y=kernel->y;
   3284       break;
   3285     }
   3286   }
   3287   /*
   3288     Two views into same image, do not thread.
   3289   */
   3290   image_view=AcquireVirtualCacheView(image,exception);
   3291   morphology_view=AcquireAuthenticCacheView(image,exception);
   3292   width=image->columns+kernel->width-1;
   3293   for (y=0; y < (ssize_t) image->rows; y++)
   3294   {
   3295     register const Quantum
   3296       *magick_restrict p;
   3297 
   3298     register Quantum
   3299       *magick_restrict q;
   3300 
   3301     register ssize_t
   3302       x;
   3303 
   3304     ssize_t
   3305       center;
   3306 
   3307     /*
   3308       Read virtual pixels, and authentic pixels, from the same image!  We read
   3309       using virtual to get virtual pixel handling, but write back into the same
   3310       image.
   3311 
   3312       Only top half of kernel is processed as we do a single pass downward
   3313       through the image iterating the distance function as we go.
   3314     */
   3315     if (status == MagickFalse)
   3316       continue;
   3317     p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,(size_t)
   3318       offset.y+1,exception);
   3319     q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
   3320       exception);
   3321     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   3322       {
   3323         status=MagickFalse;
   3324         continue;
   3325       }
   3326     center=(ssize_t) (GetPixelChannels(image)*width*offset.y+
   3327       GetPixelChannels(image)*offset.x);
   3328     for (x=0; x < (ssize_t) image->columns; x++)
   3329     {
   3330       register ssize_t
   3331         i;
   3332 
   3333       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3334       {
   3335         double
   3336           pixel;
   3337 
   3338         PixelTrait
   3339           traits;
   3340 
   3341         register const MagickRealType
   3342           *magick_restrict k;
   3343 
   3344         register const Quantum
   3345           *magick_restrict pixels;
   3346 
   3347         register ssize_t
   3348           u;
   3349 
   3350         ssize_t
   3351           v;
   3352 
   3353         traits=GetPixelChannelTraits(image,(PixelChannel) i);
   3354         if (traits == UndefinedPixelTrait)
   3355           continue;
   3356         if (((traits & CopyPixelTrait) != 0) ||
   3357             (GetPixelReadMask(image,p+center) == 0))
   3358           continue;
   3359         pixels=p;
   3360         pixel=(double) QuantumRange;
   3361         switch (method)
   3362         {
   3363           case DistanceMorphology:
   3364           {
   3365             k=(&kernel->values[kernel->width*kernel->height-1]);
   3366             for (v=0; v <= offset.y; v++)
   3367             {
   3368               for (u=0; u < (ssize_t) kernel->width; u++)
   3369               {
   3370                 if (!IsNaN(*k))
   3371                   {
   3372                     if ((pixels[i]+(*k)) < pixel)
   3373                       pixel=(double) pixels[i]+(*k);
   3374                   }
   3375                 k--;
   3376                 pixels+=GetPixelChannels(image);
   3377               }
   3378               pixels+=(image->columns-1)*GetPixelChannels(image);
   3379             }
   3380             k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
   3381             pixels=q-offset.x*GetPixelChannels(image);
   3382             for (u=0; u < offset.x; u++)
   3383             {
   3384               if (!IsNaN(*k) && ((x+u-offset.x) >= 0))
   3385                 {
   3386                   if ((pixels[i]+(*k)) < pixel)
   3387                     pixel=(double) pixels[i]+(*k);
   3388                 }
   3389               k--;
   3390               pixels+=GetPixelChannels(image);
   3391             }
   3392             break;
   3393           }
   3394           case VoronoiMorphology:
   3395           {
   3396             k=(&kernel->values[kernel->width*kernel->height-1]);
   3397             for (v=0; v < offset.y; v++)
   3398             {
   3399               for (u=0; u < (ssize_t) kernel->width; u++)
   3400               {
   3401                 if (!IsNaN(*k))
   3402                   {
   3403                     if ((pixels[i]+(*k)) < pixel)
   3404                       pixel=(double) pixels[i]+(*k);
   3405                   }
   3406                 k--;
   3407                 pixels+=GetPixelChannels(image);
   3408               }
   3409               pixels+=(image->columns-1)*GetPixelChannels(image);
   3410             }
   3411             k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
   3412             pixels=q-offset.x*GetPixelChannels(image);
   3413             for (u=0; u < offset.x; u++)
   3414             {
   3415               if (!IsNaN(*k) && ((x+u-offset.x) >= 0))
   3416                 {
   3417                   if ((pixels[i]+(*k)) < pixel)
   3418                     pixel=(double) pixels[i]+(*k);
   3419                 }
   3420               k--;
   3421               pixels+=GetPixelChannels(image);
   3422             }
   3423             break;
   3424           }
   3425           default:
   3426             break;
   3427         }
   3428         if (fabs(pixel-q[i]) > MagickEpsilon)
   3429           changed++;
   3430         q[i]=ClampToQuantum(pixel);
   3431       }
   3432       p+=GetPixelChannels(image);
   3433       q+=GetPixelChannels(image);
   3434     }
   3435     if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
   3436       status=MagickFalse;
   3437     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   3438       {
   3439         MagickBooleanType
   3440           proceed;
   3441 
   3442         proceed=SetImageProgress(image,MorphologyTag,progress++,2*image->rows);
   3443         if (proceed == MagickFalse)
   3444           status=MagickFalse;
   3445       }
   3446   }
   3447   morphology_view=DestroyCacheView(morphology_view);
   3448   image_view=DestroyCacheView(image_view);
   3449   /*
   3450     Do the reverse pass through the image.
   3451   */
   3452   image_view=AcquireVirtualCacheView(image,exception);
   3453   morphology_view=AcquireAuthenticCacheView(image,exception);
   3454   for (y=(ssize_t) image->rows-1; y >= 0; y--)
   3455   {
   3456     register const Quantum
   3457       *magick_restrict p;
   3458 
   3459     register Quantum
   3460       *magick_restrict q;
   3461 
   3462     register ssize_t
   3463       x;
   3464 
   3465     ssize_t
   3466       center;
   3467 
   3468     /*
   3469        Read virtual pixels, and authentic pixels, from the same image.  We
   3470        read using virtual to get virtual pixel handling, but write back
   3471        into the same image.
   3472 
   3473        Only the bottom half of the kernel is processed as we up the image.
   3474     */
   3475     if (status == MagickFalse)
   3476       continue;
   3477     p=GetCacheViewVirtualPixels(image_view,-offset.x,y,width,(size_t)
   3478       kernel->y+1,exception);
   3479     q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
   3480       exception);
   3481     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
   3482       {
   3483         status=MagickFalse;
   3484         continue;
   3485       }
   3486     p+=(image->columns-1)*GetPixelChannels(image);
   3487     q+=(image->columns-1)*GetPixelChannels(image);
   3488     center=(ssize_t) (offset.x*GetPixelChannels(image));
   3489     for (x=(ssize_t) image->columns-1; x >= 0; x--)
   3490     {
   3491       register ssize_t
   3492         i;
   3493 
   3494       for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
   3495       {
   3496         double
   3497           pixel;
   3498 
   3499         PixelTrait
   3500           traits;
   3501 
   3502         register const MagickRealType
   3503           *magick_restrict k;
   3504 
   3505         register const Quantum
   3506           *magick_restrict pixels;
   3507 
   3508         register ssize_t
   3509           u;
   3510 
   3511         ssize_t
   3512           v;
   3513 
   3514         traits=GetPixelChannelTraits(image,(PixelChannel) i);
   3515         if (traits == UndefinedPixelTrait)
   3516           continue;
   3517         if (((traits & CopyPixelTrait) != 0) ||
   3518             (GetPixelReadMask(image,p+center) == 0))
   3519           continue;
   3520         pixels=p;
   3521         pixel=(double) QuantumRange;
   3522         switch (method)
   3523         {
   3524           case DistanceMorphology:
   3525           {
   3526             k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
   3527             for (v=offset.y; v < (ssize_t) kernel->height; v++)
   3528             {
   3529               for (u=0; u < (ssize_t) kernel->width; u++)
   3530               {
   3531                 if (!IsNaN(*k))
   3532                   {
   3533                     if ((pixels[i]+(*k)) < pixel)
   3534                       pixel=(double) pixels[i]+(*k);
   3535                   }
   3536                 k--;
   3537                 pixels+=GetPixelChannels(image);
   3538               }
   3539               pixels+=(image->columns-1)*GetPixelChannels(image);
   3540             }
   3541             k=(&kernel->values[kernel->width*kernel->y+kernel->x-1]);
   3542             pixels=q;
   3543             for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
   3544             {
   3545               pixels+=GetPixelChannels(image);
   3546               if (!IsNaN(*k) && ((x+u-offset.x) < (ssize_t) image->columns))
   3547                 {
   3548                   if ((pixels[i]+(*k)) < pixel)
   3549                     pixel=(double) pixels[i]+(*k);
   3550                 }
   3551               k--;
   3552             }
   3553             break;
   3554           }
   3555           case VoronoiMorphology:
   3556           {
   3557             k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
   3558             for (v=offset.y; v < (ssize_t) kernel->height; v++)
   3559             {
   3560               for (u=0; u < (ssize_t) kernel->width; u++)
   3561               {
   3562                 if (!IsNaN(*k))
   3563                   {
   3564                     if ((pixels[i]+(*k)) < pixel)
   3565                       pixel=(double) pixels[i]+(*k);
   3566                   }
   3567                 k--;
   3568                 pixels+=GetPixelChannels(image);
   3569               }
   3570               pixels+=(image->columns-1)*GetPixelChannels(image);
   3571             }
   3572             k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
   3573             pixels=q;
   3574             for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
   3575             {
   3576               pixels+=GetPixelChannels(image);
   3577               if (!IsNaN(*k) && ((x+u-offset.x) < (ssize_t) image->columns))
   3578                 {
   3579                   if ((pixels[i]+(*k)) < pixel)
   3580                     pixel=(double) pixels[i]+(*k);
   3581                 }
   3582               k--;
   3583             }
   3584             break;
   3585           }
   3586           default:
   3587             break;
   3588         }
   3589         if (fabs(pixel-q[i]) > MagickEpsilon)
   3590           changed++;
   3591         q[i]=ClampToQuantum(pixel);
   3592       }
   3593       p-=GetPixelChannels(image);
   3594       q-=GetPixelChannels(image);
   3595     }
   3596     if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
   3597       status=MagickFalse;
   3598     if (image->progress_monitor != (MagickProgressMonitor) NULL)
   3599       {
   3600         MagickBooleanType
   3601           proceed;
   3602 
   3603         proceed=SetImageProgress(image,MorphologyTag,progress++,2*image->rows);
   3604         if (proceed == MagickFalse)
   3605           status=MagickFalse;
   3606       }
   3607   }
   3608   morphology_view=DestroyCacheView(morphology_view);
   3609   image_view=DestroyCacheView(image_view);
   3610   return(status ? (ssize_t) changed : -1);
   3611 }
   3612 
   3613 /*
   3614   Apply a Morphology by calling one of the above low level primitive
   3615   application functions.  This function handles any iteration loops,
   3616   composition or re-iteration of results, and compound morphology methods that
   3617   is based on multiple low-level (staged) morphology methods.
   3618 
   3619   Basically this provides the complex glue between the requested morphology
   3620   method and raw low-level implementation (above).
   3621 */
   3622 MagickPrivate Image *MorphologyApply(const Image *image,
   3623   const MorphologyMethod method, const ssize_t iterations,
   3624   const KernelInfo *kernel, const CompositeOperator compose,const double bias,
   3625   ExceptionInfo *exception)
   3626 {
   3627   CompositeOperator
   3628     curr_compose;
   3629 
   3630   Image
   3631     *curr_image,    /* Image we are working with or iterating */
   3632     *work_image,    /* secondary image for primitive iteration */
   3633     *save_image,    /* saved image - for 'edge' method only */
   3634     *rslt_image;    /* resultant image - after multi-kernel handling */
   3635 
   3636   KernelInfo
   3637     *reflected_kernel, /* A reflected copy of the kernel (if needed) */
   3638     *norm_kernel,      /* the current normal un-reflected kernel */
   3639     *rflt_kernel,      /* the current reflected kernel (if needed) */
   3640     *this_kernel;      /* the kernel being applied */
   3641 
   3642   MorphologyMethod
   3643     primitive;      /* the current morphology primitive being applied */
   3644 
   3645   CompositeOperator
   3646     rslt_compose;   /* multi-kernel compose method for results to use */
   3647 
   3648   MagickBooleanType
   3649     special,        /* do we use a direct modify function? */
   3650     verbose;        /* verbose output of results */
   3651 
   3652   size_t
   3653     method_loop,    /* Loop 1: number of compound method iterations (norm 1) */
   3654     method_limit,   /*         maximum number of compound method iterations */
   3655     kernel_number,  /* Loop 2: the kernel number being applied */
   3656     stage_loop,     /* Loop 3: primitive loop for compound morphology */
   3657     stage_limit,    /*         how many primitives are in this compound */
   3658     kernel_loop,    /* Loop 4: iterate the kernel over image */
   3659     kernel_limit,   /*         number of times to iterate kernel */
   3660     count,          /* total count of primitive steps applied */
   3661     kernel_changed, /* total count of changed using iterated kernel */
   3662     method_changed; /* total count of changed over method iteration */
   3663 
   3664   ssize_t
   3665     changed;        /* number pixels changed by last primitive operation */
   3666 
   3667   char
   3668     v_info[MagickPathExtent];
   3669 
   3670   assert(image != (Image *) NULL);
   3671   assert(image->signature == MagickCoreSignature);
   3672   assert(kernel != (KernelInfo *) NULL);
   3673   assert(kernel->signature == MagickCoreSignature);
   3674   assert(exception != (ExceptionInfo *) NULL);
   3675   assert(exception->signature == MagickCoreSignature);
   3676 
   3677   count = 0;      /* number of low-level morphology primitives performed */
   3678   if ( iterations == 0 )
   3679     return((Image *) NULL);   /* null operation - nothing to do! */
   3680 
   3681   kernel_limit = (size_t) iterations;
   3682   if ( iterations < 0 )  /* negative interations = infinite (well alomst) */
   3683      kernel_limit = image->columns>image->rows ? image->columns : image->rows;
   3684 
   3685   verbose = IsStringTrue(GetImageArtifact(image,"debug"));
   3686 
   3687   /* initialise for cleanup */
   3688   curr_image = (Image *) image;
   3689   curr_compose = image->compose;
   3690   (void) curr_compose;
   3691   work_image = save_image = rslt_image = (Image *) NULL;
   3692   reflected_kernel = (KernelInfo *) NULL;
   3693 
   3694   /* Initialize specific methods
   3695    * + which loop should use the given iteratations
   3696    * + how many primitives make up the compound morphology
   3697    * + multi-kernel compose method to use (by default)
   3698    */
   3699   method_limit = 1;       /* just do method once, unless otherwise set */
   3700   stage_limit = 1;        /* assume method is not a compound */
   3701   special = MagickFalse;   /* assume it is NOT a direct modify primitive */
   3702   rslt_compose = compose; /* and we are composing multi-kernels as given */
   3703   switch( method ) {
   3704     case SmoothMorphology:  /* 4 primitive compound morphology */
   3705       stage_limit = 4;
   3706       break;
   3707     case OpenMorphology:    /* 2 primitive compound morphology */
   3708     case OpenIntensityMorphology:
   3709     case TopHatMorphology:
   3710     case CloseMorphology:
   3711     case CloseIntensityMorphology:
   3712     case BottomHatMorphology:
   3713     case EdgeMorphology:
   3714       stage_limit = 2;
   3715       break;
   3716     case HitAndMissMorphology:
   3717       rslt_compose = LightenCompositeOp;  /* Union of multi-kernel results */
   3718       /* FALL THUR */
   3719     case ThinningMorphology:
   3720     case ThickenMorphology:
   3721       method_limit = kernel_limit;  /* iterate the whole method */
   3722       kernel_limit = 1;             /* do not do kernel iteration  */
   3723       break;
   3724     case DistanceMorphology:
   3725     case VoronoiMorphology:
   3726       special = MagickTrue;         /* use special direct primative */
   3727       break;
   3728     default:
   3729       break;
   3730   }
   3731 
   3732   /* Apply special methods with special requirments
   3733   ** For example, single run only, or post-processing requirements
   3734   */
   3735   if ( special != MagickFalse )
   3736     {
   3737       rslt_image=CloneImage(image,0,0,MagickTrue,exception);
   3738       if (rslt_image == (Image *) NULL)
   3739         goto error_cleanup;
   3740       if (SetImageStorageClass(rslt_image,DirectClass,exception) == MagickFalse)
   3741         goto error_cleanup;
   3742 
   3743       changed=MorphologyPrimitiveDirect(rslt_image,method,kernel,exception);
   3744 
   3745       if (verbose != MagickFalse)
   3746         (void) (void) FormatLocaleFile(stderr,
   3747           "%s:%.20g.%.20g #%.20g => Changed %.20g\n",
   3748           CommandOptionToMnemonic(MagickMorphologyOptions, method),
   3749           1.0,0.0,1.0, (double) changed);
   3750 
   3751       if ( changed < 0 )
   3752         goto error_cleanup;
   3753 
   3754       if ( method == VoronoiMorphology ) {
   3755         /* Preserve the alpha channel of input image - but turned it off */
   3756         (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
   3757           exception);
   3758         (void) CompositeImage(rslt_image,image,CopyAlphaCompositeOp,
   3759           MagickTrue,0,0,exception);
   3760         (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
   3761           exception);
   3762       }
   3763       goto exit_cleanup;
   3764     }
   3765 
   3766   /* Handle user (caller) specified multi-kernel composition method */
   3767   if ( compose != UndefinedCompositeOp )
   3768     rslt_compose = compose;  /* override default composition for method */
   3769   if ( rslt_compose == UndefinedCompositeOp )
   3770     rslt_compose = NoCompositeOp; /* still not defined! Then re-iterate */
   3771 
   3772   /* Some methods require a reflected kernel to use with primitives.
   3773    * Create the reflected kernel for those methods. */
   3774   switch ( method ) {
   3775     case CorrelateMorphology:
   3776     case CloseMorphology:
   3777     case CloseIntensityMorphology:
   3778     case BottomHatMorphology:
   3779     case SmoothMorphology:
   3780       reflected_kernel = CloneKernelInfo(kernel);
   3781       if (reflected_kernel == (KernelInfo *) NULL)
   3782         goto error_cleanup;
   3783       RotateKernelInfo(reflected_kernel,180);
   3784       break;
   3785     default:
   3786       break;
   3787   }
   3788 
   3789   /* Loops around more primitive morpholgy methods
   3790   **  erose, dilate, open, close, smooth, edge, etc...
   3791   */
   3792   /* Loop 1:  iterate the compound method */
   3793   method_loop = 0;
   3794   method_changed = 1;
   3795   while ( method_loop < method_limit && method_changed > 0 ) {
   3796     method_loop++;
   3797     method_changed = 0;
   3798 
   3799     /* Loop 2:  iterate over each kernel in a multi-kernel list */
   3800     norm_kernel = (KernelInfo *) kernel;
   3801     this_kernel = (KernelInfo *) kernel;
   3802     rflt_kernel = reflected_kernel;
   3803 
   3804     kernel_number = 0;
   3805     while ( norm_kernel != NULL ) {
   3806 
   3807       /* Loop 3: Compound Morphology Staging - Select Primative to apply */
   3808       stage_loop = 0;          /* the compound morphology stage number */
   3809       while ( stage_loop < stage_limit ) {
   3810         stage_loop++;   /* The stage of the compound morphology */
   3811 
   3812         /* Select primitive morphology for this stage of compound method */
   3813         this_kernel = norm_kernel; /* default use unreflected kernel */
   3814         primitive = method;        /* Assume method is a primitive */
   3815         switch( method ) {
   3816           case ErodeMorphology:      /* just erode */
   3817           case EdgeInMorphology:     /* erode and image difference */
   3818             primitive = ErodeMorphology;
   3819             break;
   3820           case DilateMorphology:     /* just dilate */
   3821           case EdgeOutMorphology:    /* dilate and image difference */
   3822             primitive = DilateMorphology;
   3823             break;
   3824           case OpenMorphology:       /* erode then dialate */
   3825           case TopHatMorphology:     /* open and image difference */
   3826             primitive = ErodeMorphology;
   3827             if ( stage_loop == 2 )
   3828               primitive = DilateMorphology;
   3829             break;
   3830           case OpenIntensityMorphology:
   3831             primitive = ErodeIntensityMorphology;
   3832             if ( stage_loop == 2 )
   3833               primitive = DilateIntensityMorphology;
   3834             break;
   3835           case CloseMorphology:      /* dilate, then erode */
   3836           case BottomHatMorphology:  /* close and image difference */
   3837             this_kernel = rflt_kernel; /* use the reflected kernel */
   3838             primitive = DilateMorphology;
   3839             if ( stage_loop == 2 )
   3840               primitive = ErodeMorphology;
   3841             break;
   3842           case CloseIntensityMorphology:
   3843             this_kernel = rflt_kernel; /* use the reflected kernel */
   3844             primitive = DilateIntensityMorphology;
   3845             if ( stage_loop == 2 )
   3846               primitive = ErodeIntensityMorphology;
   3847             break;
   3848           case SmoothMorphology:         /* open, close */
   3849             switch ( stage_loop ) {
   3850               case 1: /* start an open method, which starts with Erode */
   3851                 primitive = ErodeMorphology;
   3852                 break;
   3853               case 2:  /* now Dilate the Erode */
   3854                 primitive = DilateMorphology;
   3855                 break;
   3856               case 3:  /* Reflect kernel a close */
   3857                 this_kernel = rflt_kernel; /* use the reflected kernel */
   3858                 primitive = DilateMorphology;
   3859                 break;
   3860               case 4:  /* Finish the Close */
   3861                 this_kernel = rflt_kernel; /* use the reflected kernel */
   3862                 primitive = ErodeMorphology;
   3863                 break;
   3864             }
   3865             break;
   3866           case EdgeMorphology:        /* dilate and erode difference */
   3867             primitive = DilateMorphology;
   3868             if ( stage_loop == 2 ) {
   3869               save_image = curr_image;      /* save the image difference */
   3870               curr_image = (Image *) image;
   3871               primitive = ErodeMorphology;
   3872             }
   3873             break;
   3874           case CorrelateMorphology:
   3875             /* A Correlation is a Convolution with a reflected kernel.
   3876             ** However a Convolution is a weighted sum using a reflected
   3877             ** kernel.  It may seem stange to convert a Correlation into a
   3878             ** Convolution as the Correlation is the simplier method, but
   3879             ** Convolution is much more commonly used, and it makes sense to
   3880             ** implement it directly so as to avoid the need to duplicate the
   3881             ** kernel when it is not required (which is typically the
   3882             ** default).
   3883             */
   3884             this_kernel = rflt_kernel; /* use the reflected kernel */
   3885             primitive = ConvolveMorphology;
   3886             break;
   3887           default:
   3888             break;
   3889         }
   3890         assert( this_kernel != (KernelInfo *) NULL );
   3891 
   3892         /* Extra information for debugging compound operations */
   3893         if (verbose != MagickFalse) {
   3894           if ( stage_limit > 1 )
   3895             (void) FormatLocaleString(v_info,MagickPathExtent,"%s:%.20g.%.20g -> ",
   3896              CommandOptionToMnemonic(MagickMorphologyOptions,method),(double)
   3897              method_loop,(double) stage_loop);
   3898           else if ( primitive != method )
   3899             (void) FormatLocaleString(v_info, MagickPathExtent, "%s:%.20g -> ",
   3900               CommandOptionToMnemonic(MagickMorphologyOptions, method),(double)
   3901               method_loop);
   3902           else
   3903             v_info[0] = '\0';
   3904         }
   3905 
   3906         /* Loop 4: Iterate the kernel with primitive */
   3907         kernel_loop = 0;
   3908         kernel_changed = 0;
   3909         changed = 1;
   3910         while ( kernel_loop < kernel_limit && changed > 0 ) {
   3911           kernel_loop++;     /* the iteration of this kernel */
   3912 
   3913           /* Create a clone as the destination image, if not yet defined */
   3914           if ( work_image == (Image *) NULL )
   3915             {
   3916               work_image=CloneImage(image,0,0,MagickTrue,exception);
   3917               if (work_image == (Image *) NULL)
   3918                 goto error_cleanup;
   3919               if (SetImageStorageClass(work_image,DirectClass,exception) == MagickFalse)
   3920                 goto error_cleanup;
   3921             }
   3922 
   3923           /* APPLY THE MORPHOLOGICAL PRIMITIVE (curr -> work) */
   3924           count++;
   3925           changed = MorphologyPrimitive(curr_image, work_image, primitive,
   3926                        this_kernel, bias, exception);
   3927           if (verbose != MagickFalse) {
   3928             if ( kernel_loop > 1 )
   3929               (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line from previous */
   3930             (void) (void) FormatLocaleFile(stderr,
   3931               "%s%s%s:%.20g.%.20g #%.20g => Changed %.20g",
   3932               v_info,CommandOptionToMnemonic(MagickMorphologyOptions,
   3933               primitive),(this_kernel == rflt_kernel ) ? "*" : "",
   3934               (double) (method_loop+kernel_loop-1),(double) kernel_number,
   3935               (double) count,(double) changed);
   3936           }
   3937           if ( changed < 0 )
   3938             goto error_cleanup;
   3939           kernel_changed += changed;
   3940           method_changed += changed;
   3941 
   3942           /* prepare next loop */
   3943           { Image *tmp = work_image;   /* swap images for iteration */
   3944             work_image = curr_image;
   3945             curr_image = tmp;
   3946           }
   3947           if ( work_image == image )
   3948             work_image = (Image *) NULL; /* replace input 'image' */
   3949 
   3950         } /* End Loop 4: Iterate the kernel with primitive */
   3951 
   3952         if (verbose != MagickFalse && kernel_changed != (size_t)changed)
   3953           (void) FormatLocaleFile(stderr, "   Total %.20g",(double) kernel_changed);
   3954         if (verbose != MagickFalse && stage_loop < stage_limit)
   3955           (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line before looping */
   3956 
   3957 #if 0
   3958     (void) FormatLocaleFile(stderr, "--E-- image=0x%lx\n", (unsigned long)image);
   3959     (void) FormatLocaleFile(stderr, "      curr =0x%lx\n", (unsigned long)curr_image);
   3960     (void) FormatLocaleFile(stderr, "      work =0x%lx\n", (unsigned long)work_image);
   3961     (void) FormatLocaleFile(stderr, "      save =0x%lx\n", (unsigned long)save_image);
   3962     (void) FormatLocaleFile(stderr, "      union=0x%lx\n", (unsigned long)rslt_image);
   3963 #endif
   3964 
   3965       } /* End Loop 3: Primative (staging) Loop for Coumpound Methods */
   3966 
   3967       /*  Final Post-processing for some Compound Methods
   3968       **
   3969       ** The removal of any 'Sync' channel flag in the Image Compositon
   3970       ** below ensures the methematical compose method is applied in a
   3971       ** purely mathematical way, and only to the selected channels.
   3972       ** Turn off SVG composition 'alpha blending'.
   3973       */
   3974       switch( method ) {
   3975         case EdgeOutMorphology:
   3976         case EdgeInMorphology:
   3977         case TopHatMorphology:
   3978         case BottomHatMorphology:
   3979           if (verbose != MagickFalse)
   3980             (void) FormatLocaleFile(stderr,
   3981               "\n%s: Difference with original image",CommandOptionToMnemonic(
   3982               MagickMorphologyOptions, method) );
   3983           (void) CompositeImage(curr_image,image,DifferenceCompositeOp,
   3984             MagickTrue,0,0,exception);
   3985           break;
   3986         case EdgeMorphology:
   3987           if (verbose != MagickFalse)
   3988             (void) FormatLocaleFile(stderr,
   3989               "\n%s: Difference of Dilate and Erode",CommandOptionToMnemonic(
   3990               MagickMorphologyOptions, method) );
   3991           (void) CompositeImage(curr_image,save_image,DifferenceCompositeOp,
   3992             MagickTrue,0,0,exception);
   3993           save_image = DestroyImage(save_image); /* finished with save image */
   3994           break;
   3995         default:
   3996           break;
   3997       }
   3998 
   3999       /* multi-kernel handling:  re-iterate, or compose results */
   4000       if ( kernel->next == (KernelInfo *) NULL )
   4001         rslt_image = curr_image;   /* just return the resulting image */
   4002       else if ( rslt_compose == NoCompositeOp )
   4003         { if (verbose != MagickFalse) {
   4004             if ( this_kernel->next != (KernelInfo *) NULL )
   4005               (void) FormatLocaleFile(stderr, " (re-iterate)");
   4006             else
   4007               (void) FormatLocaleFile(stderr, " (done)");
   4008           }
   4009           rslt_image = curr_image; /* return result, and re-iterate */
   4010         }
   4011       else if ( rslt_image == (Image *) NULL)
   4012         { if (verbose != MagickFalse)
   4013             (void) FormatLocaleFile(stderr, " (save for compose)");
   4014           rslt_image = curr_image;
   4015           curr_image = (Image *) image;  /* continue with original image */
   4016         }
   4017       else
   4018         { /* Add the new 'current' result to the composition
   4019           **
   4020           ** The removal of any 'Sync' channel flag in the Image Compositon
   4021           ** below ensures the methematical compose method is applied in a
   4022           ** purely mathematical way, and only to the selected channels.
   4023           ** IE: Turn off SVG composition 'alpha blending'.
   4024           */
   4025           if (verbose != MagickFalse)
   4026             (void) FormatLocaleFile(stderr, " (compose \"%s\")",
   4027               CommandOptionToMnemonic(MagickComposeOptions, rslt_compose) );
   4028           (void) CompositeImage(rslt_image,curr_image,rslt_compose,MagickTrue,
   4029             0,0,exception);
   4030           curr_image = DestroyImage(curr_image);
   4031           curr_image = (Image *) image;  /* continue with original image */
   4032         }
   4033       if (verbose != MagickFalse)
   4034         (void) FormatLocaleFile(stderr, "\n");
   4035 
   4036       /* loop to the next kernel in a multi-kernel list */
   4037       norm_kernel = norm_kernel->next;
   4038       if ( rflt_kernel != (KernelInfo *) NULL )
   4039         rflt_kernel = rflt_kernel->next;
   4040       kernel_number++;
   4041     } /* End Loop 2: Loop over each kernel */
   4042 
   4043   } /* End Loop 1: compound method interation */
   4044 
   4045   goto exit_cleanup;
   4046 
   4047   /* Yes goto's are bad, but it makes cleanup lot more efficient */
   4048 error_cleanup:
   4049   if ( curr_image == rslt_image )
   4050     curr_image = (Image *) NULL;
   4051   if ( rslt_image != (Image *) NULL )
   4052     rslt_image = DestroyImage(rslt_image);
   4053 exit_cleanup:
   4054   if ( curr_image == rslt_image || curr_image == image )
   4055     curr_image = (Image *) NULL;
   4056   if ( curr_image != (Image *) NULL )
   4057     curr_image = DestroyImage(curr_image);
   4058   if ( work_image != (Image *) NULL )
   4059     work_image = DestroyImage(work_image);
   4060   if ( save_image != (Image *) NULL )
   4061     save_image = DestroyImage(save_image);
   4062   if ( reflected_kernel != (KernelInfo *) NULL )
   4063     reflected_kernel = DestroyKernelInfo(reflected_kernel);
   4064   return(rslt_image);
   4065 }
   4066 
   4067 
   4068 /*
   4070 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4071 %                                                                             %
   4072 %                                                                             %
   4073 %                                                                             %
   4074 %     M o r p h o l o g y I m a g e                                           %
   4075 %                                                                             %
   4076 %                                                                             %
   4077 %                                                                             %
   4078 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4079 %
   4080 %  MorphologyImage() applies a user supplied kernel to the image according to
   4081 %  the given mophology method.
   4082 %
   4083 %  This function applies any and all user defined settings before calling
   4084 %  the above internal function MorphologyApply().
   4085 %
   4086 %  User defined settings include...
   4087 %    * Output Bias for Convolution and correlation ("-define convolve:bias=??")
   4088 %    * Kernel Scale/normalize settings            ("-define convolve:scale=??")
   4089 %      This can also includes the addition of a scaled unity kernel.
   4090 %    * Show Kernel being applied            ("-define morphology:showkernel=1")
   4091 %
   4092 %  Other operators that do not want user supplied options interfering,
   4093 %  especially "convolve:bias" and "morphology:showkernel" should use
   4094 %  MorphologyApply() directly.
   4095 %
   4096 %  The format of the MorphologyImage method is:
   4097 %
   4098 %      Image *MorphologyImage(const Image *image,MorphologyMethod method,
   4099 %        const ssize_t iterations,KernelInfo *kernel,ExceptionInfo *exception)
   4100 %
   4101 %  A description of each parameter follows:
   4102 %
   4103 %    o image: the image.
   4104 %
   4105 %    o method: the morphology method to be applied.
   4106 %
   4107 %    o iterations: apply the operation this many times (or no change).
   4108 %                  A value of -1 means loop until no change found.
   4109 %                  How this is applied may depend on the morphology method.
   4110 %                  Typically this is a value of 1.
   4111 %
   4112 %    o kernel: An array of double representing the morphology kernel.
   4113 %              Warning: kernel may be normalized for the Convolve method.
   4114 %
   4115 %    o exception: return any errors or warnings in this structure.
   4116 %
   4117 */
   4118 MagickExport Image *MorphologyImage(const Image *image,
   4119   const MorphologyMethod method,const ssize_t iterations,
   4120   const KernelInfo *kernel,ExceptionInfo *exception)
   4121 {
   4122   const char
   4123     *artifact;
   4124 
   4125   CompositeOperator
   4126     compose;
   4127 
   4128   double
   4129     bias;
   4130 
   4131   Image
   4132     *morphology_image;
   4133 
   4134   KernelInfo
   4135     *curr_kernel;
   4136 
   4137   curr_kernel = (KernelInfo *) kernel;
   4138   bias=0.0;
   4139   compose = UndefinedCompositeOp;  /* use default for method */
   4140 
   4141   /* Apply Convolve/Correlate Normalization and Scaling Factors.
   4142    * This is done BEFORE the ShowKernelInfo() function is called so that
   4143    * users can see the results of the 'option:convolve:scale' option.
   4144    */
   4145   if ( method == ConvolveMorphology || method == CorrelateMorphology ) {
   4146       /* Get the bias value as it will be needed */
   4147       artifact = GetImageArtifact(image,"convolve:bias");
   4148       if ( artifact != (const char *) NULL) {
   4149         if (IsGeometry(artifact) == MagickFalse)
   4150           (void) ThrowMagickException(exception,GetMagickModule(),
   4151                OptionWarning,"InvalidSetting","'%s' '%s'",
   4152                "convolve:bias",artifact);
   4153         else
   4154           bias=StringToDoubleInterval(artifact,(double) QuantumRange+1.0);
   4155       }
   4156 
   4157       /* Scale kernel according to user wishes */
   4158       artifact = GetImageArtifact(image,"convolve:scale");
   4159       if ( artifact != (const char *) NULL ) {
   4160         if (IsGeometry(artifact) == MagickFalse)
   4161           (void) ThrowMagickException(exception,GetMagickModule(),
   4162                OptionWarning,"InvalidSetting","'%s' '%s'",
   4163                "convolve:scale",artifact);
   4164         else {
   4165           if ( curr_kernel == kernel )
   4166             curr_kernel = CloneKernelInfo(kernel);
   4167           if (curr_kernel == (KernelInfo *) NULL)
   4168             return((Image *) NULL);
   4169           ScaleGeometryKernelInfo(curr_kernel, artifact);
   4170         }
   4171       }
   4172     }
   4173 
   4174   /* display the (normalized) kernel via stderr */
   4175   artifact=GetImageArtifact(image,"morphology:showkernel");
   4176   if (IsStringTrue(artifact) != MagickFalse)
   4177     ShowKernelInfo(curr_kernel);
   4178 
   4179   /* Override the default handling of multi-kernel morphology results
   4180    * If 'Undefined' use the default method
   4181    * If 'None' (default for 'Convolve') re-iterate previous result
   4182    * Otherwise merge resulting images using compose method given.
   4183    * Default for 'HitAndMiss' is 'Lighten'.
   4184    */
   4185   {
   4186     ssize_t
   4187       parse;
   4188 
   4189     artifact = GetImageArtifact(image,"morphology:compose");
   4190     if ( artifact != (const char *) NULL) {
   4191       parse=ParseCommandOption(MagickComposeOptions,
   4192         MagickFalse,artifact);
   4193       if ( parse < 0 )
   4194         (void) ThrowMagickException(exception,GetMagickModule(),
   4195              OptionWarning,"UnrecognizedComposeOperator","'%s' '%s'",
   4196              "morphology:compose",artifact);
   4197       else
   4198         compose=(CompositeOperator)parse;
   4199     }
   4200   }
   4201   /* Apply the Morphology */
   4202   morphology_image = MorphologyApply(image,method,iterations,
   4203     curr_kernel,compose,bias,exception);
   4204 
   4205   /* Cleanup and Exit */
   4206   if ( curr_kernel != kernel )
   4207     curr_kernel=DestroyKernelInfo(curr_kernel);
   4208   return(morphology_image);
   4209 }
   4210 
   4211 /*
   4213 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4214 %                                                                             %
   4215 %                                                                             %
   4216 %                                                                             %
   4217 +     R o t a t e K e r n e l I n f o                                         %
   4218 %                                                                             %
   4219 %                                                                             %
   4220 %                                                                             %
   4221 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4222 %
   4223 %  RotateKernelInfo() rotates the kernel by the angle given.
   4224 %
   4225 %  Currently it is restricted to 90 degree angles, of either 1D kernels
   4226 %  or square kernels. And 'circular' rotations of 45 degrees for 3x3 kernels.
   4227 %  It will ignore usless rotations for specific 'named' built-in kernels.
   4228 %
   4229 %  The format of the RotateKernelInfo method is:
   4230 %
   4231 %      void RotateKernelInfo(KernelInfo *kernel, double angle)
   4232 %
   4233 %  A description of each parameter follows:
   4234 %
   4235 %    o kernel: the Morphology/Convolution kernel
   4236 %
   4237 %    o angle: angle to rotate in degrees
   4238 %
   4239 % This function is currently internal to this module only, but can be exported
   4240 % to other modules if needed.
   4241 */
   4242 static void RotateKernelInfo(KernelInfo *kernel, double angle)
   4243 {
   4244   /* angle the lower kernels first */
   4245   if ( kernel->next != (KernelInfo *) NULL)
   4246     RotateKernelInfo(kernel->next, angle);
   4247 
   4248   /* WARNING: Currently assumes the kernel (rightly) is horizontally symetrical
   4249   **
   4250   ** TODO: expand beyond simple 90 degree rotates, flips and flops
   4251   */
   4252 
   4253   /* Modulus the angle */
   4254   angle = fmod(angle, 360.0);
   4255   if ( angle < 0 )
   4256     angle += 360.0;
   4257 
   4258   if ( 337.5 < angle || angle <= 22.5 )
   4259     return;   /* Near zero angle - no change! - At least not at this time */
   4260 
   4261   /* Handle special cases */
   4262   switch (kernel->type) {
   4263     /* These built-in kernels are cylindrical kernels, rotating is useless */
   4264     case GaussianKernel:
   4265     case DoGKernel:
   4266     case LoGKernel:
   4267     case DiskKernel:
   4268     case PeaksKernel:
   4269     case LaplacianKernel:
   4270     case ChebyshevKernel:
   4271     case ManhattanKernel:
   4272     case EuclideanKernel:
   4273       return;
   4274 
   4275     /* These may be rotatable at non-90 angles in the future */
   4276     /* but simply rotating them in multiples of 90 degrees is useless */
   4277     case SquareKernel:
   4278     case DiamondKernel:
   4279     case PlusKernel:
   4280     case CrossKernel:
   4281       return;
   4282 
   4283     /* These only allows a +/-90 degree rotation (by transpose) */
   4284     /* A 180 degree rotation is useless */
   4285     case BlurKernel:
   4286       if ( 135.0 < angle && angle <= 225.0 )
   4287         return;
   4288       if ( 225.0 < angle && angle <= 315.0 )
   4289         angle -= 180;
   4290       break;
   4291 
   4292     default:
   4293       break;
   4294   }
   4295   /* Attempt rotations by 45 degrees  -- 3x3 kernels only */
   4296   if ( 22.5 < fmod(angle,90.0) && fmod(angle,90.0) <= 67.5 )
   4297     {
   4298       if ( kernel->width == 3 && kernel->height == 3 )
   4299         { /* Rotate a 3x3 square by 45 degree angle */
   4300           double t  = kernel->values[0];
   4301           kernel->values[0] = kernel->values[3];
   4302           kernel->values[3] = kernel->values[6];
   4303           kernel->values[6] = kernel->values[7];
   4304           kernel->values[7] = kernel->values[8];
   4305           kernel->values[8] = kernel->values[5];
   4306           kernel->values[5] = kernel->values[2];
   4307           kernel->values[2] = kernel->values[1];
   4308           kernel->values[1] = t;
   4309           /* rotate non-centered origin */
   4310           if ( kernel->x != 1 || kernel->y != 1 ) {
   4311             ssize_t x,y;
   4312             x = (ssize_t) kernel->x-1;
   4313             y = (ssize_t) kernel->y-1;
   4314                  if ( x == y  ) x = 0;
   4315             else if ( x == 0  ) x = -y;
   4316             else if ( x == -y ) y = 0;
   4317             else if ( y == 0  ) y = x;
   4318             kernel->x = (ssize_t) x+1;
   4319             kernel->y = (ssize_t) y+1;
   4320           }
   4321           angle = fmod(angle+315.0, 360.0);  /* angle reduced 45 degrees */
   4322           kernel->angle = fmod(kernel->angle+45.0, 360.0);
   4323         }
   4324       else
   4325         perror("Unable to rotate non-3x3 kernel by 45 degrees");
   4326     }
   4327   if ( 45.0 < fmod(angle, 180.0)  && fmod(angle,180.0) <= 135.0 )
   4328     {
   4329       if ( kernel->width == 1 || kernel->height == 1 )
   4330         { /* Do a transpose of a 1 dimensional kernel,
   4331           ** which results in a fast 90 degree rotation of some type.
   4332           */
   4333           ssize_t
   4334             t;
   4335           t = (ssize_t) kernel->width;
   4336           kernel->width = kernel->height;
   4337           kernel->height = (size_t) t;
   4338           t = kernel->x;
   4339           kernel->x = kernel->y;
   4340           kernel->y = t;
   4341           if ( kernel->width == 1 ) {
   4342             angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
   4343             kernel->angle = fmod(kernel->angle+90.0, 360.0);
   4344           } else {
   4345             angle = fmod(angle+90.0, 360.0);   /* angle increased 90 degrees */
   4346             kernel->angle = fmod(kernel->angle+270.0, 360.0);
   4347           }
   4348         }
   4349       else if ( kernel->width == kernel->height )
   4350         { /* Rotate a square array of values by 90 degrees */
   4351           { register ssize_t
   4352               i,j,x,y;
   4353 
   4354             register MagickRealType
   4355               *k,t;
   4356 
   4357             k=kernel->values;
   4358             for( i=0, x=(ssize_t) kernel->width-1;  i<=x;   i++, x--)
   4359               for( j=0, y=(ssize_t) kernel->height-1;  j<y;   j++, y--)
   4360                 { t                    = k[i+j*kernel->width];
   4361                   k[i+j*kernel->width] = k[j+x*kernel->width];
   4362                   k[j+x*kernel->width] = k[x+y*kernel->width];
   4363                   k[x+y*kernel->width] = k[y+i*kernel->width];
   4364                   k[y+i*kernel->width] = t;
   4365                 }
   4366           }
   4367           /* rotate the origin - relative to center of array */
   4368           { register ssize_t x,y;
   4369             x = (ssize_t) (kernel->x*2-kernel->width+1);
   4370             y = (ssize_t) (kernel->y*2-kernel->height+1);
   4371             kernel->x = (ssize_t) ( -y +(ssize_t) kernel->width-1)/2;
   4372             kernel->y = (ssize_t) ( +x +(ssize_t) kernel->height-1)/2;
   4373           }
   4374           angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
   4375           kernel->angle = fmod(kernel->angle+90.0, 360.0);
   4376         }
   4377       else
   4378         perror("Unable to rotate a non-square, non-linear kernel 90 degrees");
   4379     }
   4380   if ( 135.0 < angle && angle <= 225.0 )
   4381     {
   4382       /* For a 180 degree rotation - also know as a reflection
   4383        * This is actually a very very common operation!
   4384        * Basically all that is needed is a reversal of the kernel data!
   4385        * And a reflection of the origon
   4386        */
   4387       MagickRealType
   4388         t;
   4389 
   4390       register MagickRealType
   4391         *k;
   4392 
   4393       ssize_t
   4394         i,
   4395         j;
   4396 
   4397       k=kernel->values;
   4398       j=(ssize_t) (kernel->width*kernel->height-1);
   4399       for (i=0;  i < j;  i++, j--)
   4400         t=k[i],  k[i]=k[j],  k[j]=t;
   4401 
   4402       kernel->x = (ssize_t) kernel->width  - kernel->x - 1;
   4403       kernel->y = (ssize_t) kernel->height - kernel->y - 1;
   4404       angle = fmod(angle-180.0, 360.0);   /* angle+180 degrees */
   4405       kernel->angle = fmod(kernel->angle+180.0, 360.0);
   4406     }
   4407   /* At this point angle should at least between -45 (315) and +45 degrees
   4408    * In the future some form of non-orthogonal angled rotates could be
   4409    * performed here, posibily with a linear kernel restriction.
   4410    */
   4411 
   4412   return;
   4413 }
   4414 
   4415 /*
   4417 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4418 %                                                                             %
   4419 %                                                                             %
   4420 %                                                                             %
   4421 %     S c a l e G e o m e t r y K e r n e l I n f o                           %
   4422 %                                                                             %
   4423 %                                                                             %
   4424 %                                                                             %
   4425 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4426 %
   4427 %  ScaleGeometryKernelInfo() takes a geometry argument string, typically
   4428 %  provided as a  "-set option:convolve:scale {geometry}" user setting,
   4429 %  and modifies the kernel according to the parsed arguments of that setting.
   4430 %
   4431 %  The first argument (and any normalization flags) are passed to
   4432 %  ScaleKernelInfo() to scale/normalize the kernel.  The second argument
   4433 %  is then passed to UnityAddKernelInfo() to add a scled unity kernel
   4434 %  into the scaled/normalized kernel.
   4435 %
   4436 %  The format of the ScaleGeometryKernelInfo method is:
   4437 %
   4438 %      void ScaleGeometryKernelInfo(KernelInfo *kernel,
   4439 %        const double scaling_factor,const MagickStatusType normalize_flags)
   4440 %
   4441 %  A description of each parameter follows:
   4442 %
   4443 %    o kernel: the Morphology/Convolution kernel to modify
   4444 %
   4445 %    o geometry:
   4446 %             The geometry string to parse, typically from the user provided
   4447 %             "-set option:convolve:scale {geometry}" setting.
   4448 %
   4449 */
   4450 MagickExport void ScaleGeometryKernelInfo (KernelInfo *kernel,
   4451   const char *geometry)
   4452 {
   4453   MagickStatusType
   4454     flags;
   4455 
   4456   GeometryInfo
   4457     args;
   4458 
   4459   SetGeometryInfo(&args);
   4460   flags = ParseGeometry(geometry, &args);
   4461 
   4462 #if 0
   4463   /* For Debugging Geometry Input */
   4464   (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
   4465        flags, args.rho, args.sigma, args.xi, args.psi );
   4466 #endif
   4467 
   4468   if ( (flags & PercentValue) != 0 )      /* Handle Percentage flag*/
   4469     args.rho *= 0.01,  args.sigma *= 0.01;
   4470 
   4471   if ( (flags & RhoValue) == 0 )          /* Set Defaults for missing args */
   4472     args.rho = 1.0;
   4473   if ( (flags & SigmaValue) == 0 )
   4474     args.sigma = 0.0;
   4475 
   4476   /* Scale/Normalize the input kernel */
   4477   ScaleKernelInfo(kernel, args.rho, (GeometryFlags) flags);
   4478 
   4479   /* Add Unity Kernel, for blending with original */
   4480   if ( (flags & SigmaValue) != 0 )
   4481     UnityAddKernelInfo(kernel, args.sigma);
   4482 
   4483   return;
   4484 }
   4485 /*
   4486 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4487 %                                                                             %
   4488 %                                                                             %
   4489 %                                                                             %
   4490 %     S c a l e K e r n e l I n f o                                           %
   4491 %                                                                             %
   4492 %                                                                             %
   4493 %                                                                             %
   4494 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4495 %
   4496 %  ScaleKernelInfo() scales the given kernel list by the given amount, with or
   4497 %  without normalization of the sum of the kernel values (as per given flags).
   4498 %
   4499 %  By default (no flags given) the values within the kernel is scaled
   4500 %  directly using given scaling factor without change.
   4501 %
   4502 %  If either of the two 'normalize_flags' are given the kernel will first be
   4503 %  normalized and then further scaled by the scaling factor value given.
   4504 %
   4505 %  Kernel normalization ('normalize_flags' given) is designed to ensure that
   4506 %  any use of the kernel scaling factor with 'Convolve' or 'Correlate'
   4507 %  morphology methods will fall into -1.0 to +1.0 range.  Note that for
   4508 %  non-HDRI versions of IM this may cause images to have any negative results
   4509 %  clipped, unless some 'bias' is used.
   4510 %
   4511 %  More specifically.  Kernels which only contain positive values (such as a
   4512 %  'Gaussian' kernel) will be scaled so that those values sum to +1.0,
   4513 %  ensuring a 0.0 to +1.0 output range for non-HDRI images.
   4514 %
   4515 %  For Kernels that contain some negative values, (such as 'Sharpen' kernels)
   4516 %  the kernel will be scaled by the absolute of the sum of kernel values, so
   4517 %  that it will generally fall within the +/- 1.0 range.
   4518 %
   4519 %  For kernels whose values sum to zero, (such as 'Laplician' kernels) kernel
   4520 %  will be scaled by just the sum of the postive values, so that its output
   4521 %  range will again fall into the  +/- 1.0 range.
   4522 %
   4523 %  For special kernels designed for locating shapes using 'Correlate', (often
   4524 %  only containing +1 and -1 values, representing foreground/brackground
   4525 %  matching) a special normalization method is provided to scale the positive
   4526 %  values separately to those of the negative values, so the kernel will be
   4527 %  forced to become a zero-sum kernel better suited to such searches.
   4528 %
   4529 %  WARNING: Correct normalization of the kernel assumes that the '*_range'
   4530 %  attributes within the kernel structure have been correctly set during the
   4531 %  kernels creation.
   4532 %
   4533 %  NOTE: The values used for 'normalize_flags' have been selected specifically
   4534 %  to match the use of geometry options, so that '!' means NormalizeValue, '^'
   4535 %  means CorrelateNormalizeValue.  All other GeometryFlags values are ignored.
   4536 %
   4537 %  The format of the ScaleKernelInfo method is:
   4538 %
   4539 %      void ScaleKernelInfo(KernelInfo *kernel, const double scaling_factor,
   4540 %               const MagickStatusType normalize_flags )
   4541 %
   4542 %  A description of each parameter follows:
   4543 %
   4544 %    o kernel: the Morphology/Convolution kernel
   4545 %
   4546 %    o scaling_factor:
   4547 %             multiply all values (after normalization) by this factor if not
   4548 %             zero.  If the kernel is normalized regardless of any flags.
   4549 %
   4550 %    o normalize_flags:
   4551 %             GeometryFlags defining normalization method to use.
   4552 %             specifically: NormalizeValue, CorrelateNormalizeValue,
   4553 %                           and/or PercentValue
   4554 %
   4555 */
   4556 MagickExport void ScaleKernelInfo(KernelInfo *kernel,
   4557   const double scaling_factor,const GeometryFlags normalize_flags)
   4558 {
   4559   register double
   4560     pos_scale,
   4561     neg_scale;
   4562 
   4563   register ssize_t
   4564     i;
   4565 
   4566   /* do the other kernels in a multi-kernel list first */
   4567   if ( kernel->next != (KernelInfo *) NULL)
   4568     ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
   4569 
   4570   /* Normalization of Kernel */
   4571   pos_scale = 1.0;
   4572   if ( (normalize_flags&NormalizeValue) != 0 ) {
   4573     if ( fabs(kernel->positive_range + kernel->negative_range) >= MagickEpsilon )
   4574       /* non-zero-summing kernel (generally positive) */
   4575       pos_scale = fabs(kernel->positive_range + kernel->negative_range);
   4576     else
   4577       /* zero-summing kernel */
   4578       pos_scale = kernel->positive_range;
   4579   }
   4580   /* Force kernel into a normalized zero-summing kernel */
   4581   if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
   4582     pos_scale = ( fabs(kernel->positive_range) >= MagickEpsilon )
   4583                  ? kernel->positive_range : 1.0;
   4584     neg_scale = ( fabs(kernel->negative_range) >= MagickEpsilon )
   4585                  ? -kernel->negative_range : 1.0;
   4586   }
   4587   else
   4588     neg_scale = pos_scale;
   4589 
   4590   /* finialize scaling_factor for positive and negative components */
   4591   pos_scale = scaling_factor/pos_scale;
   4592   neg_scale = scaling_factor/neg_scale;
   4593 
   4594   for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
   4595     if (!IsNaN(kernel->values[i]))
   4596       kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
   4597 
   4598   /* convolution output range */
   4599   kernel->positive_range *= pos_scale;
   4600   kernel->negative_range *= neg_scale;
   4601   /* maximum and minimum values in kernel */
   4602   kernel->maximum *= (kernel->maximum >= 0.0) ? pos_scale : neg_scale;
   4603   kernel->minimum *= (kernel->minimum >= 0.0) ? pos_scale : neg_scale;
   4604 
   4605   /* swap kernel settings if user's scaling factor is negative */
   4606   if ( scaling_factor < MagickEpsilon ) {
   4607     double t;
   4608     t = kernel->positive_range;
   4609     kernel->positive_range = kernel->negative_range;
   4610     kernel->negative_range = t;
   4611     t = kernel->maximum;
   4612     kernel->maximum = kernel->minimum;
   4613     kernel->minimum = 1;
   4614   }
   4615 
   4616   return;
   4617 }
   4618 
   4619 /*
   4621 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4622 %                                                                             %
   4623 %                                                                             %
   4624 %                                                                             %
   4625 %     S h o w K e r n e l I n f o                                             %
   4626 %                                                                             %
   4627 %                                                                             %
   4628 %                                                                             %
   4629 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4630 %
   4631 %  ShowKernelInfo() outputs the details of the given kernel defination to
   4632 %  standard error, generally due to a users 'morphology:showkernel' option
   4633 %  request.
   4634 %
   4635 %  The format of the ShowKernel method is:
   4636 %
   4637 %      void ShowKernelInfo(const KernelInfo *kernel)
   4638 %
   4639 %  A description of each parameter follows:
   4640 %
   4641 %    o kernel: the Morphology/Convolution kernel
   4642 %
   4643 */
   4644 MagickPrivate void ShowKernelInfo(const KernelInfo *kernel)
   4645 {
   4646   const KernelInfo
   4647     *k;
   4648 
   4649   size_t
   4650     c, i, u, v;
   4651 
   4652   for (c=0, k=kernel;  k != (KernelInfo *) NULL;  c++, k=k->next ) {
   4653 
   4654     (void) FormatLocaleFile(stderr, "Kernel");
   4655     if ( kernel->next != (KernelInfo *) NULL )
   4656       (void) FormatLocaleFile(stderr, " #%lu", (unsigned long) c );
   4657     (void) FormatLocaleFile(stderr, " \"%s",
   4658           CommandOptionToMnemonic(MagickKernelOptions, k->type) );
   4659     if ( fabs(k->angle) >= MagickEpsilon )
   4660       (void) FormatLocaleFile(stderr, "@%lg", k->angle);
   4661     (void) FormatLocaleFile(stderr, "\" of size %lux%lu%+ld%+ld",(unsigned long)
   4662       k->width,(unsigned long) k->height,(long) k->x,(long) k->y);
   4663     (void) FormatLocaleFile(stderr,
   4664           " with values from %.*lg to %.*lg\n",
   4665           GetMagickPrecision(), k->minimum,
   4666           GetMagickPrecision(), k->maximum);
   4667     (void) FormatLocaleFile(stderr, "Forming a output range from %.*lg to %.*lg",
   4668           GetMagickPrecision(), k->negative_range,
   4669           GetMagickPrecision(), k->positive_range);
   4670     if ( fabs(k->positive_range+k->negative_range) < MagickEpsilon )
   4671       (void) FormatLocaleFile(stderr, " (Zero-Summing)\n");
   4672     else if ( fabs(k->positive_range+k->negative_range-1.0) < MagickEpsilon )
   4673       (void) FormatLocaleFile(stderr, " (Normalized)\n");
   4674     else
   4675       (void) FormatLocaleFile(stderr, " (Sum %.*lg)\n",
   4676           GetMagickPrecision(), k->positive_range+k->negative_range);
   4677     for (i=v=0; v < k->height; v++) {
   4678       (void) FormatLocaleFile(stderr, "%2lu:", (unsigned long) v );
   4679       for (u=0; u < k->width; u++, i++)
   4680         if (IsNaN(k->values[i]))
   4681           (void) FormatLocaleFile(stderr," %*s", GetMagickPrecision()+3, "nan");
   4682         else
   4683           (void) FormatLocaleFile(stderr," %*.*lg", GetMagickPrecision()+3,
   4684               GetMagickPrecision(), (double) k->values[i]);
   4685       (void) FormatLocaleFile(stderr,"\n");
   4686     }
   4687   }
   4688 }
   4689 
   4690 /*
   4692 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4693 %                                                                             %
   4694 %                                                                             %
   4695 %                                                                             %
   4696 %     U n i t y A d d K e r n a l I n f o                                     %
   4697 %                                                                             %
   4698 %                                                                             %
   4699 %                                                                             %
   4700 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4701 %
   4702 %  UnityAddKernelInfo() Adds a given amount of the 'Unity' Convolution Kernel
   4703 %  to the given pre-scaled and normalized Kernel.  This in effect adds that
   4704 %  amount of the original image into the resulting convolution kernel.  This
   4705 %  value is usually provided by the user as a percentage value in the
   4706 %  'convolve:scale' setting.
   4707 %
   4708 %  The resulting effect is to convert the defined kernels into blended
   4709 %  soft-blurs, unsharp kernels or into sharpening kernels.
   4710 %
   4711 %  The format of the UnityAdditionKernelInfo method is:
   4712 %
   4713 %      void UnityAdditionKernelInfo(KernelInfo *kernel, const double scale )
   4714 %
   4715 %  A description of each parameter follows:
   4716 %
   4717 %    o kernel: the Morphology/Convolution kernel
   4718 %
   4719 %    o scale:
   4720 %             scaling factor for the unity kernel to be added to
   4721 %             the given kernel.
   4722 %
   4723 */
   4724 MagickExport void UnityAddKernelInfo(KernelInfo *kernel,
   4725   const double scale)
   4726 {
   4727   /* do the other kernels in a multi-kernel list first */
   4728   if ( kernel->next != (KernelInfo *) NULL)
   4729     UnityAddKernelInfo(kernel->next, scale);
   4730 
   4731   /* Add the scaled unity kernel to the existing kernel */
   4732   kernel->values[kernel->x+kernel->y*kernel->width] += scale;
   4733   CalcKernelMetaData(kernel);  /* recalculate the meta-data */
   4734 
   4735   return;
   4736 }
   4737 
   4738 /*
   4740 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4741 %                                                                             %
   4742 %                                                                             %
   4743 %                                                                             %
   4744 %     Z e r o K e r n e l N a n s                                             %
   4745 %                                                                             %
   4746 %                                                                             %
   4747 %                                                                             %
   4748 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   4749 %
   4750 %  ZeroKernelNans() replaces any special 'nan' value that may be present in
   4751 %  the kernel with a zero value.  This is typically done when the kernel will
   4752 %  be used in special hardware (GPU) convolution processors, to simply
   4753 %  matters.
   4754 %
   4755 %  The format of the ZeroKernelNans method is:
   4756 %
   4757 %      void ZeroKernelNans (KernelInfo *kernel)
   4758 %
   4759 %  A description of each parameter follows:
   4760 %
   4761 %    o kernel: the Morphology/Convolution kernel
   4762 %
   4763 */
   4764 MagickPrivate void ZeroKernelNans(KernelInfo *kernel)
   4765 {
   4766   register size_t
   4767     i;
   4768 
   4769   /* do the other kernels in a multi-kernel list first */
   4770   if (kernel->next != (KernelInfo *) NULL)
   4771     ZeroKernelNans(kernel->next);
   4772 
   4773   for (i=0; i < (kernel->width*kernel->height); i++)
   4774     if (IsNaN(kernel->values[i]))
   4775       kernel->values[i]=0.0;
   4776 
   4777   return;
   4778 }
   4779