2 #include "general-support.h"
3 #include "cairo-support.h"
5 /***********************************************
8 * Get HSB values from RGB values.
10 * Modified from Smooth but originated in GTK+
11 ***********************************************/
13 ge_hsb_from_color (const CairoColor *color,
18 gdouble min, max, delta;
19 gdouble red, green, blue;
28 min = MIN(green, blue);
32 max = MAX(green, blue);
36 *brightness = (max + min) / 2;
38 if (fabs(max - min) < 0.0001)
45 if (*brightness <= 0.5)
46 *saturation = (max - min) / (max + min);
48 *saturation = (max - min) / (2 - max - min);
53 *hue = (green - blue) / delta;
54 else if (green == max)
55 *hue = 2 + (blue - red) / delta;
57 *hue = 4 + (red - green) / delta;
65 /***********************************************
68 * Get RGB values from HSB values.
70 * Modified from Smooth but originated in GTK+
71 ***********************************************/
72 #define MODULA(number, divisor) (((gint)number % divisor) + (number - (gint)number))
74 ge_color_from_hsb (gdouble hue,
80 gdouble hue_shift[3], color_shift[3];
85 if (brightness <= 0.5)
86 m2 = brightness * (1 + saturation);
88 m2 = brightness + saturation - brightness * saturation;
90 m1 = 2 * brightness - m2;
92 hue_shift[0] = hue + 120;
94 hue_shift[2] = hue - 120;
96 color_shift[0] = color_shift[1] = color_shift[2] = brightness;
98 i = (saturation == 0)?3:0;
105 m3 = MODULA(m3, 360);
107 m3 = 360 - MODULA(ABS(m3), 360);
110 color_shift[i] = m1 + (m2 - m1) * m3 / 60;
114 color_shift[i] = m1 + (m2 - m1) * (240 - m3) / 60;
119 color->r = color_shift[0];
120 color->g = color_shift[1];
121 color->b = color_shift[2];
126 ge_gdk_color_to_cairo (const GdkColor *c, CairoColor *cc)
130 g_return_if_fail (c && cc);
132 r = c->red / 65535.0;
133 g = c->green / 65535.0;
134 b = c->blue / 65535.0;
143 ge_cairo_color_to_gtk (const CairoColor *cc, GdkColor *c)
147 g_return_if_fail (c && cc);
159 ge_gtk_style_to_cairo_color_cube (GtkStyle * style, CairoColorCube *cube)
163 g_return_if_fail (style && cube);
165 for (i = 0; i < 5; i++)
167 ge_gdk_color_to_cairo (&style->bg[i], &cube->bg[i]);
168 ge_gdk_color_to_cairo (&style->fg[i], &cube->fg[i]);
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]);
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]);
179 cube->black.r = cube->black.g = cube->black.b = 0;
182 cube->white.r = cube->white.g = cube->white.b = 1;
187 ge_shade_color(const CairoColor *base, gdouble shade_ratio, CairoColor *composite)
190 gdouble saturation = 0;
191 gdouble brightness = 0;
193 g_return_if_fail (base && composite);
195 if (shade_ratio == 1.0)
197 composite->r = base->r;
198 composite->g = base->g;
199 composite->b = base->b;
200 composite->a = base->a;
205 ge_hsb_from_color (base, &hue, &saturation, &brightness);
207 brightness = MIN(brightness*shade_ratio, 1.0);
208 brightness = MAX(brightness, 0.0);
210 saturation = MIN(saturation*shade_ratio, 1.0);
211 saturation = MAX(saturation, 0.0);
213 ge_color_from_hsb (hue, saturation, brightness, composite);
214 composite->a = base->a;
218 ge_saturate_color (const CairoColor *base, gdouble saturate_level, CairoColor *composite)
221 gdouble saturation = 0;
222 gdouble brightness = 0;
224 g_return_if_fail (base && composite);
226 ge_hsb_from_color (base, &hue, &saturation, &brightness);
228 saturation = MIN(saturation*saturate_level, 1.0);
229 saturation = MAX(saturation, 0.0);
231 ge_color_from_hsb (hue, saturation, brightness, composite);
232 composite->a = base->a;
236 ge_mix_color (const CairoColor *color1, const CairoColor *color2,
237 gdouble mix_factor, CairoColor *composite)
239 g_return_if_fail (color1 && color2 && composite);
241 composite->r = color1->r * (1-mix_factor) + color2->r * mix_factor;
242 composite->g = color1->g * (1-mix_factor) + color2->g * mix_factor;
243 composite->b = color1->b * (1-mix_factor) + color2->b * mix_factor;
248 ge_gdk_drawable_to_cairo (GdkDrawable *window, GdkRectangle *area)
252 g_return_val_if_fail (window != NULL, NULL);
254 cr = (cairo_t*) gdk_cairo_create (window);
255 cairo_set_line_width (cr, 1.0);
256 cairo_set_line_cap (cr, CAIRO_LINE_CAP_SQUARE);
257 cairo_set_line_join (cr, CAIRO_LINE_JOIN_MITER);
261 cairo_rectangle (cr, area->x, area->y, area->width, area->height);
262 cairo_clip_preserve (cr);
270 ge_cairo_set_color (cairo_t *cr, const CairoColor *color)
272 g_return_if_fail (cr && color);
274 cairo_set_source_rgba (cr, color->r, color->g, color->b, color->a);
278 ge_cairo_set_gdk_color_with_alpha (cairo_t *cr, const GdkColor *color, gdouble alpha)
280 g_return_if_fail (cr && color);
282 cairo_set_source_rgba (cr, color->red / 65535.0,
283 color->green / 65535.0,
284 color->blue / 65535.0,
289 ge_cairo_pattern_add_color_stop_color (cairo_pattern_t *pattern,
291 const CairoColor *color)
293 g_return_if_fail (pattern && color);
295 cairo_pattern_add_color_stop_rgba (pattern, offset, color->r, color->g, color->b, color->a);
299 ge_cairo_pattern_add_color_stop_shade (cairo_pattern_t *pattern,
301 const CairoColor *color,
306 g_return_if_fail (pattern && color && (shade >= 0) && (shade <= 3));
312 ge_shade_color(color, shade, &shaded);
315 ge_cairo_pattern_add_color_stop_color(pattern, offset, &shaded);
319 * This function will draw a rounded corner at position x,y. If the radius
320 * is very small (or negative) it will instead just do a line_to.
321 * ge_cairo_rounded_corner assumes clockwise drawing.
324 ge_cairo_rounded_corner (cairo_t *cr,
332 cairo_line_to (cr, x, y);
339 cairo_line_to (cr, x, y);
341 case CR_CORNER_TOPLEFT:
342 cairo_arc (cr, x + radius, y + radius, radius, G_PI, G_PI * 3/2);
344 case CR_CORNER_TOPRIGHT:
345 cairo_arc (cr, x - radius, y + radius, radius, G_PI * 3/2, G_PI * 2);
347 case CR_CORNER_BOTTOMRIGHT:
348 cairo_arc (cr, x - radius, y - radius, radius, 0, G_PI * 1/2);
350 case CR_CORNER_BOTTOMLEFT:
351 cairo_arc (cr, x + radius, y - radius, radius, G_PI * 1/2, G_PI);
355 /* A bitfield and not a sane value ... */
356 g_assert_not_reached ();
357 cairo_line_to (cr, x, y);
364 ge_cairo_rounded_rectangle (cairo_t *cr,
365 double x, double y, double w, double h,
366 double radius, CairoCorners corners)
368 g_return_if_fail (cr != NULL);
370 if (radius < 0.0001 || corners == CR_CORNER_NONE)
372 cairo_rectangle (cr, x, y, w, h);
376 if ((corners == CR_CORNER_ALL) && (radius > w / 2.0 || radius > h / 2.0))
377 g_warning ("Radius is too large for width/height in ge_rounded_rectangle.\n");
378 else if (radius > w || radius > h) /* This isn't perfect. Assumes that only one corner is set. */
379 g_warning ("Radius is too large for width/height in ge_rounded_rectangle.\n");
382 if (corners & CR_CORNER_TOPLEFT)
383 cairo_move_to (cr, x+radius, y);
385 cairo_move_to (cr, x, y);
387 if (corners & CR_CORNER_TOPRIGHT)
388 cairo_arc (cr, x+w-radius, y+radius, radius, G_PI * 1.5, G_PI * 2);
390 cairo_line_to (cr, x+w, y);
392 if (corners & CR_CORNER_BOTTOMRIGHT)
393 cairo_arc (cr, x+w-radius, y+h-radius, radius, 0, G_PI * 0.5);
395 cairo_line_to (cr, x+w, y+h);
397 if (corners & CR_CORNER_BOTTOMLEFT)
398 cairo_arc (cr, x+radius, y+h-radius, radius, G_PI * 0.5, G_PI);
400 cairo_line_to (cr, x, y+h);
402 if (corners & CR_CORNER_TOPLEFT)
403 cairo_arc (cr, x+radius, y+radius, radius, G_PI, G_PI * 1.5);
405 cairo_line_to (cr, x, y);
409 /* ge_cairo_stroke_rectangle.
411 * A simple function to stroke the rectangle { x, y, w, h}.
412 * (This function only exists because of a cairo performance bug that
413 * has been fixed and it may be a good idea to get rid of it again.)
416 ge_cairo_stroke_rectangle (cairo_t *cr, double x, double y, double w, double h)
418 cairo_rectangle (cr, x, y, w, h);
423 ge_cairo_inner_rectangle (cairo_t *cr,
425 double width, double height)
427 double line_width = cairo_get_line_width (cr);
429 cairo_rectangle (cr, x + line_width / 2.0,
430 y + line_width / 2.0,
432 height - line_width);
436 ge_cairo_inner_rounded_rectangle (cairo_t *cr,
438 double width, double height,
439 double radius, CairoCorners corners)
441 double line_width = cairo_get_line_width (cr);
443 ge_cairo_rounded_rectangle (cr,
444 x + line_width / 2.0,
445 y + line_width / 2.0,
451 /***********************************************
452 * ge_cairo_simple_border -
454 * A simple routine to draw thin squared
455 * borders with a topleft and bottomright color.
457 * It originated in Smooth-Engine.
458 ***********************************************/
460 ge_cairo_simple_border (cairo_t *cr,
461 const CairoColor * tl, const CairoColor * br,
462 gint x, gint y, gint width, gint height,
463 gboolean topleft_overlap)
465 gboolean solid_color;
467 g_return_if_fail (cr != NULL);
468 g_return_if_fail (tl != NULL);
469 g_return_if_fail (br != NULL);
472 solid_color = (tl == br) || ((tl->r == br->r) && (tl->g == br->g) && (tl->b == br->b) && (tl->a == br->a));
474 topleft_overlap &= !solid_color;
478 cairo_set_line_width (cr, 1);
482 ge_cairo_set_color(cr, br);
484 cairo_move_to(cr, x + 0.5, y + height - 0.5);
485 cairo_line_to(cr, x + width - 0.5, y + height - 0.5);
486 cairo_line_to(cr, x + width - 0.5, y + 0.5);
491 ge_cairo_set_color(cr, tl);
493 cairo_move_to(cr, x + 0.5, y + height - 0.5);
494 cairo_line_to(cr, x + 0.5, y + 0.5);
495 cairo_line_to(cr, x + width - 0.5, y + 0.5);
497 if (!topleft_overlap)
502 ge_cairo_set_color(cr, br);
505 cairo_move_to(cr, x + 0.5, y + height - 0.5);
506 cairo_line_to(cr, x + width - 0.5, y + height - 0.5);
507 cairo_line_to(cr, x + width - 0.5, y + 0.5);
515 void ge_cairo_polygon (cairo_t *cr,
516 const CairoColor *color,
524 ge_cairo_set_color(cr, color);
525 cairo_move_to(cr, points[0].x, points[0].y);
527 for (i = 1; i < npoints; i++)
529 if (!((points[i].x == points[i + 1].x) &&
530 (points[i].y == points[i + 1].y)))
532 cairo_line_to(cr, points[i].x, points[i].y);
536 if ((points[npoints-1].x != points[0].y) ||
537 (points[npoints-1].y != points[0].y))
539 cairo_line_to(cr, points[0].x, points[0].y);
547 void ge_cairo_line (cairo_t *cr,
548 const CairoColor *color,
556 ge_cairo_set_color(cr, color);
557 cairo_set_line_width (cr, 1);
559 cairo_move_to(cr, x1 + 0.5, y1 + 0.5);
560 cairo_line_to(cr, x2 + 0.5, y2 + 0.5);
568 ge_cairo_mirror (cairo_t *cr,
575 cairo_matrix_t matrix;
577 cairo_matrix_init_identity (&matrix);
579 cairo_translate (cr, *x, *y);
583 if (mirror & CR_MIRROR_HORIZONTAL)
585 cairo_matrix_scale (&matrix, -1, 1);
588 if (mirror & CR_MIRROR_VERTICAL)
590 cairo_matrix_scale (&matrix, 1, -1);
594 cairo_transform (cr, &matrix);
598 ge_cairo_exchange_axis (cairo_t *cr,
605 cairo_matrix_t matrix;
607 cairo_translate (cr, *x, *y);
608 cairo_matrix_init (&matrix, 0, 1, 1, 0, 0, 0);
610 cairo_transform (cr, &matrix);
612 /* swap width/height */
621 /***********************************************
622 * ge_cairo_pattern_fill -
624 * Fill an area with some pattern
625 * Scaling or tiling if needed
626 ***********************************************/
628 ge_cairo_pattern_fill(cairo_t *canvas,
629 CairoPattern *pattern,
635 cairo_matrix_t original_matrix, current_matrix;
637 if (pattern->operator == CAIRO_OPERATOR_DEST)
640 if (width <= 0 || height <= 0)
643 cairo_pattern_get_matrix(pattern->handle, &original_matrix);
644 current_matrix = original_matrix;
646 if (pattern->scale != GE_DIRECTION_NONE)
648 gdouble scale_x = 1.0;
649 gdouble scale_y = 1.0;
651 if ((pattern->scale == GE_DIRECTION_VERTICAL) || (pattern->scale == GE_DIRECTION_BOTH))
656 if ((pattern->scale == GE_DIRECTION_HORIZONTAL) || (pattern->scale == GE_DIRECTION_BOTH))
658 scale_y = 1.0/height;
661 cairo_matrix_scale(¤t_matrix, scale_x, scale_y);
664 if (pattern->translate != GE_DIRECTION_NONE)
666 gdouble translate_x = 0;
667 gdouble translate_y = 0;
669 if ((pattern->translate == GE_DIRECTION_VERTICAL) || (pattern->translate == GE_DIRECTION_BOTH))
674 if ((pattern->translate == GE_DIRECTION_HORIZONTAL) || (pattern->translate == GE_DIRECTION_BOTH))
679 cairo_matrix_translate(¤t_matrix, translate_x, translate_y);
682 cairo_pattern_set_matrix(pattern->handle, ¤t_matrix);
686 cairo_set_source(canvas, pattern->handle);
687 cairo_set_operator(canvas, pattern->operator);
688 cairo_rectangle(canvas, x, y, width, height);
692 cairo_restore(canvas);
694 cairo_pattern_set_matrix(pattern->handle, &original_matrix);
697 /***********************************************
698 * ge_cairo_color_pattern -
700 * Create A Solid Color Pattern
701 ***********************************************/
703 ge_cairo_color_pattern(CairoColor *base)
705 CairoPattern * result = g_new0(CairoPattern, 1);
707 #if ((CAIRO_VERSION_MAJOR < 1) || ((CAIRO_VERSION_MAJOR == 1) && (CAIRO_VERSION_MINOR < 2)))
708 result->type = CAIRO_PATTERN_TYPE_SOLID;
711 result->scale = GE_DIRECTION_NONE;
712 result->translate = GE_DIRECTION_NONE;
714 result->handle = cairo_pattern_create_rgba(base->r,
719 result->operator = CAIRO_OPERATOR_SOURCE;
724 /***********************************************
725 * ge_cairo_pixbuf_pattern -
727 * Create A Tiled Pixbuf Pattern
728 ***********************************************/
730 ge_cairo_pixbuf_pattern(GdkPixbuf *pixbuf)
732 CairoPattern * result = g_new0(CairoPattern, 1);
735 cairo_surface_t * surface;
738 #if ((CAIRO_VERSION_MAJOR < 1) || ((CAIRO_VERSION_MAJOR == 1) && (CAIRO_VERSION_MINOR < 2)))
739 result->type = CAIRO_PATTERN_TYPE_SURFACE;
742 result->scale = GE_DIRECTION_NONE;
743 result->translate = GE_DIRECTION_BOTH;
745 width = gdk_pixbuf_get_width(pixbuf);
746 height = gdk_pixbuf_get_height(pixbuf);
748 surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
750 canvas = cairo_create(surface);
752 gdk_cairo_set_source_pixbuf (canvas, pixbuf, 0, 0);
753 cairo_rectangle (canvas, 0, 0, width, height);
755 cairo_destroy(canvas);
757 result->handle = cairo_pattern_create_for_surface (surface);
758 cairo_surface_destroy(surface);
760 cairo_pattern_set_extend (result->handle, CAIRO_EXTEND_REPEAT);
762 result->operator = CAIRO_OPERATOR_SOURCE;
767 /***********************************************
768 * ge_cairo_pixmap_pattern -
770 * Create A Tiled Pixmap Pattern
771 ***********************************************/
773 ge_cairo_pixmap_pattern(GdkPixmap *pixmap)
775 CairoPattern * result = NULL;
780 gdk_drawable_get_size (GDK_DRAWABLE (pixmap), &width, &height);
782 pixbuf = gdk_pixbuf_get_from_drawable(NULL, GDK_DRAWABLE (pixmap),
783 gdk_drawable_get_colormap(GDK_DRAWABLE (pixmap)),
784 0, 0, 0, 0, width, height);
786 result = ge_cairo_pixbuf_pattern(pixbuf);
788 g_object_unref (pixbuf);
793 /***********************************************
794 * ge_cairo_linear_shade_gradient_pattern -
796 * Create A Linear Shade Gradient Pattern
797 * Aka Smooth Shade Gradient, from/to gradient
798 * With End points defined as shades of the
800 ***********************************************/
802 ge_cairo_linear_shade_gradient_pattern(CairoColor *base,
807 CairoPattern * result = g_new0(CairoPattern, 1);
809 #if ((CAIRO_VERSION_MAJOR < 1) || ((CAIRO_VERSION_MAJOR == 1) && (CAIRO_VERSION_MINOR < 2)))
810 result->type = CAIRO_PATTERN_TYPE_LINEAR;
815 result->scale = GE_DIRECTION_VERTICAL;
817 result->handle = cairo_pattern_create_linear(0, 0, 1, 0);
821 result->scale = GE_DIRECTION_HORIZONTAL;
823 result->handle = cairo_pattern_create_linear(0, 0, 0, 1);
826 result->translate = GE_DIRECTION_BOTH;
827 result->operator = CAIRO_OPERATOR_SOURCE;
829 ge_cairo_pattern_add_color_stop_shade(result->handle, 0, base, shade1);
830 ge_cairo_pattern_add_color_stop_shade(result->handle, 1, base, shade2);
836 ge_cairo_pattern_destroy(CairoPattern *pattern)
841 cairo_pattern_destroy(pattern->handle);
848 * The following function will be called by GTK+ when the module
849 * is loaded and checks to see if we are compatible with the
850 * version of GTK+ that loads us.
852 GE_EXPORT const gchar* g_module_check_init (GModule *module);
854 g_module_check_init (GModule *module)
856 return gtk_check_version (GTK_MAJOR_VERSION,
858 GTK_MICRO_VERSION - GTK_INTERFACE_AGE);