/* images.c -- Image handling
$Id: images.c,v 1.59 2003/01/12 20:35:22 jsh Exp $
Copyright (C) 1999, 2000 John Harper <john@dcs.warwick.ac.uk>
This file is part of sawmill.
sawmill is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
sawmill is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with sawmill; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/* AIX requires this to be the first thing in the file. */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef __GNUC__
# if HAVE_ALLOCA_H
# include <alloca.h>
# else
# ifdef _AIX
#pragma alloca
# else
# ifndef alloca /* predefined by HP cc +Olibcalls */
char *alloca ();
# endif
# endif
# endif
#endif
#include "sawmill.h"
#include <assert.h>
static Lisp_Image *image_list;
int image_type;
#if defined HAVE_IMLIB
static ImlibData *imlib_id;
#elif defined HAVE_GDK_PIXBUF
GdkInterpType interp_type = GDK_INTERP_BILINEAR;
#endif
Colormap image_cmap;
Visual *image_visual;
int image_depth;
DEFSYM(image_load_path, "image-load-path");
DEFSYM(image_directory, "image-directory");
DEFSYM(red, "red");
DEFSYM(green, "green");
DEFSYM(blue, "blue");
DEFSYM(default_bevel_percent, "default-bevel-percent");
/* A rough estimate of the memory used by a single image_t */
#if defined HAVE_IMLIB
#define IMAGE_T_SIZE(i) (sizeof (ImlibImage) \
+ (i)->rgb_width * (i)->rgb_height * 3)
#elif defined HAVE_GDK_PIXBUF
#define IMAGE_T_SIZE(i) (gdk_pixbuf_get_width (i) \
* gdk_pixbuf_get_height (i) \
* gdk_pixbuf_get_n_channels (i))
#endif
static image_t
load_image (char *file)
{
#if defined HAVE_IMLIB
/* Fuck. Imlib's image caching is so annoying. This is the only way
I can think of disabling it, even though it may fragment memory.. */
ImlibImage *im_1 = Imlib_load_image (imlib_id, file);
if (im_1 != 0)
{
ImlibImage *im_2 = Imlib_clone_image (imlib_id, im_1);
Imlib_kill_image (imlib_id, im_1);
return im_2;
}
else
return 0;
#elif defined HAVE_GDK_PIXBUF
return gdk_pixbuf_new_from_file (file, NULL);
#endif
}
/* Make a Lisp image object from the imlib image IM. Its initial properties
will be taken from the list PLIST. */
static repv
make_image (image_t im, repv plist)
{
Lisp_Image *f;
for (f = image_list; f != 0; f = f->next)
{
if (f->image == im)
/* XXX do what with the plist..? */
return rep_VAL(f);
}
f = rep_ALLOC_CELL(sizeof(Lisp_Image));
rep_data_after_gc += sizeof (Lisp_Image) + IMAGE_T_SIZE (im);
f->car = image_type;
f->next = image_list;
image_list = f;
f->image = im;
#if !defined (HAVE_IMLIB)
f->border[0] = f->border[1] = f->border[2] = f->border[3] = 0;
#endif
#if defined NEED_PIXMAP_CACHE
f->pixmap_first = f->pixmap_last = 0;
#endif
f->plist = plist;
return rep_VAL(f);
}
static repv
find_image_file (repv file, bool *deletep)
{
repv path = global_symbol_value (Qimage_load_path), out = Qnil;
rep_GC_root gc_file, gc_path;
rep_PUSHGC(gc_file, file);
rep_PUSHGC(gc_path, path);
while (out == Qnil && rep_CONSP(path))
{
repv all = Fexpand_file_name (file, rep_CAR(path));
if (all && rep_STRINGP(all))
{
repv tem;
rep_GC_root gc_all;
rep_PUSHGC(gc_all, all);
tem = Ffile_regular_p (all);
if (tem && tem != Qnil)
{
bool delete = FALSE;
file = Flocal_file_name (all);
if (file == Qnil)
{
/* Non-local file; try to copy to temp file */
repv temp = Fmake_temp_name ();
rep_GC_root gc_temp;
rep_PUSHGC(gc_temp, temp);
tem = Fcopy_file (all, temp);
rep_POPGC;
if (tem && tem != Qnil)
{
file = temp;
delete = TRUE;
}
}
if (file && rep_STRINGP(file))
{
*deletep = delete;
out = file;
}
}
rep_POPGC;
}
path = rep_CDR(path);
}
rep_POPGC; rep_POPGC;
return out;
}
Pixmap
make_bitmap (repv file, int *widthp, int *heightp, int *x_hotp, int *y_hotp)
{
bool delete;
file = find_image_file (file, &delete);
if (file && rep_STRINGP(file))
{
Pixmap bitmap;
int ret = XReadBitmapFile (dpy, root_window, rep_STR(file),
widthp, heightp, &bitmap, x_hotp, y_hotp);
if (delete)
Fdelete_file (file);
return (ret == BitmapSuccess) ? bitmap : 0;
}
else
return 0;
}
DEFUN("make-image", Fmake_image, Smake_image,
(repv file, repv plist), rep_Subr2) /*
::doc:sawfish.wm.images#make-image::
make-image FILE [PLIST]
Return a new image object representing the image stored in FILE (a
string). PLIST defines the property list of the image.
::end:: */
{
bool delete;
rep_GC_root gc_plist;
rep_DECLARE1(file, rep_STRINGP);
if (dpy == 0)
return Qnil;
rep_PUSHGC(gc_plist, plist);
file = find_image_file (file, &delete);
rep_POPGC;
if (file && rep_STRINGP(file))
{
image_t im = load_image (rep_STR(file));
if (delete)
Fdelete_file (file);
if (im != 0)
return make_image (im, plist);
}
return Fsignal (Qerror, rep_list_2(rep_string_dup("no such image"), file));
}
DEFUN("make-image-from-x-drawable", Fmake_image_from_x_drawable,
Smake_image_from_x_drawable, (repv id, repv mask_id), rep_Subr2) /*
::doc:sawfish.wm.images#make-image-from-x-drawable::
make-image-from-x-drawable ID [MASK-ID]
::end:: */
{
Drawable d;
Pixmap mask = 0;
Window root;
int x, y, w, h, bdr, dp;
image_t im;
rep_DECLARE1 (id, rep_INTP);
d = rep_INT (id);
if (rep_INTP (mask_id))
mask = rep_INT (mask_id);
if (!XGetGeometry (dpy, d, &root, &x, &y, &w, &h, &bdr, &dp))
return Qnil;
#if defined HAVE_IMLIB
/* Imlib ignores the MASK parameter of this function.. */
im = Imlib_create_image_from_drawable (imlib_id, d, 0, 0, 0, w, h);
if (mask != 0)
{
/* ..so do it by hand */
XImage *xim = XGetImage (dpy, mask, 0, 0, w, h, AllPlanes, ZPixmap);
if (xim != 0)
{
u_char *pixels = im->rgb_data;
int x, y;
ImlibColor shape;
/* locate a suitable shape color (one that is unused by
the actual image data). */
shape.r = shape.g = shape.b = 1; /* most images include black */
try_again:
for (y = 0; y < h; y++)
{
for (x = 0; x < w; x++)
{
u_long pixel = XGetPixel (xim, x, y);
u_char *rgb = pixels + ((y * w) + x) * 3;
if (pixel != 0 && rgb[0] == shape.r
&& rgb[1] == shape.g && rgb[2] == shape.b)
{
/* this color no good. */
if (++shape.r == 256)
{
shape.r = 0;
if (++shape.g == 256)
{
shape.g = 0;
if (++shape.b == 256)
goto out; /* no possible shape color */
}
}
goto try_again;
}
}
}
/* SHAPE now has a color unused by the image data */
Imlib_set_image_shape (imlib_id, im, &shape);
for (y = 0; y < h; y++)
{
for (x = 0; x < w; x++)
{
if (XGetPixel (xim, x, y) == 0)
{
u_char *rgb = pixels + ((y * w) + x) * 3;
rgb[0] = shape.r;
rgb[1] = shape.g;
rgb[2] = shape.b;
}
}
}
Imlib_changed_image (imlib_id, im);
out:
XDestroyImage (xim);
}
}
#elif defined HAVE_GDK_PIXBUF
if (dp == 1)
{
/* gdk_pixbuf_xlib_get_from_drawable () doesn't seem to work
with single-bitplane drawables. Doing it by hand.. */
XImage *xim = XGetImage (dpy, d, 0, 0, w, h, 1, ZPixmap);
im = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, w, h);
if (xim != 0)
{
int rowstride = gdk_pixbuf_get_rowstride (im);
u_char *pixels = gdk_pixbuf_get_pixels (im);
int x, y;
for (y = 0; y < h; y++)
{
for (x = 0; x < w; x++)
{
int idx = y * rowstride + x * 3;
u_char chan = (XGetPixel (xim, x, y) != 0) ? 0 : 255;
pixels[idx+0] = chan;
pixels[idx+1] = chan;
pixels[idx+2] = chan;
}
}
XDestroyImage (xim);
}
}
else
{
im = gdk_pixbuf_xlib_get_from_drawable (0, d, image_cmap, image_visual,
0, 0, 0, 0, w, h);
}
if (mask != 0)
{
/* this code inspired by the GNOME tasklist_applet */
GdkPixbuf *tem = gdk_pixbuf_add_alpha (im, FALSE, 0, 0, 0);
if (tem != 0)
{
XImage *xim = XGetImage (dpy, mask, 0, 0, w, h, AllPlanes, ZPixmap);
gdk_pixbuf_unref (im);
im = tem;
if (xim != 0)
{
int rowstride = gdk_pixbuf_get_rowstride (im);
int channels = gdk_pixbuf_get_n_channels (im);
unsigned char *pixels = gdk_pixbuf_get_pixels (im);
int x, y;
for (y = 0; y < h; y++)
{
for (x = 0; x < w; x++)
{
int idx = y * rowstride + x * channels + 3;
pixels[idx] = (XGetPixel (xim, x, y) == 0) ? 0 : 255;
}
}
XDestroyImage (xim);
}
}
}
#endif
/* server grab may be lost in the above calls */
regrab_server ();
return im ? make_image (im, Qnil) : Qnil;
}
DEFUN("copy-image", Fcopy_image, Scopy_image, (repv source), rep_Subr1) /*
::doc:sawfish.wm.images#copy-image::
copy-image SOURCE-IMAGE
Return a new image object, a clone of SOURCE-IMAGE.
::end:: */
{
image_t im;
rep_DECLARE1(source, IMAGEP);
#if defined HAVE_IMLIB
im = Imlib_clone_image (imlib_id, VIMAGE(source)->image);
#elif defined HAVE_GDK_PIXBUF
im = gdk_pixbuf_copy (VIMAGE (source)->image);
#endif
if (im != 0)
return make_image (im, Fcopy_sequence (VIMAGE(source)->plist));
return Fsignal (Qerror,
rep_list_2 (rep_string_dup("can't clone image"), source));
}
DEFUN("flip-image-horizontally", Fflip_image_horizontally,
Sflip_image_horizontally, (repv image), rep_Subr1) /*
::doc:sawfish.wm.images#flip-image-horizontally::
flip-image-horizontally IMAGE
Flip the contents of IMAGE around the vertical axis.
::end:: */
{
rep_DECLARE1(image, IMAGEP);
#if defined HAVE_IMLIB
Imlib_flip_image_horizontal (imlib_id, VIMAGE(image)->image);
#else
{
u_char *pixels = image_pixels (VIMAGE (image));
int channels = image_channels (VIMAGE (image));
int stride = image_row_stride (VIMAGE (image));
int width = image_width (VIMAGE (image));
int height = image_height (VIMAGE (image));
u_char *buf = alloca (channels);
int y;
for (y = 0; y < height; y++)
{
u_char *left = pixels + y * stride;
u_char *right = left + channels * (width - 1);
while (left < right)
{
memcpy (buf, left, channels);
memcpy (left, right, channels);
memcpy (right, buf, channels);
left += channels;
right -= channels;
}
}
image_changed (VIMAGE (image));
}
#endif
return image;
}
DEFUN("flip-image-vertically", Fflip_image_vertically,
Sflip_image_vertically, (repv image), rep_Subr1) /*
::doc:sawfish.wm.images#flip-image-vertically::
flip-image-vertically IMAGE
Flip the contents of IMAGE around the horizontal axis.
::end:: */
{
rep_DECLARE1(image, IMAGEP);
#if defined HAVE_IMLIB
Imlib_flip_image_vertical (imlib_id, VIMAGE(image)->image);
#else
{
u_char *pixels = image_pixels (VIMAGE (image));
int stride = image_row_stride (VIMAGE (image));
int height = image_height (VIMAGE (image));
u_char *buf = alloca (stride);
u_char *top = pixels, *bottom = pixels + (height - 1) * stride;
while (top < bottom)
{
memcpy (buf, top, stride);
memcpy (top, bottom, stride);
memcpy (bottom, buf, stride);
top += stride;
bottom -= stride;
}
image_changed (VIMAGE (image));
}
#endif
return image;
}
DEFUN("flip-image-diagonally", Fflip_image_diagonally,
Sflip_image_diagonally, (repv image), rep_Subr1) /*
::doc:sawfish.wm.images#flip-image-diagonally::
flip-image-diagonally IMAGE
Flip the contents of IMAGE around a diagonal axis from the top-left to
the bottom right of the image.
::end:: */
{
rep_DECLARE1(image, IMAGEP);
#if defined HAVE_IMLIB
Imlib_rotate_image (imlib_id, VIMAGE(image)->image, 1);
#elif defined HAVE_GDK_PIXBUF
{
/* adapted from Imlib */
GdkPixbuf *in = VIMAGE (image)->image;
int channels = gdk_pixbuf_get_n_channels (in);
int in_stride = gdk_pixbuf_get_rowstride (in);
int in_width = gdk_pixbuf_get_width (in);
int in_height = gdk_pixbuf_get_height (in);
u_char *in_pixels = gdk_pixbuf_get_pixels (in);
GdkPixbuf *out = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (in),
gdk_pixbuf_get_has_alpha (in),
gdk_pixbuf_get_bits_per_sample (in),
in_height, in_width);
int out_stride = gdk_pixbuf_get_rowstride (out);
u_char *out_pixels = gdk_pixbuf_get_pixels (out);
int x, y;
for (y = 0; y < in_height; y++)
{
for (x = 0; x < in_width; x++)
{
memcpy (out_pixels + x * out_stride + y * channels,
in_pixels + y * in_stride + x * channels,
channels);
}
}
image_changed (VIMAGE (image));
gdk_pixbuf_unref (VIMAGE (image)->image);
VIMAGE (image)->image = out;
}
#endif
return image;
}
DEFUN("image-get", Fimage_get, Simage_get, (repv win, repv prop), rep_Subr2) /*
::doc:sawfish.wm.images#image-get::
image-get IMAGE PROPERTY
Return the value of the property named PROPERTY (a symbol) of IMAGE.
::end:: */
{
repv plist;
rep_DECLARE1(win, IMAGEP);
plist = VIMAGE(win)->plist;
while (rep_CONSP(plist) && rep_CONSP(rep_CDR(plist)))
{
if (rep_CAR(plist) == prop
|| (!rep_SYMBOLP(prop)
&& rep_value_cmp (rep_CAR(plist), prop) == 0))
{
return rep_CAR(rep_CDR(plist));
}
plist = rep_CDR(rep_CDR(plist));
}
return Qnil;
}
DEFUN("image-put", Fimage_put, Simage_put,
(repv win, repv prop, repv val), rep_Subr3) /*
::doc:sawfish.wm.images#image-put::
image-put IMAGE PROPERTY VALUE
Set the value of the property named PROPERTY (a symbol) of IMAGE to VALUE.
::end:: */
{
repv plist;
rep_DECLARE1(win, IMAGEP);
plist = VIMAGE(win)->plist;
while (rep_CONSP(plist) && rep_CONSP(rep_CDR(plist)))
{
if (rep_CAR(plist) == prop
|| (!rep_SYMBOLP(prop)
&& rep_value_cmp (rep_CAR(plist), prop) == 0))
{
rep_CAR(rep_CDR(plist)) = val;
return val;
}
plist = rep_CDR(rep_CDR(plist));
}
plist = Fcons(prop, Fcons(val, VIMAGE(win)->plist));
if (plist != rep_NULL)
VIMAGE(win)->plist = plist;
return val;
}
DEFUN("imagep", Fimagep, Simagep, (repv arg), rep_Subr1) /*
::doc:sawfish.wm.images#imagep::
image ARG
Returns t if ARG is an image object.
::end:: */
{
return IMAGEP(arg) ? Qt : Qnil;
}
DEFUN("image-dimensions", Fimage_dimensions, Simage_dimensions,
(repv img), rep_Subr1) /*
::doc:sawfish.wm.images#image-dimensions::
image-dimensions IMAGE
Return (WIDTH . HEIGHT) representing the dimensions in pixels of IMAGE.
::end:: */
{
rep_DECLARE1(img, IMAGEP);
return Fcons (rep_MAKE_INT(image_width(VIMAGE(img))),
rep_MAKE_INT(image_height(VIMAGE(img))));
}
DEFUN("image-shape-color", Fimage_shape_color,
Simage_shape_color, (repv img), rep_Subr1) /*
::doc:sawfish.wm.images#image-shape-color::
image-shape-color IMAGE
::end:: */
{
#if defined HAVE_IMLIB
ImlibColor shape;
rep_DECLARE1(img, IMAGEP);
Imlib_get_image_shape (imlib_id, VIMAGE(img)->image, &shape);
if (shape.r == -1 && shape.g == -1 && shape.b == -1)
return Qnil;
else
return Fget_color_rgb (rep_MAKE_INT(shape.r * 256),
rep_MAKE_INT(shape.g * 256),
rep_MAKE_INT(shape.b * 256),
Qnil);
#elif defined HAVE_GDK_PIXBUF
fprintf (stderr, "shape colors are unimplemented for gdk-pixbuf\n");
return Qnil;
#endif
}
DEFUN("set-image-shape-color", Fset_image_shape_color, Sset_image_shape_color,
(repv img, repv shape), rep_Subr2) /*
::doc:sawfish.wm.images#set-image-shape-color::
set-image-shape-color IMAGE TRANSPARENT-COLOR
::end:: */
{
#if defined HAVE_IMLIB
ImlibColor color;
rep_DECLARE1(img, IMAGEP);
rep_DECLARE2(shape, COLORP);
color.r = VCOLOR(shape)->red / 256;
color.g = VCOLOR(shape)->green / 256;
color.b = VCOLOR(shape)->blue / 256;
Imlib_set_image_shape (imlib_id, VIMAGE(img)->image, &color);
return img;
#elif defined HAVE_GDK_PIXBUF
fprintf (stderr, "shape colors are unimplemented for gdk-pixbuf\n");
return img;
#endif
}
DEFUN("image-border", Fimage_border, Simage_border, (repv img), rep_Subr1) /*
::doc:sawfish.wm.images#image-border::
image-border IMAGE
Return (LEFT RIGHT TOP BOTTOM) representing the border (in pixels) of IMAGE.
::end:: */
{
rep_DECLARE1(img, IMAGEP);
#if !defined (HAVE_IMLIB)
return rep_list_4 (rep_MAKE_INT (VIMAGE(img)->border[0]),
rep_MAKE_INT (VIMAGE(img)->border[1]),
rep_MAKE_INT (VIMAGE(img)->border[2]),
rep_MAKE_INT (VIMAGE(img)->border[3]));
#else
{
ImlibBorder border;
Imlib_get_image_border (imlib_id, VIMAGE(img)->image, &border);
return rep_list_4 (rep_MAKE_INT(border.left),
rep_MAKE_INT(border.right),
rep_MAKE_INT(border.top),
rep_MAKE_INT(border.bottom));
}
#endif
}
DEFUN("set-image-border", Fset_image_border, Sset_image_border,
(repv img, repv left, repv right, repv top, repv bottom), rep_Subr5) /*
::doc:sawfish.wm.images#set-image-border::
set-image-border IMAGE LEFT RIGHT TOP BOTTOM
Set the border of IMAGE to (LEFT RIGHT TOP BOTTOM). The border of an
image defines how that image is scaled -- only pixels inside the border
are resized.
::end:: */
{
rep_DECLARE1(img, IMAGEP);
#if !defined (HAVE_IMLIB)
VIMAGE(img)->border[0] = rep_INTP (left) ? rep_INT (left) : 0;
VIMAGE(img)->border[1] = rep_INTP (right) ? rep_INT (right) : 0;
VIMAGE(img)->border[2] = rep_INTP (top) ? rep_INT (top) : 0;
VIMAGE(img)->border[3] = rep_INTP (bottom) ? rep_INT (bottom) : 0;
image_changed (VIMAGE(img));
#else
{
ImlibBorder border;
Imlib_get_image_border (imlib_id, VIMAGE(img)->image, &border);
if (rep_INTP(left))
border.left = rep_INT(left);
if (rep_INTP(right))
border.right = rep_INT(right);
if (rep_INTP(top))
border.top = rep_INT(top);
if (rep_INTP(bottom))
border.bottom = rep_INT(bottom);
Imlib_set_image_border (imlib_id, VIMAGE(img)->image, &border);
}
#endif
return img;
}
DEFUN("image-modifier", Fimage_modifier, Simage_modifier,
(repv img, repv type), rep_Subr2) /*
::doc:sawfish.wm.images#image-modifier::
image-modifier IMAGE TYPE
TYPE may be one of nil, red, green, blue. returned modifier is
(GAMMA BRIGHTNESS CONTRAST). All values range from 0 to 255.
::end:: */
{
#if defined HAVE_IMLIB
ImlibColorModifier modifier;
void (*fun)(ImlibData *, ImlibImage *, ImlibColorModifier *);
rep_DECLARE1(img, IMAGEP);
rep_DECLARE(2, type, type == Qnil || rep_SYMBOLP (type));
fun = (type == Qred ? Imlib_get_image_red_modifier
: type == Qgreen ? Imlib_get_image_green_modifier
: type == Qblue ? Imlib_get_image_blue_modifier
: Imlib_get_image_modifier);
(*fun) (imlib_id, VIMAGE(img)->image, &modifier);
return rep_list_3 (rep_MAKE_INT(modifier.gamma),
rep_MAKE_INT(modifier.brightness),
rep_MAKE_INT(modifier.contrast));
#else
fprintf (stderr, "image modifiers are unimplemented for gdk-pixbuf\n");
return Qnil;
#endif
}
DEFUN("set-image-modifier", Fset_image_modifier, Sset_image_modifier,
(repv img, repv type, repv mod), rep_Subr3) /*
::doc:sawfish.wm.images#set-image-modifier::
set-image-modifier IMAGE TYPE MODIFIER
TYPE may be one of nil, red, green, blue. MODIFIER is (GAMMA BRIGHTNESS
CONTRAST). These are integers ranging from 0 to 255.
::end:: */
{
#if defined HAVE_IMLIB
ImlibColorModifier modifier;
void (*fun)(ImlibData *, ImlibImage *, ImlibColorModifier *);
rep_DECLARE1(img, IMAGEP);
rep_DECLARE(2, type, type == Qnil || rep_SYMBOLP (type));
if (!rep_CONSP(mod) || !rep_CONSP(rep_CDR(mod))
|| !rep_CONSP(rep_CDR(rep_CDR(mod))))
{
return rep_signal_arg_error (mod, 3);
}
modifier.gamma = rep_INT(rep_CAR(mod));
modifier.brightness = rep_INT(rep_CAR(rep_CDR(mod)));
modifier.contrast = rep_INT(rep_CAR(rep_CDR(rep_CDR(mod))));
fun = (type == Qred ? Imlib_set_image_red_modifier
: type == Qgreen ? Imlib_set_image_green_modifier
: type == Qblue ? Imlib_set_image_blue_modifier
: Imlib_set_image_modifier);
(*fun) (imlib_id, VIMAGE(img)->image, &modifier);
return img;
#elif defined HAVE_GDK_PIXBUF
fprintf (stderr, "image modifiers are unimplemented for gdk-pixbuf\n");
return img;
#endif
}
static inline void
bevel_pixel (u_char *data, bool up, int bevel_percent)
{
unsigned int t0 = data[0], t1 = data[1], t2 = data[2];
if (up)
{
data[0] = t0 + (255 - t0) * bevel_percent / 100;
data[1] = t1 + (255 - t1) * bevel_percent / 100;
data[2] = t2 + (255 - t2) * bevel_percent / 100;
}
else
{
data[0] = t0 - t0 * bevel_percent / 100;
data[1] = t1 - t1 * bevel_percent / 100;
data[2] = t2 - t2 * bevel_percent / 100;
}
}
static inline void
bevel_region (u_char *data, int row_stride, int bpp,
int rx, int ry, int rw, int rh, bool up, int bevel_percent)
{
int x, y;
for (y = ry; y < ry + rh; y++)
{
u_char *row = data + y * row_stride + rx * bpp;
for (x = rx; x < rx + rh; x++)
{
bevel_pixel (row, up, bevel_percent);
row += bpp;
}
}
}
static inline void
bevel_horizontally (u_char *data, int width, int height,
int row_stride, int channels,
int border, bool top, bool up, int bevel_percent)
{
int rows;
up = top ? up : !up;
for (rows = 0; rows < border; rows++)
{
u_char *ptr = data;
int x;
if (top)
ptr += (rows * row_stride) + (rows + 1) * channels;
else
ptr += row_stride * (height - (rows + 1)) + (rows) * channels;
for (x = rows; x < width - (rows + 1); x++)
{
bevel_pixel (ptr, up, bevel_percent);
ptr += channels;
}
}
}
static inline void
bevel_vertically (u_char *data, int width, int height,
int row_stride, int channels,
int border, bool top, bool up, int bevel_percent)
{
int cols;
up = top ? up : !up;
for (cols = 0; cols < border; cols++)
{
u_char *ptr = data;
int y;
if (top)
ptr += cols * channels + (cols * row_stride);
else
ptr += (width - (cols + 1)) * channels + ((cols) * row_stride);
for (y = cols; y <= height - (cols + 1); y++)
{
bevel_pixel (ptr, up, bevel_percent);
ptr += row_stride;
}
}
}
DEFUN("bevel-image", Fbevel_image, Sbevel_image,
(repv image, repv border, repv up, repv bevel_percent_),
rep_Subr4) /*
::doc:sawfish.wm.images#bevel-image::
bevel-image IMAGE BORDER UP [BEVEL-PERCENT]
Draw a bevelled edge outline onto IMAGE. BORDER is an integer defining
the width of the bevel. If UP is non-nil the bevel is raised.
If BEVEL-PERCENT is an integer between 0 and 100, then this is the
intensity of the bevel created.
::end:: */
{
int bevel_percent = 75;
rep_DECLARE1(image, IMAGEP);
rep_DECLARE2(border, rep_INTP);
if (!rep_INTP(bevel_percent_))
bevel_percent_ = global_symbol_value (Qdefault_bevel_percent);
if (rep_INTP(bevel_percent_))
{
bevel_percent = rep_INT (bevel_percent_);
bevel_percent = CLAMP (bevel_percent, 0, 100);
}
bevel_horizontally (image_pixels (VIMAGE(image)),
image_width (VIMAGE(image)),
image_height (VIMAGE(image)),
image_row_stride (VIMAGE(image)),
image_channels (VIMAGE(image)),
rep_INT(border), TRUE, up != Qnil, bevel_percent);
bevel_vertically (image_pixels (VIMAGE(image)),
image_width (VIMAGE(image)),
image_height (VIMAGE(image)),
image_row_stride (VIMAGE(image)),
image_channels (VIMAGE(image)),
rep_INT(border), TRUE, up != Qnil, bevel_percent);
bevel_horizontally (image_pixels (VIMAGE(image)),
image_width (VIMAGE(image)),
image_height (VIMAGE(image)),
image_row_stride (VIMAGE(image)),
image_channels (VIMAGE(image)),
rep_INT(border), FALSE, up != Qnil, bevel_percent);
bevel_vertically (image_pixels (VIMAGE(image)),
image_width (VIMAGE(image)),
image_height (VIMAGE(image)),
image_row_stride (VIMAGE(image)),
image_channels (VIMAGE(image)),
rep_INT(border), FALSE, up != Qnil, bevel_percent);
image_changed (VIMAGE(image));
return image;
}
DEFUN("clear-image", Fclear_image, Sclear_image,
(repv image, repv color), rep_Subr2) /*
::doc:sawfish.wm.images#clear-image::
clear-image IMAGE [COLOR]
Set all pixels in IMAGE to COLOR (or black if COLOR is undefined).
::end:: */
{
int width, height, stride, channels;
int x, y, r, g, b;
u_char *data;
rep_DECLARE1(image, IMAGEP);
if (COLORP(color))
{
r = VCOLOR(color)->red / 256;
g = VCOLOR(color)->green / 256;
b = VCOLOR(color)->blue / 256;
}
else
r = g = b = 0;
data = image_pixels (VIMAGE(image));
width = image_width (VIMAGE(image));
height = image_height (VIMAGE(image));
stride = image_row_stride (VIMAGE(image));
channels = image_channels (VIMAGE(image));
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
data[y * stride + x * channels + 0] = r;
data[y * stride + x * channels + 1] = g;
data[y * stride + x * channels + 2] = b;
}
}
image_changed (VIMAGE(image));
return image;
}
#if defined HAVE_GDK_PIXBUF
static void
free_pixbuf_data (guchar *pixels, gpointer data)
{
rep_free (pixels);
}
#endif
DEFUN("make-sized-image", Fmake_sized_image, Smake_sized_image,
(repv width, repv height, repv color), rep_Subr3) /*
::doc:sawfish.wm.images#make-sized-image::
make-sized-image WIDTH HEIGHT [COLOR]
Return a new image of dimensions (WIDTH, HEIGHT). The object COLOR
defines the color of its pixels.
::end:: */
{
u_char *data;
int r, g, b;
rep_DECLARE1(width, rep_INTP);
rep_DECLARE2(height, rep_INTP);
if (COLORP(color))
{
r = VCOLOR(color)->red / 256;
g = VCOLOR(color)->green / 256;
b = VCOLOR(color)->blue / 256;
}
else
r = g = b = 0;
#if defined HAVE_IMLIB
data = rep_alloc (rep_INT(width) * rep_INT(height) * 3);
if (data != 0)
{
image_t im;
int i;
for (i = 0; i < rep_INT(width) * rep_INT(height) * 3; i += 3)
{
data[i] = r;
data[i+1] = g;
data[i+2] = b;
}
im = Imlib_create_image_from_data (imlib_id, data, 0,
rep_INT(width), rep_INT(height));
rep_free (data);
#elif defined HAVE_GDK_PIXBUF
data = rep_alloc (rep_INT(width) * rep_INT(height) * 4);
if (data != 0)
{
image_t im;
int i;
for (i = 0; i < rep_INT(width) * rep_INT(height) * 4; i += 4)
{
data[i] = r;
data[i+1] = g;
data[i+2] = b;
data[i+3] = 255;
}
im = gdk_pixbuf_new_from_data (data, GDK_COLORSPACE_RGB, TRUE,
8, rep_INT (width), rep_INT (height),
rep_INT (width) * 4,
free_pixbuf_data, data);
#endif
if (im != 0)
return make_image (im, Qnil);
}
return Qnil;
}
DEFUN("tile-image", Ftile_image, Stile_image, (repv dst, repv src), rep_Subr2) /*
::doc:sawfish.wm.images#tile-image::
tile-image DEST-IMAGE SOURCE-IMAGE
Tile SOURCE-IMAGE into DEST-IMAGE.
::end:: */
{
image_t src_im, dst_im;
int src_width, src_height;
int dst_width, dst_height;
int x, src_y, dst_y;
rep_DECLARE1(dst, IMAGEP);
rep_DECLARE2(src, IMAGEP);
src_im = VIMAGE(src)->image;
dst_im = VIMAGE(dst)->image;
src_width = image_width (VIMAGE (src));
src_height = image_height (VIMAGE (src));
dst_width = image_width (VIMAGE (dst));
dst_height = image_height (VIMAGE (dst));
#if defined HAVE_IMLIB
for (dst_y = src_y = 0; dst_y < dst_height; dst_y++, src_y++)
{
if (src_y >= src_height)
src_y = 0;
for (x = 0; x < dst_width; x += src_width)
{
memcpy (dst_im->rgb_data + dst_y*dst_width*3 + x*3,
src_im->rgb_data + src_y*src_width*3,
MIN (dst_width - x, src_width) * 3);
}
}
#elif defined HAVE_GDK_PIXBUF
for (dst_y = 0; dst_y < dst_height; dst_y += src_height)
{
for (x = 0; x < dst_width; x += src_width)
{
gdk_pixbuf_copy_area (VIMAGE(src)->image, 0, 0,
MIN (src_width, dst_width - x),
MIN (src_height, dst_height - dst_y),
VIMAGE(dst)->image, x, dst_y);
}
}
#endif
image_changed (VIMAGE(dst));
return dst;
}
DEFUN("scale-image", Fscale_image, Sscale_image,
(repv img, repv w, repv h), rep_Subr3) /*
::doc:sawfish.wm.images#scale-image::
scale-image IMAGE WIDTH HEIGHT
Return a new image object, a copy of the contents of IMAGE, but scaled
to WIDTH by HEIGHT pixels.
::end:: */
{
image_t copy;
rep_DECLARE1 (img, IMAGEP);
rep_DECLARE (2, w, rep_INTP (w) && rep_INT (w) > 0);
rep_DECLARE (3, h, rep_INTP (h) && rep_INT (h) > 0);
/* XXX don't these handle borders differently..? */
#if defined HAVE_IMLIB
copy = Imlib_clone_scaled_image (imlib_id, VIMAGE (img)->image,
rep_INT (w), rep_INT (h));
#elif defined HAVE_GDK_PIXBUF
copy = gdk_pixbuf_scale_simple (VIMAGE (img)->image,
rep_INT (w), rep_INT (h),
interp_type);
#endif
if (copy != 0)
return make_image (copy, Fcopy_sequence (VIMAGE (img)->plist));
else
return rep_mem_error ();
}
DEFUN ("composite-images", Fcomposite_images,
Scomposite_images, (repv img1, repv img2, repv x, repv y), rep_Subr4) /*
::doc:sawfish.wm.images#composite-images::
composite-images DEST-IMAGE SRC-IMAGE [X Y]
Modify DEST-IMAGE by compositing SRC-IMAGE onto its current contents,
at position (X, Y), or (0, 0) if no position is given.
::end:: */
{
int w1, h1, w2, h2, copy_w, copy_h;
rep_DECLARE1 (img1, IMAGEP);
rep_DECLARE2 (img2, IMAGEP);
if (x == Qnil)
x = rep_MAKE_INT (0);
else
rep_DECLARE (3, x, rep_INTP (x) && rep_INT (x) >= 0);
if (y == Qnil)
y = rep_MAKE_INT (0);
else
rep_DECLARE (4, y, rep_INTP (y) && rep_INT (y) >= 0);
w1 = image_width (VIMAGE (img1));
h1 = image_height (VIMAGE (img1));
w2 = image_width (VIMAGE (img2));
h2 = image_height (VIMAGE (img2));
copy_w = MIN (w2, w1 - rep_INT (x));
copy_h = MIN (h2, h1 - rep_INT (y));
#if defined HAVE_IMLIB
{
u_char *img1_rgb = image_pixels (VIMAGE (img1));
u_char *img2_rgb = image_pixels (VIMAGE (img2));
int row, col;
ImlibColor shape;
Imlib_get_image_shape (imlib_id, VIMAGE (img2)->image, &shape);
for (row = 0; row < copy_h; row++)
{
for (col = 0; col < copy_w; col++)
{
u_char *img2_pixel = img2_rgb + (row * w2 + col) * 3;
if (img2_pixel[0] != shape.r
|| img2_pixel[1] != shape.g
|| img2_pixel[2] != shape.b)
{
u_char *img1_pixel;
img1_pixel = img1_rgb + (((rep_INT (y) + row) * w1)
+ (rep_INT (x) + col)) * 3;
/* constant size, so should be inlined */
memcpy (img1_pixel, img2_pixel, 3);
}
}
}
}
#elif defined HAVE_GDK_PIXBUF
gdk_pixbuf_composite (VIMAGE (img2)->image, VIMAGE (img1)->image,
rep_INT (x), rep_INT (y),
copy_w, copy_h, rep_INT (x), rep_INT (y), 1.0, 1.0,
interp_type, 255);
#endif
image_changed (VIMAGE (img1));
return img1;
}
DEFUN ("crop-image", Fcrop_image, Scrop_image,
(repv img, repv x, repv y, repv w, repv h), rep_Subr5) /*
::doc:sawfish.wm.images#crop-image::
crop-image IMAGE X Y WIDTH HEIGHT
Return a new image, with dimensions WIDTH by HEIGHT. A copy of the
contents of IMAGE starting at position (X, Y).
::end:: */
{
int img_w, img_h;
image_t out;
rep_DECLARE1 (img, IMAGEP);
img_w = image_width (VIMAGE (img));
img_h = image_height (VIMAGE (img));
rep_DECLARE (2, x, rep_INTP (x) && rep_INT (x) >= 0);
rep_DECLARE (3, y, rep_INTP (y) && rep_INT (y) >= 0);
rep_DECLARE (4, w, rep_INTP (w) && rep_INT (w) > 0
&& rep_INT (w) <= (img_w - rep_INT (x)));
rep_DECLARE (5, h, rep_INTP (h) && rep_INT (h) > 0
&& rep_INT (h) <= (img_h - rep_INT (y)));
#if defined HAVE_IMLIB
out = Imlib_crop_and_clone_image (imlib_id, VIMAGE (img)->image,
rep_INT (x), rep_INT (y),
rep_INT (w), rep_INT (h));
#elif defined HAVE_GDK_PIXBUF
{
GdkPixbuf *in = VIMAGE (img)->image;
out = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (in),
gdk_pixbuf_get_has_alpha (in),
gdk_pixbuf_get_bits_per_sample (in),
rep_INT (w), rep_INT (h));
if (out != 0)
{
gdk_pixbuf_copy_area (in, rep_INT (x), rep_INT (y),
rep_INT (w), rep_INT (h),
out, 0, 0);
}
}
#endif
if (out != 0)
return make_image (out, Fcopy_sequence (VIMAGE (img)->plist));
else
return rep_mem_error ();
}
/* image structure accessors */
int
image_width (Lisp_Image *im)
{
#if defined HAVE_IMLIB
return im->image->rgb_width;
#elif defined HAVE_GDK_PIXBUF
return gdk_pixbuf_get_width (im->image);
#endif
}
int
image_height (Lisp_Image *im)
{
#if defined HAVE_IMLIB
return im->image->rgb_height;
#elif defined HAVE_GDK_PIXBUF
return gdk_pixbuf_get_height (im->image);
#endif
}
u_char *
image_pixels (Lisp_Image *im)
{
#if defined HAVE_IMLIB
return im->image->rgb_data;
#elif defined HAVE_GDK_PIXBUF
return gdk_pixbuf_get_pixels (im->image);
#endif
}
int
image_row_stride (Lisp_Image *im)
{
#if defined HAVE_IMLIB
return im->image->rgb_width * 3;
#elif defined HAVE_GDK_PIXBUF
return gdk_pixbuf_get_rowstride (im->image);
#endif
}
int
image_channels (Lisp_Image *im)
{
#if defined HAVE_IMLIB
return 3;
#elif defined HAVE_GDK_PIXBUF
return gdk_pixbuf_get_n_channels (im->image);
#endif
}
void
image_changed (Lisp_Image *im)
{
#if defined NEED_PIXMAP_CACHE
pixmap_cache_flush_image (im);
#endif
#if defined HAVE_IMLIB
Imlib_changed_image (imlib_id, im->image);
#endif
}
/* render functions */
#if defined HAVE_GDK_PIXBUF
static void
do_scale (GdkPixbuf *src, int src_x, int src_y, int src_w, int src_h,
GdkPixbuf *dst, int dst_x, int dst_y, int dst_w, int dst_h)
{
double scale_x, scale_y;
double off_x, off_y;
if (src_w <= 0 || src_h <= 0 || dst_w <= 0 || dst_h <= 0)
return;
scale_x = dst_w / (double) src_w;
scale_y = dst_h / (double) src_h;
off_x = dst_x - scale_x * src_x;
off_y = dst_y - scale_y * src_y;
gdk_pixbuf_scale (src, dst,
dst_x, dst_y,
dst_w, dst_h,
off_x, off_y,
scale_x, scale_y,
interp_type);
}
#endif
void
image_render (Lisp_Image *image, int width, int height,
Pixmap *pixmap, Pixmap *mask)
{
image_t im = image->image;
assert (width > 0);
assert (height > 0);
#if defined HAVE_IMLIB
Imlib_render (imlib_id, im, width, height);
if (pixmap != 0)
*pixmap = Imlib_move_image (imlib_id, im);
if (mask != 0)
*mask = Imlib_move_mask (imlib_id, im);
#elif defined HAVE_GDK_PIXBUF
{
GdkPixbuf *scaled = im;
bool need_to_unref = FALSE;
int im_width = image_width (image), im_height = image_height (image);
if (pixmap_cache_ref (image, width, height, pixmap, mask))
return;
/* XXX handle cases where combined image borders are larger
XXX than the destination image.. */
if (im_width != width || im_height != height)
{
/* need to scale to width by height */
int border[4];
border[0] = image->border[0];
border[1] = image->border[1];
border[2] = image->border[2];
border[3] = image->border[3];
/* truncate borders if dest image is too small */
if (border[0] + border[1] > width)
{
border[0] = MIN (border[0], width / 2);
border[1] = MIN (border[1], width / 2);
}
if (border[2] + border[3] > height
|| image->border[2] + image->border[3] >= im_height)
{
border[2] = MIN (border[2], height / 2);
border[3] = MIN (border[3], height / 2);
}
assert (border[0] + border[1] <= width);
assert (border[2] + border[3] <= height);
/* create a new buffer */
scaled = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (im),
gdk_pixbuf_get_has_alpha (im),
gdk_pixbuf_get_bits_per_sample (im),
width, height);
need_to_unref = TRUE;
/* stretch borders to fit scaled image */
if (border[0] > 0)
{
do_scale (im, 0, image->border[2], image->border[0],
im_height - (image->border[2] + image->border[3]),
scaled, 0, border[2], border[0],
height - (border[2] + border[3]));
}
if (border[1] > 0)
{
do_scale (im, im_width - image->border[1], image->border[2],
image->border[1],
im_height - (image->border[2] + image->border[3]),
scaled, width - border[1], border[2], border[1],
height - (border[2] + border[3]));
}
if (border[2] > 0)
{
do_scale (im, image->border[0], 0,
im_width - (image->border[0] + image->border[1]),
image->border[2],
scaled, border[0], 0,
width - (border[0] + border[1]), border[2]);
}
if (border[3] > 0)
{
do_scale (im, image->border[0], im_height - image->border[3],
im_width - (image->border[0] + image->border[1]),
image->border[3],
scaled, border[0], height - border[3],
width - (border[0] + border[1]), border[3]);
}
/* now do corner intersections between borders */
if (border[0] > 0 && border[2] > 0)
{
do_scale (im, 0, 0, image->border[0], image->border[2],
scaled, 0, 0, border[0], border[2]);
}
if (border[1] > 0 && border[2] > 0)
{
do_scale (im, im_width - image->border[1], 0,
image->border[1], image->border[2],
scaled, width - border[1], 0, border[1], border[2]);
}
if (border[0] > 0 && border[3] > 0)
{
do_scale (im, 0, im_height - image->border[3],
image->border[0], image->border[3],
scaled, 0, height - border[3], border[0], border[3]);
}
if (border[1] > 0 && border[3] > 0)
{
do_scale (im, im_width - image->border[1],
im_height - image->border[3],
image->border[1], image->border[3],
scaled, width - border[1], height - border[3],
border[1], border[3]);
}
/* scale the inner parts of the image */
if (border[0] + border[1] < width
|| border[2] + border[3] < height)
{
do_scale (im, image->border[0], image->border[2],
im_width - (image->border[0] + image->border[1]),
im_height - (image->border[2] + image->border[3]),
scaled, border[0], border[2],
width - (border[0] + border[1]),
height - (border[2] + border[3]));
}
}
gdk_pixbuf_xlib_render_pixmap_and_mask (scaled, pixmap, mask, 128);
if (need_to_unref)
gdk_pixbuf_unref (scaled);
pixmap_cache_set (image, width, height, *pixmap, *mask);
}
#endif
/* Imlib sometimes calls XSync (), which could hide events */
rep_mark_input_pending (ConnectionNumber(dpy));
}
void
image_free_pixmaps (Lisp_Image *image, Pixmap pixmap, Pixmap mask)
{
#if defined NEED_PIXMAP_CACHE
pixmap_cache_unref (image, pixmap, mask);
#endif
#if defined HAVE_IMLIB
Imlib_free_pixmap (imlib_id, pixmap);
#endif
}
int
best_color_match (int red, int green, int blue)
{
#if defined HAVE_IMLIB
return Imlib_best_color_match (imlib_id, &red, &green, &blue);
#elif defined HAVE_GDK_PIXBUF
return xlib_rgb_xpixel_from_rgb ((red << 16) | (green << 8) | blue);
#endif
}
void
paste_image_to_drawable (Lisp_Image *img, Drawable d,
int x, int y, int w, int h)
{
#if defined HAVE_IMLIB
Imlib_paste_image (imlib_id, img->image, d, x, y, w, h);
/* Imlib sometimes calls XSync (), which could hide events */
rep_mark_input_pending (ConnectionNumber(dpy));
#elif defined HAVE_GDK_PIXBUF
GC gc;
XGCValues gcv;
int gcmask = 0;
Pixmap pixmap, mask;
image_render (img, w, h, &pixmap, &mask);
gcv.graphics_exposures = False;
gcmask |= GCGraphicsExposures;
if (mask != 0)
{
gcv.clip_mask = mask;
gcv.clip_x_origin = x;
gcv.clip_y_origin = y;
gcmask |= GCClipMask | GCClipXOrigin | GCClipYOrigin;
}
gc = XCreateGC (dpy, d, gcmask, &gcv);
XCopyArea (dpy, pixmap, d, gc, 0, 0, w, h, x, y);
XFreeGC (dpy, gc);
image_free_pixmaps (img, pixmap, mask);
#endif
}
/* lisp functions for accessing image contents */
static repv
get_pixel (Lisp_Image *im, int x, int y)
{
int nchannels = image_channels (im);
u_char *data = (image_pixels (im)
+ (y * image_row_stride (im)) + (x * nchannels));
repv alpha;
#ifdef HAVE_IMLIB
ImlibColor shape;
Imlib_get_image_shape (imlib_id, im->image, &shape);
alpha = (data[0] == shape.r && data[1] == shape.g
&& data[2] == shape.b) ? rep_MAKE_INT (0) : rep_MAKE_INT (255);
#else
alpha = nchannels > 3 ? rep_MAKE_INT (data[3]) : rep_MAKE_INT (255);
#endif
return rep_list_4 (rep_MAKE_INT (data[0]), rep_MAKE_INT (data[1]),
rep_MAKE_INT (data[2]), alpha);
}
static void
set_pixel (Lisp_Image *im, int x, int y, repv pixel)
{
int length = rep_INT (Flength (pixel));
if (length == 3 || length == 4)
{
int nchannels = image_channels (im);
int alpha = (length > 3) ? rep_INT (rep_CADDDR (pixel)) : 255;
u_char *data;
#ifdef HAVE_GDK_PIXBUF
if (alpha != 255 && nchannels < 4)
{
GdkPixbuf *new = gdk_pixbuf_add_alpha (im->image, FALSE, 0, 0, 0);
if (new != 0)
{
gdk_pixbuf_unref (im->image);
im->image = new;
nchannels = image_channels (im);
}
}
#endif
data = (image_pixels (im)
+ (y * image_row_stride (im)) + (x * nchannels));
#ifdef HAVE_IMLIB
if (length > 3 && rep_INT (rep_CADDDR (pixel)) < 128)
{
/* transparent */
ImlibColor shape;
Imlib_get_image_shape (imlib_id, im->image, &shape);
data[0] = shape.r;
data[1] = shape.g;
data[2] = shape.b;
}
else
#endif
{
data[0] = rep_INT (rep_CAR (pixel));
data[1] = rep_INT (rep_CADR (pixel));
data[2] = rep_INT (rep_CADDR (pixel));
if (nchannels > 3)
data[3] = alpha;
}
}
}
DEFUN("image-ref", Fimage_ref, Simage_ref,
(repv im, repv x, repv y), rep_Subr3) /*
::doc:sawfish.wm.images#image-ref::
image-ref IMAGE X Y
Return a list `(R G B A)', the red, green, blue and alpha components of
the pixel at position (X, Y) of IMAGE.
All values are in the range 0 to 255 inclusive.
::end:: */
{
int width, height;
rep_DECLARE1 (im, IMAGEP);
width = image_width (VIMAGE (im));
height = image_height (VIMAGE (im));
rep_DECLARE (2, x, rep_INTP (x) && rep_INT (x) >= 0
&& rep_INT (x) < width);
rep_DECLARE (3, y, rep_INTP (y) && rep_INT (y) >= 0
&& rep_INT (y) < height);
return get_pixel (VIMAGE (im), rep_INT (x), rep_INT (y));
}
DEFUN ("image-set", Fimage_set, Simage_set,
(repv im, repv x, repv y, repv pixel), rep_Subr4) /*
::doc:sawfish.wm.images#image-set::
image-set IMAGE X Y PIXEL
Set the pixel at position (X, Y) in IMAGE to PIXEL. PIXEL is a list of
four numbers, `(R G B A)', the red, green, blue and alpha components of
the pixel. All values are in the range 0 to 255 inclusive.
::end:: */
{
int width, height;
rep_DECLARE1 (im, IMAGEP);
width = image_width (VIMAGE (im));
height = image_height (VIMAGE (im));
rep_DECLARE (2, x, rep_INTP (x) && rep_INT (x) >= 0
&& rep_INT (x) < width);
rep_DECLARE (3, y, rep_INTP (y) && rep_INT (y) >= 0
&& rep_INT (y) < height);
rep_DECLARE (4, pixel, rep_LISTP (pixel));
set_pixel (VIMAGE (im), rep_INT (x), rep_INT (y), pixel);
image_changed (VIMAGE (im));
return Qnil;
}
DEFUN ("image-map", Fimage_map, Simage_map, (repv fun, repv im), rep_Subr2) /*
::doc:sawfish.wm.images#image-map::
image-map XFORM IMAGE
Transform the values of all pixels in IMAGE according to XFORM.
XFORM is a function taking a single argument, the four element list
representing the current pixel value (as in `image-ref'). If XFORM
returns a non-nil value it should be the new pixel value (another four
element list).
::end:: */
{
int width, height;
int x, y;
rep_GC_root gc_im, gc_fun;
rep_DECLARE1 (im, IMAGEP);
width = image_width (VIMAGE (im));
height = image_height (VIMAGE (im));
rep_DECLARE (2, fun, Ffunctionp (fun) != Qnil);
rep_PUSHGC (gc_im, im);
rep_PUSHGC (gc_fun, fun);
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
repv pix = rep_call_lisp1 (fun, get_pixel (VIMAGE (im), x, y));
if (pix == rep_NULL)
{
rep_POPGC; rep_POPGC;
return rep_NULL;
}
if (rep_CONSP (pix))
set_pixel (VIMAGE (im), x, y, pix);
}
}
image_changed (VIMAGE (im));
rep_POPGC; rep_POPGC;
return Qnil;
}
DEFUN ("image-fill", Fimage_fill, Simage_fill,
(repv fun, repv im), rep_Subr2) /*
::doc:sawfish.wm.images#image-fill::
image-fill GENERATOR IMAGE
Set the values of all pixels in IMAGE according to GENERATOR.
GENERATOR is a function taking two arguments, the coordinates of a
pixel in the image. It should return the pixel value to set the value
of the specified pixel to (as accepted by `image-set').
::end:: */
{
int width, height;
int x, y;
rep_GC_root gc_im, gc_fun;
rep_DECLARE1 (im, IMAGEP);
width = image_width (VIMAGE (im));
height = image_height (VIMAGE (im));
rep_DECLARE (2, fun, Ffunctionp (fun) != Qnil);
rep_PUSHGC (gc_im, im);
rep_PUSHGC (gc_fun, fun);
for (y = 0; y < height; y++)
{
for (x = 0; x < width; x++)
{
repv pix = rep_call_lisp2 (fun, rep_MAKE_INT (x),
rep_MAKE_INT (y));
if (pix == rep_NULL)
{
rep_POPGC; rep_POPGC;
return rep_NULL;
}
if (rep_CONSP (pix))
set_pixel (VIMAGE (im), x, y, pix);
}
}
image_changed (VIMAGE (im));
rep_POPGC; rep_POPGC;
return Qnil;
}
/* type hooks */
static int
image_cmp (repv w1, repv w2)
{
return w1 != w2;
}
static void
image_prin (repv stream, repv obj)
{
char buf[256];
sprintf (buf, "#<image %dx%d>",
image_width (VIMAGE (obj)), image_height (VIMAGE (obj)));
rep_stream_puts (stream, buf, -1, FALSE);
}
static void
image_mark (repv obj)
{
rep_MARKVAL(VIMAGE(obj)->plist);
}
static void
image_sweep (void)
{
Lisp_Image *w = image_list;
image_list = 0;
while (w != 0)
{
Lisp_Image *next = w->next;
if (!rep_GC_CELL_MARKEDP(rep_VAL(w)))
{
#if defined NEED_PIXMAP_CACHE
pixmap_cache_flush_image (w);
#endif
#if defined HAVE_IMLIB
Imlib_kill_image (imlib_id, w->image);
#elif defined HAVE_GDK_PIXBUF
gdk_pixbuf_unref (w->image);
#endif
rep_FREE_CELL(w);
}
else
{
rep_GC_CLR_CELL(rep_VAL(w));
w->next = image_list;
image_list = w;
}
w = next;
}
}
/* initialisation */
void
images_init (void)
{
repv tem;
image_type = rep_register_new_type ("image", image_cmp, image_prin,
image_prin, image_sweep, image_mark,
0, 0, 0, 0, 0, 0, 0);
if (!batch_mode_p ())
{
#if defined HAVE_IMLIB
ImlibInitParams params;
params.visualid = preferred_visual->visualid;
params.flags = PARAMS_VISUALID;
imlib_id = Imlib_init_with_params (dpy, ¶ms);
if (imlib_id == 0)
{
fprintf (stderr, "sawfish: can't initialize Imlib\n");
exit (1);
}
image_cmap = Imlib_get_colormap (imlib_id);
image_visual = Imlib_get_visual (imlib_id);
image_depth = imlib_id->x.depth;
#elif defined HAVE_GDK_PIXBUF
gdk_pixbuf_xlib_init (dpy, screen_num);
image_cmap = xlib_rgb_get_cmap ();
image_visual = xlib_rgb_get_visual ();
image_depth = xlib_rgb_get_depth ();
#endif
}
tem = rep_push_structure ("sawfish.wm.images");
rep_ADD_SUBR(Smake_image);
rep_ADD_SUBR(Smake_image_from_x_drawable);
rep_ADD_SUBR(Scopy_image);
rep_ADD_SUBR(Sflip_image_horizontally);
rep_ADD_SUBR(Sflip_image_vertically);
rep_ADD_SUBR(Sflip_image_diagonally);
rep_ADD_SUBR(Simage_get);
rep_ADD_SUBR(Simage_put);
rep_ADD_SUBR(Simagep);
rep_ADD_SUBR(Simage_dimensions);
rep_ADD_SUBR(Simage_border);
rep_ADD_SUBR(Sset_image_shape_color);
rep_ADD_SUBR(Simage_shape_color);
rep_ADD_SUBR(Sset_image_border);
rep_ADD_SUBR(Simage_modifier);
rep_ADD_SUBR(Sset_image_modifier);
rep_ADD_SUBR(Smake_sized_image);
rep_ADD_SUBR(Sbevel_image);
rep_ADD_SUBR(Sclear_image);
rep_ADD_SUBR(Stile_image);
rep_ADD_SUBR(Sscale_image);
rep_ADD_SUBR(Scomposite_images);
rep_ADD_SUBR(Scrop_image);
rep_ADD_SUBR(Simage_ref);
rep_ADD_SUBR(Simage_set);
rep_ADD_SUBR(Simage_map);
rep_ADD_SUBR(Simage_fill);
rep_pop_structure (tem);
rep_INTERN_SPECIAL(image_directory);
Fset (Qimage_directory,
rep_concat2 (rep_STR (Fsymbol_value (Qsawfish_directory, Qt)),
"/images"));
rep_INTERN_SPECIAL(image_load_path);
Fset (Qimage_load_path,
rep_list_2 (rep_string_dup("."),
Fsymbol_value (Qimage_directory, Qt)));
rep_INTERN_SPECIAL(default_bevel_percent);
Fset (Qdefault_bevel_percent, rep_MAKE_INT(50));
rep_INTERN(red);
rep_INTERN(green);
rep_INTERN(blue);
}
void
images_kill (void)
{
}
syntax highlighted by Code2HTML, v. 0.9.1