/* Metacity gradient rendering */ /* * Copyright (C) 2001 Havoc Pennington, 99% copied from wrlib in * WindowMaker, Copyright (C) 1997-2000 Dan Pascu and Alfredo Kojima * * 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., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "gradient.h" #include /* This is all Alfredo's and Dan's usual very nice WindowMaker code, * slightly GTK-ized */ static GdkPixbuf* meta_gradient_create_horizontal (int width, int height, const GdkColor *from, const GdkColor *to); static GdkPixbuf* meta_gradient_create_vertical (int width, int height, const GdkColor *from, const GdkColor *to); static GdkPixbuf* meta_gradient_create_diagonal (int width, int height, const GdkColor *from, const GdkColor *to); static GdkPixbuf* meta_gradient_create_multi_horizontal (int width, int height, const GdkColor *colors, int count); static GdkPixbuf* meta_gradient_create_multi_vertical (int width, int height, const GdkColor *colors, int count); static GdkPixbuf* meta_gradient_create_multi_diagonal (int width, int height, const GdkColor *colors, int count); /* Used as the destroy notification function for gdk_pixbuf_new() */ static void free_buffer (guchar *pixels, gpointer data) { g_free (pixels); } static GdkPixbuf* blank_pixbuf (int width, int height, gboolean no_padding) { guchar *buf; int rowstride; g_return_val_if_fail (width > 0, NULL); g_return_val_if_fail (height > 0, NULL); if (no_padding) rowstride = width * 3; else /* Always align rows to 32-bit boundaries */ rowstride = 4 * ((3 * width + 3) / 4); buf = g_try_malloc (height * rowstride); if (!buf) return NULL; return gdk_pixbuf_new_from_data (buf, GDK_COLORSPACE_RGB, FALSE, 8, width, height, rowstride, free_buffer, NULL); } GdkPixbuf* meta_gradient_create_simple (int width, int height, const GdkColor *from, const GdkColor *to, MetaGradientType style) { switch (style) { case META_GRADIENT_HORIZONTAL: return meta_gradient_create_horizontal (width, height, from, to); case META_GRADIENT_VERTICAL: return meta_gradient_create_vertical (width, height, from, to); case META_GRADIENT_DIAGONAL: return meta_gradient_create_diagonal (width, height, from, to); } g_assert_not_reached (); return NULL; } GdkPixbuf* meta_gradient_create_multi (int width, int height, const GdkColor *colors, int n_colors, MetaGradientType style) { if (n_colors > 2) { switch (style) { case META_GRADIENT_HORIZONTAL: return meta_gradient_create_multi_horizontal (width, height, colors, n_colors); case META_GRADIENT_VERTICAL: return meta_gradient_create_multi_vertical (width, height, colors, n_colors); case META_GRADIENT_DIAGONAL: return meta_gradient_create_multi_diagonal (width, height, colors, n_colors); } } else if (n_colors > 1) { return meta_gradient_create_simple (width, height, &colors[0], &colors[1], style); } else if (n_colors > 0) { return meta_gradient_create_simple (width, height, &colors[0], &colors[0], style); } g_assert_not_reached (); return NULL; } /* Interwoven essentially means we have two vertical gradients, * cut into horizontal strips of the given thickness, and then the strips * are alternated. I'm not sure what it's good for, just copied since * WindowMaker had it. */ GdkPixbuf* meta_gradient_create_interwoven (int width, int height, const GdkColor colors1[2], int thickness1, const GdkColor colors2[2], int thickness2) { int i, j, k, l, ll; long r1, g1, b1, dr1, dg1, db1; long r2, g2, b2, dr2, dg2, db2; GdkPixbuf *pixbuf; unsigned char *ptr; unsigned char rr, gg, bb; unsigned char *pixels; int rowstride; pixbuf = blank_pixbuf (width, height, FALSE); if (pixbuf == NULL) return NULL; pixels = gdk_pixbuf_get_pixels (pixbuf); rowstride = gdk_pixbuf_get_rowstride (pixbuf); r1 = colors1[0].red<<8; g1 = colors1[0].green<<8; b1 = colors1[0].blue<<8; r2 = colors2[0].red<<8; g2 = colors2[0].green<<8; b2 = colors2[0].blue<<8; dr1 = ((colors1[1].red-colors1[0].red)<<8)/(int)height; dg1 = ((colors1[1].green-colors1[0].green)<<8)/(int)height; db1 = ((colors1[1].blue-colors1[0].blue)<<8)/(int)height; dr2 = ((colors2[1].red-colors2[0].red)<<8)/(int)height; dg2 = ((colors2[1].green-colors2[0].green)<<8)/(int)height; db2 = ((colors2[1].blue-colors2[0].blue)<<8)/(int)height; for (i=0,k=0,l=0,ll=thickness1; i>16; gg = g1>>16; bb = b1>>16; } else { rr = r2>>16; gg = g2>>16; bb = b2>>16; } for (j=0; jred / 256.0); g0 = (guchar) (from->green / 256.0); b0 = (guchar) (from->blue / 256.0); rf = (guchar) (to->red / 256.0); gf = (guchar) (to->green / 256.0); bf = (guchar) (to->blue / 256.0); r = r0 << 16; g = g0 << 16; b = b0 << 16; dr = ((rf-r0)<<16)/(int)width; dg = ((gf-g0)<<16)/(int)width; db = ((bf-b0)<<16)/(int)width; /* render the first line */ for (i=0; i>16); *(ptr++) = (unsigned char)(g>>16); *(ptr++) = (unsigned char)(b>>16); r += dr; g += dg; b += db; } /* copy the first line to the other lines */ for (i=1; ired / 256.0); g0 = (guchar) (from->green / 256.0); b0 = (guchar) (from->blue / 256.0); rf = (guchar) (to->red / 256.0); gf = (guchar) (to->green / 256.0); bf = (guchar) (to->blue / 256.0); r = r0<<16; g = g0<<16; b = b0<<16; dr = ((rf-r0)<<16)/(int)height; dg = ((gf-g0)<<16)/(int)height; db = ((bf-b0)<<16)/(int)height; for (i=0; i>16; gg = g>>16; bb = b>>16; for (j=0; jred / 256.0); g0 = (guchar) (from->green / 256.0); b0 = (guchar) (from->blue / 256.0); rf = (guchar) (to->red / 256.0); gf = (guchar) (to->green / 256.0); bf = (guchar) (to->blue / 256.0); ptr = gdk_pixbuf_get_pixels (tmp); a = ((float)(width - 1))/((float)(height - 1)); width = width * 3; /* copy the first line to the other lines with corresponding offset */ for (j=0, offset=0.0; j 2, NULL); pixbuf = blank_pixbuf (width, height, FALSE); if (pixbuf == NULL) return NULL; pixels = gdk_pixbuf_get_pixels (pixbuf); rowstride = gdk_pixbuf_get_rowstride (pixbuf); ptr = pixels; if (count > width) count = width; if (count > 1) width2 = width/(count-1); else width2 = width; k = 0; r = colors[0].red << 8; g = colors[0].green << 8; b = colors[0].blue << 8; /* render the first line */ for (i=1; i>16); *ptr++ = (unsigned char)(g>>16); *ptr++ = (unsigned char)(b>>16); r += dr; g += dg; b += db; k++; } r = colors[i].red << 8; g = colors[i].green << 8; b = colors[i].blue << 8; } for (j=k; j>16); *ptr++ = (unsigned char)(g>>16); *ptr++ = (unsigned char)(b>>16); } /* copy the first line to the other lines */ for (i=1; i 2, NULL); pixbuf = blank_pixbuf (width, height, FALSE); if (pixbuf == NULL) return NULL; pixels = gdk_pixbuf_get_pixels (pixbuf); rowstride = gdk_pixbuf_get_rowstride (pixbuf); pad = rowstride - (width * 3); ptr = pixels; if (count > height) count = height; if (count > 1) height2 = height/(count-1); else height2 = height; k = 0; r = colors[0].red << 8; g = colors[0].green << 8; b = colors[0].blue << 8; for (i=1; i>16; gg = g>>16; bb = b>>16; for (x=0; x>16; gg = g>>16; bb = b>>16; if (k 2, NULL); if (width == 1) return meta_gradient_create_multi_vertical (width, height, colors, count); else if (height == 1) return meta_gradient_create_multi_horizontal (width, height, colors, count); pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, width, height); if (pixbuf == NULL) return NULL; pixels = gdk_pixbuf_get_pixels (pixbuf); rowstride = gdk_pixbuf_get_rowstride (pixbuf); if (count > width) count = width; if (count > height) count = height; /* FIXME here again is a bug that requires multi_horizontal * to have width == rowstride */ if (count > 2) tmp = meta_gradient_create_multi_horizontal (2*width-1, 1, colors, count); else /* wrlib multiplies these colors by 256 before passing them in, but * I think it's a bug in wrlib, so changed here. I could be wrong * though, if we notice two-color multi diagonals not working. */ tmp = meta_gradient_create_horizontal (2*width-1, 1, &colors[0], &colors[1]); if (!tmp) { g_object_unref (G_OBJECT (pixbuf)); return NULL; } ptr = gdk_pixbuf_get_pixels (tmp); a = ((float)(width - 1))/((float)(height - 1)); width = width * 3; /* copy the first line to the other lines with corresponding offset */ for (j=0, offset=0; jtype = type; desc->colors = g_new (GdkColor, n_colors); desc->n_colors = n_colors; memcpy (desc->colors, colors, sizeof (GdkColor) * n_colors); return desc; } void meta_gradient_description_free (MetaGradientDescription *desc) { g_return_if_fail (desc != NULL); g_free (desc->colors); g_free (desc); } GdkPixbuf* meta_gradient_description_render (const MetaGradientDescription *desc, int width, int height) { return meta_gradient_create_multi (width, height, desc->colors, desc->n_colors, desc->type); }