From: cristy Date: Fri, 16 Apr 2010 18:18:34 +0000 (+0000) Subject: (no commit message) X-Git-Tag: 7.0.1-0~9636 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=787c09caeeb16217a92190b802477636aebd55bd;p=imagemagick --- diff --git a/coders/histogram.c b/coders/histogram.c index db2293f16..00e6c8302 100644 --- a/coders/histogram.c +++ b/coders/histogram.c @@ -3,19 +3,18 @@ % % % % % % -% 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 % @@ -41,263 +40,72 @@ Include declarations. */ #include "magick/studio.h" -#include "magick/cache-view.h" +#include "magick/property.h" +#include "magick/blob.h" +#include "magick/blob-private.h" +#include "magick/cache.h" +#include "magick/color.h" #include "magick/color-private.h" -#include "magick/enhance.h" +#include "magick/constitute.h" #include "magick/exception.h" #include "magick/exception-private.h" -#include "magick/hashmap.h" +#include "magick/geometry.h" #include "magick/histogram.h" -#include "magick/image.h" -#include "magick/list.h" +#include "magick/image-private.h" +#include "magick/magick.h" #include "magick/memory_.h" +#include "magick/monitor.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/resource_.h" +#include "magick/quantum-private.h" +#include "magick/static.h" #include "magick/statistic.h" #include "magick/string_.h" - -/* - Define declarations. -*/ -#define MaxTreeDepth 8 -#define NodesInAList 1536 - -/* - 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 "magick/module.h" /* 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 *); /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % -+ 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. +% unsigned long RegisterHISTOGRAMImage(void) % */ - -static inline unsigned long ColorToNodeId(const Image *image, - const MagickPixelPacket *pixel,unsigned long index) +ModuleExport unsigned long RegisterHISTOGRAMImage(void) { - 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) -{ -#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=ExplicitFormatType; + entry->description=ConstantString("Histogram of the image"); + entry->module=ConstantString("HISTOGRAM"); + (void) RegisterMagickInfo(entry); + return(MagickImageCoderSignature); } /* @@ -305,62 +113,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. -% -% The format of the DefineImageHistogram method is: -% -% DefineImageHistogram(const Image *image,NodeInfo *node_info, -% ColorPacket **unique_colors) -% -% A description of each parameter follows. -% -% o image: the image. +% UnregisterHISTOGRAMImage() removes format registrations made by the +% HISTOGRAM module from the list of supported formats. % -% 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. +% The format of the UnregisterHISTOGRAMImage method is: % -% 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"); } /* @@ -368,291 +137,80 @@ 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. -% -% The format of the DestroyCubeInfo method is: -% -% DestroyCubeInfo(const Image *image,CubeInfo *cube_info) -% -% 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)); -} - -/* -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% % -% % -% % -+ D e s t r o y C o l o r C u b e % -% % -% % -% % -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% 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. % -% DestroyColorCube() traverses the color cube tree and frees the list of -% unique colors. +% This method is strongly based on a similar one written by +% muquit@warm.semcor.com which in turn is based on ppmhistmap of netpbm. % -% The format of the DestroyColorCube method is: +% The format of the WriteHISTOGRAMImage 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); -} - -/* -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% % -% % -% % -+ 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); } - -/* -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% % -% % -% % -% 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) -{ - ColorPacket - *histogram; - CubeInfo - *cube_info; +static MagickBooleanType WriteHISTOGRAMImage(const ImageInfo *image_info, + Image *image) +{ +#define HistogramDensity "256x200" - *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; + ChannelType + channel; - *number_colors=cube_info->colors; - root=histogram; - DefineImageHistogram(image,cube_info->root,&root); - } - } - cube_info=DestroyCubeInfo(image,cube_info); - return(histogram); -} - -/* -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% % -% % -% % -+ 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; + char + filename[MaxTextExtent]; - if (cube_info->free_nodes == 0) - { - Nodes - *nodes; + const char + *option; - /* - 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); -} - -/* -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% % -% % -% % -% 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 + ExceptionInfo + *exception; - CacheView - *image_view; + Image + *histogram_image; - CubeInfo - *cube_info; + ImageInfo + *write_info; long y; + MagickBooleanType + status; + MagickPixelPacket - pixel, - target; + *histogram; - register const IndexPacket - *indexes; + MagickRealType + maximum, + scale; + + RectangleInfo + geometry; register const PixelPacket *p; @@ -660,664 +218,173 @@ MagickExport MagickBooleanType IsHistogramImage(const Image *image, register long x; - register NodeInfo - *node_info; + register PixelPacket + *q, + *r; - register long - i; - - unsigned long - id, - index, - level; + size_t + length; + /* + 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=(MagickPixelPacket *) AcquireQuantumMemory(length, + sizeof(*histogram)); + if (histogram == (MagickPixelPacket *) 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); + /* + Initialize histogram count arrays. + */ + channel=image_info->channel; + (void) ResetMagickMemory(histogram,0,length*sizeof(*histogram)); for (y=0; y < (long) image->rows; y++) { - p=GetCacheViewVirtualPixels(image_view,0,y,image->columns,1,exception); + p=GetVirtualPixels(image,0,y,image->columns,1,&image->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); - 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; - } + if ((channel & RedChannel) != 0) + histogram[ScaleQuantumToChar(GetRedPixelComponent(p))].red++; + if ((channel & GreenChannel) != 0) + histogram[ScaleQuantumToChar(GetGreenPixelComponent(p))].green++; + if ((channel & BlueChannel) != 0) + histogram[ScaleQuantumToChar(GetBluePixelComponent(p))].blue++; 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); -} - -/* -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% % -% % -% % -% 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 < (long) histogram_image->columns; x++) + { + if (((channel & RedChannel) != 0) && (maximum < histogram[x].red)) + maximum=histogram[x].red; + if (((channel & GreenChannel) != 0) && (maximum < histogram[x].green)) + maximum=histogram[x].green; + if (((channel & BlueChannel) != 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 < (long) 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 == (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++) + if ((channel & RedChannel) != 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=(long) ceil(histogram_image->rows-scale*histogram[x].red-0.5); + r=q+y; + for ( ; y < (long) 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; + r->red=(Quantum) QuantumRange; + 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); -} - -/* -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% % -% % -% % -% 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); -} - -/* -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% % -% % -% % -% 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 ((channel & GreenChannel) != 0) { - (void) ConcatenateMagickString(tuple,",",MaxTextExtent); - ConcatenateColorComponent(&pixel,OpacityChannel,X11Compliance,tuple); + y=(long) ceil(histogram_image->rows-scale*histogram[x].green-0.5); + r=q+y; + for ( ; y < (long) histogram_image->rows; y++) + { + r->green=(Quantum) QuantumRange; + 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 ((channel & BlueChannel) != 0) { - MagickBooleanType - proceed; - - proceed=SetImageProgress(image,HistogramImageTag,i,number_colors); - if (proceed == MagickFalse) - status=MagickFalse; + y=(long) ceil(histogram_image->rows-scale*histogram[x].blue-0.5); + r=q+y; + for ( ; y < (long) histogram_image->rows; y++) + { + r->blue=(Quantum) QuantumRange; + 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); -} - -/* -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% % -% % -% % -% 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=(MagickPixelPacket *) RelinquishMagickMemory(histogram); + option=GetImageOption(image_info,"histogram:unique-colors"); + if ((option != (const char *) NULL) && (IsMagickTrue(option) != MagickFalse)) { - register ColorPacket - *p; + FILE + *file; - register IndexPacket - *restrict indexes; + int + unique_file; - register PixelPacket - *restrict q; - - 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++; + (void) RelinquishUniqueFileResource(filename); } -} - -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); - } - 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) FormatMagickString(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); }