morphology.c revision 6a2180cee55312a7c0c633670803f9face88a82a
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"
556a2180cee55312a7c0c633670803f9face88a82acristy#include "MagickCore/channel.h"
564c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/color-private.h"
574c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/enhance.h"
584c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/exception.h"
594c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/exception-private.h"
604c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/gem.h"
618ea81224e9ff022e56eb2cddb12860a8b2e90411cristy#include "MagickCore/gem-private.h"
624c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/hashmap.h"
634c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/image.h"
644c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/image-private.h"
654c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/list.h"
664c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/magick.h"
674c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/memory_.h"
68e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy#include "MagickCore/memory-private.h"
694c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/monitor-private.h"
704c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/morphology.h"
714c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/morphology-private.h"
724c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/option.h"
734c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/pixel-accessor.h"
74f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy#include "MagickCore/pixel-private.h"
754c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/prepress.h"
764c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/quantize.h"
77ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy#include "MagickCore/resource_.h"
784c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/registry.h"
794c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/semaphore.h"
804c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/splay-tree.h"
814c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/statistic.h"
824c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/string_.h"
834c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/string-private.h"
8416881e68c6165c6191fc44151a8a4320e3dd1ffdcristy#include "MagickCore/thread-private.h"
854c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/token.h"
864c08aed51c5899665ade97263692328eea4af106cristy#include "MagickCore/utility.h"
87d1dd6e4fefa0810b9893e6ac9418f79c97c1b39acristy#include "MagickCore/utility-private.h"
88a29d45f897949f04a47bb3da077395969f13dcbacristy
89c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony/*
90a29d45f897949f04a47bb3da077395969f13dcbacristy  Other global definitions used by module.
91a29d45f897949f04a47bb3da077395969f13dcbacristy*/
9229188a8682a98d4b7882cca434b170517555fc7danthonystatic inline double MagickMin(const double x,const double y)
9329188a8682a98d4b7882cca434b170517555fc7danthony{
9429188a8682a98d4b7882cca434b170517555fc7danthony  return( x < y ? x : y);
9529188a8682a98d4b7882cca434b170517555fc7danthony}
9629188a8682a98d4b7882cca434b170517555fc7danthonystatic inline double MagickMax(const double x,const double y)
9729188a8682a98d4b7882cca434b170517555fc7danthony{
9829188a8682a98d4b7882cca434b170517555fc7danthony  return( x > y ? x : y);
9929188a8682a98d4b7882cca434b170517555fc7danthony}
10029188a8682a98d4b7882cca434b170517555fc7danthony#define Minimize(assign,value) assign=MagickMin(assign,value)
10129188a8682a98d4b7882cca434b170517555fc7danthony#define Maximize(assign,value) assign=MagickMax(assign,value)
10229188a8682a98d4b7882cca434b170517555fc7danthony
10340ca0b982379d4ab2716435a46603d56b5b218b1anthony/* Integer Factorial Function - for a Binomial kernel */
10440ca0b982379d4ab2716435a46603d56b5b218b1anthony#if 1
10540ca0b982379d4ab2716435a46603d56b5b218b1anthonystatic inline size_t fact(size_t n)
10640ca0b982379d4ab2716435a46603d56b5b218b1anthony{
107f13c594b1d9509e479fd845c3b8a5bb1351c32eacristy  size_t f,l;
10840ca0b982379d4ab2716435a46603d56b5b218b1anthony  for(f=1, l=2; l <= n; f=f*l, l++);
10940ca0b982379d4ab2716435a46603d56b5b218b1anthony  return(f);
11040ca0b982379d4ab2716435a46603d56b5b218b1anthony}
11140ca0b982379d4ab2716435a46603d56b5b218b1anthony#elif 1 /* glibc floating point alternatives */
11240ca0b982379d4ab2716435a46603d56b5b218b1anthony#define fact(n) ((size_t)tgamma((double)n+1))
11340ca0b982379d4ab2716435a46603d56b5b218b1anthony#else
11440ca0b982379d4ab2716435a46603d56b5b218b1anthony#define fact(n) ((size_t)lgamma((double)n+1))
11540ca0b982379d4ab2716435a46603d56b5b218b1anthony#endif
11640ca0b982379d4ab2716435a46603d56b5b218b1anthony
11740ca0b982379d4ab2716435a46603d56b5b218b1anthony
118c4c86e0ad78bebf6b9f931a659fb82987a7042e3anthony/* Currently these are only internal to this module */
119c4c86e0ad78bebf6b9f931a659fb82987a7042e3anthonystatic void
12046a369d839971ab627bdb31a93d8bd63e81b65a3anthony  CalcKernelMetaData(KernelInfo *),
121bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  ExpandMirrorKernelInfo(KernelInfo *),
122bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  ExpandRotateKernelInfo(KernelInfo *, const double),
123ef656913b0b30d713ae94c82c47693c9dc69c9f4cristy  RotateKernelInfo(KernelInfo *, double);
124602ab9b30b644a78a4057da93d838a77391ec0acanthony
1253dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony
1263dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony/* Quick function to find last kernel in a kernel list */
1273dd0f620e7a1d12f747ce167844cd7269bfa9f12anthonystatic inline KernelInfo *LastKernelInfo(KernelInfo *kernel)
1283dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony{
1293dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  while (kernel->next != (KernelInfo *) NULL)
1303dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    kernel = kernel->next;
1313dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  return(kernel);
1323dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony}
1333dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony
134602ab9b30b644a78a4057da93d838a77391ec0acanthony/*
135602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
136602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
137602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
138602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
13983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%     A c q u i r e K e r n e l I n f o                                       %
140602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
141602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
142602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
143602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
144602ab9b30b644a78a4057da93d838a77391ec0acanthony%
1452be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%  AcquireKernelInfo() takes the given string (generally supplied by the
146602ab9b30b644a78a4057da93d838a77391ec0acanthony%  user) and converts it into a Morphology/Convolution Kernel.  This allows
147602ab9b30b644a78a4057da93d838a77391ec0acanthony%  users to specify a kernel from a number of pre-defined kernels, or to fully
148602ab9b30b644a78a4057da93d838a77391ec0acanthony%  specify their own kernel for a specific Convolution or Morphology
149602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Operation.
150602ab9b30b644a78a4057da93d838a77391ec0acanthony%
151602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The kernel so generated can be any rectangular array of floating point
152602ab9b30b644a78a4057da93d838a77391ec0acanthony%  values (doubles) with the 'control point' or 'pixel being affected'
153602ab9b30b644a78a4057da93d838a77391ec0acanthony%  anywhere within that array of values.
154602ab9b30b644a78a4057da93d838a77391ec0acanthony%
15583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  Previously IM was restricted to a square of odd size using the exact
15619910ef25dd3d99d1981a9e42c934133170ee714anthony%  center as origin, this is no longer the case, and any rectangular kernel
15783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  with any value being declared the origin. This in turn allows the use of
15883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  highly asymmetrical kernels.
159602ab9b30b644a78a4057da93d838a77391ec0acanthony%
160602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The floating point values in the kernel can also include a special value
16183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  known as 'nan' or 'not a number' to indicate that this value is not part
16283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  of the kernel array. This allows you to shaped the kernel within its
16383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  rectangular area. That is 'nan' values provide a 'mask' for the kernel
16483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  shape.  However at least one non-nan value must be provided for correct
16583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  working of a kernel.
166602ab9b30b644a78a4057da93d838a77391ec0acanthony%
1677a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  The returned kernel should be freed using the DestroyKernelInfo() when you
1687a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  are finished with it.  Do not free this memory yourself.
169602ab9b30b644a78a4057da93d838a77391ec0acanthony%
170602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Input kernel defintion strings can consist of any of three types.
171602ab9b30b644a78a4057da93d838a77391ec0acanthony%
172bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    "name:args[[@><]"
17329188a8682a98d4b7882cca434b170517555fc7danthony%         Select from one of the built in kernels, using the name and
17429188a8682a98d4b7882cca434b170517555fc7danthony%         geometry arguments supplied.  See AcquireKernelBuiltIn()
175602ab9b30b644a78a4057da93d838a77391ec0acanthony%
176bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    "WxH[+X+Y][@><]:num, num, num ..."
1771b2bc0a7da432e6e1cc0480280402df213faa940anthony%         a kernel of size W by H, with W*H floating point numbers following.
178602ab9b30b644a78a4057da93d838a77391ec0acanthony%         the 'center' can be optionally be defined at +X+Y (such that +0+0
17929188a8682a98d4b7882cca434b170517555fc7danthony%         is top left corner). If not defined the pixel in the center, for
18029188a8682a98d4b7882cca434b170517555fc7danthony%         odd sizes, or to the immediate top or left of center for even sizes
18129188a8682a98d4b7882cca434b170517555fc7danthony%         is automatically selected.
182602ab9b30b644a78a4057da93d838a77391ec0acanthony%
18329188a8682a98d4b7882cca434b170517555fc7danthony%    "num, num, num, num, ..."
18429188a8682a98d4b7882cca434b170517555fc7danthony%         list of floating point numbers defining an 'old style' odd sized
18529188a8682a98d4b7882cca434b170517555fc7danthony%         square kernel.  At least 9 values should be provided for a 3x3
18629188a8682a98d4b7882cca434b170517555fc7danthony%         square kernel, 25 for a 5x5 square kernel, 49 for 7x7, etc.
18729188a8682a98d4b7882cca434b170517555fc7danthony%         Values can be space or comma separated.  This is not recommended.
188602ab9b30b644a78a4057da93d838a77391ec0acanthony%
1897a01dcf50ce12cb2a789bedff51e9345f022432eanthony%  You can define a 'list of kernels' which can be used by some morphology
1901e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp%  operators A list is defined as a semi-colon separated list kernels.
1917a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
192dbc8989a61339951c6434d9a43e7b6fefb5da374anthony%     " kernel ; kernel ; kernel ; "
1937a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
1941dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%  Any extra ';' characters, at start, end or between kernel defintions are
19543c4925e5305a26e48d68f7893e94f55d0831c39anthony%  simply ignored.
19643c4925e5305a26e48d68f7893e94f55d0831c39anthony%
197bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The special flags will expand a single kernel, into a list of rotated
198bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  kernels. A '@' flag will expand a 3x3 kernel into a list of 45-degree
199bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  cyclic rotations, while a '>' will generate a list of 90-degree rotations.
200bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The '<' also exands using 90-degree rotates, but giving a 180-degree
201bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  reflected kernel before the +/- 90-degree rotations, which can be important
202bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  for Thinning operations.
203bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
20443c4925e5305a26e48d68f7893e94f55d0831c39anthony%  Note that 'name' kernels will start with an alphabetic character while the
20543c4925e5305a26e48d68f7893e94f55d0831c39anthony%  new kernel specification has a ':' character in its specification string.
20643c4925e5305a26e48d68f7893e94f55d0831c39anthony%  If neither is the case, it is assumed an old style of a simple list of
20743c4925e5305a26e48d68f7893e94f55d0831c39anthony%  numbers generating a odd-sized square kernel has been given.
2087a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
209602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The format of the AcquireKernal method is:
210602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2112be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%      KernelInfo *AcquireKernelInfo(const char *kernel_string)
212602ab9b30b644a78a4057da93d838a77391ec0acanthony%
213602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
214602ab9b30b644a78a4057da93d838a77391ec0acanthony%
215602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel_string: the Morphology/Convolution kernel wanted.
216602ab9b30b644a78a4057da93d838a77391ec0acanthony%
217602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
218602ab9b30b644a78a4057da93d838a77391ec0acanthony
219c84dce50867229e4872193e8eed5dbab58eb9f02anthony/* This was separated so that it could be used as a separate
2205ef8e94ff55717be2387d537bd49025780a1a558anthony** array input handling function, such as for -color-matrix
221c84dce50867229e4872193e8eed5dbab58eb9f02anthony*/
2225ef8e94ff55717be2387d537bd49025780a1a558anthonystatic KernelInfo *ParseKernelArray(const char *kernel_string)
223602ab9b30b644a78a4057da93d838a77391ec0acanthony{
2242be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  KernelInfo
225602ab9b30b644a78a4057da93d838a77391ec0acanthony    *kernel;
226602ab9b30b644a78a4057da93d838a77391ec0acanthony
227602ab9b30b644a78a4057da93d838a77391ec0acanthony  char
228602ab9b30b644a78a4057da93d838a77391ec0acanthony    token[MaxTextExtent];
229602ab9b30b644a78a4057da93d838a77391ec0acanthony
230602ab9b30b644a78a4057da93d838a77391ec0acanthony  const char
2315ef8e94ff55717be2387d537bd49025780a1a558anthony    *p,
2325ef8e94ff55717be2387d537bd49025780a1a558anthony    *end;
233602ab9b30b644a78a4057da93d838a77391ec0acanthony
234bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
235c84dce50867229e4872193e8eed5dbab58eb9f02anthony    i;
236602ab9b30b644a78a4057da93d838a77391ec0acanthony
23729188a8682a98d4b7882cca434b170517555fc7danthony  double
23829188a8682a98d4b7882cca434b170517555fc7danthony    nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
23929188a8682a98d4b7882cca434b170517555fc7danthony
24043c4925e5305a26e48d68f7893e94f55d0831c39anthony  MagickStatusType
24143c4925e5305a26e48d68f7893e94f55d0831c39anthony    flags;
24243c4925e5305a26e48d68f7893e94f55d0831c39anthony
24343c4925e5305a26e48d68f7893e94f55d0831c39anthony  GeometryInfo
24443c4925e5305a26e48d68f7893e94f55d0831c39anthony    args;
24543c4925e5305a26e48d68f7893e94f55d0831c39anthony
246a64b85d7873d5e540fe6e2941aa98ec7653a4e2dcristy  kernel=(KernelInfo *) AcquireQuantumMemory(1,sizeof(*kernel));
2472be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  if (kernel == (KernelInfo *)NULL)
248602ab9b30b644a78a4057da93d838a77391ec0acanthony    return(kernel);
249602ab9b30b644a78a4057da93d838a77391ec0acanthony  (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
25043c4925e5305a26e48d68f7893e94f55d0831c39anthony  kernel->minimum = kernel->maximum = kernel->angle = 0.0;
2517a01dcf50ce12cb2a789bedff51e9345f022432eanthony  kernel->negative_range = kernel->positive_range = 0.0;
252602ab9b30b644a78a4057da93d838a77391ec0acanthony  kernel->type = UserDefinedKernel;
2537a01dcf50ce12cb2a789bedff51e9345f022432eanthony  kernel->next = (KernelInfo *) NULL;
254d43a46bc9598004091eae232bc7938e009b494a1cristy  kernel->signature = MagickSignature;
2555e6be1e6a77c230e4a204fa9163d873104730c35cristy  if (kernel_string == (const char *) NULL)
2565e6be1e6a77c230e4a204fa9163d873104730c35cristy    return(kernel);
257602ab9b30b644a78a4057da93d838a77391ec0acanthony
2585ef8e94ff55717be2387d537bd49025780a1a558anthony  /* find end of this specific kernel definition string */
2595ef8e94ff55717be2387d537bd49025780a1a558anthony  end = strchr(kernel_string, ';');
2605ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( end == (char *) NULL )
2615ef8e94ff55717be2387d537bd49025780a1a558anthony    end = strchr(kernel_string, '\0');
2625ef8e94ff55717be2387d537bd49025780a1a558anthony
263a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* clear flags - for Expanding kernel lists thorugh rotations */
26443c4925e5305a26e48d68f7893e94f55d0831c39anthony   flags = NoValue;
26543c4925e5305a26e48d68f7893e94f55d0831c39anthony
266f68116b9d5afef3c343faeb599a3c0ffc8e9b244anthony  /* Has a ':' in argument - New user kernel specification
267f68116b9d5afef3c343faeb599a3c0ffc8e9b244anthony     FUTURE: this split on ':' could be done by StringToken()
268f68116b9d5afef3c343faeb599a3c0ffc8e9b244anthony   */
269602ab9b30b644a78a4057da93d838a77391ec0acanthony  p = strchr(kernel_string, ':');
2705ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( p != (char *) NULL && p < end)
271602ab9b30b644a78a4057da93d838a77391ec0acanthony    {
272602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* ParseGeometry() needs the geometry separated! -- Arrgghh */
273150989ed67ef9da53141a65e5f3ebdb05dd025abcristy      memcpy(token, kernel_string, (size_t) (p-kernel_string));
274602ab9b30b644a78a4057da93d838a77391ec0acanthony      token[p-kernel_string] = '\0';
275c84dce50867229e4872193e8eed5dbab58eb9f02anthony      SetGeometryInfo(&args);
276602ab9b30b644a78a4057da93d838a77391ec0acanthony      flags = ParseGeometry(token, &args);
277602ab9b30b644a78a4057da93d838a77391ec0acanthony
27829188a8682a98d4b7882cca434b170517555fc7danthony      /* Size handling and checks of geometry settings */
279602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( (flags & WidthValue) == 0 ) /* if no width then */
280602ab9b30b644a78a4057da93d838a77391ec0acanthony        args.rho = args.sigma;         /* then  width = height */
281602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.rho < 1.0 )            /* if width too small */
282602ab9b30b644a78a4057da93d838a77391ec0acanthony         args.rho = 1.0;               /* then  width = 1 */
283602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.sigma < 1.0 )          /* if height too small */
284602ab9b30b644a78a4057da93d838a77391ec0acanthony        args.sigma = args.rho;         /* then  height = width */
285bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->width = (size_t)args.rho;
286bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->height = (size_t)args.sigma;
287602ab9b30b644a78a4057da93d838a77391ec0acanthony
288602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* Offset Handling and Checks */
289602ab9b30b644a78a4057da93d838a77391ec0acanthony      if ( args.xi  < 0.0 || args.psi < 0.0 )
29083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        return(DestroyKernelInfo(kernel));
291bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->x = ((flags & XValue)!=0) ? (ssize_t)args.xi
292ea068a53d23d6dca08f1bce44c8937d54f83b983anthony                                        : (ssize_t) (kernel->width-1)/2;
293bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->y = ((flags & YValue)!=0) ? (ssize_t)args.psi
294ea068a53d23d6dca08f1bce44c8937d54f83b983anthony                                        : (ssize_t) (kernel->height-1)/2;
295bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      if ( kernel->x >= (ssize_t) kernel->width ||
296bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy           kernel->y >= (ssize_t) kernel->height )
29783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        return(DestroyKernelInfo(kernel));
298602ab9b30b644a78a4057da93d838a77391ec0acanthony
299602ab9b30b644a78a4057da93d838a77391ec0acanthony      p++; /* advance beyond the ':' */
300602ab9b30b644a78a4057da93d838a77391ec0acanthony    }
301602ab9b30b644a78a4057da93d838a77391ec0acanthony  else
302c84dce50867229e4872193e8eed5dbab58eb9f02anthony    { /* ELSE - Old old specification, forming odd-square kernel */
303602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* count up number of values given */
304602ab9b30b644a78a4057da93d838a77391ec0acanthony      p=(const char *) kernel_string;
305a699b171eff7e0178463e8f271b35a3cbb995f0ecristy      while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
30629188a8682a98d4b7882cca434b170517555fc7danthony        p++;  /* ignore "'" chars for convolve filter usage - Cristy */
3075ef8e94ff55717be2387d537bd49025780a1a558anthony      for (i=0; p < end; i++)
308602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
309602ab9b30b644a78a4057da93d838a77391ec0acanthony        GetMagickToken(p,&p,token);
310602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (*token == ',')
311602ab9b30b644a78a4057da93d838a77391ec0acanthony          GetMagickToken(p,&p,token);
312602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
313602ab9b30b644a78a4057da93d838a77391ec0acanthony      /* set the size of the kernel - old sized square */
314bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->width = kernel->height= (size_t) sqrt((double) i+1.0);
315bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
316602ab9b30b644a78a4057da93d838a77391ec0acanthony      p=(const char *) kernel_string;
31729188a8682a98d4b7882cca434b170517555fc7danthony      while ((isspace((int) ((unsigned char) *p)) != 0) || (*p == '\''))
31829188a8682a98d4b7882cca434b170517555fc7danthony        p++;  /* ignore "'" chars for convolve filter usage - Cristy */
319602ab9b30b644a78a4057da93d838a77391ec0acanthony    }
320602ab9b30b644a78a4057da93d838a77391ec0acanthony
321602ab9b30b644a78a4057da93d838a77391ec0acanthony  /* Read in the kernel values from rest of input string argument */
322e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy  kernel->values=(MagickRealType *) MagickAssumeAligned(AcquireAlignedMemory(
323e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy    kernel->width,kernel->height*sizeof(*kernel->values)));
324d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy  if (kernel->values == (MagickRealType *) NULL)
32583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    return(DestroyKernelInfo(kernel));
326c99304fe3c8d9c617da792b40b57c118bb1249afcristy  kernel->minimum = +MagickHuge;
327c99304fe3c8d9c617da792b40b57c118bb1249afcristy  kernel->maximum = -MagickHuge;
328c99304fe3c8d9c617da792b40b57c118bb1249afcristy  kernel->negative_range = kernel->positive_range = 0.0;
329bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; (i < (ssize_t) (kernel->width*kernel->height)) && (p < end); i++)
330602ab9b30b644a78a4057da93d838a77391ec0acanthony  {
331602ab9b30b644a78a4057da93d838a77391ec0acanthony    GetMagickToken(p,&p,token);
332602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (*token == ',')
333602ab9b30b644a78a4057da93d838a77391ec0acanthony      GetMagickToken(p,&p,token);
33429188a8682a98d4b7882cca434b170517555fc7danthony    if (    LocaleCompare("nan",token) == 0
335c84dce50867229e4872193e8eed5dbab58eb9f02anthony        || LocaleCompare("-",token) == 0 ) {
336ea068a53d23d6dca08f1bce44c8937d54f83b983anthony      kernel->values[i] = nan; /* this value is not part of neighbourhood */
33729188a8682a98d4b7882cca434b170517555fc7danthony    }
33829188a8682a98d4b7882cca434b170517555fc7danthony    else {
3390ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony      kernel->values[i] = StringToDouble(token,(char **) NULL);
34029188a8682a98d4b7882cca434b170517555fc7danthony      ( kernel->values[i] < 0)
341c99304fe3c8d9c617da792b40b57c118bb1249afcristy          ?  ( kernel->negative_range += kernel->values[i] )
342c99304fe3c8d9c617da792b40b57c118bb1249afcristy          :  ( kernel->positive_range += kernel->values[i] );
343c99304fe3c8d9c617da792b40b57c118bb1249afcristy      Minimize(kernel->minimum, kernel->values[i]);
344c99304fe3c8d9c617da792b40b57c118bb1249afcristy      Maximize(kernel->maximum, kernel->values[i]);
34529188a8682a98d4b7882cca434b170517555fc7danthony    }
34629188a8682a98d4b7882cca434b170517555fc7danthony  }
34729188a8682a98d4b7882cca434b170517555fc7danthony
3485ef8e94ff55717be2387d537bd49025780a1a558anthony  /* sanity check -- no more values in kernel definition */
3495ef8e94ff55717be2387d537bd49025780a1a558anthony  GetMagickToken(p,&p,token);
3505ef8e94ff55717be2387d537bd49025780a1a558anthony  if ( *token != '\0' && *token != ';' && *token != '\'' )
3515ef8e94ff55717be2387d537bd49025780a1a558anthony    return(DestroyKernelInfo(kernel));
3525ef8e94ff55717be2387d537bd49025780a1a558anthony
353c84dce50867229e4872193e8eed5dbab58eb9f02anthony#if 0
354c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* this was the old method of handling a incomplete kernel */
355bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  if ( i < (ssize_t) (kernel->width*kernel->height) ) {
356c99304fe3c8d9c617da792b40b57c118bb1249afcristy    Minimize(kernel->minimum, kernel->values[i]);
357c99304fe3c8d9c617da792b40b57c118bb1249afcristy    Maximize(kernel->maximum, kernel->values[i]);
358bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for ( ; i < (ssize_t) (kernel->width*kernel->height); i++)
35929188a8682a98d4b7882cca434b170517555fc7danthony      kernel->values[i]=0.0;
360602ab9b30b644a78a4057da93d838a77391ec0acanthony  }
361c84dce50867229e4872193e8eed5dbab58eb9f02anthony#else
362c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* Number of values for kernel was not enough - Report Error */
363bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  if ( i < (ssize_t) (kernel->width*kernel->height) )
364c84dce50867229e4872193e8eed5dbab58eb9f02anthony    return(DestroyKernelInfo(kernel));
365c84dce50867229e4872193e8eed5dbab58eb9f02anthony#endif
366c84dce50867229e4872193e8eed5dbab58eb9f02anthony
367c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* check that we recieved at least one real (non-nan) value! */
368c84dce50867229e4872193e8eed5dbab58eb9f02anthony  if ( kernel->minimum == MagickHuge )
369c84dce50867229e4872193e8eed5dbab58eb9f02anthony    return(DestroyKernelInfo(kernel));
370602ab9b30b644a78a4057da93d838a77391ec0acanthony
37143c4925e5305a26e48d68f7893e94f55d0831c39anthony  if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel size */
372bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandRotateKernelInfo(kernel, 45.0); /* cyclic rotate 3x3 kernels */
373bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
374bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandRotateKernelInfo(kernel, 90.0); /* 90 degree rotate of kernel */
375bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
376bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    ExpandMirrorKernelInfo(kernel);       /* 90 degree mirror rotate */
37743c4925e5305a26e48d68f7893e94f55d0831c39anthony
378602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
379602ab9b30b644a78a4057da93d838a77391ec0acanthony}
380c84dce50867229e4872193e8eed5dbab58eb9f02anthony
38143c4925e5305a26e48d68f7893e94f55d0831c39anthonystatic KernelInfo *ParseKernelName(const char *kernel_string)
382c84dce50867229e4872193e8eed5dbab58eb9f02anthony{
383c84dce50867229e4872193e8eed5dbab58eb9f02anthony  char
384c84dce50867229e4872193e8eed5dbab58eb9f02anthony    token[MaxTextExtent];
385c84dce50867229e4872193e8eed5dbab58eb9f02anthony
386c84dce50867229e4872193e8eed5dbab58eb9f02anthony  const char
3877a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *p,
3887a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *end;
389c84dce50867229e4872193e8eed5dbab58eb9f02anthony
3909d314ff2c17a77996c05413c2013880387e50f0ecristy  GeometryInfo
3919d314ff2c17a77996c05413c2013880387e50f0ecristy    args;
3929d314ff2c17a77996c05413c2013880387e50f0ecristy
3939d314ff2c17a77996c05413c2013880387e50f0ecristy  KernelInfo
3949d314ff2c17a77996c05413c2013880387e50f0ecristy    *kernel;
3959d314ff2c17a77996c05413c2013880387e50f0ecristy
396c84dce50867229e4872193e8eed5dbab58eb9f02anthony  MagickStatusType
397c84dce50867229e4872193e8eed5dbab58eb9f02anthony    flags;
398c84dce50867229e4872193e8eed5dbab58eb9f02anthony
3999d314ff2c17a77996c05413c2013880387e50f0ecristy  ssize_t
4009d314ff2c17a77996c05413c2013880387e50f0ecristy    type;
401c84dce50867229e4872193e8eed5dbab58eb9f02anthony
402c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* Parse special 'named' kernel */
4035ef8e94ff55717be2387d537bd49025780a1a558anthony  GetMagickToken(kernel_string,&p,token);
404042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy  type=ParseCommandOption(MagickKernelOptions,MagickFalse,token);
405c84dce50867229e4872193e8eed5dbab58eb9f02anthony  if ( type < 0 || type == UserDefinedKernel )
4065ef8e94ff55717be2387d537bd49025780a1a558anthony    return((KernelInfo *)NULL);  /* not a valid named kernel */
407c84dce50867229e4872193e8eed5dbab58eb9f02anthony
408c84dce50867229e4872193e8eed5dbab58eb9f02anthony  while (((isspace((int) ((unsigned char) *p)) != 0) ||
4095ef8e94ff55717be2387d537bd49025780a1a558anthony          (*p == ',') || (*p == ':' )) && (*p != '\0') && (*p != ';'))
410c84dce50867229e4872193e8eed5dbab58eb9f02anthony    p++;
4117a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4127a01dcf50ce12cb2a789bedff51e9345f022432eanthony  end = strchr(p, ';'); /* end of this kernel defintion */
4137a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if ( end == (char *) NULL )
4147a01dcf50ce12cb2a789bedff51e9345f022432eanthony    end = strchr(p, '\0');
4157a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4167a01dcf50ce12cb2a789bedff51e9345f022432eanthony  /* ParseGeometry() needs the geometry separated! -- Arrgghh */
4177a01dcf50ce12cb2a789bedff51e9345f022432eanthony  memcpy(token, p, (size_t) (end-p));
4187a01dcf50ce12cb2a789bedff51e9345f022432eanthony  token[end-p] = '\0';
419c84dce50867229e4872193e8eed5dbab58eb9f02anthony  SetGeometryInfo(&args);
4207a01dcf50ce12cb2a789bedff51e9345f022432eanthony  flags = ParseGeometry(token, &args);
421c84dce50867229e4872193e8eed5dbab58eb9f02anthony
4223c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony#if 0
4233c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  /* For Debugging Geometry Input */
4245acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy  (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
4251e604812fad85bb96f757a2393015ae3d061c39acristy    flags, args.rho, args.sigma, args.xi, args.psi );
4263c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony#endif
4273c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony
428c84dce50867229e4872193e8eed5dbab58eb9f02anthony  /* special handling of missing values in input string */
429c84dce50867229e4872193e8eed5dbab58eb9f02anthony  switch( type ) {
430a9892d898acb81e1ec73106d892855fdc5a69427anthony    /* Shape Kernel Defaults */
431529482f4b494010a13338a74446c510712f670b3anthony    case UnityKernel:
432529482f4b494010a13338a74446c510712f670b3anthony      if ( (flags & WidthValue) == 0 )
433a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.rho = 1.0;    /* Default scale = 1.0, zero is valid */
4345ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
4355ef8e94ff55717be2387d537bd49025780a1a558anthony    case SquareKernel:
4365ef8e94ff55717be2387d537bd49025780a1a558anthony    case DiamondKernel:
4371ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctagonKernel:
4385ef8e94ff55717be2387d537bd49025780a1a558anthony    case DiskKernel:
4395ef8e94ff55717be2387d537bd49025780a1a558anthony    case PlusKernel:
4403dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case CrossKernel:
4415ef8e94ff55717be2387d537bd49025780a1a558anthony      if ( (flags & HeightValue) == 0 )
442a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.sigma = 1.0;    /* Default scale = 1.0, zero is valid */
4435ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
444c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RingKernel:
445c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      if ( (flags & XValue) == 0 )
446a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.xi = 1.0;       /* Default scale = 1.0, zero is valid */
447c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      break;
448a9892d898acb81e1ec73106d892855fdc5a69427anthony    case RectangleKernel:    /* Rectangle - set size defaults */
449a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & WidthValue) == 0 ) /* if no width then */
450a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.rho = args.sigma;         /* then  width = height */
451a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( args.rho < 1.0 )            /* if width too small */
452a9892d898acb81e1ec73106d892855fdc5a69427anthony          args.rho = 3;                /* then  width = 3 */
453a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( args.sigma < 1.0 )          /* if height too small */
454a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.sigma = args.rho;         /* then  height = width */
455a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & XValue) == 0 )     /* center offset if not defined */
456a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.xi = (double)(((ssize_t)args.rho-1)/2);
457a9892d898acb81e1ec73106d892855fdc5a69427anthony      if ( (flags & YValue) == 0 )
458a9892d898acb81e1ec73106d892855fdc5a69427anthony        args.psi = (double)(((ssize_t)args.sigma-1)/2);
459a9892d898acb81e1ec73106d892855fdc5a69427anthony      break;
460a9892d898acb81e1ec73106d892855fdc5a69427anthony    /* Distance Kernel Defaults */
4615ef8e94ff55717be2387d537bd49025780a1a558anthony    case ChebyshevKernel:
462bee715c4c0fd9efe6e21d8627ae8664434df7750anthony    case ManhattanKernel:
4631ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctagonalKernel:
4645ef8e94ff55717be2387d537bd49025780a1a558anthony    case EuclideanKernel:
46543c4925e5305a26e48d68f7893e94f55d0831c39anthony      if ( (flags & HeightValue) == 0 )           /* no distance scale */
46643c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma = 100.0;                       /* default distance scaling */
46743c4925e5305a26e48d68f7893e94f55d0831c39anthony      else if ( (flags & AspectValue ) != 0 )     /* '!' flag */
46843c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma = QuantumRange/(args.sigma+1); /* maximum pixel distance */
46943c4925e5305a26e48d68f7893e94f55d0831c39anthony      else if ( (flags & PercentValue ) != 0 )    /* '%' flag */
47043c4925e5305a26e48d68f7893e94f55d0831c39anthony        args.sigma *= QuantumRange/100.0;         /* percentage of color range */
4715ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
4725ef8e94ff55717be2387d537bd49025780a1a558anthony    default:
4735ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
474c84dce50867229e4872193e8eed5dbab58eb9f02anthony  }
475c84dce50867229e4872193e8eed5dbab58eb9f02anthony
476f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  kernel = AcquireKernelBuiltIn((KernelInfoType)type, &args);
477529482f4b494010a13338a74446c510712f670b3anthony  if ( kernel == (KernelInfo *) NULL )
478529482f4b494010a13338a74446c510712f670b3anthony    return(kernel);
479f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony
480f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  /* global expand to rotated kernel list - only for single kernels */
481f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  if ( kernel->next == (KernelInfo *) NULL ) {
482f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony    if ( (flags & AreaValue) != 0 )         /* '@' symbol in kernel args */
483bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandRotateKernelInfo(kernel, 45.0);
484bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    else if ( (flags & GreaterValue) != 0 ) /* '>' symbol in kernel args */
485bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandRotateKernelInfo(kernel, 90.0);
486bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    else if ( (flags & LessValue) != 0 )    /* '<' symbol in kernel args */
487bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      ExpandMirrorKernelInfo(kernel);
488f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  }
489f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony
490f0176c3994ec4f0eed7a5e0078e4bd7a012194c4anthony  return(kernel);
491c84dce50867229e4872193e8eed5dbab58eb9f02anthony}
492c84dce50867229e4872193e8eed5dbab58eb9f02anthony
4935ef8e94ff55717be2387d537bd49025780a1a558anthonyMagickExport KernelInfo *AcquireKernelInfo(const char *kernel_string)
4945ef8e94ff55717be2387d537bd49025780a1a558anthony{
4957a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4967a01dcf50ce12cb2a789bedff51e9345f022432eanthony  KernelInfo
497dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    *kernel,
49843c4925e5305a26e48d68f7893e94f55d0831c39anthony    *new_kernel;
4997a01dcf50ce12cb2a789bedff51e9345f022432eanthony
5005ef8e94ff55717be2387d537bd49025780a1a558anthony  char
5015ef8e94ff55717be2387d537bd49025780a1a558anthony    token[MaxTextExtent];
5025ef8e94ff55717be2387d537bd49025780a1a558anthony
5037a01dcf50ce12cb2a789bedff51e9345f022432eanthony  const char
504dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    *p;
5057a01dcf50ce12cb2a789bedff51e9345f022432eanthony
506bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  size_t
507e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony    kernel_number;
508e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony
5095e6be1e6a77c230e4a204fa9163d873104730c35cristy  if (kernel_string == (const char *) NULL)
5105e6be1e6a77c230e4a204fa9163d873104730c35cristy    return(ParseKernelArray(kernel_string));
511dbc8989a61339951c6434d9a43e7b6fefb5da374anthony  p = kernel_string;
51243c4925e5305a26e48d68f7893e94f55d0831c39anthony  kernel = NULL;
513e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony  kernel_number = 0;
5147a01dcf50ce12cb2a789bedff51e9345f022432eanthony
515dbc8989a61339951c6434d9a43e7b6fefb5da374anthony  while ( GetMagickToken(p,NULL,token),  *token != '\0' ) {
5167a01dcf50ce12cb2a789bedff51e9345f022432eanthony
5171e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp    /* ignore extra or multiple ';' kernel separators */
518dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    if ( *token != ';' ) {
5197a01dcf50ce12cb2a789bedff51e9345f022432eanthony
520dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      /* tokens starting with alpha is a Named kernel */
52143c4925e5305a26e48d68f7893e94f55d0831c39anthony      if (isalpha((int) *token) != 0)
52243c4925e5305a26e48d68f7893e94f55d0831c39anthony        new_kernel = ParseKernelName(p);
523dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      else /* otherwise a user defined kernel array */
52443c4925e5305a26e48d68f7893e94f55d0831c39anthony        new_kernel = ParseKernelArray(p);
525dbc8989a61339951c6434d9a43e7b6fefb5da374anthony
526e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony      /* Error handling -- this is not proper error handling! */
527e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony      if ( new_kernel == (KernelInfo *) NULL ) {
52875920b23a8a3883792cbb69d569c33cc789cf1b5cristy        (void) FormatLocaleFile(stderr,"Failed to parse kernel number #%.20g\n",
5291e604812fad85bb96f757a2393015ae3d061c39acristy          (double) kernel_number);
530e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony        if ( kernel != (KernelInfo *) NULL )
531e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony          kernel=DestroyKernelInfo(kernel);
532e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony        return((KernelInfo *) NULL);
533dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      }
534e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony
535e108a3fd07919343dc9e3b7720da59ef5c4c5c55anthony      /* initialise or append the kernel list */
5363dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony      if ( kernel == (KernelInfo *) NULL )
5373dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        kernel = new_kernel;
5383dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony      else
53943c4925e5305a26e48d68f7893e94f55d0831c39anthony        LastKernelInfo(kernel)->next = new_kernel;
540dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    }
541dbc8989a61339951c6434d9a43e7b6fefb5da374anthony
542dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    /* look for the next kernel in list */
543dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    p = strchr(p, ';');
544dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    if ( p == (char *) NULL )
545dbc8989a61339951c6434d9a43e7b6fefb5da374anthony      break;
546dbc8989a61339951c6434d9a43e7b6fefb5da374anthony    p++;
5475ef8e94ff55717be2387d537bd49025780a1a558anthony
548dbc8989a61339951c6434d9a43e7b6fefb5da374anthony  }
5497a01dcf50ce12cb2a789bedff51e9345f022432eanthony  return(kernel);
5505ef8e94ff55717be2387d537bd49025780a1a558anthony}
5515ef8e94ff55717be2387d537bd49025780a1a558anthony
552602ab9b30b644a78a4057da93d838a77391ec0acanthony
553602ab9b30b644a78a4057da93d838a77391ec0acanthony/*
554602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
555602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
556602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
557602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
558602ab9b30b644a78a4057da93d838a77391ec0acanthony%     A c q u i r e K e r n e l B u i l t I n                                 %
559602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
560602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
561602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
562602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
563602ab9b30b644a78a4057da93d838a77391ec0acanthony%
564602ab9b30b644a78a4057da93d838a77391ec0acanthony%  AcquireKernelBuiltIn() returned one of the 'named' built-in types of
565602ab9b30b644a78a4057da93d838a77391ec0acanthony%  kernels used for special purposes such as gaussian blurring, skeleton
566602ab9b30b644a78a4057da93d838a77391ec0acanthony%  pruning, and edge distance determination.
567602ab9b30b644a78a4057da93d838a77391ec0acanthony%
568602ab9b30b644a78a4057da93d838a77391ec0acanthony%  They take a KernelType, and a set of geometry style arguments, which were
569602ab9b30b644a78a4057da93d838a77391ec0acanthony%  typically decoded from a user supplied string, or from a more complex
570602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Morphology Method that was requested.
571602ab9b30b644a78a4057da93d838a77391ec0acanthony%
572602ab9b30b644a78a4057da93d838a77391ec0acanthony%  The format of the AcquireKernalBuiltIn method is:
573602ab9b30b644a78a4057da93d838a77391ec0acanthony%
5742be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy%      KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
575602ab9b30b644a78a4057da93d838a77391ec0acanthony%           const GeometryInfo args)
576602ab9b30b644a78a4057da93d838a77391ec0acanthony%
577602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
578602ab9b30b644a78a4057da93d838a77391ec0acanthony%
579602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o type: the pre-defined type of kernel wanted
580602ab9b30b644a78a4057da93d838a77391ec0acanthony%
581602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o args: arguments defining or modifying the kernel
582602ab9b30b644a78a4057da93d838a77391ec0acanthony%
583602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Convolution Kernels
584602ab9b30b644a78a4057da93d838a77391ec0acanthony%
58546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    Unity
586529482f4b494010a13338a74446c510712f670b3anthony%       The a No-Op or Scaling single element kernel.
58746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
5883c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Gaussian:{radius},{sigma}
5892489f53a1153c2b619b1c9a6744602e8840bd9a9glennrp%       Generate a two-dimensional gaussian kernel, as used by -gaussian.
590c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       The sigma for the curve is required.  The resulting kernel is
591c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       normalized,
592c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
593c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       If 'sigma' is zero, you get a single pixel on a field of zeros.
594602ab9b30b644a78a4057da93d838a77391ec0acanthony%
595602ab9b30b644a78a4057da93d838a77391ec0acanthony%       NOTE: that the 'radius' is optional, but if provided can limit (clip)
596602ab9b30b644a78a4057da93d838a77391ec0acanthony%       the final size of the resulting kernel to a square 2*radius+1 in size.
597602ab9b30b644a78a4057da93d838a77391ec0acanthony%       The radius should be at least 2 times that of the sigma value, or
598602ab9b30b644a78a4057da93d838a77391ec0acanthony%       sever clipping and aliasing may result.  If not given or set to 0 the
599602ab9b30b644a78a4057da93d838a77391ec0acanthony%       radius will be determined so as to produce the best minimal error
600602ab9b30b644a78a4057da93d838a77391ec0acanthony%       result, which is usally much larger than is normally needed.
601602ab9b30b644a78a4057da93d838a77391ec0acanthony%
602501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%    LoG:{radius},{sigma}
603501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        "Laplacian of a Gaussian" or "Mexician Hat" Kernel.
604501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        The supposed ideal edge detection, zero-summing kernel.
605501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
606501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        An alturnative to this kernel is to use a "DoG" with a sigma ratio of
607501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        approx 1.6 (according to wikipedia).
608501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
609501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%    DoG:{radius},{sigma1},{sigma2}
610c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        "Difference of Gaussians" Kernel.
611c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        As "Gaussian" but with a gaussian produced by 'sigma2' subtracted
612c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        from the gaussian produced by 'sigma1'. Typically sigma2 > sigma1.
613c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        The result is a zero-summing kernel.
614c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
615c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Blur:{radius},{sigma}[,{angle}]
6164c08aed51c5899665ade97263692328eea4af106cristy%       Generates a 1 dimensional or linear gaussian blur, at the angle given
617c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       (current restricted to orthogonal angles).  If a 'radius' is given the
618c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       kernel is clipped to a width of 2*radius+1.  Kernel can be rotated
619c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       by a 90 degree angle.
620c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
621c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       If 'sigma' is zero, you get a single pixel on a field of zeros.
622c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
623c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Note that two convolutions with two "Blur" kernels perpendicular to
624f0a92fd8deb68d411304359906b12679b675691fglennrp%       each other, is equivalent to a far larger "Gaussian" kernel with the
625c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       same sigma value, However it is much faster to apply. This is how the
626c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       "-blur" operator actually works.
627c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
6283c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Comet:{width},{sigma},{angle}
6293c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Blur in one direction only, much like how a bright object leaves
630602ab9b30b644a78a4057da93d838a77391ec0acanthony%       a comet like trail.  The Kernel is actually half a gaussian curve,
6313c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Adding two such blurs in opposite directions produces a Blur Kernel.
6323c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Angle can be rotated in multiples of 90 degrees.
633602ab9b30b644a78a4057da93d838a77391ec0acanthony%
6343c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%       Note that the first argument is the width of the kernel and not the
635602ab9b30b644a78a4057da93d838a77391ec0acanthony%       radius of the kernel.
636602ab9b30b644a78a4057da93d838a77391ec0acanthony%
63740ca0b982379d4ab2716435a46603d56b5b218b1anthony%    Binomial:[{radius}]
63840ca0b982379d4ab2716435a46603d56b5b218b1anthony%       Generate a discrete kernel using a 2 dimentional Pascel's Triangle
639eef684ff80c7d5aca1493f4755426c88b3d3accdanthony%       of values. Used for special forma of image filters.
64040ca0b982379d4ab2716435a46603d56b5b218b1anthony%
641602ab9b30b644a78a4057da93d838a77391ec0acanthony%    # Still to be implemented...
642602ab9b30b644a78a4057da93d838a77391ec0acanthony%    #
6434fd27e21043be809d66c8202e779255e5b660d2danthony%    # Filter2D
6444fd27e21043be809d66c8202e779255e5b660d2danthony%    # Filter1D
6454fd27e21043be809d66c8202e779255e5b660d2danthony%    #    Set kernel values using a resize filter, and given scale (sigma)
646dcabf6d5fddb35c0eedd7a7638c102f16838c4e6glennrp%    #    Cylindrical or Linear.   Is this possible with an image?
6474fd27e21043be809d66c8202e779255e5b660d2danthony%    #
648602ab9b30b644a78a4057da93d838a77391ec0acanthony%
6493c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  Named Constant Convolution Kernels
6503c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
651c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  All these are unscaled, zero-summing kernels by default. As such for
652c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  non-HDRI version of ImageMagick some form of normalization, user scaling,
653c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  and biasing the results is recommended, to prevent the resulting image
654c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  being 'clipped'.
655c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
656c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  The 3x3 kernels (most of these) can be circularly rotated in multiples of
657c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  45 degrees to generate the 8 angled varients of each of the kernels.
6583c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
6593c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Laplacian:{type}
66043c4925e5305a26e48d68f7893e94f55d0831c39anthony%      Discrete Lapacian Kernels, (without normalization)
661c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        Type 0 :  3x3 with center:8 surounded by -1  (8 neighbourhood)
662c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%        Type 1 :  3x3 with center:4 edge:-1 corner:0 (4 neighbourhood)
6639eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 2 :  3x3 with center:4 edge:1 corner:-2
6649eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 3 :  3x3 with center:4 edge:-2 corner:1
6659eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 5 :  5x5 laplacian
6669eb4f74649b23c053b308ce1152dce51239450baanthony%        Type 7 :  7x7 laplacian
667501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        Type 15 : 5x5 LoG (sigma approx 1.4)
668501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%        Type 19 : 9x9 LoG (sigma approx 1.4)
669c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
670c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Sobel:{angle}
67146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      Sobel 'Edge' convolution kernel (3x3)
672c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
673c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -2, 0,-2 |
674c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
675c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
676c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Roberts:{angle}
67746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      Roberts convolution kernel (3x3)
678c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |  0, 0, 0 |
679c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 0 |
680c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |  0, 0, 0 |
681c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
682c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Prewitt:{angle}
683c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%      Prewitt Edge convolution kernel (3x3)
684c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
685c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
686c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 0, 1 |
687c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
6889eb4f74649b23c053b308ce1152dce51239450baanthony%    Compass:{angle}
6899eb4f74649b23c053b308ce1152dce51239450baanthony%      Prewitt's "Compass" convolution kernel (3x3)
690c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 1 |
691c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1,-2, 1 |
692c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -1, 1, 1 |
693c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
6949eb4f74649b23c053b308ce1152dce51239450baanthony%    Kirsch:{angle}
6959eb4f74649b23c053b308ce1152dce51239450baanthony%      Kirsch's "Compass" convolution kernel (3x3)
696c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3,-3, 5 |
697c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3, 0, 5 |
698c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | -3,-3, 5 |
6993c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
700c40ac1e79923a1516075ba1197ae4ed90244af9banthony%    FreiChen:{angle}
7011d5e67090dc7232b35bfcc71b31266c20838defcanthony%      Frei-Chen Edge Detector is based on a kernel that is similar to
7021d5e67090dc7232b35bfcc71b31266c20838defcanthony%      the Sobel Kernel, but is designed to be isotropic. That is it takes
7031d5e67090dc7232b35bfcc71b31266c20838defcanthony%      into account the distance of the diagonal in the kernel.
704c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
705c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
706c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2), 0, -sqrt(2) |
707c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
708c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
709c40ac1e79923a1516075ba1197ae4ed90244af9banthony%    FreiChen:{type},{angle}
710c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
711c40ac1e79923a1516075ba1197ae4ed90244af9banthony%      Frei-Chen Pre-weighted kernels...
712c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
713c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 0:  default un-nomalized version shown above.
714c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
715c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 1: Orthogonal Kernel (same as type 11 below)
716c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
717c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
718c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     0,   -1     |
719c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
720c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 2: Diagonal form of Kernel...
721c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   1,     sqrt(2),    0     |
722c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          | sqrt(2),   0,     -sqrt(2) | / 2*sqrt(2)
723c40ac1e79923a1516075ba1197ae4ed90244af9banthony%          |   0,    -sqrt(2)    -1     |
724c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
7251d5e67090dc7232b35bfcc71b31266c20838defcanthony%      However this kernel is als at the heart of the FreiChen Edge Detection
7261d5e67090dc7232b35bfcc71b31266c20838defcanthony%      Process which uses a set of 9 specially weighted kernel.  These 9
7271d5e67090dc7232b35bfcc71b31266c20838defcanthony%      kernels not be normalized, but directly applied to the image. The
7281d5e67090dc7232b35bfcc71b31266c20838defcanthony%      results is then added together, to produce the intensity of an edge in
7291d5e67090dc7232b35bfcc71b31266c20838defcanthony%      a specific direction.  The square root of the pixel value can then be
7301d5e67090dc7232b35bfcc71b31266c20838defcanthony%      taken as the cosine of the edge, and at least 2 such runs at 90 degrees
7311d5e67090dc7232b35bfcc71b31266c20838defcanthony%      from each other, both the direction and the strength of the edge can be
7321d5e67090dc7232b35bfcc71b31266c20838defcanthony%      determined.
7331d5e67090dc7232b35bfcc71b31266c20838defcanthony%
734c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 10: All 9 of the following pre-weighted kernels...
735c40ac1e79923a1516075ba1197ae4ed90244af9banthony%
736c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 11: |   1,     0,   -1     |
737c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | sqrt(2), 0, -sqrt(2) | / 2*sqrt(2)
738c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |   1,     0,   -1     |
739e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
740c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 12: | 1, sqrt(2), 1 |
741c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 0,   0,     0 | / 2*sqrt(2)
742c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, sqrt(2), 1 |
743e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
744c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 13: | sqrt(2), -1,    0     |
745c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |  -1,      0,    1     | / 2*sqrt(2)
746c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |   0,      1, -sqrt(2) |
747e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
748c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 14: |    0,     1, -sqrt(2) |
749c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |   -1,     0,     1    | / 2*sqrt(2)
750c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | sqrt(2), -1,     0    |
751e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
752c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 15: | 0, -1, 0 |
753c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1,  0, 1 | / 2
754c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 0, -1, 0 |
755e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
756c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 16: |  1, 0, -1 |
757c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |  0, 0,  0 | / 2
758c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -1, 0,  1 |
759e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
760c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 17: |  1, -2,  1 |
761c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -2,  4, -2 | / 6
762c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -1, -2,  1 |
763501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%
764c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 18: | -2, 1, -2 |
765c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 |  1, 4,  1 | / 6
766c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | -2, 1, -2 |
767e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
768c40ac1e79923a1516075ba1197ae4ed90244af9banthony%        Type 19: | 1, 1, 1 |
769c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, 1, 1 | / 3
770c40ac1e79923a1516075ba1197ae4ed90244af9banthony%                 | 1, 1, 1 |
771e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
772e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%      The first 4 are for edge detection, the next 4 are for line detection
773e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%      and the last is to add a average component to the results.
774e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
775c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      Using a special type of '-1' will return all 9 pre-weighted kernels
776c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      as a multi-kernel list, so that you can use them directly (without
777c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      normalization) with the special "-set option:morphology:compose Plus"
778c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%      setting to apply the full FreiChen Edge Detection Technique.
779c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony%
7801dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      If 'type' is large it will be taken to be an actual rotation angle for
7811dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      the default FreiChen (type 0) kernel.  As such  FreiChen:45  will look
7821dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%      like a  Sobel:45  but with 'sqrt(2)' instead of '2' values.
7831dd091ae3bc17edc26c16cc47f436a24bd48412aanthony%
784501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      WARNING: The above was layed out as per
785501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%          http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf
786501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      But rotated 90 degrees so direction is from left rather than the top.
787501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      I have yet to find any secondary confirmation of the above. The only
788501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      other source found was actual source code at
789501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%          http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf
790501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      Neigher paper defineds the kernels in a way that looks locical or
791501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%      correct when taken as a whole.
792e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony%
793602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Boolean Kernels
794602ab9b30b644a78a4057da93d838a77391ec0acanthony%
7953c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Diamond:[{radius}[,{scale}]]
7961b2bc0a7da432e6e1cc0480280402df213faa940anthony%       Generate a diamond shaped kernel with given radius to the points.
797602ab9b30b644a78a4057da93d838a77391ec0acanthony%       Kernel size will again be radius*2+1 square and defaults to radius 1,
798602ab9b30b644a78a4057da93d838a77391ec0acanthony%       generating a 3x3 kernel that is slightly larger than a square.
799602ab9b30b644a78a4057da93d838a77391ec0acanthony%
8003c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Square:[{radius}[,{scale}]]
801602ab9b30b644a78a4057da93d838a77391ec0acanthony%       Generate a square shaped kernel of size radius*2+1, and defaulting
802602ab9b30b644a78a4057da93d838a77391ec0acanthony%       to a 3x3 (radius 1).
803602ab9b30b644a78a4057da93d838a77391ec0acanthony%
8041ef941fea2534a0d20ba7d71307d35040247decbanthony%    Octagon:[{radius}[,{scale}]]
8051ef941fea2534a0d20ba7d71307d35040247decbanthony%       Generate octagonal shaped kernel of given radius and constant scale.
8060bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%       Default radius is 3 producing a 7x7 kernel. A radius of 1 will result
8071ef941fea2534a0d20ba7d71307d35040247decbanthony%       in "Diamond" kernel.
8081ef941fea2534a0d20ba7d71307d35040247decbanthony%
8093c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Disk:[{radius}[,{scale}]]
8101ef941fea2534a0d20ba7d71307d35040247decbanthony%       Generate a binary disk, thresholded at the radius given, the radius
8111ef941fea2534a0d20ba7d71307d35040247decbanthony%       may be a float-point value. Final Kernel size is floor(radius)*2+1
8120bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%       square. A radius of 5.3 is the default.
8131ef941fea2534a0d20ba7d71307d35040247decbanthony%
8141ef941fea2534a0d20ba7d71307d35040247decbanthony%       NOTE: That a low radii Disk kernels produce the same results as
8151ef941fea2534a0d20ba7d71307d35040247decbanthony%       many of the previously defined kernels, but differ greatly at larger
8161ef941fea2534a0d20ba7d71307d35040247decbanthony%       radii.  Here is a table of equivalences...
8171ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:1"    => "Diamond", "Octagon:1", or "Cross:1"
8181ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:1.5"  => "Square"
8191ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2"    => "Diamond:2"
8201ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2.5"  => "Octagon"
8211ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:2.9"  => "Square:2"
8220bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony%          "Disk:3.5"  => "Octagon:3"
8231ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:4.5"  => "Octagon:4"
8241ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:5.4"  => "Octagon:5"
8251ef941fea2534a0d20ba7d71307d35040247decbanthony%          "Disk:6.4"  => "Octagon:6"
8261ef941fea2534a0d20ba7d71307d35040247decbanthony%       All other Disk shapes are unique to this kernel, but because a "Disk"
8271ef941fea2534a0d20ba7d71307d35040247decbanthony%       is more circular when using a larger radius, using a larger radius is
8281ef941fea2534a0d20ba7d71307d35040247decbanthony%       preferred over iterating the morphological operation.
829602ab9b30b644a78a4057da93d838a77391ec0acanthony%
830a9892d898acb81e1ec73106d892855fdc5a69427anthony%    Rectangle:{geometry}
831a9892d898acb81e1ec73106d892855fdc5a69427anthony%       Simply generate a rectangle of 1's with the size given. You can also
832a9892d898acb81e1ec73106d892855fdc5a69427anthony%       specify the location of the 'control point', otherwise the closest
833a9892d898acb81e1ec73106d892855fdc5a69427anthony%       pixel to the center of the rectangle is selected.
834a9892d898acb81e1ec73106d892855fdc5a69427anthony%
835a9892d898acb81e1ec73106d892855fdc5a69427anthony%       Properly centered and odd sized rectangles work the best.
836a9892d898acb81e1ec73106d892855fdc5a69427anthony%
837c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%  Symbol Dilation Kernels
838c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
839c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    These kernel is not a good general morphological kernel, but is used
840c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    more for highlighting and marking any single pixels in an image using,
841c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    a "Dilate" method as appropriate.
842c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
843c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    For the same reasons iterating these kernels does not produce the
844c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    same result as using a larger radius for the symbol.
845c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%
8463c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    Plus:[{radius}[,{scale}]]
8473dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Cross:[{radius}[,{scale}]]
848c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Generate a kernel in the shape of a 'plus' or a 'cross' with
849c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       a each arm the length of the given radius (default 2).
8503dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%
851f0a92fd8deb68d411304359906b12679b675691fglennrp%       NOTE: "plus:1" is equivalent to a "Diamond" kernel.
852602ab9b30b644a78a4057da93d838a77391ec0acanthony%
853c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Ring:{radius1},{radius2}[,{scale}]
854c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       A ring of the values given that falls between the two radii.
855c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Defaults to a ring of approximataly 3 radius in a 7x7 kernel.
856c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       This is the 'edge' pixels of the default "Disk" kernel,
857c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       More specifically, "Ring" -> "Ring:2.5,3.5,1.0"
8583dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%
8593dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%  Hit and Miss Kernels
860602ab9b30b644a78a4057da93d838a77391ec0acanthony%
8613dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Peak:radius1,radius2
862c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       Find any peak larger than the pixels the fall between the two radii.
863c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%       The default ring of pixels is as per "Ring".
86443c4925e5305a26e48d68f7893e94f55d0831c39anthony%    Edges
865694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find flat orthogonal edges of a binary shape
8663dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    Corners
867694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find 90 degree corners of a binary shape
868529482f4b494010a13338a74446c510712f670b3anthony%    Diagonals:type
869529482f4b494010a13338a74446c510712f670b3anthony%       A special kernel to thin the 'outside' of diagonals
870694934fa79dd310f727588b1d0a7481fa6170f1danthony%    LineEnds:type
8713dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%       Find end points of lines (for pruning a skeletion)
872694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Two types of lines ends (default to both) can be searched for
873694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 0: All line ends
874694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: single kernel for 4-conneected line ends
875694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: single kernel for simple line ends
8763dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    LineJunctions
87743c4925e5305a26e48d68f7893e94f55d0831c39anthony%       Find three line junctions (within a skeletion)
878694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 0: all line junctions
879694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Y Junction kernel
880694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: Diagonal T Junction kernel
881694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 3: Orthogonal T Junction kernel
882694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 4: Diagonal X Junction kernel
883694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 5: Orthogonal + Junction kernel
884694934fa79dd310f727588b1d0a7481fa6170f1danthony%    Ridges:type
885694934fa79dd310f727588b1d0a7481fa6170f1danthony%       Find single pixel ridges or thin lines
886694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Fine single pixel thick lines and ridges
887694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: Find two pixel thick lines and ridges
8883dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony%    ConvexHull
889a9892d898acb81e1ec73106d892855fdc5a69427anthony%       Octagonal Thickening Kernel, to generate convex hulls of 45 degrees
890c40ac1e79923a1516075ba1197ae4ed90244af9banthony%    Skeleton:type
891c40ac1e79923a1516075ba1197ae4ed90244af9banthony%       Traditional skeleton generating kernels.
892694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 1: Tradional Skeleton kernel (4 connected skeleton)
893694934fa79dd310f727588b1d0a7481fa6170f1danthony%         Type 2: HIPR2 Skeleton kernel (8 connected skeleton)
894e816a586a13717bab2d6839ced6e5c3828a37f19anthony%         Type 3: Thinning skeleton based on a ressearch paper by
8952b2290b46c246ce1f14cb78f1695394e4c4a3ddfanthony%                 Dan S. Bloomberg (Default Type)
896e816a586a13717bab2d6839ced6e5c3828a37f19anthony%    ThinSE:type
897e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       A huge variety of Thinning Kernels designed to preserve conectivity.
898e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       many other kernel sets use these kernels as source definitions.
899e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       Type numbers are 41-49, 81-89, 481, and 482 which are based on
900e816a586a13717bab2d6839ced6e5c3828a37f19anthony%       the super and sub notations used in the source research paper.
901602ab9b30b644a78a4057da93d838a77391ec0acanthony%
902602ab9b30b644a78a4057da93d838a77391ec0acanthony%  Distance Measuring Kernels
903602ab9b30b644a78a4057da93d838a77391ec0acanthony%
904c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Different types of distance measuring methods, which are used with the
905c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    a 'Distance' morphology method for generating a gradient based on
906c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    distance from an edge of a binary shape, though there is a technique
907c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    for handling a anti-aliased shape.
908602ab9b30b644a78a4057da93d838a77391ec0acanthony%
909c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    See the 'Distance' Morphological Method, for information of how it is
910c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    applied.
911602ab9b30b644a78a4057da93d838a77391ec0acanthony%
912c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Chebyshev:[{radius}][x{scale}[%!]]
9131ef941fea2534a0d20ba7d71307d35040247decbanthony%       Chebyshev Distance (also known as Tchebychev or Chessboard distance)
9141ef941fea2534a0d20ba7d71307d35040247decbanthony%       is a value of one to any neighbour, orthogonal or diagonal. One why
9151ef941fea2534a0d20ba7d71307d35040247decbanthony%       of thinking of it is the number of squares a 'King' or 'Queen' in
9161ef941fea2534a0d20ba7d71307d35040247decbanthony%       chess needs to traverse reach any other position on a chess board.
9171ef941fea2534a0d20ba7d71307d35040247decbanthony%       It results in a 'square' like distance function, but one where
9181ef941fea2534a0d20ba7d71307d35040247decbanthony%       diagonals are given a value that is closer than expected.
919c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%
920bee715c4c0fd9efe6e21d8627ae8664434df7750anthony%    Manhattan:[{radius}][x{scale}[%!]]
9211ef941fea2534a0d20ba7d71307d35040247decbanthony%       Manhattan Distance (also known as Rectilinear, City Block, or the Taxi
9221ef941fea2534a0d20ba7d71307d35040247decbanthony%       Cab distance metric), it is the distance needed when you can only
9231ef941fea2534a0d20ba7d71307d35040247decbanthony%       travel in horizontal or vertical directions only.  It is the
9241ef941fea2534a0d20ba7d71307d35040247decbanthony%       distance a 'Rook' in chess would have to travel, and results in a
9251ef941fea2534a0d20ba7d71307d35040247decbanthony%       diamond like distances, where diagonals are further than expected.
9261ef941fea2534a0d20ba7d71307d35040247decbanthony%
9271ef941fea2534a0d20ba7d71307d35040247decbanthony%    Octagonal:[{radius}][x{scale}[%!]]
9281ef941fea2534a0d20ba7d71307d35040247decbanthony%       An interleving of Manhatten and Chebyshev metrics producing an
9291ef941fea2534a0d20ba7d71307d35040247decbanthony%       increasing octagonally shaped distance.  Distances matches those of
9301ef941fea2534a0d20ba7d71307d35040247decbanthony%       the "Octagon" shaped kernel of the same radius.  The minimum radius
9311ef941fea2534a0d20ba7d71307d35040247decbanthony%       and default is 2, producing a 5x5 kernel.
932c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%
933c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony%    Euclidean:[{radius}][x{scale}[%!]]
9341ef941fea2534a0d20ba7d71307d35040247decbanthony%       Euclidean distance is the 'direct' or 'as the crow flys' distance.
935c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       However by default the kernel size only has a radius of 1, which
936c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       limits the distance to 'Knight' like moves, with only orthogonal and
937c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%       diagonal measurements being correct.  As such for the default kernel
9381ef941fea2534a0d20ba7d71307d35040247decbanthony%       you will get octagonal like distance function.
9391ef941fea2534a0d20ba7d71307d35040247decbanthony%
9401ef941fea2534a0d20ba7d71307d35040247decbanthony%       However using a larger radius such as "Euclidean:4" you will get a
9411ef941fea2534a0d20ba7d71307d35040247decbanthony%       much smoother distance gradient from the edge of the shape. Especially
9421ef941fea2534a0d20ba7d71307d35040247decbanthony%       if the image is pre-processed to include any anti-aliasing pixels.
9431ef941fea2534a0d20ba7d71307d35040247decbanthony%       Of course a larger kernel is slower to use, and not always needed.
9441ef941fea2534a0d20ba7d71307d35040247decbanthony%
9451ef941fea2534a0d20ba7d71307d35040247decbanthony%    The first three Distance Measuring Kernels will only generate distances
9461ef941fea2534a0d20ba7d71307d35040247decbanthony%    of exact multiples of {scale} in binary images. As such you can use a
9471ef941fea2534a0d20ba7d71307d35040247decbanthony%    scale of 1 without loosing any information.  However you also need some
9481ef941fea2534a0d20ba7d71307d35040247decbanthony%    scaling when handling non-binary anti-aliased shapes.
9491ef941fea2534a0d20ba7d71307d35040247decbanthony%
9501ef941fea2534a0d20ba7d71307d35040247decbanthony%    The "Euclidean" Distance Kernel however does generate a non-integer
9511ef941fea2534a0d20ba7d71307d35040247decbanthony%    fractional results, and as such scaling is vital even for binary shapes.
952c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%
953602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
954602ab9b30b644a78a4057da93d838a77391ec0acanthony
9552be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristyMagickExport KernelInfo *AcquireKernelBuiltIn(const KernelInfoType type,
956602ab9b30b644a78a4057da93d838a77391ec0acanthony   const GeometryInfo *args)
957602ab9b30b644a78a4057da93d838a77391ec0acanthony{
9582be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  KernelInfo
959602ab9b30b644a78a4057da93d838a77391ec0acanthony    *kernel;
960602ab9b30b644a78a4057da93d838a77391ec0acanthony
961bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
962602ab9b30b644a78a4057da93d838a77391ec0acanthony    i;
963602ab9b30b644a78a4057da93d838a77391ec0acanthony
964bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
965602ab9b30b644a78a4057da93d838a77391ec0acanthony    u,
966602ab9b30b644a78a4057da93d838a77391ec0acanthony    v;
967602ab9b30b644a78a4057da93d838a77391ec0acanthony
968602ab9b30b644a78a4057da93d838a77391ec0acanthony  double
969602ab9b30b644a78a4057da93d838a77391ec0acanthony    nan = sqrt((double)-1.0);  /* Special Value : Not A Number */
970602ab9b30b644a78a4057da93d838a77391ec0acanthony
971c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  /* Generate a new empty kernel if needed */
972e96405a0f45f803fb9c26f75e7bdee252437febbcristy  kernel=(KernelInfo *) NULL;
973c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  switch(type) {
9741dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case UndefinedKernel:    /* These should not call this function */
9759eb4f74649b23c053b308ce1152dce51239450baanthony    case UserDefinedKernel:
976529482f4b494010a13338a74446c510712f670b3anthony      assert("Should not call this function" != (char *)NULL);
9779eb4f74649b23c053b308ce1152dce51239450baanthony      break;
978529482f4b494010a13338a74446c510712f670b3anthony    case LaplacianKernel:   /* Named Descrete Convolution Kernels */
979529482f4b494010a13338a74446c510712f670b3anthony    case SobelKernel:       /* these are defined using other kernels */
9809eb4f74649b23c053b308ce1152dce51239450baanthony    case RobertsKernel:
9819eb4f74649b23c053b308ce1152dce51239450baanthony    case PrewittKernel:
9829eb4f74649b23c053b308ce1152dce51239450baanthony    case CompassKernel:
9839eb4f74649b23c053b308ce1152dce51239450baanthony    case KirschKernel:
9841dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case FreiChenKernel:
985694934fa79dd310f727588b1d0a7481fa6170f1danthony    case EdgesKernel:       /* Hit and Miss kernels */
986694934fa79dd310f727588b1d0a7481fa6170f1danthony    case CornersKernel:
987529482f4b494010a13338a74446c510712f670b3anthony    case DiagonalsKernel:
9889eb4f74649b23c053b308ce1152dce51239450baanthony    case LineEndsKernel:
9899eb4f74649b23c053b308ce1152dce51239450baanthony    case LineJunctionsKernel:
9901dd091ae3bc17edc26c16cc47f436a24bd48412aanthony    case RidgesKernel:
9919eb4f74649b23c053b308ce1152dce51239450baanthony    case ConvexHullKernel:
9929eb4f74649b23c053b308ce1152dce51239450baanthony    case SkeletonKernel:
9939a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony    case ThinSEKernel:
994c40ac1e79923a1516075ba1197ae4ed90244af9banthony      break;               /* A pre-generated kernel is not needed */
995c40ac1e79923a1516075ba1197ae4ed90244af9banthony#if 0
996c40ac1e79923a1516075ba1197ae4ed90244af9banthony    /* set to 1 to do a compile-time check that we haven't missed anything */
997529482f4b494010a13338a74446c510712f670b3anthony    case UnityKernel:
998c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case GaussianKernel:
999501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case DoGKernel:
1000501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case LoGKernel:
1001c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case BlurKernel:
1002c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CometKernel:
100340ca0b982379d4ab2716435a46603d56b5b218b1anthony    case BinomialKernel:
1004c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiamondKernel:
1005c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SquareKernel:
1006c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RectangleKernel:
10071ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctagonKernel:
1008c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiskKernel:
1009c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PlusKernel:
1010c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CrossKernel:
1011c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RingKernel:
1012c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PeaksKernel:
1013c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case ChebyshevKernel:
1014bee715c4c0fd9efe6e21d8627ae8664434df7750anthony    case ManhattanKernel:
10151ef941fea2534a0d20ba7d71307d35040247decbanthony    case OctangonalKernel:
1016c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case EuclideanKernel:
10171dd091ae3bc17edc26c16cc47f436a24bd48412aanthony#else
10189eb4f74649b23c053b308ce1152dce51239450baanthony    default:
10191dd091ae3bc17edc26c16cc47f436a24bd48412aanthony#endif
10209eb4f74649b23c053b308ce1152dce51239450baanthony      /* Generate the base Kernel Structure */
1021c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
1022c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      if (kernel == (KernelInfo *) NULL)
1023c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        return(kernel);
1024c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      (void) ResetMagickMemory(kernel,0,sizeof(*kernel));
102543c4925e5305a26e48d68f7893e94f55d0831c39anthony      kernel->minimum = kernel->maximum = kernel->angle = 0.0;
1026c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->negative_range = kernel->positive_range = 0.0;
1027c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->type = type;
1028c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->next = (KernelInfo *) NULL;
1029c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      kernel->signature = MagickSignature;
1030c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      break;
1031c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony  }
1032602ab9b30b644a78a4057da93d838a77391ec0acanthony
1033602ab9b30b644a78a4057da93d838a77391ec0acanthony  switch(type) {
1034529482f4b494010a13338a74446c510712f670b3anthony    /*
1035529482f4b494010a13338a74446c510712f670b3anthony      Convolution Kernels
1036529482f4b494010a13338a74446c510712f670b3anthony    */
1037529482f4b494010a13338a74446c510712f670b3anthony    case UnityKernel:
1038529482f4b494010a13338a74446c510712f670b3anthony      {
1039529482f4b494010a13338a74446c510712f670b3anthony        kernel->height = kernel->width = (size_t) 1;
1040529482f4b494010a13338a74446c510712f670b3anthony        kernel->x = kernel->y = (ssize_t) 0;
1041e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1042e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(1,sizeof(*kernel->values)));
1043d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
1044529482f4b494010a13338a74446c510712f670b3anthony          return(DestroyKernelInfo(kernel));
1045529482f4b494010a13338a74446c510712f670b3anthony        kernel->maximum = kernel->values[0] = args->rho;
1046529482f4b494010a13338a74446c510712f670b3anthony        break;
1047529482f4b494010a13338a74446c510712f670b3anthony      }
1048529482f4b494010a13338a74446c510712f670b3anthony      break;
1049602ab9b30b644a78a4057da93d838a77391ec0acanthony    case GaussianKernel:
1050501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case DoGKernel:
1051501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case LoGKernel:
1052602ab9b30b644a78a4057da93d838a77391ec0acanthony      { double
1053c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma = fabs(args->sigma),
1054c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma2 = fabs(args->xi),
10559eb4f74649b23c053b308ce1152dce51239450baanthony          A, B, R;
1056c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1057c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( args->rho >= 1.0 )
1058bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = (size_t)args->rho*2+1;
1059501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        else if ( (type != DoGKernel) || (sigma >= sigma2) )
1060c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = GetOptimalKernelWidth2D(args->rho,sigma);
1061c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else
1062c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = GetOptimalKernelWidth2D(args->rho,sigma2);
1063c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->height = kernel->width;
1064bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1065e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1066e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1067e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1068d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
106983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1070602ab9b30b644a78a4057da93d838a77391ec0acanthony
107146a369d839971ab627bdb31a93d8bd63e81b65a3anthony        /* WARNING: The following generates a 'sampled gaussian' kernel.
10729eb4f74649b23c053b308ce1152dce51239450baanthony         * What we really want is a 'discrete gaussian' kernel.
107346a369d839971ab627bdb31a93d8bd63e81b65a3anthony         *
1074529482f4b494010a13338a74446c510712f670b3anthony         * How to do this is I don't know, but appears to be basied on the
1075529482f4b494010a13338a74446c510712f670b3anthony         * Error Function 'erf()' (intergral of a gaussian)
10769eb4f74649b23c053b308ce1152dce51239450baanthony         */
10779eb4f74649b23c053b308ce1152dce51239450baanthony
1078501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if ( type == GaussianKernel || type == DoGKernel )
1079501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          { /* Calculate a Gaussian,  OR positive half of a DoG */
10809eb4f74649b23c053b308ce1152dce51239450baanthony            if ( sigma > MagickEpsilon )
10819eb4f74649b23c053b308ce1152dce51239450baanthony              { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
108255a91cddcdea3aa002893186a773e1704884a9dfcristy                B = (double) (1.0/(Magick2PI*sigma*sigma));
1083bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1084bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
10859eb4f74649b23c053b308ce1152dce51239450baanthony                      kernel->values[i] = exp(-((double)(u*u+v*v))*A)*B;
10869eb4f74649b23c053b308ce1152dce51239450baanthony              }
10879eb4f74649b23c053b308ce1152dce51239450baanthony            else /* limiting case - a unity (normalized Dirac) kernel */
10889eb4f74649b23c053b308ce1152dce51239450baanthony              { (void) ResetMagickMemory(kernel->values,0, (size_t)
108975920b23a8a3883792cbb69d569c33cc789cf1b5cristy                  kernel->width*kernel->height*sizeof(*kernel->values));
10909eb4f74649b23c053b308ce1152dce51239450baanthony                kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
10919eb4f74649b23c053b308ce1152dce51239450baanthony              }
1092c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
10939eb4f74649b23c053b308ce1152dce51239450baanthony
1094501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if ( type == DoGKernel )
1095c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { /* Subtract a Negative Gaussian for "Difference of Gaussian" */
1096c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            if ( sigma2 > MagickEpsilon )
1097c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              { sigma = sigma2;                /* simplify loop expressions */
10989eb4f74649b23c053b308ce1152dce51239450baanthony                A = 1.0/(2.0*sigma*sigma);
109955a91cddcdea3aa002893186a773e1704884a9dfcristy                B = (double) (1.0/(Magick2PI*sigma*sigma));
1100bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1101bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
11029eb4f74649b23c053b308ce1152dce51239450baanthony                    kernel->values[i] -= exp(-((double)(u*u+v*v))*A)*B;
1103c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              }
11049eb4f74649b23c053b308ce1152dce51239450baanthony            else /* limiting case - a unity (normalized Dirac) kernel */
1105c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->values[kernel->x+kernel->y*kernel->width] -= 1.0;
1106c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
11079eb4f74649b23c053b308ce1152dce51239450baanthony
1108501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        if ( type == LoGKernel )
11099eb4f74649b23c053b308ce1152dce51239450baanthony          { /* Calculate a Laplacian of a Gaussian - Or Mexician Hat */
11109eb4f74649b23c053b308ce1152dce51239450baanthony            if ( sigma > MagickEpsilon )
11119eb4f74649b23c053b308ce1152dce51239450baanthony              { A = 1.0/(2.0*sigma*sigma);  /* simplify loop expressions */
111255a91cddcdea3aa002893186a773e1704884a9dfcristy                B = (double) (1.0/(MagickPI*sigma*sigma*sigma*sigma));
1113bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1114bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy                  for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
11159eb4f74649b23c053b308ce1152dce51239450baanthony                    { R = ((double)(u*u+v*v))*A;
11169eb4f74649b23c053b308ce1152dce51239450baanthony                      kernel->values[i] = (1-R)*exp(-R)*B;
11179eb4f74649b23c053b308ce1152dce51239450baanthony                    }
11189eb4f74649b23c053b308ce1152dce51239450baanthony              }
11199eb4f74649b23c053b308ce1152dce51239450baanthony            else /* special case - generate a unity kernel */
11209eb4f74649b23c053b308ce1152dce51239450baanthony              { (void) ResetMagickMemory(kernel->values,0, (size_t)
112175920b23a8a3883792cbb69d569c33cc789cf1b5cristy                  kernel->width*kernel->height*sizeof(*kernel->values));
11229eb4f74649b23c053b308ce1152dce51239450baanthony                kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
11239eb4f74649b23c053b308ce1152dce51239450baanthony              }
11249eb4f74649b23c053b308ce1152dce51239450baanthony          }
11259eb4f74649b23c053b308ce1152dce51239450baanthony
11269eb4f74649b23c053b308ce1152dce51239450baanthony        /* Note the above kernels may have been 'clipped' by a user defined
1127c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** radius, producing a smaller (darker) kernel.  Also for very small
1128c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** sigma's (> 0.1) the central value becomes larger than one, and thus
1129c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** producing a very bright kernel.
1130c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
1131c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** Normalization will still be needed.
1132c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        */
1133602ab9b30b644a78a4057da93d838a77391ec0acanthony
11343dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        /* Normalize the 2D Gaussian Kernel
11353dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        **
1136c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** NB: a CorrelateNormalize performs a normal Normalize if
1137c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there are no negative values.
11383dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        */
113946a369d839971ab627bdb31a93d8bd63e81b65a3anthony        CalcKernelMetaData(kernel);  /* the other kernel meta-data */
1140c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1141602ab9b30b644a78a4057da93d838a77391ec0acanthony
1142602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
1143602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
1144602ab9b30b644a78a4057da93d838a77391ec0acanthony    case BlurKernel:
1145602ab9b30b644a78a4057da93d838a77391ec0acanthony      { double
1146c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          sigma = fabs(args->sigma),
1147501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          alpha, beta;
1148602ab9b30b644a78a4057da93d838a77391ec0acanthony
1149c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( args->rho >= 1.0 )
1150bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = (size_t)args->rho*2+1;
1151c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else
1152501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          kernel->width = GetOptimalKernelWidth1D(args->rho,sigma);
1153602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->height = 1;
1154bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = (ssize_t) (kernel->width-1)/2;
1155c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->y = 0;
1156c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->negative_range = kernel->positive_range = 0.0;
1157e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1158e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1159e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1160d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
116183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1162602ab9b30b644a78a4057da93d838a77391ec0acanthony
1163602ab9b30b644a78a4057da93d838a77391ec0acanthony#if 1
1164602ab9b30b644a78a4057da93d838a77391ec0acanthony#define KernelRank 3
1165602ab9b30b644a78a4057da93d838a77391ec0acanthony        /* Formula derived from GetBlurKernel() in "effect.c" (plus bug fix).
1166602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** It generates a gaussian 3 times the width, and compresses it into
1167602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** the expected range.  This produces a closer normalization of the
1168602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** resulting kernel, especially for very low sigma values.
1169602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** As such while wierd it is prefered.
1170602ab9b30b644a78a4057da93d838a77391ec0acanthony        **
1171602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** I am told this method originally came from Photoshop.
11729eb4f74649b23c053b308ce1152dce51239450baanthony        **
11739eb4f74649b23c053b308ce1152dce51239450baanthony        ** A properly normalized curve is generated (apart from edge clipping)
11749eb4f74649b23c053b308ce1152dce51239450baanthony        ** even though we later normalize the result (for edge clipping)
11759eb4f74649b23c053b308ce1152dce51239450baanthony        ** to allow the correct generation of a "Difference of Blurs".
1176602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
1177c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1178c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* initialize */
1179bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        v = (ssize_t) (kernel->width*KernelRank-1)/2; /* start/end points to fit range */
11809eb4f74649b23c053b308ce1152dce51239450baanthony        (void) ResetMagickMemory(kernel->values,0, (size_t)
118175920b23a8a3883792cbb69d569c33cc789cf1b5cristy          kernel->width*kernel->height*sizeof(*kernel->values));
1182c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Calculate a Positive 1D Gaussian */
1183c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( sigma > MagickEpsilon )
1184c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { sigma *= KernelRank;               /* simplify loop expressions */
1185501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            alpha = 1.0/(2.0*sigma*sigma);
118655a91cddcdea3aa002893186a773e1704884a9dfcristy            beta= (double) (1.0/(MagickSQ2PI*sigma ));
1187c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            for ( u=-v; u <= v; u++) {
1188501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony              kernel->values[(u+v)/KernelRank] +=
1189501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony                              exp(-((double)(u*u))*alpha)*beta;
1190c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            }
1191c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
1192c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else /* special case - generate a unity kernel */
1193c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1194602ab9b30b644a78a4057da93d838a77391ec0acanthony#else
119553f576d128f0bb744a82e7f9c0d8f05b2923972canthony        /* Direct calculation without curve averaging
119653f576d128f0bb744a82e7f9c0d8f05b2923972canthony           This is equivelent to a KernelRank of 1 */
1197c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1198c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Calculate a Positive Gaussian */
1199c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if ( sigma > MagickEpsilon )
1200501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          { alpha = 1.0/(2.0*sigma*sigma);    /* simplify loop expressions */
1201501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            beta = 1.0/(MagickSQ2PI*sigma);
1202bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for ( i=0, u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
1203501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony              kernel->values[i] = exp(-((double)(u*u))*alpha)*beta;
1204c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
1205c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else /* special case - generate a unity kernel */
1206c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          { (void) ResetMagickMemory(kernel->values,0, (size_t)
120775920b23a8a3883792cbb69d569c33cc789cf1b5cristy              kernel->width*kernel->height*sizeof(*kernel->values));
1208c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
1209c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          }
1210602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
1211c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* Note the above kernel may have been 'clipped' by a user defined
1212cc6c836da2a53b6023b716e4973090a6714dc3b0anthony        ** radius, producing a smaller (darker) kernel.  Also for very small
121353f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** sigma's (> 0.1) the central value becomes larger than one, as a
121453f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** result of not generating a actual 'discrete' kernel, and thus
121553f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** producing a very bright 'impulse'.
1216c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
121753f576d128f0bb744a82e7f9c0d8f05b2923972canthony        ** Becuase of these two factors Normalization is required!
1218602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
1219cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
1220602ab9b30b644a78a4057da93d838a77391ec0acanthony        /* Normalize the 1D Gaussian Kernel
1221602ab9b30b644a78a4057da93d838a77391ec0acanthony        **
1222c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** NB: a CorrelateNormalize performs a normal Normalize if
1223c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there are no negative values.
1224602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
122546a369d839971ab627bdb31a93d8bd63e81b65a3anthony        CalcKernelMetaData(kernel);  /* the other kernel meta-data */
122646a369d839971ab627bdb31a93d8bd63e81b65a3anthony        ScaleKernelInfo(kernel, 1.0, CorrelateNormalizeValue);
1227cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
1228c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* rotate the 1D kernel by given angle */
1229501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        RotateKernelInfo(kernel, args->xi );
1230602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
1231602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
1232602ab9b30b644a78a4057da93d838a77391ec0acanthony    case CometKernel:
1233602ab9b30b644a78a4057da93d838a77391ec0acanthony      { double
12349eb4f74649b23c053b308ce1152dce51239450baanthony          sigma = fabs(args->sigma),
12359eb4f74649b23c053b308ce1152dce51239450baanthony          A;
1236602ab9b30b644a78a4057da93d838a77391ec0acanthony
1237602ab9b30b644a78a4057da93d838a77391ec0acanthony        if ( args->rho < 1.0 )
1238e1cf9465864144e8b8043d522906c1e47bbf6192anthony          kernel->width = (GetOptimalKernelWidth1D(args->rho,sigma)-1)/2+1;
1239602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
1240bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = (size_t)args->rho;
1241c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->x = kernel->y = 0;
1242602ab9b30b644a78a4057da93d838a77391ec0acanthony        kernel->height = 1;
1243c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->negative_range = kernel->positive_range = 0.0;
1244e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1245e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1246e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1247d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
124883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1249602ab9b30b644a78a4057da93d838a77391ec0acanthony
1250c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* A comet blur is half a 1D gaussian curve, so that the object is
1251602ab9b30b644a78a4057da93d838a77391ec0acanthony        ** blurred in one direction only.  This may not be quite the right
12523dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        ** curve to use so may change in the future. The function must be
12533dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        ** normalised after generation, which also resolves any clipping.
1254c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
1255c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** As we are normalizing and not subtracting gaussians,
1256c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        ** there is no need for a divisor in the gaussian formula
1257c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        **
125843c4925e5305a26e48d68f7893e94f55d0831c39anthony        ** It is less comples
1259602ab9b30b644a78a4057da93d838a77391ec0acanthony        */
12609eb4f74649b23c053b308ce1152dce51239450baanthony        if ( sigma > MagickEpsilon )
12619eb4f74649b23c053b308ce1152dce51239450baanthony          {
1262602ab9b30b644a78a4057da93d838a77391ec0acanthony#if 1
1263602ab9b30b644a78a4057da93d838a77391ec0acanthony#define KernelRank 3
1264bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            v = (ssize_t) kernel->width*KernelRank; /* start/end points */
12659eb4f74649b23c053b308ce1152dce51239450baanthony            (void) ResetMagickMemory(kernel->values,0, (size_t)
126675920b23a8a3883792cbb69d569c33cc789cf1b5cristy              kernel->width*sizeof(*kernel->values));
12679eb4f74649b23c053b308ce1152dce51239450baanthony            sigma *= KernelRank;            /* simplify the loop expression */
12689eb4f74649b23c053b308ce1152dce51239450baanthony            A = 1.0/(2.0*sigma*sigma);
12699eb4f74649b23c053b308ce1152dce51239450baanthony            /* B = 1.0/(MagickSQ2PI*sigma); */
12709eb4f74649b23c053b308ce1152dce51239450baanthony            for ( u=0; u < v; u++) {
12719eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->values[u/KernelRank] +=
12729eb4f74649b23c053b308ce1152dce51239450baanthony                  exp(-((double)(u*u))*A);
12739eb4f74649b23c053b308ce1152dce51239450baanthony              /*  exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
12749eb4f74649b23c053b308ce1152dce51239450baanthony            }
1275bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for (i=0; i < (ssize_t) kernel->width; i++)
12769eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->positive_range += kernel->values[i];
1277602ab9b30b644a78a4057da93d838a77391ec0acanthony#else
12789eb4f74649b23c053b308ce1152dce51239450baanthony            A = 1.0/(2.0*sigma*sigma);     /* simplify the loop expression */
12799eb4f74649b23c053b308ce1152dce51239450baanthony            /* B = 1.0/(MagickSQ2PI*sigma); */
1280bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            for ( i=0; i < (ssize_t) kernel->width; i++)
12819eb4f74649b23c053b308ce1152dce51239450baanthony              kernel->positive_range +=
128253f576d128f0bb744a82e7f9c0d8f05b2923972canthony                kernel->values[i] = exp(-((double)(i*i))*A);
12839eb4f74649b23c053b308ce1152dce51239450baanthony                /* exp(-((double)(i*i))/2.0*sigma*sigma)/(MagickSQ2PI*sigma); */
1284602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
12859eb4f74649b23c053b308ce1152dce51239450baanthony          }
12869eb4f74649b23c053b308ce1152dce51239450baanthony        else /* special case - generate a unity kernel */
12879eb4f74649b23c053b308ce1152dce51239450baanthony          { (void) ResetMagickMemory(kernel->values,0, (size_t)
128875920b23a8a3883792cbb69d569c33cc789cf1b5cristy              kernel->width*kernel->height*sizeof(*kernel->values));
12899eb4f74649b23c053b308ce1152dce51239450baanthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
12909eb4f74649b23c053b308ce1152dce51239450baanthony            kernel->positive_range = 1.0;
12919eb4f74649b23c053b308ce1152dce51239450baanthony          }
129246a369d839971ab627bdb31a93d8bd63e81b65a3anthony
129346a369d839971ab627bdb31a93d8bd63e81b65a3anthony        kernel->minimum = 0.0;
1294c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->maximum = kernel->values[0];
129546a369d839971ab627bdb31a93d8bd63e81b65a3anthony        kernel->negative_range = 0.0;
1296602ab9b30b644a78a4057da93d838a77391ec0acanthony
1297999bb2c20aa9d42875bb5adba44951988d4ae354anthony        ScaleKernelInfo(kernel, 1.0, NormalizeValue); /* Normalize */
1298999bb2c20aa9d42875bb5adba44951988d4ae354anthony        RotateKernelInfo(kernel, args->xi); /* Rotate by angle */
1299602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
1300602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
130140ca0b982379d4ab2716435a46603d56b5b218b1anthony    case BinomialKernel:
130240ca0b982379d4ab2716435a46603d56b5b218b1anthony      {
130340ca0b982379d4ab2716435a46603d56b5b218b1anthony        size_t
130440ca0b982379d4ab2716435a46603d56b5b218b1anthony          order_f;
130540ca0b982379d4ab2716435a46603d56b5b218b1anthony
130640ca0b982379d4ab2716435a46603d56b5b218b1anthony        if (args->rho < 1.0)
130740ca0b982379d4ab2716435a46603d56b5b218b1anthony          kernel->width = kernel->height = 3;  /* default radius = 1 */
130840ca0b982379d4ab2716435a46603d56b5b218b1anthony        else
130940ca0b982379d4ab2716435a46603d56b5b218b1anthony          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
131040ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
131140ca0b982379d4ab2716435a46603d56b5b218b1anthony
131240ca0b982379d4ab2716435a46603d56b5b218b1anthony        order_f = fact(kernel->width-1);
131340ca0b982379d4ab2716435a46603d56b5b218b1anthony
1314e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1315e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1316e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1317d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
131840ca0b982379d4ab2716435a46603d56b5b218b1anthony          return(DestroyKernelInfo(kernel));
131940ca0b982379d4ab2716435a46603d56b5b218b1anthony
132040ca0b982379d4ab2716435a46603d56b5b218b1anthony        /* set all kernel values within diamond area to scale given */
132140ca0b982379d4ab2716435a46603d56b5b218b1anthony        for ( i=0, v=0; v < (ssize_t)kernel->height; v++)
132240ca0b982379d4ab2716435a46603d56b5b218b1anthony          { size_t
1323f13c594b1d9509e479fd845c3b8a5bb1351c32eacristy              alpha = order_f / ( fact((size_t) v) * fact(kernel->height-v-1) );
132440ca0b982379d4ab2716435a46603d56b5b218b1anthony            for ( u=0; u < (ssize_t)kernel->width; u++, i++)
132540ca0b982379d4ab2716435a46603d56b5b218b1anthony              kernel->positive_range += kernel->values[i] = (double)
1326f13c594b1d9509e479fd845c3b8a5bb1351c32eacristy                (alpha * order_f / ( fact((size_t) u) * fact(kernel->height-u-1) ));
132740ca0b982379d4ab2716435a46603d56b5b218b1anthony          }
132840ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->minimum = 1.0;
132940ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->maximum = kernel->values[kernel->x+kernel->y*kernel->width];
133040ca0b982379d4ab2716435a46603d56b5b218b1anthony        kernel->negative_range = 0.0;
133140ca0b982379d4ab2716435a46603d56b5b218b1anthony        break;
133240ca0b982379d4ab2716435a46603d56b5b218b1anthony      }
1333c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1334529482f4b494010a13338a74446c510712f670b3anthony    /*
1335529482f4b494010a13338a74446c510712f670b3anthony      Convolution Kernels - Well Known Named Constant Kernels
1336529482f4b494010a13338a74446c510712f670b3anthony    */
13373c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    case LaplacianKernel:
1338e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony      { switch ( (int) args->rho ) {
13393dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony          case 0:
13409eb4f74649b23c053b308ce1152dce51239450baanthony          default: /* laplacian square filter -- default */
1341c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: -1,-1,-1  -1,8,-1  -1,-1,-1");
13423dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony            break;
13439eb4f74649b23c053b308ce1152dce51239450baanthony          case 1:  /* laplacian diamond filter */
1344c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: 0,-1,0  -1,4,-1  0,-1,0");
13453c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13463c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          case 2:
13479eb4f74649b23c053b308ce1152dce51239450baanthony            kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
13489eb4f74649b23c053b308ce1152dce51239450baanthony            break;
13499eb4f74649b23c053b308ce1152dce51239450baanthony          case 3:
1350c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  1,-2,1");
13513c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13529eb4f74649b23c053b308ce1152dce51239450baanthony          case 5:   /* a 5x5 laplacian */
13533c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            kernel=ParseKernelArray(
13549eb4f74649b23c053b308ce1152dce51239450baanthony              "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");
13553c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
13569eb4f74649b23c053b308ce1152dce51239450baanthony          case 7:   /* a 7x7 laplacian */
13573c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            kernel=ParseKernelArray(
1358c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              "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" );
13593c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            break;
1360501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          case 15:  /* a 5x5 LoG (sigma approx 1.4) */
13619eb4f74649b23c053b308ce1152dce51239450baanthony            kernel=ParseKernelArray(
13629eb4f74649b23c053b308ce1152dce51239450baanthony              "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");
13639eb4f74649b23c053b308ce1152dce51239450baanthony            break;
1364501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          case 19:  /* a 9x9 LoG (sigma approx 1.4) */
136543c4925e5305a26e48d68f7893e94f55d0831c39anthony            /* http://www.cscjournals.org/csc/manuscript/Journals/IJIP/volume3/Issue1/IJIP-15.pdf */
136643c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel=ParseKernelArray(
1367bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony              "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");
136843c4925e5305a26e48d68f7893e94f55d0831c39anthony            break;
13693c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
13703c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        if (kernel == (KernelInfo *) NULL)
13713c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          return(kernel);
13723c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        kernel->type = type;
13733c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        break;
13743c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      }
1375c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SobelKernel:
1376cceb6f05c2016ea3854b29e7770ec5ff49034ecfanthony      { /* Simple Sobel Kernel */
1377dcc2a474c98415e565434fe52f630acba36fa0c1anthony        kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1378dcc2a474c98415e565434fe52f630acba36fa0c1anthony        if (kernel == (KernelInfo *) NULL)
1379dcc2a474c98415e565434fe52f630acba36fa0c1anthony          return(kernel);
1380dcc2a474c98415e565434fe52f630acba36fa0c1anthony        kernel->type = type;
1381dcc2a474c98415e565434fe52f630acba36fa0c1anthony        RotateKernelInfo(kernel, args->rho);
1382dcc2a474c98415e565434fe52f630acba36fa0c1anthony        break;
1383dcc2a474c98415e565434fe52f630acba36fa0c1anthony      }
1384c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RobertsKernel:
1385c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1386501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 0,0,0  1,-1,0  0,0,0");
1387c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
1388c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1389c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
139046a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
1391c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1392c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
1393c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case PrewittKernel:
1394c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1395501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 1,0,-1  1,0,-1  1,0,-1");
1396c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
1397c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1398c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
139946a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
1400c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1401c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
1402c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case CompassKernel:
1403c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
1404501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 1,1,-1  1,-2,-1  1,1,-1");
1405c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
1406c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
1407c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->type = type;
140846a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
1409c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1410c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
14119eb4f74649b23c053b308ce1152dce51239450baanthony    case KirschKernel:
14129eb4f74649b23c053b308ce1152dce51239450baanthony      {
1413501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony        kernel=ParseKernelArray("3: 5,-3,-3  5,0,-3  5,-3,-3");
14149eb4f74649b23c053b308ce1152dce51239450baanthony        if (kernel == (KernelInfo *) NULL)
14159eb4f74649b23c053b308ce1152dce51239450baanthony          return(kernel);
14169eb4f74649b23c053b308ce1152dce51239450baanthony        kernel->type = type;
141746a369d839971ab627bdb31a93d8bd63e81b65a3anthony        RotateKernelInfo(kernel, args->rho);
14189eb4f74649b23c053b308ce1152dce51239450baanthony        break;
14199eb4f74649b23c053b308ce1152dce51239450baanthony      }
1420e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony    case FreiChenKernel:
1421501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      /* Direction is set to be left to right positive */
1422501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      /* http://www.math.tau.ac.il/~turkel/notes/edge_detectors.pdf -- RIGHT? */
1423501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony      /* http://ltswww.epfl.ch/~courstiv/exos_labos/sol3.pdf -- WRONG? */
14241dd091ae3bc17edc26c16cc47f436a24bd48412aanthony      { switch ( (int) args->rho ) {
1425e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony          default:
1426c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          case 0:
1427501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1428c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            if (kernel == (KernelInfo *) NULL)
1429c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony              return(kernel);
1430ef33d9f66f955c1f6f5f7105e164cc2d5c5e2a41anthony            kernel->type = type;
1431d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[3] = +(MagickRealType) MagickSQ2;
1432d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[5] = -(MagickRealType) MagickSQ2;
1433c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
1434c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            break;
1435c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 2:
1436c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel=ParseKernelArray("3: 1,2,0  2,0,-2  0,-2,-1");
1437c40ac1e79923a1516075ba1197ae4ed90244af9banthony            if (kernel == (KernelInfo *) NULL)
1438c40ac1e79923a1516075ba1197ae4ed90244af9banthony              return(kernel);
1439c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel->type = type;
1440d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[1] = kernel->values[3]= +(MagickRealType) MagickSQ2;
1441d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[5] = kernel->values[7]= -(MagickRealType) MagickSQ2;
1442c40ac1e79923a1516075ba1197ae4ed90244af9banthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
1443d4e3ffa7f64077da0f32c2d8a599737ee8d115ddcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1444c40ac1e79923a1516075ba1197ae4ed90244af9banthony            break;
1445c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 10:
1446c40ac1e79923a1516075ba1197ae4ed90244af9banthony            kernel=AcquireKernelInfo("FreiChen:11;FreiChen:12;FreiChen:13;FreiChen:14;FreiChen:15;FreiChen:16;FreiChen:17;FreiChen:18;FreiChen:19");
1447c40ac1e79923a1516075ba1197ae4ed90244af9banthony            if (kernel == (KernelInfo *) NULL)
1448c40ac1e79923a1516075ba1197ae4ed90244af9banthony              return(kernel);
1449c40ac1e79923a1516075ba1197ae4ed90244af9banthony            break;
1450e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony          case 1:
1451c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 11:
1452501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,0,-1  2,0,-2  1,0,-1");
1453e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1454e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1455c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1456d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[3] = +(MagickRealType) MagickSQ2;
1457d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[5] = -(MagickRealType) MagickSQ2;
1458e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);     /* recalculate meta-data */
145955a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1460e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1461c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 12:
1462501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,2,1  0,0,0  1,2,1");
1463e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1464e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1465c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1466d9123437a79f9d56a3e8c32aa912ae3ae1d47337cristy            kernel->values[1] = +(MagickRealType) MagickSQ2;
1467d9123437a79f9d56a3e8c32aa912ae3ae1d47337cristy            kernel->values[7] = +(MagickRealType) MagickSQ2;
1468e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);
146955a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1470e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1471c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 13:
1472501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 2,-1,0  -1,0,1  0,1,-2");
1473e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1474e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1475c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1476d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[0] = +(MagickRealType) MagickSQ2;
1477d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[8] = -(MagickRealType) MagickSQ2;
1478e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);
147955a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1480e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1481c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 14:
14821d5e67090dc7232b35bfcc71b31266c20838defcanthony            kernel=ParseKernelArray("3: 0,1,-2  -1,0,1  2,-1,0");
1483e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1484e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1485c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1486d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[2] = -(MagickRealType) MagickSQ2;
1487d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            kernel->values[6] = +(MagickRealType) MagickSQ2;
1488e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            CalcKernelMetaData(kernel);
148955a91cddcdea3aa002893186a773e1704884a9dfcristy            ScaleKernelInfo(kernel, (double) (1.0/2.0*MagickSQ2), NoValue);
1490e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1491c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 15:
1492501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 0,-1,0  1,0,1  0,-1,0");
1493e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1494e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1495c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1496e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1497e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1498c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 16:
14991d5e67090dc7232b35bfcc71b31266c20838defcanthony            kernel=ParseKernelArray("3: 1,0,-1  0,0,0  -1,0,1");
1500e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1501e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1502c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1503e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/2.0, NoValue);
1504e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1505c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 17:
1506501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: 1,-2,1  -2,4,-2  -1,-2,1");
1507e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1508e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1509c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1510e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1511e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1512c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 18:
1513501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony            kernel=ParseKernelArray("3: -2,1,-2  1,4,1  -2,1,-2");
1514e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1515e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1516c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1517e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/6.0, NoValue);
1518e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1519c40ac1e79923a1516075ba1197ae4ed90244af9banthony          case 19:
1520c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel=ParseKernelArray("3: 1,1,1  1,1,1  1,1,1");
1521e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            if (kernel == (KernelInfo *) NULL)
1522e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony              return(kernel);
1523c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony            kernel->type = type;
1524e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            ScaleKernelInfo(kernel, 1.0/3.0, NoValue);
1525e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony            break;
1526e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony        }
1527b978e458a8e1f210bcb580951cf623687236b2fecristy        if ( fabs(args->sigma) >= MagickEpsilon )
1528c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          /* Rotate by correctly supplied 'angle' */
1529c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          RotateKernelInfo(kernel, args->sigma);
1530c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony        else if ( args->rho > 30.0 || args->rho < -30.0 )
1531c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          /* Rotate by out of bounds 'type' */
1532c3cd15b3dec84044ad4a50368681517cfb5e1b3fanthony          RotateKernelInfo(kernel, args->rho);
1533e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony        break;
1534e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony      }
1535e2a60ce55aeee814017d81dae8a58621ffc2acdbanthony
1536529482f4b494010a13338a74446c510712f670b3anthony    /*
1537529482f4b494010a13338a74446c510712f670b3anthony      Boolean or Shaped Kernels
1538529482f4b494010a13338a74446c510712f670b3anthony    */
1539c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case DiamondKernel:
1540602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
1541c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (args->rho < 1.0)
1542c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          kernel->width = kernel->height = 3;  /* default radius = 1 */
1543c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        else
1544bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
1545bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1546c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1547e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1548e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1549e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1550d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
1551c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(DestroyKernelInfo(kernel));
1552c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony
1553c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        /* set all kernel values within diamond area to scale given */
1554bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
1555bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
15561d5e67090dc7232b35bfcc71b31266c20838defcanthony            if ( (labs((long) u)+labs((long) v)) <= (long) kernel->x)
1557c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->positive_range += kernel->values[i] = args->sigma;
1558c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony            else
1559c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony              kernel->values[i] = nan;
1560c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
1561c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
1562c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
1563c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case SquareKernel:
1564c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony    case RectangleKernel:
1565c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      { double
1566c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          scale;
1567602ab9b30b644a78a4057da93d838a77391ec0acanthony        if ( type == SquareKernel )
1568602ab9b30b644a78a4057da93d838a77391ec0acanthony          {
1569602ab9b30b644a78a4057da93d838a77391ec0acanthony            if (args->rho < 1.0)
1570c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony              kernel->width = kernel->height = 3;  /* default radius = 1 */
1571602ab9b30b644a78a4057da93d838a77391ec0acanthony            else
1572bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy              kernel->width = kernel->height = (size_t) (2*args->rho+1);
1573bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
15744fd27e21043be809d66c8202e779255e5b660d2danthony            scale = args->sigma;
1575602ab9b30b644a78a4057da93d838a77391ec0acanthony          }
1576602ab9b30b644a78a4057da93d838a77391ec0acanthony        else {
15772be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy            /* NOTE: user defaults set in "AcquireKernelInfo()" */
1578602ab9b30b644a78a4057da93d838a77391ec0acanthony            if ( args->rho < 1.0 || args->sigma < 1.0 )
157983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony              return(DestroyKernelInfo(kernel));    /* invalid args given */
1580bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->width = (size_t)args->rho;
1581bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->height = (size_t)args->sigma;
1582602ab9b30b644a78a4057da93d838a77391ec0acanthony            if ( args->xi  < 0.0 || args->xi  > (double)kernel->width ||
1583602ab9b30b644a78a4057da93d838a77391ec0acanthony                 args->psi < 0.0 || args->psi > (double)kernel->height )
158483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony              return(DestroyKernelInfo(kernel));    /* invalid args given */
1585bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->x = (ssize_t) args->xi;
1586bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            kernel->y = (ssize_t) args->psi;
15874fd27e21043be809d66c8202e779255e5b660d2danthony            scale = 1.0;
1588602ab9b30b644a78a4057da93d838a77391ec0acanthony          }
1589e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
1590e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
1591e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
1592d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
159383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
1594602ab9b30b644a78a4057da93d838a77391ec0acanthony
15953dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony        /* set all kernel values to scale given */
1596eaedf06777741da32408da72c1e512975c600c48cristy        u=(ssize_t) (kernel->width*kernel->height);
1597150989ed67ef9da53141a65e5f3ebdb05dd025abcristy        for ( i=0; i < u; i++)
15984fd27e21043be809d66c8202e779255e5b660d2danthony            kernel->values[i] = scale;
15994fd27e21043be809d66c8202e779255e5b660d2danthony        kernel->minimum = kernel->maximum = scale;   /* a flat shape */
16004fd27e21043be809d66c8202e779255e5b660d2danthony        kernel->positive_range = scale*u;
1601cc6c836da2a53b6023b716e4973090a6714dc3b0anthony        break;
1602602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
16031ef941fea2534a0d20ba7d71307d35040247decbanthony      case OctagonKernel:
16041ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16051ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
1606a9892d898acb81e1ec73106d892855fdc5a69427anthony            kernel->width = kernel->height = 5;  /* default radius = 2 */
16071ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16081ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16091ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16101ef941fea2534a0d20ba7d71307d35040247decbanthony
1611e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1612e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1613e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1614d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
16151ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16161ef941fea2534a0d20ba7d71307d35040247decbanthony
16171ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16181ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16191ef941fea2534a0d20ba7d71307d35040247decbanthony              if ( (labs((long) u)+labs((long) v)) <=
16201ef941fea2534a0d20ba7d71307d35040247decbanthony                        ((long)kernel->x + (long)(kernel->x/2)) )
16211ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->positive_range += kernel->values[i] = args->sigma;
16221ef941fea2534a0d20ba7d71307d35040247decbanthony              else
16231ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->values[i] = nan;
1624a9892d898acb81e1ec73106d892855fdc5a69427anthony          kernel->minimum = kernel->maximum = args->sigma;  /* a flat shape */
16251ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16261ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16271ef941fea2534a0d20ba7d71307d35040247decbanthony      case DiskKernel:
16281ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16291ef941fea2534a0d20ba7d71307d35040247decbanthony          ssize_t
16300bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony            limit = (ssize_t)(args->rho*args->rho);
16311ef941fea2534a0d20ba7d71307d35040247decbanthony
16329bf68d590429a72aa70894f6aea7fc3c94876e54anthony          if (args->rho < 0.4)           /* default radius approx 4.3 */
16339bf68d590429a72aa70894f6aea7fc3c94876e54anthony            kernel->width = kernel->height = 9L, limit = 18L;
16341ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16351ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = (size_t)fabs(args->rho)*2+1;
16361ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16371ef941fea2534a0d20ba7d71307d35040247decbanthony
1638e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1639e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1640e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1641d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
16421ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16431ef941fea2534a0d20ba7d71307d35040247decbanthony
16441ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16451ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16461ef941fea2534a0d20ba7d71307d35040247decbanthony              if ((u*u+v*v) <= limit)
16471ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->positive_range += kernel->values[i] = args->sigma;
16483dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony              else
16493dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony                kernel->values[i] = nan;
16501ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16511ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16521ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16531ef941fea2534a0d20ba7d71307d35040247decbanthony      case PlusKernel:
16541ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16551ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
16561ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 5;  /* default radius 2 */
16571ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16581ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16591ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16601ef941fea2534a0d20ba7d71307d35040247decbanthony
1661e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1662e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1663e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1664d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
16651ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16661ef941fea2534a0d20ba7d71307d35040247decbanthony
16671ef941fea2534a0d20ba7d71307d35040247decbanthony          /* set all kernel values along axises to given scale */
16681ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16691ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16701ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->values[i] = (u == 0 || v == 0) ? args->sigma : nan;
16711ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16721ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
16731ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16741ef941fea2534a0d20ba7d71307d35040247decbanthony        }
16751ef941fea2534a0d20ba7d71307d35040247decbanthony      case CrossKernel:
16761ef941fea2534a0d20ba7d71307d35040247decbanthony        {
16771ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
16781ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 5;  /* default radius 2 */
16791ef941fea2534a0d20ba7d71307d35040247decbanthony          else
16801ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
16811ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
16821ef941fea2534a0d20ba7d71307d35040247decbanthony
1683e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1684e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1685e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1686d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
16871ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
16881ef941fea2534a0d20ba7d71307d35040247decbanthony
16891ef941fea2534a0d20ba7d71307d35040247decbanthony          /* set all kernel values along axises to given scale */
16901ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
16911ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
16921ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->values[i] = (u == v || u == -v) ? args->sigma : nan;
16931ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = args->sigma;   /* a flat shape */
16941ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->positive_range = args->sigma*(kernel->width*2.0 - 1.0);
16951ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
16961ef941fea2534a0d20ba7d71307d35040247decbanthony        }
1697529482f4b494010a13338a74446c510712f670b3anthony      /*
1698529482f4b494010a13338a74446c510712f670b3anthony        HitAndMiss Kernels
1699529482f4b494010a13338a74446c510712f670b3anthony      */
17001ef941fea2534a0d20ba7d71307d35040247decbanthony      case RingKernel:
17011ef941fea2534a0d20ba7d71307d35040247decbanthony      case PeaksKernel:
17021ef941fea2534a0d20ba7d71307d35040247decbanthony        {
17031ef941fea2534a0d20ba7d71307d35040247decbanthony          ssize_t
17041ef941fea2534a0d20ba7d71307d35040247decbanthony            limit1,
17051ef941fea2534a0d20ba7d71307d35040247decbanthony            limit2,
17061ef941fea2534a0d20ba7d71307d35040247decbanthony            scale;
17071ef941fea2534a0d20ba7d71307d35040247decbanthony
17081ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < args->sigma)
17091ef941fea2534a0d20ba7d71307d35040247decbanthony            {
17101ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->width = ((size_t)args->sigma)*2+1;
17111ef941fea2534a0d20ba7d71307d35040247decbanthony              limit1 = (ssize_t)(args->rho*args->rho);
17121ef941fea2534a0d20ba7d71307d35040247decbanthony              limit2 = (ssize_t)(args->sigma*args->sigma);
17133dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony            }
17141ef941fea2534a0d20ba7d71307d35040247decbanthony          else
17151ef941fea2534a0d20ba7d71307d35040247decbanthony            {
17161ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->width = ((size_t)args->rho)*2+1;
17171ef941fea2534a0d20ba7d71307d35040247decbanthony              limit1 = (ssize_t)(args->sigma*args->sigma);
17181ef941fea2534a0d20ba7d71307d35040247decbanthony              limit2 = (ssize_t)(args->rho*args->rho);
17191ef941fea2534a0d20ba7d71307d35040247decbanthony            }
17201ef941fea2534a0d20ba7d71307d35040247decbanthony          if ( limit2 <= 0 )
17211ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = 7L, limit1 = 7L, limit2 = 11L;
17221ef941fea2534a0d20ba7d71307d35040247decbanthony
17231ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->height = kernel->width;
17241ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
1725e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
1726e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
1727e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
1728d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
17291ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
17301ef941fea2534a0d20ba7d71307d35040247decbanthony
17311ef941fea2534a0d20ba7d71307d35040247decbanthony          /* set a ring of points of 'scale' ( 0.0 for PeaksKernel ) */
17321ef941fea2534a0d20ba7d71307d35040247decbanthony          scale = (ssize_t) (( type == PeaksKernel) ? 0.0 : args->xi);
17331ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v= -kernel->y; v <= (ssize_t)kernel->y; v++)
17341ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
17351ef941fea2534a0d20ba7d71307d35040247decbanthony              { ssize_t radius=u*u+v*v;
17361ef941fea2534a0d20ba7d71307d35040247decbanthony                if (limit1 < radius && radius <= limit2)
17371ef941fea2534a0d20ba7d71307d35040247decbanthony                  kernel->positive_range += kernel->values[i] = (double) scale;
17381ef941fea2534a0d20ba7d71307d35040247decbanthony                else
17391ef941fea2534a0d20ba7d71307d35040247decbanthony                  kernel->values[i] = nan;
17401ef941fea2534a0d20ba7d71307d35040247decbanthony              }
17411ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->minimum = kernel->maximum = (double) scale;
17421ef941fea2534a0d20ba7d71307d35040247decbanthony          if ( type == PeaksKernel ) {
17431ef941fea2534a0d20ba7d71307d35040247decbanthony            /* set the central point in the middle */
17441ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->values[kernel->x+kernel->y*kernel->width] = 1.0;
17451ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->positive_range = 1.0;
17461ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->maximum = 1.0;
17471ef941fea2534a0d20ba7d71307d35040247decbanthony          }
17481ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
1749c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        }
17501ef941fea2534a0d20ba7d71307d35040247decbanthony      case EdgesKernel:
17511ef941fea2534a0d20ba7d71307d35040247decbanthony        {
1752529482f4b494010a13338a74446c510712f670b3anthony          kernel=AcquireKernelInfo("ThinSE:482");
17531ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
17541ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
17551ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
1756529482f4b494010a13338a74446c510712f670b3anthony          ExpandMirrorKernelInfo(kernel); /* mirror expansion of kernels */
17571ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17581ef941fea2534a0d20ba7d71307d35040247decbanthony        }
17591ef941fea2534a0d20ba7d71307d35040247decbanthony      case CornersKernel:
17601ef941fea2534a0d20ba7d71307d35040247decbanthony        {
1761529482f4b494010a13338a74446c510712f670b3anthony          kernel=AcquireKernelInfo("ThinSE:87");
17621ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
17631ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
17641ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
17651ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(kernel, 90.0); /* Expand 90 degree rotations */
17661ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17671ef941fea2534a0d20ba7d71307d35040247decbanthony        }
1768529482f4b494010a13338a74446c510712f670b3anthony      case DiagonalsKernel:
17691ef941fea2534a0d20ba7d71307d35040247decbanthony        {
17701ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
17711ef941fea2534a0d20ba7d71307d35040247decbanthony            case 0:
17721ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
17731ef941fea2534a0d20ba7d71307d35040247decbanthony              { KernelInfo
17741ef941fea2534a0d20ba7d71307d35040247decbanthony                  *new_kernel;
1775529482f4b494010a13338a74446c510712f670b3anthony                kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
17761ef941fea2534a0d20ba7d71307d35040247decbanthony                if (kernel == (KernelInfo *) NULL)
17771ef941fea2534a0d20ba7d71307d35040247decbanthony                  return(kernel);
17781ef941fea2534a0d20ba7d71307d35040247decbanthony                kernel->type = type;
1779529482f4b494010a13338a74446c510712f670b3anthony                new_kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
17801ef941fea2534a0d20ba7d71307d35040247decbanthony                if (new_kernel == (KernelInfo *) NULL)
17811ef941fea2534a0d20ba7d71307d35040247decbanthony                  return(DestroyKernelInfo(kernel));
17821ef941fea2534a0d20ba7d71307d35040247decbanthony                new_kernel->type = type;
17831ef941fea2534a0d20ba7d71307d35040247decbanthony                LastKernelInfo(kernel)->next = new_kernel;
17841ef941fea2534a0d20ba7d71307d35040247decbanthony                ExpandMirrorKernelInfo(kernel);
1785529482f4b494010a13338a74446c510712f670b3anthony                return(kernel);
17861ef941fea2534a0d20ba7d71307d35040247decbanthony              }
17871ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
1788529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,0,0  0,-,1  1,1,-");
17891ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17901ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
1791529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,0,1  0,-,1  0,1,-");
17921ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
17931ef941fea2534a0d20ba7d71307d35040247decbanthony          }
1794529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
1795529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
1796529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
1797529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
17981ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
17991ef941fea2534a0d20ba7d71307d35040247decbanthony        }
18001ef941fea2534a0d20ba7d71307d35040247decbanthony      case LineEndsKernel:
18011ef941fea2534a0d20ba7d71307d35040247decbanthony        { /* Kernels for finding the end of thin lines */
18021ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
18031ef941fea2534a0d20ba7d71307d35040247decbanthony            case 0:
18041ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
18051ef941fea2534a0d20ba7d71307d35040247decbanthony              /* set of kernels to find all end of lines */
1806529482f4b494010a13338a74446c510712f670b3anthony              return(AcquireKernelInfo("LineEnds:1>;LineEnds:2>"));
18071ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
18081ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel for 4-connected line ends - no rotation */
18091ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,-  0,1,1  0,0,-");
18101ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18111ef941fea2534a0d20ba7d71307d35040247decbanthony          case 2:
18121ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel to add for 8-connected lines - no rotation */
18131ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,0  0,0,1");
18141ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18151ef941fea2534a0d20ba7d71307d35040247decbanthony          case 3:
18161ef941fea2534a0d20ba7d71307d35040247decbanthony              /* kernel to add for orthogonal line ends - does not find corners */
18171ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,1  0,0,0");
18181ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18191ef941fea2534a0d20ba7d71307d35040247decbanthony          case 4:
18201ef941fea2534a0d20ba7d71307d35040247decbanthony              /* traditional line end - fails on last T end */
18211ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 0,0,0  0,1,-  0,0,-");
18221ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18231ef941fea2534a0d20ba7d71307d35040247decbanthony          }
1824529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
1825529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
1826529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
1827529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
18281ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
18291ef941fea2534a0d20ba7d71307d35040247decbanthony        }
18301ef941fea2534a0d20ba7d71307d35040247decbanthony      case LineJunctionsKernel:
18311ef941fea2534a0d20ba7d71307d35040247decbanthony        { /* kernels for finding the junctions of multiple lines */
18321ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
18331ef941fea2534a0d20ba7d71307d35040247decbanthony            case 0:
18341ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
18351ef941fea2534a0d20ba7d71307d35040247decbanthony              /* set of kernels to find all line junctions */
1836529482f4b494010a13338a74446c510712f670b3anthony              return(AcquireKernelInfo("LineJunctions:1@;LineJunctions:2>"));
18371ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
18381ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Y Junction */
18391ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,1  -,1,-  -,1,-");
18401ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18411ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
18421ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Diagonal T Junctions */
18431ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,-  -,1,-  1,-,1");
18441ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18451ef941fea2534a0d20ba7d71307d35040247decbanthony            case 3:
18461ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Orthogonal T Junctions */
18471ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: -,-,-  1,1,1  -,1,-");
18481ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18491ef941fea2534a0d20ba7d71307d35040247decbanthony            case 4:
18501ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Diagonal X Junctions */
18511ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: 1,-,1  -,1,-  1,-,1");
18521ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18531ef941fea2534a0d20ba7d71307d35040247decbanthony            case 5:
18541ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Orthogonal X Junctions - minimal diamond kernel */
18551ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3: -,1,-  1,1,1  -,1,-");
18561ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18571ef941fea2534a0d20ba7d71307d35040247decbanthony          }
1858529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
1859529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
1860529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
1861529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
18621ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
18631ef941fea2534a0d20ba7d71307d35040247decbanthony        }
18641ef941fea2534a0d20ba7d71307d35040247decbanthony      case RidgesKernel:
18651ef941fea2534a0d20ba7d71307d35040247decbanthony        { /* Ridges - Ridge finding kernels */
18661ef941fea2534a0d20ba7d71307d35040247decbanthony          KernelInfo
18671ef941fea2534a0d20ba7d71307d35040247decbanthony            *new_kernel;
18681ef941fea2534a0d20ba7d71307d35040247decbanthony          switch ( (int) args->rho ) {
18691ef941fea2534a0d20ba7d71307d35040247decbanthony            case 1:
18701ef941fea2534a0d20ba7d71307d35040247decbanthony            default:
18711ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("3x1:0,1,0");
18721ef941fea2534a0d20ba7d71307d35040247decbanthony              if (kernel == (KernelInfo *) NULL)
18731ef941fea2534a0d20ba7d71307d35040247decbanthony                return(kernel);
18741ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->type = type;
18751ef941fea2534a0d20ba7d71307d35040247decbanthony              ExpandRotateKernelInfo(kernel, 90.0); /* 2 rotated kernels (symmetrical) */
18761ef941fea2534a0d20ba7d71307d35040247decbanthony              break;
18771ef941fea2534a0d20ba7d71307d35040247decbanthony            case 2:
18781ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel=ParseKernelArray("4x1:0,1,1,0");
18791ef941fea2534a0d20ba7d71307d35040247decbanthony              if (kernel == (KernelInfo *) NULL)
18801ef941fea2534a0d20ba7d71307d35040247decbanthony                return(kernel);
18811ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->type = type;
18821ef941fea2534a0d20ba7d71307d35040247decbanthony              ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotated kernels */
18831ef941fea2534a0d20ba7d71307d35040247decbanthony
18841ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Kernels to find a stepped 'thick' line, 4 rotates + mirrors */
18851ef941fea2534a0d20ba7d71307d35040247decbanthony              /* Unfortunatally we can not yet rotate a non-square kernel */
18861ef941fea2534a0d20ba7d71307d35040247decbanthony              /* But then we can't flip a non-symetrical kernel either */
18871ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+1+1:0,1,1,- -,1,1,- -,1,1,0");
18881ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18891ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
18901ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
18911ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
18921ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+2+1:0,1,1,- -,1,1,- -,1,1,0");
18931ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18941ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
18951ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
18961ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
18971ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+1+1:-,1,1,0 -,1,1,- 0,1,1,-");
18981ef941fea2534a0d20ba7d71307d35040247decbanthony              if (new_kernel == (KernelInfo *) NULL)
18991ef941fea2534a0d20ba7d71307d35040247decbanthony                return(DestroyKernelInfo(kernel));
19001ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel->type = type;
19011ef941fea2534a0d20ba7d71307d35040247decbanthony              LastKernelInfo(kernel)->next = new_kernel;
19021ef941fea2534a0d20ba7d71307d35040247decbanthony              new_kernel=ParseKernelArray("4x3+2+1:-,1,1,0 -,1,1,- 0,1,1,-");
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("3x4+1+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("3x4+1+2:0,-,- 1,1,1 1,1,1 -,-,0");
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("3x4+1+1:-,-,0 1,1,1 1,1,1 0,-,-");
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+2:-,-,0 1,1,1 1,1,1 0,-,-");
192368cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              if (new_kernel == (KernelInfo *) NULL)
192468cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony                return(DestroyKernelInfo(kernel));
192568cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              new_kernel->type = type;
192668cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              LastKernelInfo(kernel)->next = new_kernel;
192768cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony              break;
19281ef941fea2534a0d20ba7d71307d35040247decbanthony          }
19291ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
193068cf70d51e88101b28a7fa0f28ad5d040a1ace12anthony        }
19311ef941fea2534a0d20ba7d71307d35040247decbanthony      case ConvexHullKernel:
19321ef941fea2534a0d20ba7d71307d35040247decbanthony        {
19331ef941fea2534a0d20ba7d71307d35040247decbanthony          KernelInfo
19341ef941fea2534a0d20ba7d71307d35040247decbanthony            *new_kernel;
19351ef941fea2534a0d20ba7d71307d35040247decbanthony          /* first set of 8 kernels */
19361ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel=ParseKernelArray("3: 1,1,-  1,0,-  1,-,0");
19371ef941fea2534a0d20ba7d71307d35040247decbanthony          if (kernel == (KernelInfo *) NULL)
19381ef941fea2534a0d20ba7d71307d35040247decbanthony            return(kernel);
19391ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->type = type;
19401ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(kernel, 90.0);
19411ef941fea2534a0d20ba7d71307d35040247decbanthony          /* append the mirror versions too - no flip function yet */
19421ef941fea2534a0d20ba7d71307d35040247decbanthony          new_kernel=ParseKernelArray("3: 1,1,1  1,0,-  -,-,0");
19431ef941fea2534a0d20ba7d71307d35040247decbanthony          if (new_kernel == (KernelInfo *) NULL)
19441ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
19451ef941fea2534a0d20ba7d71307d35040247decbanthony          new_kernel->type = type;
19461ef941fea2534a0d20ba7d71307d35040247decbanthony          ExpandRotateKernelInfo(new_kernel, 90.0);
19471ef941fea2534a0d20ba7d71307d35040247decbanthony          LastKernelInfo(kernel)->next = new_kernel;
19481ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
1949694934fa79dd310f727588b1d0a7481fa6170f1danthony        }
19509a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony      case SkeletonKernel:
19519a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony        {
19529a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony          switch ( (int) args->rho ) {
19539a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 1:
19549a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            default:
19559a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* Traditional Skeleton...
19569a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** A cyclically rotated single kernel
19579a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19589a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=AcquireKernelInfo("ThinSE:482");
19599a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19609a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19619a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19629a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandRotateKernelInfo(kernel, 45.0); /* 8 rotations */
19639a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19649a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 2:
19659a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* HIPR Variation of the cyclic skeleton
19669a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** Corners of the traditional method made more forgiving,
19679a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** but the retain the same cyclic order.
19689a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19699a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=AcquireKernelInfo("ThinSE:482; ThinSE:87x90;");
19709a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19719a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19729a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel->next == (KernelInfo *) NULL)
19739a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(DestroyKernelInfo(kernel));
19749a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19759a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->type = type;
19769a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandRotateKernelInfo(kernel, 90.0); /* 4 rotations of the 2 kernels */
19779a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19789a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 3:
19799a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              /* Dan Bloomberg Skeleton, from his paper on 3x3 thinning SE's
19809a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** "Connectivity-Preserving Morphological Image Thransformations"
19819a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
19829a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              **   http://www.leptonica.com/papers/conn.pdf
19839a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              */
19849a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=AcquireKernelInfo(
19859a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                            "ThinSE:41; ThinSE:42; ThinSE:43");
19869a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              if (kernel == (KernelInfo *) NULL)
19879a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony                return(kernel);
19889a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->type = type;
19899a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->type = type;
19909a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel->next->next->type = type;
19919a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              ExpandMirrorKernelInfo(kernel); /* 12 kernels total */
19929a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
19939a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony           }
19949a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony          break;
19959a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony        }
1996529482f4b494010a13338a74446c510712f670b3anthony      case ThinSEKernel:
1997529482f4b494010a13338a74446c510712f670b3anthony        { /* Special kernels for general thinning, while preserving connections
1998529482f4b494010a13338a74446c510712f670b3anthony          ** "Connectivity-Preserving Morphological Image Thransformations"
1999529482f4b494010a13338a74446c510712f670b3anthony          ** by Dan S. Bloomberg, available on Leptonica, Selected Papers,
2000529482f4b494010a13338a74446c510712f670b3anthony          **   http://www.leptonica.com/papers/conn.pdf
2001529482f4b494010a13338a74446c510712f670b3anthony          ** And
2002529482f4b494010a13338a74446c510712f670b3anthony          **   http://tpgit.github.com/Leptonica/ccthin_8c_source.html
2003529482f4b494010a13338a74446c510712f670b3anthony          **
2004529482f4b494010a13338a74446c510712f670b3anthony          ** Note kernels do not specify the origin pixel, allowing them
2005529482f4b494010a13338a74446c510712f670b3anthony          ** to be used for both thickening and thinning operations.
2006529482f4b494010a13338a74446c510712f670b3anthony          */
2007529482f4b494010a13338a74446c510712f670b3anthony          switch ( (int) args->rho ) {
2008529482f4b494010a13338a74446c510712f670b3anthony            /* SE for 4-connected thinning */
2009529482f4b494010a13338a74446c510712f670b3anthony            case 41: /* SE_4_1 */
2010529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  -,-,1");
2011529482f4b494010a13338a74446c510712f670b3anthony              break;
2012529482f4b494010a13338a74446c510712f670b3anthony            case 42: /* SE_4_2 */
2013529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  -,0,-");
2014529482f4b494010a13338a74446c510712f670b3anthony              break;
2015529482f4b494010a13338a74446c510712f670b3anthony            case 43: /* SE_4_3 */
2016529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,-,1");
2017529482f4b494010a13338a74446c510712f670b3anthony              break;
2018529482f4b494010a13338a74446c510712f670b3anthony            case 44: /* SE_4_4 */
2019529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,-");
2020529482f4b494010a13338a74446c510712f670b3anthony              break;
2021529482f4b494010a13338a74446c510712f670b3anthony            case 45: /* SE_4_5 */
2022529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,1  0,-,1  -,0,-");
2023529482f4b494010a13338a74446c510712f670b3anthony              break;
2024529482f4b494010a13338a74446c510712f670b3anthony            case 46: /* SE_4_6 */
2025529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,0,-  0,-,1  -,0,1");
2026529482f4b494010a13338a74446c510712f670b3anthony              break;
2027529482f4b494010a13338a74446c510712f670b3anthony            case 47: /* SE_4_7 */
2028529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,1  0,-,1  -,0,-");
2029529482f4b494010a13338a74446c510712f670b3anthony              break;
2030529482f4b494010a13338a74446c510712f670b3anthony            case 48: /* SE_4_8 */
2031529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,-,1  0,-,1  0,-,1");
2032529482f4b494010a13338a74446c510712f670b3anthony              break;
2033529482f4b494010a13338a74446c510712f670b3anthony            case 49: /* SE_4_9 */
2034529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  -,-,1");
2035529482f4b494010a13338a74446c510712f670b3anthony              break;
2036529482f4b494010a13338a74446c510712f670b3anthony            /* SE for 8-connected thinning - negatives of the above */
2037529482f4b494010a13338a74446c510712f670b3anthony            case 81: /* SE_8_0 */
2038529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  -,1,-");
2039529482f4b494010a13338a74446c510712f670b3anthony              break;
2040529482f4b494010a13338a74446c510712f670b3anthony            case 82: /* SE_8_2 */
2041529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,-,-");
2042529482f4b494010a13338a74446c510712f670b3anthony              break;
2043529482f4b494010a13338a74446c510712f670b3anthony            case 83: /* SE_8_3 */
2044529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  -,1,-");
2045529482f4b494010a13338a74446c510712f670b3anthony              break;
2046529482f4b494010a13338a74446c510712f670b3anthony            case 84: /* SE_8_4 */
2047529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,-");
2048529482f4b494010a13338a74446c510712f670b3anthony              break;
2049529482f4b494010a13338a74446c510712f670b3anthony            case 85: /* SE_8_5 */
2050529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,-");
2051529482f4b494010a13338a74446c510712f670b3anthony              break;
2052529482f4b494010a13338a74446c510712f670b3anthony            case 86: /* SE_8_6 */
2053529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,-  0,-,1  0,-,1");
2054529482f4b494010a13338a74446c510712f670b3anthony              break;
2055529482f4b494010a13338a74446c510712f670b3anthony            case 87: /* SE_8_7 */
2056529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,0,-");
2057529482f4b494010a13338a74446c510712f670b3anthony              break;
2058529482f4b494010a13338a74446c510712f670b3anthony            case 88: /* SE_8_8 */
2059529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,-  0,-,1  0,1,-");
2060529482f4b494010a13338a74446c510712f670b3anthony              break;
2061529482f4b494010a13338a74446c510712f670b3anthony            case 89: /* SE_8_9 */
2062529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,1,-  0,-,1  -,1,-");
2063529482f4b494010a13338a74446c510712f670b3anthony              break;
2064529482f4b494010a13338a74446c510712f670b3anthony            /* Special combined SE kernels */
20659a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 423: /* SE_4_2 , SE_4_3 Combined Kernel */
20669a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=ParseKernelArray("3: -,-,1  0,-,-  -,0,-");
20679a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
20689a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony            case 823: /* SE_8_2 , SE_8_3 Combined Kernel */
20699a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              kernel=ParseKernelArray("3: -,1,-  -,-,1  0,-,-");
20709a89ca47eecfc04e9e6b04767527c692bc09bfe2anthony              break;
2071529482f4b494010a13338a74446c510712f670b3anthony            case 481: /* SE_48_1 - General Connected Corner Kernel */
2072529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: -,1,1  0,-,1  0,0,-");
2073529482f4b494010a13338a74446c510712f670b3anthony              break;
2074529482f4b494010a13338a74446c510712f670b3anthony            default:
2075529482f4b494010a13338a74446c510712f670b3anthony            case 482: /* SE_48_2 - General Edge Kernel */
2076529482f4b494010a13338a74446c510712f670b3anthony              kernel=ParseKernelArray("3: 0,-,1  0,-,1  0,-,1");
2077529482f4b494010a13338a74446c510712f670b3anthony              break;
2078529482f4b494010a13338a74446c510712f670b3anthony          }
2079529482f4b494010a13338a74446c510712f670b3anthony          if (kernel == (KernelInfo *) NULL)
2080529482f4b494010a13338a74446c510712f670b3anthony            return(kernel);
2081529482f4b494010a13338a74446c510712f670b3anthony          kernel->type = type;
2082529482f4b494010a13338a74446c510712f670b3anthony          RotateKernelInfo(kernel, args->sigma);
2083529482f4b494010a13338a74446c510712f670b3anthony          break;
2084529482f4b494010a13338a74446c510712f670b3anthony        }
2085529482f4b494010a13338a74446c510712f670b3anthony      /*
2086529482f4b494010a13338a74446c510712f670b3anthony        Distance Measuring Kernels
2087529482f4b494010a13338a74446c510712f670b3anthony      */
20881ef941fea2534a0d20ba7d71307d35040247decbanthony      case ChebyshevKernel:
20891ef941fea2534a0d20ba7d71307d35040247decbanthony        {
20901ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
20911ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 3;  /* default radius = 1 */
20921ef941fea2534a0d20ba7d71307d35040247decbanthony          else
20931ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
20941ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
20951ef941fea2534a0d20ba7d71307d35040247decbanthony
2096e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
2097e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
2098e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
2099d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
21001ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
21011ef941fea2534a0d20ba7d71307d35040247decbanthony
21021ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
21031ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
21041ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->positive_range += ( kernel->values[i] =
21051ef941fea2534a0d20ba7d71307d35040247decbanthony                  args->sigma*MagickMax(fabs((double)u),fabs((double)v)) );
21061ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->maximum = kernel->values[0];
21071ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
2108c40ac1e79923a1516075ba1197ae4ed90244af9banthony        }
21091ef941fea2534a0d20ba7d71307d35040247decbanthony      case ManhattanKernel:
21101ef941fea2534a0d20ba7d71307d35040247decbanthony        {
21111ef941fea2534a0d20ba7d71307d35040247decbanthony          if (args->rho < 1.0)
21121ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = 3;  /* default radius = 1 */
21131ef941fea2534a0d20ba7d71307d35040247decbanthony          else
21141ef941fea2534a0d20ba7d71307d35040247decbanthony            kernel->width = kernel->height = ((size_t)args->rho)*2+1;
21151ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
21161ef941fea2534a0d20ba7d71307d35040247decbanthony
2117e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          kernel->values=(MagickRealType *) MagickAssumeAligned(
2118e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            AcquireAlignedMemory(kernel->width,kernel->height*
2119e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy            sizeof(*kernel->values)));
2120d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          if (kernel->values == (MagickRealType *) NULL)
21211ef941fea2534a0d20ba7d71307d35040247decbanthony            return(DestroyKernelInfo(kernel));
21221ef941fea2534a0d20ba7d71307d35040247decbanthony
21231ef941fea2534a0d20ba7d71307d35040247decbanthony          for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
21241ef941fea2534a0d20ba7d71307d35040247decbanthony            for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
21251ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->positive_range += ( kernel->values[i] =
21261ef941fea2534a0d20ba7d71307d35040247decbanthony                  args->sigma*(labs((long) u)+labs((long) v)) );
21271ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->maximum = kernel->values[0];
21281ef941fea2534a0d20ba7d71307d35040247decbanthony          break;
2129c40ac1e79923a1516075ba1197ae4ed90244af9banthony        }
21301ef941fea2534a0d20ba7d71307d35040247decbanthony      case OctagonalKernel:
2131602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
21321ef941fea2534a0d20ba7d71307d35040247decbanthony        if (args->rho < 2.0)
21331ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->width = kernel->height = 5;  /* default/minimum radius = 2 */
2134602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
2135bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2136bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2137602ab9b30b644a78a4057da93d838a77391ec0acanthony
2138e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
2139e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
2140e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
2141d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
214283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
2143602ab9b30b644a78a4057da93d838a77391ec0acanthony
2144bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2145bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
21461ef941fea2534a0d20ba7d71307d35040247decbanthony            {
21471ef941fea2534a0d20ba7d71307d35040247decbanthony              double
21481ef941fea2534a0d20ba7d71307d35040247decbanthony                r1 = MagickMax(fabs((double)u),fabs((double)v)),
21491ef941fea2534a0d20ba7d71307d35040247decbanthony                r2 = floor((double)(labs((long)u)+labs((long)v)+1)/1.5);
21501ef941fea2534a0d20ba7d71307d35040247decbanthony              kernel->positive_range += kernel->values[i] =
21511ef941fea2534a0d20ba7d71307d35040247decbanthony                        args->sigma*MagickMax(r1,r2);
21521ef941fea2534a0d20ba7d71307d35040247decbanthony            }
2153c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->maximum = kernel->values[0];
2154602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
2155602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2156602ab9b30b644a78a4057da93d838a77391ec0acanthony    case EuclideanKernel:
2157602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
2158602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (args->rho < 1.0)
2159c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony          kernel->width = kernel->height = 3;  /* default radius = 1 */
2160602ab9b30b644a78a4057da93d838a77391ec0acanthony        else
21611ef941fea2534a0d20ba7d71307d35040247decbanthony          kernel->width = kernel->height = ((size_t)args->rho)*2+1;
2162bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        kernel->x = kernel->y = (ssize_t) (kernel->width-1)/2;
2163602ab9b30b644a78a4057da93d838a77391ec0acanthony
2164e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy        kernel->values=(MagickRealType *) MagickAssumeAligned(
2165e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          AcquireAlignedMemory(kernel->width,kernel->height*
2166e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy          sizeof(*kernel->values)));
2167d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy        if (kernel->values == (MagickRealType *) NULL)
216883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony          return(DestroyKernelInfo(kernel));
2169602ab9b30b644a78a4057da93d838a77391ec0acanthony
2170bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy        for ( i=0, v=-kernel->y; v <= (ssize_t)kernel->y; v++)
2171bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          for ( u=-kernel->x; u <= (ssize_t)kernel->x; u++, i++)
2172c99304fe3c8d9c617da792b40b57c118bb1249afcristy            kernel->positive_range += ( kernel->values[i] =
21730ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony              args->sigma*sqrt((double)(u*u+v*v)) );
2174c99304fe3c8d9c617da792b40b57c118bb1249afcristy        kernel->maximum = kernel->values[0];
2175602ab9b30b644a78a4057da93d838a77391ec0acanthony        break;
2176602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2177602ab9b30b644a78a4057da93d838a77391ec0acanthony    default:
2178c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      {
2179529482f4b494010a13338a74446c510712f670b3anthony        /* No-Op Kernel - Basically just a single pixel on its own */
21803ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony        kernel=ParseKernelArray("1:1");
2181c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        if (kernel == (KernelInfo *) NULL)
2182c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony          return(kernel);
2183529482f4b494010a13338a74446c510712f670b3anthony        kernel->type = UndefinedKernel;
2184c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony        break;
2185c106172cd6da00b6c8fd6a4c06efdd4aa32c7f06anthony      }
2186602ab9b30b644a78a4057da93d838a77391ec0acanthony      break;
2187602ab9b30b644a78a4057da93d838a77391ec0acanthony  }
2188602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
2189602ab9b30b644a78a4057da93d838a77391ec0acanthony}
2190c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony
2191602ab9b30b644a78a4057da93d838a77391ec0acanthony/*
2192602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2193602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2194602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2195602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
21966771f1e8987fa49f52d4176281a2e8524b8e31cbcristy%     C l o n e K e r n e l I n f o                                           %
21974fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
21984fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
21994fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
22004fd27e21043be809d66c8202e779255e5b660d2danthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22014fd27e21043be809d66c8202e779255e5b660d2danthony%
22021b2bc0a7da432e6e1cc0480280402df213faa940anthony%  CloneKernelInfo() creates a new clone of the given Kernel List so that its
22031b2bc0a7da432e6e1cc0480280402df213faa940anthony%  can be modified without effecting the original.  The cloned kernel should
22040ffcd2751e5c2f2a606fa367a7fd01a424d3fdf7anthony%  be destroyed using DestoryKernelInfo() when no longer needed.
22057a01dcf50ce12cb2a789bedff51e9345f022432eanthony%
2206e636559dfadfdb115cc93f223315052a1ee89238cristy%  The format of the CloneKernelInfo method is:
22074fd27e21043be809d66c8202e779255e5b660d2danthony%
2208930be614b4595b97cd79ee864a394796740f76adanthony%      KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
22094fd27e21043be809d66c8202e779255e5b660d2danthony%
22104fd27e21043be809d66c8202e779255e5b660d2danthony%  A description of each parameter follows:
22114fd27e21043be809d66c8202e779255e5b660d2danthony%
22124fd27e21043be809d66c8202e779255e5b660d2danthony%    o kernel: the Morphology/Convolution kernel to be cloned
22134fd27e21043be809d66c8202e779255e5b660d2danthony%
22144fd27e21043be809d66c8202e779255e5b660d2danthony*/
2215ef656913b0b30d713ae94c82c47693c9dc69c9f4cristyMagickExport KernelInfo *CloneKernelInfo(const KernelInfo *kernel)
22164fd27e21043be809d66c8202e779255e5b660d2danthony{
2217bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register ssize_t
22184fd27e21043be809d66c8202e779255e5b660d2danthony    i;
22194fd27e21043be809d66c8202e779255e5b660d2danthony
222019eb64195ef744f61293025952df1e5e6de66524cristy  KernelInfo
22217a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *new_kernel;
22224fd27e21043be809d66c8202e779255e5b660d2danthony
22234fd27e21043be809d66c8202e779255e5b660d2danthony  assert(kernel != (KernelInfo *) NULL);
22247a01dcf50ce12cb2a789bedff51e9345f022432eanthony  new_kernel=(KernelInfo *) AcquireMagickMemory(sizeof(*kernel));
22257a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if (new_kernel == (KernelInfo *) NULL)
22267a01dcf50ce12cb2a789bedff51e9345f022432eanthony    return(new_kernel);
22277a01dcf50ce12cb2a789bedff51e9345f022432eanthony  *new_kernel=(*kernel); /* copy values in structure */
22287a01dcf50ce12cb2a789bedff51e9345f022432eanthony
22297a01dcf50ce12cb2a789bedff51e9345f022432eanthony  /* replace the values with a copy of the values */
2230e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy  new_kernel->values=(MagickRealType *) MagickAssumeAligned(
2231e42639a7b750e7b86ae59f3ba8f5972fee9e85d3cristy    AcquireAlignedMemory(kernel->width,kernel->height*sizeof(*kernel->values)));
2232d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy  if (new_kernel->values == (MagickRealType *) NULL)
22337a01dcf50ce12cb2a789bedff51e9345f022432eanthony    return(DestroyKernelInfo(new_kernel));
2234bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
22357a01dcf50ce12cb2a789bedff51e9345f022432eanthony    new_kernel->values[i]=kernel->values[i];
22361b2bc0a7da432e6e1cc0480280402df213faa940anthony
22371b2bc0a7da432e6e1cc0480280402df213faa940anthony  /* Also clone the next kernel in the kernel list */
22381b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL ) {
22391b2bc0a7da432e6e1cc0480280402df213faa940anthony    new_kernel->next = CloneKernelInfo(kernel->next);
22401b2bc0a7da432e6e1cc0480280402df213faa940anthony    if ( new_kernel->next == (KernelInfo *) NULL )
22411b2bc0a7da432e6e1cc0480280402df213faa940anthony      return(DestroyKernelInfo(new_kernel));
22421b2bc0a7da432e6e1cc0480280402df213faa940anthony  }
22431b2bc0a7da432e6e1cc0480280402df213faa940anthony
22447a01dcf50ce12cb2a789bedff51e9345f022432eanthony  return(new_kernel);
22454fd27e21043be809d66c8202e779255e5b660d2danthony}
22464fd27e21043be809d66c8202e779255e5b660d2danthony
22474fd27e21043be809d66c8202e779255e5b660d2danthony/*
22484fd27e21043be809d66c8202e779255e5b660d2danthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22494fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
22504fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
22514fd27e21043be809d66c8202e779255e5b660d2danthony%                                                                             %
225283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%     D e s t r o y K e r n e l I n f o                                       %
2253602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2254602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2255602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2256602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2257602ab9b30b644a78a4057da93d838a77391ec0acanthony%
225883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  DestroyKernelInfo() frees the memory used by a Convolution/Morphology
225983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  kernel.
2260602ab9b30b644a78a4057da93d838a77391ec0acanthony%
226183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  The format of the DestroyKernelInfo method is:
2262602ab9b30b644a78a4057da93d838a77391ec0acanthony%
226383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%      KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
2264602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2265602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
2266602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2267602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel: the Morphology/Convolution kernel to be destroyed
2268602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2269602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
227083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthonyMagickExport KernelInfo *DestroyKernelInfo(KernelInfo *kernel)
2271602ab9b30b644a78a4057da93d838a77391ec0acanthony{
22722be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy  assert(kernel != (KernelInfo *) NULL);
22737a01dcf50ce12cb2a789bedff51e9345f022432eanthony  if ( kernel->next != (KernelInfo *) NULL )
22749f752c092332bf2c4e599ea49e9422c13f3fb11bcristy    kernel->next=DestroyKernelInfo(kernel->next);
2275d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy  kernel->values=(MagickRealType *) RelinquishAlignedMemory(kernel->values);
22769f752c092332bf2c4e599ea49e9422c13f3fb11bcristy  kernel=(KernelInfo *) RelinquishMagickMemory(kernel);
2277602ab9b30b644a78a4057da93d838a77391ec0acanthony  return(kernel);
2278602ab9b30b644a78a4057da93d838a77391ec0acanthony}
2279c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony
2280c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony/*
2281c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2282c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2283c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2284c94cdb0501c4bec4f5828f1a585c38f4c374900aanthony%                                                                             %
2285ce0fd95b65b90e1dc307600bb14b346b777e8137anthony+     E x p a n d M i r r o r K e r n e l I n f o                             %
22863c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
22873c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
22883c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
22893c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
22903c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2291bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  ExpandMirrorKernelInfo() takes a single kernel, and expands it into a
2292bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  sequence of 90-degree rotated kernels but providing a reflected 180
2293bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  rotatation, before the -/+ 90-degree rotations.
2294bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2295bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  This special rotation order produces a better, more symetrical thinning of
2296bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  objects.
2297bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2298bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The format of the ExpandMirrorKernelInfo method is:
2299bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2300bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%      void ExpandMirrorKernelInfo(KernelInfo *kernel)
2301bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2302bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  A description of each parameter follows:
2303bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2304bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%    o kernel: the Morphology/Convolution kernel
2305bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2306bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% This function is only internel to this module, as it is not finalized,
2307bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% especially with regard to non-orthogonal angles, and rotation of larger
2308bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony% 2D kernels.
2309bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony*/
2310bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2311bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony#if 0
2312bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void FlopKernelInfo(KernelInfo *kernel)
2313bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    { /* Do a Flop by reversing each row. */
2314bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      size_t
2315bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        y;
2316bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      register ssize_t
2317bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        x,r;
2318bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      register double
2319bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        *k,t;
2320bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2321bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      for ( y=0, k=kernel->values; y < kernel->height; y++, k+=kernel->width)
2322bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony        for ( x=0, r=kernel->width-1; x<kernel->width/2; x++, r--)
2323bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony          t=k[x],  k[x]=k[r],  k[r]=t;
2324bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2325bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      kernel->x = kernel->width - kernel->x - 1;
2326bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony      angle = fmod(angle+180.0, 360.0);
2327bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    }
2328bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony#endif
2329bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2330bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void ExpandMirrorKernelInfo(KernelInfo *kernel)
2331bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony{
2332bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  KernelInfo
2333bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    *clone,
2334bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    *last;
2335bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2336bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = kernel;
2337bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2338bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2339bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 180);   /* flip */
2340bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2341bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = clone;
2342bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2343bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2344bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 90);   /* transpose */
2345bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2346bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  last = clone;
2347bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2348bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = CloneKernelInfo(last);
2349bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  RotateKernelInfo(clone, 180);  /* flop */
2350bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  LastKernelInfo(last)->next = clone;
2351bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2352bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  return;
2353bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony}
2354bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony
2355bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony/*
2356bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2357bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2358bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2359bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2360ce0fd95b65b90e1dc307600bb14b346b777e8137anthony+     E x p a n d R o t a t e K e r n e l I n f o                             %
2361bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2362bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2363bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%                                                                             %
2364bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2365bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%
2366bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  ExpandRotateKernelInfo() takes a kernel list, and expands it by rotating
2367529482f4b494010a13338a74446c510712f670b3anthony%  incrementally by the angle given, until the kernel repeats.
23683c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23693c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  WARNING: 45 degree rotations only works for 3x3 kernels.
23703c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  While 90 degree roatations only works for linear and square kernels
23713c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2372bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%  The format of the ExpandRotateKernelInfo method is:
23733c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
2374bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony%      void ExpandRotateKernelInfo(KernelInfo *kernel, double angle)
23753c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23763c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%  A description of each parameter follows:
23773c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23783c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    o kernel: the Morphology/Convolution kernel
23793c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23803c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%    o angle: angle to rotate in degrees
23813c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%
23823c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% This function is only internel to this module, as it is not finalized,
23833c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% especially with regard to non-orthogonal angles, and rotation of larger
23843c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony% 2D kernels.
23853c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony*/
238647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
238747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony/* Internal Routine - Return true if two kernels are the same */
238847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthonystatic MagickBooleanType SameKernelInfo(const KernelInfo *kernel1,
238947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony     const KernelInfo *kernel2)
239047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony{
2391bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register size_t
239247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    i;
23931d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
23941d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  /* check size and origin location */
23951d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  if (    kernel1->width != kernel2->width
23961d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony       || kernel1->height != kernel2->height
23971d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony       || kernel1->x != kernel2->x
23981d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony       || kernel1->y != kernel2->y )
239947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    return MagickFalse;
24001d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
24011d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony  /* check actual kernel values */
240247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  for (i=0; i < (kernel1->width*kernel1->height); i++) {
2403f0a92fd8deb68d411304359906b12679b675691fglennrp    /* Test for Nan equivalence */
2404a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony    if ( IfNaN(kernel1->values[i]) && !IfNaN(kernel2->values[i]) )
240547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      return MagickFalse;
2406a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony    if ( IfNaN(kernel2->values[i]) && !IfNaN(kernel1->values[i]) )
240747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      return MagickFalse;
2408f0a92fd8deb68d411304359906b12679b675691fglennrp    /* Test actual values are equivalent */
2409b978e458a8e1f210bcb580951cf623687236b2fecristy    if ( fabs(kernel1->values[i] - kernel2->values[i]) >= MagickEpsilon )
241047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      return MagickFalse;
241147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  }
24121d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony
241347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  return MagickTrue;
241447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony}
241547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
2416bfb635a40fc92bfcbdf36c018a9e64a9182d809canthonystatic void ExpandRotateKernelInfo(KernelInfo *kernel, const double angle)
24173c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony{
24183c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  KernelInfo
241984d9b5596c0900609dea18795861e2b0936b21c6cristy    *clone,
24203c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    *last;
2421a9a61ad96c5112acd968f97b689bd42ca392d70bcristy
24223c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  last = kernel;
242347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  while(1) {
242484d9b5596c0900609dea18795861e2b0936b21c6cristy    clone = CloneKernelInfo(last);
242584d9b5596c0900609dea18795861e2b0936b21c6cristy    RotateKernelInfo(clone, angle);
242684d9b5596c0900609dea18795861e2b0936b21c6cristy    if ( SameKernelInfo(kernel, clone) == MagickTrue )
242747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
2428bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony    LastKernelInfo(last)->next = clone;
242984d9b5596c0900609dea18795861e2b0936b21c6cristy    last = clone;
24303c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  }
2431bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony  clone = DestroyKernelInfo(clone); /* kernel has repeated - junk the clone */
243247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  return;
24333c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony}
24343c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony
24353c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony/*
24363c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
24373c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
24383c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
24393c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony%                                                                             %
244046a369d839971ab627bdb31a93d8bd63e81b65a3anthony+     C a l c M e t a K e r n a l I n f o                                     %
244146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
244246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
244346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
244446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
244546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
244646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  CalcKernelMetaData() recalculate the KernelInfo meta-data of this kernel only,
2447dcabf6d5fddb35c0eedd7a7638c102f16838c4e6glennrp%  using the kernel values.  This should only ne used if it is not possible to
244846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  calculate that meta-data in some easier way.
244946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  It is important that the meta-data is correct before ScaleKernelInfo() is
245146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  used to perform kernel normalization.
245246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The format of the CalcKernelMetaData method is:
245446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      void CalcKernelMetaData(KernelInfo *kernel, const double scale )
245646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  A description of each parameter follows:
245846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
245946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o kernel: the Morphology/Convolution kernel to modify
246046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
246146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  WARNING: Minimum and Maximum values are assumed to include zero, even if
246246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  zero is not part of the kernel (as in Gaussian Derived kernels). This
246346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  however is not true for flat-shaped morphological kernels.
246446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
246546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  WARNING: Only the specific kernel pointed to is modified, not a list of
246646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  multiple kernels.
246746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
246846a369d839971ab627bdb31a93d8bd63e81b65a3anthony% This is an internal function and not expected to be useful outside this
246946a369d839971ab627bdb31a93d8bd63e81b65a3anthony% module.  This could change however.
247046a369d839971ab627bdb31a93d8bd63e81b65a3anthony*/
247146a369d839971ab627bdb31a93d8bd63e81b65a3anthonystatic void CalcKernelMetaData(KernelInfo *kernel)
247246a369d839971ab627bdb31a93d8bd63e81b65a3anthony{
2473bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register size_t
247446a369d839971ab627bdb31a93d8bd63e81b65a3anthony    i;
247546a369d839971ab627bdb31a93d8bd63e81b65a3anthony
247646a369d839971ab627bdb31a93d8bd63e81b65a3anthony  kernel->minimum = kernel->maximum = 0.0;
247746a369d839971ab627bdb31a93d8bd63e81b65a3anthony  kernel->negative_range = kernel->positive_range = 0.0;
247846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  for (i=0; i < (kernel->width*kernel->height); i++)
247946a369d839971ab627bdb31a93d8bd63e81b65a3anthony    {
248046a369d839971ab627bdb31a93d8bd63e81b65a3anthony      if ( fabs(kernel->values[i]) < MagickEpsilon )
248146a369d839971ab627bdb31a93d8bd63e81b65a3anthony        kernel->values[i] = 0.0;
248246a369d839971ab627bdb31a93d8bd63e81b65a3anthony      ( kernel->values[i] < 0)
248346a369d839971ab627bdb31a93d8bd63e81b65a3anthony          ?  ( kernel->negative_range += kernel->values[i] )
248446a369d839971ab627bdb31a93d8bd63e81b65a3anthony          :  ( kernel->positive_range += kernel->values[i] );
248546a369d839971ab627bdb31a93d8bd63e81b65a3anthony      Minimize(kernel->minimum, kernel->values[i]);
248646a369d839971ab627bdb31a93d8bd63e81b65a3anthony      Maximize(kernel->maximum, kernel->values[i]);
248746a369d839971ab627bdb31a93d8bd63e81b65a3anthony    }
248846a369d839971ab627bdb31a93d8bd63e81b65a3anthony
248946a369d839971ab627bdb31a93d8bd63e81b65a3anthony  return;
249046a369d839971ab627bdb31a93d8bd63e81b65a3anthony}
249146a369d839971ab627bdb31a93d8bd63e81b65a3anthony
249246a369d839971ab627bdb31a93d8bd63e81b65a3anthony/*
249346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
249446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
249546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
249646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
24979eb4f74649b23c053b308ce1152dce51239450baanthony%     M o r p h o l o g y A p p l y                                           %
2498602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2499602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2500602ab9b30b644a78a4057da93d838a77391ec0acanthony%                                                                             %
2501602ab9b30b644a78a4057da93d838a77391ec0acanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2502602ab9b30b644a78a4057da93d838a77391ec0acanthony%
25039eb4f74649b23c053b308ce1152dce51239450baanthony%  MorphologyApply() applies a morphological method, multiple times using
250422de2722b682eb405b60ec6022a7546df994674eanthony%  a list of multiple kernels.  This is the method that should be called by
250522de2722b682eb405b60ec6022a7546df994674eanthony%  other 'operators' that internally use morphology operations as part of
250622de2722b682eb405b60ec6022a7546df994674eanthony%  their processing.
2507602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2508ec9ace44c23c302f336f6e672f6857312747d29bcristy%  It is basically equivalent to as MorphologyImage() (see below) but without
2509ec9ace44c23c302f336f6e672f6857312747d29bcristy%  any user controls.  This allows internel programs to use this method to
2510ec9ace44c23c302f336f6e672f6857312747d29bcristy%  perform a specific task without possible interference by any API user
2511ec9ace44c23c302f336f6e672f6857312747d29bcristy%  supplied settings.
2512e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%
2513f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%  It is MorphologyImage() task to extract any such user controls, and
2514e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony%  pass them to this function for processing.
25159eb4f74649b23c053b308ce1152dce51239450baanthony%
251622de2722b682eb405b60ec6022a7546df994674eanthony%  More specifically all given kernels should already be scaled, normalised,
251722de2722b682eb405b60ec6022a7546df994674eanthony%  and blended appropriatally before being parred to this routine. The
251822de2722b682eb405b60ec6022a7546df994674eanthony%  appropriate bias, and compose (typically 'UndefinedComposeOp') given.
2519602ab9b30b644a78a4057da93d838a77391ec0acanthony%
252047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%  The format of the MorphologyApply method is:
2521602ab9b30b644a78a4057da93d838a77391ec0acanthony%
25229eb4f74649b23c053b308ce1152dce51239450baanthony%      Image *MorphologyApply(const Image *image,MorphologyMethod method,
2523f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%        const ssize_t iterations,const KernelInfo *kernel,
2524f46d42620631d2581e0b6a56456e203e17c427c8anthony%        const CompositeMethod compose,const double bias,
2525f46d42620631d2581e0b6a56456e203e17c427c8anthony%        ExceptionInfo *exception)
2526602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2527602ab9b30b644a78a4057da93d838a77391ec0acanthony%  A description of each parameter follows:
2528602ab9b30b644a78a4057da93d838a77391ec0acanthony%
25298d18850dee4bed193a64866a6d2353eeeb73e145anthony%    o image: the source image
2530602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2531602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o method: the morphology method to be applied.
2532602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2533602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o iterations: apply the operation this many times (or no change).
2534602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  A value of -1 means loop until no change found.
2535602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  How this is applied may depend on the morphology method.
2536602ab9b30b644a78a4057da93d838a77391ec0acanthony%                  Typically this is a value of 1.
2537602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2538602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o channel: the channel type.
2539602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2540602ab9b30b644a78a4057da93d838a77391ec0acanthony%    o kernel: An array of double representing the morphology kernel.
2541602ab9b30b644a78a4057da93d838a77391ec0acanthony%
254247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%    o compose: How to handle or merge multi-kernel results.
25438d18850dee4bed193a64866a6d2353eeeb73e145anthony%          If 'UndefinedCompositeOp' use default for the Morphology method.
25448d18850dee4bed193a64866a6d2353eeeb73e145anthony%          If 'NoCompositeOp' force image to be re-iterated by each kernel.
25458d18850dee4bed193a64866a6d2353eeeb73e145anthony%          Otherwise merge the results using the compose method given.
254647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony%
2547f46d42620631d2581e0b6a56456e203e17c427c8anthony%    o bias: Convolution Output Bias.
2548f46d42620631d2581e0b6a56456e203e17c427c8anthony%
25499eb4f74649b23c053b308ce1152dce51239450baanthony%    o exception: return any errors or warnings in this structure.
2550602ab9b30b644a78a4057da93d838a77391ec0acanthony%
2551602ab9b30b644a78a4057da93d838a77391ec0acanthony*/
2552f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristystatic ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
2553f46d42620631d2581e0b6a56456e203e17c427c8anthony  const MorphologyMethod method,const KernelInfo *kernel,const double bias,
2554f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  ExceptionInfo *exception)
2555602ab9b30b644a78a4057da93d838a77391ec0acanthony{
25562be1538b17a2b0ca9a0bd532b8ff2c2813114e24cristy#define MorphologyTag  "Morphology/Image"
2557602ab9b30b644a78a4057da93d838a77391ec0acanthony
25585f959473f334e196c6bf39b740c12cb4963fceebcristy  CacheView
25594c08aed51c5899665ade97263692328eea4af106cristy    *image_view,
25604c08aed51c5899665ade97263692328eea4af106cristy    *morphology_view;
25615f959473f334e196c6bf39b740c12cb4963fceebcristy
2562ec9ace44c23c302f336f6e672f6857312747d29bcristy  OffsetInfo
2563ec9ace44c23c302f336f6e672f6857312747d29bcristy    offset;
2564ec9ace44c23c302f336f6e672f6857312747d29bcristy
2565c5876076825b539bd230ddadab1d876805c18291cristy  register ssize_t
2566c5876076825b539bd230ddadab1d876805c18291cristy    i;
2567c5876076825b539bd230ddadab1d876805c18291cristy
2568bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  ssize_t
2569ec9ace44c23c302f336f6e672f6857312747d29bcristy    y;
2570a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
2571a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  size_t
2572306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy    *changes,
2573306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy    changed,
2574306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy    width;
2575602ab9b30b644a78a4057da93d838a77391ec0acanthony
2576602ab9b30b644a78a4057da93d838a77391ec0acanthony  MagickBooleanType
2577602ab9b30b644a78a4057da93d838a77391ec0acanthony    status;
2578602ab9b30b644a78a4057da93d838a77391ec0acanthony
25795f959473f334e196c6bf39b740c12cb4963fceebcristy  MagickOffsetType
25805f959473f334e196c6bf39b740c12cb4963fceebcristy    progress;
2581602ab9b30b644a78a4057da93d838a77391ec0acanthony
2582e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(image != (Image *) NULL);
2583e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(image->signature == MagickSignature);
25844c08aed51c5899665ade97263692328eea4af106cristy  assert(morphology_image != (Image *) NULL);
25854c08aed51c5899665ade97263692328eea4af106cristy  assert(morphology_image->signature == MagickSignature);
2586e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(kernel != (KernelInfo *) NULL);
2587e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(kernel->signature == MagickSignature);
2588e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(exception != (ExceptionInfo *) NULL);
2589e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony  assert(exception->signature == MagickSignature);
2590602ab9b30b644a78a4057da93d838a77391ec0acanthony  status=MagickTrue;
2591602ab9b30b644a78a4057da93d838a77391ec0acanthony  progress=0;
259246ff2676b1044ea4101ac7a59b83289cd8f6cfdacristy  image_view=AcquireVirtualCacheView(image,exception);
259346ff2676b1044ea4101ac7a59b83289cd8f6cfdacristy  morphology_view=AcquireAuthenticCacheView(morphology_image,exception);
2594ec9ace44c23c302f336f6e672f6857312747d29bcristy  width=image->columns+kernel->width-1;
2595ec9ace44c23c302f336f6e672f6857312747d29bcristy  switch (method)
2596ec9ace44c23c302f336f6e672f6857312747d29bcristy  {
2597930be614b4595b97cd79ee864a394796740f76adanthony    case ConvolveMorphology:
2598930be614b4595b97cd79ee864a394796740f76adanthony    case DilateMorphology:
2599930be614b4595b97cd79ee864a394796740f76adanthony    case DilateIntensityMorphology:
2600f34d9b2df49a407af764c79e07d587af0600983aanthony    case IterativeDistanceMorphology:
2601ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
2602ec9ace44c23c302f336f6e672f6857312747d29bcristy      /*
2603ec9ace44c23c302f336f6e672f6857312747d29bcristy        Kernel needs to used with reflection about origin.
2604ec9ace44c23c302f336f6e672f6857312747d29bcristy      */
2605ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.x=(ssize_t) kernel->width-kernel->x-1;
2606ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y=(ssize_t) kernel->height-kernel->y-1;
260729188a8682a98d4b7882cca434b170517555fc7danthony      break;
2608ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
26095ef8e94ff55717be2387d537bd49025780a1a558anthony    case ErodeMorphology:
26105ef8e94ff55717be2387d537bd49025780a1a558anthony    case ErodeIntensityMorphology:
26115ef8e94ff55717be2387d537bd49025780a1a558anthony    case HitAndMissMorphology:
26125ef8e94ff55717be2387d537bd49025780a1a558anthony    case ThinningMorphology:
26135ef8e94ff55717be2387d537bd49025780a1a558anthony    case ThickenMorphology:
2614ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
2615ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.x=kernel->x;
2616ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y=kernel->y;
26175ef8e94ff55717be2387d537bd49025780a1a558anthony      break;
2618ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
2619930be614b4595b97cd79ee864a394796740f76adanthony    default:
2620ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
26219eb4f74649b23c053b308ce1152dce51239450baanthony      assert("Not a Primitive Morphology Method" != (char *) NULL);
2622930be614b4595b97cd79ee864a394796740f76adanthony      break;
2623ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
262429188a8682a98d4b7882cca434b170517555fc7danthony  }
2625c5876076825b539bd230ddadab1d876805c18291cristy  changed=0;
2626306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy  changes=(size_t *) AcquireQuantumMemory(GetOpenMPMaximumThreads(),
2627306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy    sizeof(*changes));
2628306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy  if (changes == (size_t *) NULL)
2629306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy    ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
2630c5876076825b539bd230ddadab1d876805c18291cristy  for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
2631c5876076825b539bd230ddadab1d876805c18291cristy    changes[i]=0;
2632ec9ace44c23c302f336f6e672f6857312747d29bcristy  if ((method == ConvolveMorphology) && (kernel->width == 1))
2633780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy    {
2634c5876076825b539bd230ddadab1d876805c18291cristy      const int
2635c5876076825b539bd230ddadab1d876805c18291cristy        id = GetOpenMPThreadId();
2636c5876076825b539bd230ddadab1d876805c18291cristy
2637780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      register ssize_t
2638780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        x;
2639fd1175952254cf1ac848ddb441e483c5e33d517fcristy
2640780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      /*
2641ec9ace44c23c302f336f6e672f6857312747d29bcristy        Special handling (for speed) of vertical (blur) kernels.  This performs
2642ec9ace44c23c302f336f6e672f6857312747d29bcristy        its handling in columns rather than in rows.  This is only done
2643ec9ace44c23c302f336f6e672f6857312747d29bcristy        for convolve as it is the only method that generates very large 1-D
2644ec9ace44c23c302f336f6e672f6857312747d29bcristy        vertical kernels (such as a 'BlurKernel')
2645780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy     */
26468d18850dee4bed193a64866a6d2353eeeb73e145anthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
2647c5876076825b539bd230ddadab1d876805c18291cristy     #pragma omp parallel for schedule(static,4) shared(progress,status) \
2648780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy       magick_threads(image,morphology_image,image->columns,1)
26498d18850dee4bed193a64866a6d2353eeeb73e145anthony#endif
2650780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      for (x=0; x < (ssize_t) image->columns; x++)
2651780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      {
2652780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        register const Quantum
2653780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          *restrict p;
26548d18850dee4bed193a64866a6d2353eeeb73e145anthony
2655780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        register Quantum
2656780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          *restrict q;
26578d18850dee4bed193a64866a6d2353eeeb73e145anthony
2658780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        register ssize_t
2659780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          y;
26608d18850dee4bed193a64866a6d2353eeeb73e145anthony
2661780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        ssize_t
2662780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          center;
26638d18850dee4bed193a64866a6d2353eeeb73e145anthony
2664780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        if (status == MagickFalse)
26658d18850dee4bed193a64866a6d2353eeeb73e145anthony          continue;
2666ec9ace44c23c302f336f6e672f6857312747d29bcristy        p=GetCacheViewVirtualPixels(image_view,x,-offset.y,1,image->rows+
2667780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          kernel->height-1,exception);
2668780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        q=GetCacheViewAuthenticPixels(morphology_view,x,0,1,
2669780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          morphology_image->rows,exception);
2670780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2671780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          {
2672780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            status=MagickFalse;
2673780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            continue;
2674780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          }
2675ec9ace44c23c302f336f6e672f6857312747d29bcristy        center=(ssize_t) GetPixelChannels(image)*offset.y;
2676780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        for (y=0; y < (ssize_t) image->rows; y++)
2677f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy        {
2678780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          register ssize_t
2679780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            i;
26808d18850dee4bed193a64866a6d2353eeeb73e145anthony
2681780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2682780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          {
2683780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            double
2684780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              alpha,
2685780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              pixel;
26868d18850dee4bed193a64866a6d2353eeeb73e145anthony
2687780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            PixelChannel
2688780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              channel;
2689d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy
2690780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            PixelTrait
2691780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              morphology_traits,
2692780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              traits;
26938d18850dee4bed193a64866a6d2353eeeb73e145anthony
2694780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            register const MagickRealType
2695780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              *restrict k;
26968d18850dee4bed193a64866a6d2353eeeb73e145anthony
2697780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            register const Quantum
2698780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              *restrict pixels;
26998d18850dee4bed193a64866a6d2353eeeb73e145anthony
2700780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            register ssize_t
2701780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              u;
2702f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy
2703780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            ssize_t
2704780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              v;
2705780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy
2706780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            channel=GetPixelChannelChannel(image,i);
2707780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            traits=GetPixelChannelTraits(image,channel);
2708780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            morphology_traits=GetPixelChannelTraits(morphology_image,channel);
2709780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            if ((traits == UndefinedPixelTrait) ||
2710780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                (morphology_traits == UndefinedPixelTrait))
2711f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy              continue;
2712780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            if (((morphology_traits & CopyPixelTrait) != 0) ||
2713883fde11debec15cedb05dc5d7228d8588066bc0cristy                (GetPixelReadMask(image,p+center) == 0))
2714f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy              {
2715780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                SetPixelChannel(morphology_image,channel,p[center+i],q);
2716780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                continue;
2717780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              }
2718ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
2719780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            pixels=p;
2720780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            pixel=bias;
2721780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            if ((morphology_traits & BlendPixelTrait) == 0)
2722780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              {
2723780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                /*
2724780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  No alpha blending.
2725780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                */
2726780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                for (v=0; v < (ssize_t) kernel->height; v++)
2727f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy                {
2728780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  for (u=0; u < (ssize_t) kernel->width; u++)
2729780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  {
2730a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                    if (IfNaN(*k) == MagickFalse)
2731a62cd0834ed954440c88fb0772413489f1cbd0afcristy                      pixel+=(*k)*pixels[i];
2732780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                    k--;
2733780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                    pixels+=GetPixelChannels(image);
2734780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  }
2735f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy                }
2736780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                if (fabs(pixel-p[center+i]) > MagickEpsilon)
2737c5876076825b539bd230ddadab1d876805c18291cristy                  changes[id]++;
2738780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                SetPixelChannel(morphology_image,channel,ClampToQuantum(pixel),
2739780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                  q);
2740780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                continue;
2741f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy              }
2742780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            /*
2743780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              Alpha blending.
2744780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            */
274533c51e0d3335ba3b35248454fe61c1805f65a115cristy            for (v=0; v < (ssize_t) kernel->height; v++)
2746f21bb67d868cc29539071a3489e0dd5cf1d7f8c4cristy            {
2747780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              for (u=0; u < (ssize_t) kernel->width; u++)
2748780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              {
2749a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                if (IfNaN(*k) == MagickFalse)
2750a62cd0834ed954440c88fb0772413489f1cbd0afcristy                  {
2751a62cd0834ed954440c88fb0772413489f1cbd0afcristy                    alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
2752a62cd0834ed954440c88fb0772413489f1cbd0afcristy                    pixel+=(*k)*alpha*pixels[i];
2753a62cd0834ed954440c88fb0772413489f1cbd0afcristy                  }
2754780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                k--;
2755780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy                pixels+=GetPixelChannels(image);
2756780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              }
27578d18850dee4bed193a64866a6d2353eeeb73e145anthony            }
2758780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            if (fabs(pixel-p[center+i]) > MagickEpsilon)
2759c5876076825b539bd230ddadab1d876805c18291cristy              changes[id]++;
2760780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            SetPixelChannel(morphology_image,channel,ClampToQuantum(pixel),q);
27618d18850dee4bed193a64866a6d2353eeeb73e145anthony          }
2762780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          p+=GetPixelChannels(image);
2763780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          q+=GetPixelChannels(morphology_image);
2764ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
2765780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
2766780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          status=MagickFalse;
2767780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy        if (image->progress_monitor != (MagickProgressMonitor) NULL)
2768780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          {
2769780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            MagickBooleanType
2770780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              proceed;
27718d18850dee4bed193a64866a6d2353eeeb73e145anthony
27728d18850dee4bed193a64866a6d2353eeeb73e145anthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
2773954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy            #pragma omp critical (MagickCore_MorphologyPrimitive)
27748d18850dee4bed193a64866a6d2353eeeb73e145anthony#endif
2775ec9ace44c23c302f336f6e672f6857312747d29bcristy            proceed=SetImageProgress(image,MorphologyTag,progress++,
2776ec9ace44c23c302f336f6e672f6857312747d29bcristy              image->rows);
2777780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy            if (proceed == MagickFalse)
2778780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy              status=MagickFalse;
2779780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy          }
2780780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      }
2781780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      morphology_image->type=image->type;
2782780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      morphology_view=DestroyCacheView(morphology_view);
2783780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      image_view=DestroyCacheView(image_view);
2784c5876076825b539bd230ddadab1d876805c18291cristy      for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
2785c5876076825b539bd230ddadab1d876805c18291cristy        changed+=changes[i];
2786306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy      changes=(size_t *) RelinquishMagickMemory(changes);
2787780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy      return(status ? (ssize_t) changed : 0);
2788780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy    }
27898d18850dee4bed193a64866a6d2353eeeb73e145anthony  /*
2790780ad21bc9eb5167cfdcc52daabf4d3d83b26248cristy    Normal handling of horizontal or rectangular kernels (row by row).
27918d18850dee4bed193a64866a6d2353eeeb73e145anthony  */
2792602ab9b30b644a78a4057da93d838a77391ec0acanthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
2793c5876076825b539bd230ddadab1d876805c18291cristy  #pragma omp parallel for schedule(static,4) shared(progress,status) \
27945e6b259130f9dbe0da4666f734937017babe573acristy    magick_threads(image,morphology_image,image->rows,1)
2795602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
2796bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (y=0; y < (ssize_t) image->rows; y++)
2797602ab9b30b644a78a4057da93d838a77391ec0acanthony  {
2798c5876076825b539bd230ddadab1d876805c18291cristy    const int
2799c5876076825b539bd230ddadab1d876805c18291cristy      id = GetOpenMPThreadId();
2800c5876076825b539bd230ddadab1d876805c18291cristy
28014c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
2802602ab9b30b644a78a4057da93d838a77391ec0acanthony      *restrict p;
2803602ab9b30b644a78a4057da93d838a77391ec0acanthony
28044c08aed51c5899665ade97263692328eea4af106cristy    register Quantum
2805602ab9b30b644a78a4057da93d838a77391ec0acanthony      *restrict q;
2806602ab9b30b644a78a4057da93d838a77391ec0acanthony
2807bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    register ssize_t
2808602ab9b30b644a78a4057da93d838a77391ec0acanthony      x;
2809602ab9b30b644a78a4057da93d838a77391ec0acanthony
2810ec9ace44c23c302f336f6e672f6857312747d29bcristy    ssize_t
2811ec9ace44c23c302f336f6e672f6857312747d29bcristy      center;
2812602ab9b30b644a78a4057da93d838a77391ec0acanthony
2813602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (status == MagickFalse)
2814602ab9b30b644a78a4057da93d838a77391ec0acanthony      continue;
2815ec9ace44c23c302f336f6e672f6857312747d29bcristy    p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,
2816af43471dfedff5d1314cd674fac6508d230df1bccristy      kernel->height,exception);
2817af43471dfedff5d1314cd674fac6508d230df1bccristy    q=GetCacheViewAuthenticPixels(morphology_view,0,y,morphology_image->columns,
2818af43471dfedff5d1314cd674fac6508d230df1bccristy      1,exception);
28194c08aed51c5899665ade97263692328eea4af106cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
2820602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
2821602ab9b30b644a78a4057da93d838a77391ec0acanthony        status=MagickFalse;
2822602ab9b30b644a78a4057da93d838a77391ec0acanthony        continue;
2823602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
2824ec9ace44c23c302f336f6e672f6857312747d29bcristy    center=(ssize_t) (GetPixelChannels(image)*width*offset.y+
2825ec9ace44c23c302f336f6e672f6857312747d29bcristy      GetPixelChannels(image)*offset.x);
2826bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy    for (x=0; x < (ssize_t) image->columns; x++)
2827602ab9b30b644a78a4057da93d838a77391ec0acanthony    {
2828ec9ace44c23c302f336f6e672f6857312747d29bcristy      register ssize_t
2829ec9ace44c23c302f336f6e672f6857312747d29bcristy        i;
2830602ab9b30b644a78a4057da93d838a77391ec0acanthony
2831ec9ace44c23c302f336f6e672f6857312747d29bcristy      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
2832ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
2833ec9ace44c23c302f336f6e672f6857312747d29bcristy        double
2834ec9ace44c23c302f336f6e672f6857312747d29bcristy          alpha,
2835ec9ace44c23c302f336f6e672f6857312747d29bcristy          maximum,
2836ec9ace44c23c302f336f6e672f6857312747d29bcristy          minimum,
2837ec9ace44c23c302f336f6e672f6857312747d29bcristy          pixel;
2838602ab9b30b644a78a4057da93d838a77391ec0acanthony
2839ec9ace44c23c302f336f6e672f6857312747d29bcristy        PixelChannel
2840ec9ace44c23c302f336f6e672f6857312747d29bcristy          channel;
2841602ab9b30b644a78a4057da93d838a77391ec0acanthony
2842ec9ace44c23c302f336f6e672f6857312747d29bcristy        PixelTrait
2843ec9ace44c23c302f336f6e672f6857312747d29bcristy          morphology_traits,
2844ec9ace44c23c302f336f6e672f6857312747d29bcristy          traits;
2845ec9ace44c23c302f336f6e672f6857312747d29bcristy
2846ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const MagickRealType
2847ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict k;
2848ec9ace44c23c302f336f6e672f6857312747d29bcristy
2849ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const Quantum
2850ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict pixels;
2851ec9ace44c23c302f336f6e672f6857312747d29bcristy
2852ec9ace44c23c302f336f6e672f6857312747d29bcristy        register ssize_t
2853ec9ace44c23c302f336f6e672f6857312747d29bcristy          u;
2854602ab9b30b644a78a4057da93d838a77391ec0acanthony
2855ec9ace44c23c302f336f6e672f6857312747d29bcristy        ssize_t
2856ec9ace44c23c302f336f6e672f6857312747d29bcristy          v;
2857ec9ace44c23c302f336f6e672f6857312747d29bcristy
2858ec9ace44c23c302f336f6e672f6857312747d29bcristy        channel=GetPixelChannelChannel(image,i);
2859ec9ace44c23c302f336f6e672f6857312747d29bcristy        traits=GetPixelChannelTraits(image,channel);
2860ec9ace44c23c302f336f6e672f6857312747d29bcristy        morphology_traits=GetPixelChannelTraits(morphology_image,channel);
2861ec9ace44c23c302f336f6e672f6857312747d29bcristy        if ((traits == UndefinedPixelTrait) ||
2862ec9ace44c23c302f336f6e672f6857312747d29bcristy            (morphology_traits == UndefinedPixelTrait))
2863ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
2864ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (((morphology_traits & CopyPixelTrait) != 0) ||
2865883fde11debec15cedb05dc5d7228d8588066bc0cristy            (GetPixelReadMask(image,p+center) == 0))
2866ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2867ec9ace44c23c302f336f6e672f6857312747d29bcristy            SetPixelChannel(morphology_image,channel,p[center+i],q);
2868ec9ace44c23c302f336f6e672f6857312747d29bcristy            continue;
2869ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
2870ec9ace44c23c302f336f6e672f6857312747d29bcristy        pixels=p;
2871ec9ace44c23c302f336f6e672f6857312747d29bcristy        maximum=0.0;
2872ec9ace44c23c302f336f6e672f6857312747d29bcristy        minimum=(double) QuantumRange;
2873ec9ace44c23c302f336f6e672f6857312747d29bcristy        switch (method)
2874ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
2875ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ConvolveMorphology: pixel=bias; break;
2876ec9ace44c23c302f336f6e672f6857312747d29bcristy          case HitAndMissMorphology: pixel=(double) QuantumRange; break;
2877ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ThinningMorphology: pixel=(double) QuantumRange; break;
2878ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ThickenMorphology: pixel=(double) QuantumRange; break;
2879ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ErodeMorphology: pixel=(double) QuantumRange; break;
2880ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DilateMorphology: pixel=0.0; break;
2881ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ErodeIntensityMorphology:
2882ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DilateIntensityMorphology:
2883ec9ace44c23c302f336f6e672f6857312747d29bcristy          case IterativeDistanceMorphology:
2884ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2885ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixel=(double) p[center+i];
2886ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
2887ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
2888ec9ace44c23c302f336f6e672f6857312747d29bcristy          default: pixel=0; break;
2889ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
2890ec9ace44c23c302f336f6e672f6857312747d29bcristy        switch (method)
2891ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
2892ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ConvolveMorphology:
2893ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2894ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
2895ec9ace44c23c302f336f6e672f6857312747d29bcristy               Weighted Average of pixels using reflected kernel
2896ec9ace44c23c302f336f6e672f6857312747d29bcristy
2897ec9ace44c23c302f336f6e672f6857312747d29bcristy               For correct working of this operation for asymetrical
2898ec9ace44c23c302f336f6e672f6857312747d29bcristy               kernels, the kernel needs to be applied in its reflected form.
2899ec9ace44c23c302f336f6e672f6857312747d29bcristy               That is its values needs to be reversed.
2900ec9ace44c23c302f336f6e672f6857312747d29bcristy
2901ec9ace44c23c302f336f6e672f6857312747d29bcristy               Correlation is actually the same as this but without reflecting
2902ec9ace44c23c302f336f6e672f6857312747d29bcristy               the kernel, and thus 'lower-level' that Convolution.  However
2903ec9ace44c23c302f336f6e672f6857312747d29bcristy               as Convolution is the more common method used, and it does not
2904ec9ace44c23c302f336f6e672f6857312747d29bcristy               really cost us much in terms of processing to use a reflected
2905ec9ace44c23c302f336f6e672f6857312747d29bcristy               kernel, so it is Convolution that is implemented.
2906ec9ace44c23c302f336f6e672f6857312747d29bcristy
2907ec9ace44c23c302f336f6e672f6857312747d29bcristy               Correlation will have its kernel reflected before calling
2908ec9ace44c23c302f336f6e672f6857312747d29bcristy               this function to do a Convolve.
2909ec9ace44c23c302f336f6e672f6857312747d29bcristy
2910ec9ace44c23c302f336f6e672f6857312747d29bcristy               For more details of Correlation vs Convolution see
2911ec9ace44c23c302f336f6e672f6857312747d29bcristy                 http://www.cs.umd.edu/~djacobs/CMSC426/Convolution.pdf
2912930be614b4595b97cd79ee864a394796740f76adanthony            */
2913ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
2914ec9ace44c23c302f336f6e672f6857312747d29bcristy            if ((morphology_traits & BlendPixelTrait) == 0)
2915ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
2916ec9ace44c23c302f336f6e672f6857312747d29bcristy                /*
2917ec9ace44c23c302f336f6e672f6857312747d29bcristy                  No alpha blending.
29188d18850dee4bed193a64866a6d2353eeeb73e145anthony                */
2919ec9ace44c23c302f336f6e672f6857312747d29bcristy                for (v=0; v < (ssize_t) kernel->height; v++)
2920ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
2921ec9ace44c23c302f336f6e672f6857312747d29bcristy                  for (u=0; u < (ssize_t) kernel->width; u++)
2922ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
2923a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                    if (IfNaN(*k) == MagickFalse)
292413ffe4b75e0c768d1e6506c4d5e5881bb9124763cristy                      pixel+=(*k)*pixels[i];
2925ec9ace44c23c302f336f6e672f6857312747d29bcristy                    k--;
2926ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixels+=GetPixelChannels(image);
29278d18850dee4bed193a64866a6d2353eeeb73e145anthony                  }
2928b772effec6594fbd2b304bb685cfa6226f48780bcristy                  pixels+=(image->columns-1)*GetPixelChannels(image);
29298d18850dee4bed193a64866a6d2353eeeb73e145anthony                }
29308693d3f324310fc2ca933cd239aa053d0651e0b6cristy                break;
29318d18850dee4bed193a64866a6d2353eeeb73e145anthony              }
2932ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
2933ec9ace44c23c302f336f6e672f6857312747d29bcristy              Alpha blending.
2934ec9ace44c23c302f336f6e672f6857312747d29bcristy            */
293533c51e0d3335ba3b35248454fe61c1805f65a115cristy            for (v=0; v < (ssize_t) kernel->height; v++)
2936ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
2937ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
2938ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
2939a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                if (IfNaN(*k) == MagickFalse)
2940ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
2941ec9ace44c23c302f336f6e672f6857312747d29bcristy                    alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
2942ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel+=(*k)*alpha*pixels[i];
2943602ab9b30b644a78a4057da93d838a77391ec0acanthony                  }
2944ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
2945ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
2946602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
2947b772effec6594fbd2b304bb685cfa6226f48780bcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
2948ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
2949602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
2950ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
2951ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ErodeMorphology:
2952ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2953ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
2954ec9ace44c23c302f336f6e672f6857312747d29bcristy              Minimum value within kernel neighbourhood.
2955602ab9b30b644a78a4057da93d838a77391ec0acanthony
2956ec9ace44c23c302f336f6e672f6857312747d29bcristy              The kernel is not reflected for this operation.  In normal
2957ec9ace44c23c302f336f6e672f6857312747d29bcristy              Greyscale Morphology, the kernel value should be added
2958ec9ace44c23c302f336f6e672f6857312747d29bcristy              to the real value, this is currently not done, due to the
2959ec9ace44c23c302f336f6e672f6857312747d29bcristy              nature of the boolean kernels being used.
2960930be614b4595b97cd79ee864a394796740f76adanthony            */
2961ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=kernel->values;
2962ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
2963ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
2964ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
2965ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
2966a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                if ((IfNaN(*k) == MagickFalse) && (*k >= 0.5))
2967ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
2968ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((double) pixels[i] < pixel)
2969ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i];
2970ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
2971ec9ace44c23c302f336f6e672f6857312747d29bcristy                k++;
2972ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
2973602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
2974b772effec6594fbd2b304bb685cfa6226f48780bcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
2975602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
2976602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
2977ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
2978ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DilateMorphology:
2979ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
2980ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
2981ec9ace44c23c302f336f6e672f6857312747d29bcristy               Maximum value within kernel neighbourhood.
2982ec9ace44c23c302f336f6e672f6857312747d29bcristy
2983ec9ace44c23c302f336f6e672f6857312747d29bcristy               For correct working of this operation for asymetrical kernels,
2984ec9ace44c23c302f336f6e672f6857312747d29bcristy               the kernel needs to be applied in its reflected form.  That is
2985ec9ace44c23c302f336f6e672f6857312747d29bcristy               its values needs to be reversed.
2986602ab9b30b644a78a4057da93d838a77391ec0acanthony
2987ec9ace44c23c302f336f6e672f6857312747d29bcristy               In normal Greyscale Morphology, the kernel value should be
2988ec9ace44c23c302f336f6e672f6857312747d29bcristy               added to the real value, this is currently not done, due to the
2989ec9ace44c23c302f336f6e672f6857312747d29bcristy               nature of the boolean kernels being used.
2990930be614b4595b97cd79ee864a394796740f76adanthony            */
2991ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
2992ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
2993ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
2994ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
2995ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
2996a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                if ((IfNaN(*k) == MagickFalse) && (*k > 0.5))
2997ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
2998ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((double) pixels[i] > pixel)
2999ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i];
3000ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3001ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3002ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3003602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3004b772effec6594fbd2b304bb685cfa6226f48780bcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3005602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3006602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3007ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3008ec9ace44c23c302f336f6e672f6857312747d29bcristy          case HitAndMissMorphology:
3009ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ThinningMorphology:
3010ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ThickenMorphology:
3011ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3012ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
3013ec9ace44c23c302f336f6e672f6857312747d29bcristy               Minimum of foreground pixel minus maxumum of background pixels.
3014602ab9b30b644a78a4057da93d838a77391ec0acanthony
3015ec9ace44c23c302f336f6e672f6857312747d29bcristy               The kernel is not reflected for this operation, and consists
3016ec9ace44c23c302f336f6e672f6857312747d29bcristy               of both foreground and background pixel neighbourhoods, 0.0 for
3017ec9ace44c23c302f336f6e672f6857312747d29bcristy               background, and 1.0 for foreground with either Nan or 0.5 values
3018ec9ace44c23c302f336f6e672f6857312747d29bcristy               for don't care.
3019ec9ace44c23c302f336f6e672f6857312747d29bcristy
3020ec9ace44c23c302f336f6e672f6857312747d29bcristy               This never produces a meaningless negative result.  Such results
3021ec9ace44c23c302f336f6e672f6857312747d29bcristy               cause Thinning/Thicken to not work correctly when used against a
3022ec9ace44c23c302f336f6e672f6857312747d29bcristy               greyscale image.
30235ef8e94ff55717be2387d537bd49025780a1a558anthony            */
3024ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=kernel->values;
3025ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
3026ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3027ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3028ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3029a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                if (IfNaN(*k) == MagickFalse)
3030ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
3031ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if (*k > 0.7)
3032ec9ace44c23c302f336f6e672f6857312747d29bcristy                      {
3033ec9ace44c23c302f336f6e672f6857312747d29bcristy                        if ((double) pixels[i] < pixel)
3034ec9ace44c23c302f336f6e672f6857312747d29bcristy                          pixel=(double) pixels[i];
3035ec9ace44c23c302f336f6e672f6857312747d29bcristy                      }
3036ec9ace44c23c302f336f6e672f6857312747d29bcristy                    else
3037ec9ace44c23c302f336f6e672f6857312747d29bcristy                      if (*k < 0.3)
3038ec9ace44c23c302f336f6e672f6857312747d29bcristy                        {
3039ec9ace44c23c302f336f6e672f6857312747d29bcristy                          if ((double) pixels[i] > maximum)
3040ec9ace44c23c302f336f6e672f6857312747d29bcristy                            maximum=(double) pixels[i];
3041ec9ace44c23c302f336f6e672f6857312747d29bcristy                        }
3042ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3043ec9ace44c23c302f336f6e672f6857312747d29bcristy                k++;
3044ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
30455ef8e94ff55717be2387d537bd49025780a1a558anthony              }
3046b772effec6594fbd2b304bb685cfa6226f48780bcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
30475ef8e94ff55717be2387d537bd49025780a1a558anthony            }
3048ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixel-=maximum;
3049ec9ace44c23c302f336f6e672f6857312747d29bcristy            if (pixel < 0.0)
3050ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixel=0.0;
3051ec9ace44c23c302f336f6e672f6857312747d29bcristy            if (method ==  ThinningMorphology)
3052ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixel=(double) p[center+i]-pixel;
3053ec9ace44c23c302f336f6e672f6857312747d29bcristy            else
3054ec9ace44c23c302f336f6e672f6857312747d29bcristy              if (method ==  ThickenMorphology)
3055ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixel+=(double) p[center+i]+pixel;
30565ef8e94ff55717be2387d537bd49025780a1a558anthony            break;
3057ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3058ec9ace44c23c302f336f6e672f6857312747d29bcristy          case ErodeIntensityMorphology:
3059ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3060ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
3061ec9ace44c23c302f336f6e672f6857312747d29bcristy              Select pixel with minimum intensity within kernel neighbourhood.
30626fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3063ec9ace44c23c302f336f6e672f6857312747d29bcristy              The kernel is not reflected for this operation.
3064930be614b4595b97cd79ee864a394796740f76adanthony            */
3065ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=kernel->values;
3066ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
3067ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3068ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3069ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3070a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                if ((IfNaN(*k) == MagickFalse) && (*k >= 0.5))
3071ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
3072ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if (GetPixelIntensity(image,pixels) < minimum)
3073ec9ace44c23c302f336f6e672f6857312747d29bcristy                      {
3074ec9ace44c23c302f336f6e672f6857312747d29bcristy                        pixel=(double) pixels[i];
3075ec9ace44c23c302f336f6e672f6857312747d29bcristy                        minimum=GetPixelIntensity(image,pixels);
3076ec9ace44c23c302f336f6e672f6857312747d29bcristy                      }
3077ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3078ec9ace44c23c302f336f6e672f6857312747d29bcristy                k++;
3079ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3080602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3081b772effec6594fbd2b304bb685cfa6226f48780bcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3082602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3083602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3084ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3085ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DilateIntensityMorphology:
3086ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3087ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
3088ec9ace44c23c302f336f6e672f6857312747d29bcristy              Select pixel with maximum intensity within kernel neighbourhood.
30896fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3090ec9ace44c23c302f336f6e672f6857312747d29bcristy              The kernel is not reflected for this operation.
3091930be614b4595b97cd79ee864a394796740f76adanthony            */
3092ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
3093ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
3094ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3095ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3096ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3097a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                if ((IfNaN(*k) == MagickFalse) && (*k >= 0.5))
3098ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
3099ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if (GetPixelIntensity(image,pixels) > maximum)
3100ec9ace44c23c302f336f6e672f6857312747d29bcristy                      {
3101ec9ace44c23c302f336f6e672f6857312747d29bcristy                        pixel=(double) pixels[i];
3102ec9ace44c23c302f336f6e672f6857312747d29bcristy                        maximum=GetPixelIntensity(image,pixels);
3103ec9ace44c23c302f336f6e672f6857312747d29bcristy                      }
3104ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3105ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3106ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3107602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3108b772effec6594fbd2b304bb685cfa6226f48780bcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3109602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3110602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3111ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3112ec9ace44c23c302f336f6e672f6857312747d29bcristy          case IterativeDistanceMorphology:
3113ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3114ec9ace44c23c302f336f6e672f6857312747d29bcristy            /*
3115ec9ace44c23c302f336f6e672f6857312747d29bcristy               Compute th iterative distance from black edge of a white image
3116ec9ace44c23c302f336f6e672f6857312747d29bcristy               shape.  Essentually white values are decreased to the smallest
3117ec9ace44c23c302f336f6e672f6857312747d29bcristy               'distance from edge' it can find.
31186fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3119ec9ace44c23c302f336f6e672f6857312747d29bcristy               It works by adding kernel values to the neighbourhood, and and
3120ec9ace44c23c302f336f6e672f6857312747d29bcristy               select the minimum value found. The kernel is rotated before
3121ec9ace44c23c302f336f6e672f6857312747d29bcristy               use, so kernel distances match resulting distances, when a user
3122ec9ace44c23c302f336f6e672f6857312747d29bcristy               provided asymmetric kernel is applied.
31236fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3124ec9ace44c23c302f336f6e672f6857312747d29bcristy               This code is nearly identical to True GrayScale Morphology but
3125ec9ace44c23c302f336f6e672f6857312747d29bcristy               not quite.
31266fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3127ec9ace44c23c302f336f6e672f6857312747d29bcristy               GreyDilate Kernel values added, maximum value found Kernel is
3128ec9ace44c23c302f336f6e672f6857312747d29bcristy               rotated before use.
31296fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3130ec9ace44c23c302f336f6e672f6857312747d29bcristy               GrayErode:  Kernel values subtracted and minimum value found No
3131ec9ace44c23c302f336f6e672f6857312747d29bcristy               kernel rotation used.
31326fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
3133ec9ace44c23c302f336f6e672f6857312747d29bcristy               Note the the Iterative Distance method is essentially a
3134ec9ace44c23c302f336f6e672f6857312747d29bcristy               GrayErode, but with negative kernel values, and kernel rotation
3135ec9ace44c23c302f336f6e672f6857312747d29bcristy               applied.
3136930be614b4595b97cd79ee864a394796740f76adanthony            */
3137ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
3138ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < (ssize_t) kernel->height; v++)
3139ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3140ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3141ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3142a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                if (IfNaN(*k) == MagickFalse)
3143ec9ace44c23c302f336f6e672f6857312747d29bcristy                  {
3144ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
3145ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3146ec9ace44c23c302f336f6e672f6857312747d29bcristy                  }
3147ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3148ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3149602ab9b30b644a78a4057da93d838a77391ec0acanthony              }
3150b772effec6594fbd2b304bb685cfa6226f48780bcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3151602ab9b30b644a78a4057da93d838a77391ec0acanthony            }
3152602ab9b30b644a78a4057da93d838a77391ec0acanthony            break;
3153ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3154ec9ace44c23c302f336f6e672f6857312747d29bcristy          case UndefinedMorphology:
3155ec9ace44c23c302f336f6e672f6857312747d29bcristy          default:
3156ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3157ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
3158ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (fabs(pixel-p[center+i]) > MagickEpsilon)
3159c5876076825b539bd230ddadab1d876805c18291cristy          changes[id]++;
3160ec9ace44c23c302f336f6e672f6857312747d29bcristy        SetPixelChannel(morphology_image,channel,ClampToQuantum(pixel),q);
316183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      }
3162ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      p+=GetPixelChannels(image);
3163ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      q+=GetPixelChannels(morphology_image);
3164ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
31654c08aed51c5899665ade97263692328eea4af106cristy    if ( SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3166602ab9b30b644a78a4057da93d838a77391ec0acanthony      status=MagickFalse;
3167602ab9b30b644a78a4057da93d838a77391ec0acanthony    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3168602ab9b30b644a78a4057da93d838a77391ec0acanthony      {
3169602ab9b30b644a78a4057da93d838a77391ec0acanthony        MagickBooleanType
3170602ab9b30b644a78a4057da93d838a77391ec0acanthony          proceed;
3171602ab9b30b644a78a4057da93d838a77391ec0acanthony
3172602ab9b30b644a78a4057da93d838a77391ec0acanthony#if defined(MAGICKCORE_OPENMP_SUPPORT)
3173954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy        #pragma omp critical (MagickCore_MorphologyPrimitive)
3174602ab9b30b644a78a4057da93d838a77391ec0acanthony#endif
3175602ab9b30b644a78a4057da93d838a77391ec0acanthony        proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
3176602ab9b30b644a78a4057da93d838a77391ec0acanthony        if (proceed == MagickFalse)
3177602ab9b30b644a78a4057da93d838a77391ec0acanthony          status=MagickFalse;
3178602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
3179ec9ace44c23c302f336f6e672f6857312747d29bcristy  }
31804c08aed51c5899665ade97263692328eea4af106cristy  morphology_view=DestroyCacheView(morphology_view);
31814c08aed51c5899665ade97263692328eea4af106cristy  image_view=DestroyCacheView(image_view);
3182c5876076825b539bd230ddadab1d876805c18291cristy  for (i=0; i < (ssize_t) GetOpenMPMaximumThreads(); i++)
3183c5876076825b539bd230ddadab1d876805c18291cristy    changed+=changes[i];
3184306b0f1c3332736bfaddd94c5aaff078a69cbac3cristy  changes=(size_t *) RelinquishMagickMemory(changes);
3185c5876076825b539bd230ddadab1d876805c18291cristy  return(status ? (ssize_t) changed : -1);
3186602ab9b30b644a78a4057da93d838a77391ec0acanthony}
3187602ab9b30b644a78a4057da93d838a77391ec0acanthony
3188ec9ace44c23c302f336f6e672f6857312747d29bcristy/*
3189ec9ace44c23c302f336f6e672f6857312747d29bcristy  This is almost identical to the MorphologyPrimative() function above, but
3190ec9ace44c23c302f336f6e672f6857312747d29bcristy  applies the primitive directly to the actual image using two passes, once in
3191ec9ace44c23c302f336f6e672f6857312747d29bcristy  each direction, with the results of the previous (and current) row being
3192ec9ace44c23c302f336f6e672f6857312747d29bcristy  re-used.
3193ec9ace44c23c302f336f6e672f6857312747d29bcristy
3194ec9ace44c23c302f336f6e672f6857312747d29bcristy  That is after each row is 'Sync'ed' into the image, the next row makes use of
3195ec9ace44c23c302f336f6e672f6857312747d29bcristy  those values as part of the calculation of the next row.  It repeats, but
3196ec9ace44c23c302f336f6e672f6857312747d29bcristy  going in the oppisite (bottom-up) direction.
3197ec9ace44c23c302f336f6e672f6857312747d29bcristy
3198ec9ace44c23c302f336f6e672f6857312747d29bcristy  Because of this 're-use of results' this function can not make use of multi-
3199ec9ace44c23c302f336f6e672f6857312747d29bcristy  threaded, parellel processing.
3200a8843c1f815ffad2568ec592d5b446cb1476aab5anthony*/
3201e698a255629ba03cd125572de7b35b5e21c4ee5danthonystatic ssize_t MorphologyPrimitiveDirect(Image *image,
3202f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const MorphologyMethod method,const KernelInfo *kernel,
3203f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  ExceptionInfo *exception)
3204a8843c1f815ffad2568ec592d5b446cb1476aab5anthony{
3205a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  CacheView
3206ec9ace44c23c302f336f6e672f6857312747d29bcristy    *morphology_view,
3207ec9ace44c23c302f336f6e672f6857312747d29bcristy    *image_view;
3208a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3209a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  MagickBooleanType
3210a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    status;
3211a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3212a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  MagickOffsetType
3213a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    progress;
3214a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3215ec9ace44c23c302f336f6e672f6857312747d29bcristy  OffsetInfo
3216ec9ace44c23c302f336f6e672f6857312747d29bcristy    offset;
3217a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3218a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  size_t
3219ec9ace44c23c302f336f6e672f6857312747d29bcristy    width,
3220a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    changed;
3221a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3222ec9ace44c23c302f336f6e672f6857312747d29bcristy  ssize_t
3223ec9ace44c23c302f336f6e672f6857312747d29bcristy    y;
3224a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3225a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(image != (Image *) NULL);
3226a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(image->signature == MagickSignature);
3227a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(kernel != (KernelInfo *) NULL);
3228a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(kernel->signature == MagickSignature);
3229a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(exception != (ExceptionInfo *) NULL);
3230a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  assert(exception->signature == MagickSignature);
3231ec9ace44c23c302f336f6e672f6857312747d29bcristy  status=MagickTrue;
3232ec9ace44c23c302f336f6e672f6857312747d29bcristy  changed=0;
3233ec9ace44c23c302f336f6e672f6857312747d29bcristy  progress=0;
3234ec9ace44c23c302f336f6e672f6857312747d29bcristy  switch(method)
3235ec9ace44c23c302f336f6e672f6857312747d29bcristy  {
3236a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case DistanceMorphology:
3237e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case VoronoiMorphology:
3238ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
3239ec9ace44c23c302f336f6e672f6857312747d29bcristy      /*
3240ec9ace44c23c302f336f6e672f6857312747d29bcristy        Kernel reflected about origin.
3241ec9ace44c23c302f336f6e672f6857312747d29bcristy      */
3242ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.x=(ssize_t) kernel->width-kernel->x-1;
3243ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y=(ssize_t) kernel->height-kernel->y-1;
3244a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3245ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
3246a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    default:
3247ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
3248ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.x=kernel->x;
3249ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y=kernel->y;
3250a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
3251ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
3252a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  }
3253ec9ace44c23c302f336f6e672f6857312747d29bcristy  /*
3254ec9ace44c23c302f336f6e672f6857312747d29bcristy    Two views into same image, do not thread.
3255ec9ace44c23c302f336f6e672f6857312747d29bcristy  */
3256ec9ace44c23c302f336f6e672f6857312747d29bcristy  image_view=AcquireVirtualCacheView(image,exception);
3257ec9ace44c23c302f336f6e672f6857312747d29bcristy  morphology_view=AcquireAuthenticCacheView(image,exception);
3258ec9ace44c23c302f336f6e672f6857312747d29bcristy  width=image->columns+kernel->width-1;
3259a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  for (y=0; y < (ssize_t) image->rows; y++)
3260a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  {
32614c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
3262a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      *restrict p;
3263a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
32644c08aed51c5899665ade97263692328eea4af106cristy    register Quantum
3265a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      *restrict q;
3266a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3267a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    register ssize_t
3268a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      x;
3269a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
327027be28f6ee6dcb0c20268851b6359b50fbee71fbcristy    ssize_t
327127be28f6ee6dcb0c20268851b6359b50fbee71fbcristy      center;
327227be28f6ee6dcb0c20268851b6359b50fbee71fbcristy
3273ec9ace44c23c302f336f6e672f6857312747d29bcristy    /*
3274ec9ace44c23c302f336f6e672f6857312747d29bcristy      Read virtual pixels, and authentic pixels, from the same image!  We read
3275ec9ace44c23c302f336f6e672f6857312747d29bcristy      using virtual to get virtual pixel handling, but write back into the same
3276ec9ace44c23c302f336f6e672f6857312747d29bcristy      image.
3277ec9ace44c23c302f336f6e672f6857312747d29bcristy
3278ec9ace44c23c302f336f6e672f6857312747d29bcristy      Only top half of kernel is processed as we do a single pass downward
3279ec9ace44c23c302f336f6e672f6857312747d29bcristy      through the image iterating the distance function as we go.
3280a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    */
3281a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (status == MagickFalse)
3282954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy      continue;
3283ec9ace44c23c302f336f6e672f6857312747d29bcristy    p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,(size_t)
3284ec9ace44c23c302f336f6e672f6857312747d29bcristy      offset.y+1,exception);
32856fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
32864c08aed51c5899665ade97263692328eea4af106cristy      exception);
32874c08aed51c5899665ade97263692328eea4af106cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3288954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy      {
3289954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy        status=MagickFalse;
3290954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy        continue;
3291954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy      }
329227be28f6ee6dcb0c20268851b6359b50fbee71fbcristy    center=(ssize_t) (GetPixelChannels(image)*width*offset.y+
329327be28f6ee6dcb0c20268851b6359b50fbee71fbcristy      GetPixelChannels(image)*offset.x);
3294a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    for (x=0; x < (ssize_t) image->columns; x++)
3295a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    {
3296ec9ace44c23c302f336f6e672f6857312747d29bcristy      register ssize_t
3297ec9ace44c23c302f336f6e672f6857312747d29bcristy        i;
3298a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3299ec9ace44c23c302f336f6e672f6857312747d29bcristy      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3300ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
3301ec9ace44c23c302f336f6e672f6857312747d29bcristy        double
3302ec9ace44c23c302f336f6e672f6857312747d29bcristy          pixel;
3303a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3304ec9ace44c23c302f336f6e672f6857312747d29bcristy        PixelTrait
3305ec9ace44c23c302f336f6e672f6857312747d29bcristy          traits;
3306ec9ace44c23c302f336f6e672f6857312747d29bcristy
3307ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const MagickRealType
3308ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict k;
3309ec9ace44c23c302f336f6e672f6857312747d29bcristy
3310ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const Quantum
3311ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict pixels;
3312ec9ace44c23c302f336f6e672f6857312747d29bcristy
3313ec9ace44c23c302f336f6e672f6857312747d29bcristy        register ssize_t
3314ec9ace44c23c302f336f6e672f6857312747d29bcristy          u;
3315ec9ace44c23c302f336f6e672f6857312747d29bcristy
3316ec9ace44c23c302f336f6e672f6857312747d29bcristy        ssize_t
3317ec9ace44c23c302f336f6e672f6857312747d29bcristy          v;
3318ec9ace44c23c302f336f6e672f6857312747d29bcristy
33196fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        traits=GetPixelChannelTraits(image,i);
3320ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (traits == UndefinedPixelTrait)
3321ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
332227be28f6ee6dcb0c20268851b6359b50fbee71fbcristy        if (((traits & CopyPixelTrait) != 0) ||
3323883fde11debec15cedb05dc5d7228d8588066bc0cristy            (GetPixelReadMask(image,p+center) == 0))
3324ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
3325ec9ace44c23c302f336f6e672f6857312747d29bcristy        pixels=p;
332627be28f6ee6dcb0c20268851b6359b50fbee71fbcristy        pixel=(double) QuantumRange;
3327ec9ace44c23c302f336f6e672f6857312747d29bcristy        switch (method)
3328ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
3329ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DistanceMorphology:
3330ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3331ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
3332ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v <= offset.y; v++)
3333ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3334ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3335ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3336a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                if (IfNaN(*k) == MagickFalse)
3337e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3338ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
3339ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3340e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3341ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3342ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3343e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3344cfb71b157cd8f18b2e595d0a4a7c3c9403d8014dcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3345ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3346ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3347ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixels=q-offset.x*GetPixelChannels(image);
3348ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (u=0; u < offset.x; u++)
3349ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3350a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony              if ((IfNaN(*k) == MagickFalse) && ((x+u-offset.x) >= 0))
3351ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
3352ec9ace44c23c302f336f6e672f6857312747d29bcristy                  if ((pixels[i]+(*k)) < pixel)
3353ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel=(double) pixels[i]+(*k);
3354ec9ace44c23c302f336f6e672f6857312747d29bcristy                }
3355ec9ace44c23c302f336f6e672f6857312747d29bcristy              k--;
3356ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=GetPixelChannels(image);
3357e698a255629ba03cd125572de7b35b5e21c4ee5danthony            }
3358ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3359ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3360ec9ace44c23c302f336f6e672f6857312747d29bcristy          case VoronoiMorphology:
3361ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3362ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->height-1]);
3363ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=0; v < offset.y; v++)
3364ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3365ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3366ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3367a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                if (IfNaN(*k) == MagickFalse)
3368e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3369ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
3370ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3371e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3372ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3373ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3374e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
3375cfb71b157cd8f18b2e595d0a4a7c3c9403d8014dcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3376ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3377ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3378ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixels=q-offset.x*GetPixelChannels(image);
3379ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (u=0; u < offset.x; u++)
3380ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3381a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony              if ((IfNaN(*k) == MagickFalse) && ((x+u-offset.x) >= 0))
3382ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
3383ec9ace44c23c302f336f6e672f6857312747d29bcristy                  if ((pixels[i]+(*k)) < pixel)
3384ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel=(double) pixels[i]+(*k);
3385ec9ace44c23c302f336f6e672f6857312747d29bcristy                }
3386ec9ace44c23c302f336f6e672f6857312747d29bcristy              k--;
3387ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=GetPixelChannels(image);
3388ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3389e698a255629ba03cd125572de7b35b5e21c4ee5danthony            break;
3390ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3391ec9ace44c23c302f336f6e672f6857312747d29bcristy          default:
3392ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3393ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
33946fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        if (fabs(pixel-q[i]) > MagickEpsilon)
3395ec9ace44c23c302f336f6e672f6857312747d29bcristy          changed++;
33966fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        q[i]=ClampToQuantum(pixel);
3397e698a255629ba03cd125572de7b35b5e21c4ee5danthony      }
3398ec9ace44c23c302f336f6e672f6857312747d29bcristy      p+=GetPixelChannels(image);
3399ed2315769b26818ed9d0c1291dc0457f0d8da0a4cristy      q+=GetPixelChannels(image);
3400ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
34016fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
3402a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      status=MagickFalse;
3403a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    if (image->progress_monitor != (MagickProgressMonitor) NULL)
3404ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
3405ec9ace44c23c302f336f6e672f6857312747d29bcristy        MagickBooleanType
3406ec9ace44c23c302f336f6e672f6857312747d29bcristy          proceed;
3407a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
34086fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        proceed=SetImageProgress(image,MorphologyTag,progress++,2*image->rows);
3409ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (proceed == MagickFalse)
3410ec9ace44c23c302f336f6e672f6857312747d29bcristy          status=MagickFalse;
3411ec9ace44c23c302f336f6e672f6857312747d29bcristy      }
3412ec9ace44c23c302f336f6e672f6857312747d29bcristy  }
34136fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy  morphology_view=DestroyCacheView(morphology_view);
34146fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy  image_view=DestroyCacheView(image_view);
3415ec9ace44c23c302f336f6e672f6857312747d29bcristy  /*
3416ec9ace44c23c302f336f6e672f6857312747d29bcristy    Do the reverse pass through the image.
3417ec9ace44c23c302f336f6e672f6857312747d29bcristy  */
34186fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy  image_view=AcquireVirtualCacheView(image,exception);
34196fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy  morphology_view=AcquireAuthenticCacheView(image,exception);
3420ec9ace44c23c302f336f6e672f6857312747d29bcristy  for (y=(ssize_t) image->rows-1; y >= 0; y--)
3421a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  {
34224c08aed51c5899665ade97263692328eea4af106cristy    register const Quantum
3423a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      *restrict p;
3424a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
34254c08aed51c5899665ade97263692328eea4af106cristy    register Quantum
3426a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      *restrict q;
3427a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3428a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    register ssize_t
3429a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      x;
3430a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
343127be28f6ee6dcb0c20268851b6359b50fbee71fbcristy    ssize_t
343227be28f6ee6dcb0c20268851b6359b50fbee71fbcristy      center;
343327be28f6ee6dcb0c20268851b6359b50fbee71fbcristy
3434ec9ace44c23c302f336f6e672f6857312747d29bcristy    /*
3435ec9ace44c23c302f336f6e672f6857312747d29bcristy       Read virtual pixels, and authentic pixels, from the same image.  We
3436ec9ace44c23c302f336f6e672f6857312747d29bcristy       read using virtual to get virtual pixel handling, but write back
3437ec9ace44c23c302f336f6e672f6857312747d29bcristy       into the same image.
3438a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3439ec9ace44c23c302f336f6e672f6857312747d29bcristy       Only the bottom half of the kernel is processed as we up the image.
3440ec9ace44c23c302f336f6e672f6857312747d29bcristy    */
3441db60568e12574785101a4ae8d8da076227a0a889anthony    if (status == MagickFalse)
3442954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy      continue;
3443ec9ace44c23c302f336f6e672f6857312747d29bcristy    p=GetCacheViewVirtualPixels(image_view,-offset.x,y,width,(size_t)
34444c08aed51c5899665ade97263692328eea4af106cristy      kernel->y+1,exception);
34456fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    q=GetCacheViewAuthenticPixels(morphology_view,0,y,image->columns,1,
34464c08aed51c5899665ade97263692328eea4af106cristy      exception);
34474c08aed51c5899665ade97263692328eea4af106cristy    if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
3448954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy      {
3449954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy        status=MagickFalse;
3450954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy        continue;
3451954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy      }
3452ec9ace44c23c302f336f6e672f6857312747d29bcristy    p+=(image->columns-1)*GetPixelChannels(image);
3453ec9ace44c23c302f336f6e672f6857312747d29bcristy    q+=(image->columns-1)*GetPixelChannels(image);
345427be28f6ee6dcb0c20268851b6359b50fbee71fbcristy    center=(ssize_t) (offset.x*GetPixelChannels(image));
34556fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    for (x=(ssize_t) image->columns-1; x >= 0; x--)
3456ec9ace44c23c302f336f6e672f6857312747d29bcristy    {
3457ec9ace44c23c302f336f6e672f6857312747d29bcristy      register ssize_t
3458ec9ace44c23c302f336f6e672f6857312747d29bcristy        i;
3459a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3460ec9ace44c23c302f336f6e672f6857312747d29bcristy      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
3461ec9ace44c23c302f336f6e672f6857312747d29bcristy      {
3462ec9ace44c23c302f336f6e672f6857312747d29bcristy        double
3463ec9ace44c23c302f336f6e672f6857312747d29bcristy          pixel;
3464a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3465ec9ace44c23c302f336f6e672f6857312747d29bcristy        PixelTrait
3466ec9ace44c23c302f336f6e672f6857312747d29bcristy          traits;
3467a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3468ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const MagickRealType
3469ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict k;
3470a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3471ec9ace44c23c302f336f6e672f6857312747d29bcristy        register const Quantum
3472ec9ace44c23c302f336f6e672f6857312747d29bcristy          *restrict pixels;
3473a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3474ec9ace44c23c302f336f6e672f6857312747d29bcristy        register ssize_t
3475ec9ace44c23c302f336f6e672f6857312747d29bcristy          u;
3476ec9ace44c23c302f336f6e672f6857312747d29bcristy
3477ec9ace44c23c302f336f6e672f6857312747d29bcristy        ssize_t
3478ec9ace44c23c302f336f6e672f6857312747d29bcristy          v;
3479ec9ace44c23c302f336f6e672f6857312747d29bcristy
34806fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        traits=GetPixelChannelTraits(image,i);
3481ec9ace44c23c302f336f6e672f6857312747d29bcristy        if (traits == UndefinedPixelTrait)
3482ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
348327be28f6ee6dcb0c20268851b6359b50fbee71fbcristy        if (((traits & CopyPixelTrait) != 0) ||
3484883fde11debec15cedb05dc5d7228d8588066bc0cristy             (GetPixelReadMask(image,p+center) == 0))
3485ec9ace44c23c302f336f6e672f6857312747d29bcristy          continue;
3486ec9ace44c23c302f336f6e672f6857312747d29bcristy        pixels=p;
348727be28f6ee6dcb0c20268851b6359b50fbee71fbcristy        pixel=(double) QuantumRange;
3488ec9ace44c23c302f336f6e672f6857312747d29bcristy        switch (method)
3489ec9ace44c23c302f336f6e672f6857312747d29bcristy        {
3490ec9ace44c23c302f336f6e672f6857312747d29bcristy          case DistanceMorphology:
3491ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3492ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3493ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=offset.y; v < (ssize_t) kernel->height; v++)
3494ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3495ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3496ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3497a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                if (IfNaN(*k) == MagickFalse)
3498e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3499ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
3500ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3501e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3502ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3503ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3504e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
350527be28f6ee6dcb0c20268851b6359b50fbee71fbcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3506e698a255629ba03cd125572de7b35b5e21c4ee5danthony            }
3507ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*kernel->y+kernel->x-1]);
3508ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixels=q-offset.x*GetPixelChannels(image);
3509ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
3510ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3511a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony              if ((IfNaN(*k) == MagickFalse) &&
35126fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                  ((x+u-offset.x) < (ssize_t) image->columns))
3513ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
3514ec9ace44c23c302f336f6e672f6857312747d29bcristy                  if ((pixels[i]+(*k)) < pixel)
3515ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel=(double) pixels[i]+(*k);
3516ec9ace44c23c302f336f6e672f6857312747d29bcristy                }
3517ec9ace44c23c302f336f6e672f6857312747d29bcristy              k--;
3518ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=GetPixelChannels(image);
3519ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3520ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3521ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3522ec9ace44c23c302f336f6e672f6857312747d29bcristy          case VoronoiMorphology:
3523ec9ace44c23c302f336f6e672f6857312747d29bcristy          {
3524ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3525ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (v=offset.y; v < (ssize_t) kernel->height; v++)
3526ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3527ec9ace44c23c302f336f6e672f6857312747d29bcristy              for (u=0; u < (ssize_t) kernel->width; u++)
3528ec9ace44c23c302f336f6e672f6857312747d29bcristy              {
3529a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony                if (IfNaN(*k) == MagickFalse)
3530e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  {
3531ec9ace44c23c302f336f6e672f6857312747d29bcristy                    if ((pixels[i]+(*k)) < pixel)
3532ec9ace44c23c302f336f6e672f6857312747d29bcristy                      pixel=(double) pixels[i]+(*k);
3533e698a255629ba03cd125572de7b35b5e21c4ee5danthony                  }
3534ec9ace44c23c302f336f6e672f6857312747d29bcristy                k--;
3535ec9ace44c23c302f336f6e672f6857312747d29bcristy                pixels+=GetPixelChannels(image);
3536e698a255629ba03cd125572de7b35b5e21c4ee5danthony              }
353727be28f6ee6dcb0c20268851b6359b50fbee71fbcristy              pixels+=(image->columns-1)*GetPixelChannels(image);
3538ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3539ec9ace44c23c302f336f6e672f6857312747d29bcristy            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
3540ec9ace44c23c302f336f6e672f6857312747d29bcristy            pixels=q-offset.x*GetPixelChannels(image);
3541ec9ace44c23c302f336f6e672f6857312747d29bcristy            for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
3542ec9ace44c23c302f336f6e672f6857312747d29bcristy            {
3543a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony              if ((IfNaN(*k) == MagickFalse) &&
35446fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy                  ((x+u-offset.x) < (ssize_t) image->columns))
3545ec9ace44c23c302f336f6e672f6857312747d29bcristy                {
3546ec9ace44c23c302f336f6e672f6857312747d29bcristy                  if ((pixels[i]+(*k)) < pixel)
3547ec9ace44c23c302f336f6e672f6857312747d29bcristy                    pixel=(double) pixels[i]+(*k);
3548ec9ace44c23c302f336f6e672f6857312747d29bcristy                }
3549ec9ace44c23c302f336f6e672f6857312747d29bcristy              k--;
3550ec9ace44c23c302f336f6e672f6857312747d29bcristy              pixels+=GetPixelChannels(image);
3551ec9ace44c23c302f336f6e672f6857312747d29bcristy            }
3552e698a255629ba03cd125572de7b35b5e21c4ee5danthony            break;
3553ec9ace44c23c302f336f6e672f6857312747d29bcristy          }
3554ec9ace44c23c302f336f6e672f6857312747d29bcristy          default:
3555ec9ace44c23c302f336f6e672f6857312747d29bcristy            break;
3556ec9ace44c23c302f336f6e672f6857312747d29bcristy        }
35576fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        if (fabs(pixel-q[i]) > MagickEpsilon)
3558ec9ace44c23c302f336f6e672f6857312747d29bcristy          changed++;
35596fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        q[i]=ClampToQuantum(pixel);
3560e698a255629ba03cd125572de7b35b5e21c4ee5danthony      }
35616fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy      p-=GetPixelChannels(image);
35626fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy      q-=GetPixelChannels(image);
3563ec9ace44c23c302f336f6e672f6857312747d29bcristy    }
35646fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
35656fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy      status=MagickFalse;
35666fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy    if (image->progress_monitor != (MagickProgressMonitor) NULL)
35676fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy      {
35686fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        MagickBooleanType
35696fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy          proceed;
35706fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy
35716fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        proceed=SetImageProgress(image,MorphologyTag,progress++,2*image->rows);
35726fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy        if (proceed == MagickFalse)
35736fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy          status=MagickFalse;
35746fb2996ae0b3c4bf65026ac78a8890bfb8a494d7cristy      }
3575ec9ace44c23c302f336f6e672f6857312747d29bcristy  }
3576ec9ace44c23c302f336f6e672f6857312747d29bcristy  morphology_view=DestroyCacheView(morphology_view);
3577ec9ace44c23c302f336f6e672f6857312747d29bcristy  image_view=DestroyCacheView(image_view);
3578aecf4bd539fe0591d67bf1ec961ac68b5490171fcristy  return(status ? (ssize_t) changed : -1);
3579a8843c1f815ffad2568ec592d5b446cb1476aab5anthony}
3580a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
3581ec9ace44c23c302f336f6e672f6857312747d29bcristy/*
3582ec9ace44c23c302f336f6e672f6857312747d29bcristy  Apply a Morphology by calling one of the above low level primitive
3583ec9ace44c23c302f336f6e672f6857312747d29bcristy  application functions.  This function handles any iteration loops,
3584ec9ace44c23c302f336f6e672f6857312747d29bcristy  composition or re-iteration of results, and compound morphology methods that
3585ec9ace44c23c302f336f6e672f6857312747d29bcristy  is based on multiple low-level (staged) morphology methods.
3586ec9ace44c23c302f336f6e672f6857312747d29bcristy
3587ec9ace44c23c302f336f6e672f6857312747d29bcristy  Basically this provides the complex glue between the requested morphology
3588ec9ace44c23c302f336f6e672f6857312747d29bcristy  method and raw low-level implementation (above).
3589a8843c1f815ffad2568ec592d5b446cb1476aab5anthony*/
3590cf592636b16d028b14c5fa9dffdc3a94eae7c2f3cristyMagickPrivate Image *MorphologyApply(const Image *image,
3591f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const MorphologyMethod method, const ssize_t iterations,
3592f46d42620631d2581e0b6a56456e203e17c427c8anthony  const KernelInfo *kernel, const CompositeOperator compose,const double bias,
3593f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  ExceptionInfo *exception)
3594602ab9b30b644a78a4057da93d838a77391ec0acanthony{
35951cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy  CompositeOperator
35961cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy    curr_compose;
35971cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy
3598602ab9b30b644a78a4057da93d838a77391ec0acanthony  Image
359947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *curr_image,    /* Image we are working with or iterating */
3600a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    *work_image,    /* secondary image for primitive iteration */
360147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *save_image,    /* saved image - for 'edge' method only */
360247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *rslt_image;    /* resultant image - after multi-kernel handling */
3603602ab9b30b644a78a4057da93d838a77391ec0acanthony
36044fd27e21043be809d66c8202e779255e5b660d2danthony  KernelInfo
360547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *reflected_kernel, /* A reflected copy of the kernel (if needed) */
360647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *norm_kernel,      /* the current normal un-reflected kernel */
360747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *rflt_kernel,      /* the current reflected kernel (if needed) */
360847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    *this_kernel;      /* the kernel being applied */
36094fd27e21043be809d66c8202e779255e5b660d2danthony
36104fd27e21043be809d66c8202e779255e5b660d2danthony  MorphologyMethod
3611a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    primitive;      /* the current morphology primitive being applied */
36129eb4f74649b23c053b308ce1152dce51239450baanthony
36139eb4f74649b23c053b308ce1152dce51239450baanthony  CompositeOperator
361447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_compose;   /* multi-kernel compose method for results to use */
361547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
361647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  MagickBooleanType
3617e698a255629ba03cd125572de7b35b5e21c4ee5danthony    special,        /* do we use a direct modify function? */
361847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    verbose;        /* verbose output of results */
36194fd27e21043be809d66c8202e779255e5b660d2danthony
3620bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  size_t
3621a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    method_loop,    /* Loop 1: number of compound method iterations (norm 1) */
362247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_limit,   /*         maximum number of compound method iterations */
362347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_number,  /* Loop 2: the kernel number being applied */
3624a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    stage_loop,     /* Loop 3: primitive loop for compound morphology */
3625a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    stage_limit,    /*         how many primitives are in this compound */
3626a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    kernel_loop,    /* Loop 4: iterate the kernel over image */
362747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_limit,   /*         number of times to iterate kernel */
3628a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    count,          /* total count of primitive steps applied */
362947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_changed, /* total count of changed using iterated kernel */
363047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_changed; /* total count of changed over method iteration */
363147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
3632a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  ssize_t
3633a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    changed;        /* number pixels changed by last primitive operation */
3634a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
363547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  char
363647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    v_info[80];
36371b2bc0a7da432e6e1cc0480280402df213faa940anthony
3638602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(image != (Image *) NULL);
3639602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(image->signature == MagickSignature);
36404fd27e21043be809d66c8202e779255e5b660d2danthony  assert(kernel != (KernelInfo *) NULL);
36414fd27e21043be809d66c8202e779255e5b660d2danthony  assert(kernel->signature == MagickSignature);
3642602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(exception != (ExceptionInfo *) NULL);
3643602ab9b30b644a78a4057da93d838a77391ec0acanthony  assert(exception->signature == MagickSignature);
3644602ab9b30b644a78a4057da93d838a77391ec0acanthony
3645a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  count = 0;      /* number of low-level morphology primitives performed */
3646602ab9b30b644a78a4057da93d838a77391ec0acanthony  if ( iterations == 0 )
364747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    return((Image *)NULL);   /* null operation - nothing to do! */
3648602ab9b30b644a78a4057da93d838a77391ec0acanthony
3649bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  kernel_limit = (size_t) iterations;
365047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( iterations < 0 )  /* negative interations = infinite (well alomst) */
3651e698a255629ba03cd125572de7b35b5e21c4ee5danthony     kernel_limit = image->columns>image->rows ? image->columns : image->rows;
365228ad1d779b6ca95852e860514185a7a97e06af77anthony
36536f2013165d72f7d8ef5f66bb9453126d88113809anthony  verbose = IsStringTrue(GetImageArtifact(image,"verbose"));
36544f1dcb76c95ef6410f2957ca9e7e1d391cee0d02anthony
36559eb4f74649b23c053b308ce1152dce51239450baanthony  /* initialise for cleanup */
365647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  curr_image = (Image *) image;
36571cf06410e6379f3c351adfc39a1b79a252c8e5b6cristy  curr_compose = image->compose;
3658aecf4bd539fe0591d67bf1ec961ac68b5490171fcristy  (void) curr_compose;
365947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  work_image = save_image = rslt_image = (Image *) NULL;
366047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  reflected_kernel = (KernelInfo *) NULL;
36614fd27e21043be809d66c8202e779255e5b660d2danthony
366247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Initialize specific methods
366347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   * + which loop should use the given iteratations
3664a8843c1f815ffad2568ec592d5b446cb1476aab5anthony   * + how many primitives make up the compound morphology
366547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   * + multi-kernel compose method to use (by default)
366647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   */
366747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_limit = 1;       /* just do method once, unless otherwise set */
3668ea61f01656bb0f9074677452017cc559e54093faanthony  stage_limit = 1;        /* assume method is not a compound */
36694ee950098ad0166bbbc85c3a59bc079cd321384aglennrp  special = MagickFalse;   /* assume it is NOT a direct modify primitive */
367047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  rslt_compose = compose; /* and we are composing multi-kernels as given */
36719eb4f74649b23c053b308ce1152dce51239450baanthony  switch( method ) {
3672a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case SmoothMorphology:  /* 4 primitive compound morphology */
367347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      stage_limit = 4;
3674602ab9b30b644a78a4057da93d838a77391ec0acanthony      break;
3675a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case OpenMorphology:    /* 2 primitive compound morphology */
3676930be614b4595b97cd79ee864a394796740f76adanthony    case OpenIntensityMorphology:
367747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case TopHatMorphology:
367847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseMorphology:
36794fd27e21043be809d66c8202e779255e5b660d2danthony    case CloseIntensityMorphology:
368047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case BottomHatMorphology:
368147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case EdgeMorphology:
368247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      stage_limit = 2;
3683602ab9b30b644a78a4057da93d838a77391ec0acanthony      break;
36849eb4f74649b23c053b308ce1152dce51239450baanthony    case HitAndMissMorphology:
368547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      rslt_compose = LightenCompositeOp;  /* Union of multi-kernel results */
36863ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony      /* FALL THUR */
3687c3e48258f3253188894e783dcdfd03562f7ab2c5anthony    case ThinningMorphology:
36889eb4f74649b23c053b308ce1152dce51239450baanthony    case ThickenMorphology:
36893ca9ec1fec132c7e3c037a6fea16d8fdb9d3f29aanthony      method_limit = kernel_limit;  /* iterate the whole method */
3690c3e48258f3253188894e783dcdfd03562f7ab2c5anthony      kernel_limit = 1;             /* do not do kernel iteration  */
369147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
3692a8843c1f815ffad2568ec592d5b446cb1476aab5anthony    case DistanceMorphology:
3693e698a255629ba03cd125572de7b35b5e21c4ee5danthony    case VoronoiMorphology:
3694f34d9b2df49a407af764c79e07d587af0600983aanthony      special = MagickTrue;         /* use special direct primative */
3695a8843c1f815ffad2568ec592d5b446cb1476aab5anthony      break;
369647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    default:
36979eb4f74649b23c053b308ce1152dce51239450baanthony      break;
36989eb4f74649b23c053b308ce1152dce51239450baanthony  }
3699602ab9b30b644a78a4057da93d838a77391ec0acanthony
3700e698a255629ba03cd125572de7b35b5e21c4ee5danthony  /* Apply special methods with special requirments
3701e698a255629ba03cd125572de7b35b5e21c4ee5danthony  ** For example, single run only, or post-processing requirements
3702e698a255629ba03cd125572de7b35b5e21c4ee5danthony  */
3703e698a255629ba03cd125572de7b35b5e21c4ee5danthony  if ( special == MagickTrue )
3704e698a255629ba03cd125572de7b35b5e21c4ee5danthony    {
3705e698a255629ba03cd125572de7b35b5e21c4ee5danthony      rslt_image=CloneImage(image,0,0,MagickTrue,exception);
3706e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if (rslt_image == (Image *) NULL)
3707e698a255629ba03cd125572de7b35b5e21c4ee5danthony        goto error_cleanup;
3708574cc26500992189f637cd1cdf93d0654e7df7aecristy      if (SetImageStorageClass(rslt_image,DirectClass,exception) == MagickFalse)
3709574cc26500992189f637cd1cdf93d0654e7df7aecristy        goto error_cleanup;
3710e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3711a325e90b7583eab5e70f0ceebb8257670e652f0fcristy      changed=MorphologyPrimitiveDirect(rslt_image,method,kernel,exception);
3712e698a255629ba03cd125572de7b35b5e21c4ee5danthony
37137bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony      if ( IfMagickTrue(verbose) )
37145acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) (void) FormatLocaleFile(stderr,
37151e604812fad85bb96f757a2393015ae3d061c39acristy          "%s:%.20g.%.20g #%.20g => Changed %.20g\n",
37161e604812fad85bb96f757a2393015ae3d061c39acristy          CommandOptionToMnemonic(MagickMorphologyOptions, method),
37171e604812fad85bb96f757a2393015ae3d061c39acristy          1.0,0.0,1.0, (double) changed);
3718e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3719e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if ( changed < 0 )
3720e698a255629ba03cd125572de7b35b5e21c4ee5danthony        goto error_cleanup;
3721e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3722e698a255629ba03cd125572de7b35b5e21c4ee5danthony      if ( method == VoronoiMorphology ) {
3723a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony        /* Preserve the alpha channel of input image - but turned it off */
372463240888c3975789a09c2494a4654b523931df96cristy        (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
372563240888c3975789a09c2494a4654b523931df96cristy          exception);
3726feb3e9695150978a5d2372d3fe2f60466a7c8066cristy        (void) CompositeImage(rslt_image,image,CopyAlphaCompositeOp,
372739172408bad7ef2ef00a815fa9abf9979e7857cbcristy          MagickTrue,0,0,exception);
372863240888c3975789a09c2494a4654b523931df96cristy        (void) SetImageAlphaChannel(rslt_image, DeactivateAlphaChannel,
372963240888c3975789a09c2494a4654b523931df96cristy          exception);
3730e698a255629ba03cd125572de7b35b5e21c4ee5danthony      }
3731e698a255629ba03cd125572de7b35b5e21c4ee5danthony      goto exit_cleanup;
3732e698a255629ba03cd125572de7b35b5e21c4ee5danthony    }
3733e698a255629ba03cd125572de7b35b5e21c4ee5danthony
3734c3e48258f3253188894e783dcdfd03562f7ab2c5anthony  /* Handle user (caller) specified multi-kernel composition method */
373547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( compose != UndefinedCompositeOp )
373647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_compose = compose;  /* override default composition for method */
373747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( rslt_compose == UndefinedCompositeOp )
373847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_compose = NoCompositeOp; /* still not defined! Then re-iterate */
37394fd27e21043be809d66c8202e779255e5b660d2danthony
3740a8843c1f815ffad2568ec592d5b446cb1476aab5anthony  /* Some methods require a reflected kernel to use with primitives.
3741c3e48258f3253188894e783dcdfd03562f7ab2c5anthony   * Create the reflected kernel for those methods. */
374247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  switch ( method ) {
374347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CorrelateMorphology:
374447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseMorphology:
374547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case CloseIntensityMorphology:
374647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case BottomHatMorphology:
374747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    case SmoothMorphology:
374847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      reflected_kernel = CloneKernelInfo(kernel);
374947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if (reflected_kernel == (KernelInfo *) NULL)
375047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        goto error_cleanup;
375147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      RotateKernelInfo(reflected_kernel,180);
375247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
375347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    default:
375447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      break;
375547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  }
3756602ab9b30b644a78a4057da93d838a77391ec0acanthony
3757e698a255629ba03cd125572de7b35b5e21c4ee5danthony  /* Loops around more primitive morpholgy methods
3758e698a255629ba03cd125572de7b35b5e21c4ee5danthony  **  erose, dilate, open, close, smooth, edge, etc...
3759e698a255629ba03cd125572de7b35b5e21c4ee5danthony  */
376047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Loop 1:  iterate the compound method */
376147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_loop = 0;
376247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  method_changed = 1;
376347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  while ( method_loop < method_limit && method_changed > 0 ) {
376447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_loop++;
376547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    method_changed = 0;
376647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
376747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    /* Loop 2:  iterate over each kernel in a multi-kernel list */
376847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    norm_kernel = (KernelInfo *) kernel;
3769f2faecf9facdbbb14fcba373365f9f691a9658e0cristy    this_kernel = (KernelInfo *) kernel;
377047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rflt_kernel = reflected_kernel;
3771e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony
377247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    kernel_number = 0;
377347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    while ( norm_kernel != NULL ) {
377447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
377547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /* Loop 3: Compound Morphology Staging - Select Primative to apply */
377647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      stage_loop = 0;          /* the compound morphology stage number */
377747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      while ( stage_loop < stage_limit ) {
377847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        stage_loop++;   /* The stage of the compound morphology */
377947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
3780a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        /* Select primitive morphology for this stage of compound method */
378147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        this_kernel = norm_kernel; /* default use unreflected kernel */
3782a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        primitive = method;        /* Assume method is a primitive */
378347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        switch( method ) {
378447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case ErodeMorphology:      /* just erode */
378547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeInMorphology:     /* erode and image difference */
3786a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ErodeMorphology;
378747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
378847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case DilateMorphology:     /* just dilate */
378947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeOutMorphology:    /* dilate and image difference */
3790a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateMorphology;
379147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
379247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case OpenMorphology:       /* erode then dialate */
379347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case TopHatMorphology:     /* open and image difference */
3794a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ErodeMorphology;
379547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3796a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = DilateMorphology;
379747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
379847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case OpenIntensityMorphology:
3799a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ErodeIntensityMorphology;
380047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3801a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = DilateIntensityMorphology;
3802e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony            break;
380347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CloseMorphology:      /* dilate, then erode */
380447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case BottomHatMorphology:  /* close and image difference */
380547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
3806a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateMorphology;
380747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3808a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = ErodeMorphology;
380947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
381047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CloseIntensityMorphology:
381147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
3812a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateIntensityMorphology;
381347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 )
3814a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = ErodeIntensityMorphology;
381547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
381647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case SmoothMorphology:         /* open, close */
381747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            switch ( stage_loop ) {
381847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 1: /* start an open method, which starts with Erode */
3819a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = ErodeMorphology;
382047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
382147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 2:  /* now Dilate the Erode */
3822a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = DilateMorphology;
382347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
382447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 3:  /* Reflect kernel a close */
382547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                this_kernel = rflt_kernel; /* use the reflected kernel */
3826a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = DilateMorphology;
382747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
382847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              case 4:  /* Finish the Close */
382947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                this_kernel = rflt_kernel; /* use the reflected kernel */
3830a8843c1f815ffad2568ec592d5b446cb1476aab5anthony                primitive = ErodeMorphology;
383147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony                break;
383247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            }
383347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
383447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case EdgeMorphology:        /* dilate and erode difference */
3835a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = DilateMorphology;
383647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( stage_loop == 2 ) {
383747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              save_image = curr_image;      /* save the image difference */
383847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony              curr_image = (Image *) image;
3839a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive = ErodeMorphology;
384047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            }
384147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
384247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          case CorrelateMorphology:
384347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            /* A Correlation is a Convolution with a reflected kernel.
384447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** However a Convolution is a weighted sum using a reflected
384547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** kernel.  It may seem stange to convert a Correlation into a
384647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** Convolution as the Correlation is the simplier method, but
384747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** Convolution is much more commonly used, and it makes sense to
384847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** implement it directly so as to avoid the need to duplicate the
384947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** kernel when it is not required (which is typically the
385047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            ** default).
385147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            */
385247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            this_kernel = rflt_kernel; /* use the reflected kernel */
3853a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            primitive = ConvolveMorphology;
385447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
385547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          default:
385647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            break;
385747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
3858e4d8996e5c17ae40a1160f3446956e12ec00bc9canthony        assert( this_kernel != (KernelInfo *) NULL );
38597a01dcf50ce12cb2a789bedff51e9345f022432eanthony
386047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        /* Extra information for debugging compound operations */
38617bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony        if ( IfMagickTrue(verbose) ) {
386247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          if ( stage_limit > 1 )
3863b51dff5c0d16a4c1b69ff683e786cb3b4c467694cristy            (void) FormatLocaleString(v_info,MaxTextExtent,"%s:%.20g.%.20g -> ",
3864042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy             CommandOptionToMnemonic(MagickMorphologyOptions,method),(double)
3865e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy             method_loop,(double) stage_loop);
3866a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          else if ( primitive != method )
3867b51dff5c0d16a4c1b69ff683e786cb3b4c467694cristy            (void) FormatLocaleString(v_info, MaxTextExtent, "%s:%.20g -> ",
3868042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              CommandOptionToMnemonic(MagickMorphologyOptions, method),(double)
3869e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              method_loop);
387047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          else
387147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            v_info[0] = '\0';
387247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
38739eb4f74649b23c053b308ce1152dce51239450baanthony
3874a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        /* Loop 4: Iterate the kernel with primitive */
387547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        kernel_loop = 0;
387647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        kernel_changed = 0;
387747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        changed = 1;
387847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        while ( kernel_loop < kernel_limit && changed > 0 ) {
387947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          kernel_loop++;     /* the iteration of this kernel */
38809eb4f74649b23c053b308ce1152dce51239450baanthony
3881a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          /* Create a clone as the destination image, if not yet defined */
38829eb4f74649b23c053b308ce1152dce51239450baanthony          if ( work_image == (Image *) NULL )
38839eb4f74649b23c053b308ce1152dce51239450baanthony            {
38849eb4f74649b23c053b308ce1152dce51239450baanthony              work_image=CloneImage(image,0,0,MagickTrue,exception);
38859eb4f74649b23c053b308ce1152dce51239450baanthony              if (work_image == (Image *) NULL)
38869eb4f74649b23c053b308ce1152dce51239450baanthony                goto error_cleanup;
3887574cc26500992189f637cd1cdf93d0654e7df7aecristy              if (SetImageStorageClass(work_image,DirectClass,exception) == MagickFalse)
3888574cc26500992189f637cd1cdf93d0654e7df7aecristy                goto error_cleanup;
38899eb4f74649b23c053b308ce1152dce51239450baanthony            }
38909eb4f74649b23c053b308ce1152dce51239450baanthony
3891501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony          /* APPLY THE MORPHOLOGICAL PRIMITIVE (curr -> work) */
38929eb4f74649b23c053b308ce1152dce51239450baanthony          count++;
3893e698a255629ba03cd125572de7b35b5e21c4ee5danthony          changed = MorphologyPrimitive(curr_image, work_image, primitive,
3894f46d42620631d2581e0b6a56456e203e17c427c8anthony                       this_kernel, bias, exception);
389547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
38967bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony          if ( IfMagickTrue(verbose) ) {
389747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            if ( kernel_loop > 1 )
38985acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line from previous */
38995acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) (void) FormatLocaleFile(stderr,
39001e604812fad85bb96f757a2393015ae3d061c39acristy              "%s%s%s:%.20g.%.20g #%.20g => Changed %.20g",
3901042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy              v_info,CommandOptionToMnemonic(MagickMorphologyOptions,
3902a8843c1f815ffad2568ec592d5b446cb1476aab5anthony              primitive),(this_kernel == rflt_kernel ) ? "*" : "",
3903e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              (double) (method_loop+kernel_loop-1),(double) kernel_number,
3904e8c25f9b4c9fb72cad6db08eeda58c7c5784014ecristy              (double) count,(double) changed);
390547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          }
3906a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          if ( changed < 0 )
3907a8843c1f815ffad2568ec592d5b446cb1476aab5anthony            goto error_cleanup;
3908a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          kernel_changed += changed;
3909a8843c1f815ffad2568ec592d5b446cb1476aab5anthony          method_changed += changed;
3910a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
39119eb4f74649b23c053b308ce1152dce51239450baanthony          /* prepare next loop */
39129eb4f74649b23c053b308ce1152dce51239450baanthony          { Image *tmp = work_image;   /* swap images for iteration */
39139eb4f74649b23c053b308ce1152dce51239450baanthony            work_image = curr_image;
39149eb4f74649b23c053b308ce1152dce51239450baanthony            curr_image = tmp;
39159eb4f74649b23c053b308ce1152dce51239450baanthony          }
39169eb4f74649b23c053b308ce1152dce51239450baanthony          if ( work_image == image )
391747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony            work_image = (Image *) NULL; /* replace input 'image' */
39189eb4f74649b23c053b308ce1152dce51239450baanthony
3919a8843c1f815ffad2568ec592d5b446cb1476aab5anthony        } /* End Loop 4: Iterate the kernel with primitive */
39207a01dcf50ce12cb2a789bedff51e9345f022432eanthony
39217bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony        if ( IfMagickTrue(verbose) && kernel_changed != (size_t)changed )
39225acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "   Total %.20g",(double) kernel_changed);
39237bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony        if ( IfMagickTrue(verbose) && stage_loop < stage_limit )
39245acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr, "\n"); /* add end-of-line before looping */
39259eb4f74649b23c053b308ce1152dce51239450baanthony
392647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony#if 0
39275acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "--E-- image=0x%lx\n", (unsigned long)image);
39285acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      curr =0x%lx\n", (unsigned long)curr_image);
39295acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      work =0x%lx\n", (unsigned long)work_image);
39305acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      save =0x%lx\n", (unsigned long)save_image);
39315acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "      union=0x%lx\n", (unsigned long)rslt_image);
393247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony#endif
39331b2bc0a7da432e6e1cc0480280402df213faa940anthony
393447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      } /* End Loop 3: Primative (staging) Loop for Coumpound Methods */
39351b2bc0a7da432e6e1cc0480280402df213faa940anthony
393647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /*  Final Post-processing for some Compound Methods
393747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      **
393847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** The removal of any 'Sync' channel flag in the Image Compositon
393947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** below ensures the methematical compose method is applied in a
394047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** purely mathematical way, and only to the selected channels.
394147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      ** Turn off SVG composition 'alpha blending'.
394247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      */
394347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      switch( method ) {
394447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case EdgeOutMorphology:
394547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case EdgeInMorphology:
394647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case TopHatMorphology:
394747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case BottomHatMorphology:
39487bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony          if ( IfMagickTrue(verbose) )
3949e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy            (void) FormatLocaleFile(stderr,
3950e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy              "\n%s: Difference with original image",CommandOptionToMnemonic(
3951e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy              MagickMorphologyOptions, method) );
3952feb3e9695150978a5d2372d3fe2f60466a7c8066cristy          (void) CompositeImage(curr_image,image,DifferenceCompositeOp,
395339172408bad7ef2ef00a815fa9abf9979e7857cbcristy            MagickTrue,0,0,exception);
395447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          break;
395547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        case EdgeMorphology:
39567bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony          if ( IfMagickTrue(verbose) )
3957e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy            (void) FormatLocaleFile(stderr,
3958e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy              "\n%s: Difference of Dilate and Erode",CommandOptionToMnemonic(
3959e941a75fe8bf344bc5c06a7f74bb5173c87db115cristy              MagickMorphologyOptions, method) );
3960feb3e9695150978a5d2372d3fe2f60466a7c8066cristy          (void) CompositeImage(curr_image,save_image,DifferenceCompositeOp,
396139172408bad7ef2ef00a815fa9abf9979e7857cbcristy            MagickTrue,0,0,exception);
396247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          save_image = DestroyImage(save_image); /* finished with save image */
396347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          break;
396447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        default:
396547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          break;
3966602ab9b30b644a78a4057da93d838a77391ec0acanthony      }
39679eb4f74649b23c053b308ce1152dce51239450baanthony
396847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /* multi-kernel handling:  re-iterate, or compose results */
396947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if ( kernel->next == (KernelInfo *) NULL )
3970c3e48258f3253188894e783dcdfd03562f7ab2c5anthony        rslt_image = curr_image;   /* just return the resulting image */
397147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else if ( rslt_compose == NoCompositeOp )
39727bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony        { if ( IfMagickTrue(verbose) ) {
3973c3e48258f3253188894e783dcdfd03562f7ab2c5anthony            if ( this_kernel->next != (KernelInfo *) NULL )
39745acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, " (re-iterate)");
3975c3e48258f3253188894e783dcdfd03562f7ab2c5anthony            else
39765acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy              (void) FormatLocaleFile(stderr, " (done)");
3977c3e48258f3253188894e783dcdfd03562f7ab2c5anthony          }
3978c3e48258f3253188894e783dcdfd03562f7ab2c5anthony          rslt_image = curr_image; /* return result, and re-iterate */
39799eb4f74649b23c053b308ce1152dce51239450baanthony        }
398047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else if ( rslt_image == (Image *) NULL)
39817bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony        { if ( IfMagickTrue(verbose) )
39825acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) FormatLocaleFile(stderr, " (save for compose)");
398347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          rslt_image = curr_image;
398447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          curr_image = (Image *) image;  /* continue with original image */
39859eb4f74649b23c053b308ce1152dce51239450baanthony        }
398647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      else
3987ea61f01656bb0f9074677452017cc559e54093faanthony        { /* Add the new 'current' result to the composition
398847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          **
398947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** The removal of any 'Sync' channel flag in the Image Compositon
399047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** below ensures the methematical compose method is applied in a
399147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          ** purely mathematical way, and only to the selected channels.
3992ea61f01656bb0f9074677452017cc559e54093faanthony          ** IE: Turn off SVG composition 'alpha blending'.
399347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          */
39947bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony          if ( IfMagickTrue(verbose) )
39955acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy            (void) FormatLocaleFile(stderr, " (compose \"%s\")",
3996feb3e9695150978a5d2372d3fe2f60466a7c8066cristy              CommandOptionToMnemonic(MagickComposeOptions, rslt_compose) );
399739172408bad7ef2ef00a815fa9abf9979e7857cbcristy          (void) CompositeImage(rslt_image,curr_image,rslt_compose,MagickTrue,
3998feb3e9695150978a5d2372d3fe2f60466a7c8066cristy            0,0,exception);
39990bd8549b4c1659eb72b12eae65f948e0e6e43c4banthony          curr_image = DestroyImage(curr_image);
400047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony          curr_image = (Image *) image;  /* continue with original image */
400147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        }
40027bcfe7f0f9a0028eb9efdb79c842eb50e909dc45anthony      if ( IfMagickTrue(verbose) )
40035acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy        (void) FormatLocaleFile(stderr, "\n");
40044fd27e21043be809d66c8202e779255e5b660d2danthony
400547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      /* loop to the next kernel in a multi-kernel list */
400647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      norm_kernel = norm_kernel->next;
400747f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      if ( rflt_kernel != (KernelInfo *) NULL )
400847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony        rflt_kernel = rflt_kernel->next;
400947f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony      kernel_number++;
401047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    } /* End Loop 2: Loop over each kernel */
40119eb4f74649b23c053b308ce1152dce51239450baanthony
401247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  } /* End Loop 1: compound method interation */
4013602ab9b30b644a78a4057da93d838a77391ec0acanthony
40149eb4f74649b23c053b308ce1152dce51239450baanthony  goto exit_cleanup;
40151b2bc0a7da432e6e1cc0480280402df213faa940anthony
401647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  /* Yes goto's are bad, but it makes cleanup lot more efficient */
40171b2bc0a7da432e6e1cc0480280402df213faa940anthonyerror_cleanup:
4018ea61f01656bb0f9074677452017cc559e54093faanthony  if ( curr_image == rslt_image )
4019ea61f01656bb0f9074677452017cc559e54093faanthony    curr_image = (Image *) NULL;
402047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( rslt_image != (Image *) NULL )
402147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    rslt_image = DestroyImage(rslt_image);
40221b2bc0a7da432e6e1cc0480280402df213faa940anthonyexit_cleanup:
4023ea61f01656bb0f9074677452017cc559e54093faanthony  if ( curr_image == rslt_image || curr_image == image )
4024ea61f01656bb0f9074677452017cc559e54093faanthony    curr_image = (Image *) NULL;
4025ea61f01656bb0f9074677452017cc559e54093faanthony  if ( curr_image != (Image *) NULL )
402647f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    curr_image = DestroyImage(curr_image);
40279eb4f74649b23c053b308ce1152dce51239450baanthony  if ( work_image != (Image *) NULL )
402847f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    work_image = DestroyImage(work_image);
40299eb4f74649b23c053b308ce1152dce51239450baanthony  if ( save_image != (Image *) NULL )
403047f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    save_image = DestroyImage(save_image);
403147f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  if ( reflected_kernel != (KernelInfo *) NULL )
403247f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    reflected_kernel = DestroyKernelInfo(reflected_kernel);
403347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  return(rslt_image);
40349eb4f74649b23c053b308ce1152dce51239450baanthony}
4035a8843c1f815ffad2568ec592d5b446cb1476aab5anthony
40369eb4f74649b23c053b308ce1152dce51239450baanthony
40379eb4f74649b23c053b308ce1152dce51239450baanthony/*
40389eb4f74649b23c053b308ce1152dce51239450baanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40399eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40409eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40419eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
4042f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy%     M o r p h o l o g y I m a g e                                           %
40439eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40449eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40459eb4f74649b23c053b308ce1152dce51239450baanthony%                                                                             %
40469eb4f74649b23c053b308ce1152dce51239450baanthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40479eb4f74649b23c053b308ce1152dce51239450baanthony%
4048a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony%  MorphologyImage() applies a user supplied kernel to the image according to
4049a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony%  the given mophology method.
40509eb4f74649b23c053b308ce1152dce51239450baanthony%
40519eb4f74649b23c053b308ce1152dce51239450baanthony%  This function applies any and all user defined settings before calling
40529eb4f74649b23c053b308ce1152dce51239450baanthony%  the above internal function MorphologyApply().
40539eb4f74649b23c053b308ce1152dce51239450baanthony%
40549eb4f74649b23c053b308ce1152dce51239450baanthony%  User defined settings include...
405522de2722b682eb405b60ec6022a7546df994674eanthony%    * Output Bias for Convolution and correlation ("-define convolve:bias=??")
405622de2722b682eb405b60ec6022a7546df994674eanthony%    * Kernel Scale/normalize settings             ("-define convolve:scale=??")
405746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      This can also includes the addition of a scaled unity kernel.
405822de2722b682eb405b60ec6022a7546df994674eanthony%    * Show Kernel being applied                   ("-define showkernel=1")
405922de2722b682eb405b60ec6022a7546df994674eanthony%
406022de2722b682eb405b60ec6022a7546df994674eanthony%  Other operators that do not want user supplied options interfering,
406122de2722b682eb405b60ec6022a7546df994674eanthony%  especially "convolve:bias" and "showkernel" should use MorphologyApply()
406222de2722b682eb405b60ec6022a7546df994674eanthony%  directly.
40639eb4f74649b23c053b308ce1152dce51239450baanthony%
40649eb4f74649b23c053b308ce1152dce51239450baanthony%  The format of the MorphologyImage method is:
40659eb4f74649b23c053b308ce1152dce51239450baanthony%
40669eb4f74649b23c053b308ce1152dce51239450baanthony%      Image *MorphologyImage(const Image *image,MorphologyMethod method,
4067bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy%        const ssize_t iterations,KernelInfo *kernel,ExceptionInfo *exception)
40689eb4f74649b23c053b308ce1152dce51239450baanthony%
40699eb4f74649b23c053b308ce1152dce51239450baanthony%  A description of each parameter follows:
40709eb4f74649b23c053b308ce1152dce51239450baanthony%
40719eb4f74649b23c053b308ce1152dce51239450baanthony%    o image: the image.
40729eb4f74649b23c053b308ce1152dce51239450baanthony%
40739eb4f74649b23c053b308ce1152dce51239450baanthony%    o method: the morphology method to be applied.
40749eb4f74649b23c053b308ce1152dce51239450baanthony%
40759eb4f74649b23c053b308ce1152dce51239450baanthony%    o iterations: apply the operation this many times (or no change).
40769eb4f74649b23c053b308ce1152dce51239450baanthony%                  A value of -1 means loop until no change found.
40779eb4f74649b23c053b308ce1152dce51239450baanthony%                  How this is applied may depend on the morphology method.
40789eb4f74649b23c053b308ce1152dce51239450baanthony%                  Typically this is a value of 1.
40799eb4f74649b23c053b308ce1152dce51239450baanthony%
40809eb4f74649b23c053b308ce1152dce51239450baanthony%    o kernel: An array of double representing the morphology kernel.
40819eb4f74649b23c053b308ce1152dce51239450baanthony%              Warning: kernel may be normalized for the Convolve method.
40829eb4f74649b23c053b308ce1152dce51239450baanthony%
40839eb4f74649b23c053b308ce1152dce51239450baanthony%    o exception: return any errors or warnings in this structure.
40849eb4f74649b23c053b308ce1152dce51239450baanthony%
40859eb4f74649b23c053b308ce1152dce51239450baanthony*/
4086f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristyMagickExport Image *MorphologyImage(const Image *image,
4087f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const MorphologyMethod method,const ssize_t iterations,
4088f4ad9df69835f9ed5c687a9f93e8a53e23a3258fcristy  const KernelInfo *kernel,ExceptionInfo *exception)
40899eb4f74649b23c053b308ce1152dce51239450baanthony{
40909eb4f74649b23c053b308ce1152dce51239450baanthony  KernelInfo
40919eb4f74649b23c053b308ce1152dce51239450baanthony    *curr_kernel;
40929eb4f74649b23c053b308ce1152dce51239450baanthony
409347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony  CompositeOperator
409447f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony    compose;
409547f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony
40969eb4f74649b23c053b308ce1152dce51239450baanthony  Image
40979eb4f74649b23c053b308ce1152dce51239450baanthony    *morphology_image;
40989eb4f74649b23c053b308ce1152dce51239450baanthony
4099f46d42620631d2581e0b6a56456e203e17c427c8anthony  double
4100f46d42620631d2581e0b6a56456e203e17c427c8anthony    bias;
41019eb4f74649b23c053b308ce1152dce51239450baanthony
410222de2722b682eb405b60ec6022a7546df994674eanthony  curr_kernel = (KernelInfo *) kernel;
410322de2722b682eb405b60ec6022a7546df994674eanthony  bias=0.0;
4104d228c03fa334bae897eee6c2d8721fa48e1577bacristy  compose = UndefinedCompositeOp;  /* use default for method */
410522de2722b682eb405b60ec6022a7546df994674eanthony
410646a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Apply Convolve/Correlate Normalization and Scaling Factors.
410746a369d839971ab627bdb31a93d8bd63e81b65a3anthony   * This is done BEFORE the ShowKernelInfo() function is called so that
410846a369d839971ab627bdb31a93d8bd63e81b65a3anthony   * users can see the results of the 'option:convolve:scale' option.
41099eb4f74649b23c053b308ce1152dce51239450baanthony   */
411022de2722b682eb405b60ec6022a7546df994674eanthony  if ( method == ConvolveMorphology || method == CorrelateMorphology ) {
411128ad1d779b6ca95852e860514185a7a97e06af77anthony      const char
411228ad1d779b6ca95852e860514185a7a97e06af77anthony        *artifact;
4113f46d42620631d2581e0b6a56456e203e17c427c8anthony
411422de2722b682eb405b60ec6022a7546df994674eanthony      /* Get the bias value as it will be needed */
411522de2722b682eb405b60ec6022a7546df994674eanthony      artifact = GetImageArtifact(image,"convolve:bias");
411622de2722b682eb405b60ec6022a7546df994674eanthony      if ( artifact != (const char *) NULL) {
411722de2722b682eb405b60ec6022a7546df994674eanthony        if (IfMagickFalse(IsGeometry(artifact)))
411822de2722b682eb405b60ec6022a7546df994674eanthony          (void) ThrowMagickException(exception,GetMagickModule(),
411922de2722b682eb405b60ec6022a7546df994674eanthony               OptionWarning,"InvalidSetting","'%s' '%s'",
412022de2722b682eb405b60ec6022a7546df994674eanthony               "convolve:bias",artifact);
412122de2722b682eb405b60ec6022a7546df994674eanthony        else
412222de2722b682eb405b60ec6022a7546df994674eanthony          bias=StringToDoubleInterval(artifact,(double) QuantumRange+1.0);
412322de2722b682eb405b60ec6022a7546df994674eanthony      }
412422de2722b682eb405b60ec6022a7546df994674eanthony
412522de2722b682eb405b60ec6022a7546df994674eanthony      /* Scale kernel according to user wishes */
41269eb4f74649b23c053b308ce1152dce51239450baanthony      artifact = GetImageArtifact(image,"convolve:scale");
4127e8d2f55369b0c848618327cd2e6f2291ec4e4b2canthony      if ( artifact != (const char *)NULL ) {
412822de2722b682eb405b60ec6022a7546df994674eanthony        if (IfMagickFalse(IsGeometry(artifact)))
412922de2722b682eb405b60ec6022a7546df994674eanthony          (void) ThrowMagickException(exception,GetMagickModule(),
413022de2722b682eb405b60ec6022a7546df994674eanthony               OptionWarning,"InvalidSetting","'%s' '%s'",
413122de2722b682eb405b60ec6022a7546df994674eanthony               "convolve:scale",artifact);
413222de2722b682eb405b60ec6022a7546df994674eanthony        else {
413322de2722b682eb405b60ec6022a7546df994674eanthony          if ( curr_kernel == kernel )
413422de2722b682eb405b60ec6022a7546df994674eanthony            curr_kernel = CloneKernelInfo(kernel);
413522de2722b682eb405b60ec6022a7546df994674eanthony          if (curr_kernel == (KernelInfo *) NULL)
413622de2722b682eb405b60ec6022a7546df994674eanthony            return((Image *) NULL);
413722de2722b682eb405b60ec6022a7546df994674eanthony          ScaleGeometryKernelInfo(curr_kernel, artifact);
41389eb4f74649b23c053b308ce1152dce51239450baanthony        }
41399eb4f74649b23c053b308ce1152dce51239450baanthony      }
41409eb4f74649b23c053b308ce1152dce51239450baanthony    }
41419eb4f74649b23c053b308ce1152dce51239450baanthony
41429eb4f74649b23c053b308ce1152dce51239450baanthony  /* display the (normalized) kernel via stderr */
414348656f2d1ca9ac9979eac32052e4cdc4958c9010cristy  if ( IfStringTrue(GetImageArtifact(image,"showkernel"))
414448656f2d1ca9ac9979eac32052e4cdc4958c9010cristy    || IfStringTrue(GetImageArtifact(image,"convolve:showkernel"))
414548656f2d1ca9ac9979eac32052e4cdc4958c9010cristy    || IfStringTrue(GetImageArtifact(image,"morphology:showkernel")) )
41469eb4f74649b23c053b308ce1152dce51239450baanthony    ShowKernelInfo(curr_kernel);
41479eb4f74649b23c053b308ce1152dce51239450baanthony
41483206678d008425bc56dd2dbad002f2bb26299dc2anthony  /* Override the default handling of multi-kernel morphology results
41493206678d008425bc56dd2dbad002f2bb26299dc2anthony   * If 'Undefined' use the default method
41503206678d008425bc56dd2dbad002f2bb26299dc2anthony   * If 'None' (default for 'Convolve') re-iterate previous result
41513206678d008425bc56dd2dbad002f2bb26299dc2anthony   * Otherwise merge resulting images using compose method given.
41523206678d008425bc56dd2dbad002f2bb26299dc2anthony   * Default for 'HitAndMiss' is 'Lighten'.
415347f5d0605b372bda1dc04e401c6ac8cfc4a89dadanthony   */
415428ad1d779b6ca95852e860514185a7a97e06af77anthony  { const char
415528ad1d779b6ca95852e860514185a7a97e06af77anthony      *artifact;
415622de2722b682eb405b60ec6022a7546df994674eanthony    ssize_t
415722de2722b682eb405b60ec6022a7546df994674eanthony      parse;
415822de2722b682eb405b60ec6022a7546df994674eanthony
4159f46d42620631d2581e0b6a56456e203e17c427c8anthony    artifact = GetImageArtifact(image,"morphology:compose");
416022de2722b682eb405b60ec6022a7546df994674eanthony    if ( artifact != (const char *) NULL) {
416122de2722b682eb405b60ec6022a7546df994674eanthony      parse=ParseCommandOption(MagickComposeOptions,
416270b5ea135e0cb4ed3c564fab9a15a7b1b53f1d9bcristy        MagickFalse,artifact);
416322de2722b682eb405b60ec6022a7546df994674eanthony      if ( parse < 0 )
416422de2722b682eb405b60ec6022a7546df994674eanthony        (void) ThrowMagickException(exception,GetMagickModule(),
416522de2722b682eb405b60ec6022a7546df994674eanthony             OptionWarning,"UnrecognizedComposeOperator","'%s' '%s'",
416622de2722b682eb405b60ec6022a7546df994674eanthony             "morphology:compose",artifact);
416722de2722b682eb405b60ec6022a7546df994674eanthony      else
416822de2722b682eb405b60ec6022a7546df994674eanthony        compose=(CompositeOperator)parse;
416922de2722b682eb405b60ec6022a7546df994674eanthony    }
417028ad1d779b6ca95852e860514185a7a97e06af77anthony  }
41719eb4f74649b23c053b308ce1152dce51239450baanthony  /* Apply the Morphology */
4172f46d42620631d2581e0b6a56456e203e17c427c8anthony  morphology_image = MorphologyApply(image,method,iterations,
4173f46d42620631d2581e0b6a56456e203e17c427c8anthony    curr_kernel,compose,bias,exception);
41749eb4f74649b23c053b308ce1152dce51239450baanthony
41759eb4f74649b23c053b308ce1152dce51239450baanthony  /* Cleanup and Exit */
41769eb4f74649b23c053b308ce1152dce51239450baanthony  if ( curr_kernel != kernel )
41771b2bc0a7da432e6e1cc0480280402df213faa940anthony    curr_kernel=DestroyKernelInfo(curr_kernel);
41789eb4f74649b23c053b308ce1152dce51239450baanthony  return(morphology_image);
41799eb4f74649b23c053b308ce1152dce51239450baanthony}
418083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
418183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony/*
418283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
418383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
418483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
418583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
41864fd27e21043be809d66c8202e779255e5b660d2danthony+     R o t a t e K e r n e l I n f o                                         %
418783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
418883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
418983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
419083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
419183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
419246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  RotateKernelInfo() rotates the kernel by the angle given.
419346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
419446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  Currently it is restricted to 90 degree angles, of either 1D kernels
419546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  or square kernels. And 'circular' rotations of 45 degrees for 3x3 kernels.
419646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  It will ignore usless rotations for specific 'named' built-in kernels.
419783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
41984fd27e21043be809d66c8202e779255e5b660d2danthony%  The format of the RotateKernelInfo method is:
419983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
42004fd27e21043be809d66c8202e779255e5b660d2danthony%      void RotateKernelInfo(KernelInfo *kernel, double angle)
420183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
420283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  A description of each parameter follows:
420383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
420483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o kernel: the Morphology/Convolution kernel
420583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
420683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o angle: angle to rotate in degrees
420783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
420846a369d839971ab627bdb31a93d8bd63e81b65a3anthony% This function is currently internal to this module only, but can be exported
420946a369d839971ab627bdb31a93d8bd63e81b65a3anthony% to other modules if needed.
421083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony*/
42114fd27e21043be809d66c8202e779255e5b660d2danthonystatic void RotateKernelInfo(KernelInfo *kernel, double angle)
421283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony{
42131b2bc0a7da432e6e1cc0480280402df213faa940anthony  /* angle the lower kernels first */
42141b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL)
42151b2bc0a7da432e6e1cc0480280402df213faa940anthony    RotateKernelInfo(kernel->next, angle);
42161b2bc0a7da432e6e1cc0480280402df213faa940anthony
421783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* WARNING: Currently assumes the kernel (rightly) is horizontally symetrical
421883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  **
421983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  ** TODO: expand beyond simple 90 degree rotates, flips and flops
422083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  */
422183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
422283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  /* Modulus the angle */
422383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  angle = fmod(angle, 360.0);
422483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  if ( angle < 0 )
422583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    angle += 360.0;
422683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
42273c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  if ( 337.5 < angle || angle <= 22.5 )
422843c4925e5305a26e48d68f7893e94f55d0831c39anthony    return;   /* Near zero angle - no change! - At least not at this time */
422983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
42303dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony  /* Handle special cases */
423183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  switch (kernel->type) {
423283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* These built-in kernels are cylindrical kernels, rotating is useless */
423383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case GaussianKernel:
4234501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case DoGKernel:
4235501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony    case LoGKernel:
423683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case DiskKernel:
42373dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case PeaksKernel:
42383dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case LaplacianKernel:
423983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case ChebyshevKernel:
4240bee715c4c0fd9efe6e21d8627ae8664434df7750anthony    case ManhattanKernel:
424183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case EuclideanKernel:
424283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      return;
424383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
424483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* These may be rotatable at non-90 angles in the future */
424583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* but simply rotating them in multiples of 90 degrees is useless */
424683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case SquareKernel:
424783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case DiamondKernel:
424883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case PlusKernel:
42493dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    case CrossKernel:
425083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      return;
425183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
425283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* These only allows a +/-90 degree rotation (by transpose) */
425383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    /* A 180 degree rotation is useless */
425483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    case BlurKernel:
425583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      if ( 135.0 < angle && angle <= 225.0 )
425683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        return;
425783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      if ( 225.0 < angle && angle <= 315.0 )
425883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        angle -= 180;
425983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      break;
426083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
42613dd0f620e7a1d12f747ce167844cd7269bfa9f12anthony    default:
426283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      break;
426383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  }
426457fe7a498c1302232dac8466864e84b12fad0807anthony  /* Attempt rotations by 45 degrees  -- 3x3 kernels only */
42653c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  if ( 22.5 < fmod(angle,90.0) && fmod(angle,90.0) <= 67.5 )
42663c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    {
42673c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      if ( kernel->width == 3 && kernel->height == 3 )
42683c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        { /* Rotate a 3x3 square by 45 degree angle */
4269a19f1d70e9a9f88279c4ecafe6dfafc1f9a09599cristy          double t  = kernel->values[0];
427043c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[0] = kernel->values[3];
427143c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[3] = kernel->values[6];
427243c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[6] = kernel->values[7];
427343c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[7] = kernel->values[8];
427443c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[8] = kernel->values[5];
427543c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[5] = kernel->values[2];
427643c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[2] = kernel->values[1];
427743c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->values[1] = t;
42781d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          /* rotate non-centered origin */
42791d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          if ( kernel->x != 1 || kernel->y != 1 ) {
4280bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            ssize_t x,y;
4281bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            x = (ssize_t) kernel->x-1;
4282bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy            y = (ssize_t) kernel->y-1;
42831d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                 if ( x == y  ) x = 0;
42841d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            else if ( x == 0  ) x = -y;
42851d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            else if ( x == -y ) y = 0;
42861d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            else if ( y == 0  ) y = x;
4287ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->x = (ssize_t) x+1;
4288ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->y = (ssize_t) y+1;
42891d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          }
429043c4925e5305a26e48d68f7893e94f55d0831c39anthony          angle = fmod(angle+315.0, 360.0);  /* angle reduced 45 degrees */
429143c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->angle = fmod(kernel->angle+45.0, 360.0);
42923c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
42933c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      else
42943c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        perror("Unable to rotate non-3x3 kernel by 45 degrees");
42953c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    }
42963c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  if ( 45.0 < fmod(angle, 180.0)  && fmod(angle,180.0) <= 135.0 )
42973c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    {
42983c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      if ( kernel->width == 1 || kernel->height == 1 )
42994c08aed51c5899665ade97263692328eea4af106cristy        { /* Do a transpose of a 1 dimensional kernel,
4300bfb635a40fc92bfcbdf36c018a9e64a9182d809canthony          ** which results in a fast 90 degree rotation of some type.
43013c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          */
4302bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          ssize_t
43033c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony            t;
4304bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          t = (ssize_t) kernel->width;
43053c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->width = kernel->height;
4306bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          kernel->height = (size_t) t;
43073c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          t = kernel->x;
43083c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->x = kernel->y;
43093c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony          kernel->y = t;
431043c4925e5305a26e48d68f7893e94f55d0831c39anthony          if ( kernel->width == 1 ) {
431143c4925e5305a26e48d68f7893e94f55d0831c39anthony            angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
431243c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel->angle = fmod(kernel->angle+90.0, 360.0);
431343c4925e5305a26e48d68f7893e94f55d0831c39anthony          } else {
431443c4925e5305a26e48d68f7893e94f55d0831c39anthony            angle = fmod(angle+90.0, 360.0);   /* angle increased 90 degrees */
431543c4925e5305a26e48d68f7893e94f55d0831c39anthony            kernel->angle = fmod(kernel->angle+270.0, 360.0);
431643c4925e5305a26e48d68f7893e94f55d0831c39anthony          }
43173c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
43183c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      else if ( kernel->width == kernel->height )
43193c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        { /* Rotate a square array of values by 90 degrees */
4320d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy          { register ssize_t
43211d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony              i,j,x,y;
4322d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy
4323d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            register MagickRealType
43241d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony              *k,t;
4325d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy
43261d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony            k=kernel->values;
4327d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy            for( i=0, x=(ssize_t) kernel->width-1;  i<=x;   i++, x--)
4328d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy              for( j=0, y=(ssize_t) kernel->height-1;  j<y;   j++, y--)
43291d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                { t                    = k[i+j*kernel->width];
43301d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[i+j*kernel->width] = k[j+x*kernel->width];
43311d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[j+x*kernel->width] = k[x+y*kernel->width];
43321d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[x+y*kernel->width] = k[y+i*kernel->width];
43331d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                  k[y+i*kernel->width] = t;
43341d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony                }
43351d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          }
43361d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          /* rotate the origin - relative to center of array */
4337bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy          { register ssize_t x,y;
4338eaedf06777741da32408da72c1e512975c600c48cristy            x = (ssize_t) (kernel->x*2-kernel->width+1);
4339eaedf06777741da32408da72c1e512975c600c48cristy            y = (ssize_t) (kernel->y*2-kernel->height+1);
4340ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->x = (ssize_t) ( -y +(ssize_t) kernel->width-1)/2;
4341ecd0ab5350482e4a2ea003705e3e0b2120b66384cristy            kernel->y = (ssize_t) ( +x +(ssize_t) kernel->height-1)/2;
43421d45eb9d30cfb0cb4a15d3c2d0fcfe6d72a53664anthony          }
434343c4925e5305a26e48d68f7893e94f55d0831c39anthony          angle = fmod(angle+270.0, 360.0);     /* angle reduced 90 degrees */
434443c4925e5305a26e48d68f7893e94f55d0831c39anthony          kernel->angle = fmod(kernel->angle+90.0, 360.0);
43453c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        }
43463c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony      else
43473c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony        perror("Unable to rotate a non-square, non-linear kernel 90 degrees");
43483c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony    }
434983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  if ( 135.0 < angle && angle <= 225.0 )
435083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    {
435143c4925e5305a26e48d68f7893e94f55d0831c39anthony      /* For a 180 degree rotation - also know as a reflection
435243c4925e5305a26e48d68f7893e94f55d0831c39anthony       * This is actually a very very common operation!
435343c4925e5305a26e48d68f7893e94f55d0831c39anthony       * Basically all that is needed is a reversal of the kernel data!
435443c4925e5305a26e48d68f7893e94f55d0831c39anthony       * And a reflection of the origon
435543c4925e5305a26e48d68f7893e94f55d0831c39anthony       */
4356a23c649213358645994ad489b8c9a317c60111afcristy      MagickRealType
4357a96f2494a8e79144a225056be9545cc75e868137cristy        t;
4358a96f2494a8e79144a225056be9545cc75e868137cristy
4359d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy      register MagickRealType
4360a96f2494a8e79144a225056be9545cc75e868137cristy        *k;
4361a96f2494a8e79144a225056be9545cc75e868137cristy
4362a96f2494a8e79144a225056be9545cc75e868137cristy      ssize_t
4363a96f2494a8e79144a225056be9545cc75e868137cristy        i,
4364a96f2494a8e79144a225056be9545cc75e868137cristy        j;
436583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
436683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony      k=kernel->values;
4367e42f658533644aecb733785ffd91b286d6778deacristy      j=(ssize_t) (kernel->width*kernel->height-1);
4368e42f658533644aecb733785ffd91b286d6778deacristy      for (i=0;  i < j;  i++, j--)
436983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony        t=k[i],  k[i]=k[j],  k[j]=t;
437083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
4371bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->x = (ssize_t) kernel->width  - kernel->x - 1;
4372bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy      kernel->y = (ssize_t) kernel->height - kernel->y - 1;
437343c4925e5305a26e48d68f7893e94f55d0831c39anthony      angle = fmod(angle-180.0, 360.0);   /* angle+180 degrees */
437443c4925e5305a26e48d68f7893e94f55d0831c39anthony      kernel->angle = fmod(kernel->angle+180.0, 360.0);
437583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony    }
43763c10fc8b1dff7f2c7c0681e154c7cb7cdef2b835anthony  /* At this point angle should at least between -45 (315) and +45 degrees
437783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   * In the future some form of non-orthogonal angled rotates could be
437883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   * performed here, posibily with a linear kernel restriction.
437983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony   */
438083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
438183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  return;
438283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony}
438383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony
438483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony/*
438583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
438683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
438783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
438883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
438946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%     S c a l e G e o m e t r y K e r n e l I n f o                           %
439046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
439146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
439246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
439346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
439446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
439546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  ScaleGeometryKernelInfo() takes a geometry argument string, typically
439646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  provided as a  "-set option:convolve:scale {geometry}" user setting,
439746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  and modifies the kernel according to the parsed arguments of that setting.
439846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
439946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The first argument (and any normalization flags) are passed to
440046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  ScaleKernelInfo() to scale/normalize the kernel.  The second argument
440146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  is then passed to UnityAddKernelInfo() to add a scled unity kernel
440246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  into the scaled/normalized kernel.
440346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
4404ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy%  The format of the ScaleGeometryKernelInfo method is:
440546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
4406ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy%      void ScaleGeometryKernelInfo(KernelInfo *kernel,
4407ceff20ad7a403eeb4cfc034b38e157ce9601bc2ccristy%        const double scaling_factor,const MagickStatusType normalize_flags)
440846a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
440946a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  A description of each parameter follows:
441046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
441146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o kernel: the Morphology/Convolution kernel to modify
441246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
441346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%    o geometry:
441446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%             The geometry string to parse, typically from the user provided
441546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%             "-set option:convolve:scale {geometry}" setting.
441646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%
441746a369d839971ab627bdb31a93d8bd63e81b65a3anthony*/
441846a369d839971ab627bdb31a93d8bd63e81b65a3anthonyMagickExport void ScaleGeometryKernelInfo (KernelInfo *kernel,
4419954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy  const char *geometry)
442046a369d839971ab627bdb31a93d8bd63e81b65a3anthony{
4421ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy  MagickStatusType
442246a369d839971ab627bdb31a93d8bd63e81b65a3anthony    flags;
4423ac245f8a51ea65b085d751c41d8ca4b426bdfe5bcristy
442446a369d839971ab627bdb31a93d8bd63e81b65a3anthony  GeometryInfo
442546a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args;
442646a369d839971ab627bdb31a93d8bd63e81b65a3anthony
442746a369d839971ab627bdb31a93d8bd63e81b65a3anthony  SetGeometryInfo(&args);
442822de2722b682eb405b60ec6022a7546df994674eanthony  flags = ParseGeometry(geometry, &args);
442946a369d839971ab627bdb31a93d8bd63e81b65a3anthony
443046a369d839971ab627bdb31a93d8bd63e81b65a3anthony#if 0
443146a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* For Debugging Geometry Input */
44325acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy  (void) FormatLocaleFile(stderr, "Geometry = 0x%04X : %lg x %lg %+lg %+lg\n",
443346a369d839971ab627bdb31a93d8bd63e81b65a3anthony       flags, args.rho, args.sigma, args.xi, args.psi );
443446a369d839971ab627bdb31a93d8bd63e81b65a3anthony#endif
443546a369d839971ab627bdb31a93d8bd63e81b65a3anthony
443646a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & PercentValue) != 0 )      /* Handle Percentage flag*/
443746a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args.rho *= 0.01,  args.sigma *= 0.01;
443846a369d839971ab627bdb31a93d8bd63e81b65a3anthony
443946a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & RhoValue) == 0 )          /* Set Defaults for missing args */
444046a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args.rho = 1.0;
444146a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & SigmaValue) == 0 )
444246a369d839971ab627bdb31a93d8bd63e81b65a3anthony    args.sigma = 0.0;
444346a369d839971ab627bdb31a93d8bd63e81b65a3anthony
444446a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Scale/Normalize the input kernel */
4445d228c03fa334bae897eee6c2d8721fa48e1577bacristy  ScaleKernelInfo(kernel, args.rho, (GeometryFlags) flags);
444646a369d839971ab627bdb31a93d8bd63e81b65a3anthony
444746a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Add Unity Kernel, for blending with original */
444846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( (flags & SigmaValue) != 0 )
444946a369d839971ab627bdb31a93d8bd63e81b65a3anthony    UnityAddKernelInfo(kernel, args.sigma);
445046a369d839971ab627bdb31a93d8bd63e81b65a3anthony
445146a369d839971ab627bdb31a93d8bd63e81b65a3anthony  return;
445246a369d839971ab627bdb31a93d8bd63e81b65a3anthony}
445346a369d839971ab627bdb31a93d8bd63e81b65a3anthony/*
445446a369d839971ab627bdb31a93d8bd63e81b65a3anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
445546a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
445646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
445746a369d839971ab627bdb31a93d8bd63e81b65a3anthony%                                                                             %
44586771f1e8987fa49f52d4176281a2e8524b8e31cbcristy%     S c a l e K e r n e l I n f o                                           %
4459cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4460cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4461cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4462cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4463cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
44641b2bc0a7da432e6e1cc0480280402df213faa940anthony%  ScaleKernelInfo() scales the given kernel list by the given amount, with or
44651b2bc0a7da432e6e1cc0480280402df213faa940anthony%  without normalization of the sum of the kernel values (as per given flags).
4466999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4467999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  By default (no flags given) the values within the kernel is scaled
44681b2bc0a7da432e6e1cc0480280402df213faa940anthony%  directly using given scaling factor without change.
4469999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
447046a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  If either of the two 'normalize_flags' are given the kernel will first be
447146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  normalized and then further scaled by the scaling factor value given.
4472999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4473999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  Kernel normalization ('normalize_flags' given) is designed to ensure that
4474999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  any use of the kernel scaling factor with 'Convolve' or 'Correlate'
44751b2bc0a7da432e6e1cc0480280402df213faa940anthony%  morphology methods will fall into -1.0 to +1.0 range.  Note that for
44761b2bc0a7da432e6e1cc0480280402df213faa940anthony%  non-HDRI versions of IM this may cause images to have any negative results
44771b2bc0a7da432e6e1cc0480280402df213faa940anthony%  clipped, unless some 'bias' is used.
4478999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4479999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  More specifically.  Kernels which only contain positive values (such as a
4480999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  'Gaussian' kernel) will be scaled so that those values sum to +1.0,
44811b2bc0a7da432e6e1cc0480280402df213faa940anthony%  ensuring a 0.0 to +1.0 output range for non-HDRI images.
4482999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4483999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For Kernels that contain some negative values, (such as 'Sharpen' kernels)
4484999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  the kernel will be scaled by the absolute of the sum of kernel values, so
4485999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  that it will generally fall within the +/- 1.0 range.
4486cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4487999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For kernels whose values sum to zero, (such as 'Laplician' kernels) kernel
4488999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  will be scaled by just the sum of the postive values, so that its output
4489999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  range will again fall into the  +/- 1.0 range.
4490cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4491999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  For special kernels designed for locating shapes using 'Correlate', (often
4492999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  only containing +1 and -1 values, representing foreground/brackground
4493999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  matching) a special normalization method is provided to scale the positive
44941e7f7bcae3c8dc9e7a4f8bfb1fbe5980ef15d145glennrp%  values separately to those of the negative values, so the kernel will be
4495999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  forced to become a zero-sum kernel better suited to such searches.
4496999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
44971b2bc0a7da432e6e1cc0480280402df213faa940anthony%  WARNING: Correct normalization of the kernel assumes that the '*_range'
4498999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  attributes within the kernel structure have been correctly set during the
4499999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  kernels creation.
4500999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4501999bb2c20aa9d42875bb5adba44951988d4ae354anthony%  NOTE: The values used for 'normalize_flags' have been selected specifically
450246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  to match the use of geometry options, so that '!' means NormalizeValue, '^'
450346a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  means CorrelateNormalizeValue.  All other GeometryFlags values are ignored.
4504cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
45054fd27e21043be809d66c8202e779255e5b660d2danthony%  The format of the ScaleKernelInfo method is:
4506cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4507999bb2c20aa9d42875bb5adba44951988d4ae354anthony%      void ScaleKernelInfo(KernelInfo *kernel, const double scaling_factor,
4508999bb2c20aa9d42875bb5adba44951988d4ae354anthony%               const MagickStatusType normalize_flags )
4509cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4510cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  A description of each parameter follows:
4511cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4512cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%    o kernel: the Morphology/Convolution kernel
4513cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4514999bb2c20aa9d42875bb5adba44951988d4ae354anthony%    o scaling_factor:
4515999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             multiply all values (after normalization) by this factor if not
4516999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             zero.  If the kernel is normalized regardless of any flags.
4517999bb2c20aa9d42875bb5adba44951988d4ae354anthony%
4518999bb2c20aa9d42875bb5adba44951988d4ae354anthony%    o normalize_flags:
4519999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             GeometryFlags defining normalization method to use.
4520999bb2c20aa9d42875bb5adba44951988d4ae354anthony%             specifically: NormalizeValue, CorrelateNormalizeValue,
4521999bb2c20aa9d42875bb5adba44951988d4ae354anthony%                           and/or PercentValue
4522cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4523cc6c836da2a53b6023b716e4973090a6714dc3b0anthony*/
45246771f1e8987fa49f52d4176281a2e8524b8e31cbcristyMagickExport void ScaleKernelInfo(KernelInfo *kernel,
45256771f1e8987fa49f52d4176281a2e8524b8e31cbcristy  const double scaling_factor,const GeometryFlags normalize_flags)
4526cc6c836da2a53b6023b716e4973090a6714dc3b0anthony{
4527999bb2c20aa9d42875bb5adba44951988d4ae354anthony  register double
4528999bb2c20aa9d42875bb5adba44951988d4ae354anthony    pos_scale,
4529999bb2c20aa9d42875bb5adba44951988d4ae354anthony    neg_scale;
4530999bb2c20aa9d42875bb5adba44951988d4ae354anthony
4531954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy  register ssize_t
4532954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy    i;
4533954d6e5af31b0cd13b9effa91210044b1e8f68f2cristy
453446a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* do the other kernels in a multi-kernel list first */
45351b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL)
45361b2bc0a7da432e6e1cc0480280402df213faa940anthony    ScaleKernelInfo(kernel->next, scaling_factor, normalize_flags);
45371b2bc0a7da432e6e1cc0480280402df213faa940anthony
453846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Normalization of Kernel */
4539999bb2c20aa9d42875bb5adba44951988d4ae354anthony  pos_scale = 1.0;
4540999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( (normalize_flags&NormalizeValue) != 0 ) {
4541b978e458a8e1f210bcb580951cf623687236b2fecristy    if ( fabs(kernel->positive_range + kernel->negative_range) >= MagickEpsilon )
4542f4e0031305baeb01c89cfd2842cbbec021883550anthony      /* non-zero-summing kernel (generally positive) */
4543999bb2c20aa9d42875bb5adba44951988d4ae354anthony      pos_scale = fabs(kernel->positive_range + kernel->negative_range);
4544cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    else
4545f4e0031305baeb01c89cfd2842cbbec021883550anthony      /* zero-summing kernel */
4546f4e0031305baeb01c89cfd2842cbbec021883550anthony      pos_scale = kernel->positive_range;
4547999bb2c20aa9d42875bb5adba44951988d4ae354anthony  }
454846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Force kernel into a normalized zero-summing kernel */
4549999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( (normalize_flags&CorrelateNormalizeValue) != 0 ) {
4550b978e458a8e1f210bcb580951cf623687236b2fecristy    pos_scale = ( fabs(kernel->positive_range) >= MagickEpsilon )
4551999bb2c20aa9d42875bb5adba44951988d4ae354anthony                 ? kernel->positive_range : 1.0;
4552b978e458a8e1f210bcb580951cf623687236b2fecristy    neg_scale = ( fabs(kernel->negative_range) >= MagickEpsilon )
4553999bb2c20aa9d42875bb5adba44951988d4ae354anthony                 ? -kernel->negative_range : 1.0;
4554999bb2c20aa9d42875bb5adba44951988d4ae354anthony  }
4555999bb2c20aa9d42875bb5adba44951988d4ae354anthony  else
4556999bb2c20aa9d42875bb5adba44951988d4ae354anthony    neg_scale = pos_scale;
4557999bb2c20aa9d42875bb5adba44951988d4ae354anthony
4558999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* finialize scaling_factor for positive and negative components */
4559999bb2c20aa9d42875bb5adba44951988d4ae354anthony  pos_scale = scaling_factor/pos_scale;
4560999bb2c20aa9d42875bb5adba44951988d4ae354anthony  neg_scale = scaling_factor/neg_scale;
4561cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4562bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  for (i=0; i < (ssize_t) (kernel->width*kernel->height); i++)
4563a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony    if ( ! IfNaN(kernel->values[i]) )
4564999bb2c20aa9d42875bb5adba44951988d4ae354anthony      kernel->values[i] *= (kernel->values[i] >= 0) ? pos_scale : neg_scale;
4565999bb2c20aa9d42875bb5adba44951988d4ae354anthony
4566999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* convolution output range */
4567999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->positive_range *= pos_scale;
4568999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->negative_range *= neg_scale;
4569999bb2c20aa9d42875bb5adba44951988d4ae354anthony  /* maximum and minimum values in kernel */
4570999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->maximum *= (kernel->maximum >= 0.0) ? pos_scale : neg_scale;
4571999bb2c20aa9d42875bb5adba44951988d4ae354anthony  kernel->minimum *= (kernel->minimum >= 0.0) ? pos_scale : neg_scale;
4572999bb2c20aa9d42875bb5adba44951988d4ae354anthony
457346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* swap kernel settings if user's scaling factor is negative */
4574999bb2c20aa9d42875bb5adba44951988d4ae354anthony  if ( scaling_factor < MagickEpsilon ) {
4575999bb2c20aa9d42875bb5adba44951988d4ae354anthony    double t;
4576999bb2c20aa9d42875bb5adba44951988d4ae354anthony    t = kernel->positive_range;
4577999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->positive_range = kernel->negative_range;
4578999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->negative_range = t;
4579999bb2c20aa9d42875bb5adba44951988d4ae354anthony    t = kernel->maximum;
4580999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->maximum = kernel->minimum;
4581999bb2c20aa9d42875bb5adba44951988d4ae354anthony    kernel->minimum = 1;
4582999bb2c20aa9d42875bb5adba44951988d4ae354anthony  }
4583cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4584cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  return;
4585cc6c836da2a53b6023b716e4973090a6714dc3b0anthony}
4586cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4587cc6c836da2a53b6023b716e4973090a6714dc3b0anthony/*
4588cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4589cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4590cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4591cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
459246a369d839971ab627bdb31a93d8bd63e81b65a3anthony%     S h o w K e r n e l I n f o                                             %
459383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
459483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
459583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%                                                                             %
459683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
459783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
45984fd27e21043be809d66c8202e779255e5b660d2danthony%  ShowKernelInfo() outputs the details of the given kernel defination to
45994fd27e21043be809d66c8202e779255e5b660d2danthony%  standard error, generally due to a users 'showkernel' option request.
460083ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
460183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  The format of the ShowKernel method is:
460283ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
460357fe7a498c1302232dac8466864e84b12fad0807anthony%      void ShowKernelInfo(const KernelInfo *kernel)
460483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
460583ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%  A description of each parameter follows:
460683ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
460783ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%    o kernel: the Morphology/Convolution kernel
460883ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony%
460983ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony*/
4610433d11887841b922ec5e6805f9fdd240c320b92ecristyMagickPrivate void ShowKernelInfo(const KernelInfo *kernel)
461183ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony{
461257fe7a498c1302232dac8466864e84b12fad0807anthony  const KernelInfo
46137a01dcf50ce12cb2a789bedff51e9345f022432eanthony    *k;
46147a01dcf50ce12cb2a789bedff51e9345f022432eanthony
4615bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  size_t
46167a01dcf50ce12cb2a789bedff51e9345f022432eanthony    c, i, u, v;
46177a01dcf50ce12cb2a789bedff51e9345f022432eanthony
46187a01dcf50ce12cb2a789bedff51e9345f022432eanthony  for (c=0, k=kernel;  k != (KernelInfo *) NULL;  c++, k=k->next ) {
46197a01dcf50ce12cb2a789bedff51e9345f022432eanthony
46205acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "Kernel");
46217a01dcf50ce12cb2a789bedff51e9345f022432eanthony    if ( kernel->next != (KernelInfo *) NULL )
46225acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " #%lu", (unsigned long) c );
46235acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, " \"%s",
4624042ee78fa9004bf1ac6a95f09d9d1faca631dda1cristy          CommandOptionToMnemonic(MagickKernelOptions, k->type) );
4625b978e458a8e1f210bcb580951cf623687236b2fecristy    if ( fabs(k->angle) >= MagickEpsilon )
46265acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, "@%lg", k->angle);
46275acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "\" of size %lux%lu%+ld%+ld",(unsigned long)
46281e604812fad85bb96f757a2393015ae3d061c39acristy      k->width,(unsigned long) k->height,(long) k->x,(long) k->y);
46295acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr,
46307a01dcf50ce12cb2a789bedff51e9345f022432eanthony          " with values from %.*lg to %.*lg\n",
46317a01dcf50ce12cb2a789bedff51e9345f022432eanthony          GetMagickPrecision(), k->minimum,
46327a01dcf50ce12cb2a789bedff51e9345f022432eanthony          GetMagickPrecision(), k->maximum);
46335acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy    (void) FormatLocaleFile(stderr, "Forming a output range from %.*lg to %.*lg",
46347a01dcf50ce12cb2a789bedff51e9345f022432eanthony          GetMagickPrecision(), k->negative_range,
463546a369d839971ab627bdb31a93d8bd63e81b65a3anthony          GetMagickPrecision(), k->positive_range);
463646a369d839971ab627bdb31a93d8bd63e81b65a3anthony    if ( fabs(k->positive_range+k->negative_range) < MagickEpsilon )
46375acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " (Zero-Summing)\n");
463846a369d839971ab627bdb31a93d8bd63e81b65a3anthony    else if ( fabs(k->positive_range+k->negative_range-1.0) < MagickEpsilon )
46395acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " (Normalized)\n");
464046a369d839971ab627bdb31a93d8bd63e81b65a3anthony    else
46415acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, " (Sum %.*lg)\n",
464246a369d839971ab627bdb31a93d8bd63e81b65a3anthony          GetMagickPrecision(), k->positive_range+k->negative_range);
464343c4925e5305a26e48d68f7893e94f55d0831c39anthony    for (i=v=0; v < k->height; v++) {
46445acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr, "%2lu:", (unsigned long) v );
464543c4925e5305a26e48d68f7893e94f55d0831c39anthony      for (u=0; u < k->width; u++, i++)
4646a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony        if ( IfNaN(k->values[i]) )
46475acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr," %*s", GetMagickPrecision()+3, "nan");
46487a01dcf50ce12cb2a789bedff51e9345f022432eanthony        else
46495acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy          (void) FormatLocaleFile(stderr," %*.*lg", GetMagickPrecision()+3,
4650d1e1c2296e4765b36d6a008a07d1763ac876bf77cristy              GetMagickPrecision(), (double) k->values[i]);
46515acdd94fa3dabd964fcad53b4948ed7cac2886b1cristy      (void) FormatLocaleFile(stderr,"\n");
46527a01dcf50ce12cb2a789bedff51e9345f022432eanthony    }
465383ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony  }
465483ba99b334176f4c6f9c8ab3ce3ae74d54f55208anthony}
4655cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4656cc6c836da2a53b6023b716e4973090a6714dc3b0anthony/*
4657cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4658cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4659cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4660cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
466143c4925e5305a26e48d68f7893e94f55d0831c39anthony%     U n i t y A d d K e r n a l I n f o                                     %
466243c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
466343c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
466443c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
466543c4925e5305a26e48d68f7893e94f55d0831c39anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
466643c4925e5305a26e48d68f7893e94f55d0831c39anthony%
466743c4925e5305a26e48d68f7893e94f55d0831c39anthony%  UnityAddKernelInfo() Adds a given amount of the 'Unity' Convolution Kernel
466843c4925e5305a26e48d68f7893e94f55d0831c39anthony%  to the given pre-scaled and normalized Kernel.  This in effect adds that
466943c4925e5305a26e48d68f7893e94f55d0831c39anthony%  amount of the original image into the resulting convolution kernel.  This
467043c4925e5305a26e48d68f7893e94f55d0831c39anthony%  value is usually provided by the user as a percentage value in the
467143c4925e5305a26e48d68f7893e94f55d0831c39anthony%  'convolve:scale' setting.
467243c4925e5305a26e48d68f7893e94f55d0831c39anthony%
4673501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%  The resulting effect is to convert the defined kernels into blended
4674501c2f94b2d119d9e9d9e7d2dd4c5713726c500danthony%  soft-blurs, unsharp kernels or into sharpening kernels.
467543c4925e5305a26e48d68f7893e94f55d0831c39anthony%
467646a369d839971ab627bdb31a93d8bd63e81b65a3anthony%  The format of the UnityAdditionKernelInfo method is:
467743c4925e5305a26e48d68f7893e94f55d0831c39anthony%
467843c4925e5305a26e48d68f7893e94f55d0831c39anthony%      void UnityAdditionKernelInfo(KernelInfo *kernel, const double scale )
467943c4925e5305a26e48d68f7893e94f55d0831c39anthony%
468043c4925e5305a26e48d68f7893e94f55d0831c39anthony%  A description of each parameter follows:
468143c4925e5305a26e48d68f7893e94f55d0831c39anthony%
468243c4925e5305a26e48d68f7893e94f55d0831c39anthony%    o kernel: the Morphology/Convolution kernel
468343c4925e5305a26e48d68f7893e94f55d0831c39anthony%
468443c4925e5305a26e48d68f7893e94f55d0831c39anthony%    o scale:
468543c4925e5305a26e48d68f7893e94f55d0831c39anthony%             scaling factor for the unity kernel to be added to
468643c4925e5305a26e48d68f7893e94f55d0831c39anthony%             the given kernel.
468743c4925e5305a26e48d68f7893e94f55d0831c39anthony%
468843c4925e5305a26e48d68f7893e94f55d0831c39anthony*/
468943c4925e5305a26e48d68f7893e94f55d0831c39anthonyMagickExport void UnityAddKernelInfo(KernelInfo *kernel,
469043c4925e5305a26e48d68f7893e94f55d0831c39anthony  const double scale)
469143c4925e5305a26e48d68f7893e94f55d0831c39anthony{
469246a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* do the other kernels in a multi-kernel list first */
469346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  if ( kernel->next != (KernelInfo *) NULL)
469446a369d839971ab627bdb31a93d8bd63e81b65a3anthony    UnityAddKernelInfo(kernel->next, scale);
469543c4925e5305a26e48d68f7893e94f55d0831c39anthony
469646a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* Add the scaled unity kernel to the existing kernel */
469743c4925e5305a26e48d68f7893e94f55d0831c39anthony  kernel->values[kernel->x+kernel->y*kernel->width] += scale;
469846a369d839971ab627bdb31a93d8bd63e81b65a3anthony  CalcKernelMetaData(kernel);  /* recalculate the meta-data */
469943c4925e5305a26e48d68f7893e94f55d0831c39anthony
470043c4925e5305a26e48d68f7893e94f55d0831c39anthony  return;
470143c4925e5305a26e48d68f7893e94f55d0831c39anthony}
470243c4925e5305a26e48d68f7893e94f55d0831c39anthony
470343c4925e5305a26e48d68f7893e94f55d0831c39anthony/*
470443c4925e5305a26e48d68f7893e94f55d0831c39anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
470543c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
470643c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
470743c4925e5305a26e48d68f7893e94f55d0831c39anthony%                                                                             %
470843c4925e5305a26e48d68f7893e94f55d0831c39anthony%     Z e r o K e r n e l N a n s                                             %
4709cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4710cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4711cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%                                                                             %
4712cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
4713cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4714cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  ZeroKernelNans() replaces any special 'nan' value that may be present in
4715cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  the kernel with a zero value.  This is typically done when the kernel will
4716cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  be used in special hardware (GPU) convolution processors, to simply
4717cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  matters.
4718cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4719cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  The format of the ZeroKernelNans method is:
4720cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
472146a369d839971ab627bdb31a93d8bd63e81b65a3anthony%      void ZeroKernelNans (KernelInfo *kernel)
4722cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4723cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%  A description of each parameter follows:
4724cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4725cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%    o kernel: the Morphology/Convolution kernel
4726cc6c836da2a53b6023b716e4973090a6714dc3b0anthony%
4727cc6c836da2a53b6023b716e4973090a6714dc3b0anthony*/
4728cf592636b16d028b14c5fa9dffdc3a94eae7c2f3cristyMagickPrivate void ZeroKernelNans(KernelInfo *kernel)
4729cc6c836da2a53b6023b716e4973090a6714dc3b0anthony{
4730bb50337b2a8a16ca7e903cc04ab195ff0fd47ae6cristy  register size_t
4731cc6c836da2a53b6023b716e4973090a6714dc3b0anthony    i;
4732cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
473346a369d839971ab627bdb31a93d8bd63e81b65a3anthony  /* do the other kernels in a multi-kernel list first */
47341b2bc0a7da432e6e1cc0480280402df213faa940anthony  if ( kernel->next != (KernelInfo *) NULL)
47351b2bc0a7da432e6e1cc0480280402df213faa940anthony    ZeroKernelNans(kernel->next);
47361b2bc0a7da432e6e1cc0480280402df213faa940anthony
473743c4925e5305a26e48d68f7893e94f55d0831c39anthony  for (i=0; i < (kernel->width*kernel->height); i++)
4738a322a839a56b8d9fa40751b1906ce2a4780a24d6anthony    if ( IfNaN(kernel->values[i]) )
4739cc6c836da2a53b6023b716e4973090a6714dc3b0anthony      kernel->values[i] = 0.0;
4740cc6c836da2a53b6023b716e4973090a6714dc3b0anthony
4741cc6c836da2a53b6023b716e4973090a6714dc3b0anthony  return;
4742cc6c836da2a53b6023b716e4973090a6714dc3b0anthony}
4743