1 /* Helper functions for gtk-engines
2  *
3  * Copyright (C) 2006 Andrew Johnson <acjgenius@earthlink.net>
4  * Copyright (C) 2006 Thomas Wood <thos@gnome.org>
5  * Copyright (C) 2006-2007 Benjamin Berg <benjamin@sipsolutions.net>
6  * Copyright (C) 2007 Andrea Cimitan <andrea.cimitan@gmail.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  * Project contact: <gnome-themes-list@gnome.org>
23  *
24  *
25  * Written by Andrew Johnson <acjgenius@earthlink.net>
26  * Written by Benjamin Berg <benjamin@sipsolutions.net>
27  * Written by Thomas Wood <thos@gnome.org>
28  * modified by Andrea Cimitan <andrea.cimitan@gmail.com>
29  *
30  */
31 
32 #include <math.h>
33 #include "general-support.h"
34 #include "cairo-support.h"
35 
36 /***********************************************
37  * ge_hsb_from_color -
38  *
39  *   Get HSB values from RGB values.
40  *
41  *   Modified from Smooth but originated in GTK+
42  ***********************************************/
43 void
ge_hsb_from_color(const CairoColor * color,gdouble * hue,gdouble * saturation,gdouble * brightness)44 ge_hsb_from_color (const CairoColor *color,
45                         gdouble *hue,
46                         gdouble *saturation,
47                         gdouble *brightness)
48 {
49 	gdouble min, max, delta;
50 	gdouble red, green, blue;
51 
52 	red = color->r;
53 	green = color->g;
54 	blue = color->b;
55 
56 	if (red > green)
57 	{
58 		max = MAX(red, blue);
59 		min = MIN(green, blue);
60 	}
61 	else
62 	{
63 		max = MAX(green, blue);
64 		min = MIN(red, blue);
65 	}
66 
67 	*brightness = (max + min) / 2;
68 
69 	if (fabs(max - min) < 0.0001)
70 	{
71 		*hue = 0;
72 		*saturation = 0;
73 	}
74 	else
75 	{
76 		if (*brightness <= 0.5)
77 			*saturation = (max - min) / (max + min);
78 		else
79 			*saturation = (max - min) / (2 - max - min);
80 
81 		delta = max -min;
82 
83 		if (red == max)
84 			*hue = (green - blue) / delta;
85 		else if (green == max)
86 			*hue = 2 + (blue - red) / delta;
87 		else if (blue == max)
88 			*hue = 4 + (red - green) / delta;
89 
90 		*hue *= 60;
91 		if (*hue < 0.0)
92 			*hue += 360;
93 	}
94 }
95 
96 /***********************************************
97  * ge_color_from_hsb -
98  *
99  *   Get RGB values from HSB values.
100  *
101  *   Modified from Smooth but originated in GTK+
102  ***********************************************/
103 #define MODULA(number, divisor) (((gint)number % divisor) + (number - (gint)number))
104 void
ge_color_from_hsb(gdouble hue,gdouble saturation,gdouble brightness,CairoColor * color)105 ge_color_from_hsb (gdouble hue,
106                         gdouble saturation,
107                         gdouble brightness,
108                         CairoColor *color)
109 {
110 	gint i;
111 	gdouble hue_shift[3], color_shift[3];
112 	gdouble m1, m2, m3;
113 
114 	if (!color) return;
115 
116 	if (brightness <= 0.5)
117 		m2 = brightness * (1 + saturation);
118 	else
119 		m2 = brightness + saturation - brightness * saturation;
120 
121 	m1 = 2 * brightness - m2;
122 
123 	hue_shift[0] = hue + 120;
124 	hue_shift[1] = hue;
125 	hue_shift[2] = hue - 120;
126 
127 	color_shift[0] = color_shift[1] = color_shift[2] = brightness;
128 
129 	i = (saturation == 0)?3:0;
130 
131 	for (; i < 3; i++)
132 	{
133 		m3 = hue_shift[i];
134 
135 		if (m3 > 360)
136 			m3 = MODULA(m3, 360);
137 		else if (m3 < 0)
138 			m3 = 360 - MODULA(ABS(m3), 360);
139 
140 		if (m3 < 60)
141 			color_shift[i] = m1 + (m2 - m1) * m3 / 60;
142 		else if (m3 < 180)
143 			color_shift[i] = m2;
144 		else if (m3 < 240)
145 			color_shift[i] = m1 + (m2 - m1) * (240 - m3) / 60;
146 		else
147 			color_shift[i] = m1;
148 	}
149 
150 	color->r = color_shift[0];
151 	color->g = color_shift[1];
152 	color->b = color_shift[2];
153 	color->a = 1.0;
154 }
155 
156 void
ge_gdk_color_to_cairo(const GdkColor * c,CairoColor * cc)157 ge_gdk_color_to_cairo (const GdkColor *c, CairoColor *cc)
158 {
159 	gdouble r, g, b;
160 
161 	g_return_if_fail (c && cc);
162 
163 	r = c->red / 65535.0;
164 	g = c->green / 65535.0;
165 	b = c->blue / 65535.0;
166 
167 	cc->r = r;
168 	cc->g = g;
169 	cc->b = b;
170 	cc->a = 1.0;
171 }
172 
173 void
ge_cairo_color_to_gtk(const CairoColor * cc,GdkColor * c)174 ge_cairo_color_to_gtk (const CairoColor *cc, GdkColor *c)
175 {
176 	gdouble r, g, b;
177 
178 	g_return_if_fail (c && cc);
179 
180 	r = cc->r * 65535.0;
181 	g = cc->g * 65535.0;
182 	b = cc->b * 65535.0;
183 
184 	c->red = r;
185 	c->green = g;
186 	c->blue = b;
187 }
188 
189 void
ge_gtk_style_to_cairo_color_cube(GtkStyle * style,CairoColorCube * cube)190 ge_gtk_style_to_cairo_color_cube (GtkStyle * style, CairoColorCube *cube)
191 {
192 	int i;
193 
194 	g_return_if_fail (style && cube);
195 
196 	for (i = 0; i < 5; i++)
197 	{
198 		ge_gdk_color_to_cairo (&style->bg[i], &cube->bg[i]);
199 		ge_gdk_color_to_cairo (&style->fg[i], &cube->fg[i]);
200 
201 		ge_gdk_color_to_cairo (&style->dark[i], &cube->dark[i]);
202 		ge_gdk_color_to_cairo (&style->light[i], &cube->light[i]);
203 		ge_gdk_color_to_cairo (&style->mid[i], &cube->mid[i]);
204 
205 		ge_gdk_color_to_cairo (&style->base[i], &cube->base[i]);
206 		ge_gdk_color_to_cairo (&style->text[i], &cube->text[i]);
207 		ge_gdk_color_to_cairo (&style->text_aa[i], &cube->text_aa[i]);
208 	}
209 
210 	cube->black.r = cube->black.g = cube->black.b = 0;
211 	cube->black.a = 1;
212 
213 	cube->white.r = cube->white.g = cube->white.b = 1;
214 	cube->white.a = 1;
215 }
216 
217 void
ge_shade_color(const CairoColor * base,gdouble shade_ratio,CairoColor * composite)218 ge_shade_color(const CairoColor *base, gdouble shade_ratio, CairoColor *composite)
219 {
220 	gdouble hue = 0;
221 	gdouble saturation = 0;
222 	gdouble brightness = 0;
223 
224 	g_return_if_fail (base && composite);
225 
226 	if (shade_ratio == 1.0)
227 	{
228 		composite->r = base->r;
229 		composite->g = base->g;
230 		composite->b = base->b;
231 		composite->a = base->a;
232 
233 		return;
234 	}
235 
236 	ge_hsb_from_color (base, &hue, &saturation, &brightness);
237 
238 	brightness = MIN(brightness*shade_ratio, 1.0);
239 	brightness = MAX(brightness, 0.0);
240 
241 	saturation = MIN(saturation*shade_ratio, 1.0);
242 	saturation = MAX(saturation, 0.0);
243 
244 	ge_color_from_hsb (hue, saturation, brightness, composite);
245 	composite->a = base->a;
246 }
247 
248 void
ge_saturate_color(const CairoColor * base,gdouble saturate_level,CairoColor * composite)249 ge_saturate_color (const CairoColor *base, gdouble saturate_level, CairoColor *composite)
250 {
251 	gdouble hue = 0;
252 	gdouble saturation = 0;
253 	gdouble brightness = 0;
254 
255 	g_return_if_fail (base && composite);
256 
257 	ge_hsb_from_color (base, &hue, &saturation, &brightness);
258 
259 	saturation = MIN(saturation*saturate_level, 1.0);
260 	saturation = MAX(saturation, 0.0);
261 
262 	ge_color_from_hsb (hue, saturation, brightness, composite);
263 	composite->a = base->a;
264 }
265 
266 void
ge_mix_color(const CairoColor * color1,const CairoColor * color2,gdouble mix_factor,CairoColor * composite)267 ge_mix_color (const CairoColor *color1, const CairoColor *color2,
268               gdouble mix_factor, CairoColor *composite)
269 {
270 	g_return_if_fail (color1 && color2 && composite);
271 
272 	composite->r = color1->r * (1-mix_factor) + color2->r * mix_factor;
273 	composite->g = color1->g * (1-mix_factor) + color2->g * mix_factor;
274 	composite->b = color1->b * (1-mix_factor) + color2->b * mix_factor;
275 	composite->a = 1.0;
276 }
277 
278 cairo_t *
ge_gdk_drawable_to_cairo(GdkDrawable * window,GdkRectangle * area)279 ge_gdk_drawable_to_cairo (GdkDrawable  *window, GdkRectangle *area)
280 {
281 	cairo_t *cr;
282 
283 	g_return_val_if_fail (window != NULL, NULL);
284 
285 	cr = (cairo_t*) gdk_cairo_create (window);
286 	cairo_set_line_width (cr, 1.0);
287 	cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
288 	cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
289 
290 	if (area)
291 	{
292 		cairo_rectangle (cr, area->x, area->y, area->width, area->height);
293 		cairo_clip_preserve (cr);
294 		cairo_new_path (cr);
295 	}
296 
297 	return cr;
298 }
299 
300 void
ge_cairo_set_color(cairo_t * cr,const CairoColor * color)301 ge_cairo_set_color (cairo_t *cr, const CairoColor *color)
302 {
303 	g_return_if_fail (cr && color);
304 
305 	cairo_set_source_rgba (cr, color->r, color->g, color->b, color->a);
306 }
307 
308 void
ge_cairo_set_gdk_color_with_alpha(cairo_t * cr,const GdkColor * color,gdouble alpha)309 ge_cairo_set_gdk_color_with_alpha (cairo_t *cr, const GdkColor *color, gdouble alpha)
310 {
311 	g_return_if_fail (cr && color);
312 
313 	cairo_set_source_rgba (cr, color->red / 65535.0,
314 	                           color->green / 65535.0,
315 	                           color->blue / 65535.0,
316 	                           alpha);
317 }
318 
319 void
ge_cairo_pattern_add_color_stop_color(cairo_pattern_t * pattern,gfloat offset,const CairoColor * color)320 ge_cairo_pattern_add_color_stop_color (cairo_pattern_t *pattern,
321                                        gfloat offset,
322                                        const CairoColor *color)
323 {
324 	g_return_if_fail (pattern && color);
325 
326 	cairo_pattern_add_color_stop_rgba (pattern, offset, color->r, color->g, color->b, color->a);
327 }
328 
329 void
ge_cairo_pattern_add_color_stop_shade(cairo_pattern_t * pattern,gdouble offset,const CairoColor * color,gdouble shade)330 ge_cairo_pattern_add_color_stop_shade (cairo_pattern_t *pattern,
331                                        gdouble offset,
332                                        const CairoColor *color,
333                                        gdouble shade)
334 {
335 	CairoColor shaded;
336 
337 	g_return_if_fail (pattern && color && (shade >= 0) && (shade <= 3));
338 
339 	shaded = *color;
340 
341 	if (shade != 1)
342 	{
343 		ge_shade_color(color, shade, &shaded);
344 	}
345 
346 	ge_cairo_pattern_add_color_stop_color(pattern, offset, &shaded);
347 }
348 
349 /*
350  * This function will draw a rounded corner at position x,y. If the radius
351  * is very small (or negative) it will instead just do a line_to.
352  * ge_cairo_rounded_corner assumes clockwise drawing.
353  */
354 void
ge_cairo_rounded_corner(cairo_t * cr,double x,double y,double radius,CairoCorners corner)355 ge_cairo_rounded_corner (cairo_t      *cr,
356                          double        x,
357                          double        y,
358                          double        radius,
359                          CairoCorners  corner)
360 {
361 	if (radius < 0.0001)
362 	{
363 		cairo_line_to (cr, x, y);
364 	}
365 	else
366 	{
367 		switch (corner)
368 		{
369 			case CR_CORNER_NONE:
370 				cairo_line_to (cr, x, y);
371 				break;
372 			case CR_CORNER_TOPLEFT:
373 				cairo_arc (cr, x + radius, y + radius, radius, G_PI, G_PI * 3/2);
374 				break;
375 			case CR_CORNER_TOPRIGHT:
376 				cairo_arc (cr, x - radius, y + radius, radius, G_PI * 3/2, G_PI * 2);
377 				break;
378 			case CR_CORNER_BOTTOMRIGHT:
379 				cairo_arc (cr, x - radius, y - radius, radius, 0, G_PI * 1/2);
380 				break;
381 			case CR_CORNER_BOTTOMLEFT:
382 				cairo_arc (cr, x + radius, y - radius, radius, G_PI * 1/2, G_PI);
383 				break;
384 
385 			default:
386 				/* A bitfield and not a sane value ... */
387 				g_assert_not_reached ();
388 				cairo_line_to (cr, x, y);
389 				return;
390 		}
391 	}
392 }
393 
394 void
ge_cairo_rounded_rectangle(cairo_t * cr,double x,double y,double w,double h,double radius,CairoCorners corners)395 ge_cairo_rounded_rectangle (cairo_t *cr,
396                                  double x, double y, double w, double h,
397                                  double radius, CairoCorners corners)
398 {
399 	g_return_if_fail (cr != NULL);
400 
401 	if (radius < 0.0001 || corners == CR_CORNER_NONE)
402 	{
403 		cairo_rectangle (cr, x, y, w, h);
404 		return;
405 	}
406 #ifdef DEVELOPMENT
407 	if ((corners == CR_CORNER_ALL) && (radius > w / 2.0 || radius > h / 2.0))
408 		g_warning ("Radius is too large for width/height in ge_rounded_rectangle.\n");
409 	else if (radius > w || radius > h) /* This isn't perfect. Assumes that only one corner is set. */
410 		g_warning ("Radius is too large for width/height in ge_rounded_rectangle.\n");
411 #endif
412 
413 	if (corners & CR_CORNER_TOPLEFT)
414 		cairo_move_to (cr, x+radius, y);
415 	else
416 		cairo_move_to (cr, x, y);
417 
418 	if (corners & CR_CORNER_TOPRIGHT)
419 		cairo_arc (cr, x+w-radius, y+radius, radius, G_PI * 1.5, G_PI * 2);
420 	else
421 		cairo_line_to (cr, x+w, y);
422 
423 	if (corners & CR_CORNER_BOTTOMRIGHT)
424 		cairo_arc (cr, x+w-radius, y+h-radius, radius, 0, G_PI * 0.5);
425 	else
426 		cairo_line_to (cr, x+w, y+h);
427 
428 	if (corners & CR_CORNER_BOTTOMLEFT)
429 		cairo_arc (cr, x+radius,   y+h-radius, radius, G_PI * 0.5, G_PI);
430 	else
431 		cairo_line_to (cr, x, y+h);
432 
433 	if (corners & CR_CORNER_TOPLEFT)
434 		cairo_arc (cr, x+radius,   y+radius,   radius, G_PI, G_PI * 1.5);
435 	else
436 		cairo_line_to (cr, x, y);
437 }
438 
439 
440 /* ge_cairo_stroke_rectangle.
441  *
442  *   A simple function to stroke the rectangle { x, y, w, h}.
443  *   (This function only exists because of a cairo performance bug that
444  *   has been fixed and it may be a good idea to get rid of it again.)
445  */
446 void
ge_cairo_stroke_rectangle(cairo_t * cr,double x,double y,double w,double h)447 ge_cairo_stroke_rectangle (cairo_t *cr, double x, double y, double w, double h)
448 {
449 	cairo_rectangle (cr, x, y, w, h);
450 	cairo_stroke (cr);
451 }
452 
453 void
ge_cairo_inner_rectangle(cairo_t * cr,double x,double y,double width,double height)454 ge_cairo_inner_rectangle (cairo_t *cr,
455                           double x, double y,
456                           double width, double height)
457 {
458 	double line_width = cairo_get_line_width (cr);
459 
460 	cairo_rectangle (cr, x + line_width / 2.0,
461 	                     y + line_width / 2.0,
462 	                     width - line_width,
463 	                     height - line_width);
464 }
465 
466 void
ge_cairo_inner_rounded_rectangle(cairo_t * cr,double x,double y,double width,double height,double radius,CairoCorners corners)467 ge_cairo_inner_rounded_rectangle (cairo_t *cr,
468                                   double x, double y,
469                                   double width, double height,
470                                   double radius, CairoCorners corners)
471 {
472 	double line_width = cairo_get_line_width (cr);
473 
474 	ge_cairo_rounded_rectangle (cr,
475                                     x + line_width / 2.0,
476                                     y + line_width / 2.0,
477                                     width - line_width,
478                                     height - line_width,
479                                     radius, corners);
480 }
481 
482 /***********************************************
483  * ge_cairo_simple_border -
484  *
485  *   A simple routine to draw thin squared
486  *   borders with a topleft and bottomright color.
487  *
488  *   It originated in Smooth-Engine.
489  ***********************************************/
490 void
ge_cairo_simple_border(cairo_t * cr,const CairoColor * tl,const CairoColor * br,gint x,gint y,gint width,gint height,gboolean topleft_overlap)491 ge_cairo_simple_border (cairo_t *cr,
492                         const CairoColor * tl, const CairoColor * br,
493                         gint x,	gint y, gint width, gint height,
494                         gboolean topleft_overlap)
495 {
496 	gboolean solid_color;
497 
498 	g_return_if_fail (cr != NULL);
499 	g_return_if_fail (tl != NULL);
500 	g_return_if_fail (br != NULL);
501 
502 
503 	solid_color = (tl == br) || ((tl->r == br->r) && (tl->g == br->g) && (tl->b == br->b) && (tl->a == br->a));
504 
505 	topleft_overlap &= !solid_color;
506 
507 	cairo_save(cr);
508 
509 	cairo_set_line_width (cr, 1);
510 
511 	if (topleft_overlap)
512 	{
513 		ge_cairo_set_color(cr, br);
514 
515 		cairo_move_to(cr, x + 0.5, y + height - 0.5);
516 		cairo_line_to(cr, x + width - 0.5, y + height - 0.5);
517 		cairo_line_to(cr, x + width - 0.5, y + 0.5);
518 
519 		cairo_stroke (cr);
520 	}
521 
522 	ge_cairo_set_color(cr, tl);
523 
524 	cairo_move_to(cr, x + 0.5, y + height - 0.5);
525 	cairo_line_to(cr, x + 0.5, y + 0.5);
526 	cairo_line_to(cr, x + width - 0.5, y + 0.5);
527 
528 	if (!topleft_overlap)
529 	{
530 		if (!solid_color)
531 		{
532 			cairo_stroke(cr);
533 			ge_cairo_set_color(cr, br);
534 		}
535 
536 		cairo_move_to(cr, x + 0.5, y + height - 0.5);
537 		cairo_line_to(cr, x + width - 0.5, y + height - 0.5);
538 		cairo_line_to(cr, x + width - 0.5, y + 0.5);
539 	}
540 
541 	cairo_stroke(cr);
542 
543 	cairo_restore(cr);
544 }
545 
ge_cairo_polygon(cairo_t * cr,const CairoColor * color,GdkPoint * points,gint npoints)546 void ge_cairo_polygon (cairo_t *cr,
547                        const CairoColor *color,
548                        GdkPoint *points,
549                        gint npoints)
550 {
551 	int i = 0;
552 
553 	cairo_save(cr);
554 
555 	ge_cairo_set_color(cr, color);
556 	cairo_move_to(cr, points[0].x, points[0].y);
557 
558 	for (i = 1; i < npoints; i++)
559 	{
560 		if (!((points[i].x == points[i + 1].x) &&
561 		    (points[i].y == points[i + 1].y)))
562 		{
563 			cairo_line_to(cr, points[i].x, points[i].y);
564 		}
565 	}
566 
567 	if ((points[npoints-1].x != points[0].y) ||
568 		(points[npoints-1].y != points[0].y))
569 	{
570 		cairo_line_to(cr, points[0].x, points[0].y);
571 	}
572 
573 	cairo_fill(cr);
574 
575 	cairo_restore(cr);
576 }
577 
ge_cairo_line(cairo_t * cr,const CairoColor * color,gint x1,gint y1,gint x2,gint y2)578 void ge_cairo_line (cairo_t *cr,
579                     const CairoColor *color,
580                     gint x1,
581                     gint y1,
582                     gint x2,
583                     gint y2)
584 {
585 	cairo_save(cr);
586 
587 	ge_cairo_set_color(cr, color);
588 	cairo_set_line_width (cr, 1);
589 
590 	cairo_move_to(cr, x1 + 0.5, y1 + 0.5);
591 	cairo_line_to(cr, x2 + 0.5, y2 + 0.5);
592 
593 	cairo_stroke(cr);
594 
595 	cairo_restore(cr);
596 }
597 
598 void
ge_cairo_mirror(cairo_t * cr,CairoMirror mirror,gint * x,gint * y,gint * width,gint * height)599 ge_cairo_mirror (cairo_t     *cr,
600                  CairoMirror  mirror,
601                  gint        *x,
602                  gint        *y,
603                  gint        *width,
604                  gint        *height)
605 {
606 	cairo_matrix_t matrix;
607 
608 	cairo_matrix_init_identity (&matrix);
609 
610 	cairo_translate (cr, *x, *y);
611 	*x = 0;
612 	*y = 0;
613 
614 	if (mirror & CR_MIRROR_HORIZONTAL)
615 	{
616 		cairo_matrix_scale (&matrix, -1, 1);
617 		*x = -*width;
618 	}
619 	if (mirror & CR_MIRROR_VERTICAL)
620 	{
621 		cairo_matrix_scale (&matrix, 1, -1);
622 		*y = -*height;
623 	}
624 
625 	cairo_transform (cr, &matrix);
626 }
627 
628 void
ge_cairo_exchange_axis(cairo_t * cr,gint * x,gint * y,gint * width,gint * height)629 ge_cairo_exchange_axis (cairo_t  *cr,
630                         gint     *x,
631                         gint     *y,
632                         gint     *width,
633                         gint     *height)
634 {
635 	gint tmp;
636 	cairo_matrix_t matrix;
637 
638 	cairo_translate (cr, *x, *y);
639 	cairo_matrix_init (&matrix, 0, 1, 1, 0, 0, 0);
640 
641 	cairo_transform (cr, &matrix);
642 
643 	/* swap width/height */
644 	tmp = *width;
645 	*x = 0;
646 	*y = 0;
647 	*width = *height;
648 	*height = tmp;
649 }
650 
651 
652 /***********************************************
653  * ge_cairo_pattern_fill -
654  *
655  *   Fill an area with some pattern
656  *   Scaling or tiling if needed
657  ***********************************************/
658 void
ge_cairo_pattern_fill(cairo_t * canvas,CairoPattern * pattern,gint x,gint y,gint width,gint height)659 ge_cairo_pattern_fill(cairo_t *canvas,
660                       CairoPattern *pattern,
661                       gint x,
662                       gint y,
663                       gint width,
664                       gint height)
665 {
666 	cairo_matrix_t original_matrix, current_matrix;
667 
668 	if (pattern->operator == CAIRO_OPERATOR_DEST)
669 		return;
670 
671 	if (width <= 0 || height <= 0)
672 		return;
673 
674 	cairo_pattern_get_matrix(pattern->handle, &original_matrix);
675 	current_matrix = original_matrix;
676 
677 	if (pattern->scale != GE_DIRECTION_NONE)
678 	{
679 		gdouble scale_x = 1.0;
680 		gdouble scale_y = 1.0;
681 
682 		if ((pattern->scale == GE_DIRECTION_VERTICAL) || (pattern->scale == GE_DIRECTION_BOTH))
683 		{
684 			scale_x = 1.0/width;
685 		}
686 
687 		if ((pattern->scale == GE_DIRECTION_HORIZONTAL) || (pattern->scale == GE_DIRECTION_BOTH))
688 		{
689 			scale_y = 1.0/height;
690 		}
691 
692 		cairo_matrix_scale(&current_matrix, scale_x, scale_y);
693 	}
694 
695 	if (pattern->translate != GE_DIRECTION_NONE)
696 	{
697 		gdouble translate_x = 0;
698 		gdouble translate_y = 0;
699 
700 		if ((pattern->translate == GE_DIRECTION_VERTICAL) || (pattern->translate == GE_DIRECTION_BOTH))
701 		{
702 			translate_x = 0.0-x;
703 		}
704 
705 		if ((pattern->translate == GE_DIRECTION_HORIZONTAL) || (pattern->translate == GE_DIRECTION_BOTH))
706 		{
707 			translate_y = 0.0-y;
708 		}
709 
710 		cairo_matrix_translate(&current_matrix, translate_x, translate_y);
711 	}
712 
713 	cairo_pattern_set_matrix(pattern->handle, &current_matrix);
714 
715 	cairo_save(canvas);
716 
717 	cairo_set_source(canvas, pattern->handle);
718 	cairo_set_operator(canvas, pattern->operator);
719 	cairo_rectangle(canvas, x, y, width, height);
720 
721 	cairo_fill (canvas);
722 
723 	cairo_restore(canvas);
724 
725 	cairo_pattern_set_matrix(pattern->handle, &original_matrix);
726 }
727 
728 /***********************************************
729  * ge_cairo_color_pattern -
730  *
731  *   Create A Solid Color Pattern
732  ***********************************************/
733 CairoPattern*
ge_cairo_color_pattern(CairoColor * base)734 ge_cairo_color_pattern(CairoColor *base)
735 {
736 	CairoPattern * result = g_new0(CairoPattern, 1);
737 
738 	#if  ((CAIRO_VERSION_MAJOR < 1) || ((CAIRO_VERSION_MAJOR == 1) && (CAIRO_VERSION_MINOR < 2)))
739 		result->type = CAIRO_PATTERN_TYPE_SOLID;
740 	#endif
741 
742 	result->scale = GE_DIRECTION_NONE;
743 	result->translate = GE_DIRECTION_NONE;
744 
745 	result->handle = cairo_pattern_create_rgba(base->r,
746 	                                           base->g,
747 	                                           base->b,
748 	                                           base->a);
749 
750 	result->operator = CAIRO_OPERATOR_SOURCE;
751 
752 	return result;
753 }
754 
755 /***********************************************
756  * ge_cairo_pixbuf_pattern -
757  *
758  *   Create A Tiled Pixbuf Pattern
759  ***********************************************/
760 CairoPattern*
ge_cairo_pixbuf_pattern(GdkPixbuf * pixbuf)761 ge_cairo_pixbuf_pattern(GdkPixbuf *pixbuf)
762 {
763 	CairoPattern * result = g_new0(CairoPattern, 1);
764 
765 	cairo_t *canvas;
766 	cairo_surface_t * surface;
767 	gint width, height;
768 
769 	#if ((CAIRO_VERSION_MAJOR < 1) || ((CAIRO_VERSION_MAJOR == 1) && (CAIRO_VERSION_MINOR < 2)))
770 		result->type = CAIRO_PATTERN_TYPE_SURFACE;
771 	#endif
772 
773 	result->scale = GE_DIRECTION_NONE;
774 	result->translate = GE_DIRECTION_BOTH;
775 
776 	width = gdk_pixbuf_get_width(pixbuf);
777 	height = gdk_pixbuf_get_height(pixbuf);
778 
779 	surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
780 
781 	canvas = cairo_create(surface);
782 
783 	gdk_cairo_set_source_pixbuf (canvas, pixbuf, 0, 0);
784 	cairo_rectangle (canvas, 0, 0, width, height);
785 	cairo_fill (canvas);
786 	cairo_destroy(canvas);
787 
788 	result->handle = cairo_pattern_create_for_surface (surface);
789 	cairo_surface_destroy(surface);
790 
791 	cairo_pattern_set_extend (result->handle, CAIRO_EXTEND_REPEAT);
792 
793 	result->operator = CAIRO_OPERATOR_SOURCE;
794 
795 	return result;
796 }
797 
798 /***********************************************
799  * ge_cairo_pixmap_pattern -
800  *
801  *   Create A Tiled Pixmap Pattern
802  ***********************************************/
803 CairoPattern*
ge_cairo_pixmap_pattern(GdkPixmap * pixmap)804 ge_cairo_pixmap_pattern(GdkPixmap *pixmap)
805 {
806 	CairoPattern * result = NULL;
807 
808 	GdkPixbuf * pixbuf;
809 	gint width, height;
810 
811 	gdk_drawable_get_size (GDK_DRAWABLE (pixmap), &width, &height);
812 
813 	pixbuf = gdk_pixbuf_get_from_drawable(NULL, GDK_DRAWABLE (pixmap),
814 	         gdk_drawable_get_colormap(GDK_DRAWABLE (pixmap)),
815 	         0, 0, 0, 0, width, height);
816 
817 	result = ge_cairo_pixbuf_pattern(pixbuf);
818 
819 	g_object_unref (pixbuf);
820 
821 	return result;
822 }
823 
824 /***********************************************
825  * ge_cairo_linear_shade_gradient_pattern -
826  *
827  *   Create A Linear Shade Gradient Pattern
828  *   Aka Smooth Shade Gradient, from/to gradient
829  *   With End points defined as shades of the
830  *   base color
831  ***********************************************/
832 CairoPattern *
ge_cairo_linear_shade_gradient_pattern(CairoColor * base,gdouble shade1,gdouble shade2,gboolean vertical)833 ge_cairo_linear_shade_gradient_pattern(CairoColor *base,
834                                        gdouble shade1,
835                                        gdouble shade2,
836                                        gboolean vertical)
837 {
838 	CairoPattern * result = g_new0(CairoPattern, 1);
839 
840 	#if  ((CAIRO_VERSION_MAJOR < 1) || ((CAIRO_VERSION_MAJOR == 1) && (CAIRO_VERSION_MINOR < 2)))
841 		result->type = CAIRO_PATTERN_TYPE_LINEAR;
842 	#endif
843 
844 	if (vertical)
845 	{
846 		result->scale = GE_DIRECTION_VERTICAL;
847 
848 		result->handle = cairo_pattern_create_linear(0, 0, 1, 0);
849 	}
850 	else
851 	{
852 		result->scale = GE_DIRECTION_HORIZONTAL;
853 
854 		result->handle = cairo_pattern_create_linear(0, 0, 0, 1);
855 	}
856 
857 	result->translate = GE_DIRECTION_BOTH;
858 	result->operator = CAIRO_OPERATOR_SOURCE;
859 
860 	ge_cairo_pattern_add_color_stop_shade(result->handle, 0, base, shade1);
861 	ge_cairo_pattern_add_color_stop_shade(result->handle, 1, base, shade2);
862 
863 	return result;
864 }
865 
866 void
ge_cairo_pattern_destroy(CairoPattern * pattern)867 ge_cairo_pattern_destroy(CairoPattern *pattern)
868 {
869 	if (pattern)
870 	{
871 		if (pattern->handle)
872 			cairo_pattern_destroy(pattern->handle);
873 
874 		g_free(pattern);
875 	}
876 }
877 
878 /*
879  * The following function will be called by GTK+ when the module
880  * is loaded and checks to see if we are compatible with the
881  * version of GTK+ that loads us.
882  */
883 GE_EXPORT const gchar* g_module_check_init (GModule *module);
884 const gchar*
g_module_check_init(GModule * module)885 g_module_check_init (GModule *module)
886 {
887 	return gtk_check_version (GTK_MAJOR_VERSION,
888 	                          GTK_MINOR_VERSION,
889 	                          GTK_MICRO_VERSION - GTK_INTERFACE_AGE);
890 }
891