Gradients: work in progress

This commit is contained in:
o9000
2016-10-02 22:56:48 +02:00
parent edc5a02efe
commit 822b149419
17 changed files with 848 additions and 72 deletions

View File

@@ -381,6 +381,20 @@ void show(Area *a)
a->resize_needed = TRUE;
}
void update_dependent_gradients(Area *a)
{
if (!a->on_screen)
return;
if (a->_changed) {
for (GList *l = a->dependent_gradients; l; l = l->next) {
GradientInstance *gi = (GradientInstance *)l->data;
update_gradient(gi);
}
}
for (GList *l = a->children; l; l = l->next)
update_dependent_gradients((Area *)l->data);
}
void draw(Area *a)
{
if (a->_changed) {
@@ -439,17 +453,8 @@ void draw(Area *a)
void draw_background(Area *a, cairo_t *c)
{
if ((a->bg->fill_color.alpha > 0.0) ||
(panel_config.mouse_effects && (a->has_mouse_over_effect || a->has_mouse_press_effect)) ||
(area_has_gradient_fill(a))) {
cairo_pattern_t *cairo_gradient_pattern;
if (area_has_gradient_fill(a)) {
cairo_gradient_pattern = cairo_pattern_create_linear(0.0, 0.0, 0.0, a->height - top_bottom_border_width(a));
cairo_pattern_add_color_stop_rgba(cairo_gradient_pattern, 0.1, a->bg->fill_color.rgb[0], a->bg->fill_color.rgb[1], a->bg->fill_color.rgb[2], a->bg->fill_color.alpha);
cairo_pattern_add_color_stop_rgba(cairo_gradient_pattern, 0.9, a->bg->fill_color2.rgb[0], a->bg->fill_color2.rgb[1], a->bg->fill_color2.rgb[2], a->bg->fill_color2.alpha);
cairo_set_source(c, cairo_gradient_pattern);
} else if (a->mouse_state == MOUSE_OVER)
(panel_config.mouse_effects && (a->has_mouse_over_effect || a->has_mouse_press_effect))) {
if (a->mouse_state == MOUSE_OVER)
cairo_set_source_rgba(c,
a->bg->fill_color_hover.rgb[0],
a->bg->fill_color_hover.rgb[1],
@@ -476,8 +481,21 @@ void draw_background(Area *a, cairo_t *c)
a->bg->border.radius - a->bg->border.width / 1.571);
cairo_fill(c);
/*
cairo_pattern_t *cairo_gradient_pattern;
if (area_has_gradient_fill(a)) {
cairo_gradient_pattern = cairo_pattern_create_linear(0.0, 0.0, 0.0, a->height - top_bottom_border_width(a));
cairo_pattern_add_color_stop_rgba(cairo_gradient_pattern, 0.1, a->bg->fill_color.rgb[0],
a->bg->fill_color.rgb[1], a->bg->fill_color.rgb[2], a->bg->fill_color.alpha);
cairo_pattern_add_color_stop_rgba(cairo_gradient_pattern, 0.9, a->bg->fill_color2.rgb[0],
a->bg->fill_color2.rgb[1], a->bg->fill_color2.rgb[2], a->bg->fill_color2.alpha);
cairo_set_source(c, cairo_gradient_pattern);
} else
if (area_has_gradient_fill(a))
cairo_pattern_destroy(cairo_gradient_pattern);
cairo_pattern_destroy(cairo_gradient_pattern);
*/
}
if (a->bg->border.width > 0) {
@@ -542,6 +560,7 @@ void add_area(Area *a, Area *parent)
schedule_redraw(parent);
panel_refresh = TRUE;
}
init_area_gradients(a);
}
void free_area(Area *a)
@@ -570,6 +589,7 @@ void free_area(Area *a)
if (mouse_over_area == a) {
mouse_over_area = NULL;
}
free_area_gradients(a);
}
void mouse_over(Area *area, int pressed)
@@ -802,20 +822,20 @@ void area_dump_geometry(Area *area, int indent)
area->width,
area->height);
fprintf(stderr,
"%*sBorder: left = %d, right = %d, top = %d, bottom = %d\n",
indent,
"",
left_border_width(area),
right_border_width(area),
top_border_width(area),
bottom_border_width(area));
"%*sBorder: left = %d, right = %d, top = %d, bottom = %d\n",
indent,
"",
left_border_width(area),
right_border_width(area),
top_border_width(area),
bottom_border_width(area));
fprintf(stderr,
"%*sPadding: left = right = %d, top = bottom = %d, spacing = %d\n",
indent,
"",
area->paddingxlr,
area->paddingy,
area->paddingx);
"%*sPadding: left = right = %d, top = bottom = %d, spacing = %d\n",
indent,
"",
area->paddingxlr,
area->paddingy,
area->paddingx);
if (area->_dump_geometry)
area->_dump_geometry(area, indent);
if (area->children) {
@@ -826,17 +846,104 @@ void area_dump_geometry(Area *area, int indent)
}
}
gboolean area_has_gradient_fill(Area *area)
void instantiate_gradient_offsets(Area *area, GradientInstance *gi, GList *offsets, GList **offset_instances)
{
if (!area->bg->gradient)
return FALSE;
else if ((area->bg->fill_color.alpha <= 0.0 ) && (area->bg->fill_color2.alpha <= 0.0))
return FALSE;
else if ((area->bg->fill_color.rgb[0] == area->bg->fill_color2.rgb[0]) &&
(area->bg->fill_color.rgb[1] == area->bg->fill_color2.rgb[1]) &&
(area->bg->fill_color.rgb[2] == area->bg->fill_color2.rgb[2]) &&
(area->bg->fill_color.alpha == area->bg->fill_color2.alpha))
return FALSE;
else return TRUE;
for (GList *l = offsets; l; l = l->next) {
Offset *offset = (Offset *)l->data;
OffsetInstance *offset_instance = (OffsetInstance *)calloc(1, sizeof(OffsetInstance));
offset_instance->constant = offset->constant;
if (offset_instance->constant) {
offset_instance->constant_value = offset->constant_value;
} else {
offset_instance->variable = offset->variable;
offset_instance->multiplier = offset->multiplier;
if (offset->variable_element == ORIGIN_ELEMENT)
offset_instance->variable_element = area;
else if (offset->variable_element == ORIGIN_PARENT)
offset_instance->variable_element = area->parent ? (Area *)area->parent : area;
else if (offset->variable_element == ORIGIN_PANEL)
offset_instance->variable_element = (Area *)area->panel;
else if (offset->variable_element == ORIGIN_SCREEN)
// TODO
offset_instance->variable_element = (Area *)area->panel;
else if (offset->variable_element == ORIGIN_DESKTOP)
// TODO
offset_instance->variable_element = (Area *)area->panel;
else
g_assert_not_reached();
*offset_instances = g_list_append(*offset_instances, offset_instance);
offset_instance->variable_element->dependent_gradients =
g_list_append(offset_instance->variable_element->dependent_gradients, gi);
gi->gradient_dependencies = g_list_append(gi->gradient_dependencies, offset_instance->variable_element);
}
}
}
void free_gradient_offsets(GradientInstance *gi, GList **offset_instances)
{
for (GList *l = *offset_instances; l; l = l->next) {
OffsetInstance *offset_instance = (OffsetInstance *)l->data;
if (!offset_instance->constant) {
offset_instance->variable_element->dependent_gradients =
g_list_remove_all(offset_instance->variable_element->dependent_gradients, gi);
gi->gradient_dependencies = g_list_remove_all(gi->gradient_dependencies, offset_instance->variable_element);
}
}
g_list_free_full(*offset_instances, free);
}
void instantiate_gradient_point(Area *area,
GradientInstance *gi,
ControlPoint *control,
ControlPointInstance *control_instance)
{
instantiate_gradient_offsets(area, gi, control->offsets_x, &control_instance->offsets_x);
instantiate_gradient_offsets(area, gi, control->offsets_y, &control_instance->offsets_y);
instantiate_gradient_offsets(area, gi, control->offsets_r, &control_instance->offsets_r);
}
void free_gradient_point(GradientInstance *gi, ControlPointInstance *control_instance)
{
free_gradient_offsets(gi, &control_instance->offsets_x);
free_gradient_offsets(gi, &control_instance->offsets_y);
free_gradient_offsets(gi, &control_instance->offsets_r);
}
void instantiate_gradient(Area *area, GradientClass *g, GradientInstance *gi)
{
gi->gradient_class = g;
gi->area = area;
gi->from.origin = area;
instantiate_gradient_point(area, gi, &g->from, &gi->from);
instantiate_gradient_point(area, gi, &g->to, &gi->to);
}
void free_gradient(GradientInstance *gi)
{
free_gradient_point(gi, &gi->from);
free_gradient_point(gi, &gi->to);
}
void init_area_gradients(Area *area)
{
for (GList *l = area->gradients; l; l = l->next) {
GradientClass *g = (GradientClass *)l->data;
GradientInstance *gi = (GradientInstance *)calloc(1, sizeof(GradientInstance));
instantiate_gradient(area, g, gi);
area->gradient_instances = g_list_append(area->gradient_instances, gi);
}
}
void free_area_gradients(Area *area)
{
for (GList *l = area->gradient_instances; l; l = l->next) {
GradientInstance *gi = (GradientInstance *)l->data;
free_gradient(gi);
}
g_list_free_full(area->gradient_instances, free);
}
void update_gradient(GradientInstance *gi)
{
// TODO
}

View File

@@ -11,6 +11,9 @@
#include <cairo.h>
#include <cairo-xlib.h>
#include "color.h"
#include "gradient.h"
// DATA ORGANISATION
//
// Areas in tint2 are similar to widgets in a GUI.
@@ -117,13 +120,6 @@
// The caller takes ownership of the pointer.
// The Area's _get_tooltip_text member must point to this function.
typedef struct Color {
// Values are in [0, 1], with 0 meaning no intensity.
double rgb[3];
// Values are in [0, 1], with 0 meaning fully transparent, 1 meaning fully opaque.
double alpha;
} Color;
typedef enum BorderMask {
BORDER_TOP = 1 << 0,
BORDER_BOTTOM = 1 << 1,
@@ -147,8 +143,6 @@ typedef struct Border {
typedef struct Background {
// Normal state
Color fill_color;
Color fill_color2;
gboolean gradient;
Border border;
// On mouse hover
Color fill_color_hover;
@@ -179,6 +173,12 @@ typedef struct Area {
// Size, including borders
int width, height;
Background *bg;
// Each element is a pointer to a GradientClass (list can be empty), no ownership
GList *gradients;
// Each element is a GradientInstance attached to this Area (list can be empty)
GList *gradient_instances;
// Each element is a GradientInstance that depends on this Area's geometry (position or size)
GList *dependent_gradients;
// List of children, each one a pointer to Area
GList *children;
// Pointer to the parent Area or NULL
@@ -311,11 +311,15 @@ gboolean area_is_under_mouse(void *obj, int x, int y);
// they are outside the drawing area of the button.
gboolean full_width_area_is_under_mouse(void *obj, int x, int y);
void init_area_gradients(Area *area);
void free_area_gradients(Area *area);
void area_dump_geometry(Area *area, int indent);
void mouse_over(Area *area, int pressed);
void mouse_out();
gboolean area_has_gradient_fill(Area *area);
void update_gradient(GradientInstance *gi);
void update_dependent_gradients(Area *a);
#endif

2
src/util/color.c Normal file
View File

@@ -0,0 +1,2 @@
#include "color.h"

11
src/util/color.h Normal file
View File

@@ -0,0 +1,11 @@
#ifndef COLOR_H
#define COLOR_H
typedef struct Color {
// Values are in [0, 1], with 0 meaning no intensity.
double rgb[3];
// Values are in [0, 1], with 0 meaning fully transparent, 1 meaning fully opaque.
double alpha;
} Color;
#endif // COLOR_H

View File

@@ -201,6 +201,7 @@ void get_color(char *hex, double *rgb)
void extract_values(const char *value, char **value1, char **value2, char **value3)
{
char *value0 = strdup(value);
char *b = 0, *c = 0;
if (*value1)
@@ -210,14 +211,14 @@ void extract_values(const char *value, char **value1, char **value2, char **valu
if (*value3)
free(*value3);
if ((b = strchr(value, ' '))) {
if ((b = strchr(value0, ' '))) {
b[0] = '\0';
b++;
} else {
*value2 = 0;
*value3 = 0;
}
*value1 = strdup(value);
*value1 = strdup(value0);
g_strstrip(*value1);
if (b) {
@@ -236,6 +237,63 @@ void extract_values(const char *value, char **value1, char **value2, char **valu
*value3 = strdup(c);
g_strstrip(*value3);
}
free(value0);
}
void extract_values_4(const char *value, char **value1, char **value2, char **value3, char **value4)
{
char *value0 = strdup(value);
char *b = 0, *c = 0, *d;
if (*value1)
free(*value1);
if (*value2)
free(*value2);
if (*value3)
free(*value3);
if (*value4)
free(*value4);
if ((b = strchr(value0, ' '))) {
b[0] = '\0';
b++;
} else {
*value2 = 0;
*value3 = 0;
*value4 = 0;
}
*value1 = strdup(value0);
g_strstrip(*value1);
if (b) {
if ((c = strchr(b, ' '))) {
c[0] = '\0';
c++;
} else {
c = 0;
*value3 = 0;
*value4 = 0;
}
*value2 = strdup(b);
g_strstrip(*value2);
}
if (c) {
if ((d = strchr(c, ' '))) {
d[0] = '\0';
d++;
} else {
d = 0;
*value4 = 0;
}
*value3 = strdup(c);
g_strstrip(*value3);
*value4 = strdup(d);
g_strstrip(*value4);
}
free(value0);
}
void adjust_asb(DATA32 *data, int w, int h, float alpha_adjust, float satur_adjust, float bright_adjust)

View File

@@ -51,6 +51,7 @@ void copy_file(const char *path_src, const char *path_dest);
gboolean parse_line(const char *line, char **key, char **value);
void extract_values(const char *value, char **value1, char **value2, char **value3);
void extract_values_4(const char *value, char **value1, char **value2, char **value3, char **value4);
// Executes a command in a shell.
void tint_exec(const char *command);

278
src/util/gradient.c Normal file
View File

@@ -0,0 +1,278 @@
#include "gradient.h"
#include <glib.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
gboolean read_double(const char *str, double *value)
{
if (!str[0])
return FALSE;
char *end;
*value = strtod(str, &end);
if (end[0])
return FALSE;
return TRUE;
}
gboolean read_double_with_percent(const char *str, double *value)
{
if (!str[0])
return FALSE;
char *end;
*value = strtod(str, &end);
if (end[0] == '%' && !end[1]) {
*value *= 0.01;
return TRUE;
}
if (end[0])
return FALSE;
return TRUE;
}
GradientType gradient_type_from_string(const char *str)
{
if (g_str_equal(str, "horizontal"))
return GRADIENT_HORIZONTAL;
if (g_str_equal(str, "vertical"))
return GRADIENT_VERTICAL;
if (g_str_equal(str, "centered"))
return GRADIENT_CENTERED;
if (g_str_equal(str, "linear"))
return GRADIENT_LINEAR;
if (g_str_equal(str, "radial"))
return GRADIENT_RADIAL;
fprintf(stderr, RED "Invalid gradient type: %s" RESET "\n", str);
return GRADIENT_VERTICAL;
}
gboolean read_origin_from_string(const char *str, Origin *element)
{
if (g_str_equal(str, "element")) {
*element = ORIGIN_ELEMENT;
return TRUE;
}
if (g_str_equal(str, "parent")) {
*element = ORIGIN_PARENT;
return TRUE;
}
if (g_str_equal(str, "panel")) {
*element = ORIGIN_PANEL;
return TRUE;
}
if (g_str_equal(str, "screen")) {
*element = ORIGIN_SCREEN;
return TRUE;
}
if (g_str_equal(str, "desktop")) {
*element = ORIGIN_DESKTOP;
return TRUE;
}
return FALSE;
}
Origin origin_from_string(const char *str)
{
Origin result;
if (read_origin_from_string(str, &result))
return result;
fprintf(stderr, RED "Invalid origin type: %s" RESET "\n", str);
return ORIGIN_ELEMENT;
}
gboolean read_size_from_string(const char *str, SizeVariable *variable)
{
if (g_str_equal(str, "width")) {
*variable = SIZE_WIDTH;
return TRUE;
}
if (g_str_equal(str, "height")) {
*variable = SIZE_HEIGHT;
return TRUE;
}
if (g_str_equal(str, "left")) {
*variable = SIZE_LEFT;
return TRUE;
}
if (g_str_equal(str, "right")) {
*variable = SIZE_RIGHT;
return TRUE;
}
if (g_str_equal(str, "top")) {
*variable = SIZE_TOP;
return TRUE;
}
if (g_str_equal(str, "bottom")) {
*variable = SIZE_BOTTOM;
return TRUE;
}
if (g_str_equal(str, "center")) {
*variable = SIZE_CENTER;
return TRUE;
}
if (g_str_equal(str, "radius")) {
*variable = SIZE_RADIUS;
return TRUE;
}
return FALSE;
}
gboolean read_size_variable_from_string(const char *str,
Origin *variable_element,
SizeVariable *variable,
double *multiplier)
{
if (read_size_from_string(str, variable)) {
*variable_element = ORIGIN_ELEMENT;
*multiplier = 1;
return TRUE;
}
char *value1 = 0, *value2 = 0, *value3 = 0, *value4 = 0;
extract_values_4(str, &value1, &value2, &value3, &value4);
if (value1 && value2 && !value3) {
if (read_origin_from_string(value1, variable_element) && read_size_from_string(value2, variable)) {
*multiplier = 1;
if (value1)
free(value1);
if (value2)
free(value2);
if (value3)
free(value3);
if (value4)
free(value4);
return TRUE;
}
}
if (value1 && value2 && value3 && value4) {
if (read_origin_from_string(value1, variable_element) && read_size_from_string(value2, variable) &&
g_str_equal(value3, "*") && read_double_with_percent(value4, multiplier)) {
if (value1)
free(value1);
if (value2)
free(value2);
if (value3)
free(value3);
if (value4)
free(value4);
return TRUE;
}
}
if (value1)
free(value1);
if (value2)
free(value2);
if (value3)
free(value3);
if (value4)
free(value4);
return FALSE;
}
Offset *offset_from_string(const char *str)
{
Offset *offset = (Offset *)calloc(1, sizeof(Offset));
// number ?
if (read_double(str, &offset->constant_value)) {
offset->constant = TRUE;
return offset;
}
// SIZE ?
offset->constant = FALSE;
if (read_size_variable_from_string(str, &offset->variable_element, &offset->variable, &offset->multiplier)) {
return offset;
}
free(offset);
return NULL;
}
void init_gradient(GradientClass *g, GradientType type)
{
memset(g, 0, sizeof(*g));
g->type = type;
if (g->type == GRADIENT_VERTICAL) {
g->from.origin = ORIGIN_ELEMENT;
Offset *offset_top = (Offset *)calloc(1, sizeof(Offset));
offset_top->constant = TRUE;
offset_top->constant_value = 0;
g->from.offsets_y = g_list_append(g->from.offsets_y, offset_top);
Offset *offset_bottom = (Offset *)calloc(1, sizeof(Offset));
offset_bottom->constant = FALSE;
offset_bottom->variable_element = ORIGIN_ELEMENT;
offset_bottom->variable = SIZE_HEIGHT;
offset_bottom->multiplier = 1.0;
g->from.offsets_y = g_list_append(g->from.offsets_y, offset_bottom);
} else if (g->type == GRADIENT_HORIZONTAL) {
g->from.origin = ORIGIN_ELEMENT;
Offset *offset_left = (Offset *)calloc(1, sizeof(Offset));
offset_left->constant = TRUE;
offset_left->constant_value = 0;
g->from.offsets_x = g_list_append(g->from.offsets_x, offset_left);
Offset *offset_right = (Offset *)calloc(1, sizeof(Offset));
offset_right->constant = FALSE;
offset_right->variable_element = ORIGIN_ELEMENT;
offset_right->variable = SIZE_WIDTH;
offset_right->multiplier = 1.0;
g->from.offsets_x = g_list_append(g->from.offsets_x, offset_right);
} else if (g->type == GRADIENT_CENTERED) {
g->from.origin = ORIGIN_ELEMENT;
Offset *offset_center_x = (Offset *)calloc(1, sizeof(Offset));
offset_center_x->constant = FALSE;
offset_center_x->variable_element = ORIGIN_ELEMENT;
offset_center_x->variable = SIZE_CENTER;
offset_center_x->multiplier = 1.0;
g->from.offsets_x = g_list_append(g->from.offsets_x, offset_center_x);
Offset *offset_center_y = (Offset *)calloc(1, sizeof(Offset));
offset_center_y->constant = FALSE;
offset_center_y->variable_element = ORIGIN_ELEMENT;
offset_center_y->variable = SIZE_CENTER;
offset_center_y->multiplier = 1.0;
g->from.offsets_y = g_list_append(g->from.offsets_y, offset_center_y);
Offset *offset_center_r = (Offset *)calloc(1, sizeof(Offset));
offset_center_x->constant = TRUE;
offset_center_x->constant_value = 0;
g->from.offsets_r = g_list_append(g->from.offsets_r, offset_center_r);
g->to.origin = ORIGIN_ELEMENT;
offset_center_x = (Offset *)calloc(1, sizeof(Offset));
offset_center_x->constant = FALSE;
offset_center_x->variable_element = ORIGIN_ELEMENT;
offset_center_x->variable = SIZE_CENTER;
offset_center_x->multiplier = 1.0;
g->to.offsets_x = g_list_append(g->to.offsets_x, offset_center_x);
offset_center_y = (Offset *)calloc(1, sizeof(Offset));
offset_center_y->constant = FALSE;
offset_center_y->variable_element = ORIGIN_ELEMENT;
offset_center_y->variable = SIZE_CENTER;
offset_center_y->multiplier = 1.0;
g->to.offsets_y = g_list_append(g->to.offsets_y, offset_center_y);
offset_center_r = (Offset *)calloc(1, sizeof(Offset));
offset_center_r->constant = FALSE;
offset_center_r->variable_element = ORIGIN_ELEMENT;
offset_center_r->variable = SIZE_RADIUS;
offset_center_r->multiplier = 1.0;
g->to.offsets_r = g_list_append(g->to.offsets_r, offset_center_r);
} else if (g->type == GRADIENT_LINEAR) {
// Nothing to do, the user has to add control points
} else if (g->type == GRADIENT_RADIAL) {
// Nothing to do, the user has to add control points
}
}
void cleanup_gradient(GradientClass *g)
{
g_list_free_full(g->extra_color_stops, free);
g_list_free_full(g->from.offsets_x, free);
g_list_free_full(g->from.offsets_y, free);
g_list_free_full(g->from.offsets_r, free);
g_list_free_full(g->to.offsets_x, free);
g_list_free_full(g->to.offsets_y, free);
g_list_free_full(g->to.offsets_r, free);
}

115
src/util/gradient.h Normal file
View File

@@ -0,0 +1,115 @@
#ifndef GRADIENT_H
#define GRADIENT_H
#include <glib.h>
#include <cairo.h>
#include "color.h"
//////////////////////////////////////////////////////////////////////
// Gradient types read from config options, not associated to any area
typedef enum GradientType {
GRADIENT_VERTICAL = 0,
GRADIENT_HORIZONTAL,
GRADIENT_CENTERED,
GRADIENT_LINEAR,
GRADIENT_RADIAL
} GradientType;
typedef struct ColorStop {
Color color;
// offset in 0-1
double offset;
} ColorStop;
typedef enum Origin {
ORIGIN_ELEMENT = 0,
ORIGIN_PARENT,
ORIGIN_PANEL,
ORIGIN_SCREEN,
ORIGIN_DESKTOP
} Origin;
typedef enum SizeVariable {
SIZE_WIDTH = 0,
SIZE_HEIGHT,
SIZE_LEFT,
SIZE_RIGHT,
SIZE_TOP,
SIZE_BOTTOM,
SIZE_CENTER,
SIZE_RADIUS
} SizeVariable;
typedef struct Offset {
gboolean constant;
// if constant == true
double constant_value;
// else
Origin variable_element;
SizeVariable variable;
double multiplier;
} Offset;
typedef struct ControlPoint {
Origin origin;
// Each element is an Offset
GList *offsets_x;
GList *offsets_y;
// Defined only for radial gradients
GList *offsets_r;
} ControlPoint;
typedef struct GradientClass {
GradientType type;
Color start_color;
Color end_color;
// Each element is a ColorStop
GList *extra_color_stops;
ControlPoint from;
ControlPoint to;
} GradientClass;
GradientType gradient_type_from_string(const char *str);
Origin origin_from_string(const char *str);
Offset *offset_from_string(const char *str);
void init_gradient(GradientClass *g, GradientType type);
void cleanup_gradient(GradientClass *g);
/////////////////////////////////////////
// Gradient instances associated to Areas
struct Area;
typedef struct Area Area;
typedef struct OffsetInstance {
gboolean constant;
// if constant == true
double constant_value;
// else
Area *variable_element;
SizeVariable variable;
double multiplier;
} OffsetInstance;
typedef struct ControlPointInstance {
Area *origin;
// Each element is an OffsetInstance
GList *offsets_x;
GList *offsets_y;
GList *offsets_r;
} ControlPointInstance;
typedef struct GradientInstance {
GradientClass *gradient_class;
Area *area;
ControlPointInstance from;
ControlPointInstance to;
cairo_pattern_t *pattern;
// Each element is an Area whose geometry is used to compute this gradient
// TODO why do we need it?
GList *gradient_dependencies;
} GradientInstance;
#endif // GRADIENT_H