diff options
Diffstat (limited to 'eazel-engine/src/eazel-theme-gradient.c')
-rw-r--r-- | eazel-engine/src/eazel-theme-gradient.c | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/eazel-engine/src/eazel-theme-gradient.c b/eazel-engine/src/eazel-theme-gradient.c new file mode 100644 index 0000000..fad6d4c --- /dev/null +++ b/eazel-engine/src/eazel-theme-gradient.c @@ -0,0 +1,360 @@ +/* eazel-theme-gradient.c -- code for drawing gradients + + Copyright (C) 2000 Eazel, Inc. + + This program 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 of the + License, or (at your option) any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id: eazel-theme-gradient.c,v 1.6 2001/01/18 01:20:04 jsh Exp $ + + Authors: John Harper <jsh@eazel.com> */ + +/* 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 "eazel-theme.h" +#include <gdk/gdkrgb.h> +#include <stdlib.h> + +static GdkRgbDither dither_mode = GDK_RGB_DITHER_MAX; + +eazel_engine_gradient * +eazel_engine_gradient_new (eazel_engine_gradient_direction direction, + GdkColor *from, GSList *components) +{ + eazel_engine_gradient *g; + + g_return_val_if_fail (from != NULL, NULL); + + g = g_new (eazel_engine_gradient, 1); + g->refcount = 1; + g->direction = direction; + g->from = *from; + g->components = components; + return g; +} + +eazel_engine_gradient * +eazel_engine_gradient_ref (eazel_engine_gradient *g) +{ + g_return_val_if_fail (g != NULL, NULL); + + g->refcount++; + return g; +} + +void +eazel_engine_gradient_unref (eazel_engine_gradient *g) +{ + g_return_if_fail (g != NULL); + g->refcount--; + + if (g->refcount == 0) + { + GSList *x; + for (x = g->components; x != 0; x = x->next) + g_free (x->data); + g_slist_free (g->components); + g_free (g); + } +} + +eazel_engine_gradient * +eazel_engine_make_two_point_gradient (eazel_engine_gradient_direction direction, + gulong from_rgb, gulong to_rgb) +{ + GdkColor from, to; + eazel_engine_gradient_component *component; + + g_return_val_if_fail (direction != GRADIENT_NONE, NULL); + + from.red = (from_rgb >> 16) & 255; + from.red |= from.red << 8; + from.green = (from_rgb >> 8) & 255; + from.green |= from.green << 8; + from.blue = (from_rgb >> 0) & 255; + from.blue |= from.blue << 8; + + to.red = (to_rgb >> 16) & 255; + to.red |= to.red << 8; + to.green = (to_rgb >> 8) & 255; + to.green |= to.green << 8; + to.blue = (to_rgb >> 0) & 255; + to.blue |= to.blue << 8; + + component = g_new (eazel_engine_gradient_component, 1); + component->color = to; + component->weight = 1.0; + + return eazel_engine_gradient_new (direction, &from, + g_slist_prepend (NULL, component)); +} + + +/* rendering */ + +static inline void +fill_gradient_rgb_buffer_1 (const GdkColor *from, const GdkColor *to, + int rgb_total, guchar *rgb_buf, + int rgb_first, int rgb_last) +{ + int delta_r = to->red - from->red; + int delta_g = to->green - from->green; + int delta_b = to->blue - from->blue; + guchar *rgb_ptr = rgb_buf; + int i; + + g_return_if_fail (rgb_first <= rgb_last && rgb_last <= rgb_total); + + for (i = rgb_first; i < rgb_last; i++) + { + *rgb_ptr++ = (from->red + (delta_r * i) / rgb_total) >> 8; + *rgb_ptr++ = (from->green + (delta_g * i) / rgb_total) >> 8; + *rgb_ptr++ = (from->blue + (delta_b * i) / rgb_total) >> 8; + } +} + +void +eazel_engine_fill_gradient_rgb_buffer (const eazel_engine_gradient *gradient, + int rgb_total, guchar *rgb_buf, + int rgb_first, int rgb_last) +{ + g_return_if_fail (gradient != NULL); + g_return_if_fail (rgb_buf != NULL); + + if (gradient->components == 0) + { + /* Single color `gradient' */ + fill_gradient_rgb_buffer_1 (&gradient->from, &gradient->from, + rgb_total, rgb_buf, rgb_first, rgb_last); + } + else + { + float total_weight, weight_ptr; + int rgb_ptr; + GSList *x; + const GdkColor *pred; + + total_weight = 0.0; + for (x = gradient->components; x != 0; x = x->next) + { + eazel_engine_gradient_component *c = x->data; + total_weight += c->weight; + } + + rgb_ptr = 0; + weight_ptr = 0.0; + pred = &gradient->from; + for (x = gradient->components; x != 0; x = x->next) + { + eazel_engine_gradient_component *c = x->data; + int rgb_chunk = (c->weight * rgb_total) / total_weight; + + int first = MAX (rgb_first, rgb_ptr); + int last = MIN (rgb_last, rgb_ptr + rgb_chunk); + + if (x->next == 0) + last = rgb_last; + + if (last > first) + { + fill_gradient_rgb_buffer_1 (pred, &c->color, + last - first, + rgb_buf + rgb_ptr * 3, + first - rgb_ptr, + last - rgb_ptr); + } + + pred = &c->color; + weight_ptr += c->weight; + rgb_ptr += rgb_chunk; + } + } +} + +static void +draw_vertical_gradient (GdkDrawable *drawable, GdkGC *gc, + const GdkRectangle *full_rect, + const GdkRectangle *clip_rect, + const eazel_engine_gradient *gradient) +{ + int rgb_size = clip_rect->height; + guchar *rgb = alloca (rgb_size * 3), *ptr; + + eazel_engine_fill_gradient_rgb_buffer (gradient, full_rect->height, rgb, + clip_rect->y - full_rect->y, + (clip_rect->y + clip_rect->height) + - full_rect->y); + + if (dither_mode == GDK_RGB_DITHER_NONE) + { + GdkColormap *sys_lut = gdk_colormap_get_system (); + GdkGCValues old_values; + int y; + + gdk_gc_get_values (gc, &old_values); + + ptr = rgb; + for (y = clip_rect->y; y < clip_rect->y + clip_rect->height; y++) + { + guchar r = *ptr++; + guchar g = *ptr++; + guchar b = *ptr++; + GdkColor color = { 0, r << 8, g << 8, b << 8 }; + gdk_colormap_alloc_color (sys_lut, &color, FALSE, TRUE); + gdk_gc_set_foreground (gc, &color); + gdk_draw_line (drawable, gc, clip_rect->x, y, + clip_rect->x + clip_rect->width - 1, y); + } + + gdk_gc_set_foreground (gc, &old_values.foreground); + } + else + { + guchar *xrgb = alloca (clip_rect->width * clip_rect->height * 3); + int x, y; + guchar *ptr_in = rgb, *ptr_out = xrgb; + for (y = 0; y < clip_rect->height; y++) + { + guchar r = *ptr_in++; + guchar g = *ptr_in++; + guchar b = *ptr_in++; + for (x = 0; x < clip_rect->width; x++) + { + *ptr_out++ = r; + *ptr_out++ = g; + *ptr_out++ = b; + } + } + gdk_draw_rgb_image (drawable, gc, clip_rect->x, clip_rect->y, + clip_rect->width, clip_rect->height, + dither_mode, xrgb, clip_rect->width * 3); + } +} + +static void +draw_horizontal_gradient (GdkDrawable *drawable, GdkGC *gc, + const GdkRectangle *full_rect, + const GdkRectangle *clip_rect, + const eazel_engine_gradient *gradient) +{ + int rgb_size = clip_rect->width; + guchar *rgb = alloca (rgb_size * 3), *ptr; + + eazel_engine_fill_gradient_rgb_buffer (gradient, full_rect->width, rgb, + clip_rect->x - full_rect->x, + (clip_rect->x + clip_rect->width) + - full_rect->x); + + if (dither_mode == GDK_RGB_DITHER_NONE) + { + GdkColormap *sys_lut = gdk_colormap_get_system (); + GdkGCValues old_values; + int x; + + gdk_gc_get_values (gc, &old_values); + + ptr = rgb; + for (x = clip_rect->x; x < clip_rect->x + clip_rect->width; x++) + { + guchar r = *ptr++; + guchar g = *ptr++; + guchar b = *ptr++; + GdkColor color = { 0, r << 8, g << 8, b << 8 }; + gdk_colormap_alloc_color (sys_lut, &color, FALSE, TRUE); + gdk_gc_set_foreground (gc, &color); + gdk_draw_line (drawable, gc, x, clip_rect->y, + x, clip_rect->y + clip_rect->height - 1); + } + + gdk_gc_set_foreground (gc, &old_values.foreground); + } + else + { + /* Using a row-stride of zero means we only need a single + row of rgb-data (inspired by nautilus-background.c) */ + gdk_draw_rgb_image (drawable, gc, clip_rect->x, clip_rect->y, + clip_rect->width, clip_rect->height, + dither_mode, rgb, 0); + } +} + +void +eazel_engine_draw_gradient (GdkDrawable *drawable, + GdkGC *gc, + const GdkRectangle *full_rect, + const GdkRectangle *clip_rect, + const eazel_engine_gradient *gradient) +{ + if (gradient->direction == GRADIENT_VERTICAL) + { + draw_vertical_gradient (drawable, gc, full_rect, clip_rect, gradient); + } + else if (gradient->direction == GRADIENT_HORIZONTAL) + { + draw_horizontal_gradient (drawable, gc, full_rect, clip_rect, gradient); + } +} + +void +eazel_engine_set_bg_gradient (GdkWindow *window, + eazel_engine_gradient *gradient) +{ + /* Render a tile of the specified gradient to a pixmap, then + set it as the background pixmap of the window. */ + + GdkRectangle area = { 0, 0 }; + GdkPixmap *pixmap; + GdkGC *pixmap_gc; + + int window_x, window_y, window_width, window_height, window_depth; + + gdk_window_get_geometry (window, &window_x, &window_y, + &window_width, &window_height, &window_depth); + + if (gradient->direction == GRADIENT_VERTICAL) + { + area.width = 32; + area.height = window_height; + } + else + { + area.width = window_width; + area.height = 32; + } + + pixmap = gdk_pixmap_new (window, area.width, area.height, window_depth); + pixmap_gc = gdk_gc_new (pixmap); + + eazel_engine_draw_gradient (pixmap, pixmap_gc, &area, &area, gradient); + + gdk_window_set_back_pixmap (window, pixmap, FALSE); + gdk_gc_unref (pixmap_gc); + gdk_pixmap_unref (pixmap); +} |