morphology.c revision 5e6b259130f9dbe0da4666f734937017babe573a
1701db3105315e7d7d9cf2734ae94524c6bc38e80cristy/*
2701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
4701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
5701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
6701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%    M   M    OOO    RRRR   PPPP   H   H   OOO   L       OOO    GGGG  Y   Y   %
7701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%    MM MM   O   O   R   R  P   P  H   H  O   O  L      O   O  G       Y Y    %
8701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%    M M M   O   O   RRRR   PPPP   HHHHH  O   O  L      O   O  G GGG    Y     %
9701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%    M   M   O   O   R R    P      H   H  O   O  L      O   O  G   G    Y     %
10701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%    M   M    OOO    R  R   P      H   H   OOO   LLLLL   OOO    GGG     Y     %
11701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
12701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
13701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                        MagickCore Morphology Methods                        %
14701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
15701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                              Software Design                                %
16701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                              Anthony Thyssen                                %
17c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                               January 2010                                  %
18701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
19701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
2045ef08fd6a09813e4a8f5ddadf85ba9e0ec2cdc7cristy%  Copyright 1999-2013 ImageMagick Studio LLC, a non-profit organization      %
21701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%  dedicated to making software imaging solutions freely available.           %
22701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
23701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%  You may not use this file except in compliance with the License.  You may  %
24701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%  obtain a copy of the License at                                            %
25701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
26701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%    http://www.imagemagick.org/script/license.php                            %
27701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
28701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%  Unless required by applicable law or agreed to in writing, software        %
29701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%  distributed under the License is distributed on an "AS IS" BASIS,          %
30701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   %
31701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%  See the License for the specific language governing permissions and        %
32701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%  limitations under the License.                                             %
33701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%                                                                             %
34701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%
361b2bc0a7da432e6e1cc0480280402df213faa940anthony% Morpology is the the application of various kernels, of any size and even
37602ab9b30b644a78a4057da93d838a77391ec0acanthony% shape, to a image in various ways (typically binary, but not always).
38701db3105315e7d7d9cf2734ae94524c6bc38e80cristy%
39602ab9b30b644a78a4057da93d838a77391ec0acanthony% Convolution (weighted sum or average) is just one specific type of
40602ab9b30b644a78a4057da93d838a77391ec0acanthony% morphology. Just one that is very common for image bluring and sharpening
41602ab9b30b644a78a4057da93d838a77391ec0acanthony% effects.  Not only 2D Gaussian blurring, but also 2-pass 1D Blurring.
42602ab9b30b644a78a4057da93d838a77391ec0acanthony%
43602ab9b30b644a78a4057da93d838a77391ec0acanthony% This module provides not only a general morphology function, and the ability
44602ab9b30b644a78a4057da93d838a77391ec0acanthony% to apply more advanced or iterative morphologies, but also functions for the
45602ab9b30b644a78a4057da93d838a77391ec0acanthony% generation of many different types of kernel arrays from user supplied
46602ab9b30b644a78a4057da93d838a77391ec0acanthony% arguments. Prehaps even the generation of a kernel from a small image.
47701db3105315e7d7d9cf2734ae94524c6bc38e80cristy*/
48701db3105315e7d7d9cf2734ae94524c6bc38e80cristy
49701db3105315e7d7d9cf2734ae94524c6bc38e80cristy/*
50701db3105315e7d7d9cf2734ae94524c6bc38e80cristy  Include declarations.
51701db3105315e7d7d9cf2734ae94524c6bc38e80cristy*/
524c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/studio.h"
534c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/artifact.h"
544c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/cache-view.h"
554c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/color-private.h"
564c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/enhance.h"
574c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/exception.h"
584c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/exception-private.h"
594c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/gem.h"
608ea81224e9ff022e56eb2cddb12860a8b2e90411cristy#include "MagickCore/gem-private.h"
614c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/hashmap.h"
624c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/image.h"
634c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/image-private.h"
644c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/list.h"
654c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/magick.h"
664c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/memory_.h"
67e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy#include "MagickCore/memory-private.h"
684c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/monitor-private.h"
694c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/morphology.h"
704c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/morphology-private.h"
714c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/option.h"
724c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/pixel-accessor.h"
734c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/prepress.h"
744c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/quantize.h"
75ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy#include "MagickCore/resource_.h"
764c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/registry.h"
774c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/semaphore.h"
784c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/splay-tree.h"
794c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/statistic.h"
804c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/string_.h"
814c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/string-private.h"
8216881e68c6165c6191fc44151a8a4320e3dd1ffdcristy#include "MagickCore/thread-private.h"
834c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/token.h"
844c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/utility.h"
85d1dd6e4fefa0810b9893e6ac9418f79c97c1b39acristy#include "MagickCore/utility-private.h"
86a29d45f897949f04a47bb3da077395969f13dcbacristy
87c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony
88c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony/*
89c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** The following test is for special floating point numbers of value NaN (not
90c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** a number), that may be used within a Kernel Definition.  NaN's are defined
91c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** as part of the IEEE standard for floating point number representation.
92c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony**
93c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** These are used as a Kernel value to mean that this kernel position is not
94c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** part of the kernel neighbourhood for convolution or morphology processing,
95c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** and thus should be ignored.  This allows the use of 'shaped' kernels.
96c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony**
9797fc9f3f47bd7bb92d17366594e1b77c9072660ccristy** The special property that two NaN's are never equal, even if they are from
98c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** the same variable allow you to test if a value is special NaN value.
99c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony**
100c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony** This macro  IsNaN() is thus is only true if the value given is NaN.
101a29d45f897949f04a47bb3da077395969f13dcbacristy*/
102602ab9b30b644a78a4057da93d838a77391ec0acanthony#define IsNan(a)   ((a)!=(a))
103602ab9b30b644a78a4057da93d838a77391ec0acanthony
10429188a8682a98d4b7882cca434b170517555fc7danthony/*
105a29d45f897949f04a47bb3da077395969f13dcbacristy  Other global definitions used by module.
106a29d45f897949f04a47bb3da077395969f13dcbacristy*/
10729188a8682a98d4b7882cca434b170517555fc7danthonystatic inline double MagickMin(const double x,const double y)
10829188a8682a98d4b7882cca434b170517555fc7danthony{
10929188a8682a98d4b7882cca434b170517555fc7danthony  return( x < y ? x : y);
11029188a8682a98d4b7882cca434b170517555fc7danthony}
11129188a8682a98d4b7882cca434b170517555fc7danthonystatic inline double MagickMax(const double x,const double y)
11229188a8682a98d4b7882cca434b170517555fc7danthony{
11329188a8682a98d4b7882cca434b170517555fc7danthony  return( x > y ? x : y);
11429188a8682a98d4b7882cca434b170517555fc7danthony}
11529188a8682a98d4b7882cca434b170517555fc7danthony#define Minimize(assign,value) assign=MagickMin(assign,value)
11629188a8682a98d4b7882cca434b170517555fc7danthony#define Maximize(assign,value) assign=MagickMax(assign,value)
11729188a8682a98d4b7882cca434b170517555fc7danthony
11840ca0b982379d4ab2716435a46603d56b5b218b1anthony/* Integer Factorial Function - for a Binomial kernel */
11940ca0b982379d4ab2716435a46603d56b5b218b1anthony#if 1
12040ca0b982379d4ab2716435a46603d56b5b218b1anthonystatic inline size_t fact(size_t n)
12140ca0b982379d4ab2716435a46603d56b5b218b1anthony{
122f13c594b1d9509e479fd845c3b8a5bb1351c32eacristy  size_t f,l;
12340ca0b982379d4ab2716435a46603d56b5b218b1anthony  for(f=1, l=2; l <= n; f=f*l, l++);
12440ca0b982379d4ab2716435a46603d56b5b218b1anthony  return(f);
12540ca0b982379d4ab2716435a46603d56b5b218b1anthony}
12640ca0b982379d4ab2716435a46603d56b5b218b1anthony#elif 1 /* glibc floating point alternatives */
12740ca0b982379d4ab2716435a46603d56b5b218b1anthony#define fact(n) ((size_t)tgamma((double)n+1))
12840ca0b982379d4ab2716435a46603d56b5b218b1anthony#else
12940ca0b982379d4ab2716435a46603d56b5b218b1anthony#define fact(n) ((size_t)lgamma((double)n+1))
13040ca0b982379d4ab2716435a46603d56b5b218b1anthony#endif
13140ca0b982379d4ab2716435a46603d56b5b218b1anthony
13240ca0b982379d4ab2716435a46603d56b5b218b1anthony
133c4c86e0ad78bebf6b9f931a659fb82987a7042e3anthony/* Currently these are only internal to this module */
134c4c86e0ad78bebf6b9f931a659fb82987a7042e3anthonystatic void
13546a369d839971ab627bdb31a93d8bd63e81b65a3anthony  CalcKernelMetaData(KernelInfo *),
136bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  ExpandMirrorKernelInfo(KernelInfo *),
137bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  ExpandRotateKernelInfo(KernelInfo *, const double),
138ef656913b0b30d713ae94c82c47693c9dc69c9f4cristy  RotateKernelInfo(KernelInfo *, double);
139602ab9b30b644a78a4057da93d838a77391ec0acanthony
1403dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony
1413dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony/* Quick function to find last kernel in a kernel list */
1423dd0f620e7a1d12f747ce167844cd7269bfa9f12anthonystatic inline KernelInfo *LastKernelInfo(KernelInfo *kernel)
1433dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony{
1443dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  while (kernel->next != (KernelInfo *) NULL)
1453dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    kernel = kernel->next;
1463dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  return(kernel);
1473dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony}
1483dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony
149602ab9b30b644a78a4057da93d838a77391ec0acanthony/*
150602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
151602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
152602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
153602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
15483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%     A c q u i r e K e r n e l I n f o                                       %
155602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
156602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
157602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
158602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
159602ab9b30b644a78a4057da93d838a77391ec0acanthony%
1602be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%  AcquireKernelInfo() takes the given string (generally supplied by the
161602ab9b30b644a78a4057da93d838a77391ec0acanthony%  user) and converts it into a Morphology/Convolution Kernel.  This allows
162602ab9b30b644a78a4057da93d838a77391ec0acanthony%  users to specify a kernel from a number of pre-defined kernels, or to fully
163602ab9b30b644a78a4057da93d838a77391ec0acanthony%  specify their own kernel for a specific Convolution or Morphology
164602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Operation.
165602ab9b30b644a78a4057da93d838a77391ec0acanthony%
166602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The kernel so generated can be any rectangular array of floating point
167602ab9b30b644a78a4057da93d838a77391ec0acanthony%  values (doubles) with the 'control point' or 'pixel being affected'
168602ab9b30b644a78a4057da93d838a77391ec0acanthony%  anywhere within that array of values.
169602ab9b30b644a78a4057da93d838a77391ec0acanthony%
17083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  Previously IM was restricted to a square of odd size using the exact
17119910ef25dd3d99d1981a9e42c934133170ee714anthony%  center as origin, this is no longer the case, and any rectangular kernel
17283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  with any value being declared the origin. This in turn allows the use of
17383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  highly asymmetrical kernels.
174602ab9b30b644a78a4057da93d838a77391ec0acanthony%
175602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The floating point values in the kernel can also include a special value
17683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  known as 'nan' or 'not a number' to indicate that this value is not part
17783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  of the kernel array. This allows you to shaped the kernel within its
17883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  rectangular area. That is 'nan' values provide a 'mask' for the kernel
17983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  shape.  However at least one non-nan value must be provided for correct
18083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  working of a kernel.
181602ab9b30b644a78a4057da93d838a77391ec0acanthony%
1827a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  The returned kernel should be freed using the DestroyKernelInfo() when you
1837a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  are finished with it.  Do not free this memory yourself.
184602ab9b30b644a78a4057da93d838a77391ec0acanthony%
185602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Input kernel defintion strings can consist of any of three types.
186602ab9b30b644a78a4057da93d838a77391ec0acanthony%
187bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    "name:args[[@><]"
18829188a8682a98d4b7882cca434b170517555fc7danthony%         Select from one of the built in kernels, using the name and
18929188a8682a98d4b7882cca434b170517555fc7danthony%         geometry arguments supplied.  See AcquireKernelBuiltIn()
190602ab9b30b644a78a4057da93d838a77391ec0acanthony%
191bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    "WxH[+X+Y][@><]:num, num, num ..."
1921b2bc0a7da432e6e1cc0480280402df213faa940anthony%         a kernel of size W by H, with W*H floating point numbers following.
193602ab9b30b644a78a4057da93d838a77391ec0acanthony%         the 'center' can be optionally be defined at +X+Y (such that +0+0
19429188a8682a98d4b7882cca434b170517555fc7danthony%         is top left corner). If not defined the pixel in the center, for
19529188a8682a98d4b7882cca434b170517555fc7danthony%         odd sizes, or to the immediate top or left of center for even sizes
19629188a8682a98d4b7882cca434b170517555fc7danthony%         is automatically selected.
197602ab9b30b644a78a4057da93d838a77391ec0acanthony%
19829188a8682a98d4b7882cca434b170517555fc7danthony%    "num, num, num, num, ..."
19929188a8682a98d4b7882cca434b170517555fc7danthony%         list of floating point numbers defining an 'old style' odd sized
20029188a8682a98d4b7882cca434b170517555fc7danthony%         square kernel.  At least 9 values should be provided for a 3x3
20129188a8682a98d4b7882cca434b170517555fc7danthony%         square kernel, 25 for a 5x5 square kernel, 49 for 7x7, etc.
20229188a8682a98d4b7882cca434b170517555fc7danthony%         Values can be space or comma separated.  This is not recommended.
203602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2047a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  You can define a 'list of kernels' which can be used by some morphology
2051e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp%  operators A list is defined as a semi-colon separated list kernels.
2067a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
207dbc8989a61339951c6434d9a43e7b6fefb5da374anthony%     " kernel ; kernel ; kernel ; "
2087a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
2091dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%  Any extra ';' characters, at start, end or between kernel defintions are
21043c4925e5305a26e48d68f7893e94f55d0831c39anthony%  simply ignored.
21143c4925e5305a26e48d68f7893e94f55d0831c39anthony%
212bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The special flags will expand a single kernel, into a list of rotated
213bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  kernels. A '@' flag will expand a 3x3 kernel into a list of 45-degree
214bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  cyclic rotations, while a '>' will generate a list of 90-degree rotations.
215bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The '<' also exands using 90-degree rotates, but giving a 180-degree
216bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  reflected kernel before the +/- 90-degree rotations, which can be important
217bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  for Thinning operations.
218bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
21943c4925e5305a26e48d68f7893e94f55d0831c39anthony%  Note that 'name' kernels will start with an alphabetic character while the
22043c4925e5305a26e48d68f7893e94f55d0831c39anthony%  new kernel specification has a ':' character in its specification string.
22143c4925e5305a26e48d68f7893e94f55d0831c39anthony%  If neither is the case, it is assumed an old style of a simple list of
22243c4925e5305a26e48d68f7893e94f55d0831c39anthony%  numbers generating a odd-sized square kernel has been given.
2237a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
224602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The format of the AcquireKernal method is:
225602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2262be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%      KernelInfo *AcquireKernelInfo(const char *kernel_string)
227602ab9b30b644a78a4057da93d838a77391ec0acanthony%
228602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
229602ab9b30b644a78a4057da93d838a77391ec0acanthony%
230602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel_string: the Morphology/Convolution kernel wanted.
231602ab9b30b644a78a4057da93d838a77391ec0acanthony%
232602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
233602ab9b30b644a78a4057da93d838a77391ec0acanthony
234c84dce50867229e4872193e8eed5dbab58eb9f02anthony/* This was separated so that it could be used as a separate
2355ef8e94ff55717be2387d537bd49025780a1a558anthony** array input handling function, such as for -color-matrix
236c84dce50867229e4872193e8eed5dbab58eb9f02anthony*/
2375ef8e94ff55717be2387d537bd49025780a1a558anthonystatic KernelInfo *ParseKernelArray(const char *kernel_string)
238602ab9b30b644a78a4057da93d838a77391ec0acanthony{
2392be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  KernelInfo
240602ab9b30b644a78a4057da93d838a77391ec0acanthony    *kernel;
241602ab9b30b644a78a4057da93d838a77391ec0acanthony
242602ab9b30b644a78a4057da93d838a77391ec0acanthony  char
243602ab9b30b644a78a4057da93d838a77391ec0acanthony    token[MaxTextExtent];
244602ab9b30b644a78a4057da93d838a77391ec0acanthony
245602ab9b30b644a78a4057da93d838a77391ec0acanthony  const char
2465ef8e94ff55717be2387d537bd49025780a1a558anthony    *p,
2475ef8e94ff55717be2387d537bd49025780a1a558anthony    *end;
248602ab9b30b644a78a4057da93d838a77391ec0acanthony
249bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
250c84dce50867229e4872193e8eed5dbab58eb9f02anthony    i;
251602ab9b30b644a78a4057da93d838a77391ec0acanthony
25229188a8682a98d4b7882cca434b170517555fc7danthony  double
25329188a8682a98d4b7882cca434b170517555fc7danthony    nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
25429188a8682a98d4b7882cca434b170517555fc7danthony
25543c4925e5305a26e48d68f7893e94f55d0831c39anthony  MagickStatusType
25643c4925e5305a26e48d68f7893e94f55d0831c39anthony    flags;
25743c4925e5305a26e48d68f7893e94f55d0831c39anthony
25843c4925e5305a26e48d68f7893e94f55d0831c39anthony  GeometryInfo
25943c4925e5305a26e48d68f7893e94f55d0831c39anthony    args;
26043c4925e5305a26e48d68f7893e94f55d0831c39anthony
261a64b85d7873d5e540fe6e2941aa98ec7653a4e2dcristy  kernel=(KernelInfo *) AcquireQuantumMemory(1,sizeof(*kernel));
2622be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  if (kernel == (KernelInfo *)NULL)
263602ab9b30b644a78a4057da93d838a77391ec0acanthony    return(kernel);
264602ab9b30b644a78a4057da93d838a77391ec0acanthony  (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
26543c4925e5305a26e48d68f7893e94f55d0831c39anthony  kernel->minimum = kernel->maximum = kernel->angle = 0.0;
2667a01dcf50ce12cb2a789bedff51e9345f022432eanthony  kernel->negative_range = kernel->positive_range = 0.0;
267602ab9b30b644a78a4057da93d838a77391ec0acanthony  kernel->type = UserDefinedKernel;
2687a01dcf50ce12cb2a789bedff51e9345f022432eanthony  kernel->next = (KernelInfo *) NULL;
269d43a46bc9598004091eae232bc7938e009b494a1cristy  kernel->signature = MagickSignature;
2705e6be1e6a77c230e4a204fa9163d873104730c35cristy  if (kernel_string == (const char *) NULL)
2715e6be1e6a77c230e4a204fa9163d873104730c35cristy    return(kernel);
272602ab9b30b644a78a4057da93d838a77391ec0acanthony
2735ef8e94ff55717be2387d537bd49025780a1a558anthony  /* find end of this specific kernel definition string */
2745ef8e94ff55717be2387d537bd49025780a1a558anthony  end = strchr(kernel_string, ';');
2755ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( end == (char *) NULL )
2765ef8e94ff55717be2387d537bd49025780a1a558anthony    end = strchr(kernel_string, '\0');
2775ef8e94ff55717be2387d537bd49025780a1a558anthony
278a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* clear flags - for Expanding kernel lists thorugh rotations */
27943c4925e5305a26e48d68f7893e94f55d0831c39anthony   flags = NoValue;
28043c4925e5305a26e48d68f7893e94f55d0831c39anthony
281f68116b9d5afef3c343faeb599a3c0ffc8e9b244anthony  /* Has a ':' in argument - New user kernel specification
282f68116b9d5afef3c343faeb599a3c0ffc8e9b244anthony     FUTURE: this split on ':' could be done by StringToken()
283f68116b9d5afef3c343faeb599a3c0ffc8e9b244anthony   */
284602ab9b30b644a78a4057da93d838a77391ec0acanthony  p = strchr(kernel_string, ':');
2855ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( p != (char *) NULL && p < end)
286602ab9b30b644a78a4057da93d838a77391ec0acanthony    {
287602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* ParseGeometry() needs the geometry separated! -- Arrgghh */
288150989ed67ef9da53141a65e5f3ebdb05dd025abcristy      memcpy(token, kernel_string, (size_t) (p-kernel_string));
289602ab9b30b644a78a4057da93d838a77391ec0acanthony      token[p-kernel_string] = '\0';
290c84dce50867229e4872193e8eed5dbab58eb9f02anthony      SetGeometryInfo(&args);
291602ab9b30b644a78a4057da93d838a77391ec0acanthony      flags = ParseGeometry(token, &args);
292602ab9b30b644a78a4057da93d838a77391ec0acanthony
29329188a8682a98d4b7882cca434b170517555fc7danthony      /* Size handling and checks of geometry settings */
294602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( (flags & WidthValue) == 0 ) /* if no width then */
295602ab9b30b644a78a4057da93d838a77391ec0acanthony        args.rho = args.sigma;         /* then  width = height */
296602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.rho < 1.0 )            /* if width too small */
297602ab9b30b644a78a4057da93d838a77391ec0acanthony         args.rho = 1.0;               /* then  width = 1 */
298602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.sigma < 1.0 )          /* if height too small */
299602ab9b30b644a78a4057da93d838a77391ec0acanthony        args.sigma = args.rho;         /* then  height = width */
300bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->width = (size_t)args.rho;
301bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->height = (size_t)args.sigma;
302602ab9b30b644a78a4057da93d838a77391ec0acanthony
303602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* Offset Handling and Checks */
304602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.xi  < 0.0 || args.psi < 0.0 )
30583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        return(DestroyKernelInfo(kernel));
306bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->x = ((flags & XValue)!=0) ? (ssize_t)args.xi
307ea068a53d23d6dca08f1bce44c8937d54f83b983anthony                                        : (ssize_t) (kernel->width-1)/2;
308bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->y = ((flags & YValue)!=0) ? (ssize_t)args.psi
309ea068a53d23d6dca08f1bce44c8937d54f83b983anthony                                        : (ssize_t) (kernel->height-1)/2;
310bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      if ( kernel->x >= (ssize_t) kernel->width ||
311bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy           kernel->y >= (ssize_t) kernel->height )
31283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        return(DestroyKernelInfo(kernel));
313602ab9b30b644a78a4057da93d838a77391ec0acanthony
314602ab9b30b644a78a4057da93d838a77391ec0acanthony      p++; /* advance beyond the ':' */
315602ab9b30b644a78a4057da93d838a77391ec0acanthony    }
316602ab9b30b644a78a4057da93d838a77391ec0acanthony  else
317c84dce50867229e4872193e8eed5dbab58eb9f02anthony    { /* ELSE - Old old specification, forming odd-square kernel */
318602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* count up number of values given */
319602ab9b30b644a78a4057da93d838a77391ec0acanthony      p=(const char *) kernel_string;
320a699b171eff7e0178463e8f271b35a3cbb995f0ecristy      while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
32129188a8682a98d4b7882cca434b170517555fc7danthony        p++;  /* ignore "'" chars for convolve filter usage - Cristy */
3225ef8e94ff55717be2387d537bd49025780a1a558anthony      for (i=0; p < end; i++)
323602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
324602ab9b30b644a78a4057da93d838a77391ec0acanthony        GetMagickToken(p,&p,token);
325602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (*token == ',')
326602ab9b30b644a78a4057da93d838a77391ec0acanthony          GetMagickToken(p,&p,token);
327602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
328602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* set the size of the kernel - old sized square */
329bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->width = kernel->height= (size_t) sqrt((double) i+1.0);
330bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
331602ab9b30b644a78a4057da93d838a77391ec0acanthony      p=(const char *) kernel_string;
33229188a8682a98d4b7882cca434b170517555fc7danthony      while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
33329188a8682a98d4b7882cca434b170517555fc7danthony        p++;  /* ignore "'" chars for convolve filter usage - Cristy */
334602ab9b30b644a78a4057da93d838a77391ec0acanthony    }
335602ab9b30b644a78a4057da93d838a77391ec0acanthony
336602ab9b30b644a78a4057da93d838a77391ec0acanthony  /* Read in the kernel values from rest of input string argument */
337e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy  kernel->values=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
338e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy    kernel->width,kernel->height*sizeof(*kernel->values)));
339d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy  if (kernel->values == (MagickRealType *) NULL)
34083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    return(DestroyKernelInfo(kernel));
341c99304fe3c8d9c617da792b40b57c118bb1249afcristy  kernel->minimum = +MagickHuge;
342c99304fe3c8d9c617da792b40b57c118bb1249afcristy  kernel->maximum = -MagickHuge;
343c99304fe3c8d9c617da792b40b57c118bb1249afcristy  kernel->negative_range = kernel->positive_range = 0.0;
344bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; (i < (ssize_t) (kernel->width*kernel->height)) && (p < end); i++)
345602ab9b30b644a78a4057da93d838a77391ec0acanthony  {
346602ab9b30b644a78a4057da93d838a77391ec0acanthony    GetMagickToken(p,&p,token);
347602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (*token == ',')
348602ab9b30b644a78a4057da93d838a77391ec0acanthony      GetMagickToken(p,&p,token);
34929188a8682a98d4b7882cca434b170517555fc7danthony    if (    LocaleCompare("nan",token) == 0
350c84dce50867229e4872193e8eed5dbab58eb9f02anthony        || LocaleCompare("-",token) == 0 ) {
351ea068a53d23d6dca08f1bce44c8937d54f83b983anthony      kernel->values[i] = nan; /* this value is not part of neighbourhood */
35229188a8682a98d4b7882cca434b170517555fc7danthony    }
35329188a8682a98d4b7882cca434b170517555fc7danthony    else {
3540ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony      kernel->values[i] = StringToDouble(token,(char **) NULL);
35529188a8682a98d4b7882cca434b170517555fc7danthony      ( kernel->values[i] < 0)
356c99304fe3c8d9c617da792b40b57c118bb1249afcristy          ?  ( kernel->negative_range += kernel->values[i] )
357c99304fe3c8d9c617da792b40b57c118bb1249afcristy          :  ( kernel->positive_range += kernel->values[i] );
358c99304fe3c8d9c617da792b40b57c118bb1249afcristy      Minimize(kernel->minimum, kernel->values[i]);
359c99304fe3c8d9c617da792b40b57c118bb1249afcristy      Maximize(kernel->maximum, kernel->values[i]);
36029188a8682a98d4b7882cca434b170517555fc7danthony    }
36129188a8682a98d4b7882cca434b170517555fc7danthony  }
36229188a8682a98d4b7882cca434b170517555fc7danthony
3635ef8e94ff55717be2387d537bd49025780a1a558anthony  /* sanity check -- no more values in kernel definition */
3645ef8e94ff55717be2387d537bd49025780a1a558anthony  GetMagickToken(p,&p,token);
3655ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( *token != '\0' && *token != ';' && *token != '\'' )
3665ef8e94ff55717be2387d537bd49025780a1a558anthony    return(DestroyKernelInfo(kernel));
3675ef8e94ff55717be2387d537bd49025780a1a558anthony
368c84dce50867229e4872193e8eed5dbab58eb9f02anthony#if 0
369c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* this was the old method of handling a incomplete kernel */
370bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  if ( i < (ssize_t) (kernel->width*kernel->height) ) {
371c99304fe3c8d9c617da792b40b57c118bb1249afcristy    Minimize(kernel->minimum, kernel->values[i]);
372c99304fe3c8d9c617da792b40b57c118bb1249afcristy    Maximize(kernel->maximum, kernel->values[i]);
373bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for ( ; i < (ssize_t) (kernel->width*kernel->height); i++)
37429188a8682a98d4b7882cca434b170517555fc7danthony      kernel->values[i]=0.0;
375602ab9b30b644a78a4057da93d838a77391ec0acanthony  }
376c84dce50867229e4872193e8eed5dbab58eb9f02anthony#else
377c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* Number of values for kernel was not enough - Report Error */
378bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  if ( i < (ssize_t) (kernel->width*kernel->height) )
379c84dce50867229e4872193e8eed5dbab58eb9f02anthony    return(DestroyKernelInfo(kernel));
380c84dce50867229e4872193e8eed5dbab58eb9f02anthony#endif
381c84dce50867229e4872193e8eed5dbab58eb9f02anthony
382c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* check that we recieved at least one real (non-nan) value! */
383c84dce50867229e4872193e8eed5dbab58eb9f02anthony  if ( kernel->minimum == MagickHuge )
384c84dce50867229e4872193e8eed5dbab58eb9f02anthony    return(DestroyKernelInfo(kernel));
385602ab9b30b644a78a4057da93d838a77391ec0acanthony
38643c4925e5305a26e48d68f7893e94f55d0831c39anthony  if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel size */
387bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandRotateKernelInfo(kernel, 45.0); /* cyclic rotate 3x3 kernels */
388bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
389bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandRotateKernelInfo(kernel, 90.0); /* 90 degree rotate of kernel */
390bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
391bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandMirrorKernelInfo(kernel);       /* 90 degree mirror rotate */
39243c4925e5305a26e48d68f7893e94f55d0831c39anthony
393602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
394602ab9b30b644a78a4057da93d838a77391ec0acanthony}
395c84dce50867229e4872193e8eed5dbab58eb9f02anthony
39643c4925e5305a26e48d68f7893e94f55d0831c39anthonystatic KernelInfo *ParseKernelName(const char *kernel_string)
397c84dce50867229e4872193e8eed5dbab58eb9f02anthony{
398c84dce50867229e4872193e8eed5dbab58eb9f02anthony  char
399c84dce50867229e4872193e8eed5dbab58eb9f02anthony    token[MaxTextExtent];
400c84dce50867229e4872193e8eed5dbab58eb9f02anthony
401c84dce50867229e4872193e8eed5dbab58eb9f02anthony  const char
4027a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *p,
4037a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *end;
404c84dce50867229e4872193e8eed5dbab58eb9f02anthony
4059d314ff2c17a77996c05413c2013880387e50f0ecristy  GeometryInfo
4069d314ff2c17a77996c05413c2013880387e50f0ecristy    args;
4079d314ff2c17a77996c05413c2013880387e50f0ecristy
4089d314ff2c17a77996c05413c2013880387e50f0ecristy  KernelInfo
4099d314ff2c17a77996c05413c2013880387e50f0ecristy    *kernel;
4109d314ff2c17a77996c05413c2013880387e50f0ecristy
411c84dce50867229e4872193e8eed5dbab58eb9f02anthony  MagickStatusType
412c84dce50867229e4872193e8eed5dbab58eb9f02anthony    flags;
413c84dce50867229e4872193e8eed5dbab58eb9f02anthony
4149d314ff2c17a77996c05413c2013880387e50f0ecristy  ssize_t
4159d314ff2c17a77996c05413c2013880387e50f0ecristy    type;
416c84dce50867229e4872193e8eed5dbab58eb9f02anthony
417c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* Parse special 'named' kernel */
4185ef8e94ff55717be2387d537bd49025780a1a558anthony  GetMagickToken(kernel_string,&p,token);
419042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy  type=ParseCommandOption(MagickKernelOptions,MagickFalse,token);
420c84dce50867229e4872193e8eed5dbab58eb9f02anthony  if ( type < 0 || type == UserDefinedKernel )
4215ef8e94ff55717be2387d537bd49025780a1a558anthony    return((KernelInfo *)NULL);  /* not a valid named kernel */
422c84dce50867229e4872193e8eed5dbab58eb9f02anthony
423c84dce50867229e4872193e8eed5dbab58eb9f02anthony  while (((isspace((int) ((unsigned char) *p)) != 0) ||
4245ef8e94ff55717be2387d537bd49025780a1a558anthony          (*p == ',') || (*p == ':' )) && (*p != '\0') && (*p != ';'))
425c84dce50867229e4872193e8eed5dbab58eb9f02anthony    p++;
4267a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4277a01dcf50ce12cb2a789bedff51e9345f022432eanthony  end = strchr(p, ';'); /* end of this kernel defintion */
4287a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if ( end == (char *) NULL )
4297a01dcf50ce12cb2a789bedff51e9345f022432eanthony    end = strchr(p, '\0');
4307a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4317a01dcf50ce12cb2a789bedff51e9345f022432eanthony  /* ParseGeometry() needs the geometry separated! -- Arrgghh */
4327a01dcf50ce12cb2a789bedff51e9345f022432eanthony  memcpy(token, p, (size_t) (end-p));
4337a01dcf50ce12cb2a789bedff51e9345f022432eanthony  token[end-p] = '\0';
434c84dce50867229e4872193e8eed5dbab58eb9f02anthony  SetGeometryInfo(&args);
4357a01dcf50ce12cb2a789bedff51e9345f022432eanthony  flags = ParseGeometry(token, &args);
436c84dce50867229e4872193e8eed5dbab58eb9f02anthony
4373c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony#if 0
4383c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  /* For Debugging Geometry Input */
4395acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy  (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
4401e604812fad85bb96f757a2393015ae3d061c39acristy    flags, args.rho, args.sigma, args.xi, args.psi );
4413c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony#endif
4423c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony
443c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* special handling of missing values in input string */
444c84dce50867229e4872193e8eed5dbab58eb9f02anthony  switch( type ) {
445a9892d898acb81e1ec73106d892855fdc5a69427anthony    /* Shape Kernel Defaults */
446529482f4b494010a13338a74446c510712f670b3anthony    case UnityKernel:
447529482f4b494010a13338a74446c510712f670b3anthony      if ( (flags & WidthValue) == 0 )
448a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.rho = 1.0;    /* Default scale = 1.0, zero is valid */
4495ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
4505ef8e94ff55717be2387d537bd49025780a1a558anthony    case SquareKernel:
4515ef8e94ff55717be2387d537bd49025780a1a558anthony    case DiamondKernel:
4521ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctagonKernel:
4535ef8e94ff55717be2387d537bd49025780a1a558anthony    case DiskKernel:
4545ef8e94ff55717be2387d537bd49025780a1a558anthony    case PlusKernel:
4553dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case CrossKernel:
4565ef8e94ff55717be2387d537bd49025780a1a558anthony      if ( (flags & HeightValue) == 0 )
457a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.sigma = 1.0;    /* Default scale = 1.0, zero is valid */
4585ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
459c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RingKernel:
460c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      if ( (flags & XValue) == 0 )
461a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.xi = 1.0;       /* Default scale = 1.0, zero is valid */
462c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      break;
463a9892d898acb81e1ec73106d892855fdc5a69427anthony    case RectangleKernel:    /* Rectangle - set size defaults */
464a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & WidthValue) == 0 ) /* if no width then */
465a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.rho = args.sigma;         /* then  width = height */
466a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( args.rho < 1.0 )            /* if width too small */
467a9892d898acb81e1ec73106d892855fdc5a69427anthony          args.rho = 3;                /* then  width = 3 */
468a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( args.sigma < 1.0 )          /* if height too small */
469a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.sigma = args.rho;         /* then  height = width */
470a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & XValue) == 0 )     /* center offset if not defined */
471a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.xi = (double)(((ssize_t)args.rho-1)/2);
472a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & YValue) == 0 )
473a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.psi = (double)(((ssize_t)args.sigma-1)/2);
474a9892d898acb81e1ec73106d892855fdc5a69427anthony      break;
475a9892d898acb81e1ec73106d892855fdc5a69427anthony    /* Distance Kernel Defaults */
4765ef8e94ff55717be2387d537bd49025780a1a558anthony    case ChebyshevKernel:
477bee715c4c0fd9efe6e21d8627ae8664434df7750anthony    case ManhattanKernel:
4781ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctagonalKernel:
4795ef8e94ff55717be2387d537bd49025780a1a558anthony    case EuclideanKernel:
48043c4925e5305a26e48d68f7893e94f55d0831c39anthony      if ( (flags & HeightValue) == 0 )           /* no distance scale */
48143c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma = 100.0;                       /* default distance scaling */
48243c4925e5305a26e48d68f7893e94f55d0831c39anthony      else if ( (flags & AspectValue ) != 0 )     /* '!' flag */
48343c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma = QuantumRange/(args.sigma+1); /* maximum pixel distance */
48443c4925e5305a26e48d68f7893e94f55d0831c39anthony      else if ( (flags & PercentValue ) != 0 )    /* '%' flag */
48543c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma *= QuantumRange/100.0;         /* percentage of color range */
4865ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
4875ef8e94ff55717be2387d537bd49025780a1a558anthony    default:
4885ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
489c84dce50867229e4872193e8eed5dbab58eb9f02anthony  }
490c84dce50867229e4872193e8eed5dbab58eb9f02anthony
491f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  kernel = AcquireKernelBuiltIn((KernelInfoType)type, &args);
492529482f4b494010a13338a74446c510712f670b3anthony  if ( kernel == (KernelInfo *) NULL )
493529482f4b494010a13338a74446c510712f670b3anthony    return(kernel);
494f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony
495f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  /* global expand to rotated kernel list - only for single kernels */
496f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  if ( kernel->next == (KernelInfo *) NULL ) {
497f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony    if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel args */
498bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandRotateKernelInfo(kernel, 45.0);
499bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
500bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandRotateKernelInfo(kernel, 90.0);
501bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
502bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandMirrorKernelInfo(kernel);
503f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  }
504f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony
505f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  return(kernel);
506c84dce50867229e4872193e8eed5dbab58eb9f02anthony}
507c84dce50867229e4872193e8eed5dbab58eb9f02anthony
5085ef8e94ff55717be2387d537bd49025780a1a558anthonyMagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string)
5095ef8e94ff55717be2387d537bd49025780a1a558anthony{
5107a01dcf50ce12cb2a789bedff51e9345f022432eanthony
5117a01dcf50ce12cb2a789bedff51e9345f022432eanthony  KernelInfo
512dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    *kernel,
51343c4925e5305a26e48d68f7893e94f55d0831c39anthony    *new_kernel;
5147a01dcf50ce12cb2a789bedff51e9345f022432eanthony
5155ef8e94ff55717be2387d537bd49025780a1a558anthony  char
5165ef8e94ff55717be2387d537bd49025780a1a558anthony    token[MaxTextExtent];
5175ef8e94ff55717be2387d537bd49025780a1a558anthony
5187a01dcf50ce12cb2a789bedff51e9345f022432eanthony  const char
519dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    *p;
5207a01dcf50ce12cb2a789bedff51e9345f022432eanthony
521bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  size_t
522e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony    kernel_number;
523e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony
5245e6be1e6a77c230e4a204fa9163d873104730c35cristy  if (kernel_string == (const char *) NULL)
5255e6be1e6a77c230e4a204fa9163d873104730c35cristy    return(ParseKernelArray(kernel_string));
526dbc8989a61339951c6434d9a43e7b6fefb5da374anthony  p = kernel_string;
52743c4925e5305a26e48d68f7893e94f55d0831c39anthony  kernel = NULL;
528e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony  kernel_number = 0;
5297a01dcf50ce12cb2a789bedff51e9345f022432eanthony
530dbc8989a61339951c6434d9a43e7b6fefb5da374anthony  while ( GetMagickToken(p,NULL,token),  *token != '\0' ) {
5317a01dcf50ce12cb2a789bedff51e9345f022432eanthony
5321e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp    /* ignore extra or multiple ';' kernel separators */
533dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    if ( *token != ';' ) {
5347a01dcf50ce12cb2a789bedff51e9345f022432eanthony
535dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      /* tokens starting with alpha is a Named kernel */
53643c4925e5305a26e48d68f7893e94f55d0831c39anthony      if (isalpha((int) *token) != 0)
53743c4925e5305a26e48d68f7893e94f55d0831c39anthony        new_kernel = ParseKernelName(p);
538dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      else /* otherwise a user defined kernel array */
53943c4925e5305a26e48d68f7893e94f55d0831c39anthony        new_kernel = ParseKernelArray(p);
540dbc8989a61339951c6434d9a43e7b6fefb5da374anthony
541e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony      /* Error handling -- this is not proper error handling! */
542e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony      if ( new_kernel == (KernelInfo *) NULL ) {
5435acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "Failed to parse kernel number #%.20g\n",
5441e604812fad85bb96f757a2393015ae3d061c39acristy          (double) kernel_number);
545e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony        if ( kernel != (KernelInfo *) NULL )
546e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony          kernel=DestroyKernelInfo(kernel);
547e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony        return((KernelInfo *) NULL);
548dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      }
549e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony
550e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony      /* initialise or append the kernel list */
5513dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony      if ( kernel == (KernelInfo *) NULL )
5523dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        kernel = new_kernel;
5533dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony      else
55443c4925e5305a26e48d68f7893e94f55d0831c39anthony        LastKernelInfo(kernel)->next = new_kernel;
555dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    }
556dbc8989a61339951c6434d9a43e7b6fefb5da374anthony
557dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    /* look for the next kernel in list */
558dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    p = strchr(p, ';');
559dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    if ( p == (char *) NULL )
560dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      break;
561dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    p++;
5625ef8e94ff55717be2387d537bd49025780a1a558anthony
563dbc8989a61339951c6434d9a43e7b6fefb5da374anthony  }
5647a01dcf50ce12cb2a789bedff51e9345f022432eanthony  return(kernel);
5655ef8e94ff55717be2387d537bd49025780a1a558anthony}
5665ef8e94ff55717be2387d537bd49025780a1a558anthony
567602ab9b30b644a78a4057da93d838a77391ec0acanthony
568602ab9b30b644a78a4057da93d838a77391ec0acanthony/*
569602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
570602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
571602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
572602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
573602ab9b30b644a78a4057da93d838a77391ec0acanthony%     A c q u i r e K e r n e l B u i l t I n                                 %
574602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
575602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
576602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
577602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
578602ab9b30b644a78a4057da93d838a77391ec0acanthony%
579602ab9b30b644a78a4057da93d838a77391ec0acanthony%  AcquireKernelBuiltIn() returned one of the 'named' built-in types of
580602ab9b30b644a78a4057da93d838a77391ec0acanthony%  kernels used for special purposes such as gaussian blurring, skeleton
581602ab9b30b644a78a4057da93d838a77391ec0acanthony%  pruning, and edge distance determination.
582602ab9b30b644a78a4057da93d838a77391ec0acanthony%
583602ab9b30b644a78a4057da93d838a77391ec0acanthony%  They take a KernelType, and a set of geometry style arguments, which were
584602ab9b30b644a78a4057da93d838a77391ec0acanthony%  typically decoded from a user supplied string, or from a more complex
585602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Morphology Method that was requested.
586602ab9b30b644a78a4057da93d838a77391ec0acanthony%
587602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The format of the AcquireKernalBuiltIn method is:
588602ab9b30b644a78a4057da93d838a77391ec0acanthony%
5892be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%      KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
590602ab9b30b644a78a4057da93d838a77391ec0acanthony%           const GeometryInfo args)
591602ab9b30b644a78a4057da93d838a77391ec0acanthony%
592602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
593602ab9b30b644a78a4057da93d838a77391ec0acanthony%
594602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o type: the pre-defined type of kernel wanted
595602ab9b30b644a78a4057da93d838a77391ec0acanthony%
596602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o args: arguments defining or modifying the kernel
597602ab9b30b644a78a4057da93d838a77391ec0acanthony%
598602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Convolution Kernels
599602ab9b30b644a78a4057da93d838a77391ec0acanthony%
60046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    Unity
601529482f4b494010a13338a74446c510712f670b3anthony%       The a No-Op or Scaling single element kernel.
60246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
6033c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Gaussian:{radius},{sigma}
6042489f53a1153c2b619b1c9a6744602e8840bd9a9glennrp%       Generate a two-dimensional gaussian kernel, as used by -gaussian.
605c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       The sigma for the curve is required.  The resulting kernel is
606c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       normalized,
607c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
608c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       If 'sigma' is zero, you get a single pixel on a field of zeros.
609602ab9b30b644a78a4057da93d838a77391ec0acanthony%
610602ab9b30b644a78a4057da93d838a77391ec0acanthony%       NOTE: that the 'radius' is optional, but if provided can limit (clip)
611602ab9b30b644a78a4057da93d838a77391ec0acanthony%       the final size of the resulting kernel to a square 2*radius+1 in size.
612602ab9b30b644a78a4057da93d838a77391ec0acanthony%       The radius should be at least 2 times that of the sigma value, or
613602ab9b30b644a78a4057da93d838a77391ec0acanthony%       sever clipping and aliasing may result.  If not given or set to 0 the
614602ab9b30b644a78a4057da93d838a77391ec0acanthony%       radius will be determined so as to produce the best minimal error
615602ab9b30b644a78a4057da93d838a77391ec0acanthony%       result, which is usally much larger than is normally needed.
616602ab9b30b644a78a4057da93d838a77391ec0acanthony%
617501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%    LoG:{radius},{sigma}
618501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        "Laplacian of a Gaussian" or "Mexician Hat" Kernel.
619501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        The supposed ideal edge detection, zero-summing kernel.
620501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
621501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        An alturnative to this kernel is to use a "DoG" with a sigma ratio of
622501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        approx 1.6 (according to wikipedia).
623501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
624501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%    DoG:{radius},{sigma1},{sigma2}
625c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        "Difference of Gaussians" Kernel.
626c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        As "Gaussian" but with a gaussian produced by 'sigma2' subtracted
627c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        from the gaussian produced by 'sigma1'. Typically sigma2 > sigma1.
628c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        The result is a zero-summing kernel.
629c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
630c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Blur:{radius},{sigma}[,{angle}]
6314c08aed51c5899665ade97263692328eea4af106cristy%       Generates a 1 dimensional or linear gaussian blur, at the angle given
632c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       (current restricted to orthogonal angles).  If a 'radius' is given the
633c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       kernel is clipped to a width of 2*radius+1.  Kernel can be rotated
634c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       by a 90 degree angle.
635c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
636c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       If 'sigma' is zero, you get a single pixel on a field of zeros.
637c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
638c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Note that two convolutions with two "Blur" kernels perpendicular to
639f0a92fd8deb68d411304359906b12679b675691fglennrp%       each other, is equivalent to a far larger "Gaussian" kernel with the
640c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       same sigma value, However it is much faster to apply. This is how the
641c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       "-blur" operator actually works.
642c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
6433c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Comet:{width},{sigma},{angle}
6443c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Blur in one direction only, much like how a bright object leaves
645602ab9b30b644a78a4057da93d838a77391ec0acanthony%       a comet like trail.  The Kernel is actually half a gaussian curve,
6463c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Adding two such blurs in opposite directions produces a Blur Kernel.
6473c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Angle can be rotated in multiples of 90 degrees.
648602ab9b30b644a78a4057da93d838a77391ec0acanthony%
6493c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Note that the first argument is the width of the kernel and not the
650602ab9b30b644a78a4057da93d838a77391ec0acanthony%       radius of the kernel.
651602ab9b30b644a78a4057da93d838a77391ec0acanthony%
65240ca0b982379d4ab2716435a46603d56b5b218b1anthony%    Binomial:[{radius}]
65340ca0b982379d4ab2716435a46603d56b5b218b1anthony%       Generate a discrete kernel using a 2 dimentional Pascel's Triangle
654eef684ff80c7d5aca1493f4755426c88b3d3accdanthony%       of values. Used for special forma of image filters.
65540ca0b982379d4ab2716435a46603d56b5b218b1anthony%
656602ab9b30b644a78a4057da93d838a77391ec0acanthony%    # Still to be implemented...
657602ab9b30b644a78a4057da93d838a77391ec0acanthony%    #
6584fd27e21043be809d66c8202e779255e5b660d2danthony%    # Filter2D
6594fd27e21043be809d66c8202e779255e5b660d2danthony%    # Filter1D
6604fd27e21043be809d66c8202e779255e5b660d2danthony%    #    Set kernel values using a resize filter, and given scale (sigma)
661dcabf6d5fddb35c0eedd7a7638c102f16838c4e6glennrp%    #    Cylindrical or Linear.   Is this possible with an image?
6624fd27e21043be809d66c8202e779255e5b660d2danthony%    #
663602ab9b30b644a78a4057da93d838a77391ec0acanthony%
6643c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  Named Constant Convolution Kernels
6653c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
666c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  All these are unscaled, zero-summing kernels by default. As such for
667c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  non-HDRI version of ImageMagick some form of normalization, user scaling,
668c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  and biasing the results is recommended, to prevent the resulting image
669c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  being 'clipped'.
670c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
671c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  The 3x3 kernels (most of these) can be circularly rotated in multiples of
672c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  45 degrees to generate the 8 angled varients of each of the kernels.
6733c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
6743c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Laplacian:{type}
67543c4925e5305a26e48d68f7893e94f55d0831c39anthony%      Discrete Lapacian Kernels, (without normalization)
676c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        Type 0 :  3x3 with center:8 surounded by -1  (8 neighbourhood)
677c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        Type 1 :  3x3 with center:4 edge:-1 corner:0 (4 neighbourhood)
6789eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 2 :  3x3 with center:4 edge:1 corner:-2
6799eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 3 :  3x3 with center:4 edge:-2 corner:1
6809eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 5 :  5x5 laplacian
6819eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 7 :  7x7 laplacian
682501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        Type 15 : 5x5 LoG (sigma approx 1.4)
683501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        Type 19 : 9x9 LoG (sigma approx 1.4)
684c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
685c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Sobel:{angle}
68646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      Sobel 'Edge' convolution kernel (3x3)
687c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
688c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -2, 0,-2 |
689c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
690c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
691c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Roberts:{angle}
69246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      Roberts convolution kernel (3x3)
693c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |  0, 0, 0 |
694c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 0 |
695c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |  0, 0, 0 |
696c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
697c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Prewitt:{angle}
698c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%      Prewitt Edge convolution kernel (3x3)
699c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
700c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
701c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
702c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
7039eb4f74649b23c053b308ce1152dce51239450baanthony%    Compass:{angle}
7049eb4f74649b23c053b308ce1152dce51239450baanthony%      Prewitt's "Compass" convolution kernel (3x3)
705c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 1 |
706c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1,-2, 1 |
707c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 1 |
708c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
7099eb4f74649b23c053b308ce1152dce51239450baanthony%    Kirsch:{angle}
7109eb4f74649b23c053b308ce1152dce51239450baanthony%      Kirsch's "Compass" convolution kernel (3x3)
711c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3,-3, 5 |
712c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3, 0, 5 |
713c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3,-3, 5 |
7143c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
715c40ac1e79923a1516075ba1197ae4ed90244af9banthony%    FreiChen:{angle}
7161d5e67090dc7232b35bfcc71b31266c20838defcanthony%      Frei-Chen Edge Detector is based on a kernel that is similar to
7171d5e67090dc7232b35bfcc71b31266c20838defcanthony%      the Sobel Kernel, but is designed to be isotropic. That is it takes
7181d5e67090dc7232b35bfcc71b31266c20838defcanthony%      into account the distance of the diagonal in the kernel.
719c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
720c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
721c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2), 0, -sqrt(2) |
722c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
723c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
724c40ac1e79923a1516075ba1197ae4ed90244af9banthony%    FreiChen:{type},{angle}
725c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
726c40ac1e79923a1516075ba1197ae4ed90244af9banthony%      Frei-Chen Pre-weighted kernels...
727c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
728c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 0:  default un-nomalized version shown above.
729c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
730c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 1: Orthogonal Kernel (same as type 11 below)
731c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
732c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
733c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
734c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
735c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 2: Diagonal form of Kernel...
736c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     sqrt(2),    0     |
737c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2),   0,     -sqrt(2) | / 2*sqrt(2)
738c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   0,    -sqrt(2)    -1     |
739c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
7401d5e67090dc7232b35bfcc71b31266c20838defcanthony%      However this kernel is als at the heart of the FreiChen Edge Detection
7411d5e67090dc7232b35bfcc71b31266c20838defcanthony%      Process which uses a set of 9 specially weighted kernel.  These 9
7421d5e67090dc7232b35bfcc71b31266c20838defcanthony%      kernels not be normalized, but directly applied to the image. The
7431d5e67090dc7232b35bfcc71b31266c20838defcanthony%      results is then added together, to produce the intensity of an edge in
7441d5e67090dc7232b35bfcc71b31266c20838defcanthony%      a specific direction.  The square root of the pixel value can then be
7451d5e67090dc7232b35bfcc71b31266c20838defcanthony%      taken as the cosine of the edge, and at least 2 such runs at 90 degrees
7461d5e67090dc7232b35bfcc71b31266c20838defcanthony%      from each other, both the direction and the strength of the edge can be
7471d5e67090dc7232b35bfcc71b31266c20838defcanthony%      determined.
7481d5e67090dc7232b35bfcc71b31266c20838defcanthony%
749c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 10: All 9 of the following pre-weighted kernels...
750c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
751c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 11: |   1,     0,   -1     |
752c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
753c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |   1,     0,   -1     |
754e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
755c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 12: | 1, sqrt(2), 1 |
756c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 0,   0,     0 | / 2*sqrt(2)
757c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, sqrt(2), 1 |
758e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
759c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 13: | sqrt(2), -1,    0     |
760c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |  -1,      0,    1     | / 2*sqrt(2)
761c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |   0,      1, -sqrt(2) |
762e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
763c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 14: |    0,     1, -sqrt(2) |
764c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |   -1,     0,     1    | / 2*sqrt(2)
765c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | sqrt(2), -1,     0    |
766e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
767c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 15: | 0, -1, 0 |
768c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1,  0, 1 | / 2
769c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 0, -1, 0 |
770e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
771c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 16: |  1, 0, -1 |
772c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |  0, 0,  0 | / 2
773c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -1, 0,  1 |
774e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
775c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 17: |  1, -2,  1 |
776c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -2,  4, -2 | / 6
777c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -1, -2,  1 |
778501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
779c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 18: | -2, 1, -2 |
780c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |  1, 4,  1 | / 6
781c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -2, 1, -2 |
782e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
783c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 19: | 1, 1, 1 |
784c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, 1, 1 | / 3
785c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, 1, 1 |
786e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
787e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%      The first 4 are for edge detection, the next 4 are for line detection
788e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%      and the last is to add a average component to the results.
789e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
790c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      Using a special type of '-1' will return all 9 pre-weighted kernels
791c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      as a multi-kernel list, so that you can use them directly (without
792c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      normalization) with the special "-set option:morphology:compose Plus"
793c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      setting to apply the full FreiChen Edge Detection Technique.
794c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
7951dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      If 'type' is large it will be taken to be an actual rotation angle for
7961dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      the default FreiChen (type 0) kernel.  As such  FreiChen:45  will look
7971dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      like a  Sobel:45  but with 'sqrt(2)' instead of '2' values.
7981dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%
799501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      WARNING: The above was layed out as per
800501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%          http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf
801501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      But rotated 90 degrees so direction is from left rather than the top.
802501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      I have yet to find any secondary confirmation of the above. The only
803501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      other source found was actual source code at
804501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%          http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf
805501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      Neigher paper defineds the kernels in a way that looks locical or
806501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      correct when taken as a whole.
807e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
808602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Boolean Kernels
809602ab9b30b644a78a4057da93d838a77391ec0acanthony%
8103c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Diamond:[{radius}[,{scale}]]
8111b2bc0a7da432e6e1cc0480280402df213faa940anthony%       Generate a diamond shaped kernel with given radius to the points.
812602ab9b30b644a78a4057da93d838a77391ec0acanthony%       Kernel size will again be radius*2+1 square and defaults to radius 1,
813602ab9b30b644a78a4057da93d838a77391ec0acanthony%       generating a 3x3 kernel that is slightly larger than a square.
814602ab9b30b644a78a4057da93d838a77391ec0acanthony%
8153c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Square:[{radius}[,{scale}]]
816602ab9b30b644a78a4057da93d838a77391ec0acanthony%       Generate a square shaped kernel of size radius*2+1, and defaulting
817602ab9b30b644a78a4057da93d838a77391ec0acanthony%       to a 3x3 (radius 1).
818602ab9b30b644a78a4057da93d838a77391ec0acanthony%
8191ef941fea2534a0d20ba7d71307d35040247decbanthony%    Octagon:[{radius}[,{scale}]]
8201ef941fea2534a0d20ba7d71307d35040247decbanthony%       Generate octagonal shaped kernel of given radius and constant scale.
8210bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%       Default radius is 3 producing a 7x7 kernel. A radius of 1 will result
8221ef941fea2534a0d20ba7d71307d35040247decbanthony%       in "Diamond" kernel.
8231ef941fea2534a0d20ba7d71307d35040247decbanthony%
8243c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Disk:[{radius}[,{scale}]]
8251ef941fea2534a0d20ba7d71307d35040247decbanthony%       Generate a binary disk, thresholded at the radius given, the radius
8261ef941fea2534a0d20ba7d71307d35040247decbanthony%       may be a float-point value. Final Kernel size is floor(radius)*2+1
8270bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%       square. A radius of 5.3 is the default.
8281ef941fea2534a0d20ba7d71307d35040247decbanthony%
8291ef941fea2534a0d20ba7d71307d35040247decbanthony%       NOTE: That a low radii Disk kernels produce the same results as
8301ef941fea2534a0d20ba7d71307d35040247decbanthony%       many of the previously defined kernels, but differ greatly at larger
8311ef941fea2534a0d20ba7d71307d35040247decbanthony%       radii.  Here is a table of equivalences...
8321ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:1"    => "Diamond", "Octagon:1", or "Cross:1"
8331ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:1.5"  => "Square"
8341ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2"    => "Diamond:2"
8351ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2.5"  => "Octagon"
8361ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2.9"  => "Square:2"
8370bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%          "Disk:3.5"  => "Octagon:3"
8381ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:4.5"  => "Octagon:4"
8391ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:5.4"  => "Octagon:5"
8401ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:6.4"  => "Octagon:6"
8411ef941fea2534a0d20ba7d71307d35040247decbanthony%       All other Disk shapes are unique to this kernel, but because a "Disk"
8421ef941fea2534a0d20ba7d71307d35040247decbanthony%       is more circular when using a larger radius, using a larger radius is
8431ef941fea2534a0d20ba7d71307d35040247decbanthony%       preferred over iterating the morphological operation.
844602ab9b30b644a78a4057da93d838a77391ec0acanthony%
845a9892d898acb81e1ec73106d892855fdc5a69427anthony%    Rectangle:{geometry}
846a9892d898acb81e1ec73106d892855fdc5a69427anthony%       Simply generate a rectangle of 1's with the size given. You can also
847a9892d898acb81e1ec73106d892855fdc5a69427anthony%       specify the location of the 'control point', otherwise the closest
848a9892d898acb81e1ec73106d892855fdc5a69427anthony%       pixel to the center of the rectangle is selected.
849a9892d898acb81e1ec73106d892855fdc5a69427anthony%
850a9892d898acb81e1ec73106d892855fdc5a69427anthony%       Properly centered and odd sized rectangles work the best.
851a9892d898acb81e1ec73106d892855fdc5a69427anthony%
852c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  Symbol Dilation Kernels
853c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
854c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    These kernel is not a good general morphological kernel, but is used
855c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    more for highlighting and marking any single pixels in an image using,
856c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    a "Dilate" method as appropriate.
857c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
858c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    For the same reasons iterating these kernels does not produce the
859c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    same result as using a larger radius for the symbol.
860c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
8613c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Plus:[{radius}[,{scale}]]
8623dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Cross:[{radius}[,{scale}]]
863c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Generate a kernel in the shape of a 'plus' or a 'cross' with
864c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       a each arm the length of the given radius (default 2).
8653dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%
866f0a92fd8deb68d411304359906b12679b675691fglennrp%       NOTE: "plus:1" is equivalent to a "Diamond" kernel.
867602ab9b30b644a78a4057da93d838a77391ec0acanthony%
868c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Ring:{radius1},{radius2}[,{scale}]
869c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       A ring of the values given that falls between the two radii.
870c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Defaults to a ring of approximataly 3 radius in a 7x7 kernel.
871c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       This is the 'edge' pixels of the default "Disk" kernel,
872c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       More specifically, "Ring" -> "Ring:2.5,3.5,1.0"
8733dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%
8743dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%  Hit and Miss Kernels
875602ab9b30b644a78a4057da93d838a77391ec0acanthony%
8763dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Peak:radius1,radius2
877c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Find any peak larger than the pixels the fall between the two radii.
878c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       The default ring of pixels is as per "Ring".
87943c4925e5305a26e48d68f7893e94f55d0831c39anthony%    Edges
880694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find flat orthogonal edges of a binary shape
8813dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Corners
882694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find 90 degree corners of a binary shape
883529482f4b494010a13338a74446c510712f670b3anthony%    Diagonals:type
884529482f4b494010a13338a74446c510712f670b3anthony%       A special kernel to thin the 'outside' of diagonals
885694934fa79dd310f727588b1d0a7481fa6170f1danthony%    LineEnds:type
8863dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%       Find end points of lines (for pruning a skeletion)
887694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Two types of lines ends (default to both) can be searched for
888694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 0: All line ends
889694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: single kernel for 4-conneected line ends
890694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: single kernel for simple line ends
8913dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    LineJunctions
89243c4925e5305a26e48d68f7893e94f55d0831c39anthony%       Find three line junctions (within a skeletion)
893694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 0: all line junctions
894694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Y Junction kernel
895694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: Diagonal T Junction kernel
896694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 3: Orthogonal T Junction kernel
897694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 4: Diagonal X Junction kernel
898694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 5: Orthogonal + Junction kernel
899694934fa79dd310f727588b1d0a7481fa6170f1danthony%    Ridges:type
900694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find single pixel ridges or thin lines
901694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Fine single pixel thick lines and ridges
902694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: Find two pixel thick lines and ridges
9033dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    ConvexHull
904a9892d898acb81e1ec73106d892855fdc5a69427anthony%       Octagonal Thickening Kernel, to generate convex hulls of 45 degrees
905c40ac1e79923a1516075ba1197ae4ed90244af9banthony%    Skeleton:type
906c40ac1e79923a1516075ba1197ae4ed90244af9banthony%       Traditional skeleton generating kernels.
907694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Tradional Skeleton kernel (4 connected skeleton)
908694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: HIPR2 Skeleton kernel (8 connected skeleton)
909e816a586a13717bab2d6839ced6e5c3828a37f19anthony%         Type 3: Thinning skeleton based on a ressearch paper by
9102b2290b46c246ce1f14cb78f1695394e4c4a3ddfanthony%                 Dan S. Bloomberg (Default Type)
911e816a586a13717bab2d6839ced6e5c3828a37f19anthony%    ThinSE:type
912e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       A huge variety of Thinning Kernels designed to preserve conectivity.
913e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       many other kernel sets use these kernels as source definitions.
914e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       Type numbers are 41-49, 81-89, 481, and 482 which are based on
915e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       the super and sub notations used in the source research paper.
916602ab9b30b644a78a4057da93d838a77391ec0acanthony%
917602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Distance Measuring Kernels
918602ab9b30b644a78a4057da93d838a77391ec0acanthony%
919c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Different types of distance measuring methods, which are used with the
920c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    a 'Distance' morphology method for generating a gradient based on
921c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    distance from an edge of a binary shape, though there is a technique
922c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    for handling a anti-aliased shape.
923602ab9b30b644a78a4057da93d838a77391ec0acanthony%
924c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    See the 'Distance' Morphological Method, for information of how it is
925c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    applied.
926602ab9b30b644a78a4057da93d838a77391ec0acanthony%
927c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Chebyshev:[{radius}][x{scale}[%!]]
9281ef941fea2534a0d20ba7d71307d35040247decbanthony%       Chebyshev Distance (also known as Tchebychev or Chessboard distance)
9291ef941fea2534a0d20ba7d71307d35040247decbanthony%       is a value of one to any neighbour, orthogonal or diagonal. One why
9301ef941fea2534a0d20ba7d71307d35040247decbanthony%       of thinking of it is the number of squares a 'King' or 'Queen' in
9311ef941fea2534a0d20ba7d71307d35040247decbanthony%       chess needs to traverse reach any other position on a chess board.
9321ef941fea2534a0d20ba7d71307d35040247decbanthony%       It results in a 'square' like distance function, but one where
9331ef941fea2534a0d20ba7d71307d35040247decbanthony%       diagonals are given a value that is closer than expected.
934c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%
935bee715c4c0fd9efe6e21d8627ae8664434df7750anthony%    Manhattan:[{radius}][x{scale}[%!]]
9361ef941fea2534a0d20ba7d71307d35040247decbanthony%       Manhattan Distance (also known as Rectilinear, City Block, or the Taxi
9371ef941fea2534a0d20ba7d71307d35040247decbanthony%       Cab distance metric), it is the distance needed when you can only
9381ef941fea2534a0d20ba7d71307d35040247decbanthony%       travel in horizontal or vertical directions only.  It is the
9391ef941fea2534a0d20ba7d71307d35040247decbanthony%       distance a 'Rook' in chess would have to travel, and results in a
9401ef941fea2534a0d20ba7d71307d35040247decbanthony%       diamond like distances, where diagonals are further than expected.
9411ef941fea2534a0d20ba7d71307d35040247decbanthony%
9421ef941fea2534a0d20ba7d71307d35040247decbanthony%    Octagonal:[{radius}][x{scale}[%!]]
9431ef941fea2534a0d20ba7d71307d35040247decbanthony%       An interleving of Manhatten and Chebyshev metrics producing an
9441ef941fea2534a0d20ba7d71307d35040247decbanthony%       increasing octagonally shaped distance.  Distances matches those of
9451ef941fea2534a0d20ba7d71307d35040247decbanthony%       the "Octagon" shaped kernel of the same radius.  The minimum radius
9461ef941fea2534a0d20ba7d71307d35040247decbanthony%       and default is 2, producing a 5x5 kernel.
947c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%
948c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Euclidean:[{radius}][x{scale}[%!]]
9491ef941fea2534a0d20ba7d71307d35040247decbanthony%       Euclidean distance is the 'direct' or 'as the crow flys' distance.
950c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       However by default the kernel size only has a radius of 1, which
951c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       limits the distance to 'Knight' like moves, with only orthogonal and
952c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       diagonal measurements being correct.  As such for the default kernel
9531ef941fea2534a0d20ba7d71307d35040247decbanthony%       you will get octagonal like distance function.
9541ef941fea2534a0d20ba7d71307d35040247decbanthony%
9551ef941fea2534a0d20ba7d71307d35040247decbanthony%       However using a larger radius such as "Euclidean:4" you will get a
9561ef941fea2534a0d20ba7d71307d35040247decbanthony%       much smoother distance gradient from the edge of the shape. Especially
9571ef941fea2534a0d20ba7d71307d35040247decbanthony%       if the image is pre-processed to include any anti-aliasing pixels.
9581ef941fea2534a0d20ba7d71307d35040247decbanthony%       Of course a larger kernel is slower to use, and not always needed.
9591ef941fea2534a0d20ba7d71307d35040247decbanthony%
9601ef941fea2534a0d20ba7d71307d35040247decbanthony%    The first three Distance Measuring Kernels will only generate distances
9611ef941fea2534a0d20ba7d71307d35040247decbanthony%    of exact multiples of {scale} in binary images. As such you can use a
9621ef941fea2534a0d20ba7d71307d35040247decbanthony%    scale of 1 without loosing any information.  However you also need some
9631ef941fea2534a0d20ba7d71307d35040247decbanthony%    scaling when handling non-binary anti-aliased shapes.
9641ef941fea2534a0d20ba7d71307d35040247decbanthony%
9651ef941fea2534a0d20ba7d71307d35040247decbanthony%    The "Euclidean" Distance Kernel however does generate a non-integer
9661ef941fea2534a0d20ba7d71307d35040247decbanthony%    fractional results, and as such scaling is vital even for binary shapes.
967c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%
968602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
969602ab9b30b644a78a4057da93d838a77391ec0acanthony
9702be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristyMagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
971602ab9b30b644a78a4057da93d838a77391ec0acanthony   const GeometryInfo *args)
972602ab9b30b644a78a4057da93d838a77391ec0acanthony{
9732be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  KernelInfo
974602ab9b30b644a78a4057da93d838a77391ec0acanthony    *kernel;
975602ab9b30b644a78a4057da93d838a77391ec0acanthony
976bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
977602ab9b30b644a78a4057da93d838a77391ec0acanthony    i;
978602ab9b30b644a78a4057da93d838a77391ec0acanthony
979bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
980602ab9b30b644a78a4057da93d838a77391ec0acanthony    u,
981602ab9b30b644a78a4057da93d838a77391ec0acanthony    v;
982602ab9b30b644a78a4057da93d838a77391ec0acanthony
983602ab9b30b644a78a4057da93d838a77391ec0acanthony  double
984602ab9b30b644a78a4057da93d838a77391ec0acanthony    nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
985602ab9b30b644a78a4057da93d838a77391ec0acanthony
986c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  /* Generate a new empty kernel if needed */
987e96405a0f45f803fb9c26f75e7bdee252437febbcristy  kernel=(KernelInfo *) NULL;
988c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  switch(type) {
9891dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case UndefinedKernel:    /* These should not call this function */
9909eb4f74649b23c053b308ce1152dce51239450baanthony    case UserDefinedKernel:
991529482f4b494010a13338a74446c510712f670b3anthony      assert("Should not call this function" != (char *)NULL);
9929eb4f74649b23c053b308ce1152dce51239450baanthony      break;
993529482f4b494010a13338a74446c510712f670b3anthony    case LaplacianKernel:   /* Named Descrete Convolution Kernels */
994529482f4b494010a13338a74446c510712f670b3anthony    case SobelKernel:       /* these are defined using other kernels */
9959eb4f74649b23c053b308ce1152dce51239450baanthony    case RobertsKernel:
9969eb4f74649b23c053b308ce1152dce51239450baanthony    case PrewittKernel:
9979eb4f74649b23c053b308ce1152dce51239450baanthony    case CompassKernel:
9989eb4f74649b23c053b308ce1152dce51239450baanthony    case KirschKernel:
9991dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case FreiChenKernel:
1000694934fa79dd310f727588b1d0a7481fa6170f1danthony    case EdgesKernel:       /* Hit and Miss kernels */
1001694934fa79dd310f727588b1d0a7481fa6170f1danthony    case CornersKernel:
1002529482f4b494010a13338a74446c510712f670b3anthony    case DiagonalsKernel:
10039eb4f74649b23c053b308ce1152dce51239450baanthony    case LineEndsKernel:
10049eb4f74649b23c053b308ce1152dce51239450baanthony    case LineJunctionsKernel:
10051dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case RidgesKernel:
10069eb4f74649b23c053b308ce1152dce51239450baanthony    case ConvexHullKernel:
10079eb4f74649b23c053b308ce1152dce51239450baanthony    case SkeletonKernel:
10089a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony    case ThinSEKernel:
1009c40ac1e79923a1516075ba1197ae4ed90244af9banthony      break;               /* A pre-generated kernel is not needed */
1010c40ac1e79923a1516075ba1197ae4ed90244af9banthony#if 0
1011c40ac1e79923a1516075ba1197ae4ed90244af9banthony    /* set to 1 to do a compile-time check that we haven't missed anything */
1012529482f4b494010a13338a74446c510712f670b3anthony    case UnityKernel:
1013c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case GaussianKernel:
1014501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case DoGKernel:
1015501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case LoGKernel:
1016c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case BlurKernel:
1017c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CometKernel:
101840ca0b982379d4ab2716435a46603d56b5b218b1anthony    case BinomialKernel:
1019c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiamondKernel:
1020c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SquareKernel:
1021c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RectangleKernel:
10221ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctagonKernel:
1023c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiskKernel:
1024c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PlusKernel:
1025c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CrossKernel:
1026c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RingKernel:
1027c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PeaksKernel:
1028c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case ChebyshevKernel:
1029bee715c4c0fd9efe6e21d8627ae8664434df7750anthony    case ManhattanKernel:
10301ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctangonalKernel:
1031c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case EuclideanKernel:
10321dd091ae3bc17edc26c16cc47f436a24bd48412aanthony#else
10339eb4f74649b23c053b308ce1152dce51239450baanthony    default:
10341dd091ae3bc17edc26c16cc47f436a24bd48412aanthony#endif
10359eb4f74649b23c053b308ce1152dce51239450baanthony      /* Generate the base Kernel Structure */
1036c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
1037c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      if (kernel == (KernelInfo *) NULL)
1038c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        return(kernel);
1039c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
104043c4925e5305a26e48d68f7893e94f55d0831c39anthony      kernel->minimum = kernel->maximum = kernel->angle = 0.0;
1041c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->negative_range = kernel->positive_range = 0.0;
1042c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->type = type;
1043c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->next = (KernelInfo *) NULL;
1044c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->signature = MagickSignature;
1045c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      break;
1046c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  }
1047602ab9b30b644a78a4057da93d838a77391ec0acanthony
1048602ab9b30b644a78a4057da93d838a77391ec0acanthony  switch(type) {
1049529482f4b494010a13338a74446c510712f670b3anthony    /*
1050529482f4b494010a13338a74446c510712f670b3anthony      Convolution Kernels
1051529482f4b494010a13338a74446c510712f670b3anthony    */
1052529482f4b494010a13338a74446c510712f670b3anthony    case UnityKernel:
1053529482f4b494010a13338a74446c510712f670b3anthony      {
1054529482f4b494010a13338a74446c510712f670b3anthony        kernel->height = kernel->width = (size_t) 1;
1055529482f4b494010a13338a74446c510712f670b3anthony        kernel->x = kernel->y = (ssize_t) 0;
1056e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1057e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(1,sizeof(*kernel->values)));
1058d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
1059529482f4b494010a13338a74446c510712f670b3anthony          return(DestroyKernelInfo(kernel));
1060529482f4b494010a13338a74446c510712f670b3anthony        kernel->maximum = kernel->values[0] = args->rho;
1061529482f4b494010a13338a74446c510712f670b3anthony        break;
1062529482f4b494010a13338a74446c510712f670b3anthony      }
1063529482f4b494010a13338a74446c510712f670b3anthony      break;
1064602ab9b30b644a78a4057da93d838a77391ec0acanthony    case GaussianKernel:
1065501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case DoGKernel:
1066501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case LoGKernel:
1067602ab9b30b644a78a4057da93d838a77391ec0acanthony      { double
1068c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma = fabs(args->sigma),
1069c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma2 = fabs(args->xi),
10709eb4f74649b23c053b308ce1152dce51239450baanthony          A, B, R;
1071c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1072c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( args->rho >= 1.0 )
1073bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = (size_t)args->rho*2+1;
1074501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        else if ( (type != DoGKernel) || (sigma >= sigma2) )
1075c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = GetOptimalKernelWidth2D(args->rho,sigma);
1076c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else
1077c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = GetOptimalKernelWidth2D(args->rho,sigma2);
1078c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->height = kernel->width;
1079bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1080e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1081e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1082e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1083d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
108483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1085602ab9b30b644a78a4057da93d838a77391ec0acanthony
108646a369d839971ab627bdb31a93d8bd63e81b65a3anthony        /* WARNING: The following generates a 'sampled gaussian' kernel.
10879eb4f74649b23c053b308ce1152dce51239450baanthony         * What we really want is a 'discrete gaussian' kernel.
108846a369d839971ab627bdb31a93d8bd63e81b65a3anthony         *
1089529482f4b494010a13338a74446c510712f670b3anthony         * How to do this is I don't know, but appears to be basied on the
1090529482f4b494010a13338a74446c510712f670b3anthony         * Error Function 'erf()' (intergral of a gaussian)
10919eb4f74649b23c053b308ce1152dce51239450baanthony         */
10929eb4f74649b23c053b308ce1152dce51239450baanthony
1093501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if ( type == GaussianKernel || type == DoGKernel )
1094501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          { /* Calculate a Gaussian,  OR positive half of a DoG */
10959eb4f74649b23c053b308ce1152dce51239450baanthony            if ( sigma > MagickEpsilon )
10969eb4f74649b23c053b308ce1152dce51239450baanthony              { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
109755a91cddcdea3aa002893186a773e1704884a9dfcristy                B = (double) (1.0/(Magick2PI*sigma*sigma));
1098bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1099bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
11009eb4f74649b23c053b308ce1152dce51239450baanthony                      kernel->values[i] = exp(-((double)(u*u+v*v))*A)*B;
11019eb4f74649b23c053b308ce1152dce51239450baanthony              }
11029eb4f74649b23c053b308ce1152dce51239450baanthony            else /* limiting case - a unity (normalized Dirac) kernel */
11039eb4f74649b23c053b308ce1152dce51239450baanthony              { (void) ResetMagickMemory(kernel->values,0, (size_t)
11049eb4f74649b23c053b308ce1152dce51239450baanthony                            kernel->width*kernel->height*sizeof(double));
11059eb4f74649b23c053b308ce1152dce51239450baanthony                kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
11069eb4f74649b23c053b308ce1152dce51239450baanthony              }
1107c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
11089eb4f74649b23c053b308ce1152dce51239450baanthony
1109501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if ( type == DoGKernel )
1110c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { /* Subtract a Negative Gaussian for "Difference of Gaussian" */
1111c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            if ( sigma2 > MagickEpsilon )
1112c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              { sigma = sigma2;                /* simplify loop expressions */
11139eb4f74649b23c053b308ce1152dce51239450baanthony                A = 1.0/(2.0*sigma*sigma);
111455a91cddcdea3aa002893186a773e1704884a9dfcristy                B = (double) (1.0/(Magick2PI*sigma*sigma));
1115bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1116bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
11179eb4f74649b23c053b308ce1152dce51239450baanthony                    kernel->values[i] -= exp(-((double)(u*u+v*v))*A)*B;
1118c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              }
11199eb4f74649b23c053b308ce1152dce51239450baanthony            else /* limiting case - a unity (normalized Dirac) kernel */
1120c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->values[kernel->x+kernel->y*kernel->width] -= 1.0;
1121c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
11229eb4f74649b23c053b308ce1152dce51239450baanthony
1123501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if ( type == LoGKernel )
11249eb4f74649b23c053b308ce1152dce51239450baanthony          { /* Calculate a Laplacian of a Gaussian - Or Mexician Hat */
11259eb4f74649b23c053b308ce1152dce51239450baanthony            if ( sigma > MagickEpsilon )
11269eb4f74649b23c053b308ce1152dce51239450baanthony              { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
112755a91cddcdea3aa002893186a773e1704884a9dfcristy                B = (double) (1.0/(MagickPI*sigma*sigma*sigma*sigma));
1128bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1129bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
11309eb4f74649b23c053b308ce1152dce51239450baanthony                    { R = ((double)(u*u+v*v))*A;
11319eb4f74649b23c053b308ce1152dce51239450baanthony                      kernel->values[i] = (1-R)*exp(-R)*B;
11329eb4f74649b23c053b308ce1152dce51239450baanthony                    }
11339eb4f74649b23c053b308ce1152dce51239450baanthony              }
11349eb4f74649b23c053b308ce1152dce51239450baanthony            else /* special case - generate a unity kernel */
11359eb4f74649b23c053b308ce1152dce51239450baanthony              { (void) ResetMagickMemory(kernel->values,0, (size_t)
11369eb4f74649b23c053b308ce1152dce51239450baanthony                            kernel->width*kernel->height*sizeof(double));
11379eb4f74649b23c053b308ce1152dce51239450baanthony                kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
11389eb4f74649b23c053b308ce1152dce51239450baanthony              }
11399eb4f74649b23c053b308ce1152dce51239450baanthony          }
11409eb4f74649b23c053b308ce1152dce51239450baanthony
11419eb4f74649b23c053b308ce1152dce51239450baanthony        /* Note the above kernels may have been 'clipped' by a user defined
1142c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** radius, producing a smaller (darker) kernel.  Also for very small
1143c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** sigma's (> 0.1) the central value becomes larger than one, and thus
1144c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** producing a very bright kernel.
1145c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
1146c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** Normalization will still be needed.
1147c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        */
1148602ab9b30b644a78a4057da93d838a77391ec0acanthony
11493dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        /* Normalize the 2D Gaussian Kernel
11503dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        **
1151c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** NB: a CorrelateNormalize performs a normal Normalize if
1152c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there are no negative values.
11533dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        */
115446a369d839971ab627bdb31a93d8bd63e81b65a3anthony        CalcKernelMetaData(kernel);  /* the other kernel meta-data */
1155c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1156602ab9b30b644a78a4057da93d838a77391ec0acanthony
1157602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
1158602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
1159602ab9b30b644a78a4057da93d838a77391ec0acanthony    case BlurKernel:
1160602ab9b30b644a78a4057da93d838a77391ec0acanthony      { double
1161c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma = fabs(args->sigma),
1162501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          alpha, beta;
1163602ab9b30b644a78a4057da93d838a77391ec0acanthony
1164c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( args->rho >= 1.0 )
1165bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = (size_t)args->rho*2+1;
1166c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else
1167501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
1168602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->height = 1;
1169bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = (ssize_t) (kernel->width-1)/2;
1170c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->y = 0;
1171c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->negative_range = kernel->positive_range = 0.0;
1172e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1173e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1174e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1175d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
117683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1177602ab9b30b644a78a4057da93d838a77391ec0acanthony
1178602ab9b30b644a78a4057da93d838a77391ec0acanthony#if 1
1179602ab9b30b644a78a4057da93d838a77391ec0acanthony#define KernelRank 3
1180602ab9b30b644a78a4057da93d838a77391ec0acanthony        /* Formula derived from GetBlurKernel() in "effect.c" (plus bug fix).
1181602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** It generates a gaussian 3 times the width, and compresses it into
1182602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** the expected range.  This produces a closer normalization of the
1183602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** resulting kernel, especially for very low sigma values.
1184602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** As such while wierd it is prefered.
1185602ab9b30b644a78a4057da93d838a77391ec0acanthony        **
1186602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** I am told this method originally came from Photoshop.
11879eb4f74649b23c053b308ce1152dce51239450baanthony        **
11889eb4f74649b23c053b308ce1152dce51239450baanthony        ** A properly normalized curve is generated (apart from edge clipping)
11899eb4f74649b23c053b308ce1152dce51239450baanthony        ** even though we later normalize the result (for edge clipping)
11909eb4f74649b23c053b308ce1152dce51239450baanthony        ** to allow the correct generation of a "Difference of Blurs".
1191602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
1192c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1193c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* initialize */
1194bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        v = (ssize_t) (kernel->width*KernelRank-1)/2; /* start/end points to fit range */
11959eb4f74649b23c053b308ce1152dce51239450baanthony        (void) ResetMagickMemory(kernel->values,0, (size_t)
11969eb4f74649b23c053b308ce1152dce51239450baanthony                     kernel->width*kernel->height*sizeof(double));
1197c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Calculate a Positive 1D Gaussian */
1198c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( sigma > MagickEpsilon )
1199c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { sigma *= KernelRank;               /* simplify loop expressions */
1200501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            alpha = 1.0/(2.0*sigma*sigma);
120155a91cddcdea3aa002893186a773e1704884a9dfcristy            beta= (double) (1.0/(MagickSQ2PI*sigma ));
1202c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            for ( u=-v; u <= v; u++) {
1203501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony              kernel->values[(u+v)/KernelRank] +=
1204501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony                              exp(-((double)(u*u))*alpha)*beta;
1205c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            }
1206c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
1207c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else /* special case - generate a unity kernel */
1208c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1209602ab9b30b644a78a4057da93d838a77391ec0acanthony#else
121053f576d128f0bb744a82e7f9c0d8f05b2923972canthony        /* Direct calculation without curve averaging
121153f576d128f0bb744a82e7f9c0d8f05b2923972canthony           This is equivelent to a KernelRank of 1 */
1212c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1213c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Calculate a Positive Gaussian */
1214c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( sigma > MagickEpsilon )
1215501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          { alpha = 1.0/(2.0*sigma*sigma);    /* simplify loop expressions */
1216501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            beta = 1.0/(MagickSQ2PI*sigma);
1217bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for ( i=0, u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1218501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony              kernel->values[i] = exp(-((double)(u*u))*alpha)*beta;
1219c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
1220c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else /* special case - generate a unity kernel */
1221c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { (void) ResetMagickMemory(kernel->values,0, (size_t)
1222c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony                         kernel->width*kernel->height*sizeof(double));
1223c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1224c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
1225602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
1226c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Note the above kernel may have been 'clipped' by a user defined
1227cc6c836da2a53b6023b716e4973090a6714dc3b0anthony        ** radius, producing a smaller (darker) kernel.  Also for very small
122853f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** sigma's (> 0.1) the central value becomes larger than one, as a
122953f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** result of not generating a actual 'discrete' kernel, and thus
123053f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** producing a very bright 'impulse'.
1231c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
123253f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** Becuase of these two factors Normalization is required!
1233602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
1234cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
1235602ab9b30b644a78a4057da93d838a77391ec0acanthony        /* Normalize the 1D Gaussian Kernel
1236602ab9b30b644a78a4057da93d838a77391ec0acanthony        **
1237c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** NB: a CorrelateNormalize performs a normal Normalize if
1238c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there are no negative values.
1239602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
124046a369d839971ab627bdb31a93d8bd63e81b65a3anthony        CalcKernelMetaData(kernel);  /* the other kernel meta-data */
124146a369d839971ab627bdb31a93d8bd63e81b65a3anthony        ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1242cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
1243c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* rotate the 1D kernel by given angle */
1244501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        RotateKernelInfo(kernel, args->xi );
1245602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
1246602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
1247602ab9b30b644a78a4057da93d838a77391ec0acanthony    case CometKernel:
1248602ab9b30b644a78a4057da93d838a77391ec0acanthony      { double
12499eb4f74649b23c053b308ce1152dce51239450baanthony          sigma = fabs(args->sigma),
12509eb4f74649b23c053b308ce1152dce51239450baanthony          A;
1251602ab9b30b644a78a4057da93d838a77391ec0acanthony
1252602ab9b30b644a78a4057da93d838a77391ec0acanthony        if ( args->rho < 1.0 )
1253e1cf9465864144e8b8043d522906c1e47bbf6192anthony          kernel->width = (GetOptimalKernelWidth1D(args->rho,sigma)-1)/2+1;
1254602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
1255bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = (size_t)args->rho;
1256c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->x = kernel->y = 0;
1257602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->height = 1;
1258c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->negative_range = kernel->positive_range = 0.0;
1259e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1260e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1261e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1262d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
126383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1264602ab9b30b644a78a4057da93d838a77391ec0acanthony
1265c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* A comet blur is half a 1D gaussian curve, so that the object is
1266602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** blurred in one direction only.  This may not be quite the right
12673dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        ** curve to use so may change in the future. The function must be
12683dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        ** normalised after generation, which also resolves any clipping.
1269c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
1270c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** As we are normalizing and not subtracting gaussians,
1271c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there is no need for a divisor in the gaussian formula
1272c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
127343c4925e5305a26e48d68f7893e94f55d0831c39anthony        ** It is less comples
1274602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
12759eb4f74649b23c053b308ce1152dce51239450baanthony        if ( sigma > MagickEpsilon )
12769eb4f74649b23c053b308ce1152dce51239450baanthony          {
1277602ab9b30b644a78a4057da93d838a77391ec0acanthony#if 1
1278602ab9b30b644a78a4057da93d838a77391ec0acanthony#define KernelRank 3
1279bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            v = (ssize_t) kernel->width*KernelRank; /* start/end points */
12809eb4f74649b23c053b308ce1152dce51239450baanthony            (void) ResetMagickMemory(kernel->values,0, (size_t)
12819eb4f74649b23c053b308ce1152dce51239450baanthony                          kernel->width*sizeof(double));
12829eb4f74649b23c053b308ce1152dce51239450baanthony            sigma *= KernelRank;            /* simplify the loop expression */
12839eb4f74649b23c053b308ce1152dce51239450baanthony            A = 1.0/(2.0*sigma*sigma);
12849eb4f74649b23c053b308ce1152dce51239450baanthony            /* B = 1.0/(MagickSQ2PI*sigma); */
12859eb4f74649b23c053b308ce1152dce51239450baanthony            for ( u=0; u < v; u++) {
12869eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->values[u/KernelRank] +=
12879eb4f74649b23c053b308ce1152dce51239450baanthony                  exp(-((double)(u*u))*A);
12889eb4f74649b23c053b308ce1152dce51239450baanthony              /*  exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
12899eb4f74649b23c053b308ce1152dce51239450baanthony            }
1290bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (i=0; i < (ssize_t) kernel->width; i++)
12919eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->positive_range += kernel->values[i];
1292602ab9b30b644a78a4057da93d838a77391ec0acanthony#else
12939eb4f74649b23c053b308ce1152dce51239450baanthony            A = 1.0/(2.0*sigma*sigma);     /* simplify the loop expression */
12949eb4f74649b23c053b308ce1152dce51239450baanthony            /* B = 1.0/(MagickSQ2PI*sigma); */
1295bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for ( i=0; i < (ssize_t) kernel->width; i++)
12969eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->positive_range +=
129753f576d128f0bb744a82e7f9c0d8f05b2923972canthony                kernel->values[i] = exp(-((double)(i*i))*A);
12989eb4f74649b23c053b308ce1152dce51239450baanthony                /* exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
1299602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
13009eb4f74649b23c053b308ce1152dce51239450baanthony          }
13019eb4f74649b23c053b308ce1152dce51239450baanthony        else /* special case - generate a unity kernel */
13029eb4f74649b23c053b308ce1152dce51239450baanthony          { (void) ResetMagickMemory(kernel->values,0, (size_t)
13039eb4f74649b23c053b308ce1152dce51239450baanthony                         kernel->width*kernel->height*sizeof(double));
13049eb4f74649b23c053b308ce1152dce51239450baanthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
13059eb4f74649b23c053b308ce1152dce51239450baanthony            kernel->positive_range = 1.0;
13069eb4f74649b23c053b308ce1152dce51239450baanthony          }
130746a369d839971ab627bdb31a93d8bd63e81b65a3anthony
130846a369d839971ab627bdb31a93d8bd63e81b65a3anthony        kernel->minimum = 0.0;
1309c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->maximum = kernel->values[0];
131046a369d839971ab627bdb31a93d8bd63e81b65a3anthony        kernel->negative_range = 0.0;
1311602ab9b30b644a78a4057da93d838a77391ec0acanthony
1312999bb2c20aa9d42875bb5adba44951988d4ae354anthony        ScaleKernelInfo(kernel, 1.0, NormalizeValue); /* Normalize */
1313999bb2c20aa9d42875bb5adba44951988d4ae354anthony        RotateKernelInfo(kernel, args->xi); /* Rotate by angle */
1314602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
1315602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
131640ca0b982379d4ab2716435a46603d56b5b218b1anthony    case BinomialKernel:
131740ca0b982379d4ab2716435a46603d56b5b218b1anthony      {
131840ca0b982379d4ab2716435a46603d56b5b218b1anthony        size_t
131940ca0b982379d4ab2716435a46603d56b5b218b1anthony          order_f;
132040ca0b982379d4ab2716435a46603d56b5b218b1anthony
132140ca0b982379d4ab2716435a46603d56b5b218b1anthony        if (args->rho < 1.0)
132240ca0b982379d4ab2716435a46603d56b5b218b1anthony          kernel->width = kernel->height = 3;  /* default radius = 1 */
132340ca0b982379d4ab2716435a46603d56b5b218b1anthony        else
132440ca0b982379d4ab2716435a46603d56b5b218b1anthony          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
132540ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
132640ca0b982379d4ab2716435a46603d56b5b218b1anthony
132740ca0b982379d4ab2716435a46603d56b5b218b1anthony        order_f = fact(kernel->width-1);
132840ca0b982379d4ab2716435a46603d56b5b218b1anthony
1329e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1330e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1331e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1332d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
133340ca0b982379d4ab2716435a46603d56b5b218b1anthony          return(DestroyKernelInfo(kernel));
133440ca0b982379d4ab2716435a46603d56b5b218b1anthony
133540ca0b982379d4ab2716435a46603d56b5b218b1anthony        /* set all kernel values within diamond area to scale given */
133640ca0b982379d4ab2716435a46603d56b5b218b1anthony        for ( i=0, v=0; v < (ssize_t)kernel->height; v++)
133740ca0b982379d4ab2716435a46603d56b5b218b1anthony          { size_t
1338f13c594b1d9509e479fd845c3b8a5bb1351c32eacristy              alpha = order_f / ( fact((size_t) v) * fact(kernel->height-v-1) );
133940ca0b982379d4ab2716435a46603d56b5b218b1anthony            for ( u=0; u < (ssize_t)kernel->width; u++, i++)
134040ca0b982379d4ab2716435a46603d56b5b218b1anthony              kernel->positive_range += kernel->values[i] = (double)
1341f13c594b1d9509e479fd845c3b8a5bb1351c32eacristy                (alpha * order_f / ( fact((size_t) u) * fact(kernel->height-u-1) ));
134240ca0b982379d4ab2716435a46603d56b5b218b1anthony          }
134340ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->minimum = 1.0;
134440ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->maximum = kernel->values[kernel->x+kernel->y*kernel->width];
134540ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->negative_range = 0.0;
134640ca0b982379d4ab2716435a46603d56b5b218b1anthony        break;
134740ca0b982379d4ab2716435a46603d56b5b218b1anthony      }
1348c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1349529482f4b494010a13338a74446c510712f670b3anthony    /*
1350529482f4b494010a13338a74446c510712f670b3anthony      Convolution Kernels - Well Known Named Constant Kernels
1351529482f4b494010a13338a74446c510712f670b3anthony    */
13523c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    case LaplacianKernel:
1353e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony      { switch ( (int) args->rho ) {
13543dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony          case 0:
13559eb4f74649b23c053b308ce1152dce51239450baanthony          default: /* laplacian square filter -- default */
1356c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: -1,-1,-1  -1,8,-1  -1,-1,-1");
13573dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony            break;
13589eb4f74649b23c053b308ce1152dce51239450baanthony          case 1:  /* laplacian diamond filter */
1359c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: 0,-1,0  -1,4,-1  0,-1,0");
13603c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13613c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          case 2:
13629eb4f74649b23c053b308ce1152dce51239450baanthony            kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
13639eb4f74649b23c053b308ce1152dce51239450baanthony            break;
13649eb4f74649b23c053b308ce1152dce51239450baanthony          case 3:
1365c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  1,-2,1");
13663c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13679eb4f74649b23c053b308ce1152dce51239450baanthony          case 5:   /* a 5x5 laplacian */
13683c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            kernel=ParseKernelArray(
13699eb4f74649b23c053b308ce1152dce51239450baanthony              "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");
13703c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13719eb4f74649b23c053b308ce1152dce51239450baanthony          case 7:   /* a 7x7 laplacian */
13723c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            kernel=ParseKernelArray(
1373c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              "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" );
13743c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
1375501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          case 15:  /* a 5x5 LoG (sigma approx 1.4) */
13769eb4f74649b23c053b308ce1152dce51239450baanthony            kernel=ParseKernelArray(
13779eb4f74649b23c053b308ce1152dce51239450baanthony              "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");
13789eb4f74649b23c053b308ce1152dce51239450baanthony            break;
1379501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          case 19:  /* a 9x9 LoG (sigma approx 1.4) */
138043c4925e5305a26e48d68f7893e94f55d0831c39anthony            /* http://www.cscjournals.org/csc/manuscript/Journals/IJIP/volume3/Issue1/IJIP-15.pdf */
138143c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel=ParseKernelArray(
1382bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony              "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");
138343c4925e5305a26e48d68f7893e94f55d0831c39anthony            break;
13843c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
13853c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        if (kernel == (KernelInfo *) NULL)
13863c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          return(kernel);
13873c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        kernel->type = type;
13883c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        break;
13893c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      }
1390c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SobelKernel:
1391cceb6f05c2016ea3854b29e7770ec5ff49034ecfanthony      { /* Simple Sobel Kernel */
1392dcc2a474c98415e565434fe52f630acba36fa0c1anthony        kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1393dcc2a474c98415e565434fe52f630acba36fa0c1anthony        if (kernel == (KernelInfo *) NULL)
1394dcc2a474c98415e565434fe52f630acba36fa0c1anthony          return(kernel);
1395dcc2a474c98415e565434fe52f630acba36fa0c1anthony        kernel->type = type;
1396dcc2a474c98415e565434fe52f630acba36fa0c1anthony        RotateKernelInfo(kernel, args->rho);
1397dcc2a474c98415e565434fe52f630acba36fa0c1anthony        break;
1398dcc2a474c98415e565434fe52f630acba36fa0c1anthony      }
1399c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RobertsKernel:
1400c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1401501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 0,0,0  1,-1,0  0,0,0");
1402c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
1403c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1404c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
140546a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
1406c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1407c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
1408c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PrewittKernel:
1409c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1410501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 1,0,-1  1,0,-1  1,0,-1");
1411c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
1412c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1413c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
141446a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
1415c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1416c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
1417c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CompassKernel:
1418c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1419501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 1,1,-1  1,-2,-1  1,1,-1");
1420c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
1421c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1422c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
142346a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
1424c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1425c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
14269eb4f74649b23c053b308ce1152dce51239450baanthony    case KirschKernel:
14279eb4f74649b23c053b308ce1152dce51239450baanthony      {
1428501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 5,-3,-3  5,0,-3  5,-3,-3");
14299eb4f74649b23c053b308ce1152dce51239450baanthony        if (kernel == (KernelInfo *) NULL)
14309eb4f74649b23c053b308ce1152dce51239450baanthony          return(kernel);
14319eb4f74649b23c053b308ce1152dce51239450baanthony        kernel->type = type;
143246a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
14339eb4f74649b23c053b308ce1152dce51239450baanthony        break;
14349eb4f74649b23c053b308ce1152dce51239450baanthony      }
1435e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony    case FreiChenKernel:
1436501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      /* Direction is set to be left to right positive */
1437501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      /* http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf -- RIGHT? */
1438501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      /* http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf -- WRONG? */
14391dd091ae3bc17edc26c16cc47f436a24bd48412aanthony      { switch ( (int) args->rho ) {
1440e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony          default:
1441c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          case 0:
1442501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1443c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            if (kernel == (KernelInfo *) NULL)
1444c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony              return(kernel);
1445ef33d9f66f955c1f6f5f7105e164cc2d5c5e2a41anthony            kernel->type = type;
1446d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[3] = +(MagickRealType) MagickSQ2;
1447d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[5] = -(MagickRealType) MagickSQ2;
1448c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
1449c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            break;
1450c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 2:
1451c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel=ParseKernelArray("3: 1,2,0  2,0,-2  0,-2,-1");
1452c40ac1e79923a1516075ba1197ae4ed90244af9banthony            if (kernel == (KernelInfo *) NULL)
1453c40ac1e79923a1516075ba1197ae4ed90244af9banthony              return(kernel);
1454c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel->type = type;
1455d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[1] = kernel->values[3]= +(MagickRealType) MagickSQ2;
1456d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[5] = kernel->values[7]= -(MagickRealType) MagickSQ2;
1457c40ac1e79923a1516075ba1197ae4ed90244af9banthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
1458d4e3ffa7f64077da0f32c2d8a599737ee8d115ddcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1459c40ac1e79923a1516075ba1197ae4ed90244af9banthony            break;
1460c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 10:
1461c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel=AcquireKernelInfo("FreiChen:11;FreiChen:12;FreiChen:13;FreiChen:14;FreiChen:15;FreiChen:16;FreiChen:17;FreiChen:18;FreiChen:19");
1462c40ac1e79923a1516075ba1197ae4ed90244af9banthony            if (kernel == (KernelInfo *) NULL)
1463c40ac1e79923a1516075ba1197ae4ed90244af9banthony              return(kernel);
1464c40ac1e79923a1516075ba1197ae4ed90244af9banthony            break;
1465e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony          case 1:
1466c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 11:
1467501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1468e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1469e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1470c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1471d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[3] = +(MagickRealType) MagickSQ2;
1472d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[5] = -(MagickRealType) MagickSQ2;
1473e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
147455a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1475e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1476c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 12:
1477501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,2,1  0,0,0  1,2,1");
1478e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1479e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1480c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1481d9123437a79f9d56a3e8c32aa912ae3ae1d47337cristy            kernel->values[1] = +(MagickRealType) MagickSQ2;
1482d9123437a79f9d56a3e8c32aa912ae3ae1d47337cristy            kernel->values[7] = +(MagickRealType) MagickSQ2;
1483e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);
148455a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1485e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1486c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 13:
1487501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 2,-1,0  -1,0,1  0,1,-2");
1488e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1489e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1490c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1491d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[0] = +(MagickRealType) MagickSQ2;
1492d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[8] = -(MagickRealType) MagickSQ2;
1493e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);
149455a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1495e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1496c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 14:
14971d5e67090dc7232b35bfcc71b31266c20838defcanthony            kernel=ParseKernelArray("3: 0,1,-2  -1,0,1  2,-1,0");
1498e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1499e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1500c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1501d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[2] = -(MagickRealType) MagickSQ2;
1502d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[6] = +(MagickRealType) MagickSQ2;
1503e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);
150455a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1505e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1506c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 15:
1507501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 0,-1,0  1,0,1  0,-1,0");
1508e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1509e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1510c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1511e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1512e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1513c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 16:
15141d5e67090dc7232b35bfcc71b31266c20838defcanthony            kernel=ParseKernelArray("3: 1,0,-1  0,0,0  -1,0,1");
1515e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1516e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1517c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1518e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1519e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1520c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 17:
1521501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  -1,-2,1");
1522e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1523e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1524c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1525e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1526e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1527c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 18:
1528501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
1529e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1530e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1531c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1532e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1533e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1534c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 19:
1535c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel=ParseKernelArray("3: 1,1,1  1,1,1  1,1,1");
1536e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1537e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1538c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1539e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/3.0, NoValue);
1540e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1541e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony        }
1542b978e458a8e1f210bcb580951cf623687236b2fecristy        if ( fabs(args->sigma) >= MagickEpsilon )
1543c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          /* Rotate by correctly supplied 'angle' */
1544c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          RotateKernelInfo(kernel, args->sigma);
1545c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony        else if ( args->rho > 30.0 || args->rho < -30.0 )
1546c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          /* Rotate by out of bounds 'type' */
1547c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          RotateKernelInfo(kernel, args->rho);
1548e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony        break;
1549e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony      }
1550e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony
1551529482f4b494010a13338a74446c510712f670b3anthony    /*
1552529482f4b494010a13338a74446c510712f670b3anthony      Boolean or Shaped Kernels
1553529482f4b494010a13338a74446c510712f670b3anthony    */
1554c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiamondKernel:
1555602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
1556c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (args->rho < 1.0)
1557c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = kernel->height = 3;  /* default radius = 1 */
1558c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else
1559bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1560bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1561c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1562e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1563e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1564e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1565d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
1566c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(DestroyKernelInfo(kernel));
1567c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1568c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* set all kernel values within diamond area to scale given */
1569bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1570bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
15711d5e67090dc7232b35bfcc71b31266c20838defcanthony            if ( (labs((long) u)+labs((long) v)) <= (long) kernel->x)
1572c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->positive_range += kernel->values[i] = args->sigma;
1573c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            else
1574c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->values[i] = nan;
1575c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
1576c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1577c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
1578c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SquareKernel:
1579c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RectangleKernel:
1580c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      { double
1581c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          scale;
1582602ab9b30b644a78a4057da93d838a77391ec0acanthony        if ( type == SquareKernel )
1583602ab9b30b644a78a4057da93d838a77391ec0acanthony          {
1584602ab9b30b644a78a4057da93d838a77391ec0acanthony            if (args->rho < 1.0)
1585c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony              kernel->width = kernel->height = 3;  /* default radius = 1 */
1586602ab9b30b644a78a4057da93d838a77391ec0acanthony            else
1587bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              kernel->width = kernel->height = (size_t) (2*args->rho+1);
1588bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
15894fd27e21043be809d66c8202e779255e5b660d2danthony            scale = args->sigma;
1590602ab9b30b644a78a4057da93d838a77391ec0acanthony          }
1591602ab9b30b644a78a4057da93d838a77391ec0acanthony        else {
15922be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy            /* NOTE: user defaults set in "AcquireKernelInfo()" */
1593602ab9b30b644a78a4057da93d838a77391ec0acanthony            if ( args->rho < 1.0 || args->sigma < 1.0 )
159483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony              return(DestroyKernelInfo(kernel));    /* invalid args given */
1595bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->width = (size_t)args->rho;
1596bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->height = (size_t)args->sigma;
1597602ab9b30b644a78a4057da93d838a77391ec0acanthony            if ( args->xi  < 0.0 || args->xi  > (double)kernel->width ||
1598602ab9b30b644a78a4057da93d838a77391ec0acanthony                 args->psi < 0.0 || args->psi > (double)kernel->height )
159983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony              return(DestroyKernelInfo(kernel));    /* invalid args given */
1600bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->x = (ssize_t) args->xi;
1601bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->y = (ssize_t) args->psi;
16024fd27e21043be809d66c8202e779255e5b660d2danthony            scale = 1.0;
1603602ab9b30b644a78a4057da93d838a77391ec0acanthony          }
1604e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1605e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1606e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1607d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
160883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1609602ab9b30b644a78a4057da93d838a77391ec0acanthony
16103dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        /* set all kernel values to scale given */
1611eaedf06777741da32408da72c1e512975c600c48cristy        u=(ssize_t) (kernel->width*kernel->height);
1612150989ed67ef9da53141a65e5f3ebdb05dd025abcristy        for ( i=0; i < u; i++)
16134fd27e21043be809d66c8202e779255e5b660d2danthony            kernel->values[i] = scale;
16144fd27e21043be809d66c8202e779255e5b660d2danthony        kernel->minimum = kernel->maximum = scale;   /* a flat shape */
16154fd27e21043be809d66c8202e779255e5b660d2danthony        kernel->positive_range = scale*u;
1616cc6c836da2a53b6023b716e4973090a6714dc3b0anthony        break;
1617602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
16181ef941fea2534a0d20ba7d71307d35040247decbanthony      case OctagonKernel:
16191ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16201ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
1621a9892d898acb81e1ec73106d892855fdc5a69427anthony            kernel->width = kernel->height = 5;  /* default radius = 2 */
16221ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16231ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16241ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16251ef941fea2534a0d20ba7d71307d35040247decbanthony
1626e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1627e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1628e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1629d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
16301ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16311ef941fea2534a0d20ba7d71307d35040247decbanthony
16321ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16331ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16341ef941fea2534a0d20ba7d71307d35040247decbanthony              if ( (labs((long) u)+labs((long) v)) <=
16351ef941fea2534a0d20ba7d71307d35040247decbanthony                        ((long)kernel->x + (long)(kernel->x/2)) )
16361ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->positive_range += kernel->values[i] = args->sigma;
16371ef941fea2534a0d20ba7d71307d35040247decbanthony              else
16381ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->values[i] = nan;
1639a9892d898acb81e1ec73106d892855fdc5a69427anthony          kernel->minimum = kernel->maximum = args->sigma;  /* a flat shape */
16401ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16411ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16421ef941fea2534a0d20ba7d71307d35040247decbanthony      case DiskKernel:
16431ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16441ef941fea2534a0d20ba7d71307d35040247decbanthony          ssize_t
16450bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony            limit = (ssize_t)(args->rho*args->rho);
16461ef941fea2534a0d20ba7d71307d35040247decbanthony
16479bf68d590429a72aa70894f6aea7fc3c94876e54anthony          if (args->rho < 0.4)           /* default radius approx 4.3 */
16489bf68d590429a72aa70894f6aea7fc3c94876e54anthony            kernel->width = kernel->height = 9L, limit = 18L;
16491ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16501ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = (size_t)fabs(args->rho)*2+1;
16511ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16521ef941fea2534a0d20ba7d71307d35040247decbanthony
1653e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1654e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1655e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1656d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
16571ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16581ef941fea2534a0d20ba7d71307d35040247decbanthony
16591ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16601ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16611ef941fea2534a0d20ba7d71307d35040247decbanthony              if ((u*u+v*v) <= limit)
16621ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->positive_range += kernel->values[i] = args->sigma;
16633dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony              else
16643dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony                kernel->values[i] = nan;
16651ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16661ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16671ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16681ef941fea2534a0d20ba7d71307d35040247decbanthony      case PlusKernel:
16691ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16701ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
16711ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 5;  /* default radius 2 */
16721ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16731ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16741ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16751ef941fea2534a0d20ba7d71307d35040247decbanthony
1676e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1677e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1678e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1679d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
16801ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16811ef941fea2534a0d20ba7d71307d35040247decbanthony
16821ef941fea2534a0d20ba7d71307d35040247decbanthony          /* set all kernel values along axises to given scale */
16831ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16841ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16851ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->values[i] = (u == 0 || v == 0) ? args->sigma : nan;
16861ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16871ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
16881ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16891ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16901ef941fea2534a0d20ba7d71307d35040247decbanthony      case CrossKernel:
16911ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16921ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
16931ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 5;  /* default radius 2 */
16941ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16951ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16961ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16971ef941fea2534a0d20ba7d71307d35040247decbanthony
1698e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1699e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1700e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1701d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
17021ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
17031ef941fea2534a0d20ba7d71307d35040247decbanthony
17041ef941fea2534a0d20ba7d71307d35040247decbanthony          /* set all kernel values along axises to given scale */
17051ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
17061ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
17071ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->values[i] = (u == v || u == -v) ? args->sigma : nan;
17081ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
17091ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
17101ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17111ef941fea2534a0d20ba7d71307d35040247decbanthony        }
1712529482f4b494010a13338a74446c510712f670b3anthony      /*
1713529482f4b494010a13338a74446c510712f670b3anthony        HitAndMiss Kernels
1714529482f4b494010a13338a74446c510712f670b3anthony      */
17151ef941fea2534a0d20ba7d71307d35040247decbanthony      case RingKernel:
17161ef941fea2534a0d20ba7d71307d35040247decbanthony      case PeaksKernel:
17171ef941fea2534a0d20ba7d71307d35040247decbanthony        {
17181ef941fea2534a0d20ba7d71307d35040247decbanthony          ssize_t
17191ef941fea2534a0d20ba7d71307d35040247decbanthony            limit1,
17201ef941fea2534a0d20ba7d71307d35040247decbanthony            limit2,
17211ef941fea2534a0d20ba7d71307d35040247decbanthony            scale;
17221ef941fea2534a0d20ba7d71307d35040247decbanthony
17231ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < args->sigma)
17241ef941fea2534a0d20ba7d71307d35040247decbanthony            {
17251ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->width = ((size_t)args->sigma)*2+1;
17261ef941fea2534a0d20ba7d71307d35040247decbanthony              limit1 = (ssize_t)(args->rho*args->rho);
17271ef941fea2534a0d20ba7d71307d35040247decbanthony              limit2 = (ssize_t)(args->sigma*args->sigma);
17283dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony            }
17291ef941fea2534a0d20ba7d71307d35040247decbanthony          else
17301ef941fea2534a0d20ba7d71307d35040247decbanthony            {
17311ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->width = ((size_t)args->rho)*2+1;
17321ef941fea2534a0d20ba7d71307d35040247decbanthony              limit1 = (ssize_t)(args->sigma*args->sigma);
17331ef941fea2534a0d20ba7d71307d35040247decbanthony              limit2 = (ssize_t)(args->rho*args->rho);
17341ef941fea2534a0d20ba7d71307d35040247decbanthony            }
17351ef941fea2534a0d20ba7d71307d35040247decbanthony          if ( limit2 <= 0 )
17361ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = 7L, limit1 = 7L, limit2 = 11L;
17371ef941fea2534a0d20ba7d71307d35040247decbanthony
17381ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->height = kernel->width;
17391ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1740e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1741e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1742e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1743d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
17441ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
17451ef941fea2534a0d20ba7d71307d35040247decbanthony
17461ef941fea2534a0d20ba7d71307d35040247decbanthony          /* set a ring of points of 'scale' ( 0.0 for PeaksKernel ) */
17471ef941fea2534a0d20ba7d71307d35040247decbanthony          scale = (ssize_t) (( type == PeaksKernel) ? 0.0 : args->xi);
17481ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v= -kernel->y; v <= (ssize_t)kernel->y; v++)
17491ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
17501ef941fea2534a0d20ba7d71307d35040247decbanthony              { ssize_t radius=u*u+v*v;
17511ef941fea2534a0d20ba7d71307d35040247decbanthony                if (limit1 < radius && radius <= limit2)
17521ef941fea2534a0d20ba7d71307d35040247decbanthony                  kernel->positive_range += kernel->values[i] = (double) scale;
17531ef941fea2534a0d20ba7d71307d35040247decbanthony                else
17541ef941fea2534a0d20ba7d71307d35040247decbanthony                  kernel->values[i] = nan;
17551ef941fea2534a0d20ba7d71307d35040247decbanthony              }
17561ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = (double) scale;
17571ef941fea2534a0d20ba7d71307d35040247decbanthony          if ( type == PeaksKernel ) {
17581ef941fea2534a0d20ba7d71307d35040247decbanthony            /* set the central point in the middle */
17591ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
17601ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->positive_range = 1.0;
17611ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->maximum = 1.0;
17621ef941fea2534a0d20ba7d71307d35040247decbanthony          }
17631ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
1764c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        }
17651ef941fea2534a0d20ba7d71307d35040247decbanthony      case EdgesKernel:
17661ef941fea2534a0d20ba7d71307d35040247decbanthony        {
1767529482f4b494010a13338a74446c510712f670b3anthony          kernel=AcquireKernelInfo("ThinSE:482");
17681ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
17691ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
17701ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
1771529482f4b494010a13338a74446c510712f670b3anthony          ExpandMirrorKernelInfo(kernel); /* mirror expansion of kernels */
17721ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17731ef941fea2534a0d20ba7d71307d35040247decbanthony        }
17741ef941fea2534a0d20ba7d71307d35040247decbanthony      case CornersKernel:
17751ef941fea2534a0d20ba7d71307d35040247decbanthony        {
1776529482f4b494010a13338a74446c510712f670b3anthony          kernel=AcquireKernelInfo("ThinSE:87");
17771ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
17781ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
17791ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
17801ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(kernel, 90.0); /* Expand 90 degree rotations */
17811ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17821ef941fea2534a0d20ba7d71307d35040247decbanthony        }
1783529482f4b494010a13338a74446c510712f670b3anthony      case DiagonalsKernel:
17841ef941fea2534a0d20ba7d71307d35040247decbanthony        {
17851ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
17861ef941fea2534a0d20ba7d71307d35040247decbanthony            case 0:
17871ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
17881ef941fea2534a0d20ba7d71307d35040247decbanthony              { KernelInfo
17891ef941fea2534a0d20ba7d71307d35040247decbanthony                  *new_kernel;
1790529482f4b494010a13338a74446c510712f670b3anthony                kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
17911ef941fea2534a0d20ba7d71307d35040247decbanthony                if (kernel == (KernelInfo *) NULL)
17921ef941fea2534a0d20ba7d71307d35040247decbanthony                  return(kernel);
17931ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->type = type;
1794529482f4b494010a13338a74446c510712f670b3anthony                new_kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
17951ef941fea2534a0d20ba7d71307d35040247decbanthony                if (new_kernel == (KernelInfo *) NULL)
17961ef941fea2534a0d20ba7d71307d35040247decbanthony                  return(DestroyKernelInfo(kernel));
17971ef941fea2534a0d20ba7d71307d35040247decbanthony                new_kernel->type = type;
17981ef941fea2534a0d20ba7d71307d35040247decbanthony                LastKernelInfo(kernel)->next = new_kernel;
17991ef941fea2534a0d20ba7d71307d35040247decbanthony                ExpandMirrorKernelInfo(kernel);
1800529482f4b494010a13338a74446c510712f670b3anthony                return(kernel);
18011ef941fea2534a0d20ba7d71307d35040247decbanthony              }
18021ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
1803529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
18041ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18051ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
1806529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
18071ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18081ef941fea2534a0d20ba7d71307d35040247decbanthony          }
1809529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
1810529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
1811529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
1812529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
18131ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
18141ef941fea2534a0d20ba7d71307d35040247decbanthony        }
18151ef941fea2534a0d20ba7d71307d35040247decbanthony      case LineEndsKernel:
18161ef941fea2534a0d20ba7d71307d35040247decbanthony        { /* Kernels for finding the end of thin lines */
18171ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
18181ef941fea2534a0d20ba7d71307d35040247decbanthony            case 0:
18191ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
18201ef941fea2534a0d20ba7d71307d35040247decbanthony              /* set of kernels to find all end of lines */
1821529482f4b494010a13338a74446c510712f670b3anthony              return(AcquireKernelInfo("LineEnds:1>;LineEnds:2>"));
18221ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
18231ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel for 4-connected line ends - no rotation */
18241ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,-  0,1,1  0,0,-");
18251ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18261ef941fea2534a0d20ba7d71307d35040247decbanthony          case 2:
18271ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel to add for 8-connected lines - no rotation */
18281ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,0  0,0,1");
18291ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18301ef941fea2534a0d20ba7d71307d35040247decbanthony          case 3:
18311ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel to add for orthogonal line ends - does not find corners */
18321ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,1  0,0,0");
18331ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18341ef941fea2534a0d20ba7d71307d35040247decbanthony          case 4:
18351ef941fea2534a0d20ba7d71307d35040247decbanthony              /* traditional line end - fails on last T end */
18361ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,-  0,0,-");
18371ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18381ef941fea2534a0d20ba7d71307d35040247decbanthony          }
1839529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
1840529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
1841529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
1842529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
18431ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
18441ef941fea2534a0d20ba7d71307d35040247decbanthony        }
18451ef941fea2534a0d20ba7d71307d35040247decbanthony      case LineJunctionsKernel:
18461ef941fea2534a0d20ba7d71307d35040247decbanthony        { /* kernels for finding the junctions of multiple lines */
18471ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
18481ef941fea2534a0d20ba7d71307d35040247decbanthony            case 0:
18491ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
18501ef941fea2534a0d20ba7d71307d35040247decbanthony              /* set of kernels to find all line junctions */
1851529482f4b494010a13338a74446c510712f670b3anthony              return(AcquireKernelInfo("LineJunctions:1@;LineJunctions:2>"));
18521ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
18531ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Y Junction */
18541ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,1  -,1,-  -,1,-");
18551ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18561ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
18571ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Diagonal T Junctions */
18581ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,-  -,1,-  1,-,1");
18591ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18601ef941fea2534a0d20ba7d71307d35040247decbanthony            case 3:
18611ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Orthogonal T Junctions */
18621ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: -,-,-  1,1,1  -,1,-");
18631ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18641ef941fea2534a0d20ba7d71307d35040247decbanthony            case 4:
18651ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Diagonal X Junctions */
18661ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,1  -,1,-  1,-,1");
18671ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18681ef941fea2534a0d20ba7d71307d35040247decbanthony            case 5:
18691ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Orthogonal X Junctions - minimal diamond kernel */
18701ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: -,1,-  1,1,1  -,1,-");
18711ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18721ef941fea2534a0d20ba7d71307d35040247decbanthony          }
1873529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
1874529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
1875529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
1876529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
18771ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
18781ef941fea2534a0d20ba7d71307d35040247decbanthony        }
18791ef941fea2534a0d20ba7d71307d35040247decbanthony      case RidgesKernel:
18801ef941fea2534a0d20ba7d71307d35040247decbanthony        { /* Ridges - Ridge finding kernels */
18811ef941fea2534a0d20ba7d71307d35040247decbanthony          KernelInfo
18821ef941fea2534a0d20ba7d71307d35040247decbanthony            *new_kernel;
18831ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
18841ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
18851ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
18861ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3x1:0,1,0");
18871ef941fea2534a0d20ba7d71307d35040247decbanthony              if (kernel == (KernelInfo *) NULL)
18881ef941fea2534a0d20ba7d71307d35040247decbanthony                return(kernel);
18891ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->type = type;
18901ef941fea2534a0d20ba7d71307d35040247decbanthony              ExpandRotateKernelInfo(kernel, 90.0); /* 2 rotated kernels (symmetrical) */
18911ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18921ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
18931ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("4x1:0,1,1,0");
18941ef941fea2534a0d20ba7d71307d35040247decbanthony              if (kernel == (KernelInfo *) NULL)
18951ef941fea2534a0d20ba7d71307d35040247decbanthony                return(kernel);
18961ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->type = type;
18971ef941fea2534a0d20ba7d71307d35040247decbanthony              ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotated kernels */
18981ef941fea2534a0d20ba7d71307d35040247decbanthony
18991ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Kernels to find a stepped 'thick' line, 4 rotates + mirrors */
19001ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Unfortunatally we can not yet rotate a non-square kernel */
19011ef941fea2534a0d20ba7d71307d35040247decbanthony              /* But then we can't flip a non-symetrical kernel either */
19021ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+1+1:0,1,1,- -,1,1,- -,1,1,0");
19031ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
19041ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
19051ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19061ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19071ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+2+1:0,1,1,- -,1,1,- -,1,1,0");
19081ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
19091ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
19101ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19111ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19121ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+1+1:-,1,1,0 -,1,1,- 0,1,1,-");
19131ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
19141ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
19151ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19161ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19171ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+2+1:-,1,1,0 -,1,1,- 0,1,1,-");
19181ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
19191ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
19201ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19211ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19221ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("3x4+1+1:0,-,- 1,1,1 1,1,1 -,-,0");
19231ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
19241ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
19251ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19261ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19271ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("3x4+1+2:0,-,- 1,1,1 1,1,1 -,-,0");
19281ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
19291ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
19301ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19311ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19321ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("3x4+1+1:-,-,0 1,1,1 1,1,1 0,-,-");
19331ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
19341ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
19351ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19361ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19371ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("3x4+1+2:-,-,0 1,1,1 1,1,1 0,-,-");
193868cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              if (new_kernel == (KernelInfo *) NULL)
193968cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony                return(DestroyKernelInfo(kernel));
194068cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              new_kernel->type = type;
194168cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              LastKernelInfo(kernel)->next = new_kernel;
194268cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              break;
19431ef941fea2534a0d20ba7d71307d35040247decbanthony          }
19441ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
194568cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony        }
19461ef941fea2534a0d20ba7d71307d35040247decbanthony      case ConvexHullKernel:
19471ef941fea2534a0d20ba7d71307d35040247decbanthony        {
19481ef941fea2534a0d20ba7d71307d35040247decbanthony          KernelInfo
19491ef941fea2534a0d20ba7d71307d35040247decbanthony            *new_kernel;
19501ef941fea2534a0d20ba7d71307d35040247decbanthony          /* first set of 8 kernels */
19511ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel=ParseKernelArray("3: 1,1,-  1,0,-  1,-,0");
19521ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
19531ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
19541ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
19551ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(kernel, 90.0);
19561ef941fea2534a0d20ba7d71307d35040247decbanthony          /* append the mirror versions too - no flip function yet */
19571ef941fea2534a0d20ba7d71307d35040247decbanthony          new_kernel=ParseKernelArray("3: 1,1,1  1,0,-  -,-,0");
19581ef941fea2534a0d20ba7d71307d35040247decbanthony          if (new_kernel == (KernelInfo *) NULL)
19591ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
19601ef941fea2534a0d20ba7d71307d35040247decbanthony          new_kernel->type = type;
19611ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(new_kernel, 90.0);
19621ef941fea2534a0d20ba7d71307d35040247decbanthony          LastKernelInfo(kernel)->next = new_kernel;
19631ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
1964694934fa79dd310f727588b1d0a7481fa6170f1danthony        }
19659a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony      case SkeletonKernel:
19669a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony        {
19679a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony          switch ( (int) args->rho ) {
19689a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 1:
19699a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            default:
19709a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* Traditional Skeleton...
19719a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** A cyclically rotated single kernel
19729a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19739a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=AcquireKernelInfo("ThinSE:482");
19749a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19759a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19769a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19779a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandRotateKernelInfo(kernel, 45.0); /* 8 rotations */
19789a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19799a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 2:
19809a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* HIPR Variation of the cyclic skeleton
19819a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** Corners of the traditional method made more forgiving,
19829a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** but the retain the same cyclic order.
19839a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19849a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=AcquireKernelInfo("ThinSE:482; ThinSE:87x90;");
19859a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19869a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19879a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel->next == (KernelInfo *) NULL)
19889a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(DestroyKernelInfo(kernel));
19899a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19909a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->type = type;
19919a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotations of the 2 kernels */
19929a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19939a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 3:
19949a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* Dan Bloomberg Skeleton, from his paper on 3x3 thinning SE's
19959a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** "Connectivity-Preserving Morphological Image Thransformations"
19969a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
19979a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              **   http://www.leptonica.com/papers/conn.pdf
19989a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19999a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=AcquireKernelInfo(
20009a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                            "ThinSE:41; ThinSE:42; ThinSE:43");
20019a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
20029a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
20039a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
20049a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->type = type;
20059a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->next->type = type;
20069a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandMirrorKernelInfo(kernel); /* 12 kernels total */
20079a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
20089a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony           }
20099a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony          break;
20109a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony        }
2011529482f4b494010a13338a74446c510712f670b3anthony      case ThinSEKernel:
2012529482f4b494010a13338a74446c510712f670b3anthony        { /* Special kernels for general thinning, while preserving connections
2013529482f4b494010a13338a74446c510712f670b3anthony          ** "Connectivity-Preserving Morphological Image Thransformations"
2014529482f4b494010a13338a74446c510712f670b3anthony          ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
2015529482f4b494010a13338a74446c510712f670b3anthony          **   http://www.leptonica.com/papers/conn.pdf
2016529482f4b494010a13338a74446c510712f670b3anthony          ** And
2017529482f4b494010a13338a74446c510712f670b3anthony          **   http://tpgit.github.com/Leptonica/ccthin_8c_source.html
2018529482f4b494010a13338a74446c510712f670b3anthony          **
2019529482f4b494010a13338a74446c510712f670b3anthony          ** Note kernels do not specify the origin pixel, allowing them
2020529482f4b494010a13338a74446c510712f670b3anthony          ** to be used for both thickening and thinning operations.
2021529482f4b494010a13338a74446c510712f670b3anthony          */
2022529482f4b494010a13338a74446c510712f670b3anthony          switch ( (int) args->rho ) {
2023529482f4b494010a13338a74446c510712f670b3anthony            /* SE for 4-connected thinning */
2024529482f4b494010a13338a74446c510712f670b3anthony            case 41: /* SE_4_1 */
2025529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  -,-,1");
2026529482f4b494010a13338a74446c510712f670b3anthony              break;
2027529482f4b494010a13338a74446c510712f670b3anthony            case 42: /* SE_4_2 */
2028529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  -,0,-");
2029529482f4b494010a13338a74446c510712f670b3anthony              break;
2030529482f4b494010a13338a74446c510712f670b3anthony            case 43: /* SE_4_3 */
2031529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,-,1");
2032529482f4b494010a13338a74446c510712f670b3anthony              break;
2033529482f4b494010a13338a74446c510712f670b3anthony            case 44: /* SE_4_4 */
2034529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,-");
2035529482f4b494010a13338a74446c510712f670b3anthony              break;
2036529482f4b494010a13338a74446c510712f670b3anthony            case 45: /* SE_4_5 */
2037529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,1  0,-,1  -,0,-");
2038529482f4b494010a13338a74446c510712f670b3anthony              break;
2039529482f4b494010a13338a74446c510712f670b3anthony            case 46: /* SE_4_6 */
2040529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,1");
2041529482f4b494010a13338a74446c510712f670b3anthony              break;
2042529482f4b494010a13338a74446c510712f670b3anthony            case 47: /* SE_4_7 */
2043529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,1  0,-,1  -,0,-");
2044529482f4b494010a13338a74446c510712f670b3anthony              break;
2045529482f4b494010a13338a74446c510712f670b3anthony            case 48: /* SE_4_8 */
2046529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  0,-,1");
2047529482f4b494010a13338a74446c510712f670b3anthony              break;
2048529482f4b494010a13338a74446c510712f670b3anthony            case 49: /* SE_4_9 */
2049529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  -,-,1");
2050529482f4b494010a13338a74446c510712f670b3anthony              break;
2051529482f4b494010a13338a74446c510712f670b3anthony            /* SE for 8-connected thinning - negatives of the above */
2052529482f4b494010a13338a74446c510712f670b3anthony            case 81: /* SE_8_0 */
2053529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  -,1,-");
2054529482f4b494010a13338a74446c510712f670b3anthony              break;
2055529482f4b494010a13338a74446c510712f670b3anthony            case 82: /* SE_8_2 */
2056529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,-,-");
2057529482f4b494010a13338a74446c510712f670b3anthony              break;
2058529482f4b494010a13338a74446c510712f670b3anthony            case 83: /* SE_8_3 */
2059529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  -,1,-");
2060529482f4b494010a13338a74446c510712f670b3anthony              break;
2061529482f4b494010a13338a74446c510712f670b3anthony            case 84: /* SE_8_4 */
2062529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,-");
2063529482f4b494010a13338a74446c510712f670b3anthony              break;
2064529482f4b494010a13338a74446c510712f670b3anthony            case 85: /* SE_8_5 */
2065529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,-");
2066529482f4b494010a13338a74446c510712f670b3anthony              break;
2067529482f4b494010a13338a74446c510712f670b3anthony            case 86: /* SE_8_6 */
2068529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,1");
2069529482f4b494010a13338a74446c510712f670b3anthony              break;
2070529482f4b494010a13338a74446c510712f670b3anthony            case 87: /* SE_8_7 */
2071529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,0,-");
2072529482f4b494010a13338a74446c510712f670b3anthony              break;
2073529482f4b494010a13338a74446c510712f670b3anthony            case 88: /* SE_8_8 */
2074529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,1,-");
2075529482f4b494010a13338a74446c510712f670b3anthony              break;
2076529482f4b494010a13338a74446c510712f670b3anthony            case 89: /* SE_8_9 */
2077529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,1,-  0,-,1  -,1,-");
2078529482f4b494010a13338a74446c510712f670b3anthony              break;
2079529482f4b494010a13338a74446c510712f670b3anthony            /* Special combined SE kernels */
20809a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 423: /* SE_4_2 , SE_4_3 Combined Kernel */
20819a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=ParseKernelArray("3: -,-,1  0,-,-  -,0,-");
20829a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
20839a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 823: /* SE_8_2 , SE_8_3 Combined Kernel */
20849a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=ParseKernelArray("3: -,1,-  -,-,1  0,-,-");
20859a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
2086529482f4b494010a13338a74446c510712f670b3anthony            case 481: /* SE_48_1 - General Connected Corner Kernel */
2087529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,1  0,-,1  0,0,-");
2088529482f4b494010a13338a74446c510712f670b3anthony              break;
2089529482f4b494010a13338a74446c510712f670b3anthony            default:
2090529482f4b494010a13338a74446c510712f670b3anthony            case 482: /* SE_48_2 - General Edge Kernel */
2091529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,1");
2092529482f4b494010a13338a74446c510712f670b3anthony              break;
2093529482f4b494010a13338a74446c510712f670b3anthony          }
2094529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
2095529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
2096529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
2097529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
2098529482f4b494010a13338a74446c510712f670b3anthony          break;
2099529482f4b494010a13338a74446c510712f670b3anthony        }
2100529482f4b494010a13338a74446c510712f670b3anthony      /*
2101529482f4b494010a13338a74446c510712f670b3anthony        Distance Measuring Kernels
2102529482f4b494010a13338a74446c510712f670b3anthony      */
21031ef941fea2534a0d20ba7d71307d35040247decbanthony      case ChebyshevKernel:
21041ef941fea2534a0d20ba7d71307d35040247decbanthony        {
21051ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
21061ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 3;  /* default radius = 1 */
21071ef941fea2534a0d20ba7d71307d35040247decbanthony          else
21081ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
21091ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
21101ef941fea2534a0d20ba7d71307d35040247decbanthony
2111e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
2112e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
2113e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
2114d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
21151ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
21161ef941fea2534a0d20ba7d71307d35040247decbanthony
21171ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
21181ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
21191ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->positive_range += ( kernel->values[i] =
21201ef941fea2534a0d20ba7d71307d35040247decbanthony                  args->sigma*MagickMax(fabs((double)u),fabs((double)v)) );
21211ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->maximum = kernel->values[0];
21221ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
2123c40ac1e79923a1516075ba1197ae4ed90244af9banthony        }
21241ef941fea2534a0d20ba7d71307d35040247decbanthony      case ManhattanKernel:
21251ef941fea2534a0d20ba7d71307d35040247decbanthony        {
21261ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
21271ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 3;  /* default radius = 1 */
21281ef941fea2534a0d20ba7d71307d35040247decbanthony          else
21291ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
21301ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
21311ef941fea2534a0d20ba7d71307d35040247decbanthony
2132e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
2133e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
2134e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
2135d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
21361ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
21371ef941fea2534a0d20ba7d71307d35040247decbanthony
21381ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
21391ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
21401ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->positive_range += ( kernel->values[i] =
21411ef941fea2534a0d20ba7d71307d35040247decbanthony                  args->sigma*(labs((long) u)+labs((long) v)) );
21421ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->maximum = kernel->values[0];
21431ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
2144c40ac1e79923a1516075ba1197ae4ed90244af9banthony        }
21451ef941fea2534a0d20ba7d71307d35040247decbanthony      case OctagonalKernel:
2146602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
21471ef941fea2534a0d20ba7d71307d35040247decbanthony        if (args->rho < 2.0)
21481ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->width = kernel->height = 5;  /* default/minimum radius = 2 */
2149602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
2150bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2151bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2152602ab9b30b644a78a4057da93d838a77391ec0acanthony
2153e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
2154e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
2155e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
2156d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
215783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
2158602ab9b30b644a78a4057da93d838a77391ec0acanthony
2159bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2160bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
21611ef941fea2534a0d20ba7d71307d35040247decbanthony            {
21621ef941fea2534a0d20ba7d71307d35040247decbanthony              double
21631ef941fea2534a0d20ba7d71307d35040247decbanthony                r1 = MagickMax(fabs((double)u),fabs((double)v)),
21641ef941fea2534a0d20ba7d71307d35040247decbanthony                r2 = floor((double)(labs((long)u)+labs((long)v)+1)/1.5);
21651ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->positive_range += kernel->values[i] =
21661ef941fea2534a0d20ba7d71307d35040247decbanthony                        args->sigma*MagickMax(r1,r2);
21671ef941fea2534a0d20ba7d71307d35040247decbanthony            }
2168c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->maximum = kernel->values[0];
2169602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
2170602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2171602ab9b30b644a78a4057da93d838a77391ec0acanthony    case EuclideanKernel:
2172602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
2173602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (args->rho < 1.0)
2174c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony          kernel->width = kernel->height = 3;  /* default radius = 1 */
2175602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
21761ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2177bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2178602ab9b30b644a78a4057da93d838a77391ec0acanthony
2179e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
2180e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
2181e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
2182d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
218383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
2184602ab9b30b644a78a4057da93d838a77391ec0acanthony
2185bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2186bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2187c99304fe3c8d9c617da792b40b57c118bb1249afcristy            kernel->positive_range += ( kernel->values[i] =
21880ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony              args->sigma*sqrt((double)(u*u+v*v)) );
2189c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->maximum = kernel->values[0];
2190602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
2191602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2192602ab9b30b644a78a4057da93d838a77391ec0acanthony    default:
2193c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
2194529482f4b494010a13338a74446c510712f670b3anthony        /* No-Op Kernel - Basically just a single pixel on its own */
21953ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony        kernel=ParseKernelArray("1:1");
2196c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
2197c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
2198529482f4b494010a13338a74446c510712f670b3anthony        kernel->type = UndefinedKernel;
2199c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
2200c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
2201602ab9b30b644a78a4057da93d838a77391ec0acanthony      break;
2202602ab9b30b644a78a4057da93d838a77391ec0acanthony  }
2203602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
2204602ab9b30b644a78a4057da93d838a77391ec0acanthony}
2205c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony
2206602ab9b30b644a78a4057da93d838a77391ec0acanthony/*
2207602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2208602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2209602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2210602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
22116771f1e8987fa49f52d4176281a2e8524b8e31cbcristy%     C l o n e K e r n e l I n f o                                           %
22124fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
22134fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
22144fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
22154fd27e21043be809d66c8202e779255e5b660d2danthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22164fd27e21043be809d66c8202e779255e5b660d2danthony%
22171b2bc0a7da432e6e1cc0480280402df213faa940anthony%  CloneKernelInfo() creates a new clone of the given Kernel List so that its
22181b2bc0a7da432e6e1cc0480280402df213faa940anthony%  can be modified without effecting the original.  The cloned kernel should
22190ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony%  be destroyed using DestoryKernelInfo() when no longer needed.
22207a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
2221e636559dfadfdb115cc93f223315052a1ee89238cristy%  The format of the CloneKernelInfo method is:
22224fd27e21043be809d66c8202e779255e5b660d2danthony%
2223930be614b4595b97cd79ee864a394796740f76adanthony%      KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
22244fd27e21043be809d66c8202e779255e5b660d2danthony%
22254fd27e21043be809d66c8202e779255e5b660d2danthony%  A description of each parameter follows:
22264fd27e21043be809d66c8202e779255e5b660d2danthony%
22274fd27e21043be809d66c8202e779255e5b660d2danthony%    o kernel: the Morphology/Convolution kernel to be cloned
22284fd27e21043be809d66c8202e779255e5b660d2danthony%
22294fd27e21043be809d66c8202e779255e5b660d2danthony*/
2230ef656913b0b30d713ae94c82c47693c9dc69c9f4cristyMagickExport KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
22314fd27e21043be809d66c8202e779255e5b660d2danthony{
2232bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
22334fd27e21043be809d66c8202e779255e5b660d2danthony    i;
22344fd27e21043be809d66c8202e779255e5b660d2danthony
223519eb64195ef744f61293025952df1e5e6de66524cristy  KernelInfo
22367a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *new_kernel;
22374fd27e21043be809d66c8202e779255e5b660d2danthony
22384fd27e21043be809d66c8202e779255e5b660d2danthony  assert(kernel != (KernelInfo *) NULL);
22397a01dcf50ce12cb2a789bedff51e9345f022432eanthony  new_kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
22407a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if (new_kernel == (KernelInfo *) NULL)
22417a01dcf50ce12cb2a789bedff51e9345f022432eanthony    return(new_kernel);
22427a01dcf50ce12cb2a789bedff51e9345f022432eanthony  *new_kernel=(*kernel); /* copy values in structure */
22437a01dcf50ce12cb2a789bedff51e9345f022432eanthony
22447a01dcf50ce12cb2a789bedff51e9345f022432eanthony  /* replace the values with a copy of the values */
2245e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy  new_kernel->values=(MagickRealType *) MagickAssumeAligned(
2246e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy    AcquireAlignedMemory(kernel->width,kernel->height*sizeof(*kernel->values)));
2247d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy  if (new_kernel->values == (MagickRealType *) NULL)
22487a01dcf50ce12cb2a789bedff51e9345f022432eanthony    return(DestroyKernelInfo(new_kernel));
2249bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
22507a01dcf50ce12cb2a789bedff51e9345f022432eanthony    new_kernel->values[i]=kernel->values[i];
22511b2bc0a7da432e6e1cc0480280402df213faa940anthony
22521b2bc0a7da432e6e1cc0480280402df213faa940anthony  /* Also clone the next kernel in the kernel list */
22531b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL ) {
22541b2bc0a7da432e6e1cc0480280402df213faa940anthony    new_kernel->next = CloneKernelInfo(kernel->next);
22551b2bc0a7da432e6e1cc0480280402df213faa940anthony    if ( new_kernel->next == (KernelInfo *) NULL )
22561b2bc0a7da432e6e1cc0480280402df213faa940anthony      return(DestroyKernelInfo(new_kernel));
22571b2bc0a7da432e6e1cc0480280402df213faa940anthony  }
22581b2bc0a7da432e6e1cc0480280402df213faa940anthony
22597a01dcf50ce12cb2a789bedff51e9345f022432eanthony  return(new_kernel);
22604fd27e21043be809d66c8202e779255e5b660d2danthony}
22614fd27e21043be809d66c8202e779255e5b660d2danthony
22624fd27e21043be809d66c8202e779255e5b660d2danthony/*
22634fd27e21043be809d66c8202e779255e5b660d2danthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22644fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
22654fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
22664fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
226783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%     D e s t r o y K e r n e l I n f o                                       %
2268602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2269602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2270602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2271602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2272602ab9b30b644a78a4057da93d838a77391ec0acanthony%
227383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  DestroyKernelInfo() frees the memory used by a Convolution/Morphology
227483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  kernel.
2275602ab9b30b644a78a4057da93d838a77391ec0acanthony%
227683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  The format of the DestroyKernelInfo method is:
2277602ab9b30b644a78a4057da93d838a77391ec0acanthony%
227883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%      KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
2279602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2280602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
2281602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2282602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel: the Morphology/Convolution kernel to be destroyed
2283602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2284602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
228583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthonyMagickExport KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
2286602ab9b30b644a78a4057da93d838a77391ec0acanthony{
22872be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  assert(kernel != (KernelInfo *) NULL);
22887a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if ( kernel->next != (KernelInfo *) NULL )
22899f752c092332bf2c4e599ea49e9422c13f3fb11bcristy    kernel->next=DestroyKernelInfo(kernel->next);
2290d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy  kernel->values=(MagickRealType *) RelinquishAlignedMemory(kernel->values);
22919f752c092332bf2c4e599ea49e9422c13f3fb11bcristy  kernel=(KernelInfo *) RelinquishMagickMemory(kernel);
2292602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
2293602ab9b30b644a78a4057da93d838a77391ec0acanthony}
2294c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony
2295c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony/*
2296c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2297c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2298c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2299c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2300ce0fd95b65b90e1dc307600bb14b346b777e8137anthony+     E x p a n d M i r r o r K e r n e l I n f o                             %
23013c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
23023c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
23033c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
23043c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
23053c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2306bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  ExpandMirrorKernelInfo() takes a single kernel, and expands it into a
2307bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  sequence of 90-degree rotated kernels but providing a reflected 180
2308bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  rotatation, before the -/+ 90-degree rotations.
2309bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2310bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  This special rotation order produces a better, more symetrical thinning of
2311bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  objects.
2312bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2313bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The format of the ExpandMirrorKernelInfo method is:
2314bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2315bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%      void ExpandMirrorKernelInfo(KernelInfo *kernel)
2316bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2317bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  A description of each parameter follows:
2318bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2319bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    o kernel: the Morphology/Convolution kernel
2320bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2321bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% This function is only internel to this module, as it is not finalized,
2322bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% especially with regard to non-orthogonal angles, and rotation of larger
2323bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% 2D kernels.
2324bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony*/
2325bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2326bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony#if 0
2327bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void FlopKernelInfo(KernelInfo *kernel)
2328bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    { /* Do a Flop by reversing each row. */
2329bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      size_t
2330bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        y;
2331bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      register ssize_t
2332bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        x,r;
2333bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      register double
2334bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        *k,t;
2335bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2336bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      for ( y=0, k=kernel->values; y < kernel->height; y++, k+=kernel->width)
2337bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        for ( x=0, r=kernel->width-1; x<kernel->width/2; x++, r--)
2338bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony          t=k[x],  k[x]=k[r],  k[r]=t;
2339bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2340bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      kernel->x = kernel->width - kernel->x - 1;
2341bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      angle = fmod(angle+180.0, 360.0);
2342bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    }
2343bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony#endif
2344bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2345bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void ExpandMirrorKernelInfo(KernelInfo *kernel)
2346bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony{
2347bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  KernelInfo
2348bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    *clone,
2349bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    *last;
2350bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2351bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = kernel;
2352bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2353bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2354bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 180);   /* flip */
2355bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2356bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = clone;
2357bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2358bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2359bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 90);   /* transpose */
2360bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2361bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = clone;
2362bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2363bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2364bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 180);  /* flop */
2365bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2366bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2367bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  return;
2368bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony}
2369bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2370bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony/*
2371bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2372bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2373bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2374bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2375ce0fd95b65b90e1dc307600bb14b346b777e8137anthony+     E x p a n d R o t a t e K e r n e l I n f o                             %
2376bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2377bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2378bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2379bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2380bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2381bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  ExpandRotateKernelInfo() takes a kernel list, and expands it by rotating
2382529482f4b494010a13338a74446c510712f670b3anthony%  incrementally by the angle given, until the kernel repeats.
23833c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23843c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  WARNING: 45 degree rotations only works for 3x3 kernels.
23853c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  While 90 degree roatations only works for linear and square kernels
23863c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2387bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The format of the ExpandRotateKernelInfo method is:
23883c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2389bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%      void ExpandRotateKernelInfo(KernelInfo *kernel, double angle)
23903c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23913c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  A description of each parameter follows:
23923c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23933c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    o kernel: the Morphology/Convolution kernel
23943c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23953c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    o angle: angle to rotate in degrees
23963c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23973c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% This function is only internel to this module, as it is not finalized,
23983c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% especially with regard to non-orthogonal angles, and rotation of larger
23993c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% 2D kernels.
24003c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony*/
240147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
240247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony/* Internal Routine - Return true if two kernels are the same */
240347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthonystatic MagickBooleanType SameKernelInfo(const KernelInfo *kernel1,
240447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony     const KernelInfo *kernel2)
240547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony{
2406bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register size_t
240747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    i;
24081d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
24091d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  /* check size and origin location */
24101d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  if (    kernel1->width != kernel2->width
24111d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony       || kernel1->height != kernel2->height
24121d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony       || kernel1->x != kernel2->x
24131d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony       || kernel1->y != kernel2->y )
241447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    return MagickFalse;
24151d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
24161d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  /* check actual kernel values */
241747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  for (i=0; i < (kernel1->width*kernel1->height); i++) {
2418f0a92fd8deb68d411304359906b12679b675691fglennrp    /* Test for Nan equivalence */
241947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    if ( IsNan(kernel1->values[i]) && !IsNan(kernel2->values[i]) )
242047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      return MagickFalse;
242147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    if ( IsNan(kernel2->values[i]) && !IsNan(kernel1->values[i]) )
242247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      return MagickFalse;
2423f0a92fd8deb68d411304359906b12679b675691fglennrp    /* Test actual values are equivalent */
2424b978e458a8e1f210bcb580951cf623687236b2fecristy    if ( fabs(kernel1->values[i] - kernel2->values[i]) >= MagickEpsilon )
242547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      return MagickFalse;
242647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  }
24271d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
242847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  return MagickTrue;
242947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony}
243047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
2431bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void ExpandRotateKernelInfo(KernelInfo *kernel, const double angle)
24323c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony{
24333c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  KernelInfo
243484d9b5596c0900609dea18795861e2b0936b21c6cristy    *clone,
24353c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    *last;
2436a9a61ad96c5112acd968f97b689bd42ca392d70bcristy
24373c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  last = kernel;
243847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  while(1) {
243984d9b5596c0900609dea18795861e2b0936b21c6cristy    clone = CloneKernelInfo(last);
244084d9b5596c0900609dea18795861e2b0936b21c6cristy    RotateKernelInfo(clone, angle);
244184d9b5596c0900609dea18795861e2b0936b21c6cristy    if ( SameKernelInfo(kernel, clone) == MagickTrue )
244247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
2443bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    LastKernelInfo(last)->next = clone;
244484d9b5596c0900609dea18795861e2b0936b21c6cristy    last = clone;
24453c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  }
2446bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = DestroyKernelInfo(clone); /* kernel has repeated - junk the clone */
244747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  return;
24483c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony}
24493c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony
24503c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony/*
24513c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
24523c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
24533c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
24543c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
245546a369d839971ab627bdb31a93d8bd63e81b65a3anthony+     C a l c M e t a K e r n a l I n f o                                     %
245646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
245746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
245846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
245946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
246046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
246146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  CalcKernelMetaData() recalculate the KernelInfo meta-data of this kernel only,
2462dcabf6d5fddb35c0eedd7a7638c102f16838c4e6glennrp%  using the kernel values.  This should only ne used if it is not possible to
246346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  calculate that meta-data in some easier way.
246446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
246546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  It is important that the meta-data is correct before ScaleKernelInfo() is
246646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  used to perform kernel normalization.
246746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
246846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The format of the CalcKernelMetaData method is:
246946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
247046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      void CalcKernelMetaData(KernelInfo *kernel, const double scale )
247146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
247246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  A description of each parameter follows:
247346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
247446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o kernel: the Morphology/Convolution kernel to modify
247546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
247646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  WARNING: Minimum and Maximum values are assumed to include zero, even if
247746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  zero is not part of the kernel (as in Gaussian Derived kernels). This
247846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  however is not true for flat-shaped morphological kernels.
247946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
248046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  WARNING: Only the specific kernel pointed to is modified, not a list of
248146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  multiple kernels.
248246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
248346a369d839971ab627bdb31a93d8bd63e81b65a3anthony% This is an internal function and not expected to be useful outside this
248446a369d839971ab627bdb31a93d8bd63e81b65a3anthony% module.  This could change however.
248546a369d839971ab627bdb31a93d8bd63e81b65a3anthony*/
248646a369d839971ab627bdb31a93d8bd63e81b65a3anthonystatic void CalcKernelMetaData(KernelInfo *kernel)
248746a369d839971ab627bdb31a93d8bd63e81b65a3anthony{
2488bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register size_t
248946a369d839971ab627bdb31a93d8bd63e81b65a3anthony    i;
249046a369d839971ab627bdb31a93d8bd63e81b65a3anthony
249146a369d839971ab627bdb31a93d8bd63e81b65a3anthony  kernel->minimum = kernel->maximum = 0.0;
249246a369d839971ab627bdb31a93d8bd63e81b65a3anthony  kernel->negative_range = kernel->positive_range = 0.0;
249346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  for (i=0; i < (kernel->width*kernel->height); i++)
249446a369d839971ab627bdb31a93d8bd63e81b65a3anthony    {
249546a369d839971ab627bdb31a93d8bd63e81b65a3anthony      if ( fabs(kernel->values[i]) < MagickEpsilon )
249646a369d839971ab627bdb31a93d8bd63e81b65a3anthony        kernel->values[i] = 0.0;
249746a369d839971ab627bdb31a93d8bd63e81b65a3anthony      ( kernel->values[i] < 0)
249846a369d839971ab627bdb31a93d8bd63e81b65a3anthony          ?  ( kernel->negative_range += kernel->values[i] )
249946a369d839971ab627bdb31a93d8bd63e81b65a3anthony          :  ( kernel->positive_range += kernel->values[i] );
250046a369d839971ab627bdb31a93d8bd63e81b65a3anthony      Minimize(kernel->minimum, kernel->values[i]);
250146a369d839971ab627bdb31a93d8bd63e81b65a3anthony      Maximize(kernel->maximum, kernel->values[i]);
250246a369d839971ab627bdb31a93d8bd63e81b65a3anthony    }
250346a369d839971ab627bdb31a93d8bd63e81b65a3anthony
250446a369d839971ab627bdb31a93d8bd63e81b65a3anthony  return;
250546a369d839971ab627bdb31a93d8bd63e81b65a3anthony}
250646a369d839971ab627bdb31a93d8bd63e81b65a3anthony
250746a369d839971ab627bdb31a93d8bd63e81b65a3anthony/*
250846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
250946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
251046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
251146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
25129eb4f74649b23c053b308ce1152dce51239450baanthony%     M o r p h o l o g y A p p l y                                           %
2513602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2514602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2515602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2516602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2517602ab9b30b644a78a4057da93d838a77391ec0acanthony%
25189eb4f74649b23c053b308ce1152dce51239450baanthony%  MorphologyApply() applies a morphological method, multiple times using
251922de2722b682eb405b60ec6022a7546df994674eanthony%  a list of multiple kernels.  This is the method that should be called by
252022de2722b682eb405b60ec6022a7546df994674eanthony%  other 'operators' that internally use morphology operations as part of
252122de2722b682eb405b60ec6022a7546df994674eanthony%  their processing.
2522602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2523f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%  It is basically equivalent to as MorphologyImage() (see below) but
2524e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%  without any user controls.  This allows internel programs to use this
2525dcabf6d5fddb35c0eedd7a7638c102f16838c4e6glennrp%  function, to actually perform a specific task without possible interference
2526e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%  by any API user supplied settings.
2527e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%
2528f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%  It is MorphologyImage() task to extract any such user controls, and
2529e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%  pass them to this function for processing.
25309eb4f74649b23c053b308ce1152dce51239450baanthony%
253122de2722b682eb405b60ec6022a7546df994674eanthony%  More specifically all given kernels should already be scaled, normalised,
253222de2722b682eb405b60ec6022a7546df994674eanthony%  and blended appropriatally before being parred to this routine. The
253322de2722b682eb405b60ec6022a7546df994674eanthony%  appropriate bias, and compose (typically 'UndefinedComposeOp') given.
2534602ab9b30b644a78a4057da93d838a77391ec0acanthony%
253547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%  The format of the MorphologyApply method is:
2536602ab9b30b644a78a4057da93d838a77391ec0acanthony%
25379eb4f74649b23c053b308ce1152dce51239450baanthony%      Image *MorphologyApply(const Image *image,MorphologyMethod method,
2538f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%        const ssize_t iterations,const KernelInfo *kernel,
2539f46d42620631d2581e0b6a56456e203e17c427c8anthony%        const CompositeMethod compose,const double bias,
2540f46d42620631d2581e0b6a56456e203e17c427c8anthony%        ExceptionInfo *exception)
2541602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2542602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
2543602ab9b30b644a78a4057da93d838a77391ec0acanthony%
25448d18850dee4bed193a64866a6d2353eeeb73e145anthony%    o image: the source image
2545602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2546602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o method: the morphology method to be applied.
2547602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2548602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o iterations: apply the operation this many times (or no change).
2549602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  A value of -1 means loop until no change found.
2550602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  How this is applied may depend on the morphology method.
2551602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  Typically this is a value of 1.
2552602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2553602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o channel: the channel type.
2554602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2555602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel: An array of double representing the morphology kernel.
2556602ab9b30b644a78a4057da93d838a77391ec0acanthony%
255747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%    o compose: How to handle or merge multi-kernel results.
25588d18850dee4bed193a64866a6d2353eeeb73e145anthony%          If 'UndefinedCompositeOp' use default for the Morphology method.
25598d18850dee4bed193a64866a6d2353eeeb73e145anthony%          If 'NoCompositeOp' force image to be re-iterated by each kernel.
25608d18850dee4bed193a64866a6d2353eeeb73e145anthony%          Otherwise merge the results using the compose method given.
256147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%
2562f46d42620631d2581e0b6a56456e203e17c427c8anthony%    o bias: Convolution Output Bias.
2563f46d42620631d2581e0b6a56456e203e17c427c8anthony%
25649eb4f74649b23c053b308ce1152dce51239450baanthony%    o exception: return any errors or warnings in this structure.
2565602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2566602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
2567602ab9b30b644a78a4057da93d838a77391ec0acanthony
25689eb4f74649b23c053b308ce1152dce51239450baanthony/* Apply a Morphology Primative to an image using the given kernel.
2569a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** Two pre-created images must be provided, and no image is created.
2570ea61f01656bb0f9074677452017cc559e54093faanthony** It returns the number of pixels that changed between the images
2571a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** for result convergence determination.
25729eb4f74649b23c053b308ce1152dce51239450baanthony*/
2573f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristystatic ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
2574f46d42620631d2581e0b6a56456e203e17c427c8anthony  const MorphologyMethod method,const KernelInfo *kernel,const double bias,
2575f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  ExceptionInfo *exception)
2576602ab9b30b644a78a4057da93d838a77391ec0acanthony{
25772be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy#define MorphologyTag  "Morphology/Image"
2578602ab9b30b644a78a4057da93d838a77391ec0acanthony
25795f959473f334e196c6bf39b740c12cb4963fceebcristy  CacheView
25804c08aed51c5899665ade97263692328eea4af106cristy    *image_view,
25814c08aed51c5899665ade97263692328eea4af106cristy    *morphology_view;
25825f959473f334e196c6bf39b740c12cb4963fceebcristy
2583bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  ssize_t
2584a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    y, offx, offy;
2585a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
2586a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  size_t
2587db60568e12574785101a4ae8d8da076227a0a889anthony    virt_width,
2588602ab9b30b644a78a4057da93d838a77391ec0acanthony    changed;
2589602ab9b30b644a78a4057da93d838a77391ec0acanthony
2590602ab9b30b644a78a4057da93d838a77391ec0acanthony  MagickBooleanType
2591602ab9b30b644a78a4057da93d838a77391ec0acanthony    status;
2592602ab9b30b644a78a4057da93d838a77391ec0acanthony
25935f959473f334e196c6bf39b740c12cb4963fceebcristy  MagickOffsetType
25945f959473f334e196c6bf39b740c12cb4963fceebcristy    progress;
2595602ab9b30b644a78a4057da93d838a77391ec0acanthony
2596e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(image != (Image *) NULL);
2597e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(image->signature == MagickSignature);
25984c08aed51c5899665ade97263692328eea4af106cristy  assert(morphology_image != (Image *) NULL);
25994c08aed51c5899665ade97263692328eea4af106cristy  assert(morphology_image->signature == MagickSignature);
2600e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(kernel != (KernelInfo *) NULL);
2601e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(kernel->signature == MagickSignature);
2602e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(exception != (ExceptionInfo *) NULL);
2603e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(exception->signature == MagickSignature);
2604e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony
2605602ab9b30b644a78a4057da93d838a77391ec0acanthony  status=MagickTrue;
2606602ab9b30b644a78a4057da93d838a77391ec0acanthony  changed=0;
2607602ab9b30b644a78a4057da93d838a77391ec0acanthony  progress=0;
2608602ab9b30b644a78a4057da93d838a77391ec0acanthony
260946ff2676b1044ea4101ac7a59b83289cd8f6cfdacristy  image_view=AcquireVirtualCacheView(image,exception);
261046ff2676b1044ea4101ac7a59b83289cd8f6cfdacristy  morphology_view=AcquireAuthenticCacheView(morphology_image,exception);
26116eac8d74fdd2c99429aba3b8404008909a44261fanthony  virt_width=image->columns+kernel->width-1;
261229188a8682a98d4b7882cca434b170517555fc7danthony
2613cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  /* Some methods (including convolve) needs use a reflected kernel.
26149eb4f74649b23c053b308ce1152dce51239450baanthony   * Adjust 'origin' offsets to loop though kernel as a reflection.
261529188a8682a98d4b7882cca434b170517555fc7danthony   */
2616c99304fe3c8d9c617da792b40b57c118bb1249afcristy  offx = kernel->x;
2617c99304fe3c8d9c617da792b40b57c118bb1249afcristy  offy = kernel->y;
261829188a8682a98d4b7882cca434b170517555fc7danthony  switch(method) {
2619930be614b4595b97cd79ee864a394796740f76adanthony    case ConvolveMorphology:
2620930be614b4595b97cd79ee864a394796740f76adanthony    case DilateMorphology:
2621930be614b4595b97cd79ee864a394796740f76adanthony    case DilateIntensityMorphology:
2622f34d9b2df49a407af764c79e07d587af0600983aanthony    case IterativeDistanceMorphology:
26235ef8e94ff55717be2387d537bd49025780a1a558anthony      /* kernel needs to used with reflection about origin */
2624bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      offx = (ssize_t) kernel->width-offx-1;
2625bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      offy = (ssize_t) kernel->height-offy-1;
262629188a8682a98d4b7882cca434b170517555fc7danthony      break;
26275ef8e94ff55717be2387d537bd49025780a1a558anthony    case ErodeMorphology:
26285ef8e94ff55717be2387d537bd49025780a1a558anthony    case ErodeIntensityMorphology:
26295ef8e94ff55717be2387d537bd49025780a1a558anthony    case HitAndMissMorphology:
26305ef8e94ff55717be2387d537bd49025780a1a558anthony    case ThinningMorphology:
26315ef8e94ff55717be2387d537bd49025780a1a558anthony    case ThickenMorphology:
26323ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony      /* kernel is used as is, without reflection */
26335ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
2634930be614b4595b97cd79ee864a394796740f76adanthony    default:
26359eb4f74649b23c053b308ce1152dce51239450baanthony      assert("Not a Primitive Morphology Method" != (char *) NULL);
2636930be614b4595b97cd79ee864a394796740f76adanthony      break;
263729188a8682a98d4b7882cca434b170517555fc7danthony  }
263829188a8682a98d4b7882cca434b170517555fc7danthony
26398d18850dee4bed193a64866a6d2353eeeb73e145anthony  if ( method == ConvolveMorphology && kernel->width == 1 )
26408d18850dee4bed193a64866a6d2353eeeb73e145anthony  { /* Special handling (for speed) of vertical (blur) kernels.
26418d18850dee4bed193a64866a6d2353eeeb73e145anthony    ** This performs its handling in columns rather than in rows.
2642ea61f01656bb0f9074677452017cc559e54093faanthony    ** This is only done for convolve as it is the only method that
26438d18850dee4bed193a64866a6d2353eeeb73e145anthony    ** generates very large 1-D vertical kernels (such as a 'BlurKernel')
26448d18850dee4bed193a64866a6d2353eeeb73e145anthony    **
26458d18850dee4bed193a64866a6d2353eeeb73e145anthony    ** Timing tests (on single CPU laptop)
26468d18850dee4bed193a64866a6d2353eeeb73e145anthony    ** Using a vertical 1-d Blue with normal row-by-row (below)
26478d18850dee4bed193a64866a6d2353eeeb73e145anthony    **   time convert logo: -morphology Convolve Blur:0x10+90 null:
26488d18850dee4bed193a64866a6d2353eeeb73e145anthony    **      0.807u
26498d18850dee4bed193a64866a6d2353eeeb73e145anthony    ** Using this column method
26508d18850dee4bed193a64866a6d2353eeeb73e145anthony    **   time convert logo: -morphology Convolve Blur:0x10+90 null:
26518d18850dee4bed193a64866a6d2353eeeb73e145anthony    **      0.620u
26528d18850dee4bed193a64866a6d2353eeeb73e145anthony    **
26538d18850dee4bed193a64866a6d2353eeeb73e145anthony    ** Anthony Thyssen, 14 June 2010
26548d18850dee4bed193a64866a6d2353eeeb73e145anthony    */
2655fd1175952254cf1ac848ddb441e483c5e33d517fcristy    register ssize_t
2656fd1175952254cf1ac848ddb441e483c5e33d517fcristy      x;
2657fd1175952254cf1ac848ddb441e483c5e33d517fcristy
26588d18850dee4bed193a64866a6d2353eeeb73e145anthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
2659ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy    #pragma omp parallel for schedule(static,4) shared(progress,status) \
26605e6b259130f9dbe0da4666f734937017babe573acristy      magick_threads(image,morphology_image,image->columns,1)
26618d18850dee4bed193a64866a6d2353eeeb73e145anthony#endif
26628d18850dee4bed193a64866a6d2353eeeb73e145anthony    for (x=0; x < (ssize_t) image->columns; x++)
26638d18850dee4bed193a64866a6d2353eeeb73e145anthony    {
26644c08aed51c5899665ade97263692328eea4af106cristy      register const Quantum
26658d18850dee4bed193a64866a6d2353eeeb73e145anthony        *restrict p;
26668d18850dee4bed193a64866a6d2353eeeb73e145anthony
26674c08aed51c5899665ade97263692328eea4af106cristy      register Quantum
26688d18850dee4bed193a64866a6d2353eeeb73e145anthony        *restrict q;
26698d18850dee4bed193a64866a6d2353eeeb73e145anthony
26708d18850dee4bed193a64866a6d2353eeeb73e145anthony      register ssize_t
26718d18850dee4bed193a64866a6d2353eeeb73e145anthony        y;
26728d18850dee4bed193a64866a6d2353eeeb73e145anthony
267355a91cddcdea3aa002893186a773e1704884a9dfcristy      ssize_t
26748d18850dee4bed193a64866a6d2353eeeb73e145anthony        r;
26758d18850dee4bed193a64866a6d2353eeeb73e145anthony
26768d18850dee4bed193a64866a6d2353eeeb73e145anthony      if (status == MagickFalse)
26778d18850dee4bed193a64866a6d2353eeeb73e145anthony        continue;
2678c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy      p=GetCacheViewVirtualPixels(image_view,x,-offy,1,image->rows+
2679c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy        kernel->height-1,exception);
2680c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy      q=GetCacheViewAuthenticPixels(morphology_view,x,0,1,
2681c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy        morphology_image->rows,exception);
26824c08aed51c5899665ade97263692328eea4af106cristy      if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
26838d18850dee4bed193a64866a6d2353eeeb73e145anthony        {
26848d18850dee4bed193a64866a6d2353eeeb73e145anthony          status=MagickFalse;
26858d18850dee4bed193a64866a6d2353eeeb73e145anthony          continue;
26868d18850dee4bed193a64866a6d2353eeeb73e145anthony        }
2687a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      /* offset to origin in 'p'. while 'q' points to it directly */
2688a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      r = offy;
26898d18850dee4bed193a64866a6d2353eeeb73e145anthony
26908d18850dee4bed193a64866a6d2353eeeb73e145anthony      for (y=0; y < (ssize_t) image->rows; y++)
26918d18850dee4bed193a64866a6d2353eeeb73e145anthony      {
269270b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy        PixelInfo
269370b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy          result;
269470b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy
2695d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        register const MagickRealType
26968d18850dee4bed193a64866a6d2353eeeb73e145anthony          *restrict k;
26978d18850dee4bed193a64866a6d2353eeeb73e145anthony
26984c08aed51c5899665ade97263692328eea4af106cristy        register const Quantum
26998d18850dee4bed193a64866a6d2353eeeb73e145anthony          *restrict k_pixels;
27008d18850dee4bed193a64866a6d2353eeeb73e145anthony
2701d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        register ssize_t
2702d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          v;
2703d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy
27048d18850dee4bed193a64866a6d2353eeeb73e145anthony        /* Copy input image to the output image for unused channels
27058d18850dee4bed193a64866a6d2353eeeb73e145anthony        * This removes need for 'cloning' a new image every iteration
27068d18850dee4bed193a64866a6d2353eeeb73e145anthony        */
27074c08aed51c5899665ade97263692328eea4af106cristy        SetPixelRed(morphology_image,GetPixelRed(image,p+r*
2708ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          GetPixelChannels(image)),q);
27094c08aed51c5899665ade97263692328eea4af106cristy        SetPixelGreen(morphology_image,GetPixelGreen(image,p+r*
2710ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          GetPixelChannels(image)),q);
27114c08aed51c5899665ade97263692328eea4af106cristy        SetPixelBlue(morphology_image,GetPixelBlue(image,p+r*
2712ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          GetPixelChannels(image)),q);
27138d18850dee4bed193a64866a6d2353eeeb73e145anthony        if (image->colorspace == CMYKColorspace)
27144c08aed51c5899665ade97263692328eea4af106cristy          SetPixelBlack(morphology_image,GetPixelBlack(image,p+r*
2715ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            GetPixelChannels(image)),q);
27168d18850dee4bed193a64866a6d2353eeeb73e145anthony
27178d18850dee4bed193a64866a6d2353eeeb73e145anthony        /* Set the bias of the weighted average output */
271853f576d128f0bb744a82e7f9c0d8f05b2923972canthony        result.red   =
271953f576d128f0bb744a82e7f9c0d8f05b2923972canthony        result.green =
272053f576d128f0bb744a82e7f9c0d8f05b2923972canthony        result.blue  =
27214c08aed51c5899665ade97263692328eea4af106cristy        result.alpha =
272253f576d128f0bb744a82e7f9c0d8f05b2923972canthony        result.black = bias;
27238d18850dee4bed193a64866a6d2353eeeb73e145anthony
27248d18850dee4bed193a64866a6d2353eeeb73e145anthony
27258d18850dee4bed193a64866a6d2353eeeb73e145anthony        /* Weighted Average of pixels using reflected kernel
27268d18850dee4bed193a64866a6d2353eeeb73e145anthony        **
27278d18850dee4bed193a64866a6d2353eeeb73e145anthony        ** NOTE for correct working of this operation for asymetrical
27288d18850dee4bed193a64866a6d2353eeeb73e145anthony        ** kernels, the kernel needs to be applied in its reflected form.
27298d18850dee4bed193a64866a6d2353eeeb73e145anthony        ** That is its values needs to be reversed.
27308d18850dee4bed193a64866a6d2353eeeb73e145anthony        */
27318d18850dee4bed193a64866a6d2353eeeb73e145anthony        k = &kernel->values[ kernel->height-1 ];
27328d18850dee4bed193a64866a6d2353eeeb73e145anthony        k_pixels = p;
27330ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony        if ( (image->channel_mask != DefaultChannels) ||
27348a46d827a124555f0c48fb2368ec1bba8e079ab6cristy             (image->alpha_trait != BlendPixelTrait) )
27358d18850dee4bed193a64866a6d2353eeeb73e145anthony          { /* No 'Sync' involved.
27360ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony            ** Convolution is just a simple greyscale channel operation
27378d18850dee4bed193a64866a6d2353eeeb73e145anthony            */
27388d18850dee4bed193a64866a6d2353eeeb73e145anthony            for (v=0; v < (ssize_t) kernel->height; v++) {
27398d18850dee4bed193a64866a6d2353eeeb73e145anthony              if ( IsNan(*k) ) continue;
27404c08aed51c5899665ade97263692328eea4af106cristy              result.red     += (*k)*GetPixelRed(image,k_pixels);
27414c08aed51c5899665ade97263692328eea4af106cristy              result.green   += (*k)*GetPixelGreen(image,k_pixels);
27424c08aed51c5899665ade97263692328eea4af106cristy              result.blue    += (*k)*GetPixelBlue(image,k_pixels);
27434c08aed51c5899665ade97263692328eea4af106cristy              if (image->colorspace == CMYKColorspace)
27444c08aed51c5899665ade97263692328eea4af106cristy                result.black+=(*k)*GetPixelBlack(image,k_pixels);
27454c08aed51c5899665ade97263692328eea4af106cristy              result.alpha += (*k)*GetPixelAlpha(image,k_pixels);
27468d18850dee4bed193a64866a6d2353eeeb73e145anthony              k--;
2747ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels+=GetPixelChannels(image);
27488d18850dee4bed193a64866a6d2353eeeb73e145anthony            }
2749ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
27504c08aed51c5899665ade97263692328eea4af106cristy              SetPixelRed(morphology_image,ClampToQuantum(result.red),q);
2751ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
27524c08aed51c5899665ade97263692328eea4af106cristy              SetPixelGreen(morphology_image,ClampToQuantum(result.green),q);
2753ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
27544c08aed51c5899665ade97263692328eea4af106cristy              SetPixelBlue(morphology_image,ClampToQuantum(result.blue),q);
2755ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
27564c08aed51c5899665ade97263692328eea4af106cristy                (image->colorspace == CMYKColorspace))
27574c08aed51c5899665ade97263692328eea4af106cristy              SetPixelBlack(morphology_image,ClampToQuantum(result.black),q);
2758ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
27598a46d827a124555f0c48fb2368ec1bba8e079ab6cristy                (image->alpha_trait == BlendPixelTrait))
27604c08aed51c5899665ade97263692328eea4af106cristy              SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
27618d18850dee4bed193a64866a6d2353eeeb73e145anthony          }
27628d18850dee4bed193a64866a6d2353eeeb73e145anthony        else
27638d18850dee4bed193a64866a6d2353eeeb73e145anthony          { /* Channel 'Sync' Flag, and Alpha Channel enabled.
27648d18850dee4bed193a64866a6d2353eeeb73e145anthony            ** Weight the color channels with Alpha Channel so that
27658d18850dee4bed193a64866a6d2353eeeb73e145anthony            ** transparent pixels are not part of the results.
27668d18850dee4bed193a64866a6d2353eeeb73e145anthony            */
2767a19f1d70e9a9f88279c4ecafe6dfafc1f9a09599cristy            double
276842052799a46abaf2b4fc90fe390cdd856038b4f7anthony              alpha,  /* alpha weighting for colors : alpha  */
276942052799a46abaf2b4fc90fe390cdd856038b4f7anthony              gamma;  /* divisor, sum of color alpha weighting */
277042052799a46abaf2b4fc90fe390cdd856038b4f7anthony            size_t
277142052799a46abaf2b4fc90fe390cdd856038b4f7anthony              count;  /* alpha valus collected, number kernel values */
27728d18850dee4bed193a64866a6d2353eeeb73e145anthony
277342052799a46abaf2b4fc90fe390cdd856038b4f7anthony            count=0;
27748d18850dee4bed193a64866a6d2353eeeb73e145anthony            gamma=0.0;
27758d18850dee4bed193a64866a6d2353eeeb73e145anthony            for (v=0; v < (ssize_t) kernel->height; v++) {
27768d18850dee4bed193a64866a6d2353eeeb73e145anthony              if ( IsNan(*k) ) continue;
27770ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony              alpha=QuantumScale*GetPixelAlpha(image,k_pixels);
27780ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony              gamma += alpha; /* normalize alpha weights only */
277942052799a46abaf2b4fc90fe390cdd856038b4f7anthony              count++;        /* number of alpha values collected */
27800ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony              alpha*=(*k);    /* include kernel weighting now */
27814c08aed51c5899665ade97263692328eea4af106cristy              result.red     += alpha*GetPixelRed(image,k_pixels);
27824c08aed51c5899665ade97263692328eea4af106cristy              result.green   += alpha*GetPixelGreen(image,k_pixels);
27834c08aed51c5899665ade97263692328eea4af106cristy              result.blue    += alpha*GetPixelBlue(image,k_pixels);
27844c08aed51c5899665ade97263692328eea4af106cristy              if (image->colorspace == CMYKColorspace)
27854c08aed51c5899665ade97263692328eea4af106cristy                result.black += alpha*GetPixelBlack(image,k_pixels);
278642052799a46abaf2b4fc90fe390cdd856038b4f7anthony              result.alpha   += (*k)*GetPixelAlpha(image,k_pixels);
27878d18850dee4bed193a64866a6d2353eeeb73e145anthony              k--;
2788ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels+=GetPixelChannels(image);
27898d18850dee4bed193a64866a6d2353eeeb73e145anthony            }
27908d18850dee4bed193a64866a6d2353eeeb73e145anthony            /* Sync'ed channels, all channels are modified */
27919b52834318bcfea7da9c8b4ec8eb4682f009234dcristy            gamma=(double)count/(fabs((double) gamma) < MagickEpsilon ? MagickEpsilon : gamma);
27920ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony            SetPixelRed(morphology_image,ClampToQuantum(gamma*result.red),q);
27930ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony            SetPixelGreen(morphology_image,ClampToQuantum(gamma*result.green),q);
27940ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony            SetPixelBlue(morphology_image,ClampToQuantum(gamma*result.blue),q);
27958d18850dee4bed193a64866a6d2353eeeb73e145anthony            if (image->colorspace == CMYKColorspace)
27960ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony              SetPixelBlack(morphology_image,ClampToQuantum(gamma*result.black),q);
27970ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony            SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
27988d18850dee4bed193a64866a6d2353eeeb73e145anthony          }
27998d18850dee4bed193a64866a6d2353eeeb73e145anthony
28008d18850dee4bed193a64866a6d2353eeeb73e145anthony        /* Count up changed pixels */
2801ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        if ((GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(morphology_image,q))
2802ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            || (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(morphology_image,q))
2803ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            || (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(morphology_image,q))
2804ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            || (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(morphology_image,q))
28054c08aed51c5899665ade97263692328eea4af106cristy            || ((image->colorspace == CMYKColorspace) &&
2806ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(morphology_image,q))))
28078d18850dee4bed193a64866a6d2353eeeb73e145anthony          changed++;  /* The pixel was changed in some way! */
2808ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        p+=GetPixelChannels(image);
2809ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        q+=GetPixelChannels(morphology_image);
28108d18850dee4bed193a64866a6d2353eeeb73e145anthony      } /* y */
28114c08aed51c5899665ade97263692328eea4af106cristy      if ( SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
28128d18850dee4bed193a64866a6d2353eeeb73e145anthony        status=MagickFalse;
28138d18850dee4bed193a64866a6d2353eeeb73e145anthony      if (image->progress_monitor != (MagickProgressMonitor) NULL)
28148d18850dee4bed193a64866a6d2353eeeb73e145anthony        {
28158d18850dee4bed193a64866a6d2353eeeb73e145anthony          MagickBooleanType
28168d18850dee4bed193a64866a6d2353eeeb73e145anthony            proceed;
28178d18850dee4bed193a64866a6d2353eeeb73e145anthony
28188d18850dee4bed193a64866a6d2353eeeb73e145anthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
2819ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy          #pragma omp critical (MagickCore_MorphologyImage)
28208d18850dee4bed193a64866a6d2353eeeb73e145anthony#endif
28218d18850dee4bed193a64866a6d2353eeeb73e145anthony          proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
28228d18850dee4bed193a64866a6d2353eeeb73e145anthony          if (proceed == MagickFalse)
28238d18850dee4bed193a64866a6d2353eeeb73e145anthony            status=MagickFalse;
28248d18850dee4bed193a64866a6d2353eeeb73e145anthony        }
28258d18850dee4bed193a64866a6d2353eeeb73e145anthony    } /* x */
28264c08aed51c5899665ade97263692328eea4af106cristy    morphology_image->type=image->type;
28274c08aed51c5899665ade97263692328eea4af106cristy    morphology_view=DestroyCacheView(morphology_view);
28284c08aed51c5899665ade97263692328eea4af106cristy    image_view=DestroyCacheView(image_view);
2829aecf4bd539fe0591d67bf1ec961ac68b5490171fcristy    return(status ? (ssize_t) changed : 0);
28308d18850dee4bed193a64866a6d2353eeeb73e145anthony  }
28318d18850dee4bed193a64866a6d2353eeeb73e145anthony
28328d18850dee4bed193a64866a6d2353eeeb73e145anthony  /*
28338d18850dee4bed193a64866a6d2353eeeb73e145anthony  ** Normal handling of horizontal or rectangular kernels (row by row)
28348d18850dee4bed193a64866a6d2353eeeb73e145anthony  */
2835602ab9b30b644a78a4057da93d838a77391ec0acanthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
2836ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy  #pragma omp parallel for schedule(static,4) shared(progress,status) \
28375e6b259130f9dbe0da4666f734937017babe573acristy    magick_threads(image,morphology_image,image->rows,1)
2838602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
2839bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (y=0; y < (ssize_t) image->rows; y++)
2840602ab9b30b644a78a4057da93d838a77391ec0acanthony  {
28414c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
2842602ab9b30b644a78a4057da93d838a77391ec0acanthony      *restrict p;
2843602ab9b30b644a78a4057da93d838a77391ec0acanthony
28444c08aed51c5899665ade97263692328eea4af106cristy    register Quantum
2845602ab9b30b644a78a4057da93d838a77391ec0acanthony      *restrict q;
2846602ab9b30b644a78a4057da93d838a77391ec0acanthony
2847bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    register ssize_t
2848602ab9b30b644a78a4057da93d838a77391ec0acanthony      x;
2849602ab9b30b644a78a4057da93d838a77391ec0acanthony
2850bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    size_t
2851602ab9b30b644a78a4057da93d838a77391ec0acanthony      r;
2852602ab9b30b644a78a4057da93d838a77391ec0acanthony
2853602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (status == MagickFalse)
2854602ab9b30b644a78a4057da93d838a77391ec0acanthony      continue;
28554c08aed51c5899665ade97263692328eea4af106cristy    p=GetCacheViewVirtualPixels(image_view, -offx, y-offy, virt_width,
2856c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy      kernel->height,  exception);
2857c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy    q=GetCacheViewAuthenticPixels(morphology_view,0,y,
2858c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy      morphology_image->columns,1,exception);
28594c08aed51c5899665ade97263692328eea4af106cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2860602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
2861602ab9b30b644a78a4057da93d838a77391ec0acanthony        status=MagickFalse;
2862602ab9b30b644a78a4057da93d838a77391ec0acanthony        continue;
2863602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2864a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    /* offset to origin in 'p'. while 'q' points to it directly */
2865db60568e12574785101a4ae8d8da076227a0a889anthony    r = virt_width*offy + offx;
286629188a8682a98d4b7882cca434b170517555fc7danthony
2867bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (x=0; x < (ssize_t) image->columns; x++)
2868602ab9b30b644a78a4057da93d838a77391ec0acanthony    {
2869d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      PixelInfo
2870d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        result,
2871d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        min,
2872d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        max;
2873602ab9b30b644a78a4057da93d838a77391ec0acanthony
2874d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      register const MagickRealType
2875602ab9b30b644a78a4057da93d838a77391ec0acanthony        *restrict k;
2876602ab9b30b644a78a4057da93d838a77391ec0acanthony
28774c08aed51c5899665ade97263692328eea4af106cristy      register const Quantum
2878602ab9b30b644a78a4057da93d838a77391ec0acanthony        *restrict k_pixels;
2879602ab9b30b644a78a4057da93d838a77391ec0acanthony
2880d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      register ssize_t
2881d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        u;
2882d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy
2883d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      ssize_t
2884d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        v;
2885602ab9b30b644a78a4057da93d838a77391ec0acanthony
2886c406ea496196b962e4bc865ed8f6a4491241c6edanthony      /* Copy input image to the output image for unused channels
288783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony       * This removes need for 'cloning' a new image every iteration
288829188a8682a98d4b7882cca434b170517555fc7danthony       */
28894c08aed51c5899665ade97263692328eea4af106cristy      SetPixelRed(morphology_image,GetPixelRed(image,p+r*
2890ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        GetPixelChannels(image)),q);
28914c08aed51c5899665ade97263692328eea4af106cristy      SetPixelGreen(morphology_image,GetPixelGreen(image,p+r*
2892ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        GetPixelChannels(image)),q);
28934c08aed51c5899665ade97263692328eea4af106cristy      SetPixelBlue(morphology_image,GetPixelBlue(image,p+r*
2894ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy        GetPixelChannels(image)),q);
2895602ab9b30b644a78a4057da93d838a77391ec0acanthony      if (image->colorspace == CMYKColorspace)
28964c08aed51c5899665ade97263692328eea4af106cristy        SetPixelBlack(morphology_image,GetPixelBlack(image,p+r*
2897ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          GetPixelChannels(image)),q);
2898602ab9b30b644a78a4057da93d838a77391ec0acanthony
28995ef8e94ff55717be2387d537bd49025780a1a558anthony      /* Defaults */
29005ef8e94ff55717be2387d537bd49025780a1a558anthony      min.red     =
29015ef8e94ff55717be2387d537bd49025780a1a558anthony      min.green   =
29025ef8e94ff55717be2387d537bd49025780a1a558anthony      min.blue    =
29034c08aed51c5899665ade97263692328eea4af106cristy      min.alpha =
2904a19f1d70e9a9f88279c4ecafe6dfafc1f9a09599cristy      min.black   = (double) QuantumRange;
29055ef8e94ff55717be2387d537bd49025780a1a558anthony      max.red     =
29065ef8e94ff55717be2387d537bd49025780a1a558anthony      max.green   =
29075ef8e94ff55717be2387d537bd49025780a1a558anthony      max.blue    =
29084c08aed51c5899665ade97263692328eea4af106cristy      max.alpha =
2909a19f1d70e9a9f88279c4ecafe6dfafc1f9a09599cristy      max.black   = (double) 0;
29109eb4f74649b23c053b308ce1152dce51239450baanthony      /* default result is the original pixel value */
2911a19f1d70e9a9f88279c4ecafe6dfafc1f9a09599cristy      result.red     = (double) GetPixelRed(image,p+r*GetPixelChannels(image));
2912a19f1d70e9a9f88279c4ecafe6dfafc1f9a09599cristy      result.green   = (double) GetPixelGreen(image,p+r*GetPixelChannels(image));
2913a19f1d70e9a9f88279c4ecafe6dfafc1f9a09599cristy      result.blue    = (double) GetPixelBlue(image,p+r*GetPixelChannels(image));
29144c08aed51c5899665ade97263692328eea4af106cristy      result.black   = 0.0;
29154c08aed51c5899665ade97263692328eea4af106cristy      if (image->colorspace == CMYKColorspace)
2916a19f1d70e9a9f88279c4ecafe6dfafc1f9a09599cristy        result.black = (double) GetPixelBlack(image,p+r*GetPixelChannels(image));
2917a19f1d70e9a9f88279c4ecafe6dfafc1f9a09599cristy      result.alpha=(double) GetPixelAlpha(image,p+r*GetPixelChannels(image));
29185ef8e94ff55717be2387d537bd49025780a1a558anthony
2919602ab9b30b644a78a4057da93d838a77391ec0acanthony      switch (method) {
2920602ab9b30b644a78a4057da93d838a77391ec0acanthony        case ConvolveMorphology:
29218d18850dee4bed193a64866a6d2353eeeb73e145anthony          /* Set the bias of the weighted average output */
29229eb4f74649b23c053b308ce1152dce51239450baanthony          result.red     =
29239eb4f74649b23c053b308ce1152dce51239450baanthony          result.green   =
29249eb4f74649b23c053b308ce1152dce51239450baanthony          result.blue    =
29254c08aed51c5899665ade97263692328eea4af106cristy          result.alpha =
2926f46d42620631d2581e0b6a56456e203e17c427c8anthony          result.black   = bias;
2927930be614b4595b97cd79ee864a394796740f76adanthony          break;
29284fd27e21043be809d66c8202e779255e5b660d2danthony        case DilateIntensityMorphology:
29294fd27e21043be809d66c8202e779255e5b660d2danthony        case ErodeIntensityMorphology:
29309eb4f74649b23c053b308ce1152dce51239450baanthony          /* use a boolean flag indicating when first match found */
29319eb4f74649b23c053b308ce1152dce51239450baanthony          result.red = 0.0;  /* result is not used otherwise */
29324fd27e21043be809d66c8202e779255e5b660d2danthony          break;
2933602ab9b30b644a78a4057da93d838a77391ec0acanthony        default:
2934602ab9b30b644a78a4057da93d838a77391ec0acanthony          break;
2935602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2936602ab9b30b644a78a4057da93d838a77391ec0acanthony
2937602ab9b30b644a78a4057da93d838a77391ec0acanthony      switch ( method ) {
2938602ab9b30b644a78a4057da93d838a77391ec0acanthony        case ConvolveMorphology:
2939930be614b4595b97cd79ee864a394796740f76adanthony            /* Weighted Average of pixels using reflected kernel
2940930be614b4595b97cd79ee864a394796740f76adanthony            **
2941930be614b4595b97cd79ee864a394796740f76adanthony            ** NOTE for correct working of this operation for asymetrical
2942930be614b4595b97cd79ee864a394796740f76adanthony            ** kernels, the kernel needs to be applied in its reflected form.
2943930be614b4595b97cd79ee864a394796740f76adanthony            ** That is its values needs to be reversed.
2944930be614b4595b97cd79ee864a394796740f76adanthony            **
2945930be614b4595b97cd79ee864a394796740f76adanthony            ** Correlation is actually the same as this but without reflecting
2946930be614b4595b97cd79ee864a394796740f76adanthony            ** the kernel, and thus 'lower-level' that Convolution.  However
2947930be614b4595b97cd79ee864a394796740f76adanthony            ** as Convolution is the more common method used, and it does not
2948930be614b4595b97cd79ee864a394796740f76adanthony            ** really cost us much in terms of processing to use a reflected
29495ef8e94ff55717be2387d537bd49025780a1a558anthony            ** kernel, so it is Convolution that is implemented.
2950930be614b4595b97cd79ee864a394796740f76adanthony            **
2951930be614b4595b97cd79ee864a394796740f76adanthony            ** Correlation will have its kernel reflected before calling
2952930be614b4595b97cd79ee864a394796740f76adanthony            ** this function to do a Convolve.
2953930be614b4595b97cd79ee864a394796740f76adanthony            **
2954930be614b4595b97cd79ee864a394796740f76adanthony            ** For more details of Correlation vs Convolution see
2955930be614b4595b97cd79ee864a394796740f76adanthony            **   http://www.cs.umd.edu/~djacobs/CMSC426/Convolution.pdf
2956930be614b4595b97cd79ee864a394796740f76adanthony            */
29578d18850dee4bed193a64866a6d2353eeeb73e145anthony            k = &kernel->values[ kernel->width*kernel->height-1 ];
29588d18850dee4bed193a64866a6d2353eeeb73e145anthony            k_pixels = p;
29595f95f4f77efc46ff53593d750491c8f60698c983cristy            if ( (image->channel_mask != DefaultChannels) ||
29608a46d827a124555f0c48fb2368ec1bba8e079ab6cristy                 (image->alpha_trait != BlendPixelTrait) )
29618d18850dee4bed193a64866a6d2353eeeb73e145anthony              { /* No 'Sync' involved.
29628d18850dee4bed193a64866a6d2353eeeb73e145anthony                ** Convolution is simple greyscale channel operation
29638d18850dee4bed193a64866a6d2353eeeb73e145anthony                */
29648d18850dee4bed193a64866a6d2353eeeb73e145anthony                for (v=0; v < (ssize_t) kernel->height; v++) {
29658d18850dee4bed193a64866a6d2353eeeb73e145anthony                  for (u=0; u < (ssize_t) kernel->width; u++, k--) {
29668d18850dee4bed193a64866a6d2353eeeb73e145anthony                    if ( IsNan(*k) ) continue;
29674c08aed51c5899665ade97263692328eea4af106cristy                    result.red     += (*k)*
2968ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelRed(image,k_pixels+u*GetPixelChannels(image));
29694c08aed51c5899665ade97263692328eea4af106cristy                    result.green   += (*k)*
2970ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelGreen(image,k_pixels+u*GetPixelChannels(image));
29714c08aed51c5899665ade97263692328eea4af106cristy                    result.blue    += (*k)*
2972ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelBlue(image,k_pixels+u*GetPixelChannels(image));
29734c08aed51c5899665ade97263692328eea4af106cristy                    if (image->colorspace == CMYKColorspace)
29744c08aed51c5899665ade97263692328eea4af106cristy                      result.black += (*k)*
2975ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                        GetPixelBlack(image,k_pixels+u*GetPixelChannels(image));
29764c08aed51c5899665ade97263692328eea4af106cristy                    result.alpha += (*k)*
2977ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image));
29788d18850dee4bed193a64866a6d2353eeeb73e145anthony                  }
2979ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  k_pixels += virt_width*GetPixelChannels(image);
29808d18850dee4bed193a64866a6d2353eeeb73e145anthony                }
2981ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
29822b9582a27910c7baaeb04b7e969638328fa70095cristy                  SetPixelRed(morphology_image,ClampToQuantum(result.red),
29832b9582a27910c7baaeb04b7e969638328fa70095cristy                    q);
2984ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
29852b9582a27910c7baaeb04b7e969638328fa70095cristy                  SetPixelGreen(morphology_image,ClampToQuantum(result.green),
29862b9582a27910c7baaeb04b7e969638328fa70095cristy                    q);
2987ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
29882b9582a27910c7baaeb04b7e969638328fa70095cristy                  SetPixelBlue(morphology_image,ClampToQuantum(result.blue),
29892b9582a27910c7baaeb04b7e969638328fa70095cristy                    q);
2990ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
29914c08aed51c5899665ade97263692328eea4af106cristy                    (image->colorspace == CMYKColorspace))
29922b9582a27910c7baaeb04b7e969638328fa70095cristy                  SetPixelBlack(morphology_image,ClampToQuantum(result.black),
29932b9582a27910c7baaeb04b7e969638328fa70095cristy                    q);
2994ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
29958a46d827a124555f0c48fb2368ec1bba8e079ab6cristy                    (image->alpha_trait == BlendPixelTrait))
29962b9582a27910c7baaeb04b7e969638328fa70095cristy                  SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),
29972b9582a27910c7baaeb04b7e969638328fa70095cristy                    q);
29988d18850dee4bed193a64866a6d2353eeeb73e145anthony              }
29998d18850dee4bed193a64866a6d2353eeeb73e145anthony            else
30008d18850dee4bed193a64866a6d2353eeeb73e145anthony              { /* Channel 'Sync' Flag, and Alpha Channel enabled.
30015ef8e94ff55717be2387d537bd49025780a1a558anthony                ** Weight the color channels with Alpha Channel so that
30025ef8e94ff55717be2387d537bd49025780a1a558anthony                ** transparent pixels are not part of the results.
30035ef8e94ff55717be2387d537bd49025780a1a558anthony                */
3004a19f1d70e9a9f88279c4ecafe6dfafc1f9a09599cristy                double
300542052799a46abaf2b4fc90fe390cdd856038b4f7anthony                  alpha,  /* alpha weighting for colors : alpha  */
300642052799a46abaf2b4fc90fe390cdd856038b4f7anthony                  gamma;  /* divisor, sum of color alpha weighting */
300742052799a46abaf2b4fc90fe390cdd856038b4f7anthony                size_t
300842052799a46abaf2b4fc90fe390cdd856038b4f7anthony                  count;  /* alpha valus collected, number kernel values */
3009602ab9b30b644a78a4057da93d838a77391ec0acanthony
301042052799a46abaf2b4fc90fe390cdd856038b4f7anthony                count=0;
3011602ab9b30b644a78a4057da93d838a77391ec0acanthony                gamma=0.0;
3012bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for (v=0; v < (ssize_t) kernel->height; v++) {
3013bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3014602ab9b30b644a78a4057da93d838a77391ec0acanthony                    if ( IsNan(*k) ) continue;
30150ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony                    alpha=QuantumScale*GetPixelAlpha(image,
30160ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony                                k_pixels+u*GetPixelChannels(image));
301742052799a46abaf2b4fc90fe390cdd856038b4f7anthony                    gamma += alpha;    /* normalize alpha weights only */
301842052799a46abaf2b4fc90fe390cdd856038b4f7anthony                    count++;           /* number of alpha values collected */
301942052799a46abaf2b4fc90fe390cdd856038b4f7anthony                    alpha=alpha*(*k);  /* include kernel weighting now */
30204c08aed51c5899665ade97263692328eea4af106cristy                    result.red     += alpha*
3021ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelRed(image,k_pixels+u*GetPixelChannels(image));
30224c08aed51c5899665ade97263692328eea4af106cristy                    result.green   += alpha*
3023ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelGreen(image,k_pixels+u*GetPixelChannels(image));
30244c08aed51c5899665ade97263692328eea4af106cristy                    result.blue    += alpha*
3025ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelBlue(image,k_pixels+u*GetPixelChannels(image));
30264c08aed51c5899665ade97263692328eea4af106cristy                    if (image->colorspace == CMYKColorspace)
302742052799a46abaf2b4fc90fe390cdd856038b4f7anthony                      result.black += alpha*
3028ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                        GetPixelBlack(image,k_pixels+u*GetPixelChannels(image));
302942052799a46abaf2b4fc90fe390cdd856038b4f7anthony                    result.alpha   += (*k)*
3030ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image));
3031602ab9b30b644a78a4057da93d838a77391ec0acanthony                  }
3032ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  k_pixels += virt_width*GetPixelChannels(image);
3033602ab9b30b644a78a4057da93d838a77391ec0acanthony                }
3034c406ea496196b962e4bc865ed8f6a4491241c6edanthony                /* Sync'ed channels, all channels are modified */
30359b52834318bcfea7da9c8b4ec8eb4682f009234dcristy                gamma=(double)count/(fabs((double) gamma) < MagickEpsilon ? MagickEpsilon : gamma);
30364c08aed51c5899665ade97263692328eea4af106cristy                SetPixelRed(morphology_image,
30374c08aed51c5899665ade97263692328eea4af106cristy                  ClampToQuantum(gamma*result.red),q);
30384c08aed51c5899665ade97263692328eea4af106cristy                SetPixelGreen(morphology_image,
30394c08aed51c5899665ade97263692328eea4af106cristy                  ClampToQuantum(gamma*result.green),q);
30404c08aed51c5899665ade97263692328eea4af106cristy                SetPixelBlue(morphology_image,
30414c08aed51c5899665ade97263692328eea4af106cristy                  ClampToQuantum(gamma*result.blue),q);
3042c406ea496196b962e4bc865ed8f6a4491241c6edanthony                if (image->colorspace == CMYKColorspace)
30434c08aed51c5899665ade97263692328eea4af106cristy                  SetPixelBlack(morphology_image,
30444c08aed51c5899665ade97263692328eea4af106cristy                    ClampToQuantum(gamma*result.black),q);
30454c08aed51c5899665ade97263692328eea4af106cristy                SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
3046602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3047602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3048602ab9b30b644a78a4057da93d838a77391ec0acanthony
30494fd27e21043be809d66c8202e779255e5b660d2danthony        case ErodeMorphology:
30505ef8e94ff55717be2387d537bd49025780a1a558anthony            /* Minimum Value within kernel neighbourhood
3051930be614b4595b97cd79ee864a394796740f76adanthony            **
3052930be614b4595b97cd79ee864a394796740f76adanthony            ** NOTE that the kernel is not reflected for this operation!
3053930be614b4595b97cd79ee864a394796740f76adanthony            **
3054930be614b4595b97cd79ee864a394796740f76adanthony            ** NOTE: in normal Greyscale Morphology, the kernel value should
3055930be614b4595b97cd79ee864a394796740f76adanthony            ** be added to the real value, this is currently not done, due to
3056930be614b4595b97cd79ee864a394796740f76adanthony            ** the nature of the boolean kernels being used.
3057930be614b4595b97cd79ee864a394796740f76adanthony            */
30584fd27e21043be809d66c8202e779255e5b660d2danthony            k = kernel->values;
3059602ab9b30b644a78a4057da93d838a77391ec0acanthony            k_pixels = p;
3060bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (v=0; v < (ssize_t) kernel->height; v++) {
3061bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              for (u=0; u < (ssize_t) kernel->width; u++, k++) {
3062602ab9b30b644a78a4057da93d838a77391ec0acanthony                if ( IsNan(*k) || (*k) < 0.5 ) continue;
30634c08aed51c5899665ade97263692328eea4af106cristy                Minimize(min.red,     (double)
3064ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
3065f34d9b2df49a407af764c79e07d587af0600983aanthony                Minimize(min.green,   (double)
3066ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
30674c08aed51c5899665ade97263692328eea4af106cristy                Minimize(min.blue,    (double)
3068ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3069f34d9b2df49a407af764c79e07d587af0600983aanthony                Minimize(min.alpha,   (double)
3070f34d9b2df49a407af764c79e07d587af0600983aanthony                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
30714c08aed51c5899665ade97263692328eea4af106cristy                if (image->colorspace == CMYKColorspace)
3072f34d9b2df49a407af764c79e07d587af0600983aanthony                  Minimize(min.black,  (double)
3073ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
3074602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3075ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3076602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3077602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3078602ab9b30b644a78a4057da93d838a77391ec0acanthony
30794fd27e21043be809d66c8202e779255e5b660d2danthony        case DilateMorphology:
30805ef8e94ff55717be2387d537bd49025780a1a558anthony            /* Maximum Value within kernel neighbourhood
3081930be614b4595b97cd79ee864a394796740f76adanthony            **
3082930be614b4595b97cd79ee864a394796740f76adanthony            ** NOTE for correct working of this operation for asymetrical
3083930be614b4595b97cd79ee864a394796740f76adanthony            ** kernels, the kernel needs to be applied in its reflected form.
3084930be614b4595b97cd79ee864a394796740f76adanthony            ** That is its values needs to be reversed.
3085930be614b4595b97cd79ee864a394796740f76adanthony            **
3086930be614b4595b97cd79ee864a394796740f76adanthony            ** NOTE: in normal Greyscale Morphology, the kernel value should
3087930be614b4595b97cd79ee864a394796740f76adanthony            ** be added to the real value, this is currently not done, due to
3088930be614b4595b97cd79ee864a394796740f76adanthony            ** the nature of the boolean kernels being used.
3089930be614b4595b97cd79ee864a394796740f76adanthony            **
3090930be614b4595b97cd79ee864a394796740f76adanthony            */
30914fd27e21043be809d66c8202e779255e5b660d2danthony            k = &kernel->values[ kernel->width*kernel->height-1 ];
3092602ab9b30b644a78a4057da93d838a77391ec0acanthony            k_pixels = p;
3093bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (v=0; v < (ssize_t) kernel->height; v++) {
3094bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3095602ab9b30b644a78a4057da93d838a77391ec0acanthony                if ( IsNan(*k) || (*k) < 0.5 ) continue;
30964c08aed51c5899665ade97263692328eea4af106cristy                Maximize(max.red,     (double)
3097ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
3098f34d9b2df49a407af764c79e07d587af0600983aanthony                Maximize(max.green,   (double)
3099ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
3100f34d9b2df49a407af764c79e07d587af0600983aanthony                Maximize(max.blue,    (double)
3101ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3102f34d9b2df49a407af764c79e07d587af0600983aanthony                Maximize(max.alpha,   (double)
3103f34d9b2df49a407af764c79e07d587af0600983aanthony                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
31044c08aed51c5899665ade97263692328eea4af106cristy                if (image->colorspace == CMYKColorspace)
31054c08aed51c5899665ade97263692328eea4af106cristy                  Maximize(max.black,   (double)
3106ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
3107602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3108ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3109602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3110602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3111602ab9b30b644a78a4057da93d838a77391ec0acanthony
31125ef8e94ff55717be2387d537bd49025780a1a558anthony        case HitAndMissMorphology:
31135ef8e94ff55717be2387d537bd49025780a1a558anthony        case ThinningMorphology:
31145ef8e94ff55717be2387d537bd49025780a1a558anthony        case ThickenMorphology:
31155ef8e94ff55717be2387d537bd49025780a1a558anthony            /* Minimum of Foreground Pixel minus Maxumum of Background Pixels
31165ef8e94ff55717be2387d537bd49025780a1a558anthony            **
31175ef8e94ff55717be2387d537bd49025780a1a558anthony            ** NOTE that the kernel is not reflected for this operation,
31185ef8e94ff55717be2387d537bd49025780a1a558anthony            ** and consists of both foreground and background pixel
31195ef8e94ff55717be2387d537bd49025780a1a558anthony            ** neighbourhoods, 0.0 for background, and 1.0 for foreground
31205ef8e94ff55717be2387d537bd49025780a1a558anthony            ** with either Nan or 0.5 values for don't care.
31215ef8e94ff55717be2387d537bd49025780a1a558anthony            **
31224c827ef1be3d42450ff45cedad271e6568be0ea9anthony            ** Note that this will never produce a meaningless negative
31234c827ef1be3d42450ff45cedad271e6568be0ea9anthony            ** result.  Such results can cause Thinning/Thicken to not work
31244c827ef1be3d42450ff45cedad271e6568be0ea9anthony            ** correctly when used against a greyscale image.
31255ef8e94ff55717be2387d537bd49025780a1a558anthony            */
31265ef8e94ff55717be2387d537bd49025780a1a558anthony            k = kernel->values;
31275ef8e94ff55717be2387d537bd49025780a1a558anthony            k_pixels = p;
3128bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (v=0; v < (ssize_t) kernel->height; v++) {
3129bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              for (u=0; u < (ssize_t) kernel->width; u++, k++) {
31305ef8e94ff55717be2387d537bd49025780a1a558anthony                if ( IsNan(*k) ) continue;
31315ef8e94ff55717be2387d537bd49025780a1a558anthony                if ( (*k) > 0.7 )
31325ef8e94ff55717be2387d537bd49025780a1a558anthony                { /* minimim of foreground pixels */
31334c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(min.red,     (double)
3134ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
31354c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(min.green,   (double)
3136ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
31374c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(min.blue,    (double)
3138ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3139f34d9b2df49a407af764c79e07d587af0600983aanthony                  Minimize(min.alpha,(double)
3140f34d9b2df49a407af764c79e07d587af0600983aanthony                    GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
31415ef8e94ff55717be2387d537bd49025780a1a558anthony                  if ( image->colorspace == CMYKColorspace)
31424c08aed51c5899665ade97263692328eea4af106cristy                    Minimize(min.black,(double)
3143ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
31445ef8e94ff55717be2387d537bd49025780a1a558anthony                }
31455ef8e94ff55717be2387d537bd49025780a1a558anthony                else if ( (*k) < 0.3 )
31465ef8e94ff55717be2387d537bd49025780a1a558anthony                { /* maximum of background pixels */
31474c08aed51c5899665ade97263692328eea4af106cristy                  Maximize(max.red,     (double)
3148ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
31494c08aed51c5899665ade97263692328eea4af106cristy                  Maximize(max.green,   (double)
3150ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
31514c08aed51c5899665ade97263692328eea4af106cristy                  Maximize(max.blue,    (double)
3152ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3153f34d9b2df49a407af764c79e07d587af0600983aanthony                  Maximize(max.alpha,(double)
3154f34d9b2df49a407af764c79e07d587af0600983aanthony                    GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
31554c08aed51c5899665ade97263692328eea4af106cristy                  if (image->colorspace == CMYKColorspace)
31564c08aed51c5899665ade97263692328eea4af106cristy                    Maximize(max.black,   (double)
3157ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                      GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
31585ef8e94ff55717be2387d537bd49025780a1a558anthony                }
31595ef8e94ff55717be2387d537bd49025780a1a558anthony              }
3160ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
31615ef8e94ff55717be2387d537bd49025780a1a558anthony            }
31624c827ef1be3d42450ff45cedad271e6568be0ea9anthony            /* Pattern Match if difference is positive */
31635ef8e94ff55717be2387d537bd49025780a1a558anthony            min.red     -= max.red;     Maximize( min.red,     0.0 );
31645ef8e94ff55717be2387d537bd49025780a1a558anthony            min.green   -= max.green;   Maximize( min.green,   0.0 );
31655ef8e94ff55717be2387d537bd49025780a1a558anthony            min.blue    -= max.blue;    Maximize( min.blue,    0.0 );
31664c08aed51c5899665ade97263692328eea4af106cristy            min.black   -= max.black;   Maximize( min.black,   0.0 );
316770b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy            min.alpha -= max.alpha; Maximize( min.alpha, 0.0 );
31685ef8e94ff55717be2387d537bd49025780a1a558anthony            break;
31695ef8e94ff55717be2387d537bd49025780a1a558anthony
31704fd27e21043be809d66c8202e779255e5b660d2danthony        case ErodeIntensityMorphology:
3171930be614b4595b97cd79ee864a394796740f76adanthony            /* Select Pixel with Minimum Intensity within kernel neighbourhood
3172930be614b4595b97cd79ee864a394796740f76adanthony            **
3173930be614b4595b97cd79ee864a394796740f76adanthony            ** WARNING: the intensity test fails for CMYK and does not
3174c406ea496196b962e4bc865ed8f6a4491241c6edanthony            ** take into account the moderating effect of the alpha channel
3175930be614b4595b97cd79ee864a394796740f76adanthony            ** on the intensity.
3176930be614b4595b97cd79ee864a394796740f76adanthony            **
3177930be614b4595b97cd79ee864a394796740f76adanthony            ** NOTE that the kernel is not reflected for this operation!
3178930be614b4595b97cd79ee864a394796740f76adanthony            */
31794fd27e21043be809d66c8202e779255e5b660d2danthony            k = kernel->values;
3180602ab9b30b644a78a4057da93d838a77391ec0acanthony            k_pixels = p;
3181bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (v=0; v < (ssize_t) kernel->height; v++) {
3182bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              for (u=0; u < (ssize_t) kernel->width; u++, k++) {
31834fd27e21043be809d66c8202e779255e5b660d2danthony                if ( IsNan(*k) || (*k) < 0.5 ) continue;
318429188a8682a98d4b7882cca434b170517555fc7danthony                if ( result.red == 0.0 ||
3185ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                     GetPixelIntensity(image,k_pixels+u*GetPixelChannels(image)) < GetPixelIntensity(morphology_image,q) ) {
318629188a8682a98d4b7882cca434b170517555fc7danthony                  /* copy the whole pixel - no channel selection */
3187c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                  SetPixelRed(morphology_image,GetPixelRed(image,
3188ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    k_pixels+u*GetPixelChannels(image)),q);
3189c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                  SetPixelGreen(morphology_image,GetPixelGreen(image,
3190ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    k_pixels+u*GetPixelChannels(image)),q);
3191c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                  SetPixelBlue(morphology_image,GetPixelBlue(image,
3192ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    k_pixels+u*GetPixelChannels(image)),q);
3193c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                  SetPixelAlpha(morphology_image,GetPixelAlpha(image,
3194ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    k_pixels+u*GetPixelChannels(image)),q);
319529188a8682a98d4b7882cca434b170517555fc7danthony                  if ( result.red > 0.0 ) changed++;
319629188a8682a98d4b7882cca434b170517555fc7danthony                  result.red = 1.0;
319729188a8682a98d4b7882cca434b170517555fc7danthony                }
3198602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3199ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3200602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3201602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3202602ab9b30b644a78a4057da93d838a77391ec0acanthony
32034fd27e21043be809d66c8202e779255e5b660d2danthony        case DilateIntensityMorphology:
3204930be614b4595b97cd79ee864a394796740f76adanthony            /* Select Pixel with Maximum Intensity within kernel neighbourhood
3205930be614b4595b97cd79ee864a394796740f76adanthony            **
3206930be614b4595b97cd79ee864a394796740f76adanthony            ** WARNING: the intensity test fails for CMYK and does not
32079eb4f74649b23c053b308ce1152dce51239450baanthony            ** take into account the moderating effect of the alpha channel
32089eb4f74649b23c053b308ce1152dce51239450baanthony            ** on the intensity (yet).
3209930be614b4595b97cd79ee864a394796740f76adanthony            **
3210930be614b4595b97cd79ee864a394796740f76adanthony            ** NOTE for correct working of this operation for asymetrical
3211930be614b4595b97cd79ee864a394796740f76adanthony            ** kernels, the kernel needs to be applied in its reflected form.
3212930be614b4595b97cd79ee864a394796740f76adanthony            ** That is its values needs to be reversed.
3213930be614b4595b97cd79ee864a394796740f76adanthony            */
32144fd27e21043be809d66c8202e779255e5b660d2danthony            k = &kernel->values[ kernel->width*kernel->height-1 ];
3215602ab9b30b644a78a4057da93d838a77391ec0acanthony            k_pixels = p;
3216bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (v=0; v < (ssize_t) kernel->height; v++) {
3217bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
32184fd27e21043be809d66c8202e779255e5b660d2danthony                if ( IsNan(*k) || (*k) < 0.5 ) continue; /* boolean kernel */
321929188a8682a98d4b7882cca434b170517555fc7danthony                if ( result.red == 0.0 ||
3220ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                     GetPixelIntensity(image,k_pixels+u*GetPixelChannels(image)) > GetPixelIntensity(morphology_image,q) ) {
322129188a8682a98d4b7882cca434b170517555fc7danthony                  /* copy the whole pixel - no channel selection */
3222c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                  SetPixelRed(morphology_image,GetPixelRed(image,
3223ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    k_pixels+u*GetPixelChannels(image)),q);
3224c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                  SetPixelGreen(morphology_image,GetPixelGreen(image,
3225ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    k_pixels+u*GetPixelChannels(image)),q);
3226c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                  SetPixelBlue(morphology_image,GetPixelBlue(image,
3227ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    k_pixels+u*GetPixelChannels(image)),q);
3228c3e9048b352dd133cd2b85ff0de964b3d0dd37abcristy                  SetPixelAlpha(morphology_image,GetPixelAlpha(image,
3229ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    k_pixels+u*GetPixelChannels(image)),q);
323029188a8682a98d4b7882cca434b170517555fc7danthony                  if ( result.red > 0.0 ) changed++;
323129188a8682a98d4b7882cca434b170517555fc7danthony                  result.red = 1.0;
323229188a8682a98d4b7882cca434b170517555fc7danthony                }
3233602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3234ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3235602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3236602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3237db60568e12574785101a4ae8d8da076227a0a889anthony
3238f34d9b2df49a407af764c79e07d587af0600983aanthony        case IterativeDistanceMorphology:
3239f34d9b2df49a407af764c79e07d587af0600983aanthony            /* Work out an iterative distance from black edge of a white image
3240f34d9b2df49a407af764c79e07d587af0600983aanthony            ** shape.  Essentually white values are decreased to the smallest
3241f34d9b2df49a407af764c79e07d587af0600983aanthony            ** 'distance from edge' it can find.
3242f34d9b2df49a407af764c79e07d587af0600983aanthony            **
3243f34d9b2df49a407af764c79e07d587af0600983aanthony            ** It works by adding kernel values to the neighbourhood, and and
3244f34d9b2df49a407af764c79e07d587af0600983aanthony            ** select the minimum value found. The kernel is rotated before
3245f34d9b2df49a407af764c79e07d587af0600983aanthony            ** use, so kernel distances match resulting distances, when a user
3246f34d9b2df49a407af764c79e07d587af0600983aanthony            ** provided asymmetric kernel is applied.
3247f34d9b2df49a407af764c79e07d587af0600983aanthony            **
3248f34d9b2df49a407af764c79e07d587af0600983aanthony            **
3249f34d9b2df49a407af764c79e07d587af0600983aanthony            ** This code is almost identical to True GrayScale Morphology But
3250f34d9b2df49a407af764c79e07d587af0600983aanthony            ** not quite.
3251930be614b4595b97cd79ee864a394796740f76adanthony            **
3252f34d9b2df49a407af764c79e07d587af0600983aanthony            ** GreyDilate  Kernel values added, maximum value found Kernel is
3253f34d9b2df49a407af764c79e07d587af0600983aanthony            ** rotated before use.
3254f34d9b2df49a407af764c79e07d587af0600983aanthony            **
3255f34d9b2df49a407af764c79e07d587af0600983aanthony            ** GrayErode:  Kernel values subtracted and minimum value found No
3256f34d9b2df49a407af764c79e07d587af0600983aanthony            ** kernel rotation used.
3257f34d9b2df49a407af764c79e07d587af0600983aanthony            **
3258f34d9b2df49a407af764c79e07d587af0600983aanthony            ** Note the the Iterative Distance method is essentially a
3259f34d9b2df49a407af764c79e07d587af0600983aanthony            ** GrayErode, but with negative kernel values, and kernel
3260f34d9b2df49a407af764c79e07d587af0600983aanthony            ** rotation applied.
3261930be614b4595b97cd79ee864a394796740f76adanthony            */
326229188a8682a98d4b7882cca434b170517555fc7danthony            k = &kernel->values[ kernel->width*kernel->height-1 ];
3263602ab9b30b644a78a4057da93d838a77391ec0acanthony            k_pixels = p;
3264bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (v=0; v < (ssize_t) kernel->height; v++) {
3265bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3266602ab9b30b644a78a4057da93d838a77391ec0acanthony                if ( IsNan(*k) ) continue;
3267f34d9b2df49a407af764c79e07d587af0600983aanthony                Minimize(result.red,     (*k)+(double)
3268f34d9b2df49a407af764c79e07d587af0600983aanthony                     GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
3269f34d9b2df49a407af764c79e07d587af0600983aanthony                Minimize(result.green,   (*k)+(double)
3270f34d9b2df49a407af764c79e07d587af0600983aanthony                     GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
3271f34d9b2df49a407af764c79e07d587af0600983aanthony                Minimize(result.blue,    (*k)+(double)
3272f34d9b2df49a407af764c79e07d587af0600983aanthony                     GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3273f34d9b2df49a407af764c79e07d587af0600983aanthony                Minimize(result.alpha,   (*k)+(double)
3274f34d9b2df49a407af764c79e07d587af0600983aanthony                     GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3275602ab9b30b644a78a4057da93d838a77391ec0acanthony                if ( image->colorspace == CMYKColorspace)
3276f34d9b2df49a407af764c79e07d587af0600983aanthony                  Maximize(result.black, (*k)+(double)
3277f34d9b2df49a407af764c79e07d587af0600983aanthony                      GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
3278602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3279ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3280602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3281602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3282f34d9b2df49a407af764c79e07d587af0600983aanthony
3283602ab9b30b644a78a4057da93d838a77391ec0acanthony        case UndefinedMorphology:
3284602ab9b30b644a78a4057da93d838a77391ec0acanthony        default:
3285602ab9b30b644a78a4057da93d838a77391ec0acanthony            break; /* Do nothing */
328683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      }
32875ef8e94ff55717be2387d537bd49025780a1a558anthony      /* Final mathematics of results (combine with original image?)
32885ef8e94ff55717be2387d537bd49025780a1a558anthony      **
32895ef8e94ff55717be2387d537bd49025780a1a558anthony      ** NOTE: Difference Morphology operators Edge* and *Hat could also
32905ef8e94ff55717be2387d537bd49025780a1a558anthony      ** be done here but works better with iteration as a image difference
32915ef8e94ff55717be2387d537bd49025780a1a558anthony      ** in the controling function (below).  Thicken and Thinning however
32925ef8e94ff55717be2387d537bd49025780a1a558anthony      ** should be done here so thay can be iterated correctly.
32935ef8e94ff55717be2387d537bd49025780a1a558anthony      */
32945ef8e94ff55717be2387d537bd49025780a1a558anthony      switch ( method ) {
32955ef8e94ff55717be2387d537bd49025780a1a558anthony        case HitAndMissMorphology:
32965ef8e94ff55717be2387d537bd49025780a1a558anthony        case ErodeMorphology:
32975ef8e94ff55717be2387d537bd49025780a1a558anthony          result = min;    /* minimum of neighbourhood */
32985ef8e94ff55717be2387d537bd49025780a1a558anthony          break;
32995ef8e94ff55717be2387d537bd49025780a1a558anthony        case DilateMorphology:
33005ef8e94ff55717be2387d537bd49025780a1a558anthony          result = max;    /* maximum of neighbourhood */
33015ef8e94ff55717be2387d537bd49025780a1a558anthony          break;
33025ef8e94ff55717be2387d537bd49025780a1a558anthony        case ThinningMorphology:
33035ef8e94ff55717be2387d537bd49025780a1a558anthony          /* subtract pattern match from original */
33045ef8e94ff55717be2387d537bd49025780a1a558anthony          result.red     -= min.red;
33055ef8e94ff55717be2387d537bd49025780a1a558anthony          result.green   -= min.green;
33065ef8e94ff55717be2387d537bd49025780a1a558anthony          result.blue    -= min.blue;
33074c08aed51c5899665ade97263692328eea4af106cristy          result.black   -= min.black;
3308f34d9b2df49a407af764c79e07d587af0600983aanthony          result.alpha   -= min.alpha;
33095ef8e94ff55717be2387d537bd49025780a1a558anthony          break;
33105ef8e94ff55717be2387d537bd49025780a1a558anthony        case ThickenMorphology:
33114c827ef1be3d42450ff45cedad271e6568be0ea9anthony          /* Add the pattern matchs to the original */
33124c827ef1be3d42450ff45cedad271e6568be0ea9anthony          result.red     += min.red;
33134c827ef1be3d42450ff45cedad271e6568be0ea9anthony          result.green   += min.green;
33144c827ef1be3d42450ff45cedad271e6568be0ea9anthony          result.blue    += min.blue;
33154c08aed51c5899665ade97263692328eea4af106cristy          result.black   += min.black;
3316f34d9b2df49a407af764c79e07d587af0600983aanthony          result.alpha   += min.alpha;
33175ef8e94ff55717be2387d537bd49025780a1a558anthony          break;
33185ef8e94ff55717be2387d537bd49025780a1a558anthony        default:
33195ef8e94ff55717be2387d537bd49025780a1a558anthony          /* result directly calculated or assigned */
33205ef8e94ff55717be2387d537bd49025780a1a558anthony          break;
33215ef8e94ff55717be2387d537bd49025780a1a558anthony      }
33225ef8e94ff55717be2387d537bd49025780a1a558anthony      /* Assign the resulting pixel values - Clamping Result */
332383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      switch ( method ) {
332483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        case UndefinedMorphology:
3325c406ea496196b962e4bc865ed8f6a4491241c6edanthony        case ConvolveMorphology:
332683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        case DilateIntensityMorphology:
332783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        case ErodeIntensityMorphology:
3328930be614b4595b97cd79ee864a394796740f76adanthony          break;  /* full pixel was directly assigned - not a channel method */
332983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        default:
3330ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
33314c08aed51c5899665ade97263692328eea4af106cristy            SetPixelRed(morphology_image,ClampToQuantum(result.red),q);
3332ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
33334c08aed51c5899665ade97263692328eea4af106cristy            SetPixelGreen(morphology_image,ClampToQuantum(result.green),q);
3334ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
33354c08aed51c5899665ade97263692328eea4af106cristy            SetPixelBlue(morphology_image,ClampToQuantum(result.blue),q);
3336ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
33374c08aed51c5899665ade97263692328eea4af106cristy              (image->colorspace == CMYKColorspace))
33384c08aed51c5899665ade97263692328eea4af106cristy            SetPixelBlack(morphology_image,ClampToQuantum(result.black),q);
3339ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
33408a46d827a124555f0c48fb2368ec1bba8e079ab6cristy              (image->alpha_trait == BlendPixelTrait))
33414c08aed51c5899665ade97263692328eea4af106cristy            SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
334283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          break;
334383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      }
33445ef8e94ff55717be2387d537bd49025780a1a558anthony      /* Count up changed pixels */
3345ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      if ((GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(morphology_image,q)) ||
3346ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(morphology_image,q)) ||
3347ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(morphology_image,q)) ||
3348ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(morphology_image,q)) ||
33494c08aed51c5899665ade97263692328eea4af106cristy          ((image->colorspace == CMYKColorspace) &&
3350ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy           (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(morphology_image,q))))
3351c406ea496196b962e4bc865ed8f6a4491241c6edanthony        changed++;  /* The pixel was changed in some way! */
3352ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      p+=GetPixelChannels(image);
3353ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      q+=GetPixelChannels(morphology_image);
335483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    } /* x */
33554c08aed51c5899665ade97263692328eea4af106cristy    if ( SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3356602ab9b30b644a78a4057da93d838a77391ec0acanthony      status=MagickFalse;
3357602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3358602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
3359602ab9b30b644a78a4057da93d838a77391ec0acanthony        MagickBooleanType
3360602ab9b30b644a78a4057da93d838a77391ec0acanthony          proceed;
3361602ab9b30b644a78a4057da93d838a77391ec0acanthony
3362602ab9b30b644a78a4057da93d838a77391ec0acanthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
3363ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy        #pragma omp critical (MagickCore_MorphologyImage)
3364602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
3365602ab9b30b644a78a4057da93d838a77391ec0acanthony        proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
3366602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (proceed == MagickFalse)
3367602ab9b30b644a78a4057da93d838a77391ec0acanthony          status=MagickFalse;
3368602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
336983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  } /* y */
33704c08aed51c5899665ade97263692328eea4af106cristy  morphology_view=DestroyCacheView(morphology_view);
33714c08aed51c5899665ade97263692328eea4af106cristy  image_view=DestroyCacheView(image_view);
3372a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  return(status ? (ssize_t)changed : -1);
3373602ab9b30b644a78a4057da93d838a77391ec0acanthony}
3374602ab9b30b644a78a4057da93d838a77391ec0acanthony
3375a8843c1f815ffad2568ec592d5b446cb1476aab5anthony/* This is almost identical to the MorphologyPrimative() function above,
3376f34d9b2df49a407af764c79e07d587af0600983aanthony** but will apply the primitive directly to the actual image using two
3377f34d9b2df49a407af764c79e07d587af0600983aanthony** passes, once in each direction, with the results of the previous (and
3378f34d9b2df49a407af764c79e07d587af0600983aanthony** current) row being re-used.
3379a8843c1f815ffad2568ec592d5b446cb1476aab5anthony**
3380a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** That is after each row is 'Sync'ed' into the image, the next row will
3381a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** make use of those values as part of the calculation of the next row.
3382a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** It then repeats, but going in the oppisite (bottom-up) direction.
3383a8843c1f815ffad2568ec592d5b446cb1476aab5anthony**
3384f34d9b2df49a407af764c79e07d587af0600983aanthony** Because of this 're-use of results' this function can not make use
3385a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** of multi-threaded, parellel processing.
3386a8843c1f815ffad2568ec592d5b446cb1476aab5anthony*/
3387e698a255629ba03cd125572de7b35b5e21c4ee5danthonystatic ssize_t MorphologyPrimitiveDirect(Image *image,
3388f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const MorphologyMethod method,const KernelInfo *kernel,
3389f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  ExceptionInfo *exception)
3390a8843c1f815ffad2568ec592d5b446cb1476aab5anthony{
3391a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  CacheView
3392a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    *auth_view,
3393a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    *virt_view;
3394a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3395a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  MagickBooleanType
3396a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    status;
3397a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3398a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  MagickOffsetType
3399a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    progress;
3400a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3401a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  ssize_t
3402a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    y, offx, offy;
3403a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3404a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  size_t
3405db60568e12574785101a4ae8d8da076227a0a889anthony    virt_width,
3406a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    changed;
3407a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3408a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  status=MagickTrue;
3409a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  changed=0;
3410a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  progress=0;
3411a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3412a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(image != (Image *) NULL);
3413a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(image->signature == MagickSignature);
3414a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(kernel != (KernelInfo *) NULL);
3415a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(kernel->signature == MagickSignature);
3416a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(exception != (ExceptionInfo *) NULL);
3417a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(exception->signature == MagickSignature);
3418a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3419a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* Some methods (including convolve) needs use a reflected kernel.
3420a8843c1f815ffad2568ec592d5b446cb1476aab5anthony   * Adjust 'origin' offsets to loop though kernel as a reflection.
3421a8843c1f815ffad2568ec592d5b446cb1476aab5anthony   */
3422a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  offx = kernel->x;
3423a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  offy = kernel->y;
3424a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  switch(method) {
3425a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case DistanceMorphology:
3426e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case VoronoiMorphology:
3427a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      /* kernel needs to used with reflection about origin */
3428a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      offx = (ssize_t) kernel->width-offx-1;
3429a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      offy = (ssize_t) kernel->height-offy-1;
3430a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3431a8843c1f815ffad2568ec592d5b446cb1476aab5anthony#if 0
3432a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case ?????Morphology:
3433a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      /* kernel is used as is, without reflection */
3434a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3435a8843c1f815ffad2568ec592d5b446cb1476aab5anthony#endif
3436a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    default:
3437a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      assert("Not a PrimativeDirect Morphology Method" != (char *) NULL);
3438a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3439a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  }
3440a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3441a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* DO NOT THREAD THIS CODE! */
3442a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* two views into same image (virtual, and actual) */
344346ff2676b1044ea4101ac7a59b83289cd8f6cfdacristy  virt_view=AcquireVirtualCacheView(image,exception);
344446ff2676b1044ea4101ac7a59b83289cd8f6cfdacristy  auth_view=AcquireAuthenticCacheView(image,exception);
3445db60568e12574785101a4ae8d8da076227a0a889anthony  virt_width=image->columns+kernel->width-1;
3446a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3447a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  for (y=0; y < (ssize_t) image->rows; y++)
3448a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  {
34494c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
3450a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      *restrict p;
3451a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
34524c08aed51c5899665ade97263692328eea4af106cristy    register Quantum
3453a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      *restrict q;
3454a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3455a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    register ssize_t
3456a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      x;
3457a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3458a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ssize_t
3459a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      r;
3460a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3461a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    /* NOTE read virtual pixels, and authentic pixels, from the same image!
3462a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ** we read using virtual to get virtual pixel handling, but write back
3463a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ** into the same image.
3464a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    **
3465a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ** Only top half of kernel is processed as we do a single pass downward
3466a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ** through the image iterating the distance function as we go.
3467a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    */
3468a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (status == MagickFalse)
3469a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
34704c08aed51c5899665ade97263692328eea4af106cristy    p=GetCacheViewVirtualPixels(virt_view,-offx,y-offy,virt_width,(size_t)
34714c08aed51c5899665ade97263692328eea4af106cristy      offy+1,exception);
3472db60568e12574785101a4ae8d8da076227a0a889anthony    q=GetCacheViewAuthenticPixels(auth_view, 0, y, image->columns, 1,
34734c08aed51c5899665ade97263692328eea4af106cristy      exception);
34744c08aed51c5899665ade97263692328eea4af106cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3475a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      status=MagickFalse;
3476a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (status == MagickFalse)
3477a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3478a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3479a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    /* offset to origin in 'p'. while 'q' points to it directly */
3480aecf4bd539fe0591d67bf1ec961ac68b5490171fcristy    r = (ssize_t) virt_width*offy + offx;
3481a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3482a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    for (x=0; x < (ssize_t) image->columns; x++)
3483a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    {
3484d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      PixelInfo
3485d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        result;
3486a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3487d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      register const MagickRealType
3488a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        *restrict k;
3489a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
34904c08aed51c5899665ade97263692328eea4af106cristy      register const Quantum
3491a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        *restrict k_pixels;
3492a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3493d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      register ssize_t
3494d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        u;
3495d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy
3496d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      ssize_t
3497d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        v;
3498a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3499e698a255629ba03cd125572de7b35b5e21c4ee5danthony      /* Starting Defaults */
35004c08aed51c5899665ade97263692328eea4af106cristy      GetPixelInfo(image,&result);
3501803640d20a6a664315eddfff6f8531d0c5e0871dcristy      GetPixelInfoPixel(image,q,&result);
3502e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if ( method != VoronoiMorphology )
35034c08aed51c5899665ade97263692328eea4af106cristy        result.alpha = QuantumRange - result.alpha;
3504a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3505a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      switch ( method ) {
3506a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        case DistanceMorphology:
3507a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            /* Add kernel Value and select the minimum value found. */
3508a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            k = &kernel->values[ kernel->width*kernel->height-1 ];
3509a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            k_pixels = p;
3510a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            for (v=0; v <= (ssize_t) offy; v++) {
3511a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3512a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                if ( IsNan(*k) ) continue;
35134c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.red,     (*k)+
3514ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
35154c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.green,   (*k)+
3516ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
35174c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.blue,    (*k)+
3518ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
35194c08aed51c5899665ade97263692328eea4af106cristy                if (image->colorspace == CMYKColorspace)
35204c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(result.black,(*k)+
3521ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
35224c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.alpha, (*k)+
3523ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3524a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              }
3525ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3526a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            }
3527a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            /* repeat with the just processed pixels of this row */
3528db60568e12574785101a4ae8d8da076227a0a889anthony            k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3529ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            k_pixels = q-offx*GetPixelChannels(image);
3530a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              for (u=0; u < (ssize_t) offx; u++, k--) {
3531a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                if ( x+u-offx < 0 ) continue;  /* off the edge! */
3532a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                if ( IsNan(*k) ) continue;
35334c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.red,     (*k)+
3534ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
35354c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.green,   (*k)+
3536ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
35374c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.blue,    (*k)+
3538ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
35394c08aed51c5899665ade97263692328eea4af106cristy                if (image->colorspace == CMYKColorspace)
35404c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(result.black,(*k)+
3541ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
35424c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.alpha,(*k)+
3543ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3544a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              }
3545a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            break;
3546e698a255629ba03cd125572de7b35b5e21c4ee5danthony        case VoronoiMorphology:
3547ea068a53d23d6dca08f1bce44c8937d54f83b983anthony            /* Apply Distance to 'Matte' channel, while coping the color
3548ea068a53d23d6dca08f1bce44c8937d54f83b983anthony            ** values of the closest pixel.
3549e698a255629ba03cd125572de7b35b5e21c4ee5danthony            **
3550e698a255629ba03cd125572de7b35b5e21c4ee5danthony            ** This is experimental, and realy the 'alpha' component should
3551f34d9b2df49a407af764c79e07d587af0600983aanthony            ** be completely separate 'masking' channel so that alpha can
3552f34d9b2df49a407af764c79e07d587af0600983aanthony            ** also be used as part of the results.
3553e698a255629ba03cd125572de7b35b5e21c4ee5danthony            */
3554e698a255629ba03cd125572de7b35b5e21c4ee5danthony            k = &kernel->values[ kernel->width*kernel->height-1 ];
3555e698a255629ba03cd125572de7b35b5e21c4ee5danthony            k_pixels = p;
3556e698a255629ba03cd125572de7b35b5e21c4ee5danthony            for (v=0; v <= (ssize_t) offy; v++) {
3557e698a255629ba03cd125572de7b35b5e21c4ee5danthony              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3558e698a255629ba03cd125572de7b35b5e21c4ee5danthony                if ( IsNan(*k) ) continue;
3559ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
3560e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3561803640d20a6a664315eddfff6f8531d0c5e0871dcristy                    GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
35624a06a14c06f675b6a2052a3c814e7da54877a733cristy                      &result);
35634c08aed51c5899665ade97263692328eea4af106cristy                    result.alpha += *k;
3564e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3565e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3566ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3567e698a255629ba03cd125572de7b35b5e21c4ee5danthony            }
3568e698a255629ba03cd125572de7b35b5e21c4ee5danthony            /* repeat with the just processed pixels of this row */
3569e698a255629ba03cd125572de7b35b5e21c4ee5danthony            k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3570ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            k_pixels = q-offx*GetPixelChannels(image);
3571e698a255629ba03cd125572de7b35b5e21c4ee5danthony              for (u=0; u < (ssize_t) offx; u++, k--) {
3572e698a255629ba03cd125572de7b35b5e21c4ee5danthony                if ( x+u-offx < 0 ) continue;  /* off the edge! */
3573e698a255629ba03cd125572de7b35b5e21c4ee5danthony                if ( IsNan(*k) ) continue;
3574ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
3575e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3576803640d20a6a664315eddfff6f8531d0c5e0871dcristy                    GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
35774a06a14c06f675b6a2052a3c814e7da54877a733cristy                      &result);
35784c08aed51c5899665ade97263692328eea4af106cristy                    result.alpha += *k;
3579e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3580e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3581e698a255629ba03cd125572de7b35b5e21c4ee5danthony            break;
3582a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        default:
3583a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          /* result directly calculated or assigned */
3584a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          break;
3585a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      }
3586a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      /* Assign the resulting pixel values - Clamping Result */
3587e698a255629ba03cd125572de7b35b5e21c4ee5danthony      switch ( method ) {
3588e698a255629ba03cd125572de7b35b5e21c4ee5danthony        case VoronoiMorphology:
3589803640d20a6a664315eddfff6f8531d0c5e0871dcristy          SetPixelInfoPixel(image,&result,q);
3590e698a255629ba03cd125572de7b35b5e21c4ee5danthony          break;
3591e698a255629ba03cd125572de7b35b5e21c4ee5danthony        default:
3592ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
35934c08aed51c5899665ade97263692328eea4af106cristy            SetPixelRed(image,ClampToQuantum(result.red),q);
3594ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
35954c08aed51c5899665ade97263692328eea4af106cristy            SetPixelGreen(image,ClampToQuantum(result.green),q);
3596ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
35974c08aed51c5899665ade97263692328eea4af106cristy            SetPixelBlue(image,ClampToQuantum(result.blue),q);
3598ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
35994c08aed51c5899665ade97263692328eea4af106cristy              (image->colorspace == CMYKColorspace))
36004c08aed51c5899665ade97263692328eea4af106cristy            SetPixelBlack(image,ClampToQuantum(result.black),q);
3601ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0 &&
36028a46d827a124555f0c48fb2368ec1bba8e079ab6cristy              (image->alpha_trait == BlendPixelTrait))
36034c08aed51c5899665ade97263692328eea4af106cristy            SetPixelAlpha(image,ClampToQuantum(result.alpha),q);
3604e698a255629ba03cd125572de7b35b5e21c4ee5danthony          break;
3605e698a255629ba03cd125572de7b35b5e21c4ee5danthony      }
3606a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      /* Count up changed pixels */
3607ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      if ((GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(image,q)) ||
3608ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(image,q)) ||
3609ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(image,q)) ||
3610ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(image,q)) ||
36114c08aed51c5899665ade97263692328eea4af106cristy          ((image->colorspace == CMYKColorspace) &&
3612ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy           (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(image,q))))
3613a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        changed++;  /* The pixel was changed in some way! */
3614a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3615ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      p+=GetPixelChannels(image); /* increment pixel buffers */
3616ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      q+=GetPixelChannels(image);
3617a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    } /* x */
36184fd27e21043be809d66c8202e779255e5b660d2danthony
3619a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if ( SyncCacheViewAuthenticPixels(auth_view,exception) == MagickFalse)
3620a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      status=MagickFalse;
3621a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3622a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      if ( SetImageProgress(image,MorphologyTag,progress++,image->rows)
3623a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                == MagickFalse )
3624a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        status=MagickFalse;
3625a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3626a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  } /* y */
3627a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3628a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* Do the reversed pass through the image */
3629a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  for (y=(ssize_t)image->rows-1; y >= 0; y--)
3630a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  {
36314c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
3632a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      *restrict p;
3633a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
36344c08aed51c5899665ade97263692328eea4af106cristy    register Quantum
3635a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      *restrict q;
3636a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3637a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    register ssize_t
3638a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      x;
3639a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3640a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ssize_t
3641a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      r;
3642a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3643db60568e12574785101a4ae8d8da076227a0a889anthony    if (status == MagickFalse)
3644db60568e12574785101a4ae8d8da076227a0a889anthony      break;
3645a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    /* NOTE read virtual pixels, and authentic pixels, from the same image!
3646a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ** we read using virtual to get virtual pixel handling, but write back
3647a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ** into the same image.
3648a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    **
3649a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ** Only the bottom half of the kernel will be processes as we
3650a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    ** up the image.
3651a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    */
36524c08aed51c5899665ade97263692328eea4af106cristy    p=GetCacheViewVirtualPixels(virt_view,-offx,y,virt_width,(size_t)
36534c08aed51c5899665ade97263692328eea4af106cristy      kernel->y+1,exception);
3654db60568e12574785101a4ae8d8da076227a0a889anthony    q=GetCacheViewAuthenticPixels(auth_view, 0, y, image->columns, 1,
36554c08aed51c5899665ade97263692328eea4af106cristy      exception);
36564c08aed51c5899665ade97263692328eea4af106cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3657a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      status=MagickFalse;
3658a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (status == MagickFalse)
3659a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3660a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3661a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    /* adjust positions to end of row */
3662ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy    p += (image->columns-1)*GetPixelChannels(image);
3663ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy    q += (image->columns-1)*GetPixelChannels(image);
3664a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3665a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    /* offset to origin in 'p'. while 'q' points to it directly */
3666a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    r = offx;
3667a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3668a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    for (x=(ssize_t)image->columns-1; x >= 0; x--)
3669a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    {
3670d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      PixelInfo
3671d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        result;
3672a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3673d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      register const MagickRealType
3674a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        *restrict k;
3675a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
36764c08aed51c5899665ade97263692328eea4af106cristy      register const Quantum
3677a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        *restrict k_pixels;
3678a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3679d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      register ssize_t
3680d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        u;
3681d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy
3682d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      ssize_t
3683d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        v;
3684a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3685e698a255629ba03cd125572de7b35b5e21c4ee5danthony      /* Default - previously modified pixel */
36864c08aed51c5899665ade97263692328eea4af106cristy      GetPixelInfo(image,&result);
3687803640d20a6a664315eddfff6f8531d0c5e0871dcristy      GetPixelInfoPixel(image,q,&result);
3688e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if ( method != VoronoiMorphology )
36894c08aed51c5899665ade97263692328eea4af106cristy        result.alpha = QuantumRange - result.alpha;
3690a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3691a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      switch ( method ) {
3692a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        case DistanceMorphology:
3693a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            /* Add kernel Value and select the minimum value found. */
3694db60568e12574785101a4ae8d8da076227a0a889anthony            k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3695a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            k_pixels = p;
3696a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            for (v=offy; v < (ssize_t) kernel->height; v++) {
3697a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3698a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                if ( IsNan(*k) ) continue;
36994c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.red,     (*k)+
3700ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
37014c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.green,   (*k)+
3702ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
37034c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.blue,    (*k)+
3704ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3705a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                if ( image->colorspace == CMYKColorspace)
37064c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(result.black,(*k)+
3707ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
37084c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.alpha, (*k)+
3709ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3710a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              }
3711ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3712a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            }
3713a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            /* repeat with the just processed pixels of this row */
3714db60568e12574785101a4ae8d8da076227a0a889anthony            k = &kernel->values[ kernel->width*(kernel->y)+kernel->x-1 ];
3715ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            k_pixels = q-offx*GetPixelChannels(image);
3716a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              for (u=offx+1; u < (ssize_t) kernel->width; u++, k--) {
3717e698a255629ba03cd125572de7b35b5e21c4ee5danthony                if ( (x+u-offx) >= (ssize_t)image->columns ) continue;
3718a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                if ( IsNan(*k) ) continue;
37194c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.red,     (*k)+
3720ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
37214c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.green,   (*k)+
3722ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
37234c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.blue,    (*k)+
3724ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
3725a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                if ( image->colorspace == CMYKColorspace)
37264c08aed51c5899665ade97263692328eea4af106cristy                  Minimize(result.black,   (*k)+
3727ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
37284c08aed51c5899665ade97263692328eea4af106cristy                Minimize(result.alpha, (*k)+
3729ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
3730a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              }
3731a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            break;
3732e698a255629ba03cd125572de7b35b5e21c4ee5danthony        case VoronoiMorphology:
3733e698a255629ba03cd125572de7b35b5e21c4ee5danthony            /* Apply Distance to 'Matte' channel, coping the closest color.
3734e698a255629ba03cd125572de7b35b5e21c4ee5danthony            **
3735e698a255629ba03cd125572de7b35b5e21c4ee5danthony            ** This is experimental, and realy the 'alpha' component should
3736e698a255629ba03cd125572de7b35b5e21c4ee5danthony            ** be completely separate 'masking' channel.
3737e698a255629ba03cd125572de7b35b5e21c4ee5danthony            */
3738e698a255629ba03cd125572de7b35b5e21c4ee5danthony            k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
3739e698a255629ba03cd125572de7b35b5e21c4ee5danthony            k_pixels = p;
3740e698a255629ba03cd125572de7b35b5e21c4ee5danthony            for (v=offy; v < (ssize_t) kernel->height; v++) {
3741e698a255629ba03cd125572de7b35b5e21c4ee5danthony              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
3742e698a255629ba03cd125572de7b35b5e21c4ee5danthony                if ( IsNan(*k) ) continue;
3743ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
3744e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3745803640d20a6a664315eddfff6f8531d0c5e0871dcristy                    GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
37464a06a14c06f675b6a2052a3c814e7da54877a733cristy                      &result);
37474c08aed51c5899665ade97263692328eea4af106cristy                    result.alpha += *k;
3748e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3749e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3750ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              k_pixels += virt_width*GetPixelChannels(image);
3751e698a255629ba03cd125572de7b35b5e21c4ee5danthony            }
3752e698a255629ba03cd125572de7b35b5e21c4ee5danthony            /* repeat with the just processed pixels of this row */
3753e698a255629ba03cd125572de7b35b5e21c4ee5danthony            k = &kernel->values[ kernel->width*(kernel->y)+kernel->x-1 ];
3754ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy            k_pixels = q-offx*GetPixelChannels(image);
3755e698a255629ba03cd125572de7b35b5e21c4ee5danthony              for (u=offx+1; u < (ssize_t) kernel->width; u++, k--) {
3756e698a255629ba03cd125572de7b35b5e21c4ee5danthony                if ( (x+u-offx) >= (ssize_t)image->columns ) continue;
3757e698a255629ba03cd125572de7b35b5e21c4ee5danthony                if ( IsNan(*k) ) continue;
3758ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy                if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
3759e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3760803640d20a6a664315eddfff6f8531d0c5e0871dcristy                    GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
37614a06a14c06f675b6a2052a3c814e7da54877a733cristy                      &result);
37624c08aed51c5899665ade97263692328eea4af106cristy                    result.alpha += *k;
3763e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3764e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3765e698a255629ba03cd125572de7b35b5e21c4ee5danthony            break;
3766a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        default:
3767a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          /* result directly calculated or assigned */
3768a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          break;
3769a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      }
3770a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      /* Assign the resulting pixel values - Clamping Result */
3771e698a255629ba03cd125572de7b35b5e21c4ee5danthony      switch ( method ) {
3772e698a255629ba03cd125572de7b35b5e21c4ee5danthony        case VoronoiMorphology:
3773803640d20a6a664315eddfff6f8531d0c5e0871dcristy          SetPixelInfoPixel(image,&result,q);
3774e698a255629ba03cd125572de7b35b5e21c4ee5danthony          break;
3775e698a255629ba03cd125572de7b35b5e21c4ee5danthony        default:
3776ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
37774c08aed51c5899665ade97263692328eea4af106cristy            SetPixelRed(image,ClampToQuantum(result.red),q);
3778ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
37794c08aed51c5899665ade97263692328eea4af106cristy            SetPixelGreen(image,ClampToQuantum(result.green),q);
3780ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
37814c08aed51c5899665ade97263692328eea4af106cristy            SetPixelBlue(image,ClampToQuantum(result.blue),q);
3782ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
37834c08aed51c5899665ade97263692328eea4af106cristy              (image->colorspace == CMYKColorspace))
37844c08aed51c5899665ade97263692328eea4af106cristy            SetPixelBlack(image,ClampToQuantum(result.black),q);
3785ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0 &&
37868a46d827a124555f0c48fb2368ec1bba8e079ab6cristy              (image->alpha_trait == BlendPixelTrait))
37874c08aed51c5899665ade97263692328eea4af106cristy            SetPixelAlpha(image,ClampToQuantum(result.alpha),q);
3788e698a255629ba03cd125572de7b35b5e21c4ee5danthony          break;
3789e698a255629ba03cd125572de7b35b5e21c4ee5danthony      }
3790a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      /* Count up changed pixels */
3791ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      if (   (GetPixelRed(image,p+r*GetPixelChannels(image)) != GetPixelRed(image,q))
3792ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          || (GetPixelGreen(image,p+r*GetPixelChannels(image)) != GetPixelGreen(image,q))
3793ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          || (GetPixelBlue(image,p+r*GetPixelChannels(image)) != GetPixelBlue(image,q))
3794ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy          || (GetPixelAlpha(image,p+r*GetPixelChannels(image)) != GetPixelAlpha(image,q))
37954c08aed51c5899665ade97263692328eea4af106cristy          || ((image->colorspace == CMYKColorspace) &&
3796ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy              (GetPixelBlack(image,p+r*GetPixelChannels(image)) != GetPixelBlack(image,q))))
3797a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        changed++;  /* The pixel was changed in some way! */
3798a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3799ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      p-=GetPixelChannels(image); /* go backward through pixel buffers */
3800ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      q-=GetPixelChannels(image);
3801a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    } /* x */
3802a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if ( SyncCacheViewAuthenticPixels(auth_view,exception) == MagickFalse)
3803a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      status=MagickFalse;
3804a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3805a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      if ( SetImageProgress(image,MorphologyTag,progress++,image->rows)
3806a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                == MagickFalse )
3807a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        status=MagickFalse;
3808a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3809a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  } /* y */
3810e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3811a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  auth_view=DestroyCacheView(auth_view);
3812a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  virt_view=DestroyCacheView(virt_view);
3813aecf4bd539fe0591d67bf1ec961ac68b5490171fcristy  return(status ? (ssize_t) changed : -1);
3814a8843c1f815ffad2568ec592d5b446cb1476aab5anthony}
3815a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3816f34d9b2df49a407af764c79e07d587af0600983aanthony/* Apply a Morphology by calling one of the above low level primitive
3817f34d9b2df49a407af764c79e07d587af0600983aanthony** application functions.  This function handles any iteration loops,
3818f34d9b2df49a407af764c79e07d587af0600983aanthony** composition or re-iteration of results, and compound morphology methods
3819f34d9b2df49a407af764c79e07d587af0600983aanthony** that is based on multiple low-level (staged) morphology methods.
3820a8843c1f815ffad2568ec592d5b446cb1476aab5anthony**
382122de2722b682eb405b60ec6022a7546df994674eanthony** Basically this provides the complex glue between the requested morphology
3822a8843c1f815ffad2568ec592d5b446cb1476aab5anthony** method and raw low-level implementation (above).
3823a8843c1f815ffad2568ec592d5b446cb1476aab5anthony*/
3824cf592636b16d028b14c5fa9dffdc3a94eae7c2f3cristyMagickPrivate Image *MorphologyApply(const Image *image,
3825f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const MorphologyMethod method, const ssize_t iterations,
3826f46d42620631d2581e0b6a56456e203e17c427c8anthony  const KernelInfo *kernel, const CompositeOperator compose,const double bias,
3827f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  ExceptionInfo *exception)
3828602ab9b30b644a78a4057da93d838a77391ec0acanthony{
38291cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy  CompositeOperator
38301cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy    curr_compose;
38311cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy
3832602ab9b30b644a78a4057da93d838a77391ec0acanthony  Image
383347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *curr_image,    /* Image we are working with or iterating */
3834a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    *work_image,    /* secondary image for primitive iteration */
383547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *save_image,    /* saved image - for 'edge' method only */
383647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *rslt_image;    /* resultant image - after multi-kernel handling */
3837602ab9b30b644a78a4057da93d838a77391ec0acanthony
38384fd27e21043be809d66c8202e779255e5b660d2danthony  KernelInfo
383947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *reflected_kernel, /* A reflected copy of the kernel (if needed) */
384047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *norm_kernel,      /* the current normal un-reflected kernel */
384147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *rflt_kernel,      /* the current reflected kernel (if needed) */
384247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *this_kernel;      /* the kernel being applied */
38434fd27e21043be809d66c8202e779255e5b660d2danthony
38444fd27e21043be809d66c8202e779255e5b660d2danthony  MorphologyMethod
3845a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    primitive;      /* the current morphology primitive being applied */
38469eb4f74649b23c053b308ce1152dce51239450baanthony
38479eb4f74649b23c053b308ce1152dce51239450baanthony  CompositeOperator
384847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_compose;   /* multi-kernel compose method for results to use */
384947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
385047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  MagickBooleanType
3851e698a255629ba03cd125572de7b35b5e21c4ee5danthony    special,        /* do we use a direct modify function? */
385247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    verbose;        /* verbose output of results */
38534fd27e21043be809d66c8202e779255e5b660d2danthony
3854bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  size_t
3855a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    method_loop,    /* Loop 1: number of compound method iterations (norm 1) */
385647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_limit,   /*         maximum number of compound method iterations */
385747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_number,  /* Loop 2: the kernel number being applied */
3858a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    stage_loop,     /* Loop 3: primitive loop for compound morphology */
3859a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    stage_limit,    /*         how many primitives are in this compound */
3860a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    kernel_loop,    /* Loop 4: iterate the kernel over image */
386147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_limit,   /*         number of times to iterate kernel */
3862a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    count,          /* total count of primitive steps applied */
386347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_changed, /* total count of changed using iterated kernel */
386447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_changed; /* total count of changed over method iteration */
386547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
3866a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  ssize_t
3867a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    changed;        /* number pixels changed by last primitive operation */
3868a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
386947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  char
387047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    v_info[80];
38711b2bc0a7da432e6e1cc0480280402df213faa940anthony
3872602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(image != (Image *) NULL);
3873602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(image->signature == MagickSignature);
38744fd27e21043be809d66c8202e779255e5b660d2danthony  assert(kernel != (KernelInfo *) NULL);
38754fd27e21043be809d66c8202e779255e5b660d2danthony  assert(kernel->signature == MagickSignature);
3876602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(exception != (ExceptionInfo *) NULL);
3877602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(exception->signature == MagickSignature);
3878602ab9b30b644a78a4057da93d838a77391ec0acanthony
3879a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  count = 0;      /* number of low-level morphology primitives performed */
3880602ab9b30b644a78a4057da93d838a77391ec0acanthony  if ( iterations == 0 )
388147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    return((Image *)NULL);   /* null operation - nothing to do! */
3882602ab9b30b644a78a4057da93d838a77391ec0acanthony
3883bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  kernel_limit = (size_t) iterations;
388447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( iterations < 0 )  /* negative interations = infinite (well alomst) */
3885e698a255629ba03cd125572de7b35b5e21c4ee5danthony     kernel_limit = image->columns>image->rows ? image->columns : image->rows;
388628ad1d779b6ca95852e860514185a7a97e06af77anthony
38876f2013165d72f7d8ef5f66bb9453126d88113809anthony  verbose = IsStringTrue(GetImageArtifact(image,"verbose"));
38884f1dcb76c95ef6410f2957ca9e7e1d391cee0d02anthony
38899eb4f74649b23c053b308ce1152dce51239450baanthony  /* initialise for cleanup */
389047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  curr_image = (Image *) image;
38911cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy  curr_compose = image->compose;
3892aecf4bd539fe0591d67bf1ec961ac68b5490171fcristy  (void) curr_compose;
389347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  work_image = save_image = rslt_image = (Image *) NULL;
389447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  reflected_kernel = (KernelInfo *) NULL;
38954fd27e21043be809d66c8202e779255e5b660d2danthony
389647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Initialize specific methods
389747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   * + which loop should use the given iteratations
3898a8843c1f815ffad2568ec592d5b446cb1476aab5anthony   * + how many primitives make up the compound morphology
389947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   * + multi-kernel compose method to use (by default)
390047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   */
390147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_limit = 1;       /* just do method once, unless otherwise set */
3902ea61f01656bb0f9074677452017cc559e54093faanthony  stage_limit = 1;        /* assume method is not a compound */
39034ee950098ad0166bbbc85c3a59bc079cd321384aglennrp  special = MagickFalse;   /* assume it is NOT a direct modify primitive */
390447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  rslt_compose = compose; /* and we are composing multi-kernels as given */
39059eb4f74649b23c053b308ce1152dce51239450baanthony  switch( method ) {
3906a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case SmoothMorphology:  /* 4 primitive compound morphology */
390747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      stage_limit = 4;
3908602ab9b30b644a78a4057da93d838a77391ec0acanthony      break;
3909a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case OpenMorphology:    /* 2 primitive compound morphology */
3910930be614b4595b97cd79ee864a394796740f76adanthony    case OpenIntensityMorphology:
391147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case TopHatMorphology:
391247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseMorphology:
39134fd27e21043be809d66c8202e779255e5b660d2danthony    case CloseIntensityMorphology:
391447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case BottomHatMorphology:
391547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case EdgeMorphology:
391647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      stage_limit = 2;
3917602ab9b30b644a78a4057da93d838a77391ec0acanthony      break;
39189eb4f74649b23c053b308ce1152dce51239450baanthony    case HitAndMissMorphology:
391947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      rslt_compose = LightenCompositeOp;  /* Union of multi-kernel results */
39203ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony      /* FALL THUR */
3921c3e48258f3253188894e783dcdfd03562f7ab2c5anthony    case ThinningMorphology:
39229eb4f74649b23c053b308ce1152dce51239450baanthony    case ThickenMorphology:
39233ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony      method_limit = kernel_limit;  /* iterate the whole method */
3924c3e48258f3253188894e783dcdfd03562f7ab2c5anthony      kernel_limit = 1;             /* do not do kernel iteration  */
392547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
3926a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case DistanceMorphology:
3927e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case VoronoiMorphology:
3928f34d9b2df49a407af764c79e07d587af0600983aanthony      special = MagickTrue;         /* use special direct primative */
3929a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
393047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    default:
39319eb4f74649b23c053b308ce1152dce51239450baanthony      break;
39329eb4f74649b23c053b308ce1152dce51239450baanthony  }
3933602ab9b30b644a78a4057da93d838a77391ec0acanthony
3934e698a255629ba03cd125572de7b35b5e21c4ee5danthony  /* Apply special methods with special requirments
3935e698a255629ba03cd125572de7b35b5e21c4ee5danthony  ** For example, single run only, or post-processing requirements
3936e698a255629ba03cd125572de7b35b5e21c4ee5danthony  */
3937e698a255629ba03cd125572de7b35b5e21c4ee5danthony  if ( special == MagickTrue )
3938e698a255629ba03cd125572de7b35b5e21c4ee5danthony    {
3939e698a255629ba03cd125572de7b35b5e21c4ee5danthony      rslt_image=CloneImage(image,0,0,MagickTrue,exception);
3940e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if (rslt_image == (Image *) NULL)
3941e698a255629ba03cd125572de7b35b5e21c4ee5danthony        goto error_cleanup;
3942574cc26500992189f637cd1cdf93d0654e7df7aecristy      if (SetImageStorageClass(rslt_image,DirectClass,exception) == MagickFalse)
3943574cc26500992189f637cd1cdf93d0654e7df7aecristy        goto error_cleanup;
3944e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3945e698a255629ba03cd125572de7b35b5e21c4ee5danthony      changed = MorphologyPrimitiveDirect(rslt_image, method,
3946f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy         kernel, exception);
3947e698a255629ba03cd125572de7b35b5e21c4ee5danthony
39487bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony      if ( IfMagickTrue(verbose) )
39495acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) (void) FormatLocaleFile(stderr,
39501e604812fad85bb96f757a2393015ae3d061c39acristy          "%s:%.20g.%.20g #%.20g => Changed %.20g\n",
39511e604812fad85bb96f757a2393015ae3d061c39acristy          CommandOptionToMnemonic(MagickMorphologyOptions, method),
39521e604812fad85bb96f757a2393015ae3d061c39acristy          1.0,0.0,1.0, (double) changed);
3953e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3954e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if ( changed < 0 )
3955e698a255629ba03cd125572de7b35b5e21c4ee5danthony        goto error_cleanup;
3956e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3957e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if ( method == VoronoiMorphology ) {
3958e698a255629ba03cd125572de7b35b5e21c4ee5danthony        /* Preserve the alpha channel of input image - but turned off */
395963240888c3975789a09c2494a4654b523931df96cristy        (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
396063240888c3975789a09c2494a4654b523931df96cristy          exception);
3961feb3e9695150978a5d2372d3fe2f60466a7c8066cristy        (void) CompositeImage(rslt_image,image,CopyAlphaCompositeOp,
396239172408bad7ef2ef00a815fa9abf9979e7857cbcristy          MagickTrue,0,0,exception);
396363240888c3975789a09c2494a4654b523931df96cristy        (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
396463240888c3975789a09c2494a4654b523931df96cristy          exception);
3965e698a255629ba03cd125572de7b35b5e21c4ee5danthony      }
3966e698a255629ba03cd125572de7b35b5e21c4ee5danthony      goto exit_cleanup;
3967e698a255629ba03cd125572de7b35b5e21c4ee5danthony    }
3968e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3969c3e48258f3253188894e783dcdfd03562f7ab2c5anthony  /* Handle user (caller) specified multi-kernel composition method */
397047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( compose != UndefinedCompositeOp )
397147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_compose = compose;  /* override default composition for method */
397247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( rslt_compose == UndefinedCompositeOp )
397347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_compose = NoCompositeOp; /* still not defined! Then re-iterate */
39744fd27e21043be809d66c8202e779255e5b660d2danthony
3975a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* Some methods require a reflected kernel to use with primitives.
3976c3e48258f3253188894e783dcdfd03562f7ab2c5anthony   * Create the reflected kernel for those methods. */
397747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  switch ( method ) {
397847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CorrelateMorphology:
397947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseMorphology:
398047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseIntensityMorphology:
398147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case BottomHatMorphology:
398247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case SmoothMorphology:
398347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      reflected_kernel = CloneKernelInfo(kernel);
398447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if (reflected_kernel == (KernelInfo *) NULL)
398547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        goto error_cleanup;
398647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      RotateKernelInfo(reflected_kernel,180);
398747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
398847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    default:
398947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
399047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  }
3991602ab9b30b644a78a4057da93d838a77391ec0acanthony
3992e698a255629ba03cd125572de7b35b5e21c4ee5danthony  /* Loops around more primitive morpholgy methods
3993e698a255629ba03cd125572de7b35b5e21c4ee5danthony  **  erose, dilate, open, close, smooth, edge, etc...
3994e698a255629ba03cd125572de7b35b5e21c4ee5danthony  */
399547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Loop 1:  iterate the compound method */
399647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_loop = 0;
399747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_changed = 1;
399847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  while ( method_loop < method_limit && method_changed > 0 ) {
399947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_loop++;
400047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_changed = 0;
400147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
400247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    /* Loop 2:  iterate over each kernel in a multi-kernel list */
400347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    norm_kernel = (KernelInfo *) kernel;
4004f2faecf9facdbbb14fcba373365f9f691a9658e0cristy    this_kernel = (KernelInfo *) kernel;
400547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rflt_kernel = reflected_kernel;
4006e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony
400747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_number = 0;
400847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    while ( norm_kernel != NULL ) {
400947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
401047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /* Loop 3: Compound Morphology Staging - Select Primative to apply */
401147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      stage_loop = 0;          /* the compound morphology stage number */
401247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      while ( stage_loop < stage_limit ) {
401347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        stage_loop++;   /* The stage of the compound morphology */
401447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
4015a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        /* Select primitive morphology for this stage of compound method */
401647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        this_kernel = norm_kernel; /* default use unreflected kernel */
4017a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        primitive = method;        /* Assume method is a primitive */
401847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        switch( method ) {
401947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case ErodeMorphology:      /* just erode */
402047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeInMorphology:     /* erode and image difference */
4021a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ErodeMorphology;
402247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
402347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case DilateMorphology:     /* just dilate */
402447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeOutMorphology:    /* dilate and image difference */
4025a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateMorphology;
402647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
402747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case OpenMorphology:       /* erode then dialate */
402847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case TopHatMorphology:     /* open and image difference */
4029a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ErodeMorphology;
403047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
4031a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = DilateMorphology;
403247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
403347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case OpenIntensityMorphology:
4034a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ErodeIntensityMorphology;
403547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
4036a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = DilateIntensityMorphology;
4037e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony            break;
403847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CloseMorphology:      /* dilate, then erode */
403947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case BottomHatMorphology:  /* close and image difference */
404047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
4041a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateMorphology;
404247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
4043a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = ErodeMorphology;
404447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
404547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CloseIntensityMorphology:
404647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
4047a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateIntensityMorphology;
404847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
4049a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = ErodeIntensityMorphology;
405047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
405147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case SmoothMorphology:         /* open, close */
405247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            switch ( stage_loop ) {
405347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 1: /* start an open method, which starts with Erode */
4054a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = ErodeMorphology;
405547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
405647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 2:  /* now Dilate the Erode */
4057a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = DilateMorphology;
405847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
405947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 3:  /* Reflect kernel a close */
406047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                this_kernel = rflt_kernel; /* use the reflected kernel */
4061a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = DilateMorphology;
406247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
406347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 4:  /* Finish the Close */
406447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                this_kernel = rflt_kernel; /* use the reflected kernel */
4065a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = ErodeMorphology;
406647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
406747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            }
406847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
406947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeMorphology:        /* dilate and erode difference */
4070a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateMorphology;
407147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 ) {
407247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              save_image = curr_image;      /* save the image difference */
407347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              curr_image = (Image *) image;
4074a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = ErodeMorphology;
407547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            }
407647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
407747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CorrelateMorphology:
407847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            /* A Correlation is a Convolution with a reflected kernel.
407947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** However a Convolution is a weighted sum using a reflected
408047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** kernel.  It may seem stange to convert a Correlation into a
408147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** Convolution as the Correlation is the simplier method, but
408247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** Convolution is much more commonly used, and it makes sense to
408347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** implement it directly so as to avoid the need to duplicate the
408447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** kernel when it is not required (which is typically the
408547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** default).
408647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            */
408747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
4088a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ConvolveMorphology;
408947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
409047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          default:
409147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
409247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
4093e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony        assert( this_kernel != (KernelInfo *) NULL );
40947a01dcf50ce12cb2a789bedff51e9345f022432eanthony
409547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        /* Extra information for debugging compound operations */
40967bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony        if ( IfMagickTrue(verbose) ) {
409747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          if ( stage_limit > 1 )
4098b51dff5c0d16a4c1b69ff683e786cb3b4c467694cristy            (void) FormatLocaleString(v_info,MaxTextExtent,"%s:%.20g.%.20g -> ",
4099042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy             CommandOptionToMnemonic(MagickMorphologyOptions,method),(double)
4100e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy             method_loop,(double) stage_loop);
4101a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          else if ( primitive != method )
4102b51dff5c0d16a4c1b69ff683e786cb3b4c467694cristy            (void) FormatLocaleString(v_info, MaxTextExtent, "%s:%.20g -> ",
4103042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              CommandOptionToMnemonic(MagickMorphologyOptions, method),(double)
4104e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              method_loop);
410547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          else
410647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            v_info[0] = '\0';
410747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
41089eb4f74649b23c053b308ce1152dce51239450baanthony
4109a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        /* Loop 4: Iterate the kernel with primitive */
411047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        kernel_loop = 0;
411147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        kernel_changed = 0;
411247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        changed = 1;
411347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        while ( kernel_loop < kernel_limit && changed > 0 ) {
411447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          kernel_loop++;     /* the iteration of this kernel */
41159eb4f74649b23c053b308ce1152dce51239450baanthony
4116a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          /* Create a clone as the destination image, if not yet defined */
41179eb4f74649b23c053b308ce1152dce51239450baanthony          if ( work_image == (Image *) NULL )
41189eb4f74649b23c053b308ce1152dce51239450baanthony            {
41199eb4f74649b23c053b308ce1152dce51239450baanthony              work_image=CloneImage(image,0,0,MagickTrue,exception);
41209eb4f74649b23c053b308ce1152dce51239450baanthony              if (work_image == (Image *) NULL)
41219eb4f74649b23c053b308ce1152dce51239450baanthony                goto error_cleanup;
4122574cc26500992189f637cd1cdf93d0654e7df7aecristy              if (SetImageStorageClass(work_image,DirectClass,exception) == MagickFalse)
4123574cc26500992189f637cd1cdf93d0654e7df7aecristy                goto error_cleanup;
4124db60568e12574785101a4ae8d8da076227a0a889anthony              /* work_image->type=image->type; ??? */
41259eb4f74649b23c053b308ce1152dce51239450baanthony            }
41269eb4f74649b23c053b308ce1152dce51239450baanthony
4127501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          /* APPLY THE MORPHOLOGICAL PRIMITIVE (curr -> work) */
41289eb4f74649b23c053b308ce1152dce51239450baanthony          count++;
4129e698a255629ba03cd125572de7b35b5e21c4ee5danthony          changed = MorphologyPrimitive(curr_image, work_image, primitive,
4130f46d42620631d2581e0b6a56456e203e17c427c8anthony                       this_kernel, bias, exception);
413147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
41327bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony          if ( IfMagickTrue(verbose) ) {
413347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( kernel_loop > 1 )
41345acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line from previous */
41355acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) (void) FormatLocaleFile(stderr,
41361e604812fad85bb96f757a2393015ae3d061c39acristy              "%s%s%s:%.20g.%.20g #%.20g => Changed %.20g",
4137042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              v_info,CommandOptionToMnemonic(MagickMorphologyOptions,
4138a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive),(this_kernel == rflt_kernel ) ? "*" : "",
4139e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              (double) (method_loop+kernel_loop-1),(double) kernel_number,
4140e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              (double) count,(double) changed);
414147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          }
4142a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          if ( changed < 0 )
4143a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            goto error_cleanup;
4144a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          kernel_changed += changed;
4145a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          method_changed += changed;
4146a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
41479eb4f74649b23c053b308ce1152dce51239450baanthony          /* prepare next loop */
41489eb4f74649b23c053b308ce1152dce51239450baanthony          { Image *tmp = work_image;   /* swap images for iteration */
41499eb4f74649b23c053b308ce1152dce51239450baanthony            work_image = curr_image;
41509eb4f74649b23c053b308ce1152dce51239450baanthony            curr_image = tmp;
41519eb4f74649b23c053b308ce1152dce51239450baanthony          }
41529eb4f74649b23c053b308ce1152dce51239450baanthony          if ( work_image == image )
415347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            work_image = (Image *) NULL; /* replace input 'image' */
41549eb4f74649b23c053b308ce1152dce51239450baanthony
4155a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        } /* End Loop 4: Iterate the kernel with primitive */
41567a01dcf50ce12cb2a789bedff51e9345f022432eanthony
41577bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony        if ( IfMagickTrue(verbose) && kernel_changed != (size_t)changed )
41585acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "   Total %.20g",(double) kernel_changed);
41597bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony        if ( IfMagickTrue(verbose) && stage_loop < stage_limit )
41605acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line before looping */
41619eb4f74649b23c053b308ce1152dce51239450baanthony
416247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony#if 0
41635acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "--E-- image=0x%lx\n", (unsigned long)image);
41645acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      curr =0x%lx\n", (unsigned long)curr_image);
41655acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      work =0x%lx\n", (unsigned long)work_image);
41665acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      save =0x%lx\n", (unsigned long)save_image);
41675acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      union=0x%lx\n", (unsigned long)rslt_image);
416847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony#endif
41691b2bc0a7da432e6e1cc0480280402df213faa940anthony
417047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      } /* End Loop 3: Primative (staging) Loop for Coumpound Methods */
41711b2bc0a7da432e6e1cc0480280402df213faa940anthony
417247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /*  Final Post-processing for some Compound Methods
417347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      **
417447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** The removal of any 'Sync' channel flag in the Image Compositon
417547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** below ensures the methematical compose method is applied in a
417647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** purely mathematical way, and only to the selected channels.
417747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** Turn off SVG composition 'alpha blending'.
417847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      */
417947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      switch( method ) {
418047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case EdgeOutMorphology:
418147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case EdgeInMorphology:
418247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case TopHatMorphology:
418347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case BottomHatMorphology:
41847bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony          if ( IfMagickTrue(verbose) )
4185e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy            (void) FormatLocaleFile(stderr,
4186e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy              "\n%s: Difference with original image",CommandOptionToMnemonic(
4187e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy              MagickMorphologyOptions, method) );
4188feb3e9695150978a5d2372d3fe2f60466a7c8066cristy          (void) CompositeImage(curr_image,image,DifferenceCompositeOp,
418939172408bad7ef2ef00a815fa9abf9979e7857cbcristy            MagickTrue,0,0,exception);
419047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          break;
419147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case EdgeMorphology:
41927bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony          if ( IfMagickTrue(verbose) )
4193e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy            (void) FormatLocaleFile(stderr,
4194e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy              "\n%s: Difference of Dilate and Erode",CommandOptionToMnemonic(
4195e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy              MagickMorphologyOptions, method) );
4196feb3e9695150978a5d2372d3fe2f60466a7c8066cristy          (void) CompositeImage(curr_image,save_image,DifferenceCompositeOp,
419739172408bad7ef2ef00a815fa9abf9979e7857cbcristy            MagickTrue,0,0,exception);
419847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          save_image = DestroyImage(save_image); /* finished with save image */
419947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          break;
420047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        default:
420147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          break;
4202602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
42039eb4f74649b23c053b308ce1152dce51239450baanthony
420447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /* multi-kernel handling:  re-iterate, or compose results */
420547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if ( kernel->next == (KernelInfo *) NULL )
4206c3e48258f3253188894e783dcdfd03562f7ab2c5anthony        rslt_image = curr_image;   /* just return the resulting image */
420747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else if ( rslt_compose == NoCompositeOp )
42087bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony        { if ( IfMagickTrue(verbose) ) {
4209c3e48258f3253188894e783dcdfd03562f7ab2c5anthony            if ( this_kernel->next != (KernelInfo *) NULL )
42105acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, " (re-iterate)");
4211c3e48258f3253188894e783dcdfd03562f7ab2c5anthony            else
42125acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, " (done)");
4213c3e48258f3253188894e783dcdfd03562f7ab2c5anthony          }
4214c3e48258f3253188894e783dcdfd03562f7ab2c5anthony          rslt_image = curr_image; /* return result, and re-iterate */
42159eb4f74649b23c053b308ce1152dce51239450baanthony        }
421647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else if ( rslt_image == (Image *) NULL)
42177bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony        { if ( IfMagickTrue(verbose) )
42185acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) FormatLocaleFile(stderr, " (save for compose)");
421947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          rslt_image = curr_image;
422047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          curr_image = (Image *) image;  /* continue with original image */
42219eb4f74649b23c053b308ce1152dce51239450baanthony        }
422247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else
4223ea61f01656bb0f9074677452017cc559e54093faanthony        { /* Add the new 'current' result to the composition
422447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          **
422547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** The removal of any 'Sync' channel flag in the Image Compositon
422647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** below ensures the methematical compose method is applied in a
422747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** purely mathematical way, and only to the selected channels.
4228ea61f01656bb0f9074677452017cc559e54093faanthony          ** IE: Turn off SVG composition 'alpha blending'.
422947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          */
42307bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony          if ( IfMagickTrue(verbose) )
42315acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) FormatLocaleFile(stderr, " (compose \"%s\")",
4232feb3e9695150978a5d2372d3fe2f60466a7c8066cristy              CommandOptionToMnemonic(MagickComposeOptions, rslt_compose) );
423339172408bad7ef2ef00a815fa9abf9979e7857cbcristy          (void) CompositeImage(rslt_image,curr_image,rslt_compose,MagickTrue,
4234feb3e9695150978a5d2372d3fe2f60466a7c8066cristy            0,0,exception);
42350bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony          curr_image = DestroyImage(curr_image);
423647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          curr_image = (Image *) image;  /* continue with original image */
423747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
42387bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony      if ( IfMagickTrue(verbose) )
42395acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "\n");
42404fd27e21043be809d66c8202e779255e5b660d2danthony
424147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /* loop to the next kernel in a multi-kernel list */
424247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      norm_kernel = norm_kernel->next;
424347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if ( rflt_kernel != (KernelInfo *) NULL )
424447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        rflt_kernel = rflt_kernel->next;
424547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      kernel_number++;
424647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    } /* End Loop 2: Loop over each kernel */
42479eb4f74649b23c053b308ce1152dce51239450baanthony
424847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  } /* End Loop 1: compound method interation */
4249602ab9b30b644a78a4057da93d838a77391ec0acanthony
42509eb4f74649b23c053b308ce1152dce51239450baanthony  goto exit_cleanup;
42511b2bc0a7da432e6e1cc0480280402df213faa940anthony
425247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Yes goto's are bad, but it makes cleanup lot more efficient */
42531b2bc0a7da432e6e1cc0480280402df213faa940anthonyerror_cleanup:
4254ea61f01656bb0f9074677452017cc559e54093faanthony  if ( curr_image == rslt_image )
4255ea61f01656bb0f9074677452017cc559e54093faanthony    curr_image = (Image *) NULL;
425647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( rslt_image != (Image *) NULL )
425747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_image = DestroyImage(rslt_image);
42581b2bc0a7da432e6e1cc0480280402df213faa940anthonyexit_cleanup:
4259ea61f01656bb0f9074677452017cc559e54093faanthony  if ( curr_image == rslt_image || curr_image == image )
4260ea61f01656bb0f9074677452017cc559e54093faanthony    curr_image = (Image *) NULL;
4261ea61f01656bb0f9074677452017cc559e54093faanthony  if ( curr_image != (Image *) NULL )
426247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    curr_image = DestroyImage(curr_image);
42639eb4f74649b23c053b308ce1152dce51239450baanthony  if ( work_image != (Image *) NULL )
426447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    work_image = DestroyImage(work_image);
42659eb4f74649b23c053b308ce1152dce51239450baanthony  if ( save_image != (Image *) NULL )
426647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    save_image = DestroyImage(save_image);
426747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( reflected_kernel != (KernelInfo *) NULL )
426847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    reflected_kernel = DestroyKernelInfo(reflected_kernel);
426947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  return(rslt_image);
42709eb4f74649b23c053b308ce1152dce51239450baanthony}
4271a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
42729eb4f74649b23c053b308ce1152dce51239450baanthony
42739eb4f74649b23c053b308ce1152dce51239450baanthony/*
42749eb4f74649b23c053b308ce1152dce51239450baanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42759eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
42769eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
42779eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
4278f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%     M o r p h o l o g y I m a g e                                           %
42799eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
42809eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
42819eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
42829eb4f74649b23c053b308ce1152dce51239450baanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42839eb4f74649b23c053b308ce1152dce51239450baanthony%
4284f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%  MorphologyImage() applies a user supplied kernel to the image
42859eb4f74649b23c053b308ce1152dce51239450baanthony%  according to the given mophology method.
42869eb4f74649b23c053b308ce1152dce51239450baanthony%
42879eb4f74649b23c053b308ce1152dce51239450baanthony%  This function applies any and all user defined settings before calling
42889eb4f74649b23c053b308ce1152dce51239450baanthony%  the above internal function MorphologyApply().
42899eb4f74649b23c053b308ce1152dce51239450baanthony%
42909eb4f74649b23c053b308ce1152dce51239450baanthony%  User defined settings include...
429122de2722b682eb405b60ec6022a7546df994674eanthony%    * Output Bias for Convolution and correlation ("-define convolve:bias=??")
429222de2722b682eb405b60ec6022a7546df994674eanthony%    * Kernel Scale/normalize settings             ("-define convolve:scale=??")
429346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      This can also includes the addition of a scaled unity kernel.
429422de2722b682eb405b60ec6022a7546df994674eanthony%    * Show Kernel being applied                   ("-define showkernel=1")
429522de2722b682eb405b60ec6022a7546df994674eanthony%
429622de2722b682eb405b60ec6022a7546df994674eanthony%  Other operators that do not want user supplied options interfering,
429722de2722b682eb405b60ec6022a7546df994674eanthony%  especially "convolve:bias" and "showkernel" should use MorphologyApply()
429822de2722b682eb405b60ec6022a7546df994674eanthony%  directly.
42999eb4f74649b23c053b308ce1152dce51239450baanthony%
43009eb4f74649b23c053b308ce1152dce51239450baanthony%  The format of the MorphologyImage method is:
43019eb4f74649b23c053b308ce1152dce51239450baanthony%
43029eb4f74649b23c053b308ce1152dce51239450baanthony%      Image *MorphologyImage(const Image *image,MorphologyMethod method,
4303bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy%        const ssize_t iterations,KernelInfo *kernel,ExceptionInfo *exception)
43049eb4f74649b23c053b308ce1152dce51239450baanthony%
43059eb4f74649b23c053b308ce1152dce51239450baanthony%  A description of each parameter follows:
43069eb4f74649b23c053b308ce1152dce51239450baanthony%
43079eb4f74649b23c053b308ce1152dce51239450baanthony%    o image: the image.
43089eb4f74649b23c053b308ce1152dce51239450baanthony%
43099eb4f74649b23c053b308ce1152dce51239450baanthony%    o method: the morphology method to be applied.
43109eb4f74649b23c053b308ce1152dce51239450baanthony%
43119eb4f74649b23c053b308ce1152dce51239450baanthony%    o iterations: apply the operation this many times (or no change).
43129eb4f74649b23c053b308ce1152dce51239450baanthony%                  A value of -1 means loop until no change found.
43139eb4f74649b23c053b308ce1152dce51239450baanthony%                  How this is applied may depend on the morphology method.
43149eb4f74649b23c053b308ce1152dce51239450baanthony%                  Typically this is a value of 1.
43159eb4f74649b23c053b308ce1152dce51239450baanthony%
43169eb4f74649b23c053b308ce1152dce51239450baanthony%    o kernel: An array of double representing the morphology kernel.
43179eb4f74649b23c053b308ce1152dce51239450baanthony%              Warning: kernel may be normalized for the Convolve method.
43189eb4f74649b23c053b308ce1152dce51239450baanthony%
43199eb4f74649b23c053b308ce1152dce51239450baanthony%    o exception: return any errors or warnings in this structure.
43209eb4f74649b23c053b308ce1152dce51239450baanthony%
43219eb4f74649b23c053b308ce1152dce51239450baanthony*/
4322f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristyMagickExport Image *MorphologyImage(const Image *image,
4323f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const MorphologyMethod method,const ssize_t iterations,
4324f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const KernelInfo *kernel,ExceptionInfo *exception)
43259eb4f74649b23c053b308ce1152dce51239450baanthony{
43269eb4f74649b23c053b308ce1152dce51239450baanthony  KernelInfo
43279eb4f74649b23c053b308ce1152dce51239450baanthony    *curr_kernel;
43289eb4f74649b23c053b308ce1152dce51239450baanthony
432947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  CompositeOperator
433047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    compose;
433147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
43329eb4f74649b23c053b308ce1152dce51239450baanthony  Image
43339eb4f74649b23c053b308ce1152dce51239450baanthony    *morphology_image;
43349eb4f74649b23c053b308ce1152dce51239450baanthony
4335f46d42620631d2581e0b6a56456e203e17c427c8anthony  double
4336f46d42620631d2581e0b6a56456e203e17c427c8anthony    bias;
43379eb4f74649b23c053b308ce1152dce51239450baanthony
433822de2722b682eb405b60ec6022a7546df994674eanthony  curr_kernel = (KernelInfo *) kernel;
433922de2722b682eb405b60ec6022a7546df994674eanthony  bias=0.0;
4340d228c03fa334bae897eee6c2d8721fa48e1577bacristy  compose = UndefinedCompositeOp;  /* use default for method */
434122de2722b682eb405b60ec6022a7546df994674eanthony
434246a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Apply Convolve/Correlate Normalization and Scaling Factors.
434346a369d839971ab627bdb31a93d8bd63e81b65a3anthony   * This is done BEFORE the ShowKernelInfo() function is called so that
434446a369d839971ab627bdb31a93d8bd63e81b65a3anthony   * users can see the results of the 'option:convolve:scale' option.
43459eb4f74649b23c053b308ce1152dce51239450baanthony   */
434622de2722b682eb405b60ec6022a7546df994674eanthony  if ( method == ConvolveMorphology || method == CorrelateMorphology ) {
434728ad1d779b6ca95852e860514185a7a97e06af77anthony      const char
434828ad1d779b6ca95852e860514185a7a97e06af77anthony        *artifact;
4349f46d42620631d2581e0b6a56456e203e17c427c8anthony
435022de2722b682eb405b60ec6022a7546df994674eanthony      /* Get the bias value as it will be needed */
435122de2722b682eb405b60ec6022a7546df994674eanthony      artifact = GetImageArtifact(image,"convolve:bias");
435222de2722b682eb405b60ec6022a7546df994674eanthony      if ( artifact != (const char *) NULL) {
435322de2722b682eb405b60ec6022a7546df994674eanthony        if (IfMagickFalse(IsGeometry(artifact)))
435422de2722b682eb405b60ec6022a7546df994674eanthony          (void) ThrowMagickException(exception,GetMagickModule(),
435522de2722b682eb405b60ec6022a7546df994674eanthony               OptionWarning,"InvalidSetting","'%s' '%s'",
435622de2722b682eb405b60ec6022a7546df994674eanthony               "convolve:bias",artifact);
435722de2722b682eb405b60ec6022a7546df994674eanthony        else
435822de2722b682eb405b60ec6022a7546df994674eanthony          bias=StringToDoubleInterval(artifact,(double) QuantumRange+1.0);
435922de2722b682eb405b60ec6022a7546df994674eanthony      }
436022de2722b682eb405b60ec6022a7546df994674eanthony
436122de2722b682eb405b60ec6022a7546df994674eanthony      /* Scale kernel according to user wishes */
43629eb4f74649b23c053b308ce1152dce51239450baanthony      artifact = GetImageArtifact(image,"convolve:scale");
4363e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony      if ( artifact != (const char *)NULL ) {
436422de2722b682eb405b60ec6022a7546df994674eanthony        if (IfMagickFalse(IsGeometry(artifact)))
436522de2722b682eb405b60ec6022a7546df994674eanthony          (void) ThrowMagickException(exception,GetMagickModule(),
436622de2722b682eb405b60ec6022a7546df994674eanthony               OptionWarning,"InvalidSetting","'%s' '%s'",
436722de2722b682eb405b60ec6022a7546df994674eanthony               "convolve:scale",artifact);
436822de2722b682eb405b60ec6022a7546df994674eanthony        else {
436922de2722b682eb405b60ec6022a7546df994674eanthony          if ( curr_kernel == kernel )
437022de2722b682eb405b60ec6022a7546df994674eanthony            curr_kernel = CloneKernelInfo(kernel);
437122de2722b682eb405b60ec6022a7546df994674eanthony          if (curr_kernel == (KernelInfo *) NULL)
437222de2722b682eb405b60ec6022a7546df994674eanthony            return((Image *) NULL);
437322de2722b682eb405b60ec6022a7546df994674eanthony          ScaleGeometryKernelInfo(curr_kernel, artifact);
43749eb4f74649b23c053b308ce1152dce51239450baanthony        }
43759eb4f74649b23c053b308ce1152dce51239450baanthony      }
43769eb4f74649b23c053b308ce1152dce51239450baanthony    }
43779eb4f74649b23c053b308ce1152dce51239450baanthony
43789eb4f74649b23c053b308ce1152dce51239450baanthony  /* display the (normalized) kernel via stderr */
437948656f2d1ca9ac9979eac32052e4cdc4958c9010cristy  if ( IfStringTrue(GetImageArtifact(image,"showkernel"))
438048656f2d1ca9ac9979eac32052e4cdc4958c9010cristy    || IfStringTrue(GetImageArtifact(image,"convolve:showkernel"))
438148656f2d1ca9ac9979eac32052e4cdc4958c9010cristy    || IfStringTrue(GetImageArtifact(image,"morphology:showkernel")) )
43829eb4f74649b23c053b308ce1152dce51239450baanthony    ShowKernelInfo(curr_kernel);
43839eb4f74649b23c053b308ce1152dce51239450baanthony
43843206678d008425bc56dd2dbad002f2bb26299dc2anthony  /* Override the default handling of multi-kernel morphology results
43853206678d008425bc56dd2dbad002f2bb26299dc2anthony   * If 'Undefined' use the default method
43863206678d008425bc56dd2dbad002f2bb26299dc2anthony   * If 'None' (default for 'Convolve') re-iterate previous result
43873206678d008425bc56dd2dbad002f2bb26299dc2anthony   * Otherwise merge resulting images using compose method given.
43883206678d008425bc56dd2dbad002f2bb26299dc2anthony   * Default for 'HitAndMiss' is 'Lighten'.
438947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   */
439028ad1d779b6ca95852e860514185a7a97e06af77anthony  { const char
439128ad1d779b6ca95852e860514185a7a97e06af77anthony      *artifact;
439222de2722b682eb405b60ec6022a7546df994674eanthony    ssize_t
439322de2722b682eb405b60ec6022a7546df994674eanthony      parse;
439422de2722b682eb405b60ec6022a7546df994674eanthony
4395f46d42620631d2581e0b6a56456e203e17c427c8anthony    artifact = GetImageArtifact(image,"morphology:compose");
439622de2722b682eb405b60ec6022a7546df994674eanthony    if ( artifact != (const char *) NULL) {
439722de2722b682eb405b60ec6022a7546df994674eanthony      parse=ParseCommandOption(MagickComposeOptions,
439870b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy        MagickFalse,artifact);
439922de2722b682eb405b60ec6022a7546df994674eanthony      if ( parse < 0 )
440022de2722b682eb405b60ec6022a7546df994674eanthony        (void) ThrowMagickException(exception,GetMagickModule(),
440122de2722b682eb405b60ec6022a7546df994674eanthony             OptionWarning,"UnrecognizedComposeOperator","'%s' '%s'",
440222de2722b682eb405b60ec6022a7546df994674eanthony             "morphology:compose",artifact);
440322de2722b682eb405b60ec6022a7546df994674eanthony      else
440422de2722b682eb405b60ec6022a7546df994674eanthony        compose=(CompositeOperator)parse;
440522de2722b682eb405b60ec6022a7546df994674eanthony    }
440628ad1d779b6ca95852e860514185a7a97e06af77anthony  }
44079eb4f74649b23c053b308ce1152dce51239450baanthony  /* Apply the Morphology */
4408f46d42620631d2581e0b6a56456e203e17c427c8anthony  morphology_image = MorphologyApply(image,method,iterations,
4409f46d42620631d2581e0b6a56456e203e17c427c8anthony    curr_kernel,compose,bias,exception);
44109eb4f74649b23c053b308ce1152dce51239450baanthony
44119eb4f74649b23c053b308ce1152dce51239450baanthony  /* Cleanup and Exit */
44129eb4f74649b23c053b308ce1152dce51239450baanthony  if ( curr_kernel != kernel )
44131b2bc0a7da432e6e1cc0480280402df213faa940anthony    curr_kernel=DestroyKernelInfo(curr_kernel);
44149eb4f74649b23c053b308ce1152dce51239450baanthony  return(morphology_image);
44159eb4f74649b23c053b308ce1152dce51239450baanthony}
441683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
441783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony/*
441883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
442083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
442183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
44224fd27e21043be809d66c8202e779255e5b660d2danthony+     R o t a t e K e r n e l I n f o                                         %
442383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
442483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
442583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
442683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
442783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
442846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  RotateKernelInfo() rotates the kernel by the angle given.
442946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
443046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  Currently it is restricted to 90 degree angles, of either 1D kernels
443146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  or square kernels. And 'circular' rotations of 45 degrees for 3x3 kernels.
443246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  It will ignore usless rotations for specific 'named' built-in kernels.
443383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
44344fd27e21043be809d66c8202e779255e5b660d2danthony%  The format of the RotateKernelInfo method is:
443583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
44364fd27e21043be809d66c8202e779255e5b660d2danthony%      void RotateKernelInfo(KernelInfo *kernel, double angle)
443783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
443883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  A description of each parameter follows:
443983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
444083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o kernel: the Morphology/Convolution kernel
444183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
444283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o angle: angle to rotate in degrees
444383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
444446a369d839971ab627bdb31a93d8bd63e81b65a3anthony% This function is currently internal to this module only, but can be exported
444546a369d839971ab627bdb31a93d8bd63e81b65a3anthony% to other modules if needed.
444683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony*/
44474fd27e21043be809d66c8202e779255e5b660d2danthonystatic void RotateKernelInfo(KernelInfo *kernel, double angle)
444883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony{
44491b2bc0a7da432e6e1cc0480280402df213faa940anthony  /* angle the lower kernels first */
44501b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL)
44511b2bc0a7da432e6e1cc0480280402df213faa940anthony    RotateKernelInfo(kernel->next, angle);
44521b2bc0a7da432e6e1cc0480280402df213faa940anthony
445383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* WARNING: Currently assumes the kernel (rightly) is horizontally symetrical
445483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  **
445583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  ** TODO: expand beyond simple 90 degree rotates, flips and flops
445683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  */
445783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
445883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* Modulus the angle */
445983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  angle = fmod(angle, 360.0);
446083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  if ( angle < 0 )
446183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    angle += 360.0;
446283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
44633c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  if ( 337.5 < angle || angle <= 22.5 )
446443c4925e5305a26e48d68f7893e94f55d0831c39anthony    return;   /* Near zero angle - no change! - At least not at this time */
446583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
44663dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  /* Handle special cases */
446783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  switch (kernel->type) {
446883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* These built-in kernels are cylindrical kernels, rotating is useless */
446983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case GaussianKernel:
4470501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case DoGKernel:
4471501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case LoGKernel:
447283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case DiskKernel:
44733dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case PeaksKernel:
44743dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case LaplacianKernel:
447583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case ChebyshevKernel:
4476bee715c4c0fd9efe6e21d8627ae8664434df7750anthony    case ManhattanKernel:
447783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case EuclideanKernel:
447883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      return;
447983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
448083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* These may be rotatable at non-90 angles in the future */
448183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* but simply rotating them in multiples of 90 degrees is useless */
448283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case SquareKernel:
448383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case DiamondKernel:
448483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case PlusKernel:
44853dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case CrossKernel:
448683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      return;
448783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
448883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* These only allows a +/-90 degree rotation (by transpose) */
448983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* A 180 degree rotation is useless */
449083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case BlurKernel:
449183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      if ( 135.0 < angle && angle <= 225.0 )
449283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        return;
449383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      if ( 225.0 < angle && angle <= 315.0 )
449483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        angle -= 180;
449583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      break;
449683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
44973dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    default:
449883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      break;
449983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  }
450057fe7a498c1302232dac8466864e84b12fad0807anthony  /* Attempt rotations by 45 degrees  -- 3x3 kernels only */
45013c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  if ( 22.5 < fmod(angle,90.0) && fmod(angle,90.0) <= 67.5 )
45023c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    {
45033c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      if ( kernel->width == 3 && kernel->height == 3 )
45043c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        { /* Rotate a 3x3 square by 45 degree angle */
4505a19f1d70e9a9f88279c4ecafe6dfafc1f9a09599cristy          double t  = kernel->values[0];
450643c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[0] = kernel->values[3];
450743c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[3] = kernel->values[6];
450843c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[6] = kernel->values[7];
450943c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[7] = kernel->values[8];
451043c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[8] = kernel->values[5];
451143c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[5] = kernel->values[2];
451243c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[2] = kernel->values[1];
451343c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[1] = t;
45141d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          /* rotate non-centered origin */
45151d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          if ( kernel->x != 1 || kernel->y != 1 ) {
4516bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            ssize_t x,y;
4517bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            x = (ssize_t) kernel->x-1;
4518bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            y = (ssize_t) kernel->y-1;
45191d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                 if ( x == y  ) x = 0;
45201d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            else if ( x == 0  ) x = -y;
45211d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            else if ( x == -y ) y = 0;
45221d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            else if ( y == 0  ) y = x;
4523ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->x = (ssize_t) x+1;
4524ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->y = (ssize_t) y+1;
45251d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          }
452643c4925e5305a26e48d68f7893e94f55d0831c39anthony          angle = fmod(angle+315.0, 360.0);  /* angle reduced 45 degrees */
452743c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->angle = fmod(kernel->angle+45.0, 360.0);
45283c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
45293c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      else
45303c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        perror("Unable to rotate non-3x3 kernel by 45 degrees");
45313c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    }
45323c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  if ( 45.0 < fmod(angle, 180.0)  && fmod(angle,180.0) <= 135.0 )
45333c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    {
45343c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      if ( kernel->width == 1 || kernel->height == 1 )
45354c08aed51c5899665ade97263692328eea4af106cristy        { /* Do a transpose of a 1 dimensional kernel,
4536bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony          ** which results in a fast 90 degree rotation of some type.
45373c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          */
4538bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          ssize_t
45393c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            t;
4540bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          t = (ssize_t) kernel->width;
45413c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->width = kernel->height;
4542bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->height = (size_t) t;
45433c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          t = kernel->x;
45443c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->x = kernel->y;
45453c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->y = t;
454643c4925e5305a26e48d68f7893e94f55d0831c39anthony          if ( kernel->width == 1 ) {
454743c4925e5305a26e48d68f7893e94f55d0831c39anthony            angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
454843c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel->angle = fmod(kernel->angle+90.0, 360.0);
454943c4925e5305a26e48d68f7893e94f55d0831c39anthony          } else {
455043c4925e5305a26e48d68f7893e94f55d0831c39anthony            angle = fmod(angle+90.0, 360.0);   /* angle increased 90 degrees */
455143c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel->angle = fmod(kernel->angle+270.0, 360.0);
455243c4925e5305a26e48d68f7893e94f55d0831c39anthony          }
45533c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
45543c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      else if ( kernel->width == kernel->height )
45553c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        { /* Rotate a square array of values by 90 degrees */
4556d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          { register ssize_t
45571d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony              i,j,x,y;
4558d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy
4559d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            register MagickRealType
45601d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony              *k,t;
4561d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy
45621d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            k=kernel->values;
4563d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            for( i=0, x=(ssize_t) kernel->width-1;  i<=x;   i++, x--)
4564d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy              for( j=0, y=(ssize_t) kernel->height-1;  j<y;   j++, y--)
45651d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                { t                    = k[i+j*kernel->width];
45661d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[i+j*kernel->width] = k[j+x*kernel->width];
45671d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[j+x*kernel->width] = k[x+y*kernel->width];
45681d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[x+y*kernel->width] = k[y+i*kernel->width];
45691d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[y+i*kernel->width] = t;
45701d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                }
45711d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          }
45721d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          /* rotate the origin - relative to center of array */
4573bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          { register ssize_t x,y;
4574eaedf06777741da32408da72c1e512975c600c48cristy            x = (ssize_t) (kernel->x*2-kernel->width+1);
4575eaedf06777741da32408da72c1e512975c600c48cristy            y = (ssize_t) (kernel->y*2-kernel->height+1);
4576ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->x = (ssize_t) ( -y +(ssize_t) kernel->width-1)/2;
4577ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->y = (ssize_t) ( +x +(ssize_t) kernel->height-1)/2;
45781d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          }
457943c4925e5305a26e48d68f7893e94f55d0831c39anthony          angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
458043c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->angle = fmod(kernel->angle+90.0, 360.0);
45813c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
45823c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      else
45833c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        perror("Unable to rotate a non-square, non-linear kernel 90 degrees");
45843c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    }
458583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  if ( 135.0 < angle && angle <= 225.0 )
458683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    {
458743c4925e5305a26e48d68f7893e94f55d0831c39anthony      /* For a 180 degree rotation - also know as a reflection
458843c4925e5305a26e48d68f7893e94f55d0831c39anthony       * This is actually a very very common operation!
458943c4925e5305a26e48d68f7893e94f55d0831c39anthony       * Basically all that is needed is a reversal of the kernel data!
459043c4925e5305a26e48d68f7893e94f55d0831c39anthony       * And a reflection of the origon
459143c4925e5305a26e48d68f7893e94f55d0831c39anthony       */
4592a96f2494a8e79144a225056be9545cc75e868137cristy      double
4593a96f2494a8e79144a225056be9545cc75e868137cristy        t;
4594a96f2494a8e79144a225056be9545cc75e868137cristy
4595d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      register MagickRealType
4596a96f2494a8e79144a225056be9545cc75e868137cristy        *k;
4597a96f2494a8e79144a225056be9545cc75e868137cristy
4598a96f2494a8e79144a225056be9545cc75e868137cristy      ssize_t
4599a96f2494a8e79144a225056be9545cc75e868137cristy        i,
4600a96f2494a8e79144a225056be9545cc75e868137cristy        j;
460183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
460283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      k=kernel->values;
4603e42f658533644aecb733785ffd91b286d6778deacristy      j=(ssize_t) (kernel->width*kernel->height-1);
4604e42f658533644aecb733785ffd91b286d6778deacristy      for (i=0;  i < j;  i++, j--)
460583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        t=k[i],  k[i]=k[j],  k[j]=t;
460683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
4607bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->x = (ssize_t) kernel->width  - kernel->x - 1;
4608bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->y = (ssize_t) kernel->height - kernel->y - 1;
460943c4925e5305a26e48d68f7893e94f55d0831c39anthony      angle = fmod(angle-180.0, 360.0);   /* angle+180 degrees */
461043c4925e5305a26e48d68f7893e94f55d0831c39anthony      kernel->angle = fmod(kernel->angle+180.0, 360.0);
461183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    }
46123c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  /* At this point angle should at least between -45 (315) and +45 degrees
461383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   * In the future some form of non-orthogonal angled rotates could be
461483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   * performed here, posibily with a linear kernel restriction.
461583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   */
461683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
461783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  return;
461883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony}
461983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
462083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony/*
462183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
462283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
462383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
462483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
462546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%     S c a l e G e o m e t r y K e r n e l I n f o                           %
462646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
462746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
462846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
462946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
463046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
463146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  ScaleGeometryKernelInfo() takes a geometry argument string, typically
463246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  provided as a  "-set option:convolve:scale {geometry}" user setting,
463346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  and modifies the kernel according to the parsed arguments of that setting.
463446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
463546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The first argument (and any normalization flags) are passed to
463646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  ScaleKernelInfo() to scale/normalize the kernel.  The second argument
463746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  is then passed to UnityAddKernelInfo() to add a scled unity kernel
463846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  into the scaled/normalized kernel.
463946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
4640ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy%  The format of the ScaleGeometryKernelInfo method is:
464146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
4642ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy%      void ScaleGeometryKernelInfo(KernelInfo *kernel,
4643ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy%        const double scaling_factor,const MagickStatusType normalize_flags)
464446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
464546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  A description of each parameter follows:
464646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
464746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o kernel: the Morphology/Convolution kernel to modify
464846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
464946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o geometry:
465046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%             The geometry string to parse, typically from the user provided
465146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%             "-set option:convolve:scale {geometry}" setting.
465246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
465346a369d839971ab627bdb31a93d8bd63e81b65a3anthony*/
465446a369d839971ab627bdb31a93d8bd63e81b65a3anthonyMagickExport void ScaleGeometryKernelInfo (KernelInfo *kernel,
465546a369d839971ab627bdb31a93d8bd63e81b65a3anthony     const char *geometry)
465646a369d839971ab627bdb31a93d8bd63e81b65a3anthony{
465722de2722b682eb405b60ec6022a7546df994674eanthony  //GeometryFlags
4658ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy  MagickStatusType
465946a369d839971ab627bdb31a93d8bd63e81b65a3anthony    flags;
4660ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy
466146a369d839971ab627bdb31a93d8bd63e81b65a3anthony  GeometryInfo
466246a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args;
466346a369d839971ab627bdb31a93d8bd63e81b65a3anthony
466446a369d839971ab627bdb31a93d8bd63e81b65a3anthony  SetGeometryInfo(&args);
466522de2722b682eb405b60ec6022a7546df994674eanthony  flags = ParseGeometry(geometry, &args);
466646a369d839971ab627bdb31a93d8bd63e81b65a3anthony
466746a369d839971ab627bdb31a93d8bd63e81b65a3anthony#if 0
466846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* For Debugging Geometry Input */
46695acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy  (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
467046a369d839971ab627bdb31a93d8bd63e81b65a3anthony       flags, args.rho, args.sigma, args.xi, args.psi );
467146a369d839971ab627bdb31a93d8bd63e81b65a3anthony#endif
467246a369d839971ab627bdb31a93d8bd63e81b65a3anthony
467346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & PercentValue) != 0 )      /* Handle Percentage flag*/
467446a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args.rho *= 0.01,  args.sigma *= 0.01;
467546a369d839971ab627bdb31a93d8bd63e81b65a3anthony
467646a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & RhoValue) == 0 )          /* Set Defaults for missing args */
467746a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args.rho = 1.0;
467846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & SigmaValue) == 0 )
467946a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args.sigma = 0.0;
468046a369d839971ab627bdb31a93d8bd63e81b65a3anthony
468146a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Scale/Normalize the input kernel */
4682d228c03fa334bae897eee6c2d8721fa48e1577bacristy  ScaleKernelInfo(kernel, args.rho, (GeometryFlags) flags);
468346a369d839971ab627bdb31a93d8bd63e81b65a3anthony
468446a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Add Unity Kernel, for blending with original */
468546a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & SigmaValue) != 0 )
468646a369d839971ab627bdb31a93d8bd63e81b65a3anthony    UnityAddKernelInfo(kernel, args.sigma);
468746a369d839971ab627bdb31a93d8bd63e81b65a3anthony
468846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  return;
468946a369d839971ab627bdb31a93d8bd63e81b65a3anthony}
469046a369d839971ab627bdb31a93d8bd63e81b65a3anthony/*
469146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
469246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
469346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
469446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
46956771f1e8987fa49f52d4176281a2e8524b8e31cbcristy%     S c a l e K e r n e l I n f o                                           %
4696cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4697cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4698cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4699cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4700cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
47011b2bc0a7da432e6e1cc0480280402df213faa940anthony%  ScaleKernelInfo() scales the given kernel list by the given amount, with or
47021b2bc0a7da432e6e1cc0480280402df213faa940anthony%  without normalization of the sum of the kernel values (as per given flags).
4703999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4704999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  By default (no flags given) the values within the kernel is scaled
47051b2bc0a7da432e6e1cc0480280402df213faa940anthony%  directly using given scaling factor without change.
4706999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
470746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  If either of the two 'normalize_flags' are given the kernel will first be
470846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  normalized and then further scaled by the scaling factor value given.
4709999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4710999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  Kernel normalization ('normalize_flags' given) is designed to ensure that
4711999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  any use of the kernel scaling factor with 'Convolve' or 'Correlate'
47121b2bc0a7da432e6e1cc0480280402df213faa940anthony%  morphology methods will fall into -1.0 to +1.0 range.  Note that for
47131b2bc0a7da432e6e1cc0480280402df213faa940anthony%  non-HDRI versions of IM this may cause images to have any negative results
47141b2bc0a7da432e6e1cc0480280402df213faa940anthony%  clipped, unless some 'bias' is used.
4715999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4716999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  More specifically.  Kernels which only contain positive values (such as a
4717999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  'Gaussian' kernel) will be scaled so that those values sum to +1.0,
47181b2bc0a7da432e6e1cc0480280402df213faa940anthony%  ensuring a 0.0 to +1.0 output range for non-HDRI images.
4719999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4720999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For Kernels that contain some negative values, (such as 'Sharpen' kernels)
4721999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  the kernel will be scaled by the absolute of the sum of kernel values, so
4722999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  that it will generally fall within the +/- 1.0 range.
4723cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4724999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For kernels whose values sum to zero, (such as 'Laplician' kernels) kernel
4725999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  will be scaled by just the sum of the postive values, so that its output
4726999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  range will again fall into the  +/- 1.0 range.
4727cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4728999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For special kernels designed for locating shapes using 'Correlate', (often
4729999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  only containing +1 and -1 values, representing foreground/brackground
4730999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  matching) a special normalization method is provided to scale the positive
47311e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp%  values separately to those of the negative values, so the kernel will be
4732999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  forced to become a zero-sum kernel better suited to such searches.
4733999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
47341b2bc0a7da432e6e1cc0480280402df213faa940anthony%  WARNING: Correct normalization of the kernel assumes that the '*_range'
4735999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  attributes within the kernel structure have been correctly set during the
4736999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  kernels creation.
4737999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4738999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  NOTE: The values used for 'normalize_flags' have been selected specifically
473946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  to match the use of geometry options, so that '!' means NormalizeValue, '^'
474046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  means CorrelateNormalizeValue.  All other GeometryFlags values are ignored.
4741cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
47424fd27e21043be809d66c8202e779255e5b660d2danthony%  The format of the ScaleKernelInfo method is:
4743cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4744999bb2c20aa9d42875bb5adba44951988d4ae354anthony%      void ScaleKernelInfo(KernelInfo *kernel, const double scaling_factor,
4745999bb2c20aa9d42875bb5adba44951988d4ae354anthony%               const MagickStatusType normalize_flags )
4746cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4747cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  A description of each parameter follows:
4748cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4749cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%    o kernel: the Morphology/Convolution kernel
4750cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4751999bb2c20aa9d42875bb5adba44951988d4ae354anthony%    o scaling_factor:
4752999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             multiply all values (after normalization) by this factor if not
4753999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             zero.  If the kernel is normalized regardless of any flags.
4754999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4755999bb2c20aa9d42875bb5adba44951988d4ae354anthony%    o normalize_flags:
4756999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             GeometryFlags defining normalization method to use.
4757999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             specifically: NormalizeValue, CorrelateNormalizeValue,
4758999bb2c20aa9d42875bb5adba44951988d4ae354anthony%                           and/or PercentValue
4759cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4760cc6c836da2a53b6023b716e4973090a6714dc3b0anthony*/
47616771f1e8987fa49f52d4176281a2e8524b8e31cbcristyMagickExport void ScaleKernelInfo(KernelInfo *kernel,
47626771f1e8987fa49f52d4176281a2e8524b8e31cbcristy  const double scaling_factor,const GeometryFlags normalize_flags)
4763cc6c836da2a53b6023b716e4973090a6714dc3b0anthony{
4764bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
4765cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    i;
4766cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4767999bb2c20aa9d42875bb5adba44951988d4ae354anthony  register double
4768999bb2c20aa9d42875bb5adba44951988d4ae354anthony    pos_scale,
4769999bb2c20aa9d42875bb5adba44951988d4ae354anthony    neg_scale;
4770999bb2c20aa9d42875bb5adba44951988d4ae354anthony
477146a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* do the other kernels in a multi-kernel list first */
47721b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL)
47731b2bc0a7da432e6e1cc0480280402df213faa940anthony    ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
47741b2bc0a7da432e6e1cc0480280402df213faa940anthony
477546a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Normalization of Kernel */
4776999bb2c20aa9d42875bb5adba44951988d4ae354anthony  pos_scale = 1.0;
4777999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( (normalize_flags&NormalizeValue) != 0 ) {
4778b978e458a8e1f210bcb580951cf623687236b2fecristy    if ( fabs(kernel->positive_range + kernel->negative_range) >= MagickEpsilon )
4779f4e0031305baeb01c89cfd2842cbbec021883550anthony      /* non-zero-summing kernel (generally positive) */
4780999bb2c20aa9d42875bb5adba44951988d4ae354anthony      pos_scale = fabs(kernel->positive_range + kernel->negative_range);
4781cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    else
4782f4e0031305baeb01c89cfd2842cbbec021883550anthony      /* zero-summing kernel */
4783f4e0031305baeb01c89cfd2842cbbec021883550anthony      pos_scale = kernel->positive_range;
4784999bb2c20aa9d42875bb5adba44951988d4ae354anthony  }
478546a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Force kernel into a normalized zero-summing kernel */
4786999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
4787b978e458a8e1f210bcb580951cf623687236b2fecristy    pos_scale = ( fabs(kernel->positive_range) >= MagickEpsilon )
4788999bb2c20aa9d42875bb5adba44951988d4ae354anthony                 ? kernel->positive_range : 1.0;
4789b978e458a8e1f210bcb580951cf623687236b2fecristy    neg_scale = ( fabs(kernel->negative_range) >= MagickEpsilon )
4790999bb2c20aa9d42875bb5adba44951988d4ae354anthony                 ? -kernel->negative_range : 1.0;
4791999bb2c20aa9d42875bb5adba44951988d4ae354anthony  }
4792999bb2c20aa9d42875bb5adba44951988d4ae354anthony  else
4793999bb2c20aa9d42875bb5adba44951988d4ae354anthony    neg_scale = pos_scale;
4794999bb2c20aa9d42875bb5adba44951988d4ae354anthony
4795999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* finialize scaling_factor for positive and negative components */
4796999bb2c20aa9d42875bb5adba44951988d4ae354anthony  pos_scale = scaling_factor/pos_scale;
4797999bb2c20aa9d42875bb5adba44951988d4ae354anthony  neg_scale = scaling_factor/neg_scale;
4798cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4799bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
4800cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    if ( ! IsNan(kernel->values[i]) )
4801999bb2c20aa9d42875bb5adba44951988d4ae354anthony      kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
4802999bb2c20aa9d42875bb5adba44951988d4ae354anthony
4803999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* convolution output range */
4804999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->positive_range *= pos_scale;
4805999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->negative_range *= neg_scale;
4806999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* maximum and minimum values in kernel */
4807999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->maximum *= (kernel->maximum >= 0.0) ? pos_scale : neg_scale;
4808999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->minimum *= (kernel->minimum >= 0.0) ? pos_scale : neg_scale;
4809999bb2c20aa9d42875bb5adba44951988d4ae354anthony
481046a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* swap kernel settings if user's scaling factor is negative */
4811999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( scaling_factor < MagickEpsilon ) {
4812999bb2c20aa9d42875bb5adba44951988d4ae354anthony    double t;
4813999bb2c20aa9d42875bb5adba44951988d4ae354anthony    t = kernel->positive_range;
4814999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->positive_range = kernel->negative_range;
4815999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->negative_range = t;
4816999bb2c20aa9d42875bb5adba44951988d4ae354anthony    t = kernel->maximum;
4817999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->maximum = kernel->minimum;
4818999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->minimum = 1;
4819999bb2c20aa9d42875bb5adba44951988d4ae354anthony  }
4820cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4821cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  return;
4822cc6c836da2a53b6023b716e4973090a6714dc3b0anthony}
4823cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4824cc6c836da2a53b6023b716e4973090a6714dc3b0anthony/*
4825cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4826cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4827cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4828cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
482946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%     S h o w K e r n e l I n f o                                             %
483083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
483183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
483283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
483383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
483483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
48354fd27e21043be809d66c8202e779255e5b660d2danthony%  ShowKernelInfo() outputs the details of the given kernel defination to
48364fd27e21043be809d66c8202e779255e5b660d2danthony%  standard error, generally due to a users 'showkernel' option request.
483783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
483883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  The format of the ShowKernel method is:
483983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
484057fe7a498c1302232dac8466864e84b12fad0807anthony%      void ShowKernelInfo(const KernelInfo *kernel)
484183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
484283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  A description of each parameter follows:
484383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
484483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o kernel: the Morphology/Convolution kernel
484583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
484683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony*/
4847433d11887841b922ec5e6805f9fdd240c320b92ecristyMagickPrivate void ShowKernelInfo(const KernelInfo *kernel)
484883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony{
484957fe7a498c1302232dac8466864e84b12fad0807anthony  const KernelInfo
48507a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *k;
48517a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4852bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  size_t
48537a01dcf50ce12cb2a789bedff51e9345f022432eanthony    c, i, u, v;
48547a01dcf50ce12cb2a789bedff51e9345f022432eanthony
48557a01dcf50ce12cb2a789bedff51e9345f022432eanthony  for (c=0, k=kernel;  k != (KernelInfo *) NULL;  c++, k=k->next ) {
48567a01dcf50ce12cb2a789bedff51e9345f022432eanthony
48575acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "Kernel");
48587a01dcf50ce12cb2a789bedff51e9345f022432eanthony    if ( kernel->next != (KernelInfo *) NULL )
48595acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " #%lu", (unsigned long) c );
48605acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, " \"%s",
4861042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy          CommandOptionToMnemonic(MagickKernelOptions, k->type) );
4862b978e458a8e1f210bcb580951cf623687236b2fecristy    if ( fabs(k->angle) >= MagickEpsilon )
48635acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, "@%lg", k->angle);
48645acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "\" of size %lux%lu%+ld%+ld",(unsigned long)
48651e604812fad85bb96f757a2393015ae3d061c39acristy      k->width,(unsigned long) k->height,(long) k->x,(long) k->y);
48665acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr,
48677a01dcf50ce12cb2a789bedff51e9345f022432eanthony          " with values from %.*lg to %.*lg\n",
48687a01dcf50ce12cb2a789bedff51e9345f022432eanthony          GetMagickPrecision(), k->minimum,
48697a01dcf50ce12cb2a789bedff51e9345f022432eanthony          GetMagickPrecision(), k->maximum);
48705acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "Forming a output range from %.*lg to %.*lg",
48717a01dcf50ce12cb2a789bedff51e9345f022432eanthony          GetMagickPrecision(), k->negative_range,
487246a369d839971ab627bdb31a93d8bd63e81b65a3anthony          GetMagickPrecision(), k->positive_range);
487346a369d839971ab627bdb31a93d8bd63e81b65a3anthony    if ( fabs(k->positive_range+k->negative_range) < MagickEpsilon )
48745acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " (Zero-Summing)\n");
487546a369d839971ab627bdb31a93d8bd63e81b65a3anthony    else if ( fabs(k->positive_range+k->negative_range-1.0) < MagickEpsilon )
48765acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " (Normalized)\n");
487746a369d839971ab627bdb31a93d8bd63e81b65a3anthony    else
48785acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " (Sum %.*lg)\n",
487946a369d839971ab627bdb31a93d8bd63e81b65a3anthony          GetMagickPrecision(), k->positive_range+k->negative_range);
488043c4925e5305a26e48d68f7893e94f55d0831c39anthony    for (i=v=0; v < k->height; v++) {
48815acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, "%2lu:", (unsigned long) v );
488243c4925e5305a26e48d68f7893e94f55d0831c39anthony      for (u=0; u < k->width; u++, i++)
48837a01dcf50ce12cb2a789bedff51e9345f022432eanthony        if ( IsNan(k->values[i]) )
48845acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr," %*s", GetMagickPrecision()+3, "nan");
48857a01dcf50ce12cb2a789bedff51e9345f022432eanthony        else
48865acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr," %*.*lg", GetMagickPrecision()+3,
4887d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy              GetMagickPrecision(), (double) k->values[i]);
48885acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr,"\n");
48897a01dcf50ce12cb2a789bedff51e9345f022432eanthony    }
489083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  }
489183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony}
4892cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4893cc6c836da2a53b6023b716e4973090a6714dc3b0anthony/*
4894cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4895cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4896cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4897cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
489843c4925e5305a26e48d68f7893e94f55d0831c39anthony%     U n i t y A d d K e r n a l I n f o                                     %
489943c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
490043c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
490143c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
490243c4925e5305a26e48d68f7893e94f55d0831c39anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
490343c4925e5305a26e48d68f7893e94f55d0831c39anthony%
490443c4925e5305a26e48d68f7893e94f55d0831c39anthony%  UnityAddKernelInfo() Adds a given amount of the 'Unity' Convolution Kernel
490543c4925e5305a26e48d68f7893e94f55d0831c39anthony%  to the given pre-scaled and normalized Kernel.  This in effect adds that
490643c4925e5305a26e48d68f7893e94f55d0831c39anthony%  amount of the original image into the resulting convolution kernel.  This
490743c4925e5305a26e48d68f7893e94f55d0831c39anthony%  value is usually provided by the user as a percentage value in the
490843c4925e5305a26e48d68f7893e94f55d0831c39anthony%  'convolve:scale' setting.
490943c4925e5305a26e48d68f7893e94f55d0831c39anthony%
4910501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%  The resulting effect is to convert the defined kernels into blended
4911501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%  soft-blurs, unsharp kernels or into sharpening kernels.
491243c4925e5305a26e48d68f7893e94f55d0831c39anthony%
491346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The format of the UnityAdditionKernelInfo method is:
491443c4925e5305a26e48d68f7893e94f55d0831c39anthony%
491543c4925e5305a26e48d68f7893e94f55d0831c39anthony%      void UnityAdditionKernelInfo(KernelInfo *kernel, const double scale )
491643c4925e5305a26e48d68f7893e94f55d0831c39anthony%
491743c4925e5305a26e48d68f7893e94f55d0831c39anthony%  A description of each parameter follows:
491843c4925e5305a26e48d68f7893e94f55d0831c39anthony%
491943c4925e5305a26e48d68f7893e94f55d0831c39anthony%    o kernel: the Morphology/Convolution kernel
492043c4925e5305a26e48d68f7893e94f55d0831c39anthony%
492143c4925e5305a26e48d68f7893e94f55d0831c39anthony%    o scale:
492243c4925e5305a26e48d68f7893e94f55d0831c39anthony%             scaling factor for the unity kernel to be added to
492343c4925e5305a26e48d68f7893e94f55d0831c39anthony%             the given kernel.
492443c4925e5305a26e48d68f7893e94f55d0831c39anthony%
492543c4925e5305a26e48d68f7893e94f55d0831c39anthony*/
492643c4925e5305a26e48d68f7893e94f55d0831c39anthonyMagickExport void UnityAddKernelInfo(KernelInfo *kernel,
492743c4925e5305a26e48d68f7893e94f55d0831c39anthony  const double scale)
492843c4925e5305a26e48d68f7893e94f55d0831c39anthony{
492946a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* do the other kernels in a multi-kernel list first */
493046a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( kernel->next != (KernelInfo *) NULL)
493146a369d839971ab627bdb31a93d8bd63e81b65a3anthony    UnityAddKernelInfo(kernel->next, scale);
493243c4925e5305a26e48d68f7893e94f55d0831c39anthony
493346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Add the scaled unity kernel to the existing kernel */
493443c4925e5305a26e48d68f7893e94f55d0831c39anthony  kernel->values[kernel->x+kernel->y*kernel->width] += scale;
493546a369d839971ab627bdb31a93d8bd63e81b65a3anthony  CalcKernelMetaData(kernel);  /* recalculate the meta-data */
493643c4925e5305a26e48d68f7893e94f55d0831c39anthony
493743c4925e5305a26e48d68f7893e94f55d0831c39anthony  return;
493843c4925e5305a26e48d68f7893e94f55d0831c39anthony}
493943c4925e5305a26e48d68f7893e94f55d0831c39anthony
494043c4925e5305a26e48d68f7893e94f55d0831c39anthony/*
494143c4925e5305a26e48d68f7893e94f55d0831c39anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
494243c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
494343c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
494443c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
494543c4925e5305a26e48d68f7893e94f55d0831c39anthony%     Z e r o K e r n e l N a n s                                             %
4946cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4947cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4948cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4949cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4950cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4951cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  ZeroKernelNans() replaces any special 'nan' value that may be present in
4952cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  the kernel with a zero value.  This is typically done when the kernel will
4953cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  be used in special hardware (GPU) convolution processors, to simply
4954cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  matters.
4955cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4956cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  The format of the ZeroKernelNans method is:
4957cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
495846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      void ZeroKernelNans (KernelInfo *kernel)
4959cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4960cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  A description of each parameter follows:
4961cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4962cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%    o kernel: the Morphology/Convolution kernel
4963cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4964cc6c836da2a53b6023b716e4973090a6714dc3b0anthony*/
4965cf592636b16d028b14c5fa9dffdc3a94eae7c2f3cristyMagickPrivate void ZeroKernelNans(KernelInfo *kernel)
4966cc6c836da2a53b6023b716e4973090a6714dc3b0anthony{
4967bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register size_t
4968cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    i;
4969cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
497046a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* do the other kernels in a multi-kernel list first */
49711b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL)
49721b2bc0a7da432e6e1cc0480280402df213faa940anthony    ZeroKernelNans(kernel->next);
49731b2bc0a7da432e6e1cc0480280402df213faa940anthony
497443c4925e5305a26e48d68f7893e94f55d0831c39anthony  for (i=0; i < (kernel->width*kernel->height); i++)
4975cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    if ( IsNan(kernel->values[i]) )
4976cc6c836da2a53b6023b716e4973090a6714dc3b0anthony      kernel->values[i] = 0.0;
4977cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4978cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  return;
4979cc6c836da2a53b6023b716e4973090a6714dc3b0anthony}
4980