diff options
Diffstat (limited to 'app/fvwm/libs/Picture.c')
-rw-r--r-- | app/fvwm/libs/Picture.c | 482 |
1 files changed, 482 insertions, 0 deletions
diff --git a/app/fvwm/libs/Picture.c b/app/fvwm/libs/Picture.c new file mode 100644 index 000000000..33cdb1f88 --- /dev/null +++ b/app/fvwm/libs/Picture.c @@ -0,0 +1,482 @@ + /**************************************************************************** + * This module is all original code + * by Rob Nation + * Copyright 1993, Robert Nation + * You may use this code for any purpose, as long as the original + * copyright remains in the source code and all documentation + ****************************************************************************/ +/* + Changed 02/12/97 by Dan Espen: + - added routines to determine color closeness, for color use reduction. + Some of the logic comes from pixy2, so the copyright is below. + */ +/* + * $Id: Picture.c,v 1.1 2006/11/26 10:53:41 matthieu Exp $ + * Copyright 1996, Romano Giannetti. No guarantees or warantees or anything + * are provided or implied in any way whatsoever. Use this program at your + * own risk. Permission to use this program for any purpose is given, + * as long as the copyright is kept intact. + * + * Romano Giannetti - Dipartimento di Ingegneria dell'Informazione + * via Diotisalvi, 2 PISA + * mailto:romano@iet.unipi.it + * http://www.iet.unipi.it/~romano + * + */ + +/**************************************************************************** + * + * Routines to handle initialization, loading, and removing of xpm's or mono- + * icon images. + * + ****************************************************************************/ + +#include "config.h" + +#include <stdio.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <X11/keysym.h> +#include <sys/types.h> +#include <sys/time.h> + +#include <X11/Xlib.h> +#include <X11/Xutil.h> + +#ifdef XPM +/* static function prototypes */ +static void c100_init_base_table (); /* prototype */ +static void c200_substitute_color(char **,int); /* prototype */ +static void c300_color_to_rgb(char *, XColor *); /* prototype */ +static double c400_distance(XColor *, XColor *); /* prototype */ +#endif + +#include "fvwmlib.h" + + +static Picture *PictureList=NULL; +Colormap PictureCMap; +Display *PictureSaveDisplay; /* Save area for display pointer */ + +/* This routine called during fvwm and some modules initialization */ +void InitPictureCMap(Display *dpy,Window Root) +{ + XWindowAttributes root_attr; + PictureSaveDisplay = dpy; /* save for latter */ + XGetWindowAttributes(dpy,Root,&root_attr); + PictureCMap=root_attr.colormap; +} + + +Picture *LoadPicture(Display *dpy,Window Root,char *path, int color_limit) +{ + int l; + Picture *p; +#ifdef XPM + XpmAttributes xpm_attributes; + int rc; + XpmImage my_image = {0}; +#endif + + p=(Picture*)safemalloc(sizeof(Picture)); + p->count=1; + p->name=path; + p->next=NULL; + +#ifdef XPM + /* Try to load it as an X Pixmap first */ + xpm_attributes.colormap=PictureCMap; + xpm_attributes.closeness=40000; /* Allow for "similar" colors */ + xpm_attributes.valuemask= + XpmSize | XpmReturnPixels | XpmColormap | XpmCloseness; + + rc =XpmReadFileToXpmImage(path, &my_image, NULL); + if (rc == XpmSuccess) { + color_reduce_pixmap(&my_image, color_limit); + rc = XpmCreatePixmapFromXpmImage(dpy, Root, &my_image, + &p->picture,&p->mask, + &xpm_attributes); + if (rc == XpmSuccess) { + p->width = my_image.width; + p->height = my_image.height; + XpmFreeXpmImage(&my_image); + p->depth = DefaultDepthOfScreen(DefaultScreenOfDisplay(dpy)); + return p; + } + XpmFreeXpmImage(&my_image); + } +#endif + + /* If no XPM support, or XPM loading failed, try bitmap */ + if(XReadBitmapFile(dpy,Root,path,&p->width,&p->height,&p->picture,&l,&l) + == BitmapSuccess) + { + p->depth = 0; + p->mask = None; + return p; + } + + free(p); + return NULL; +} + +Picture *GetPicture(Display *dpy,Window Root,char *IconPath,char *PixmapPath, + char *name, int color_limit) +{ + char *path; + Picture *p; + + if(!(path=findIconFile(name,PixmapPath,R_OK))) + if(!(path=findIconFile(name,IconPath,R_OK))) + return NULL; + p = LoadPicture(dpy,Root,path, color_limit); + if (!p) + free(path); + return p; +} + +Picture *CachePicture(Display *dpy,Window Root,char *IconPath,char *PixmapPath, + char *name, int color_limit) +{ + char *path; + Picture *p=PictureList; + + /* First find the full pathname */ +#ifdef XPM + if(!(path=findIconFile(name,PixmapPath,R_OK))) + if(!(path=findIconFile(name,IconPath,R_OK))) + return NULL; +#else + /* Ignore the given pixmap path when compiled without XPM support */ + if(!(path=findIconFile(name,IconPath,R_OK))) + return NULL; +#endif + + /* See if the picture is already cached */ + while(p) + { + register char *p1, *p2; + + for (p1=path, p2=p->name; *p1 && *p2; ++p1, ++p2) + if (*p1 != *p2) + break; + + if(!*p1 && !*p2) /* We have found a picture with the wanted name */ + { + p->count++; /* Put another weight on the picture */ + free(path); + return p; + } + p=p->next; + } + + /* Not previously cached, have to load it ourself. Put it first in list */ + p=LoadPicture(dpy,Root,path, color_limit); + if(p) + { + p->next=PictureList; + PictureList=p; + } + else + free(path); + return p; +} + + +void DestroyPicture(Display *dpy,Picture *p) +{ + Picture *q=PictureList; + + if (!p) /* bag out if NULL */ + return; + if(--(p->count)>0) /* Remove a weight, still too heavy? */ + return; + + /* Let it fly */ + if(p->name!=NULL) + free(p->name); + if(p->picture!=None) + XFreePixmap(dpy,p->picture); + if(p->mask!=None) + XFreePixmap(dpy,p->mask); + + /* Link it out of the list (it might not be there) */ + if(p==q) /* in head? simple */ + PictureList=p->next; + else + { + while(q && q->next!=p) /* fast forward until end or found */ + q=q->next; + if(q) /* not end? means we found it in there, possibly at end */ + q->next=p->next; /* link around it */ + } + free(p); +} + +/**************************************************************************** + * + * Find the specified icon file somewhere along the given path. + * + * There is a possible race condition here: We check the file and later + * do something with it. By then, the file might not be accessible. + * Oh well. + * + ****************************************************************************/ +char *findIconFile(char *icon, char *pathlist, int type) +{ + char *path; + char *dir_end; + int l; + size_t pathlen; + + if (!icon) + return NULL; + + l = (pathlist) ? strlen(pathlist) : 0; + pathlen = strlen(icon) + l + 10; + path = safemalloc(pathlen); + *path = '\0'; + if (*icon == '/' || pathlist == NULL || *pathlist == '\0') + { + /* No search if icon begins with a slash */ + /* No search if pathlist is empty */ + strlcpy(path, icon, pathlen); + return path; + } + + /* Search each element of the pathlist for the icon file */ + while ((pathlist)&&(*pathlist)) + { + dir_end = strchr(pathlist, ':'); + if (dir_end != NULL) + { + strncpy(path, pathlist, dir_end - pathlist); + path[dir_end - pathlist] = 0; + } + else + strlcpy(path, pathlist, pathlen); + + strlcat(path, "/", pathlen); + strlcat(path, icon, pathlen); + if (access(path, type) == 0) + return path; + strlcat(path, ".gz", pathlen); + if (access(path, type) == 0) + return path; + + /* Point to next element of the path */ + if(dir_end == NULL) + pathlist = NULL; + else + pathlist = dir_end + 1; + } + /* Hmm, couldn't find the file. Return NULL */ + free(path); + return NULL; +} + + +#ifdef XPM +/* This structure is used to quickly access the RGB values of the colors */ +/* without repeatedly having to transform them. */ +typedef struct { + char * c_color; /* Pointer to the name of the color */ + XColor rgb_space; /* rgb color info */ +} Color_Info; + +/* First thing in base array are colors probably already in the color map + because they have familiar names. + I pasted them into a xpm and spread them out so that similar colors are + spread out. + Toward the end are some colors to fill in the gaps. + Currently 61 colors in this list. + */ +static Color_Info base_array[] = { + {"white"}, + {"black"}, + {"grey"}, + {"green"}, + {"blue"}, + {"red"}, + {"cyan"}, + {"yellow"}, + {"magenta"}, + {"DodgerBlue"}, + {"SteelBlue"}, + {"chartreuse"}, + {"wheat"}, + {"turquoise"}, + {"CadetBlue"}, + {"gray87"}, + {"CornflowerBlue"}, + {"YellowGreen"}, + {"NavyBlue"}, + {"MediumBlue"}, + {"plum"}, + {"aquamarine"}, + {"orchid"}, + {"ForestGreen"}, + {"lightyellow"}, + {"brown"}, + {"orange"}, + {"red3"}, + {"HotPink"}, + {"LightBlue"}, + {"gray47"}, + {"pink"}, + {"red4"}, + {"violet"}, + {"purple"}, + {"gray63"}, + {"gray94"}, + {"plum1"}, + {"PeachPuff"}, + {"maroon"}, + {"lavender"}, + {"salmon"}, /* for peachpuff, orange gap */ + {"blue4"}, /* for navyblue/mediumblue gap */ + {"PaleGreen4"}, /* for forestgreen, yellowgreen gap */ + {"#AA7700"}, /* brick, no close named color */ + {"#11EE88"}, /* light green, no close named color */ + {"#884466"}, /* dark brown, no close named color */ + {"#CC8888"}, /* light brick, no close named color */ + {"#EECC44"}, /* gold, no close named color */ + {"#AAAA44"}, /* dull green, no close named color */ + {"#FF1188"}, /* pinkish red */ + {"#992299"}, /* purple */ + {"#CCFFAA"}, /* light green */ + {"#664400"}, /* dark brown*/ + {"#AADD99"}, /* light green */ + {"#66CCFF"}, /* light blue */ + {"#CC2299"}, /* dark red */ + {"#FF11CC"}, /* bright pink */ + {"#11CC99"}, /* grey/green */ + {"#AA77AA"}, /* purple/red */ + {"#EEBB77"} /* orange/yellow */ +}; + +#define NColors (sizeof(base_array) / sizeof(Color_Info)) + +/* if c_color isn't set, copy it from one of the other colours */ +Bool xpmcolor_require_c_color(XpmColor *p) +{ + if (p->c_color != NULL) + return False; + else if (p->g_color != NULL) + p->c_color = strdup(p->g_color); + else if (p->g4_color != NULL) + p->c_color = strdup(p->g4_color); + else if (p->m_color != NULL) + p->c_color = strdup(p->m_color); + else + p->c_color = strdup("none"); + + return True; +} + +/* given an xpm, change colors to colors close to the + subset above. */ +void +color_reduce_pixmap(XpmImage *image,int color_limit) { + int i; + XpmColor *color_table_ptr; + static char base_init = 'n'; + + if (color_limit > 0) { /* If colors to be limited */ + if (base_init == 'n') { /* if base table not created yet */ + c100_init_base_table(); /* init the base table */ + base_init = 'y'; /* remember that its set now. */ + } /* end base table init */ + color_table_ptr = image->colorTable; /* start of xpm color table */ + for(i=0; i<image->ncolors; i++) { /* all colors in the xpm */ + /* Theres an array for this in the xpm library, but it doesn't + appear to be part of the API. Too bad. dje 01/09/00 */ + char **visual_color = 0; + if (color_table_ptr->c_color) { + visual_color = &color_table_ptr->c_color; + } else if (color_table_ptr->g_color) { + visual_color = &color_table_ptr->g_color; + } else if (color_table_ptr->g4_color) { + visual_color = &color_table_ptr->g4_color; + } else { /* its got to be one of these */ + visual_color = &color_table_ptr->m_color; + } + c200_substitute_color(visual_color,color_limit); + color_table_ptr +=1; /* counter for loop */ + } /* end all colors in xpm */ + } /* end colors limited */ + return; /* return, no rc! */ +} + +/* from the color names in the base table, calc rgbs */ +static void +c100_init_base_table () { + int i; + for (i=0; i<NColors; i++) { /* change all base colors to numbers */ + c300_color_to_rgb(base_array[i].c_color, &base_array[i].rgb_space); + } +} + + +/* Replace the color in my_color by the closest matching color + from base_table */ +void c200_substitute_color(char **my_color, int color_limit) { + int i, limit, minind; + double mindst=1e20; + double dst; + XColor rgb; /* place to calc rgb for each color in xpm */ + size_t len; + + if (!strcasecmp(*my_color,"none")) { + return ; /* do not substitute the "none" color */ + } + + c300_color_to_rgb(*my_color, &rgb); /* get rgb for a color in xpm */ + /* Loop over all base_array colors; find out which one is closest + to my_color */ + minind = 0; /* Its going to find something... */ + limit = NColors; /* init to max */ + if (color_limit < NColors) { /* can't do more than I have */ + limit = color_limit; /* Do reduction using subset */ + } /* end reducing limit */ + for(i=0; i < limit; i++) { /* loop over base array */ + dst = c400_distance (&rgb, &base_array[i].rgb_space); /* distance */ + if (dst < mindst ) { /* less than min and better than last */ + mindst=dst; /* new minimum */ + minind=i; /* save loc of new winner */ + if (dst <= 100) { /* if close enough */ + break; /* done */ + } /* end close enough */ + } /* end new low distance */ + } /* end all base colors */ + /* Finally: replace the color string by the newly determined color string */ + free(*my_color); /* free old color */ + len = strlen(base_array[minind].c_color) + 1; + *my_color = safemalloc(len); /* area for new color */ + strlcpy(*my_color,base_array[minind].c_color,len); /* put it there */ + return; /* all done */ + } + +static void c300_color_to_rgb(char *c_color, XColor *rgb_space) { + int rc; + rc=XParseColor(PictureSaveDisplay, PictureCMap, c_color, rgb_space); + if (rc==0) { + fprintf(stderr,"color_to_rgb: can't parse color %s, rc %d\n", c_color, rc); + return; + } +} + +/* A macro for squaring things */ +#define SQUARE(X) ((X)*(X)) +/* RGB Color distance sum of square of differences */ +double c400_distance(XColor *target_ptr, XColor *base_ptr) { + register double dst; + dst = SQUARE((double)(base_ptr->red - target_ptr->red )/655.35) + + SQUARE((double)(base_ptr->green - target_ptr->green)/655.35) + + SQUARE((double)(base_ptr->blue - target_ptr->blue )/655.35); + return dst; +} +#endif /* XPM */ |