]> granicus.if.org Git - imagemagick/blobdiff - coders/histogram.c
(no commit message)
[imagemagick] / coders / histogram.c
index db2293f16a4cf17bab9718f6325735d2b8aa3309..6adcad6ea945e6aa81478a5c7f5b3e99e649b380 100644 (file)
@@ -3,22 +3,21 @@
 %                                                                             %
 %                                                                             %
 %                                                                             %
-%      H   H   IIIII   SSSSS  TTTTT   OOO    GGGG  RRRR    AAA   M   M        %
-%      H   H     I     SS       T    O   O  G      R   R  A   A  MM MM        %
-%      HHHHH     I      SSS     T    O   O  G  GG  RRRR   AAAAA  M M M        %
-%      H   H     I        SS    T    O   O  G   G  R R    A   A  M   M        %
-%      H   H   IIIII   SSSSS    T     OOO    GGG   R  R   A   A  M   M        %
+%       H   H  IIIII  SSSSS  TTTTT   OOO    GGGG  RRRR    AAA   M   M         %
+%       H   H    I    SS       T    O   O  G      R   R  A   A  MM MM         %
+%       HHHHH    I     SSS     T    O   O  G  GG  RRRR   AAAAA  M M M         %
+%       H   H    I       SS    T    O   O  G   G  R R    A   A  M   M         %
+%       H   H  IIIII  SSSSS    T     OOO    GGG   R  R   A   A  M   M         %
 %                                                                             %
 %                                                                             %
-%                        MagickCore Histogram Methods                         %
+%                          Write A Histogram Image.                           %
 %                                                                             %
 %                              Software Design                                %
-%                              Anthony Thyssen                                %
-%                               Fred Weinhaus                                 %
-%                                August 2009                                  %
+%                                John Cristy                                  %
+%                                 July 1992                                   %
 %                                                                             %
 %                                                                             %
-%  Copyright 1999-2010 ImageMagick Studio LLC, a non-profit organization      %
+%  Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization      %
 %  dedicated to making software imaging solutions freely available.           %
 %                                                                             %
 %  You may not use this file except in compliance with the License.  You may  %
 /*
   Include declarations.
 */
-#include "magick/studio.h"
-#include "magick/cache-view.h"
-#include "magick/color-private.h"
-#include "magick/enhance.h"
-#include "magick/exception.h"
-#include "magick/exception-private.h"
-#include "magick/hashmap.h"
-#include "magick/histogram.h"
-#include "magick/image.h"
-#include "magick/list.h"
-#include "magick/memory_.h"
-#include "magick/monitor-private.h"
-#include "magick/option.h"
-#include "magick/pixel-private.h"
-#include "magick/prepress.h"
-#include "magick/quantize.h"
-#include "magick/registry.h"
-#include "magick/semaphore.h"
-#include "magick/splay-tree.h"
-#include "magick/statistic.h"
-#include "magick/string_.h"
-\f
-/*
-  Define declarations.
-*/
-#define MaxTreeDepth  8
-#define NodesInAList  1536
-\f
-/*
-  Typedef declarations.
-*/
-typedef struct _NodeInfo
-{
-  struct _NodeInfo
-    *child[16];
-
-  ColorPacket
-    *list;
-
-  MagickSizeType
-    number_unique;
-
-  unsigned long
-    level;
-} NodeInfo;
-
-typedef struct _Nodes
-{
-  NodeInfo
-    nodes[NodesInAList];
-
-  struct _Nodes
-    *next;
-} Nodes;
-
-typedef struct _CubeInfo
-{
-  NodeInfo
-    *root;
-
-  long
-    x,
-    progress;
-
-  unsigned long
-    colors,
-    free_nodes;
-
-  NodeInfo
-    *node_info;
-
-  Nodes
-    *node_queue;
-} CubeInfo;
+#include "MagickCore/studio.h"
+#include "MagickCore/property.h"
+#include "MagickCore/blob.h"
+#include "MagickCore/blob-private.h"
+#include "MagickCore/cache.h"
+#include "MagickCore/color.h"
+#include "MagickCore/color-private.h"
+#include "MagickCore/constitute.h"
+#include "MagickCore/exception.h"
+#include "MagickCore/exception-private.h"
+#include "MagickCore/geometry.h"
+#include "MagickCore/histogram.h"
+#include "MagickCore/image-private.h"
+#include "MagickCore/magick.h"
+#include "MagickCore/memory_.h"
+#include "MagickCore/monitor.h"
+#include "MagickCore/monitor-private.h"
+#include "MagickCore/option.h"
+#include "MagickCore/pixel-accessor.h"
+#include "MagickCore/quantum-private.h"
+#include "MagickCore/resource_.h"
+#include "MagickCore/static.h"
+#include "MagickCore/statistic.h"
+#include "MagickCore/string_.h"
+#include "MagickCore/module.h"
+#include "MagickCore/utility.h"
 \f
 /*
   Forward declarations.
 */
-static CubeInfo
-  *GetCubeInfo(void);
-
-static NodeInfo
-  *GetNodeInfo(CubeInfo *,const unsigned long);
-
-static void
-  DestroyColorCube(const Image *,NodeInfo *);
+static MagickBooleanType
+  WriteHISTOGRAMImage(const ImageInfo *,Image *);
 \f
 /*
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %                                                                             %
 %                                                                             %
 %                                                                             %
-+   C l a s s i f y I m a g e C o l o r s                                     %
+%   R e g i s t e r H I S T O G R A M I m a g e                               %
 %                                                                             %
 %                                                                             %
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  ClassifyImageColors() builds a populated CubeInfo tree for the specified
-%  image.  The returned tree should be deallocated using DestroyCubeInfo()
-%  once it is no longer needed.
+%  RegisterHISTOGRAMImage() adds attributes for the Histogram image format
+%  to the list of supported formats.  The attributes include the image format
+%  tag, a method to read and/or write the format, whether the format
+%  supports the saving of more than one frame to the same file or blob,
+%  whether the format supports native in-memory I/O, and a brief
+%  description of the format.
 %
-%  The format of the ClassifyImageColors() method is:
+%  The format of the RegisterHISTOGRAMImage method is:
 %
-%      CubeInfo *ClassifyImageColors(const Image *image,
-%        ExceptionInfo *exception)
-%
-%  A description of each parameter follows.
-%
-%    o image: the image.
-%
-%    o exception: return any errors or warnings in this structure.
+%      size_t RegisterHISTOGRAMImage(void)
 %
 */
-
-static inline unsigned long ColorToNodeId(const Image *image,
-  const MagickPixelPacket *pixel,unsigned long index)
-{
-  unsigned long
-    id;
-
-  id=(unsigned long) (
-    ((ScaleQuantumToChar(ClampToQuantum(pixel->red)) >> index) & 0x01) |
-    ((ScaleQuantumToChar(ClampToQuantum(pixel->green)) >> index) & 0x01) << 1 |
-    ((ScaleQuantumToChar(ClampToQuantum(pixel->blue)) >> index) & 0x01) << 2);
-  if (image->matte != MagickFalse)
-    id|=((ScaleQuantumToChar(ClampToQuantum(pixel->opacity)) >> index) &
-      0x01) << 3;
-  return(id);
-}
-
-static CubeInfo *ClassifyImageColors(const Image *image,
-  ExceptionInfo *exception)
+ModuleExport size_t RegisterHISTOGRAMImage(void)
 {
-#define EvaluateImageTag  "  Compute image colors...  "
-
-  CacheView
-    *image_view;
-
-  CubeInfo
-    *cube_info;
-
-  long
-    y;
-
-  MagickBooleanType
-    proceed;
-
-  MagickPixelPacket
-    pixel,
-    target;
-
-  NodeInfo
-    *node_info;
-
-  register const IndexPacket
-    *indexes;
-
-  register const PixelPacket
-    *p;
-
-  register long
-    i,
-    x;
-
-  register unsigned long
-    id,
-    index,
-    level;
-
-  /*
-    Initialize color description tree.
-  */
-  assert(image != (const Image *) NULL);
-  assert(image->signature == MagickSignature);
-  if (image->debug != MagickFalse)
-    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
-  cube_info=GetCubeInfo();
-  if (cube_info == (CubeInfo *) NULL)
-    {
-      (void) ThrowMagickException(exception,GetMagickModule(),
-        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
-      return(cube_info);
-    }
-  GetMagickPixelPacket(image,&pixel);
-  GetMagickPixelPacket(image,&target);
-  image_view=AcquireCacheView(image);
-  for (y=0; y < (long) image->rows; y++)
-  {
-    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
-    if (p == (const PixelPacket *) NULL)
-      break;
-    indexes=GetCacheViewVirtualIndexQueue(image_view);
-    for (x=0; x < (long) image->columns; x++)
-    {
-      /*
-        Start at the root and proceed level by level.
-      */
-      node_info=cube_info->root;
-      index=MaxTreeDepth-1;
-      for (level=1; level < MaxTreeDepth; level++)
-      {
-        SetMagickPixelPacket(image,p,indexes+x,&pixel);
-        id=ColorToNodeId(image,&pixel,index);
-        if (node_info->child[id] == (NodeInfo *) NULL)
-          {
-            node_info->child[id]=GetNodeInfo(cube_info,level);
-            if (node_info->child[id] == (NodeInfo *) NULL)
-              {
-                (void) ThrowMagickException(exception,GetMagickModule(),
-                  ResourceLimitError,"MemoryAllocationFailed","`%s'",
-                  image->filename);
-                return(0);
-              }
-          }
-        node_info=node_info->child[id];
-        index--;
-      }
-      for (i=0; i < (long) node_info->number_unique; i++)
-      {
-        SetMagickPixelPacket(image,&node_info->list[i].pixel,
-          &node_info->list[i].index,&target);
-        if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
-          break;
-      }
-      if (i < (long) node_info->number_unique)
-        node_info->list[i].count++;
-      else
-        {
-          if (node_info->number_unique == 0)
-            node_info->list=(ColorPacket *) AcquireMagickMemory(
-              sizeof(*node_info->list));
-          else
-            node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
-              (size_t) (i+1),sizeof(*node_info->list));
-          if (node_info->list == (ColorPacket *) NULL)
-            {
-              (void) ThrowMagickException(exception,GetMagickModule(),
-                ResourceLimitError,"MemoryAllocationFailed","`%s'",
-                image->filename);
-              return(0);
-            }
-          node_info->list[i].pixel=(*p);
-          if ((image->colorspace == CMYKColorspace) ||
-              (image->storage_class == PseudoClass))
-            node_info->list[i].index=indexes[x];
-          node_info->list[i].count=1;
-          node_info->number_unique++;
-          cube_info->colors++;
-        }
-      p++;
-    }
-    proceed=SetImageProgress(image,EvaluateImageTag,y,image->rows);
-    if (proceed == MagickFalse)
-      break;
-  }
-  image_view=DestroyCacheView(image_view);
-  return(cube_info);
+  MagickInfo
+    *entry;
+
+  entry=SetMagickInfo("HISTOGRAM");
+  entry->encoder=(EncodeImageHandler *) WriteHISTOGRAMImage;
+  entry->adjoin=MagickFalse;
+  entry->format_type=ImplicitFormatType;
+  entry->description=ConstantString("Histogram of the image");
+  entry->module=ConstantString("HISTOGRAM");
+  (void) RegisterMagickInfo(entry);
+  return(MagickImageCoderSignature);
 }
 \f
 /*
@@ -305,62 +115,23 @@ static CubeInfo *ClassifyImageColors(const Image *image,
 %                                                                             %
 %                                                                             %
 %                                                                             %
-+   D e f i n e I m a g e H i s t o g r a m                                   %
+%   U n r e g i s t e r H I S T O G R A M I m a g e                           %
 %                                                                             %
 %                                                                             %
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  DefineImageHistogram() traverses the color cube tree and notes each colormap
-%  entry.  A colormap entry is any node in the color cube tree where the
-%  of unique colors is not zero.
+%  UnregisterHISTOGRAMImage() removes format registrations made by the
+%  HISTOGRAM module from the list of supported formats.
 %
-%  The format of the DefineImageHistogram method is:
+%  The format of the UnregisterHISTOGRAMImage method is:
 %
-%      DefineImageHistogram(const Image *image,NodeInfo *node_info,
-%        ColorPacket **unique_colors)
-%
-%  A description of each parameter follows.
-%
-%    o image: the image.
-%
-%    o node_info: the address of a structure of type NodeInfo which points to a
-%      node in the color cube tree that is to be pruned.
-%
-%    o histogram: the image histogram.
+%      UnregisterHISTOGRAMImage(void)
 %
 */
-static void DefineImageHistogram(const Image *image,NodeInfo *node_info,
-  ColorPacket **histogram)
+ModuleExport void UnregisterHISTOGRAMImage(void)
 {
-  register long
-    i;
-
-  unsigned long
-    number_children;
-
-  /*
-    Traverse any children.
-  */
-  number_children=image->matte == MagickFalse ? 8UL : 16UL;
-  for (i=0; i < (long) number_children; i++)
-    if (node_info->child[i] != (NodeInfo *) NULL)
-      DefineImageHistogram(image,node_info->child[i],histogram);
-  if (node_info->level == (MaxTreeDepth-1))
-    {
-      register ColorPacket
-        *p;
-
-      p=node_info->list;
-      for (i=0; i < (long) node_info->number_unique; i++)
-      {
-        (*histogram)->pixel=p->pixel;
-        (*histogram)->index=p->index;
-        (*histogram)->count=p->count;
-        (*histogram)++;
-        p++;
-      }
-    }
+  (void) UnregisterMagickInfo("HISTOGRAM");
 }
 \f
 /*
@@ -368,956 +139,257 @@ static void DefineImageHistogram(const Image *image,NodeInfo *node_info,
 %                                                                             %
 %                                                                             %
 %                                                                             %
-+   D e s t r o y C u b e I n f o                                             %
+%   W r i t e H I S T O G R A M I m a g e                                     %
 %                                                                             %
 %                                                                             %
 %                                                                             %
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 %
-%  DestroyCubeInfo() deallocates memory associated with a CubeInfo structure.
+%  WriteHISTOGRAMImage() writes an image to a file in Histogram format.
+%  The image shows a histogram of the color (or gray) values in the image.  The
+%  image consists of three overlaid histograms:  a red one for the red channel,
+%  a green one for the green channel, and a blue one for the blue channel.  The
+%  image comment contains a list of unique pixel values and the number of times
+%  each occurs in the image.
 %
-%  The format of the DestroyCubeInfo method is:
+%  This method is strongly based on a similar one written by
+%  muquit@warm.semcor.com which in turn is based on ppmhistmap of netpbm.
 %
-%      DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
+%  The format of the WriteHISTOGRAMImage method is:
 %
-%  A description of each parameter follows:
-%
-%    o image: the image.
-%
-%    o cube_info: the address of a structure of type CubeInfo.
-%
-*/
-static CubeInfo *DestroyCubeInfo(const Image *image,CubeInfo *cube_info)
-{
-  register Nodes
-    *nodes;
-
-  /*
-    Release color cube tree storage.
-  */
-  DestroyColorCube(image,cube_info->root);
-  do
-  {
-    nodes=cube_info->node_queue->next;
-    cube_info->node_queue=(Nodes *)
-      RelinquishMagickMemory(cube_info->node_queue);
-    cube_info->node_queue=nodes;
-  } while (cube_info->node_queue != (Nodes *) NULL);
-  return((CubeInfo *) RelinquishMagickMemory(cube_info));
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
-+  D e s t r o y C o l o r C u b e                                            %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  DestroyColorCube() traverses the color cube tree and frees the list of
-%  unique colors.
-%
-%  The format of the DestroyColorCube method is:
-%
-%      void DestroyColorCube(const Image *image,const NodeInfo *node_info)
+%      MagickBooleanType WriteHISTOGRAMImage(const ImageInfo *image_info,
+%        Image *image)
 %
 %  A description of each parameter follows.
 %
-%    o image: the image.
+%    o image_info: the image info.
 %
-%    o node_info: the address of a structure of type NodeInfo which points to a
-%      node in the color cube tree that is to be pruned.
+%    o image:  The image.
 %
 */
-static void DestroyColorCube(const Image *image,NodeInfo *node_info)
-{
-  register long
-    i;
 
-  unsigned long
-    number_children;
-
-  /*
-    Traverse any children.
-  */
-  number_children=image->matte == MagickFalse ? 8UL : 16UL;
-  for (i=0; i < (long) number_children; i++)
-    if (node_info->child[i] != (NodeInfo *) NULL)
-      DestroyColorCube(image,node_info->child[i]);
-  if (node_info->list != (ColorPacket *) NULL)
-    node_info->list=(ColorPacket *) RelinquishMagickMemory(node_info->list);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
-+   G e t C u b e I n f o                                                     %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  GetCubeInfo() initializes the CubeInfo data structure.
-%
-%  The format of the GetCubeInfo method is:
-%
-%      cube_info=GetCubeInfo()
-%
-%  A description of each parameter follows.
-%
-%    o cube_info: A pointer to the Cube structure.
-%
-*/
-static CubeInfo *GetCubeInfo(void)
+static inline size_t MagickMax(const size_t x,const size_t y)
 {
-  CubeInfo
-    *cube_info;
-
-  /*
-    Initialize tree to describe color cube.
-  */
-  cube_info=(CubeInfo *) AcquireAlignedMemory(1,sizeof(*cube_info));
-  if (cube_info == (CubeInfo *) NULL)
-    return((CubeInfo *) NULL);
-  (void) ResetMagickMemory(cube_info,0,sizeof(*cube_info));
-  /*
-    Initialize root node.
-  */
-  cube_info->root=GetNodeInfo(cube_info,0);
-  if (cube_info->root == (NodeInfo *) NULL)
-    return((CubeInfo *) NULL);
-  return(cube_info);
+  if (x > y)
+    return(x);
+  return(y);
 }
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%  G e t I m a g e H i s t o g r a m                                          %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  GetImageHistogram() returns the unique colors in an image.
-%
-%  The format of the GetImageHistogram method is:
-%
-%      unsigned long GetImageHistogram(const Image *image,
-%        unsigned long *number_colors,ExceptionInfo *exception)
-%
-%  A description of each parameter follows.
-%
-%    o image: the image.
-%
-%    o file:  Write a histogram of the color distribution to this file handle.
-%
-%    o exception: return any errors or warnings in this structure.
-%
-*/
-MagickExport ColorPacket *GetImageHistogram(const Image *image,
-  unsigned long *number_colors,ExceptionInfo *exception)
+
+static MagickBooleanType WriteHISTOGRAMImage(const ImageInfo *image_info,
+  Image *image)
 {
-  ColorPacket
-    *histogram;
+#define HistogramDensity  "256x200"
 
-  CubeInfo
-    *cube_info;
+  ChannelType
+    channel;
 
-  *number_colors=0;
-  histogram=(ColorPacket *) NULL;
-  cube_info=ClassifyImageColors(image,exception);
-  if (cube_info != (CubeInfo *) NULL)
-    {
-      histogram=(ColorPacket *) AcquireQuantumMemory((size_t) cube_info->colors,
-        sizeof(*histogram));
-      if (histogram == (ColorPacket *) NULL)
-        (void) ThrowMagickException(exception,GetMagickModule(),
-          ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
-      else
-        {
-          ColorPacket
-            *root;
+  char
+    filename[MaxTextExtent];
 
-          *number_colors=cube_info->colors;
-          root=histogram;
-          DefineImageHistogram(image,cube_info->root,&root);
-        }
-    }
-  cube_info=DestroyCubeInfo(image,cube_info);
-  return(histogram);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
-+  G e t N o d e I n f o                                                      %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  GetNodeInfo() allocates memory for a new node in the color cube tree and
-%  presets all fields to zero.
-%
-%  The format of the GetNodeInfo method is:
-%
-%      NodeInfo *GetNodeInfo(CubeInfo *cube_info,const unsigned long level)
-%
-%  A description of each parameter follows.
-%
-%    o cube_info: A pointer to the CubeInfo structure.
-%
-%    o level: Specifies the level in the storage_class the node resides.
-%
-*/
-static NodeInfo *GetNodeInfo(CubeInfo *cube_info,const unsigned long level)
-{
-  NodeInfo
-    *node_info;
+  const char
+    *option;
 
-  if (cube_info->free_nodes == 0)
-    {
-      Nodes
-        *nodes;
+  ExceptionInfo
+    *exception;
 
-      /*
-        Allocate a new nodes of nodes.
-      */
-      nodes=(Nodes *) AcquireAlignedMemory(1,sizeof(*nodes));
-      if (nodes == (Nodes *) NULL)
-        return((NodeInfo *) NULL);
-      nodes->next=cube_info->node_queue;
-      cube_info->node_queue=nodes;
-      cube_info->node_info=nodes->nodes;
-      cube_info->free_nodes=NodesInAList;
-    }
-  cube_info->free_nodes--;
-  node_info=cube_info->node_info++;
-  (void) ResetMagickMemory(node_info,0,sizeof(*node_info));
-  node_info->level=level;
-  return(node_info);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%  I s H i s t o g r a m I m a g e                                            %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  IsHistogramImage() returns MagickTrue if the image has 1024 unique colors or
-%  less.
-%
-%  The format of the IsHistogramImage method is:
-%
-%      MagickBooleanType IsHistogramImage(const Image *image,
-%        ExceptionInfo *exception)
-%
-%  A description of each parameter follows.
-%
-%    o image: the image.
-%
-%    o exception: return any errors or warnings in this structure.
-%
-*/
-MagickExport MagickBooleanType IsHistogramImage(const Image *image,
-  ExceptionInfo *exception)
-{
-#define MaximumUniqueColors  1024
+  Image
+    *histogram_image;
 
-  CacheView
-    *image_view;
+  ImageInfo
+    *write_info;
 
-  CubeInfo
-    *cube_info;
+  MagickBooleanType
+    status;
 
-  long
-    y;
+  PixelInfo
+    *histogram;
 
-  MagickPixelPacket
-    pixel,
-    target;
+  MagickRealType
+    maximum,
+    scale;
 
-  register const IndexPacket
-    *indexes;
+  RectangleInfo
+    geometry;
 
-  register const PixelPacket
+  register const Quantum
     *p;
 
-  register long
-    x;
+  register Quantum
+    *q,
+    *r;
 
-  register NodeInfo
-    *node_info;
+  register ssize_t
+    x;
 
-  register long
-    i;
+  size_t
+    length;
 
-  unsigned long
-    id,
-    index,
-    level;
+  ssize_t
+    y;
 
+  /*
+    Allocate histogram image.
+  */
+  assert(image_info != (const ImageInfo *) NULL);
+  assert(image_info->signature == MagickSignature);
   assert(image != (Image *) NULL);
   assert(image->signature == MagickSignature);
   if (image->debug != MagickFalse)
-    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
-  if ((image->storage_class == PseudoClass) && (image->colors <= 256))
-    return(MagickTrue);
-  if (image->storage_class == PseudoClass)
-    return(MagickFalse);
+    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
+      image_info->filename);
+  SetGeometry(image,&geometry);
+  if (image_info->density == (char *) NULL)
+    (void) ParseAbsoluteGeometry(HistogramDensity,&geometry);
+  else
+    (void) ParseAbsoluteGeometry(image_info->density,&geometry);
+  histogram_image=CloneImage(image,geometry.width,geometry.height,MagickTrue,
+    &image->exception);
+  if (histogram_image == (Image *) NULL)
+    ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
+  (void) SetImageStorageClass(histogram_image,DirectClass);
   /*
-    Initialize color description tree.
+    Allocate histogram count arrays.
   */
-  cube_info=GetCubeInfo();
-  if (cube_info == (CubeInfo *) NULL)
+  length=MagickMax((size_t) ScaleQuantumToChar((Quantum) QuantumRange)+1UL,
+    histogram_image->columns);
+  histogram=(PixelInfo *) AcquireQuantumMemory(length,
+    sizeof(*histogram));
+  if (histogram == (PixelInfo *) NULL)
     {
-      (void) ThrowMagickException(exception,GetMagickModule(),
-        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
-      return(MagickFalse);
+      histogram_image=DestroyImage(histogram_image);
+      ThrowWriterException(ResourceLimitError,"MemoryAllocationFailed");
     }
-  GetMagickPixelPacket(image,&pixel);
-  GetMagickPixelPacket(image,&target);
-  image_view=AcquireCacheView(image);
-  for (y=0; y < (long) image->rows; y++)
+  /*
+    Initialize histogram count arrays.
+  */
+  channel=image_info->channel;
+  (void) ResetMagickMemory(histogram,0,length*sizeof(*histogram));
+  for (y=0; y < (ssize_t) image->rows; y++)
   {
-    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
-    if (p == (const PixelPacket *) NULL)
+    p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception);
+    if (p == (const Quantum *) NULL)
       break;
-    indexes=GetCacheViewVirtualIndexQueue(image_view);
-    for (x=0; x < (long) image->columns; x++)
+    for (x=0; x < (ssize_t) image->columns; x++)
     {
-      /*
-        Start at the root and proceed level by level.
-      */
-      node_info=cube_info->root;
-      index=MaxTreeDepth-1;
-      for (level=1; level < MaxTreeDepth; level++)
-      {
-        SetMagickPixelPacket(image,p,indexes+x,&pixel);
-        id=ColorToNodeId(image,&pixel,index);
-        if (node_info->child[id] == (NodeInfo *) NULL)
-          {
-            node_info->child[id]=GetNodeInfo(cube_info,level);
-            if (node_info->child[id] == (NodeInfo *) NULL)
-              {
-                (void) ThrowMagickException(exception,GetMagickModule(),
-                  ResourceLimitError,"MemoryAllocationFailed","`%s'",
-                  image->filename);
-                break;
-              }
-          }
-        node_info=node_info->child[id];
-        index--;
-      }
-      if (level < MaxTreeDepth)
-        break;
-      for (i=0; i < (long) node_info->number_unique; i++)
-      {
-        SetMagickPixelPacket(image,&node_info->list[i].pixel,
-          &node_info->list[i].index,&target);
-        if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
-          break;
-      }
-      if (i < (long) node_info->number_unique)
-        node_info->list[i].count++;
-      else
-        {
-          /*
-            Add this unique color to the color list.
-          */
-          if (node_info->number_unique == 0)
-            node_info->list=(ColorPacket *) AcquireMagickMemory(
-              sizeof(*node_info->list));
-          else
-            node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
-              (size_t) (i+1),sizeof(*node_info->list));
-          if (node_info->list == (ColorPacket *) NULL)
-            {
-              (void) ThrowMagickException(exception,GetMagickModule(),
-                ResourceLimitError,"MemoryAllocationFailed","`%s'",
-                image->filename);
-              break;
-            }
-          node_info->list[i].pixel=(*p);
-          if ((image->colorspace == CMYKColorspace) ||
-              (image->storage_class == PseudoClass))
-            node_info->list[i].index=indexes[x];
-          node_info->list[i].count=1;
-          node_info->number_unique++;
-          cube_info->colors++;
-          if (cube_info->colors > MaximumUniqueColors)
-            break;
-        }
-      p++;
+      if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
+        histogram[ScaleQuantumToChar(GetPixelRed(image,p))].red++;
+      if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
+        histogram[ScaleQuantumToChar(GetPixelGreen(image,p))].green++;
+      if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
+        histogram[ScaleQuantumToChar(GetPixelBlue(image,p))].blue++;
+      p+=GetPixelChannels(image);
     }
-    if (x < (long) image->columns)
-      break;
   }
-  image_view=DestroyCacheView(image_view);
-  cube_info=DestroyCubeInfo(image,cube_info);
-  return(y < (long) image->rows ? MagickFalse : MagickTrue);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%  I s P a l e t t e I m a g e                                                %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  IsPaletteImage() returns MagickTrue if the image is PseudoClass and has 256
-%  unique colors or less.
-%
-%  The format of the IsPaletteImage method is:
-%
-%      MagickBooleanType IsPaletteImage(const Image *image,
-%        ExceptionInfo *exception)
-%
-%  A description of each parameter follows.
-%
-%    o image: the image.
-%
-%    o exception: return any errors or warnings in this structure.
-%
-*/
-MagickExport MagickBooleanType IsPaletteImage(const Image *image,
-  ExceptionInfo *exception)
-{
-  CacheView
-    *image_view;
-
-  CubeInfo
-    *cube_info;
-
-  long
-    y;
-
-  MagickPixelPacket
-    pixel,
-    target;
-
-  register const IndexPacket
-    *indexes;
-
-  register const PixelPacket
-    *p;
-
-  register long
-    x;
-
-  register NodeInfo
-    *node_info;
-
-  register long
-    i;
-
-  unsigned long
-    id,
-    index,
-    level;
-
-  assert(image != (Image *) NULL);
-  assert(image->signature == MagickSignature);
-  if (image->debug != MagickFalse)
-    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
-  if ((image->storage_class == PseudoClass) && (image->colors <= 256))
-    return(MagickTrue);
-  if (image->storage_class == PseudoClass)
-    return(MagickFalse);
+  maximum=histogram[0].red;
+  for (x=0; x < (ssize_t) histogram_image->columns; x++)
+  {
+    if (((GetPixelRedTraits(image) & ActivePixelTrait) != 0) &&
+        (maximum < histogram[x].red))
+      maximum=histogram[x].red;
+    if (((GetPixelGreenTraits(image) & ActivePixelTrait) != 0) &&
+        (maximum < histogram[x].green))
+      maximum=histogram[x].green;
+    if (((GetPixelBlueTraits(image) & ActivePixelTrait) != 0) &&
+        (maximum < histogram[x].blue))
+      maximum=histogram[x].blue;
+  }
+  scale=(MagickRealType) histogram_image->rows/maximum;
   /*
-    Initialize color description tree.
+    Initialize histogram image.
   */
-  cube_info=GetCubeInfo();
-  if (cube_info == (CubeInfo *) NULL)
-    {
-      (void) ThrowMagickException(exception,GetMagickModule(),
-        ResourceLimitError,"MemoryAllocationFailed","`%s'",image->filename);
-      return(MagickFalse);
-    }
-  GetMagickPixelPacket(image,&pixel);
-  GetMagickPixelPacket(image,&target);
-  image_view=AcquireCacheView(image);
-  for (y=0; y < (long) image->rows; y++)
+  exception=(&image->exception);
+  (void) QueryColorDatabase("#000000",&histogram_image->background_color,
+    &image->exception);
+  (void) SetImageBackgroundColor(histogram_image);
+  for (x=0; x < (ssize_t) histogram_image->columns; x++)
   {
-    p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception);
-    if (p == (const PixelPacket *) NULL)
+    q=GetAuthenticPixels(histogram_image,x,0,1,histogram_image->rows,exception);
+    if (q == (const Quantum *) NULL)
       break;
-    indexes=GetCacheViewVirtualIndexQueue(image_view);
-    for (x=0; x < (long) image->columns; x++)
-    {
-      /*
-        Start at the root and proceed level by level.
-      */
-      node_info=cube_info->root;
-      index=MaxTreeDepth-1;
-      for (level=1; level < MaxTreeDepth; level++)
+    if ((GetPixelRedTraits(image) & ActivePixelTrait) != 0)
       {
-        SetMagickPixelPacket(image,p,indexes+x,&pixel);
-        id=ColorToNodeId(image,&pixel,index);
-        if (node_info->child[id] == (NodeInfo *) NULL)
-          {
-            node_info->child[id]=GetNodeInfo(cube_info,level);
-            if (node_info->child[id] == (NodeInfo *) NULL)
-              {
-                (void) ThrowMagickException(exception,GetMagickModule(),
-                  ResourceLimitError,"MemoryAllocationFailed","`%s'",
-                  image->filename);
-                break;
-              }
-          }
-        node_info=node_info->child[id];
-        index--;
-      }
-      if (level < MaxTreeDepth)
-        break;
-      for (i=0; i < (long) node_info->number_unique; i++)
-      {
-        SetMagickPixelPacket(image,&node_info->list[i].pixel,
-          &node_info->list[i].index,&target);
-        if (IsMagickColorEqual(&pixel,&target) != MagickFalse)
-          break;
-      }
-      if (i < (long) node_info->number_unique)
-        node_info->list[i].count++;
-      else
+        y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].red-0.5);
+        r=q+y;
+        for ( ; y < (ssize_t) histogram_image->rows; y++)
         {
-          /*
-            Add this unique color to the color list.
-          */
-          if (node_info->number_unique == 0)
-            node_info->list=(ColorPacket *) AcquireMagickMemory(
-              sizeof(*node_info->list));
-          else
-            node_info->list=(ColorPacket *) ResizeQuantumMemory(node_info->list,
-              (size_t) (i+1),sizeof(*node_info->list));
-          if (node_info->list == (ColorPacket *) NULL)
-            {
-              (void) ThrowMagickException(exception,GetMagickModule(),
-                ResourceLimitError,"MemoryAllocationFailed","`%s'",
-                image->filename);
-              break;
-            }
-          node_info->list[i].pixel=(*p);
-          if ((image->colorspace == CMYKColorspace) ||
-              (image->storage_class == PseudoClass))
-            node_info->list[i].index=indexes[x];
-          node_info->list[i].count=1;
-          node_info->number_unique++;
-          cube_info->colors++;
-          if (cube_info->colors > 256)
-            break;
+          SetPixelRed(histogram_image,QuantumRange,r);
+          r++;
         }
-      p++;
-    }
-    if (x < (long) image->columns)
-      break;
-  }
-  image_view=DestroyCacheView(image_view);
-  cube_info=DestroyCubeInfo(image,cube_info);
-  return(y < (long) image->rows ? MagickFalse : MagickTrue);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%     M i n M a x S t r e t c h I m a g e                                     %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  MinMaxStretchImage() uses the exact minimum and maximum values found in
-%  each of the channels given, as the BlackPoint and WhitePoint to linearly
-%  stretch the colors (and histogram) of the image.  The stretch points are
-%  also moved further inward by the adjustment values given.
-%
-%  If the adjustment values are both zero this function is equivelent to a
-%  perfect normalization (or autolevel) of the image.
-%
-%  Each channel is stretched independantally of each other (producing color
-%  distortion) unless the special 'SyncChannels' flag is also provided in the
-%  channels setting. If this flag is present the minimum and maximum point
-%  will be extracted from all the given channels, and those channels will be
-%  stretched by exactly the same amount (preventing color distortion).
-%
-%  In the special case that only ONE value is found in a channel of the image
-%  that value is not stretched, that value is left as is.
-%
-%  The 'SyncChannels' is turned on in the 'DefaultChannels' setting by
-%  default.
-%
-%  The format of the MinMaxStretchImage method is:
-%
-%      MagickBooleanType MinMaxStretchImage(Image *image,
-%        const ChannelType channel, const double black_adjust,
-%        const double white_adjust)
-%
-%  A description of each parameter follows:
-%
-%    o image: The image to auto-level
-%
-%    o channel: The channels to auto-level.  If the special 'SyncChannels'
-%      flag is set, all the given channels are stretched by the same amount.
-%
-%    o black_adjust, white_adjust:  Move the Black/White Point inward
-%      from the minimum and maximum points by this color value.
-%
-*/
-
-MagickExport MagickBooleanType MinMaxStretchImage(Image *image,
-  const ChannelType channel,const double black_value,const double white_value)
-{
-  double
-    min,max;
-
-  MagickStatusType
-    status;
-
-  status=MagickTrue;
-  if ((channel & SyncChannels) != 0)
-    {
-      /*
-        Auto-level all channels equally.
-      */
-      (void) GetImageChannelRange(image,channel,&min,&max,&image->exception);
-      min+=black_value;
-      max-=white_value;
-      if ( fabs(min-max) >= MagickEpsilon )
-        status = LevelImageChannel(image,channel,min,max,1.0);
-      return(status);
-    }
-  /*
-    Auto-level each channel separately.
-  */
-  if ((channel & RedChannel) != 0)
-    {
-      (void) GetImageChannelRange(image,RedChannel,&min,&max,&image->exception);
-      min+=black_value;
-      max-=white_value;
-      if ( fabs(min-max) >= MagickEpsilon )
-        status&=LevelImageChannel(image,RedChannel,min,max,1.0);
-    }
-  if ((channel & GreenChannel) != 0)
-    {
-      (void) GetImageChannelRange(image,GreenChannel,&min,&max,
-        &image->exception);
-      min+=black_value;
-      max-=white_value;
-      if ( fabs(min-max) >= MagickEpsilon )
-        status&=LevelImageChannel(image,GreenChannel,min,max,1.0);
-    }
-  if ((channel & BlueChannel) != 0)
-    {
-      (void) GetImageChannelRange(image,BlueChannel,&min,&max,
-        &image->exception);
-      min+=black_value;
-      max-=white_value;
-      if ( fabs(min-max) >= MagickEpsilon )
-        status&=LevelImageChannel(image,BlueChannel,min,max,1.0);
-    }
-  if (((channel & OpacityChannel) != 0) &&
-       (image->matte == MagickTrue))
-    {
-      (void) GetImageChannelRange(image,OpacityChannel,&min,&max,
-        &image->exception);
-      min+=black_value;
-      max-=white_value;
-      if ( fabs(min-max) >= MagickEpsilon )
-        status&=LevelImageChannel(image,OpacityChannel,min,max,1.0);
-    }
-  if (((channel & IndexChannel) != 0) &&
-       (image->colorspace == CMYKColorspace))
-    {
-      (void) GetImageChannelRange(image,IndexChannel,&min,&max,
-        &image->exception);
-      min+=black_value;
-      max-=white_value;
-      if ( fabs(min-max) >= MagickEpsilon )
-        status&=LevelImageChannel(image,IndexChannel,min,max,1.0);
-    }
-  return(status);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%  G e t N u m b e r C o l o r s                                              %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  GetNumberColors() returns the number of unique colors in an image.
-%
-%  The format of the GetNumberColors method is:
-%
-%      unsigned long GetNumberColors(const Image *image,FILE *file,
-%        ExceptionInfo *exception)
-%
-%  A description of each parameter follows.
-%
-%    o image: the image.
-%
-%    o file:  Write a histogram of the color distribution to this file handle.
-%
-%    o exception: return any errors or warnings in this structure.
-%
-*/
-
-#if defined(__cplusplus) || defined(c_plusplus)
-extern "C" {
-#endif
-
-static int HistogramCompare(const void *x,const void *y)
-{
-  const ColorPacket
-    *color_1,
-    *color_2;
-
-  color_1=(const ColorPacket *) x;
-  color_2=(const ColorPacket *) y;
-  if (color_2->pixel.red != color_1->pixel.red)
-    return((int) color_1->pixel.red-(int) color_2->pixel.red);
-  if (color_2->pixel.green != color_1->pixel.green)
-    return((int) color_1->pixel.green-(int) color_2->pixel.green);
-  if (color_2->pixel.blue != color_1->pixel.blue)
-    return((int) color_1->pixel.blue-(int) color_2->pixel.blue);
-  return((int) color_2->count-(int) color_1->count);
-}
-
-#if defined(__cplusplus) || defined(c_plusplus)
-}
-#endif
-
-MagickExport unsigned long GetNumberColors(const Image *image,FILE *file,
-  ExceptionInfo *exception)
-{
-#define HistogramImageTag  "Histogram/Image"
-
-  char
-    color[MaxTextExtent],
-    hex[MaxTextExtent],
-    tuple[MaxTextExtent];
-
-  ColorPacket
-    *histogram;
-
-  MagickBooleanType
-    status;
-
-  MagickPixelPacket
-    pixel;
-
-  register ColorPacket
-    *p;
-
-  register long
-    i;
-
-  unsigned long
-    number_colors;
-
-  number_colors=0;
-  if (file == (FILE *) NULL)
-    {
-      CubeInfo
-        *cube_info;
-
-      cube_info=ClassifyImageColors(image,exception);
-      if (cube_info != (CubeInfo *) NULL)
-        number_colors=cube_info->colors;
-      cube_info=DestroyCubeInfo(image,cube_info);
-      return(number_colors);
-    }
-  histogram=GetImageHistogram(image,&number_colors,exception);
-  if (histogram == (ColorPacket *) NULL)
-    return(number_colors);
-  qsort((void *) histogram,(size_t) number_colors,sizeof(*histogram),
-    HistogramCompare);
-  GetMagickPixelPacket(image,&pixel);
-  p=histogram;
-  for (i=0; i < (long) number_colors; i++)
-  {
-    SetMagickPixelPacket(image,&p->pixel,&p->index,&pixel);
-    (void) CopyMagickString(tuple,"(",MaxTextExtent);
-    ConcatenateColorComponent(&pixel,RedChannel,X11Compliance,tuple);
-    (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
-    ConcatenateColorComponent(&pixel,GreenChannel,X11Compliance,tuple);
-    (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
-    ConcatenateColorComponent(&pixel,BlueChannel,X11Compliance,tuple);
-    if (pixel.colorspace == CMYKColorspace)
-      {
-        (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
-        ConcatenateColorComponent(&pixel,IndexChannel,X11Compliance,tuple);
       }
-    if (pixel.matte != MagickFalse)
+    if ((GetPixelGreenTraits(image) & ActivePixelTrait) != 0)
       {
-        (void) ConcatenateMagickString(tuple,",",MaxTextExtent);
-        ConcatenateColorComponent(&pixel,OpacityChannel,X11Compliance,tuple);
+        y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].green-0.5);
+        r=q+y;
+        for ( ; y < (ssize_t) histogram_image->rows; y++)
+        {
+          SetPixelGreen(histogram_image,QuantumRange,r);
+          r++;
+        }
       }
-    (void) ConcatenateMagickString(tuple,")",MaxTextExtent);
-    (void) QueryMagickColorname(image,&pixel,SVGCompliance,color,exception);
-    GetColorTuple(&pixel,MagickTrue,hex);
-    (void) fprintf(file,MagickSizeFormat,p->count);
-    (void) fprintf(file,": %s %s %s\n",tuple,hex,color);
-    if (image->progress_monitor != (MagickProgressMonitor) NULL)
+    if ((GetPixelBlueTraits(image) & ActivePixelTrait) != 0)
       {
-        MagickBooleanType
-          proceed;
-
-        proceed=SetImageProgress(image,HistogramImageTag,i,number_colors);
-        if (proceed == MagickFalse)
-          status=MagickFalse;
+        y=(ssize_t) ceil(histogram_image->rows-scale*histogram[x].blue-0.5);
+        r=q+y;
+        for ( ; y < (ssize_t) histogram_image->rows; y++)
+        {
+          SetPixelBlue(histogram_image,QuantumRange,r);
+          r++;
+        }
       }
-    p++;
+    if (SyncAuthenticPixels(histogram_image,exception) == MagickFalse)
+      break;
+    status=SetImageProgress(image,SaveImageTag,y,histogram_image->rows);
+    if (status == MagickFalse)
+      break;
   }
-  (void) fflush(file);
-  histogram=(ColorPacket *) RelinquishMagickMemory(histogram);
-  return(number_colors);
-}
-\f
-/*
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%  U n i q u e I m a g e C o l o r s                                          %
-%                                                                             %
-%                                                                             %
-%                                                                             %
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-%
-%  UniqueImageColors() returns the unique colors of an image.
-%
-%  The format of the UniqueImageColors method is:
-%
-%      Image *UniqueImageColors(const Image *image,ExceptionInfo *exception)
-%
-%  A description of each parameter follows.
-%
-%    o image: the image.
-%
-%    o exception: return any errors or warnings in this structure.
-%
-*/
-
-static void UniqueColorsToImage(Image *image,CubeInfo *cube_info,
-  const NodeInfo *node_info,ExceptionInfo *exception)
-{
-#define UniqueColorsImageTag  "UniqueColors/Image"
-
-  MagickBooleanType
-    status;
-
-  register long
-    i;
-
-  unsigned long
-    number_children;
-
   /*
-    Traverse any children.
+    Relinquish resources.
   */
-  number_children=image->matte == MagickFalse ? 8UL : 16UL;
-  for (i=0; i < (long) number_children; i++)
-    if (node_info->child[i] != (NodeInfo *) NULL)
-      UniqueColorsToImage(image,cube_info,node_info->child[i],exception);
-  if (node_info->level == (MaxTreeDepth-1))
+  histogram=(PixelInfo *) RelinquishMagickMemory(histogram);
+  option=GetImageOption(image_info,"histogram:unique-colors");
+  if ((option == (const char *) NULL) || (IsMagickTrue(option) != MagickFalse))
     {
-      register ColorPacket
-        *p;
-
-      register IndexPacket
-        *restrict indexes;
+      FILE
+        *file;
 
-      register PixelPacket
-        *restrict q;
+      int
+        unique_file;
 
-      p=node_info->list;
-      for (i=0; i < (long) node_info->number_unique; i++)
-      {
-        q=QueueAuthenticPixels(image,cube_info->x,0,1,1,exception);
-        if (q == (PixelPacket *) NULL)
-          continue;
-        indexes=GetAuthenticIndexQueue(image);
-        *q=p->pixel;
-        if (image->colorspace == CMYKColorspace)
-          *indexes=p->index;
-        if (SyncAuthenticPixels(image,exception) == MagickFalse)
-          break;
-        cube_info->x++;
-        p++;
-      }
-      if (image->progress_monitor != (MagickProgressMonitor) NULL)
+      /*
+        Add a unique colors as an image comment.
+      */
+      file=(FILE *) NULL;
+      unique_file=AcquireUniqueFileResource(filename);
+      if (unique_file != -1)
+        file=fdopen(unique_file,"wb");
+      if ((unique_file != -1) && (file != (FILE *) NULL))
         {
-          MagickBooleanType
-            proceed;
+          char
+            *property;
 
-          proceed=SetImageProgress(image,UniqueColorsImageTag,
-            cube_info->progress,cube_info->colors);
-          if (proceed == MagickFalse)
-            status=MagickFalse;
+          (void) GetNumberColors(image,file,&image->exception);
+          (void) fclose(file);
+          property=FileToString(filename,~0UL,&image->exception);
+          if (property != (char *) NULL)
+            {
+              (void) SetImageProperty(histogram_image,"comment",property);
+              property=DestroyString(property);
+            }
         }
-      cube_info->progress++;
-    }
-}
-
-MagickExport Image *UniqueImageColors(const Image *image,
-  ExceptionInfo *exception)
-{
-  CubeInfo
-    *cube_info;
-
-  Image
-    *unique_image;
-
-  cube_info=ClassifyImageColors(image,exception);
-  if (cube_info == (CubeInfo *) NULL)
-    return((Image *) NULL);
-  unique_image=CloneImage(image,cube_info->colors,1,MagickTrue,exception);
-  if (unique_image == (Image *) NULL)
-    return(unique_image);
-  if (SetImageStorageClass(unique_image,DirectClass) == MagickFalse)
-    {
-      InheritException(exception,&unique_image->exception);
-      unique_image=DestroyImage(unique_image);
-      return((Image *) NULL);
+      (void) RelinquishUniqueFileResource(filename);
     }
-  UniqueColorsToImage(unique_image,cube_info,cube_info->root,exception);
-  if (cube_info->colors < MaxColormapSize)
-    {
-      QuantizeInfo
-        *quantize_info;
-
-      quantize_info=AcquireQuantizeInfo((ImageInfo *) NULL);
-      quantize_info->number_colors=MaxColormapSize;
-      quantize_info->dither=MagickFalse;
-      quantize_info->tree_depth=8;
-      (void) QuantizeImage(quantize_info,unique_image);
-      quantize_info=DestroyQuantizeInfo(quantize_info);
-    }
-  cube_info=DestroyCubeInfo(image,cube_info);
-  return(unique_image);
+  /*
+    Write Histogram image.
+  */
+  (void) CopyMagickString(histogram_image->filename,image_info->filename,
+    MaxTextExtent);
+  write_info=CloneImageInfo(image_info);
+  (void) SetImageInfo(write_info,1,&image->exception);
+  if (LocaleCompare(write_info->magick,"HISTOGRAM") == 0)
+    (void) FormatLocaleString(histogram_image->filename,MaxTextExtent,
+      "miff:%s",write_info->filename);
+  status=WriteImage(write_info,histogram_image);
+  histogram_image=DestroyImage(histogram_image);
+  write_info=DestroyImageInfo(write_info);
+  return(status);
 }