/* images.c -- Image handling $Id: images.c,v 1.59 2003/01/12 20:35:22 jsh Exp $ Copyright (C) 1999, 2000 John Harper 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 #endif #ifndef __GNUC__ # if HAVE_ALLOCA_H # include # else # ifdef _AIX #pragma alloca # else # ifndef alloca /* predefined by HP cc +Olibcalls */ char *alloca (); # endif # endif # endif #endif #include "sawmill.h" #include 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_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) { }