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