static void gtk_border_image_compute_border_size (GtkBorderImageSliceSize sizes[3], double offset, double area_size, double start_border_width, double end_border_width, const GtkCssValue *start_border, const GtkCssValue *end_border) { double start, end; if (_gtk_css_number_value_get_unit (start_border) == GTK_CSS_NUMBER) start = start_border_width * _gtk_css_number_value_get (start_border, 100); else start = _gtk_css_number_value_get (start_border, area_size); if (_gtk_css_number_value_get_unit (end_border) == GTK_CSS_NUMBER) end = end_border_width * _gtk_css_number_value_get (end_border, 100); else end = _gtk_css_number_value_get (end_border, area_size); /* XXX: reduce vertical and horizontal by the same factor */ if (start + end > area_size) { start = start * area_size / (start + end); end = end * area_size / (start + end); } sizes[0].offset = offset; sizes[0].size = start; sizes[1].offset = offset + start; sizes[1].size = area_size - start - end; sizes[2].offset = offset + area_size - end; sizes[2].size = end; }
static void gtk_css_image_linear_print (GtkCssImage *image, GString *string) { GtkCssImageLinear *linear = GTK_CSS_IMAGE_LINEAR (image); guint i; if (linear->repeating) g_string_append (string, "repeating-linear-gradient("); else g_string_append (string, "linear-gradient("); if (_gtk_css_number_value_get_unit (linear->angle) == GTK_CSS_NUMBER) { guint side = _gtk_css_number_value_get (linear->angle, 100); if (side != (1 << GTK_CSS_BOTTOM)) { g_string_append (string, "to"); if (side & (1 << GTK_CSS_TOP)) g_string_append (string, " top"); else if (side & (1 << GTK_CSS_BOTTOM)) g_string_append (string, " bottom"); if (side & (1 << GTK_CSS_LEFT)) g_string_append (string, " left"); else if (side & (1 << GTK_CSS_RIGHT)) g_string_append (string, " right"); g_string_append (string, ", "); } } else { _gtk_css_value_print (linear->angle, string); g_string_append (string, ", "); } for (i = 0; i < linear->stops->len; i++) { GtkCssImageLinearColorStop *stop; if (i > 0) g_string_append (string, ", "); stop = &g_array_index (linear->stops, GtkCssImageLinearColorStop, i); _gtk_css_value_print (stop->color, string); if (stop->offset) { g_string_append (string, " "); _gtk_css_value_print (stop->offset, string); } } g_string_append (string, ")"); }
static gboolean gtk_css_image_radial_parse (GtkCssImage *image, GtkCssParser *parser) { GtkCssImageRadial *radial = GTK_CSS_IMAGE_RADIAL (image); gboolean has_shape = FALSE; gboolean has_size = FALSE; gboolean found_one = FALSE; guint i; static struct { const char *name; guint value; } names[] = { { "closest-side", GTK_CSS_CLOSEST_SIDE }, { "farthest-side", GTK_CSS_FARTHEST_SIDE }, { "closest-corner", GTK_CSS_CLOSEST_CORNER }, { "farthest-corner", GTK_CSS_FARTHEST_CORNER } }; if (_gtk_css_parser_try (parser, "repeating-radial-gradient(", TRUE)) radial->repeating = TRUE; else if (_gtk_css_parser_try (parser, "radial-gradient(", TRUE)) radial->repeating = FALSE; else { _gtk_css_parser_error (parser, "Not a radial gradient"); return FALSE; } do { found_one = FALSE; if (!has_shape && _gtk_css_parser_try (parser, "circle", TRUE)) { radial->circle = TRUE; found_one = has_shape = TRUE; } else if (!has_shape && _gtk_css_parser_try (parser, "ellipse", TRUE)) { radial->circle = FALSE; found_one = has_shape = TRUE; } else if (!has_size) { for (i = 0; i < G_N_ELEMENTS (names); i++) { if (_gtk_css_parser_try (parser, names[i].name, TRUE)) { found_one = has_size = TRUE; radial->size = names[i].value; break; } } if (!has_size) { if (_gtk_css_parser_has_number (parser)) radial->sizes[0] = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_LENGTH | GTK_CSS_PARSE_PERCENT); if (_gtk_css_parser_has_number (parser)) radial->sizes[1] = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_LENGTH | GTK_CSS_PARSE_PERCENT); found_one = has_size = radial->sizes[0] != NULL; } } } while (found_one && !(has_shape && has_size)); if (_gtk_css_parser_try (parser, "at", TRUE)) { radial->position = _gtk_css_position_value_parse (parser); if (!radial->position) return FALSE; if (!_gtk_css_parser_try (parser, ",", TRUE)) { _gtk_css_parser_error (parser, "Expected a comma here"); return FALSE; } } else { radial->position = _gtk_css_position_value_new (_gtk_css_number_value_new (50, GTK_CSS_PERCENT), _gtk_css_number_value_new (50, GTK_CSS_PERCENT)); if ((has_shape || has_size) && !_gtk_css_parser_try (parser, ",", TRUE)) { _gtk_css_parser_error (parser, "Expected a comma here"); return FALSE; } } if (!has_size) { radial->size = GTK_CSS_FARTHEST_CORNER; } if (!has_shape) { if (radial->sizes[0] && radial->sizes[1]) radial->circle = FALSE; else radial->circle = TRUE; } if (has_shape && radial->circle) { if (radial->sizes[0] && radial->sizes[1]) { _gtk_css_parser_error (parser, "Circular gradient can only have one size"); return FALSE; } if (radial->sizes[0] && _gtk_css_number_value_get_unit (radial->sizes[0]) == GTK_CSS_PERCENT) { _gtk_css_parser_error (parser, "Circular gradient cannot have percentage as size"); return FALSE; } } if (has_size && !radial->circle) { if (!radial->sizes[1]) radial->sizes[1] = _gtk_css_value_ref (radial->sizes[0]); } do { GtkCssImageRadialColorStop stop; stop.color = _gtk_css_color_value_parse (parser); if (stop.color == NULL) return FALSE; if (_gtk_css_parser_has_number (parser)) { stop.offset = _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_PERCENT | GTK_CSS_PARSE_LENGTH); if (stop.offset == NULL) { _gtk_css_value_unref (stop.color); return FALSE; } } else { stop.offset = NULL; } g_array_append_val (radial->stops, stop); } while (_gtk_css_parser_try (parser, ",", TRUE)); if (!_gtk_css_parser_try (parser, ")", TRUE)) { _gtk_css_parser_error (parser, "Missing closing bracket at end of radial gradient"); return FALSE; } return TRUE; }
static void gtk_css_image_linear_draw (GtkCssImage *image, cairo_t *cr, double width, double height) { GtkCssImageLinear *linear = GTK_CSS_IMAGE_LINEAR (image); cairo_pattern_t *pattern; double angle; /* actual angle of the gradiant line in degrees */ double x, y; /* coordinates of start point */ double length; /* distance in pixels for 100% */ double start, end; /* position of first/last point on gradient line - with gradient line being [0, 1] */ double offset; int i, last; if (_gtk_css_number_value_get_unit (linear->angle) == GTK_CSS_NUMBER) { guint side = _gtk_css_number_value_get (linear->angle, 100); /* special casing the regular cases here so we don't get rounding errors */ switch (side) { case 1 << GTK_CSS_RIGHT: angle = 90; break; case 1 << GTK_CSS_LEFT: angle = 270; break; case 1 << GTK_CSS_TOP: angle = 0; break; case 1 << GTK_CSS_BOTTOM: angle = 180; break; default: angle = atan2 (side & 1 << GTK_CSS_TOP ? -width : width, side & 1 << GTK_CSS_LEFT ? -height : height); angle = 180 * angle / G_PI + 90; break; } } else { angle = _gtk_css_number_value_get (linear->angle, 100); } gtk_css_image_linear_compute_start_point (angle, width, height, &x, &y); length = sqrt (x * x + y * y); gtk_css_image_linear_get_start_end (linear, length, &start, &end); pattern = cairo_pattern_create_linear (x * (start - 0.5), y * (start - 0.5), x * (end - 0.5), y * (end - 0.5)); if (linear->repeating) cairo_pattern_set_extend (pattern, CAIRO_EXTEND_REPEAT); else cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD); offset = start; last = -1; for (i = 0; i < linear->stops->len; i++) { GtkCssImageLinearColorStop *stop; double pos, step; stop = &g_array_index (linear->stops, GtkCssImageLinearColorStop, i); if (stop->offset == NULL) { if (i == 0) pos = 0.0; else if (i + 1 == linear->stops->len) pos = 1.0; else continue; } else pos = _gtk_css_number_value_get (stop->offset, length) / length; pos = MAX (pos, offset); step = (pos - offset) / (i - last); for (last = last + 1; last <= i; last++) { const GdkRGBA *rgba; stop = &g_array_index (linear->stops, GtkCssImageLinearColorStop, last); rgba = _gtk_css_rgba_value_get_rgba (stop->color); offset += step; cairo_pattern_add_color_stop_rgba (pattern, (offset - start) / (end - start), rgba->red, rgba->green, rgba->blue, rgba->alpha); } offset = pos; last = i; } cairo_rectangle (cr, 0, 0, width, height); cairo_translate (cr, width / 2, height / 2); cairo_set_source (cr, pattern); cairo_fill (cr); cairo_pattern_destroy (pattern); }