summaryrefslogtreecommitdiff
path: root/app/fvwm/libs/Picture.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/fvwm/libs/Picture.c')
-rw-r--r--app/fvwm/libs/Picture.c482
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 */