/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % CCCC OOO L OOO RRRR % % C O O L O O R R % % C O O L O O RRRR % % C O O L O O R R % % CCCC OOO LLLLL OOO R R % % % % % % ImageMagick Color Methods % % % % Software Design % % John Cristy % % July 1992 % % % % % % Copyright 1999-2007 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 % % obtain a copy of the License at % % % % http://www.imagemagick.org/script/license.php % % % % Unless required by applicable law or agreed to in writing, software % % distributed under the License is distributed on an "AS IS" BASIS, % % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % % See the License for the specific language governing permissions and % % limitations under the License. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % */ /* Include declarations. */ #include "magick/studio.h" #include "magick/blob.h" #include "magick/cache-view.h" #include "magick/color.h" #include "magick/color-private.h" #include "magick/client.h" #include "magick/configure.h" #include "magick/exception.h" #include "magick/exception-private.h" #include "magick/gem.h" #include "magick/geometry.h" #include "magick/image-private.h" #include "magick/memory_.h" #include "magick/monitor.h" #include "magick/option.h" #include "magick/pixel-private.h" #include "magick/quantize.h" #include "magick/quantum.h" #include "magick/semaphore.h" #include "magick/string_.h" #include "magick/token.h" #include "magick/utility.h" #include "magick/xml-tree.h" /* Define declarations. */ #define ColorFilename "colors.xml" #define MaxTreeDepth 8 #define NodesInAList 1536 /* Declare color map. */ static const char *ColorMap = (const char *) "" "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; /* 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; /* Static declarations. */ static LinkedListInfo *color_list = (LinkedListInfo *) NULL; static SemaphoreInfo *color_semaphore = (SemaphoreInfo *) NULL; static volatile MagickBooleanType instantiate_color = MagickFalse; /* Forward declarations. */ static CubeInfo *GetCubeInfo(void); static NodeInfo *GetNodeInfo(CubeInfo *,const unsigned long); static MagickBooleanType InitializeColorList(ExceptionInfo *), LoadColorLists(const char *,ExceptionInfo *); static void DestroyColorCube(const Image *,NodeInfo *); /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + C l a s s i f y I m a g e C o l o r s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ClassifyImageColors() builds a populated CubeInfo tree for the specified % image. The returned tree should be deallocated using DestroyCubeInfo() % once it is no longer needed. % % The format of the ClassifyImageColors() 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. % */ static inline unsigned long ColorToNodeId(const Image *image, const MagickPixelPacket *pixel,unsigned long index) { unsigned long id; id=(unsigned long) ( ((ScaleQuantumToChar(RoundToQuantum(pixel->red)) >> index) & 0x01) | ((ScaleQuantumToChar(RoundToQuantum(pixel->green)) >> index) & 0x01) << 1 | ((ScaleQuantumToChar(RoundToQuantum(pixel->blue)) >> index) & 0x01) << 2); if (image->matte != MagickFalse) id|=((ScaleQuantumToChar(RoundToQuantum(pixel->opacity)) >> index) & 0x01) << 3; return(id); } static CubeInfo *ClassifyImageColors(const Image *image, ExceptionInfo *exception) { #define EvaluateImageColorsText " Compute image colors... " CubeInfo *cube_info; long y; MagickBooleanType status; MagickPixelPacket pixel, target; NodeInfo *node_info; register const IndexPacket *indexes; register const PixelPacket *p; register long i, x; register unsigned long id, index, level; ViewInfo *image_view; /* 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=OpenCacheView(image); for (y=0; y < (long) image->rows; y++) { p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,exception); if (p == (const PixelPacket *) NULL) break; indexes=AcquireCacheViewIndexes(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++; } if ((image->progress_monitor != (MagickProgressMonitor) NULL) && (QuantumTick(y,image->rows) != MagickFalse)) { status=image->progress_monitor(EvaluateImageColorsText,y,image->rows, image->client_data); if (status == MagickFalse) break; } } image_view=CloseCacheView(image_view); return(cube_info); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + C o n c a t e n a t e C o l o r C o m p o n e n t % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ConcatenateColorComponent() returns the pixel as a canonical string. % % The format of the ConcatenateColorComponent() method is: % % void ConcatenateColorComponent(const MagickPixelPacket *pixel, % const ChannelType channel,const ComplianceType compliance,char *tuple) % % A description of each parameter follows. % % o pixel: The pixel. % % channel: The channel. % % o compliance: Adhere to this color standard: SVG, X11, or XPM. % % tuple: The color tuple. % */ MagickExport void ConcatenateColorComponent(const MagickPixelPacket *pixel, const ChannelType channel,const ComplianceType compliance,char *tuple) { char component[MaxTextExtent]; MagickRealType color; color=0.0; switch (channel) { case RedChannel: { color=pixel->red; break; } case GreenChannel: { color=pixel->green; break; } case BlueChannel: { color=pixel->blue; break; } case OpacityChannel: { color=pixel->opacity; break; } case IndexChannel: { color=pixel->index; break; } default: break; } if (compliance != SVGCompliance) { if (pixel->depth > 16) { (void) FormatMagickString(component,MaxTextExtent,"%10lu", (unsigned long) ScaleQuantumToLong(RoundToQuantum(color))); (void) ConcatenateMagickString(tuple,component,MaxTextExtent); return; } if (pixel->depth > 8) { (void) FormatMagickString(component,MaxTextExtent,"%5d", ScaleQuantumToShort(RoundToQuantum(color))); (void) ConcatenateMagickString(tuple,component,MaxTextExtent); return; } (void) FormatMagickString(component,MaxTextExtent,"%3d", ScaleQuantumToChar(RoundToQuantum(color))); (void) ConcatenateMagickString(tuple,component,MaxTextExtent); return; } if (channel == OpacityChannel) { (void) FormatMagickString(component,MaxTextExtent,"%g", (double) (QuantumScale*color)); (void) ConcatenateMagickString(tuple,component,MaxTextExtent); return; } if (pixel->depth > 8) { (void) FormatMagickString(component,MaxTextExtent,"%g%%", (double) (100.0*QuantumScale*color)); (void) ConcatenateMagickString(tuple,component,MaxTextExtent); return; } (void) FormatMagickString(component,MaxTextExtent,"%d", ScaleQuantumToChar(RoundToQuantum(color))); (void) ConcatenateMagickString(tuple,component,MaxTextExtent); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + D e f i n e I m a g e H i s t o g r a m % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % 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. % % 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. % */ static void DefineImageHistogram(const Image *image,NodeInfo *node_info, ColorPacket **histogram) { 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++; } } } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + D e s t r o y C o l o r L i s t % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % DestroyColorList() deallocates memory associated with the color list. % % The format of the DestroyColorList method is: % % DestroyColorList(void) % */ static void *DestroyColorElement(void *color_info) { register ColorInfo *p; p=(ColorInfo *) color_info; if (p->path != (char *) NULL) p->path=DestroyString(p->path); if (p->name != (char *) NULL) p->name=DestroyString(p->name); p=(ColorInfo *) RelinquishMagickMemory(p); return((void *) NULL); } MagickExport void DestroyColorList(void) { AcquireSemaphoreInfo(&color_semaphore); if (color_list != (LinkedListInfo *) NULL) color_list=DestroyLinkedList(color_list,DestroyColorElement); instantiate_color=MagickFalse; RelinquishSemaphoreInfo(color_semaphore); color_semaphore=DestroySemaphoreInfo(color_semaphore); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + D e s t r o y C u b e I n f o % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % 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 % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % 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) % % 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. % */ 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 o l o r I n f o % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetColorInfo() searches the color list for the specified name and if found % returns attributes for that color. % % The format of the GetColorInfo method is: % % const PixelPacket *GetColorInfo(const char *name, % ExceptionInfo *exception) % % A description of each parameter follows: % % o color_info: search the color list for the specified name and if found % return attributes for that color. % % o name: The color name. % % o exception: Return any errors or warnings in this structure. % */ MagickExport const ColorInfo *GetColorInfo(const char *name, ExceptionInfo *exception) { char colorname[MaxTextExtent]; register const ColorInfo *p; register char *q; assert(exception != (ExceptionInfo *) NULL); if ((color_list == (LinkedListInfo *) NULL) || (instantiate_color == MagickFalse)) if (InitializeColorList(exception) == MagickFalse) return((const ColorInfo *) NULL); if ((color_list == (LinkedListInfo *) NULL) || (IsLinkedListEmpty(color_list) != MagickFalse)) return((const ColorInfo *) NULL); if ((name == (const char *) NULL) || (LocaleCompare(name,"*") == 0)) return((const ColorInfo *) GetValueFromLinkedList(color_list,0)); /* Strip names of whitespace. */ (void) CopyMagickString(colorname,name,MaxTextExtent); for (q=colorname; *q != '\0'; q++) { if (isspace((int) ((unsigned char) *q)) == 0) continue; (void) CopyMagickString(q,q+1,MaxTextExtent); q--; } /* Search for named color. */ AcquireSemaphoreInfo(&color_semaphore); ResetLinkedListIterator(color_list); p=(const ColorInfo *) GetNextValueInLinkedList(color_list); while (p != (const ColorInfo *) NULL) { if (LocaleCompare(colorname,p->name) == 0) break; p=(const ColorInfo *) GetNextValueInLinkedList(color_list); } if (p == (ColorInfo *) NULL) (void) ThrowMagickException(exception,GetMagickModule(),OptionWarning, "UnrecognizedColor","`%s'",name); RelinquishSemaphoreInfo(color_semaphore); return(p); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t C o l o r I n f o L i s t % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetColorInfoList() returns any colors that match the specified pattern. % % The format of the GetColorInfoList function is: % % const ColorInfo **GetColorInfoList(const char *pattern, % unsigned long *number_colors,ExceptionInfo *exception) % % A description of each parameter follows: % % o pattern: Specifies a pointer to a text string containing a pattern. % % o number_colors: This integer returns the number of colors in the list. % % o exception: Return any errors or warnings in this structure. % */ #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif static int ColorInfoCompare(const void *x,const void *y) { const ColorInfo **p, **q; p=(const ColorInfo **) x, q=(const ColorInfo **) y; if (LocaleCompare((*p)->path,(*q)->path) == 0) return(LocaleCompare((*p)->name,(*q)->name)); return(LocaleCompare((*p)->path,(*q)->path)); } #if defined(__cplusplus) || defined(c_plusplus) } #endif MagickExport const ColorInfo **GetColorInfoList(const char *pattern, unsigned long *number_colors,ExceptionInfo *exception) { const ColorInfo **colors; register const ColorInfo *p; register long i; /* Allocate color list. */ assert(pattern != (char *) NULL); (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern); assert(number_colors != (unsigned long *) NULL); *number_colors=0; p=GetColorInfo("*",exception); if (p == (const ColorInfo *) NULL) return((const ColorInfo **) NULL); colors=(const ColorInfo **) AcquireQuantumMemory((size_t) GetNumberOfElementsInLinkedList(color_list)+1UL,sizeof(*colors)); if (colors == (const ColorInfo **) NULL) return((const ColorInfo **) NULL); /* Generate color list. */ AcquireSemaphoreInfo(&color_semaphore); ResetLinkedListIterator(color_list); p=(const ColorInfo *) GetNextValueInLinkedList(color_list); for (i=0; p != (const ColorInfo *) NULL; ) { if ((p->stealth == MagickFalse) && (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse)) colors[i++]=p; p=(const ColorInfo *) GetNextValueInLinkedList(color_list); } RelinquishSemaphoreInfo(color_semaphore); qsort((void *) colors,(size_t) i,sizeof(*colors),ColorInfoCompare); colors[i]=(ColorInfo *) NULL; *number_colors=(unsigned long) i; return(colors); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t C o l o r L i s t % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetColorList() returns any colors that match the specified pattern. % % The format of the GetColorList function is: % % char **GetColorList(const char *pattern,unsigned long *number_colors, % ExceptionInfo *exception) % % A description of each parameter follows: % % o pattern: Specifies a pointer to a text string containing a pattern. % % o number_colors: This integer returns the number of colors in the list. % % o exception: Return any errors or warnings in this structure. % */ #if defined(__cplusplus) || defined(c_plusplus) extern "C" { #endif static int ColorCompare(const void *x,const void *y) { register const char **p, **q; p=(const char **) x; q=(const char **) y; return(LocaleCompare(*p,*q)); } #if defined(__cplusplus) || defined(c_plusplus) } #endif MagickExport char **GetColorList(const char *pattern, unsigned long *number_colors,ExceptionInfo *exception) { char **colors; register const ColorInfo *p; register long i; /* Allocate color list. */ assert(pattern != (char *) NULL); (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",pattern); assert(number_colors != (unsigned long *) NULL); *number_colors=0; p=GetColorInfo("*",exception); if (p == (const ColorInfo *) NULL) return((char **) NULL); colors=(char **) AcquireQuantumMemory((size_t) GetNumberOfElementsInLinkedList(color_list)+1UL,sizeof(*colors)); if (colors == (char **) NULL) return((char **) NULL); /* Generate color list. */ AcquireSemaphoreInfo(&color_semaphore); ResetLinkedListIterator(color_list); p=(const ColorInfo *) GetNextValueInLinkedList(color_list); for (i=0; p != (const ColorInfo *) NULL; ) { if ((p->stealth == MagickFalse) && (GlobExpression(p->name,pattern,MagickFalse) != MagickFalse)) colors[i++]=ConstantString(p->name); p=(const ColorInfo *) GetNextValueInLinkedList(color_list); } RelinquishSemaphoreInfo(color_semaphore); qsort((void *) colors,(size_t) i,sizeof(*colors),ColorCompare); colors[i]=(char *) NULL; *number_colors=(unsigned long) i; return(colors); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + G e t C o l o r T u p l e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetColorTuple() returns a color as a color tuple string (e.g. rgba(255,0,0)) % or hex string (e.g. #FF0000). % % The format of the GetColorTuple method is: % % GetColorTuple(const MagickPixelPacket *pixel,const MagickBooleanType hex, % char *tuple) % % A description of each parameter follows. % % o pixel: The pixel. % % o hex: A value other than zero returns the tuple in a hexidecimal format. % % o tuple: Return the color tuple as this string. % */ static void ConcatentateHexColorComponent(const MagickPixelPacket *pixel, const ChannelType channel,char *tuple) { char component[MaxTextExtent]; MagickRealType color; color=0.0; switch (channel) { case RedChannel: { color=pixel->red; break; } case GreenChannel: { color=pixel->green; break; } case BlueChannel: { color=pixel->blue; break; } case OpacityChannel: { color=(MagickRealType) QuantumRange-pixel->opacity; break; } case IndexChannel: { color=pixel->index; break; } default: break; } if (pixel->depth > 32) { (void) FormatMagickString(component,MaxTextExtent,"%08lX", ScaleQuantumToLong(RoundToQuantum(color))); (void) ConcatenateMagickString(tuple,component,MaxTextExtent); return; } if (pixel->depth > 16) { (void) FormatMagickString(component,MaxTextExtent,"%08X", (unsigned int) ScaleQuantumToLong(RoundToQuantum(color))); (void) ConcatenateMagickString(tuple,component,MaxTextExtent); return; } if (pixel->depth > 8) { (void) FormatMagickString(component,MaxTextExtent,"%04X", ScaleQuantumToShort(RoundToQuantum(color))); (void) ConcatenateMagickString(tuple,component,MaxTextExtent); return; } (void) FormatMagickString(component,MaxTextExtent,"%02X", ScaleQuantumToChar(RoundToQuantum(color))); (void) ConcatenateMagickString(tuple,component,MaxTextExtent); return; } MagickExport void GetColorTuple(const MagickPixelPacket *pixel, const MagickBooleanType hex,char *tuple) { MagickPixelPacket color; assert(pixel != (const MagickPixelPacket *) NULL); assert(tuple != (char *) NULL); (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",tuple); *tuple='\0'; if (hex != MagickFalse) { /* Convert pixel to hex color. */ (void) ConcatenateMagickString(tuple,"#",MaxTextExtent); ConcatentateHexColorComponent(pixel,RedChannel,tuple); ConcatentateHexColorComponent(pixel,GreenChannel,tuple); ConcatentateHexColorComponent(pixel,BlueChannel,tuple); if (pixel->colorspace == CMYKColorspace) ConcatentateHexColorComponent(pixel,IndexChannel,tuple); if ((pixel->matte != MagickFalse) && (pixel->opacity != OpaqueOpacity)) ConcatentateHexColorComponent(pixel,OpacityChannel,tuple); return; } /* Convert pixel to rgb() or cmyk() color. */ color=(*pixel); if (color.depth > 8) { #define SVGCompliant(component) ((MagickRealType) \ ScaleCharToQuantum(ScaleQuantumToChar(RoundToQuantum(component)))); MagickStatusType status; /* SVG requires color depths > 8 expressed as percentages. */ status=color.red == SVGCompliant(color.red); status&=color.green == SVGCompliant(color.green); status&=color.blue == SVGCompliant(color.blue); if (color.colorspace != CMYKColorspace) status&=color.index == SVGCompliant(color.index); if (color.matte != MagickFalse) status&=color.opacity == SVGCompliant(color.opacity); if (status != MagickFalse) color.depth=8; } (void) ConcatenateMagickString(tuple,MagickOptionToMnemonic( MagickColorspaceOptions,(long) color.colorspace),MaxTextExtent); if ((color.matte != MagickFalse) && (color.opacity != OpaqueOpacity)) (void) ConcatenateMagickString(tuple,"a",MaxTextExtent); (void) ConcatenateMagickString(tuple,"(",MaxTextExtent); ConcatenateColorComponent(&color,RedChannel,SVGCompliance,tuple); (void) ConcatenateMagickString(tuple,",",MaxTextExtent); ConcatenateColorComponent(&color,GreenChannel,SVGCompliance,tuple); (void) ConcatenateMagickString(tuple,",",MaxTextExtent); ConcatenateColorComponent(&color,BlueChannel,SVGCompliance,tuple); if (color.colorspace == CMYKColorspace) { (void) ConcatenateMagickString(tuple,",",MaxTextExtent); ConcatenateColorComponent(&color,IndexChannel,SVGCompliance,tuple); } if ((color.matte != MagickFalse) && (color.opacity != OpaqueOpacity)) { (void) ConcatenateMagickString(tuple,",",MaxTextExtent); ConcatenateColorComponent(&color,OpacityChannel,SVGCompliance,tuple); } (void) ConcatenateMagickString(tuple,")",MaxTextExtent); LocaleLower(tuple); return; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + 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) { CubeInfo *cube_info; /* Initialize tree to describe color cube. */ cube_info=(CubeInfo *) AcquireMagickMemory(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); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % 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; *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; *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; if (cube_info->free_nodes == 0) { Nodes *nodes; /* Allocate a new nodes of nodes. */ nodes=(Nodes *) AcquireMagickMemory(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); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % 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->count != color_1->count) return((int) color_2->count-(int) color_1->count); 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); return((int) color_1->pixel.blue-(int) color_2->pixel.blue); } #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; 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) { (void) ConcatenateMagickString(tuple,",",MaxTextExtent); ConcatenateColorComponent(&pixel,OpacityChannel,X11Compliance, tuple); } (void) ConcatenateMagickString(tuple,")",MaxTextExtent); (void) QueryMagickColorname(image,&pixel,SVGCompliance,MagickFalse, color,exception); (void) QueryMagickColorname(image,&pixel,SVGCompliance,MagickTrue,hex, exception); (void) fprintf(file,MagickSizeFormat,p->count); (void) fprintf(file,": %s %s %s\n",tuple,hex,color); if ((image->progress_monitor != (MagickProgressMonitor) NULL) && (QuantumTick(i,number_colors) != MagickFalse)) (void) image->progress_monitor(HistogramImageTag,i,number_colors, image->client_data); p++; } (void) fflush(file); histogram=(ColorPacket *) RelinquishMagickMemory(histogram); return(number_colors); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + I n i t i a l i z e C o l o r L i s t % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % InitializeColorList() initializes the color list. % % The format of the InitializeColorList method is: % % MagickBooleanType InitializeColorList(ExceptionInfo *exception) % % A description of each parameter follows. % % o exception: Return any errors or warnings in this structure. % */ static MagickBooleanType InitializeColorList(ExceptionInfo *exception) { if ((color_list == (LinkedListInfo *) NULL) && (instantiate_color == MagickFalse)) { AcquireSemaphoreInfo(&color_semaphore); if ((color_list == (LinkedListInfo *) NULL) && (instantiate_color == MagickFalse)) { (void) LoadColorLists(ColorFilename,exception); instantiate_color=MagickTrue; } RelinquishSemaphoreInfo(color_semaphore); } return(color_list != (LinkedListInfo *) NULL ? MagickTrue : MagickFalse); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + I s C o l o r S i m i l a r % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % IsColorSimilar() returns MagickTrue if the distance between two colors is % less than the specified distance in a linear three dimensional color space. % This method is used by ColorFloodFill() and other algorithms which % compare two colors. % % The format of the IsColorSimilar method is: % % void IsColorSimilar(const Image *image,const PixelPacket *p, % const PixelPacket *q) % % A description of each parameter follows: % % o image: The image. % % o p: Pixel p. % % o q: Pixel q. % */ static inline double MagickMax(const double x,const double y) { if (x > y) return(x); return(y); } MagickExport MagickBooleanType IsColorSimilar(const Image *image, const PixelPacket *p,const PixelPacket *q) { MagickRealType fuzz, pixel; register MagickRealType alpha, beta, distance; if ((image->fuzz == 0.0) && (image->matte == MagickFalse)) return(IsColorEqual(p,q)); fuzz=3.0*MagickMax(image->fuzz,MagickSQ1_2)*MagickMax(image->fuzz, MagickSQ1_2); alpha=1.0; beta=1.0; if (image->matte != MagickFalse) { alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity)); beta=(MagickRealType) (QuantumScale*(QuantumRange-q->opacity)); } pixel=alpha*p->red-beta*q->red; distance=pixel*pixel; if (distance > fuzz) return(MagickFalse); pixel=alpha*p->green-beta*q->green; distance+=pixel*pixel; if (distance > fuzz) return(MagickFalse); pixel=alpha*p->blue-beta*q->blue; distance+=pixel*pixel; if (distance > fuzz) return(MagickFalse); return(MagickTrue); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % I s G r a y I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % IsGrayImage() returns MagickTrue if all the pixels in the image have the same % red, green, and blue intensities. % % The format of the IsGrayImage method is: % % MagickBooleanType IsGrayImage(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 IsGrayImage(const Image *image, ExceptionInfo *exception) { long y; register const PixelPacket *p; register long i, x; assert(image != (Image *) NULL); assert(image->signature == MagickSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); if ((image->colorspace != RGBColorspace) && (image->colorspace != GRAYColorspace)) return(MagickFalse); switch (image->storage_class) { case DirectClass: case UndefinedClass: { ViewInfo *image_view; image_view=OpenCacheView(image); for (y=0; y < (long) image->rows; y++) { p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,exception); if (p == (const PixelPacket *) NULL) break; for (x=0; x < (long) image->columns; x++) { if (p->red != p->green) break; if (p->green != p->blue) break; p++; } if (x < (long) image->columns) break; } image_view=CloseCacheView(image_view); return(y < (long) image->rows ? MagickFalse : MagickTrue); } case PseudoClass: { p=image->colormap; for (i=0; i < (long) image->colors; i++) { if (p->red != p->green) break; if (p->green != p->blue) break; p++; } return(i < (long) image->colors ? MagickFalse : MagickTrue); } } return(MagickTrue); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % 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) { 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; ViewInfo *image_view; 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); /* Initialize color description tree. */ 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=OpenCacheView(image); for (y=0; y < (long) image->rows; y++) { p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,exception); if (p == (const PixelPacket *) NULL) break; indexes=AcquireCacheViewIndexes(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 > 1024) break; } p++; } if (x < (long) image->columns) break; } image_view=CloseCacheView(image_view); cube_info=DestroyCubeInfo(image,cube_info); return(y < (long) image->rows ? MagickFalse : MagickTrue); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + I s I m a g e S i m i l a r % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % IsImageSimilar() returns true if the target is similar to a region of the % image. % % The format of the IsImageSimilar method is: % % MagickBooleanType IsImageSimilar(const Image *image, % const Image *target_image,long *x_offset,long *y_offset, % ExceptionInfo *exception) % % A description of each parameter follows: % % o image: The image. % % o target_image: The target image. % % o x_offset: On input the starting x position to search for a match; % on output the x position of the first match found. % % o y_offset: On input the starting y position to search for a match; % on output the y position of the first match found. % % o exception: Return any errors or warnings in this structure. % */ MagickExport MagickBooleanType IsImageSimilar(const Image *image, const Image *target_image,long *x_offset,long *y_offset, ExceptionInfo *exception) { #define SearchImageText " Searching image... " long j, y; MagickBooleanType status; MagickPixelPacket target, pixel; register const PixelPacket *p, *q; register const IndexPacket *indexes, *target_indexes; register long i, x; ViewInfo *image_view, *target_view; assert(image != (Image *) NULL); assert(image->signature == MagickSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); assert(target_image != (Image *) NULL); assert(target_image->signature == MagickSignature); assert(x_offset != (long *) NULL); assert(y_offset != (long *) NULL); assert(exception != (ExceptionInfo *) NULL); x=0; GetMagickPixelPacket(image,&pixel); GetMagickPixelPacket(image,&target); image_view=OpenCacheView(image); target_view=OpenCacheView(target_image); for (y=(*y_offset); y < (long) image->rows; y++) { for (x=y == 0 ? *x_offset : 0; x < (long) image->columns; x++) { for (j=0; j < (long) target_image->rows; j++) { for (i=0; i < (long) target_image->columns; i++) { p=AcquireCacheViewPixels(image_view,x+i,y+j,1,1,exception); indexes=AcquireCacheViewIndexes(image_view); SetMagickPixelPacket(image,p,indexes,&pixel); q=AcquireCacheViewPixels(target_view,i,j,1,1,exception); target_indexes=AcquireCacheViewIndexes(target_view); SetMagickPixelPacket(image,q,target_indexes,&target); if (IsMagickColorSimilar(&pixel,&target) == MagickFalse) break; } if (i < (long) target_image->columns) break; } if (j == (long) target_image->rows) break; } if (x < (long) image->columns) break; if ((image->progress_monitor != (MagickProgressMonitor) NULL) && (QuantumTick(y,image->rows) != MagickFalse)) { status=image->progress_monitor(SearchImageText,y,image->rows, image->client_data); if (status == MagickFalse) break; } } target_view=CloseCacheView(target_view); image_view=CloseCacheView(image_view); *x_offset=x; *y_offset=y; return(y < (long) image->rows ? MagickTrue : MagickFalse); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + I s M a g i c k C o l o r S i m i l a r % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % IsMagickColorSimilar() returns true if the distance between two colors is % less than the specified distance in a linear three dimensional color space. % This method is used by ColorFloodFill() and other algorithms which % compare two colors. % % The format of the IsMagickColorSimilar method is: % % MagickBooleanType IsMagickColorSimilar(const MagickPixelPacket *p, % const MagickPixelPacket *q) % % A description of each parameter follows: % % o p: Pixel p. % % o q: Pixel q. % */ MagickExport MagickBooleanType IsMagickColorSimilar(const MagickPixelPacket *p, const MagickPixelPacket *q) { MagickRealType fuzz, pixel; register MagickRealType alpha, beta, distance; if ((p->fuzz == 0.0) && (q->fuzz == 0.0)) return(IsMagickColorEqual(p,q)); if (p->fuzz == 0.0) fuzz=3.0*MagickMax(q->fuzz,MagickSQ1_2)*MagickMax(q->fuzz,MagickSQ1_2); else if (q->fuzz == 0.0) fuzz=3.0*MagickMax(p->fuzz,MagickSQ1_2)*MagickMax(p->fuzz,MagickSQ1_2); else fuzz=3.0*MagickMax(p->fuzz,MagickSQ1_2)*MagickMax(q->fuzz,MagickSQ1_2); if (p->colorspace == CMYKColorspace) { if (p->fuzz == 0.0) fuzz+=MagickMax(q->fuzz,MagickSQ1_2)*MagickMax(q->fuzz,MagickSQ1_2); else if (q->fuzz == 0.0) fuzz+=MagickMax(p->fuzz,MagickSQ1_2)*MagickMax(p->fuzz,MagickSQ1_2); else fuzz+=MagickMax(p->fuzz,MagickSQ1_2)*MagickMax(q->fuzz,MagickSQ1_2); } alpha=1.0; if (p->matte != MagickFalse) alpha=(MagickRealType) (QuantumScale*(QuantumRange-p->opacity)); beta=1.0; if (q->matte != MagickFalse) beta=(MagickRealType) (QuantumScale*(QuantumRange-q->opacity)); pixel=alpha*p->red-beta*q->red; distance=pixel*pixel; if (distance > fuzz) return(MagickFalse); pixel=alpha*p->green-beta*q->green; distance+=pixel*pixel; if (distance > fuzz) return(MagickFalse); pixel=alpha*p->blue-beta*q->blue; distance+=pixel*pixel; if (distance > fuzz) return(MagickFalse); if (p->colorspace == CMYKColorspace) { pixel=alpha*p->index-beta*q->index; distance+=pixel*pixel; if (distance > fuzz) return(MagickFalse); } return(MagickTrue); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % I s M o n o c h r o m e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % IsMonochromeImage() returns MagickTrue if all the pixels in the image have % the same red, green, and blue intensities and the intensity is either % 0 or QuantumRange. % % The format of the IsMonochromeImage method is: % % MagickBooleanType IsMonochromeImage(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 IsMonochromeImage(const Image *image, ExceptionInfo *exception) { long y; register const PixelPacket *p; register long x; ViewInfo *image_view; assert(image != (Image *) NULL); assert(image->signature == MagickSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); if ((image->colorspace != RGBColorspace) && (image->colorspace != GRAYColorspace)) return(MagickFalse); image_view=OpenCacheView(image); for (y=0; y < (long) image->rows; y++) { p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,exception); if (p == (const PixelPacket *) NULL) break; for (x=0; x < (long) image->columns; x++) { if ((p->red != p->green) || (p->green != p->blue) || ((p->red != 0) && (p->red != (Quantum) QuantumRange))) break; if ((p->opacity != (Quantum) OpaqueOpacity) && (p->opacity != (Quantum) TransparentOpacity)) break; p++; } if (x < (long) image->columns) break; } image_view=CloseCacheView(image_view); return(y < (long) image->rows ? MagickFalse : MagickTrue); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + I s O p a c i t y S i m i l a r % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % IsOpacitySimilar() returns true if the distance between two opacity % values is less than the specified distance in a linear color space. This % method is used by MatteFloodFill() and other algorithms which compare % two opacity values. % % The format of the IsOpacitySimilar method is: % % void IsOpacitySimilar(const Image *image,const PixelPacket *p, % const PixelPacket *q) % % A description of each parameter follows: % % o image: The image. % % o p: Pixel p. % % o q: Pixel q. % */ MagickExport MagickBooleanType IsOpacitySimilar(const Image *image, const PixelPacket *p,const PixelPacket *q) { MagickRealType fuzz, pixel; register MagickRealType distance; if (image->matte == MagickFalse) return(MagickTrue); if (p->opacity == q->opacity) return(MagickTrue); fuzz=MagickMax(image->fuzz,MagickSQ1_2)*MagickMax(image->fuzz,MagickSQ1_2); pixel=(MagickRealType) p->opacity-(MagickRealType) q->opacity; distance=pixel*pixel; if (distance > fuzz) return(MagickFalse); return(MagickTrue); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % I s O p a q u e I m a g e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % IsOpaqueImage() returns MagickTrue if none of the pixels in the image have % an opacity value other than opaque (0). % % The format of the IsOpaqueImage method is: % % MagickBooleanType IsOpaqueImage(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 IsOpaqueImage(const Image *image, ExceptionInfo *exception) { long y; register const PixelPacket *p; register long x; ViewInfo *image_view; /* Determine if image is opaque. */ assert(image != (Image *) NULL); assert(image->signature == MagickSignature); if (image->debug != MagickFalse) (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename); if (image->matte == MagickFalse) return(MagickTrue); image_view=OpenCacheView(image); for (y=0; y < (long) image->rows; y++) { p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,exception); if (p == (const PixelPacket *) NULL) break; for (x=0; x < (long) image->columns; x++) { if (p->opacity != OpaqueOpacity) break; p++; } if (x < (long) image->columns) break; } image_view=CloseCacheView(image_view); 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) { 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; ViewInfo *image_view; 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); /* Initialize color description tree. */ 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=OpenCacheView(image); for (y=0; y < (long) image->rows; y++) { p=AcquireCacheViewPixels(image_view,0,y,image->columns,1,exception); if (p == (const PixelPacket *) NULL) break; indexes=AcquireCacheViewIndexes(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 > 256) break; } p++; } if (x < (long) image->columns) break; } image_view=CloseCacheView(image_view); cube_info=DestroyCubeInfo(image,cube_info); return(y < (long) image->rows ? MagickFalse : MagickTrue); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % L i s t C o l o r I n f o % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ListColorInfo() lists color names to the specified file. Color names % are a convenience. Rather than defining a color by its red, green, and % blue intensities just use a color name such as white, blue, or yellow. % % The format of the ListColorInfo method is: % % MagickBooleanType ListColorInfo(FILE *file,ExceptionInfo *exception) % % A description of each parameter follows. % % o file: List color names to this file handle. % % o exception: Return any errors or warnings in this structure. % */ MagickExport MagickBooleanType ListColorInfo(FILE *file, ExceptionInfo *exception) { char tuple[MaxTextExtent]; const char *path; const ColorInfo **color_info; register long i; unsigned long number_colors; /* List name and attributes of each color in the list. */ if (file == (const FILE *) NULL) file=stdout; color_info=GetColorInfoList("*",&number_colors,exception); if (color_info == (const ColorInfo **) NULL) return(MagickFalse); path=(const char *) NULL; for (i=0; i < (long) number_colors; i++) { if (color_info[i]->stealth != MagickFalse) continue; if ((path == (const char *) NULL) || (LocaleCompare(path,color_info[i]->path) != 0)) { if (color_info[i]->path != (char *) NULL) (void) fprintf(file,"\nPath: %s\n\n",color_info[i]->path); (void) fprintf(file,"Name Color " " Compliance\n"); (void) fprintf(file,"-------------------------------------------------" "------------------------------\n"); } path=color_info[i]->path; (void) fprintf(file,"%-21.21s ",color_info[i]->name); GetColorTuple(&color_info[i]->color,MagickFalse,tuple); (void) fprintf(file,"%-45.45s ",tuple); if ((color_info[i]->compliance & SVGCompliance) != 0) (void) fprintf(file,"SVG "); if ((color_info[i]->compliance & X11Compliance) != 0) (void) fprintf(file,"X11 "); if ((color_info[i]->compliance & XPMCompliance) != 0) (void) fprintf(file,"XPM "); (void) fprintf(file,"\n"); } color_info=(const ColorInfo **) RelinquishMagickMemory((void *) color_info); (void) fflush(file); return(MagickTrue); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + L o a d C o l o r L i s t % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % LoadColorList() loads the color configuration file which provides a mapping % between color attributes and a color name. % % The format of the LoadColorList method is: % % MagickBooleanType LoadColorList(const char *xml,const char *filename, % const unsigned long depth,ExceptionInfo *exception) % % A description of each parameter follows: % % o xml: The color list in XML format. % % o filename: The color list filename. % % o depth: depth of statements. % % o exception: Return any errors or warnings in this structure. % */ static MagickBooleanType LoadColorList(const char *xml,const char *filename, const unsigned long depth,ExceptionInfo *exception) { const char *attribute; ColorInfo *color_info = (ColorInfo *) NULL; MagickBooleanType status; XMLTreeInfo *color, *color_map, *include; /* Load the color map file. */ (void) LogMagickEvent(ConfigureEvent,GetMagickModule(), "Loading color map \"%s\" ...",filename); if (xml == (const char *) NULL) return(MagickFalse); if (color_list == (LinkedListInfo *) NULL) { color_list=NewLinkedList(0); if (color_list == (LinkedListInfo *) NULL) { ThrowFileException(exception,ResourceLimitError, "MemoryAllocationFailed",filename); return(MagickFalse); } } color_map=NewXMLTree(xml,exception); if (color_map == (XMLTreeInfo *) NULL) return(MagickFalse); status=MagickTrue; include=GetXMLTreeChild(color_map,"include"); while (include != (XMLTreeInfo *) NULL) { /* Process include element. */ attribute=GetXMLTreeAttribute(include,"file"); if (attribute != (const char *) NULL) { if (depth > 200) (void) ThrowMagickException(exception,GetMagickModule(), ConfigureError,"IncludeElementNestedTooDeeply","`%s'",filename); else { char path[MaxTextExtent], *xml; GetPathComponent(filename,HeadPath,path); if (*path != '\0') (void) ConcatenateMagickString(path,DirectorySeparator, MaxTextExtent); (void) ConcatenateMagickString(path,attribute,MaxTextExtent); xml=FileToString(path,~0,exception); if (xml == (char *) NULL) { status=LoadColorList(xml,path,depth+1,exception); xml=DestroyString(xml); } } } include=GetNextXMLTreeTag(include); } color=GetXMLTreeChild(color_map,"color"); while (color != (XMLTreeInfo *) NULL) { const char *attribute; /* Process color element. */ color_info=(ColorInfo *) AcquireMagickMemory(sizeof(*color_info)); if (color_info == (ColorInfo *) NULL) ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); (void) ResetMagickMemory(color_info,0,sizeof(*color_info)); GetMagickPixelPacket((const Image *) NULL,&color_info->color); color_info->path=ConstantString(filename); color_info->signature=MagickSignature; attribute=GetXMLTreeAttribute(color,"name"); if (attribute != (const char *) NULL) color_info->name=ConstantString(attribute); attribute=GetXMLTreeAttribute(color,"color"); if (attribute != (const char *) NULL) (void) QueryMagickColor(attribute,&color_info->color,exception); attribute=GetXMLTreeAttribute(color,"compliance"); if (attribute != (const char *) NULL) { long compliance; compliance=color_info->compliance; if (GlobExpression(attribute,"*SVG*",MagickTrue) != MagickFalse) compliance|=SVGCompliance; if (GlobExpression(attribute,"*X11*",MagickTrue) != MagickFalse) compliance|=X11Compliance; if (GlobExpression(attribute,"*XPM*",MagickTrue) != MagickFalse) compliance|=XPMCompliance; color_info->compliance=(ComplianceType) compliance; } attribute=GetXMLTreeAttribute(color,"stealth"); if (attribute != (const char *) NULL) color_info->stealth=IsMagickTrue(attribute); status=AppendValueToLinkedList(color_list,color_info); if (status == MagickFalse) (void) ThrowMagickException(exception,GetMagickModule(), ResourceLimitError,"MemoryAllocationFailed","`%s'",filename); color=GetNextXMLTreeTag(color); } color_map=DestroyXMLTree(color_map); return(status); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % L o a d C o l o r L i s t s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % LoadColorList() loads one or more color configuration file which provides a % mapping between color attributes and a color name. % % The format of the LoadColorLists method is: % % MagickBooleanType LoadColorLists(const char *filename, % ExceptionInfo *exception) % % A description of each parameter follows: % % o filename: The font file name. % % o exception: Return any errors or warnings in this structure. % */ static MagickBooleanType LoadColorLists(const char *filename, ExceptionInfo *exception) { #if defined(UseEmbeddableMagick) return(LoadColorList(ColorMap,"built-in",0,exception)); #else const StringInfo *option; LinkedListInfo *options; MagickStatusType status; status=MagickFalse; options=GetConfigureOptions(filename,exception); option=(const StringInfo *) GetNextValueInLinkedList(options); while (option != (const StringInfo *) NULL) { status|=LoadColorList((const char *) GetStringInfoDatum(option), GetStringInfoPath(option),0,exception); option=(const StringInfo *) GetNextValueInLinkedList(options); } options=DestroyConfigureOptions(options); if ((color_list == (LinkedListInfo *) NULL) || (IsLinkedListEmpty(color_list) != MagickFalse)) status|=LoadColorList(ColorMap,"built-in",0,exception); return(status != 0 ? MagickTrue : MagickFalse); #endif } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % Q u e r y C o l o r D a t a b a s e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % QueryColorDatabase() returns the red, green, blue, and opacity intensities % for a given color name. % % The format of the QueryColorDatabase method is: % % MagickBooleanType QueryColorDatabase(const char *name,PixelPacket *color, % ExceptionInfo *exception) % % A description of each parameter follows: % % o name: The color name (e.g. white, blue, yellow). % % o color: The red, green, blue, and opacity intensities values of the % named color in this structure. % % o exception: Return any errors or warnings in this structure. % */ static inline double MagickMin(const double x,const double y) { if (x < y) return(x); return(y); } MagickExport MagickBooleanType QueryColorDatabase(const char *name, PixelPacket *color,ExceptionInfo *exception) { MagickBooleanType status; MagickPixelPacket pixel; status=QueryMagickColor(name,&pixel,exception); color->opacity=RoundToQuantum(pixel.opacity); if (pixel.colorspace == CMYKColorspace) { color->red=RoundToQuantum((MagickRealType) (QuantumRange-MagickMin( QuantumRange,(MagickRealType) (QuantumScale*pixel.red*(QuantumRange- pixel.index)+pixel.index)))); color->green=RoundToQuantum((MagickRealType) (QuantumRange-MagickMin( QuantumRange,(MagickRealType) (QuantumScale*pixel.green*(QuantumRange- pixel.index)+pixel.index)))); color->blue=RoundToQuantum((MagickRealType) (QuantumRange-MagickMin( QuantumRange,(MagickRealType) (QuantumScale*pixel.blue*(QuantumRange- pixel.index)+pixel.index)))); return(status); } color->red=RoundToQuantum(pixel.red); color->green=RoundToQuantum(pixel.green); color->blue=RoundToQuantum(pixel.blue); return(status); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % Q u e r y C o l o r n a m e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % QueryColorname() returns a named color for the given color intensity. If % an exact match is not found, a rgb() color is returned instead. % % The format of the QueryColorname method is: % % MagickBooleanType QueryColorname(const Image *image, % const PixelPacket *color,const ComplianceType compliance,char *name, % ExceptionInfo *exception) % % A description of each parameter follows. % % o image: The image. % % o color: The color intensities. % % o compliance: Adhere to this color standard: SVG, X11, or XPM. % % o name: Return the color name or hex value. % % o exception: Return any errors or warnings in this structure. % */ MagickExport MagickBooleanType QueryColorname(const Image *image, const PixelPacket *color,const ComplianceType compliance,char *name, ExceptionInfo *exception) { MagickBooleanType status; MagickPixelPacket pixel; GetMagickPixelPacket(image,&pixel); SetMagickPixelPacket(image,color,(IndexPacket *) NULL,&pixel); status=QueryMagickColorname(image,&pixel,compliance,MagickFalse,name, exception); return(status); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % Q u e r y M a g i c k C o l o r % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % QueryMagickColor() returns the red, green, blue, and opacity intensities % for a given color name. % % The format of the QueryMagickColor method is: % % MagickBooleanType QueryMagickColor(const char *name, % MagickPixelPacket *color,ExceptionInfo *exception) % % A description of each parameter follows: % % o name: The color name (e.g. white, blue, yellow). % % o color: The red, green, blue, and opacity intensities values of the % named color in this structure. % % o exception: Return any errors or warnings in this structure. % */ MagickExport MagickBooleanType QueryMagickColor(const char *name, MagickPixelPacket *color,ExceptionInfo *exception) { GeometryInfo geometry_info; long type; MagickRealType scale; MagickStatusType flags; register const ColorInfo *p; register long i; /* Initialize color return value. */ assert(name != (const char *) NULL); (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",name); assert(color != (MagickPixelPacket *) NULL); GetMagickPixelPacket((Image *) NULL,color); if ((name == (char *) NULL) || (*name == '\0')) name=BackgroundColor; while (isspace((int) ((unsigned char) *name)) != 0) name++; if (*name == '#') { char c; LongPixelPacket pixel; unsigned long depth, n; /* Parse hex color. */ (void) ResetMagickMemory(&pixel,0,sizeof(pixel)); name++; for (n=0; isxdigit((int) ((unsigned char) name[n])) != MagickFalse; n++); if ((n % 3) == 0) { do { pixel.red=pixel.green; pixel.green=pixel.blue; pixel.blue=0; for (i=(long) (n/3-1); i >= 0; i--) { c=(*name++); pixel.blue<<=4; if ((c >= '0') && (c <= '9')) pixel.blue|=(int) (c-'0'); else if ((c >= 'A') && (c <= 'F')) pixel.blue|=(int) c-((int) 'A'-10); else if ((c >= 'a') && (c <= 'f')) pixel.blue|=(int) c-((int) 'a'-10); else return(MagickFalse); } } while (isxdigit((int) ((unsigned char) *name)) != MagickFalse); depth=4*(n/3); } else { if ((n % 4) != 0) { (void) ThrowMagickException(exception,GetMagickModule(), OptionWarning,"UnrecognizedColor","`%s'",name); return(MagickFalse); } do { pixel.red=pixel.green; pixel.green=pixel.blue; pixel.blue=pixel.opacity; pixel.opacity=0; for (i=(long) (n/4-1); i >= 0; i--) { c=(*name++); pixel.opacity<<=4; if ((c >= '0') && (c <= '9')) pixel.opacity|=(int) (c-'0'); else if ((c >= 'A') && (c <= 'F')) pixel.opacity|=(int) c-((int) 'A'-10); else if ((c >= 'a') && (c <= 'f')) pixel.opacity|=(int) c-((int) 'a'-10); else return(MagickFalse); } } while (isxdigit((int) ((unsigned char) *name)) != MagickFalse); depth=4*(n/4); } color->colorspace=RGBColorspace; color->matte=MagickFalse; color->red=(MagickRealType) ScaleAnyToQuantum(pixel.red,depth); color->green=(MagickRealType) ScaleAnyToQuantum(pixel.green,depth); color->blue=(MagickRealType) ScaleAnyToQuantum(pixel.blue,depth); color->opacity=(MagickRealType) OpaqueOpacity; if ((n % 3) != 0) { color->matte=MagickTrue; color->opacity=(MagickRealType) (QuantumRange- ScaleAnyToQuantum(pixel.opacity,depth)); } color->index=0.0; return(MagickTrue); } if (strchr(name,'(') != (char *) NULL) { char colorspace[MaxTextExtent]; /* Parse color of the form rgb(100,255,0). */ (void) CopyMagickString(colorspace,name,MaxTextExtent); for (i=0; colorspace[i] != '\0'; i++) if (colorspace[i] == '(') break; colorspace[i--]='\0'; LocaleLower(colorspace); color->matte=MagickFalse; if ((i > 0) && (colorspace[i] == 'a')) { colorspace[i]='\0'; color->matte=MagickTrue; } type=ParseMagickOption(MagickColorspaceOptions,MagickFalse,colorspace); if (type < 0) { (void) ThrowMagickException(exception,GetMagickModule(), OptionWarning,"UnrecognizedColor","`%s'",name); return(MagickFalse); } color->colorspace=(ColorspaceType) type; SetGeometryInfo(&geometry_info); flags=ParseGeometry(name+i+1,&geometry_info); scale=(MagickRealType) ScaleCharToQuantum(1); if ((flags & PercentValue) != 0) scale=(MagickRealType) (QuantumRange/100.0); if ((flags & RhoValue) != 0) color->red=(MagickRealType) RoundToQuantum(scale*geometry_info.rho); if ((flags & SigmaValue) != 0) color->green=(MagickRealType) RoundToQuantum(scale* geometry_info.sigma); if ((flags & XiValue) != 0) color->blue=(MagickRealType) RoundToQuantum(scale*geometry_info.xi); if ((flags & PsiValue) != 0) { if (color->colorspace == CMYKColorspace) color->index=(MagickRealType) RoundToQuantum(scale* geometry_info.psi); else if (color->matte != MagickFalse) color->opacity=(MagickRealType) RoundToQuantum((MagickRealType) (QuantumRange-QuantumRange*geometry_info.psi)); } if (((flags & ChiValue) != 0) && (color->matte != MagickFalse)) color->opacity=(MagickRealType) RoundToQuantum((MagickRealType) (QuantumRange-QuantumRange*geometry_info.chi)); if (LocaleCompare(colorspace,"HSL") == 0) { PixelPacket pixel; geometry_info.rho=fmod(fmod(geometry_info.rho,360.0)+360.0,360.0)/ 360.0; geometry_info.sigma/=100.0; geometry_info.xi/=100.0; ConvertHSLToRGB(geometry_info.rho,geometry_info.sigma, geometry_info.xi,&pixel.red,&pixel.green,&pixel.blue); color->red=(MagickRealType) pixel.red; color->green=(MagickRealType) pixel.green; color->blue=(MagickRealType) pixel.blue; } return(MagickTrue); } /* Parse named color. */ p=GetColorInfo(name,exception); if (p == (const ColorInfo *) NULL) return(MagickFalse); color->colorspace=RGBColorspace; color->matte=p->color.opacity != OpaqueOpacity ? MagickTrue : MagickFalse; color->red=(MagickRealType) p->color.red; color->green=(MagickRealType) p->color.green; color->blue=(MagickRealType) p->color.blue; color->opacity=(MagickRealType) p->color.opacity; color->index=0.0; return(MagickTrue); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % Q u e r y M a g i c k C o l o r n a m e % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % QueryMagickColorname() returns a named color for the given color intensity. % If an exact match is not found, a hex value is returned instead. For % example an intensity of rgb:(0,0,0) returns black whereas rgb:(223,223,223) % returns #dfdfdf. % % The format of the QueryMagickColorname method is: % % MagickBooleanType QueryMagickColorname(const Image *image, % const PixelPacket *color,const ComplianceType compliance, % const MagickBooleanType hex,char *name,ExceptionInfo *exception) % % A description of each parameter follows. % % o image: The image. % % o color: The color intensities. % % o Compliance: Adhere to this color standard: SVG, X11, or XPM. % % o hex: A value other than zero returns the tuple in a hexidecimal format. % % o name: Return the color name or hex value. % % o exception: Return any errors or warnings in this structure. % */ MagickExport MagickBooleanType QueryMagickColorname(const Image *image, const MagickPixelPacket *color,const ComplianceType compliance, const MagickBooleanType hex,char *name,ExceptionInfo *exception) { MagickPixelPacket pixel; MagickRealType opacity; register const ColorInfo *p; *name='\0'; pixel=(*color); if (compliance == XPMCompliance) { pixel.matte=MagickFalse; pixel.depth=(unsigned long) MagickMin(1.0*image->depth,16.0); } if (hex != MagickFalse) { GetColorTuple(&pixel,hex,name); return(MagickTrue); } (void) GetColorInfo("*",exception); ResetLinkedListIterator(color_list); opacity=image->matte != MagickFalse ? color->opacity : OpaqueOpacity; p=(const ColorInfo *) GetNextValueInLinkedList(color_list); while (p != (const ColorInfo *) NULL) { if (((p->compliance & compliance) != 0) && ((p->color.red == color->red)) && (p->color.green == color->green) && (p->color.blue == color->blue) && (p->color.opacity == opacity)) { (void) CopyMagickString(name,p->name,MaxTextExtent); return(MagickTrue); } p=(const ColorInfo *) GetNextValueInLinkedList(color_list); } GetColorTuple(&pixel,hex,name); return(MagickTrue); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % 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" 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) UniqueColorsToImage(image,cube_info,node_info->child[i],exception); if (node_info->level == (MaxTreeDepth-1)) { register ColorPacket *p; register IndexPacket *indexes; register PixelPacket *q; p=node_info->list; for (i=0; i < (long) node_info->number_unique; i++) { q=SetImagePixels(image,cube_info->x,0,1,1); if (q == (PixelPacket *) NULL) continue; indexes=GetIndexes(image); *q=p->pixel; if (image->colorspace == CMYKColorspace) *indexes=p->index; (void) SyncImagePixels(image); cube_info->x++; p++; } if ((image->progress_monitor != (MagickProgressMonitor) NULL) && (QuantumTick(cube_info->progress,cube_info->colors) != MagickFalse)) (void) image->progress_monitor(UniqueColorsImageTag,cube_info->progress, cube_info->colors,image->client_data); 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); 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=cube_info->colors; (void) QuantizeImage(quantize_info,unique_image); quantize_info=DestroyQuantizeInfo(quantize_info); } cube_info=DestroyCubeInfo(image,cube_info); return(unique_image); }