xenocara/app/fvwm/libs/Picture.c

483 lines
15 KiB
C
Raw Permalink Normal View History

/****************************************************************************
* 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.2 2011/01/18 19:28:33 espie 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 FvwmPicture *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;
}
FvwmPicture *LoadPicture(Display *dpy,Window Root,char *path, int color_limit)
{
int l;
FvwmPicture *p;
#ifdef XPM
XpmAttributes xpm_attributes;
int rc;
XpmImage my_image = {0};
#endif
p=(FvwmPicture*)safemalloc(sizeof(FvwmPicture));
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;
}
FvwmPicture *GetPicture(Display *dpy,Window Root,char *IconPath,
char *PixmapPath, char *name, int color_limit)
{
char *path;
FvwmPicture *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;
}
FvwmPicture *CachePicture(Display *dpy,Window Root,char *IconPath,char *PixmapPath,
char *name, int color_limit)
{
char *path;
FvwmPicture *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,FvwmPicture *p)
{
FvwmPicture *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 */