]> granicus.if.org Git - imagemagick/commitdiff
(no commit message)
authorcristy <urban-warrior@git.imagemagick.org>
Sun, 3 Mar 2013 22:21:55 +0000 (22:21 +0000)
committercristy <urban-warrior@git.imagemagick.org>
Sun, 3 Mar 2013 22:21:55 +0000 (22:21 +0000)
MagickCore/morphology-private.h
MagickCore/morphology.c

index 2a937d11178e2a11011f0821a63b3b96ffbd2e23..048cbabc5d4e216ba9703ebc61a186e4bd0b3b9b 100644 (file)
@@ -41,7 +41,7 @@ extern "C" {
 
   This macro  IsNaN() is thus is only true if the value given is NaN.
 */
-#define IsNaN(a) ((a)!=(a))
+#define IsNaN(a) ((a) != (a) ? MagickTrue : MagickFalse)
 
 extern MagickPrivate Image
   *MorphologyApply(const Image *,const MorphologyMethod,const ssize_t,
index 4bcf90a7f85a4f36b181f59c49644decde43dded..3520416bf9bcb2b08506c3978c6a0e30fc8f70b6 100644 (file)
@@ -2504,10 +2504,10 @@ static void CalcKernelMetaData(KernelInfo *kernel)
 %  other 'operators' that internally use morphology operations as part of
 %  their processing.
 %
-%  It is basically equivalent to as MorphologyImage() (see below) but
-%  without any user controls.  This allows internel programs to use this
-%  function, to actually perform a specific task without possible interference
-%  by any API user supplied settings.
+%  It is basically equivalent to as MorphologyImage() (see below) but without
+%  any user controls.  This allows internel programs to use this method to
+%  perform a specific task without possible interference by any API user
+%  supplied settings.
 %
 %  It is MorphologyImage() task to extract any such user controls, and
 %  pass them to this function for processing.
@@ -2548,12 +2548,6 @@ static void CalcKernelMetaData(KernelInfo *kernel)
 %    o exception: return any errors or warnings in this structure.
 %
 */
-
-/* Apply a Morphology Primative to an image using the given kernel.
-** Two pre-created images must be provided, and no image is created.
-** It returns the number of pixels that changed between the images
-** for result convergence determination.
-*/
 static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
   const MorphologyMethod method,const KernelInfo *kernel,const double bias,
   ExceptionInfo *exception)
@@ -2564,11 +2558,14 @@ static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
     *image_view,
     *morphology_view;
 
+  OffsetInfo
+    offset;
+
   ssize_t
-    y, offx, offy;
+    y;
 
   size_t
-    virt_width,
+    width,
     changed;
 
   MagickBooleanType
@@ -2585,59 +2582,52 @@ static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
   assert(kernel->signature == MagickSignature);
   assert(exception != (ExceptionInfo *) NULL);
   assert(exception->signature == MagickSignature);
-
   status=MagickTrue;
   changed=0;
   progress=0;
-
   image_view=AcquireVirtualCacheView(image,exception);
   morphology_view=AcquireAuthenticCacheView(morphology_image,exception);
-  virt_width=image->columns+kernel->width-1;
-
-  /* Some methods (including convolve) needs use a reflected kernel.
-   * Adjust 'origin' offsets to loop though kernel as a reflection.
-   */
-  offx = kernel->x;
-  offy = kernel->y;
-  switch(method) {
+  width=image->columns+kernel->width-1;
+  switch (method)
+  {
     case ConvolveMorphology:
     case DilateMorphology:
     case DilateIntensityMorphology:
     case IterativeDistanceMorphology:
-      /* kernel needs to used with reflection about origin */
-      offx = (ssize_t) kernel->width-offx-1;
-      offy = (ssize_t) kernel->height-offy-1;
+    {
+      /*
+        Kernel needs to used with reflection about origin.
+      */
+      offset.x=(ssize_t) kernel->width-kernel->x-1;
+      offset.y=(ssize_t) kernel->height-kernel->y-1;
       break;
+    }
     case ErodeMorphology:
     case ErodeIntensityMorphology:
     case HitAndMissMorphology:
     case ThinningMorphology:
     case ThickenMorphology:
-      /* kernel is used as is, without reflection */
+    {
+      offset.x=kernel->x;
+      offset.y=kernel->y;
       break;
+    }
     default:
+    {
       assert("Not a Primitive Morphology Method" != (char *) NULL);
       break;
+    }
   }
-
-  if (method == ConvolveMorphology && kernel->width == 1)
+  if ((method == ConvolveMorphology) && (kernel->width == 1))
     {
       register ssize_t
         x;
 
       /*
-        Anthony Thyssen, 14 June 2010
-
-        Special handling (for speed) of vertical (blur) kernels.  This
-        performs its handling in columns rather than in rows.  This is
-        only done for convolve as it is the only method that generates very
-        large 1-D vertical kernels (such as a 'BlurKernel')
-       
-        Timing tests (on single CPU laptop).  Using a vertical 1-d Blue with
-        normal row-by-row (below):
-          time convert logo: -morphology Convolve Blur:0x10+90 null: 0.807u
-        Using this column method
-          time convert logo: -morphology Convolve Blur:0x10+90 null: 0.620u
+        Special handling (for speed) of vertical (blur) kernels.  This performs
+        its handling in columns rather than in rows.  This is only done
+        for convolve as it is the only method that generates very large 1-D
+        vertical kernels (such as a 'BlurKernel')
      */
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
      #pragma omp parallel for schedule(static,4) shared(progress,status) \
@@ -2659,7 +2649,7 @@ static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
 
         if (status == MagickFalse)
           continue;
-        p=GetCacheViewVirtualPixels(image_view,x,-offy,1,image->rows+
+        p=GetCacheViewVirtualPixels(image_view,x,-offset.y,1,image->rows+
           kernel->height-1,exception);
         q=GetCacheViewAuthenticPixels(morphology_view,x,0,1,
           morphology_image->rows,exception);
@@ -2668,7 +2658,7 @@ static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
             status=MagickFalse;
             continue;
           }
-        center=(ssize_t) GetPixelChannels(image)*offy;
+        center=(ssize_t) GetPixelChannels(image)*offset.y;
         for (y=0; y < (ssize_t) image->rows; y++)
         {
           register ssize_t
@@ -2712,7 +2702,7 @@ static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
                 SetPixelChannel(morphology_image,channel,p[center+i],q);
                 continue;
               }
-            k=(&kernel->values[kernel->height-1]);
+            k=(&kernel->values[kernel->width*kernel->height-1]);
             pixels=p;
             pixel=bias;
             gamma=0.0;
@@ -2765,7 +2755,7 @@ static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
           }
           p+=GetPixelChannels(image);
           q+=GetPixelChannels(morphology_image);
-        } /* y */
+        }
         if (SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
           status=MagickFalse;
         if (image->progress_monitor != (MagickProgressMonitor) NULL)
@@ -2776,7 +2766,8 @@ static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
 #if defined(MAGICKCORE_OPENMP_SUPPORT)
             #pragma omp critical (MagickCore_MorphologyImage)
 #endif
-            proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
+            proceed=SetImageProgress(image,MorphologyTag,progress++,
+              image->rows);
             if (proceed == MagickFalse)
               status=MagickFalse;
           }
@@ -2804,12 +2795,12 @@ static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
     register ssize_t
       x;
 
-    size_t
-      r;
+    ssize_t
+      center;
 
     if (status == MagickFalse)
       continue;
-    p=GetCacheViewVirtualPixels(image_view,-offx,y-offy,virt_width,
+    p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,
       kernel->height,exception);
     q=GetCacheViewAuthenticPixels(morphology_view,0,y,morphology_image->columns,
       1,exception);
@@ -2818,496 +2809,357 @@ static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
         status=MagickFalse;
         continue;
       }
-    /* offset to origin in 'p'. while 'q' points to it directly */
-    r = GetPixelChannels(image)*virt_width*offy + GetPixelChannels(image)*offx;
-
+    center=(ssize_t) (GetPixelChannels(image)*width*offset.y+
+      GetPixelChannels(image)*offset.x);
     for (x=0; x < (ssize_t) image->columns; x++)
     {
-      PixelInfo
-        result,
-        min,
-        max;
+      register ssize_t
+        i;
 
-      register const MagickRealType
-        *restrict k;
+      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+      {
+        double
+          alpha,
+          gamma,
+          maximum,
+          minimum,
+          pixel;
 
-      register const Quantum
-        *restrict k_pixels;
+        PixelChannel
+          channel;
 
-      register ssize_t
-        u;
+        PixelTrait
+          morphology_traits,
+          traits;
 
-      ssize_t
-        v;
+        register const MagickRealType
+          *restrict k;
 
-      /* Copy input image to the output image for unused channels
-       * This removes need for 'cloning' a new image every iteration
-       */
-      SetPixelRed(morphology_image,GetPixelRed(image,p+r),q);
-      SetPixelGreen(morphology_image,GetPixelGreen(image,p+r),q);
-      SetPixelBlue(morphology_image,GetPixelBlue(image,p+r),q);
-      if (image->colorspace == CMYKColorspace)
-        SetPixelBlack(morphology_image,GetPixelBlack(image,p+r),q);
-
-      /* Defaults */
-      min.red     =
-      min.green   =
-      min.blue    =
-      min.alpha =
-      min.black   = (double) QuantumRange;
-      max.red     =
-      max.green   =
-      max.blue    =
-      max.alpha =
-      max.black   = (double) 0;
-      /* default result is the original pixel value */
-      result.red     = (double) GetPixelRed(image,p+r);
-      result.green   = (double) GetPixelGreen(image,p+r);
-      result.blue    = (double) GetPixelBlue(image,p+r);
-      result.black   = 0.0;
-      if (image->colorspace == CMYKColorspace)
-        result.black = (double) GetPixelBlack(image,p+r);
-      result.alpha=(double) GetPixelAlpha(image,p+r);
-
-      switch (method) {
-        case ConvolveMorphology:
-          /* Set the bias of the weighted average output */
-          result.red     =
-          result.green   =
-          result.blue    =
-          result.alpha =
-          result.black   = bias;
-          break;
-        case DilateIntensityMorphology:
-        case ErodeIntensityMorphology:
-          /* use a boolean flag indicating when first match found */
-          result.red = 0.0;  /* result is not used otherwise */
-          break;
-        default:
-          break;
-      }
+        register const Quantum
+          *restrict pixels;
+
+        register ssize_t
+          u;
+
+        ssize_t
+          v;
+
+        channel=GetPixelChannelChannel(image,i);
+        traits=GetPixelChannelTraits(image,channel);
+        morphology_traits=GetPixelChannelTraits(morphology_image,channel);
+        if ((traits == UndefinedPixelTrait) ||
+            (morphology_traits == UndefinedPixelTrait))
+          continue;
+        if (((morphology_traits & CopyPixelTrait) != 0) ||
+            (GetPixelMask(image,p) != 0))
+          {
+            SetPixelChannel(morphology_image,channel,p[center+i],q);
+            continue;
+          }
+        pixels=p;
+        maximum=0.0;
+        minimum=(double) QuantumRange;
+        switch (method)
+        {
+          case ConvolveMorphology: pixel=bias; break;
+          case HitAndMissMorphology: pixel=(double) QuantumRange; break;
+          case ThinningMorphology: pixel=(double) QuantumRange; break;
+          case ThickenMorphology: pixel=(double) QuantumRange; break;
+          case ErodeMorphology: pixel=(double) QuantumRange; break;
+          case DilateMorphology: pixel=0.0; break;
+          case ErodeIntensityMorphology:
+          case DilateIntensityMorphology:
+          case IterativeDistanceMorphology:
+          {
+            pixel=(double) p[center+i];
+            break;
+          }
+          default: pixel=0; break;
+        }
+        gamma=0.0;
+        switch (method)
+        {
+          case ConvolveMorphology:
+          {
+            /*
+               Weighted Average of pixels using reflected kernel
+
+               For correct working of this operation for asymetrical
+               kernels, the kernel needs to be applied in its reflected form.
+               That is its values needs to be reversed.
 
-      switch ( method ) {
-        case ConvolveMorphology:
-            /* Weighted Average of pixels using reflected kernel
-            **
-            ** NOTE for correct working of this operation for asymetrical
-            ** kernels, the kernel needs to be applied in its reflected form.
-            ** That is its values needs to be reversed.
-            **
-            ** Correlation is actually the same as this but without reflecting
-            ** the kernel, and thus 'lower-level' that Convolution.  However
-            ** as Convolution is the more common method used, and it does not
-            ** really cost us much in terms of processing to use a reflected
-            ** kernel, so it is Convolution that is implemented.
-            **
-            ** Correlation will have its kernel reflected before calling
-            ** this function to do a Convolve.
-            **
-            ** For more details of Correlation vs Convolution see
-            **   http://www.cs.umd.edu/~djacobs/CMSC426/Convolution.pdf
+               Correlation is actually the same as this but without reflecting
+               the kernel, and thus 'lower-level' that Convolution.  However
+               as Convolution is the more common method used, and it does not
+               really cost us much in terms of processing to use a reflected
+               kernel, so it is Convolution that is implemented.
+
+               Correlation will have its kernel reflected before calling
+               this function to do a Convolve.
+
+               For more details of Correlation vs Convolution see
+                 http://www.cs.umd.edu/~djacobs/CMSC426/Convolution.pdf
             */
-            k = &kernel->values[ kernel->width*kernel->height-1 ];
-            k_pixels = p;
-            if ( (image->channel_mask != DefaultChannels) ||
-                 (image->alpha_trait != BlendPixelTrait) )
-              { /* No 'Sync' involved.
-                ** Convolution is simple greyscale channel operation
+            k=(&kernel->values[kernel->width*kernel->height-1]);
+            if ((morphology_traits & BlendPixelTrait) == 0)
+              {
+                /*
+                  No alpha blending.
                 */
-                for (v=0; v < (ssize_t) kernel->height; v++) {
-                  for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                    if ( IsNaN(*k) ) continue;
-                    result.red     += (*k)*
-                      GetPixelRed(image,k_pixels+u*GetPixelChannels(image));
-                    result.green   += (*k)*
-                      GetPixelGreen(image,k_pixels+u*GetPixelChannels(image));
-                    result.blue    += (*k)*
-                      GetPixelBlue(image,k_pixels+u*GetPixelChannels(image));
-                    if (image->colorspace == CMYKColorspace)
-                      result.black += (*k)*
-                        GetPixelBlack(image,k_pixels+u*GetPixelChannels(image));
-                    result.alpha += (*k)*
-                      GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image));
+                for (v=0; v < (ssize_t) kernel->height; v++)
+                {
+                  for (u=0; u < (ssize_t) kernel->width; u++)
+                  {
+                    if (IsNaN(*k) == MagickFalse)
+                      {
+                        pixel+=(*k)*pixels[i];
+                        gamma+=(*k);
+                      }
+                    k--;
+                    pixels+=GetPixelChannels(image);
                   }
-                  k_pixels += virt_width*GetPixelChannels(image);
+                  pixels+=image->columns*GetPixelChannels(image);
                 }
-                if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-                  SetPixelRed(morphology_image,ClampToQuantum(result.red),
-                    q);
-                if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-                  SetPixelGreen(morphology_image,ClampToQuantum(result.green),
-                    q);
-                if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-                  SetPixelBlue(morphology_image,ClampToQuantum(result.blue),
-                    q);
-                if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-                    (image->colorspace == CMYKColorspace))
-                  SetPixelBlack(morphology_image,ClampToQuantum(result.black),
-                    q);
-                if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
-                    (image->alpha_trait == BlendPixelTrait))
-                  SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),
-                    q);
+                gamma=PerceptibleReciprocal(gamma);
+                pixel*=gamma;
+                continue;
               }
-            else
-              { /* Channel 'Sync' Flag, and Alpha Channel enabled.
-                ** Weight the color channels with Alpha Channel so that
-                ** transparent pixels are not part of the results.
-                */
-                double
-                  gamma;  /* divisor, sum of color alpha weighting */
-
-                MagickRealType
-                  alpha;  /* alpha weighting for colors : alpha  */
-
-                size_t
-                  count;  /* alpha valus collected, number kernel values */
-
-                count=0;
-                gamma=0.0;
-                for (v=0; v < (ssize_t) kernel->height; v++) {
-                  for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                    if ( IsNaN(*k) ) continue;
-                    alpha=QuantumScale*GetPixelAlpha(image,
-                                k_pixels+u*GetPixelChannels(image));
-                    gamma += alpha;    /* normalize alpha weights only */
-                    count++;           /* number of alpha values collected */
-                    alpha=alpha*(*k);  /* include kernel weighting now */
-                    result.red     += alpha*
-                      GetPixelRed(image,k_pixels+u*GetPixelChannels(image));
-                    result.green   += alpha*
-                      GetPixelGreen(image,k_pixels+u*GetPixelChannels(image));
-                    result.blue    += alpha*
-                      GetPixelBlue(image,k_pixels+u*GetPixelChannels(image));
-                    if (image->colorspace == CMYKColorspace)
-                      result.black += alpha*
-                        GetPixelBlack(image,k_pixels+u*GetPixelChannels(image));
-                    result.alpha   += (*k)*
-                      GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image));
+            /*
+              Alpha blending.
+            */
+            for (v=0; v < (ssize_t) kernel->width; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if (IsNaN(*k) == MagickFalse)
+                  {
+                    alpha=(double) (QuantumScale*GetPixelAlpha(image,pixels));
+                    pixel+=(*k)*alpha*pixels[i];
+                    gamma+=(*k)*alpha;
                   }
-                  k_pixels += virt_width*GetPixelChannels(image);
-                }
-                /* Sync'ed channels, all channels are modified */
-                gamma=(double)count/(fabs((double) gamma) < MagickEpsilon ? MagickEpsilon : gamma);
-                SetPixelRed(morphology_image,
-                  ClampToQuantum(gamma*result.red),q);
-                SetPixelGreen(morphology_image,
-                  ClampToQuantum(gamma*result.green),q);
-                SetPixelBlue(morphology_image,
-                  ClampToQuantum(gamma*result.blue),q);
-                if (image->colorspace == CMYKColorspace)
-                  SetPixelBlack(morphology_image,
-                    ClampToQuantum(gamma*result.black),q);
-                SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
+                k--;
+                pixels+=GetPixelChannels(image);
               }
+              pixels+=image->columns*GetPixelChannels(image);
+            }
+            gamma=PerceptibleReciprocal(gamma);
+            pixel*=gamma;
             break;
+          }
+          case ErodeMorphology:
+          {
+            /*
+              Minimum value within kernel neighbourhood.
 
-        case ErodeMorphology:
-            /* Minimum Value within kernel neighbourhood
-            **
-            ** NOTE that the kernel is not reflected for this operation!
-            **
-            ** NOTE: in normal Greyscale Morphology, the kernel value should
-            ** be added to the real value, this is currently not done, due to
-            ** the nature of the boolean kernels being used.
+              The kernel is not reflected for this operation.  In normal
+              Greyscale Morphology, the kernel value should be added
+              to the real value, this is currently not done, due to the
+              nature of the boolean kernels being used.
             */
-            k = kernel->values;
-            k_pixels = p;
-            for (v=0; v < (ssize_t) kernel->height; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k++) {
-                if ( IsNaN(*k) || (*k) < 0.5 ) continue;
-                Minimize(min.red,     (double)
-                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(min.green,   (double)
-                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(min.blue,    (double)
-                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(min.alpha,   (double)
-                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-                if (image->colorspace == CMYKColorspace)
-                  Minimize(min.black,  (double)
-                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
+            k=kernel->values;
+            for (v=0; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if ((IsNaN(*k) == MagickFalse) && (*k >= 0.5))
+                  {
+                    if ((double) pixels[i] < pixel)
+                      pixel=(double) pixels[i];
+                  }
+                k++;
+                pixels+=GetPixelChannels(image);
               }
-              k_pixels += virt_width*GetPixelChannels(image);
+              pixels+=image->columns*GetPixelChannels(image);
             }
             break;
+          }
+          case DilateMorphology:
+          {
+            /*
+               Maximum value within kernel neighbourhood.
+
+               For correct working of this operation for asymetrical kernels,
+               the kernel needs to be applied in its reflected form.  That is
+               its values needs to be reversed.
 
-        case DilateMorphology:
-            /* Maximum Value within kernel neighbourhood
-            **
-            ** NOTE for correct working of this operation for asymetrical
-            ** kernels, the kernel needs to be applied in its reflected form.
-            ** That is its values needs to be reversed.
-            **
-            ** NOTE: in normal Greyscale Morphology, the kernel value should
-            ** be added to the real value, this is currently not done, due to
-            ** the nature of the boolean kernels being used.
-            **
+               In normal Greyscale Morphology, the kernel value should be
+               added to the real value, this is currently not done, due to the
+               nature of the boolean kernels being used.
             */
-            k = &kernel->values[ kernel->width*kernel->height-1 ];
-            k_pixels = p;
-            for (v=0; v < (ssize_t) kernel->height; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                if ( IsNaN(*k) || (*k) < 0.5 ) continue;
-                Maximize(max.red,     (double)
-                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                Maximize(max.green,   (double)
-                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                Maximize(max.blue,    (double)
-                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                Maximize(max.alpha,   (double)
-                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-                if (image->colorspace == CMYKColorspace)
-                  Maximize(max.black,   (double)
-                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
+            k=(&kernel->values[kernel->width*kernel->height-1]);
+            for (v=0; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if ((IsNaN(*k) == MagickFalse) && (*k > 0.5))
+                  {
+                    if ((double) pixels[i] > pixel)
+                      pixel=(double) pixels[i];
+                  }
+                k--;
+                pixels+=GetPixelChannels(image);
               }
-              k_pixels += virt_width*GetPixelChannels(image);
+              pixels+=image->columns*GetPixelChannels(image);
             }
             break;
+          }
+          case HitAndMissMorphology:
+          case ThinningMorphology:
+          case ThickenMorphology:
+          {
+            /*
+               Minimum of foreground pixel minus maxumum of background pixels.
+
+               The kernel is not reflected for this operation, and consists
+               of both foreground and background pixel neighbourhoods, 0.0 for
+               background, and 1.0 for foreground with either Nan or 0.5 values
+               for don't care.
 
-        case HitAndMissMorphology:
-        case ThinningMorphology:
-        case ThickenMorphology:
-            /* Minimum of Foreground Pixel minus Maxumum of Background Pixels
-            **
-            ** NOTE that the kernel is not reflected for this operation,
-            ** and consists of both foreground and background pixel
-            ** neighbourhoods, 0.0 for background, and 1.0 for foreground
-            ** with either Nan or 0.5 values for don't care.
-            **
-            ** Note that this will never produce a meaningless negative
-            ** result.  Such results can cause Thinning/Thicken to not work
-            ** correctly when used against a greyscale image.
+               This never produces a meaningless negative result.  Such results
+               cause Thinning/Thicken to not work correctly when used against a
+               greyscale image.
             */
-            k = kernel->values;
-            k_pixels = p;
-            for (v=0; v < (ssize_t) kernel->height; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k++) {
-                if ( IsNaN(*k) ) continue;
-                if ( (*k) > 0.7 )
-                { /* minimim of foreground pixels */
-                  Minimize(min.red,     (double)
-                    GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                  Minimize(min.green,   (double)
-                    GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                  Minimize(min.blue,    (double)
-                    GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                  Minimize(min.alpha,(double)
-                    GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-                  if ( image->colorspace == CMYKColorspace)
-                    Minimize(min.black,(double)
-                      GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
-                }
-                else if ( (*k) < 0.3 )
-                { /* maximum of background pixels */
-                  Maximize(max.red,     (double)
-                    GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                  Maximize(max.green,   (double)
-                    GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                  Maximize(max.blue,    (double)
-                    GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                  Maximize(max.alpha,(double)
-                    GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-                  if (image->colorspace == CMYKColorspace)
-                    Maximize(max.black,   (double)
-                      GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
-                }
+            k=kernel->values;
+            for (v=0; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if (IsNaN(*k) == MagickFalse)
+                  {
+                    if (*k > 0.7)
+                      {
+                        if ((double) pixels[i] < pixel)
+                          pixel=(double) pixels[i];
+                      }
+                    else
+                      if (*k < 0.3)
+                        {
+                          if ((double) pixels[i] > maximum)
+                            maximum=(double) pixels[i];
+                        }
+                  }
+                k++;
+                pixels+=GetPixelChannels(image);
               }
-              k_pixels += virt_width*GetPixelChannels(image);
+              pixels+=image->columns*GetPixelChannels(image);
             }
-            /* Pattern Match if difference is positive */
-            min.red     -= max.red;     Maximize( min.red,     0.0 );
-            min.green   -= max.green;   Maximize( min.green,   0.0 );
-            min.blue    -= max.blue;    Maximize( min.blue,    0.0 );
-            min.black   -= max.black;   Maximize( min.black,   0.0 );
-            min.alpha -= max.alpha; Maximize( min.alpha, 0.0 );
+            pixel-=maximum;
+            if (pixel < 0.0)
+              pixel=0.0;
+            if (method ==  ThinningMorphology)
+              pixel=(double) p[center+i]-pixel;
+            else
+              if (method ==  ThickenMorphology)
+                pixel+=(double) p[center+i]+pixel;
             break;
-
-        case ErodeIntensityMorphology:
-            /* Select Pixel with Minimum Intensity within kernel neighbourhood
-            **
-            ** WARNING: the intensity test fails for CMYK and does not
-            ** take into account the moderating effect of the alpha channel
-            ** on the intensity.
-            **
-            ** NOTE that the kernel is not reflected for this operation!
+          }
+          case ErodeIntensityMorphology:
+          {
+            /*
+              Select pixel with minimum intensity within kernel neighbourhood.
+            
+              The kernel is not reflected for this operation.
             */
-            k = kernel->values;
-            k_pixels = p;
-            for (v=0; v < (ssize_t) kernel->height; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k++) {
-                if ( IsNaN(*k) || (*k) < 0.5 ) continue;
-                if ( result.red == 0.0 ||
-                     GetPixelIntensity(image,k_pixels+u*GetPixelChannels(image)) < GetPixelIntensity(morphology_image,q) ) {
-                  /* copy the whole pixel - no channel selection */
-                  SetPixelRed(morphology_image,GetPixelRed(image,
-                    k_pixels+u*GetPixelChannels(image)),q);
-                  SetPixelGreen(morphology_image,GetPixelGreen(image,
-                    k_pixels+u*GetPixelChannels(image)),q);
-                  SetPixelBlue(morphology_image,GetPixelBlue(image,
-                    k_pixels+u*GetPixelChannels(image)),q);
-                  SetPixelAlpha(morphology_image,GetPixelAlpha(image,
-                    k_pixels+u*GetPixelChannels(image)),q);
-                  if ( result.red > 0.0 ) changed++;
-                  result.red = 1.0;
-                }
+            k=kernel->values;
+            for (v=0; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if ((IsNaN(*k) == MagickFalse) && (*k >= 0.5))
+                  {
+                    if (GetPixelIntensity(image,pixels) < minimum)
+                      {
+                        pixel=(double) pixels[i];
+                        minimum=GetPixelIntensity(image,pixels);
+                      }
+                  }
+                k++;
+                pixels+=GetPixelChannels(image);
               }
-              k_pixels += virt_width*GetPixelChannels(image);
+              pixels+=image->columns*GetPixelChannels(image);
             }
             break;
-
-        case DilateIntensityMorphology:
-            /* Select Pixel with Maximum Intensity within kernel neighbourhood
-            **
-            ** WARNING: the intensity test fails for CMYK and does not
-            ** take into account the moderating effect of the alpha channel
-            ** on the intensity (yet).
-            **
-            ** NOTE for correct working of this operation for asymetrical
-            ** kernels, the kernel needs to be applied in its reflected form.
-            ** That is its values needs to be reversed.
+          }
+          case DilateIntensityMorphology:
+          {
+            /*
+              Select pixel with maximum intensity within kernel neighbourhood.
+            
+              The kernel is not reflected for this operation.
             */
-            k = &kernel->values[ kernel->width*kernel->height-1 ];
-            k_pixels = p;
-            for (v=0; v < (ssize_t) kernel->height; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                if ( IsNaN(*k) || (*k) < 0.5 ) continue; /* boolean kernel */
-                if ( result.red == 0.0 ||
-                     GetPixelIntensity(image,k_pixels+u*GetPixelChannels(image)) > GetPixelIntensity(morphology_image,q) ) {
-                  /* copy the whole pixel - no channel selection */
-                  SetPixelRed(morphology_image,GetPixelRed(image,
-                    k_pixels+u*GetPixelChannels(image)),q);
-                  SetPixelGreen(morphology_image,GetPixelGreen(image,
-                    k_pixels+u*GetPixelChannels(image)),q);
-                  SetPixelBlue(morphology_image,GetPixelBlue(image,
-                    k_pixels+u*GetPixelChannels(image)),q);
-                  SetPixelAlpha(morphology_image,GetPixelAlpha(image,
-                    k_pixels+u*GetPixelChannels(image)),q);
-                  if ( result.red > 0.0 ) changed++;
-                  result.red = 1.0;
-                }
+            k=(&kernel->values[kernel->width*kernel->height-1]);
+            for (v=0; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if ((IsNaN(*k) == MagickFalse) && (*k >= 0.5))
+                  {
+                    if (GetPixelIntensity(image,pixels) > maximum)
+                      {
+                        pixel=(double) pixels[i];
+                        maximum=GetPixelIntensity(image,pixels);
+                      }
+                  }
+                k--;
+                pixels+=GetPixelChannels(image);
               }
-              k_pixels += virt_width*GetPixelChannels(image);
+              pixels+=image->columns*GetPixelChannels(image);
             }
             break;
-
-        case IterativeDistanceMorphology:
-            /* Work out an iterative distance from black edge of a white image
-            ** shape.  Essentually white values are decreased to the smallest
-            ** 'distance from edge' it can find.
-            **
-            ** It works by adding kernel values to the neighbourhood, and and
-            ** select the minimum value found. The kernel is rotated before
-            ** use, so kernel distances match resulting distances, when a user
-            ** provided asymmetric kernel is applied.
-            **
-            **
-            ** This code is almost identical to True GrayScale Morphology But
-            ** not quite.
-            **
-            ** GreyDilate  Kernel values added, maximum value found Kernel is
-            ** rotated before use.
-            **
-            ** GrayErode:  Kernel values subtracted and minimum value found No
-            ** kernel rotation used.
-            **
-            ** Note the the Iterative Distance method is essentially a
-            ** GrayErode, but with negative kernel values, and kernel
-            ** rotation applied.
+          }
+          case IterativeDistanceMorphology:
+          {
+            /*
+               Compute th iterative distance from black edge of a white image
+               shape.  Essentually white values are decreased to the smallest
+               'distance from edge' it can find.
+              
+               It works by adding kernel values to the neighbourhood, and and
+               select the minimum value found. The kernel is rotated before
+               use, so kernel distances match resulting distances, when a user
+               provided asymmetric kernel is applied.
+              
+               This code is nearly identical to True GrayScale Morphology but
+               not quite.
+              
+               GreyDilate Kernel values added, maximum value found Kernel is
+               rotated before use.
+              
+               GrayErode:  Kernel values subtracted and minimum value found No
+               kernel rotation used.
+              
+               Note the the Iterative Distance method is essentially a
+               GrayErode, but with negative kernel values, and kernel rotation
+               applied.
             */
-            k = &kernel->values[ kernel->width*kernel->height-1 ];
-            k_pixels = p;
-            for (v=0; v < (ssize_t) kernel->height; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                if ( IsNaN(*k) ) continue;
-                Minimize(result.red,     (*k)+(double)
-                     GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.green,   (*k)+(double)
-                     GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.blue,    (*k)+(double)
-                     GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.alpha,   (*k)+(double)
-                     GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-                if ( image->colorspace == CMYKColorspace)
-                  Maximize(result.black, (*k)+(double)
-                      GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
+            k=(&kernel->values[kernel->width*kernel->height-1]);
+            for (v=0; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if (IsNaN(*k) == MagickFalse)
+                  {
+                    if ((pixels[i]+(*k)) < pixel)
+                      pixel=(double) pixels[i]+(*k);
+                  }
+                k--;
+                pixels+=GetPixelChannels(image);
               }
-              k_pixels += virt_width*GetPixelChannels(image);
+              pixels+=image->columns*GetPixelChannels(image);
             }
             break;
-
-        case UndefinedMorphology:
-        default:
-            break; /* Do nothing */
-      }
-      /* Final mathematics of results (combine with original image?)
-      **
-      ** NOTE: Difference Morphology operators Edge* and *Hat could also
-      ** be done here but works better with iteration as a image difference
-      ** in the controling function (below).  Thicken and Thinning however
-      ** should be done here so thay can be iterated correctly.
-      */
-      switch ( method ) {
-        case HitAndMissMorphology:
-        case ErodeMorphology:
-          result = min;    /* minimum of neighbourhood */
-          break;
-        case DilateMorphology:
-          result = max;    /* maximum of neighbourhood */
-          break;
-        case ThinningMorphology:
-          /* subtract pattern match from original */
-          result.red     -= min.red;
-          result.green   -= min.green;
-          result.blue    -= min.blue;
-          result.black   -= min.black;
-          result.alpha   -= min.alpha;
-          break;
-        case ThickenMorphology:
-          /* Add the pattern matchs to the original */
-          result.red     += min.red;
-          result.green   += min.green;
-          result.blue    += min.blue;
-          result.black   += min.black;
-          result.alpha   += min.alpha;
-          break;
-        default:
-          /* result directly calculated or assigned */
-          break;
-      }
-      /* Assign the resulting pixel values - Clamping Result */
-      switch ( method ) {
-        case UndefinedMorphology:
-        case ConvolveMorphology:
-        case DilateIntensityMorphology:
-        case ErodeIntensityMorphology:
-          break;  /* full pixel was directly assigned - not a channel method */
-        default:
-          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelRed(morphology_image,ClampToQuantum(result.red),q);
-          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelGreen(morphology_image,ClampToQuantum(result.green),q);
-          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelBlue(morphology_image,ClampToQuantum(result.blue),q);
-          if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-              (image->colorspace == CMYKColorspace))
-            SetPixelBlack(morphology_image,ClampToQuantum(result.black),q);
-          if (((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0) &&
-              (image->alpha_trait == BlendPixelTrait))
-            SetPixelAlpha(morphology_image,ClampToQuantum(result.alpha),q);
-          break;
+          }
+          case UndefinedMorphology:
+          default:
+            break;
+        }
+        if (fabs(pixel-p[center+i]) > MagickEpsilon)
+          changed++;
+        SetPixelChannel(morphology_image,channel,ClampToQuantum(pixel),q);
       }
-      /* Count up changed pixels */
-      if ((GetPixelRed(image,p+r) != GetPixelRed(morphology_image,q)) ||
-          (GetPixelGreen(image,p+r) != GetPixelGreen(morphology_image,q)) ||
-          (GetPixelBlue(image,p+r) != GetPixelBlue(morphology_image,q)) ||
-          (GetPixelAlpha(image,p+r) != GetPixelAlpha(morphology_image,q)) ||
-          ((image->colorspace == CMYKColorspace) &&
-           (GetPixelBlack(image,p+r) != GetPixelBlack(morphology_image,q))))
-        changed++;  /* The pixel was changed in some way! */
       p+=GetPixelChannels(image);
       q+=GetPixelChannels(morphology_image);
-    } /* x */
+    }
     if ( SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
       status=MagickFalse;
     if (image->progress_monitor != (MagickProgressMonitor) NULL)
@@ -3322,31 +3174,32 @@ static ssize_t MorphologyPrimitive(const Image *image,Image *morphology_image,
         if (proceed == MagickFalse)
           status=MagickFalse;
       }
-  } /* y */
+  }
   morphology_view=DestroyCacheView(morphology_view);
   image_view=DestroyCacheView(image_view);
   return(status ? (ssize_t)changed : -1);
 }
 
-/* This is almost identical to the MorphologyPrimative() function above,
-** but will apply the primitive directly to the actual image using two
-** passes, once in each direction, with the results of the previous (and
-** current) row being re-used.
-**
-** That is after each row is 'Sync'ed' into the image, the next row will
-** make use of those values as part of the calculation of the next row.
-** It then repeats, but going in the oppisite (bottom-up) direction.
-**
-** Because of this 're-use of results' this function can not make use
-** of multi-threaded, parellel processing.
+/*
+  This is almost identical to the MorphologyPrimative() function above, but
+  applies the primitive directly to the actual image using two passes, once in
+  each direction, with the results of the previous (and current) row being
+  re-used.
+
+  That is after each row is 'Sync'ed' into the image, the next row makes use of
+  those values as part of the calculation of the next row.  It repeats, but
+  going in the oppisite (bottom-up) direction.
+
+  Because of this 're-use of results' this function can not make use of multi-
+  threaded, parellel processing.
 */
 static ssize_t MorphologyPrimitiveDirect(Image *image,
   const MorphologyMethod method,const KernelInfo *kernel,
   ExceptionInfo *exception)
 {
   CacheView
-    *auth_view,
-    *virt_view;
+    *morphology_view,
+    *image_view;
 
   MagickBooleanType
     status;
@@ -3354,16 +3207,15 @@ static ssize_t MorphologyPrimitiveDirect(Image *image,
   MagickOffsetType
     progress;
 
-  ssize_t
-    y, offx, offy;
+  OffsetInfo
+    offset;
 
   size_t
-    virt_width,
+    width,
     changed;
 
-  status=MagickTrue;
-  changed=0;
-  progress=0;
+  ssize_t
+    y;
 
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
@@ -3371,35 +3223,34 @@ static ssize_t MorphologyPrimitiveDirect(Image *image,
   assert(kernel->signature == MagickSignature);
   assert(exception != (ExceptionInfo *) NULL);
   assert(exception->signature == MagickSignature);
-
-  /* Some methods (including convolve) needs use a reflected kernel.
-   * Adjust 'origin' offsets to loop though kernel as a reflection.
-   */
-  offx = kernel->x;
-  offy = kernel->y;
-  switch(method) {
+  status=MagickTrue;
+  changed=0;
+  progress=0;
+  switch(method)
+  {
     case DistanceMorphology:
     case VoronoiMorphology:
-      /* kernel needs to used with reflection about origin */
-      offx = (ssize_t) kernel->width-offx-1;
-      offy = (ssize_t) kernel->height-offy-1;
-      break;
-#if 0
-    case ?????Morphology:
-      /* kernel is used as is, without reflection */
+    {
+      /*
+        Kernel reflected about origin.
+      */
+      offset.x=(ssize_t) kernel->width-kernel->x-1;
+      offset.y=(ssize_t) kernel->height-kernel->y-1;
       break;
-#endif
+    }
     default:
-      assert("Not a PrimativeDirect Morphology Method" != (char *) NULL);
+    {
+      offset.x=kernel->x;
+      offset.y=kernel->y;
       break;
+    }
   }
-
-  /* DO NOT THREAD THIS CODE! */
-  /* two views into same image (virtual, and actual) */
-  virt_view=AcquireVirtualCacheView(image,exception);
-  auth_view=AcquireAuthenticCacheView(image,exception);
-  virt_width=image->columns+kernel->width-1;
-
+  /*
+    Two views into same image, do not thread.
+  */
+  image_view=AcquireVirtualCacheView(image,exception);
+  morphology_view=AcquireAuthenticCacheView(image,exception);
+  width=image->columns+kernel->width-1;
   for (y=0; y < (ssize_t) image->rows; y++)
   {
     register const Quantum
@@ -3412,177 +3263,157 @@ static ssize_t MorphologyPrimitiveDirect(Image *image,
       x;
 
     ssize_t
-      r;
-
-    /* NOTE read virtual pixels, and authentic pixels, from the same image!
-    ** we read using virtual to get virtual pixel handling, but write back
-    ** into the same image.
-    **
-    ** Only top half of kernel is processed as we do a single pass downward
-    ** through the image iterating the distance function as we go.
+      center;
+
+    /*
+      Read virtual pixels, and authentic pixels, from the same image!  We read
+      using virtual to get virtual pixel handling, but write back into the same
+      image.
+
+      Only top half of kernel is processed as we do a single pass downward
+      through the image iterating the distance function as we go.
     */
     if (status == MagickFalse)
       break;
-    p=GetCacheViewVirtualPixels(virt_view,-offx,y-offy,virt_width,(size_t)
-      offy+1,exception);
-    q=GetCacheViewAuthenticPixels(auth_view, 0, y, image->columns, 1,
+    p=GetCacheViewVirtualPixels(image_view,-offset.x,y-offset.y,width,(size_t)
+      offset.y+1,exception);
+    q=GetCacheViewAuthenticPixels(morphology_view, 0, y, image->columns, 1,
       exception);
     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
       status=MagickFalse;
     if (status == MagickFalse)
       break;
-
-    /* offset to origin in 'p'. while 'q' points to it directly */
-    r = (ssize_t) GetPixelChannels(image)*virt_width*offy + GetPixelChannels(image)*offx;
-
+    center=(ssize_t) GetPixelChannels(image)*width*offset.y+
+      GetPixelChannels(image)*offset.x;
     for (x=0; x < (ssize_t) image->columns; x++)
     {
-      PixelInfo
-        result;
+      register ssize_t
+        i;
 
-      register const MagickRealType
-        *restrict k;
+      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+      {
+        double
+          pixel;
 
-      register const Quantum
-        *restrict k_pixels;
+        PixelChannel
+          channel;
 
-      register ssize_t
-        u;
+        PixelTrait
+          traits;
 
-      ssize_t
-        v;
-
-      /* Starting Defaults */
-      GetPixelInfo(image,&result);
-      GetPixelInfoPixel(image,q,&result);
-      if ( method != VoronoiMorphology )
-        result.alpha = QuantumRange - result.alpha;
-
-      switch ( method ) {
-        case DistanceMorphology:
-            /* Add kernel Value and select the minimum value found. */
-            k = &kernel->values[ kernel->width*kernel->height-1 ];
-            k_pixels = p;
-            for (v=0; v <= (ssize_t) offy; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                if ( IsNaN(*k) ) continue;
-                Minimize(result.red,     (*k)+
-                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.green,   (*k)+
-                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.blue,    (*k)+
-                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                if (image->colorspace == CMYKColorspace)
-                  Minimize(result.black,(*k)+
-                    GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.alpha, (*k)+
-                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-              }
-              k_pixels += virt_width*GetPixelChannels(image);
-            }
-            /* repeat with the just processed pixels of this row */
-            k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
-            k_pixels = q-offx*GetPixelChannels(image);
-              for (u=0; u < (ssize_t) offx; u++, k--) {
-                if ( x+u-offx < 0 ) continue;  /* off the edge! */
-                if ( IsNaN(*k) ) continue;
-                Minimize(result.red,     (*k)+
-                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.green,   (*k)+
-                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.blue,    (*k)+
-                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                if (image->colorspace == CMYKColorspace)
-                  Minimize(result.black,(*k)+
-                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.alpha,(*k)+
-                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-              }
-            break;
-        case VoronoiMorphology:
-            /* Apply Distance to 'Matte' channel, while coping the color
-            ** values of the closest pixel.
-            **
-            ** This is experimental, and realy the 'alpha' component should
-            ** be completely separate 'masking' channel so that alpha can
-            ** also be used as part of the results.
-            */
-            k = &kernel->values[ kernel->width*kernel->height-1 ];
-            k_pixels = p;
-            for (v=0; v <= (ssize_t) offy; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                if ( IsNaN(*k) ) continue;
-                if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
+        register const MagickRealType
+          *restrict k;
+
+        register const Quantum
+          *restrict pixels;
+
+        register ssize_t
+          u;
+
+        ssize_t
+          v;
+
+        channel=GetPixelChannelChannel(image,i);
+        traits=GetPixelChannelTraits(image,channel);
+        if (traits == UndefinedPixelTrait)
+          continue;
+        if (((traits & CopyPixelTrait) != 0) || (GetPixelMask(image,p) != 0))
+          continue;
+        pixels=p;
+        pixel=(double) p[center+i];
+        switch (method)
+        {
+          case DistanceMorphology:
+          {
+            k=(&kernel->values[kernel->width*kernel->height-1]);
+            for (v=0; v <= offset.y; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if (IsNaN(*k) == MagickFalse)
                   {
-                    GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
-                      &result);
-                    result.alpha += *k;
+                    if ((pixels[i]+(*k)) < pixel)
+                      pixel=(double) pixels[i]+(*k);
                   }
+                k--;
+                pixels+=GetPixelChannels(image);
               }
-              k_pixels += virt_width*GetPixelChannels(image);
+              pixels+=width*GetPixelChannels(image);
+            }
+            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
+            pixels=q-offset.x*GetPixelChannels(image);
+            for (u=0; u < offset.x; u++)
+            {
+              if ((IsNaN(*k) == MagickFalse) || ((x+u-offset.x) < 0))
+                {
+                  if ((pixels[i]+(*k)) < pixel)
+                    pixel=(double) pixels[i]+(*k);
+                }
+              k--;
+              pixels+=GetPixelChannels(image);
             }
-            /* repeat with the just processed pixels of this row */
-            k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
-            k_pixels = q-offx*GetPixelChannels(image);
-              for (u=0; u < (ssize_t) offx; u++, k--) {
-                if ( x+u-offx < 0 ) continue;  /* off the edge! */
-                if ( IsNaN(*k) ) continue;
-                if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
+            break;
+          }
+          case VoronoiMorphology:
+          {
+            k=(&kernel->values[kernel->width*kernel->height-1]);
+            for (v=0; v < offset.y; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if (IsNaN(*k) == MagickFalse)
                   {
-                    GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
-                      &result);
-                    result.alpha += *k;
+                    if ((pixels[i]+(*k)) < pixel)
+                      pixel=(double) pixels[i]+(*k);
                   }
+                k--;
+                pixels+=GetPixelChannels(image);
               }
+              pixels+=width*GetPixelChannels(image);
+            }
+            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
+            pixels=q-offset.x*GetPixelChannels(image);
+            for (u=0; u < offset.x; u++)
+            {
+              if ((IsNaN(*k) == MagickFalse) || ((x+u-offset.x) < 0))
+                {
+                  if ((pixels[i]+(*k)) < pixel)
+                    pixel=(double) pixels[i]+(*k);
+                }
+              k--;
+              pixels+=GetPixelChannels(image);
+            }
             break;
-        default:
-          /* result directly calculated or assigned */
-          break;
-      }
-      /* Assign the resulting pixel values - Clamping Result */
-      switch ( method ) {
-        case VoronoiMorphology:
-          SetPixelInfoPixel(image,&result,q);
-          break;
-        default:
-          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelRed(image,ClampToQuantum(result.red),q);
-          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelGreen(image,ClampToQuantum(result.green),q);
-          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelBlue(image,ClampToQuantum(result.blue),q);
-          if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-              (image->colorspace == CMYKColorspace))
-            SetPixelBlack(image,ClampToQuantum(result.black),q);
-          if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0 &&
-              (image->alpha_trait == BlendPixelTrait))
-            SetPixelAlpha(image,ClampToQuantum(result.alpha),q);
-          break;
+          }
+          default:
+            break;
+        }
+        if (fabs(pixel-p[center+i]) > MagickEpsilon)
+          changed++;
+        SetPixelChannel(image,channel,ClampToQuantum(pixel),q);
       }
-      /* Count up changed pixels */
-      if ((GetPixelRed(image,p+r) != GetPixelRed(image,q)) ||
-          (GetPixelGreen(image,p+r) != GetPixelGreen(image,q)) ||
-          (GetPixelBlue(image,p+r) != GetPixelBlue(image,q)) ||
-          (GetPixelAlpha(image,p+r) != GetPixelAlpha(image,q)) ||
-          ((image->colorspace == CMYKColorspace) &&
-           (GetPixelBlack(image,p+r) != GetPixelBlack(image,q))))
-        changed++;  /* The pixel was changed in some way! */
-
-      p+=GetPixelChannels(image); /* increment pixel buffers */
+      p+=GetPixelChannels(image);
       q+=GetPixelChannels(image);
-    } /* x */
-
-    if ( SyncCacheViewAuthenticPixels(auth_view,exception) == MagickFalse)
+    }
+    if ( SyncCacheViewAuthenticPixels(morphology_view,exception) == MagickFalse)
       status=MagickFalse;
     if (image->progress_monitor != (MagickProgressMonitor) NULL)
-      if ( SetImageProgress(image,MorphologyTag,progress++,image->rows)
-                == MagickFalse )
-        status=MagickFalse;
-
-  } /* y */
+      {
+        MagickBooleanType
+          proceed;
 
-  /* Do the reversed pass through the image */
-  for (y=(ssize_t)image->rows-1; y >= 0; y--)
+#if defined(MAGICKCORE_OPENMP_SUPPORT)
+        #pragma omp critical (MagickCore_MorphologyImage)
+#endif
+        proceed=SetImageProgress(image,MorphologyTag,progress++,image->rows);
+        if (proceed == MagickFalse)
+          status=MagickFalse;
+      }
+  }
+  /*
+    Do the reverse pass through the image.
+  */
+  for (y=(ssize_t) image->rows-1; y >= 0; y--)
   {
     register const Quantum
       *restrict p;
@@ -3594,188 +3425,153 @@ static ssize_t MorphologyPrimitiveDirect(Image *image,
       x;
 
     ssize_t
-      r;
+      center;
 
+    /*
+       Read virtual pixels, and authentic pixels, from the same image.  We
+       read using virtual to get virtual pixel handling, but write back
+       into the same image.
+
+       Only the bottom half of the kernel is processed as we up the image.
+    */
     if (status == MagickFalse)
       break;
-    /* NOTE read virtual pixels, and authentic pixels, from the same image!
-    ** we read using virtual to get virtual pixel handling, but write back
-    ** into the same image.
-    **
-    ** Only the bottom half of the kernel will be processes as we
-    ** up the image.
-    */
-    p=GetCacheViewVirtualPixels(virt_view,-offx,y,virt_width,(size_t)
+    p=GetCacheViewVirtualPixels(image_view,-offset.x,y,width,(size_t)
       kernel->y+1,exception);
-    q=GetCacheViewAuthenticPixels(auth_view, 0, y, image->columns, 1,
+    q=GetCacheViewAuthenticPixels(morphology_view, 0, y, image->columns, 1,
       exception);
     if ((p == (const Quantum *) NULL) || (q == (Quantum *) NULL))
       status=MagickFalse;
     if (status == MagickFalse)
       break;
+    p+=(image->columns-1)*GetPixelChannels(image);
+    q+=(image->columns-1)*GetPixelChannels(image);
+    center=(ssize_t) (GetPixelChannels(image)*offset.x);
+    for (x=(ssize_t)image->columns-1; x >= 0; x--)
+    {
+      register ssize_t
+        i;
 
-    /* adjust positions to end of row */
-    p += (image->columns-1)*GetPixelChannels(image);
-    q += (image->columns-1)*GetPixelChannels(image);
+      for (i=0; i < (ssize_t) GetPixelChannels(image); i++)
+      {
+        double
+          pixel;
 
-    /* offset to origin in 'p'. while 'q' points to it directly */
-    r = GetPixelChannels(image)*offx;
+        PixelChannel
+          channel;
 
-    for (x=(ssize_t)image->columns-1; x >= 0; x--)
-    {
-      PixelInfo
-        result;
+        PixelTrait
+          traits;
 
-      register const MagickRealType
-        *restrict k;
+        register const MagickRealType
+          *restrict k;
 
-      register const Quantum
-        *restrict k_pixels;
+        register const Quantum
+          *restrict pixels;
 
-      register ssize_t
-        u;
+        register ssize_t
+          u;
 
-      ssize_t
-        v;
-
-      /* Default - previously modified pixel */
-      GetPixelInfo(image,&result);
-      GetPixelInfoPixel(image,q,&result);
-      if ( method != VoronoiMorphology )
-        result.alpha = QuantumRange - result.alpha;
-
-      switch ( method ) {
-        case DistanceMorphology:
-            /* Add kernel Value and select the minimum value found. */
-            k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
-            k_pixels = p;
-            for (v=offy; v < (ssize_t) kernel->height; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                if ( IsNaN(*k) ) continue;
-                Minimize(result.red,     (*k)+
-                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.green,   (*k)+
-                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.blue,    (*k)+
-                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                if ( image->colorspace == CMYKColorspace)
-                  Minimize(result.black,(*k)+
-                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.alpha, (*k)+
-                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-              }
-              k_pixels += virt_width*GetPixelChannels(image);
-            }
-            /* repeat with the just processed pixels of this row */
-            k = &kernel->values[ kernel->width*(kernel->y)+kernel->x-1 ];
-            k_pixels = q-offx*GetPixelChannels(image);
-              for (u=offx+1; u < (ssize_t) kernel->width; u++, k--) {
-                if ( (x+u-offx) >= (ssize_t)image->columns ) continue;
-                if ( IsNaN(*k) ) continue;
-                Minimize(result.red,     (*k)+
-                  GetPixelRed(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.green,   (*k)+
-                  GetPixelGreen(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.blue,    (*k)+
-                  GetPixelBlue(image,k_pixels+u*GetPixelChannels(image)));
-                if ( image->colorspace == CMYKColorspace)
-                  Minimize(result.black,   (*k)+
-                    GetPixelBlack(image,k_pixels+u*GetPixelChannels(image)));
-                Minimize(result.alpha, (*k)+
-                  GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)));
-              }
-            break;
-        case VoronoiMorphology:
-            /* Apply Distance to 'Matte' channel, coping the closest color.
-            **
-            ** This is experimental, and realy the 'alpha' component should
-            ** be completely separate 'masking' channel.
-            */
-            k = &kernel->values[ kernel->width*(kernel->y+1)-1 ];
-            k_pixels = p;
-            for (v=offy; v < (ssize_t) kernel->height; v++) {
-              for (u=0; u < (ssize_t) kernel->width; u++, k--) {
-                if ( IsNaN(*k) ) continue;
-                if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
+        ssize_t
+          v;
+
+        channel=GetPixelChannelChannel(image,i);
+        traits=GetPixelChannelTraits(image,channel);
+        if (traits == UndefinedPixelTrait)
+          continue;
+        if (((traits & CopyPixelTrait) != 0) || (GetPixelMask(image,p) != 0))
+          continue;
+        pixels=p;
+        pixel=(double) p[center+i];
+        switch (method)
+        {
+          case DistanceMorphology:
+          {
+            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
+            for (v=offset.y; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if (IsNaN(*k) == MagickFalse)
                   {
-                    GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
-                      &result);
-                    result.alpha += *k;
+                    if ((pixels[i]+(*k)) < pixel)
+                      pixel=(double) pixels[i]+(*k);
                   }
+                k--;
+                pixels+=GetPixelChannels(image);
               }
-              k_pixels += virt_width*GetPixelChannels(image);
+              pixels+=width*GetPixelChannels(image);
             }
-            /* repeat with the just processed pixels of this row */
-            k = &kernel->values[ kernel->width*(kernel->y)+kernel->x-1 ];
-            k_pixels = q-offx*GetPixelChannels(image);
-              for (u=offx+1; u < (ssize_t) kernel->width; u++, k--) {
-                if ( (x+u-offx) >= (ssize_t)image->columns ) continue;
-                if ( IsNaN(*k) ) continue;
-                if( result.alpha > (*k)+GetPixelAlpha(image,k_pixels+u*GetPixelChannels(image)) )
+            k=(&kernel->values[kernel->width*kernel->y+kernel->x-1]);
+            pixels=q-offset.x*GetPixelChannels(image);
+            for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
+            {
+              if ((IsNaN(*k) == MagickFalse) ||
+                  ((x+u-offset.x) >= (ssize_t)image->columns))
+                {
+                  if ((pixels[i]+(*k)) < pixel)
+                    pixel=(double) pixels[i]+(*k);
+                }
+              k--;
+              pixels+=GetPixelChannels(image);
+            }
+            break;
+          }
+          case VoronoiMorphology:
+          {
+            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
+            for (v=offset.y; v < (ssize_t) kernel->height; v++)
+            {
+              for (u=0; u < (ssize_t) kernel->width; u++)
+              {
+                if (IsNaN(*k) == MagickFalse)
                   {
-                    GetPixelInfoPixel(image,k_pixels+u*GetPixelChannels(image),
-                      &result);
-                    result.alpha += *k;
+                    if ((pixels[i]+(*k)) < pixel)
+                      pixel=(double) pixels[i]+(*k);
                   }
+                k--;
+                pixels+=GetPixelChannels(image);
               }
+              pixels+=width*GetPixelChannels(image);
+            }
+            k=(&kernel->values[kernel->width*(kernel->y+1)-1]);
+            pixels=q-offset.x*GetPixelChannels(image);
+            for (u=offset.x+1; u < (ssize_t) kernel->width; u++)
+            {
+              if ((IsNaN(*k) == MagickFalse) || ((x+u-offset.x) < 0))
+                {
+                  if ((pixels[i]+(*k)) < pixel)
+                    pixel=(double) pixels[i]+(*k);
+                }
+              k--;
+              pixels+=GetPixelChannels(image);
+            }
             break;
-        default:
-          /* result directly calculated or assigned */
-          break;
-      }
-      /* Assign the resulting pixel values - Clamping Result */
-      switch ( method ) {
-        case VoronoiMorphology:
-          SetPixelInfoPixel(image,&result,q);
-          break;
-        default:
-          if ((GetPixelRedTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelRed(image,ClampToQuantum(result.red),q);
-          if ((GetPixelGreenTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelGreen(image,ClampToQuantum(result.green),q);
-          if ((GetPixelBlueTraits(image) & UpdatePixelTrait) != 0)
-            SetPixelBlue(image,ClampToQuantum(result.blue),q);
-          if (((GetPixelBlackTraits(image) & UpdatePixelTrait) != 0) &&
-              (image->colorspace == CMYKColorspace))
-            SetPixelBlack(image,ClampToQuantum(result.black),q);
-          if ((GetPixelAlphaTraits(image) & UpdatePixelTrait) != 0 &&
-              (image->alpha_trait == BlendPixelTrait))
-            SetPixelAlpha(image,ClampToQuantum(result.alpha),q);
-          break;
+          }
+          default:
+            break;
+        }
+        if (fabs(pixel-p[center+i]) > MagickEpsilon)
+          changed++;
+        SetPixelChannel(image,channel,ClampToQuantum(pixel),q);
       }
-      /* Count up changed pixels */
-      if (   (GetPixelRed(image,p+r) != GetPixelRed(image,q))
-          || (GetPixelGreen(image,p+r) != GetPixelGreen(image,q))
-          || (GetPixelBlue(image,p+r) != GetPixelBlue(image,q))
-          || (GetPixelAlpha(image,p+r) != GetPixelAlpha(image,q))
-          || ((image->colorspace == CMYKColorspace) &&
-              (GetPixelBlack(image,p+r) != GetPixelBlack(image,q))))
-        changed++;  /* The pixel was changed in some way! */
-
-      p-=GetPixelChannels(image); /* go backward through pixel buffers */
-      q-=GetPixelChannels(image);
-    } /* x */
-    if ( SyncCacheViewAuthenticPixels(auth_view,exception) == MagickFalse)
-      status=MagickFalse;
-    if (image->progress_monitor != (MagickProgressMonitor) NULL)
-      if ( SetImageProgress(image,MorphologyTag,progress++,image->rows)
-                == MagickFalse )
-        status=MagickFalse;
-
-  } /* y */
-
-  auth_view=DestroyCacheView(auth_view);
-  virt_view=DestroyCacheView(virt_view);
+      p+=GetPixelChannels(image);
+      q+=GetPixelChannels(image);
+    }
+  }
+  morphology_view=DestroyCacheView(morphology_view);
+  image_view=DestroyCacheView(image_view);
   return(status ? (ssize_t) changed : -1);
 }
 
-/* Apply a Morphology by calling one of the above low level primitive
-** application functions.  This function handles any iteration loops,
-** composition or re-iteration of results, and compound morphology methods
-** that is based on multiple low-level (staged) morphology methods.
-**
-** Basically this provides the complex glue between the requested morphology
-** method and raw low-level implementation (above).
+/*
+  Apply a Morphology by calling one of the above low level primitive
+  application functions.  This function handles any iteration loops,
+  composition or re-iteration of results, and compound morphology methods that
+  is based on multiple low-level (staged) morphology methods.
+
+  Basically this provides the complex glue between the requested morphology
+  method and raw low-level implementation (above).
 */
 MagickPrivate Image *MorphologyApply(const Image *image,
   const MorphologyMethod method, const ssize_t iterations,