summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthias Clasen <mclasen@redhat.com>2021-01-28 01:43:57 +0000
committerMatthias Clasen <mclasen@redhat.com>2021-01-28 01:43:57 +0000
commitc60247f51fec81923c827388a6b61774e61db94d (patch)
tree5c7aab254001bf194fb8a86a072695fe5a944cdb
parent2e7923cad01ed6fe41f37c33ec87c7399db2f8f5 (diff)
parenta1dd6521e8310fd81073aa5282465092ff182d80 (diff)
downloadgtk+-c60247f51fec81923c827388a6b61774e61db94d.tar.gz
Merge branch 'wip/fl/gl-gradients' into 'master'
OpenGL gradient rendering improvements and additions See merge request GNOME/gtk!3105
-rw-r--r--gsk/gl/gskglrenderer.c53
-rw-r--r--gsk/gl/gskglrenderops.c8
-rw-r--r--gsk/gl/gskglrenderopsprivate.h18
-rw-r--r--gsk/gl/opbuffer.h4
-rw-r--r--gsk/gskrendernode.h2
-rw-r--r--gsk/gskrendernodeimpl.c27
-rw-r--r--gsk/resources/glsl/conic_gradient.glsl94
-rw-r--r--gsk/resources/glsl/linear_gradient.glsl121
-rw-r--r--gsk/resources/glsl/preamble.glsl10
-rw-r--r--gsk/resources/glsl/radial_gradient.glsl98
10 files changed, 256 insertions, 179 deletions
diff --git a/gsk/gl/gskglrenderer.c b/gsk/gl/gskglrenderer.c
index c1ef336eed..7f1aa2c6cd 100644
--- a/gsk/gl/gskglrenderer.c
+++ b/gsk/gl/gskglrenderer.c
@@ -1452,6 +1452,7 @@ render_linear_gradient_node (GskGLRenderer *self,
ops_set_linear_gradient (builder,
n_color_stops,
stops,
+ gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE,
builder->dx + start->x,
builder->dy + start->y,
builder->dx + end->x,
@@ -1485,6 +1486,7 @@ render_radial_gradient_node (GskGLRenderer *self,
ops_set_radial_gradient (builder,
n_color_stops,
stops,
+ gsk_render_node_get_node_type (node) == GSK_REPEATING_RADIAL_GRADIENT_NODE,
builder->dx + center->x,
builder->dy + center->y,
start, end,
@@ -1510,7 +1512,7 @@ render_conic_gradient_node (GskGLRenderer *self,
{
const GskColorStop *stops = gsk_conic_gradient_node_get_color_stops (node, NULL);
const graphene_point_t *center = gsk_conic_gradient_node_get_center (node);
- const float rotation = gsk_conic_gradient_node_get_rotation (node);
+ const float angle = gsk_conic_gradient_node_get_angle (node);
ops_set_program (builder, &self->programs->conic_gradient_program);
ops_set_conic_gradient (builder,
@@ -1518,7 +1520,7 @@ render_conic_gradient_node (GskGLRenderer *self,
stops,
builder->dx + center->x,
builder->dy + center->y,
- rotation);
+ angle);
load_vertex_data (ops_draw (builder, NULL), &node->bounds, builder);
}
@@ -3041,14 +3043,19 @@ apply_linear_gradient_op (const Program *program,
op->n_color_stops.value * 5,
(float *)op->color_stops.value);
- glUniform2f (program->linear_gradient.start_point_location, op->start_point[0], op->start_point[1]);
- glUniform2f (program->linear_gradient.end_point_location, op->end_point[0], op->end_point[1]);
+ glUniform4f (program->linear_gradient.points_location,
+ op->start_point[0], op->start_point[1],
+ op->end_point[0] - op->start_point[0], op->end_point[1] - op->start_point[1]);
+ glUniform1i (program->linear_gradient.repeat_location, op->repeat);
}
static inline void
apply_radial_gradient_op (const Program *program,
const OpRadialGradient *op)
{
+ float scale;
+ float bias;
+
OP_PRINT (" -> Radial gradient");
if (op->n_color_stops.send)
glUniform1i (program->radial_gradient.num_color_stops_location, op->n_color_stops.value);
@@ -3058,16 +3065,23 @@ apply_radial_gradient_op (const Program *program,
op->n_color_stops.value * 5,
(float *)op->color_stops.value);
- glUniform1f (program->radial_gradient.start_location, op->start);
- glUniform1f (program->radial_gradient.end_location, op->end);
- glUniform2f (program->radial_gradient.radius_location, op->radius[0], op->radius[1]);
- glUniform2f (program->radial_gradient.center_location, op->center[0], op->center[1]);
+ scale = 1.0f / (op->end - op->start);
+ bias = -op->start * scale;
+
+ glUniform1i (program->radial_gradient.repeat_location, op->repeat);
+ glUniform2f (program->radial_gradient.range_location, scale, bias);
+ glUniform4f (program->radial_gradient.geometry_location,
+ op->center[0], op->center[1],
+ 1.0f / op->radius[0], 1.0f / op->radius[1]);
}
static inline void
apply_conic_gradient_op (const Program *program,
const OpConicGradient *op)
{
+ float bias;
+ float scale;
+
OP_PRINT (" -> Conic gradient");
if (op->n_color_stops.send)
glUniform1i (program->conic_gradient.num_color_stops_location, op->n_color_stops.value);
@@ -3077,8 +3091,9 @@ apply_conic_gradient_op (const Program *program,
op->n_color_stops.value * 5,
(float *)op->color_stops.value);
- glUniform1f (program->conic_gradient.rotation_location, op->rotation);
- glUniform2f (program->conic_gradient.center_location, op->center[0], op->center[1]);
+ scale = 0.5f * M_1_PI;
+ bias = op->angle * scale + 2.0f;
+ glUniform4f (program->conic_gradient.geometry_location, op->center[0], op->center[1], scale, bias);
}
static inline void
@@ -3368,22 +3383,20 @@ gsk_gl_renderer_create_programs (GskGLRenderer *self,
/* linear gradient */
INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, color_stops);
INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, num_color_stops);
- INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, start_point);
- INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, end_point);
+ INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, repeat);
+ INIT_PROGRAM_UNIFORM_LOCATION (linear_gradient, points);
/* radial gradient */
INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, color_stops);
INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, num_color_stops);
- INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, center);
- INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, start);
- INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, end);
- INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, radius);
+ INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, repeat);
+ INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, geometry);
+ INIT_PROGRAM_UNIFORM_LOCATION (radial_gradient, range);
/* conic gradient */
INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, color_stops);
INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, num_color_stops);
- INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, center);
- INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, rotation);
+ INIT_PROGRAM_UNIFORM_LOCATION (conic_gradient, geometry);
/* blur */
INIT_PROGRAM_UNIFORM_LOCATION (blur, blur_radius);
@@ -3729,10 +3742,12 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
break;
case GSK_LINEAR_GRADIENT_NODE:
+ case GSK_REPEATING_LINEAR_GRADIENT_NODE:
render_linear_gradient_node (self, node, builder);
break;
case GSK_RADIAL_GRADIENT_NODE:
+ case GSK_REPEATING_RADIAL_GRADIENT_NODE:
render_radial_gradient_node (self, node, builder);
break;
@@ -3799,8 +3814,6 @@ gsk_gl_renderer_add_render_ops (GskGLRenderer *self,
render_gl_shader_node (self, node, builder);
break;
- case GSK_REPEATING_LINEAR_GRADIENT_NODE:
- case GSK_REPEATING_RADIAL_GRADIENT_NODE:
case GSK_CAIRO_NODE:
default:
{
diff --git a/gsk/gl/gskglrenderops.c b/gsk/gl/gskglrenderops.c
index 8bd420e7bd..36f7e37e35 100644
--- a/gsk/gl/gskglrenderops.c
+++ b/gsk/gl/gskglrenderops.c
@@ -859,6 +859,7 @@ void
ops_set_linear_gradient (RenderOpBuilder *self,
guint n_color_stops,
const GskColorStop *color_stops,
+ gboolean repeat,
float start_x,
float start_y,
float end_x,
@@ -912,6 +913,7 @@ ops_set_linear_gradient (RenderOpBuilder *self,
sizeof (GskColorStop) * real_n_color_stops);
}
+ op->repeat = repeat;
op->start_point[0] = start_x;
op->start_point[1] = start_y;
op->end_point[0] = end_x;
@@ -922,6 +924,7 @@ void
ops_set_radial_gradient (RenderOpBuilder *self,
guint n_color_stops,
const GskColorStop *color_stops,
+ gboolean repeat,
float center_x,
float center_y,
float start,
@@ -945,6 +948,7 @@ ops_set_radial_gradient (RenderOpBuilder *self,
op->radius[1] = vradius;
op->start = start;
op->end = end;
+ op->repeat = repeat;
}
void
@@ -953,7 +957,7 @@ ops_set_conic_gradient (RenderOpBuilder *self,
const GskColorStop *color_stops,
float center_x,
float center_y,
- float rotation)
+ float angle)
{
const guint real_n_color_stops = MIN (GL_MAX_GRADIENT_STOPS, n_color_stops);
OpConicGradient *op;
@@ -967,6 +971,6 @@ ops_set_conic_gradient (RenderOpBuilder *self,
op->color_stops.send = true;
op->center[0] = center_x;
op->center[1] = center_y;
- op->rotation = rotation;
+ op->angle = angle;
}
diff --git a/gsk/gl/gskglrenderopsprivate.h b/gsk/gl/gskglrenderopsprivate.h
index 24544b0d44..b23060014b 100644
--- a/gsk/gl/gskglrenderopsprivate.h
+++ b/gsk/gl/gskglrenderopsprivate.h
@@ -118,22 +118,20 @@ struct _Program
struct {
int num_color_stops_location;
int color_stops_location;
- int start_point_location;
- int end_point_location;
+ int points_location;
+ int repeat_location;
} linear_gradient;
struct {
int num_color_stops_location;
int color_stops_location;
- int center_location;
- int start_location;
- int end_location;
- int radius_location;
+ int geometry_location;
+ int range_location;
+ int repeat_location;
} radial_gradient;
struct {
int num_color_stops_location;
int color_stops_location;
- int center_location;
- int rotation_location;
+ int geometry_location;
} conic_gradient;
struct {
int blur_radius_location;
@@ -319,6 +317,7 @@ void ops_set_unblurred_outset_shadow (RenderOpBuilder *se
void ops_set_linear_gradient (RenderOpBuilder *self,
guint n_color_stops,
const GskColorStop *color_stops,
+ gboolean repeat,
float start_x,
float start_y,
float end_x,
@@ -326,6 +325,7 @@ void ops_set_linear_gradient (RenderOpBuilder *self,
void ops_set_radial_gradient (RenderOpBuilder *self,
guint n_color_stops,
const GskColorStop *color_stops,
+ gboolean repeat,
float center_x,
float center_y,
float start,
@@ -337,7 +337,7 @@ void ops_set_conic_gradient (RenderOpBuilder *self,
const GskColorStop *color_stops,
float center_x,
float center_y,
- float rotation);
+ float angle);
GskQuadVertex * ops_draw (RenderOpBuilder *builder,
const GskQuadVertex vertex_data[GL_N_VERTICES]);
diff --git a/gsk/gl/opbuffer.h b/gsk/gl/opbuffer.h
index db9b5c9425..ea954249c5 100644
--- a/gsk/gl/opbuffer.h
+++ b/gsk/gl/opbuffer.h
@@ -146,6 +146,7 @@ typedef struct
IntUniformValue n_color_stops;
float start_point[2];
float end_point[2];
+ gboolean repeat;
} OpLinearGradient;
typedef struct
@@ -156,6 +157,7 @@ typedef struct
float end;
float radius[2];
float center[2];
+ gboolean repeat;
} OpRadialGradient;
typedef struct
@@ -163,7 +165,7 @@ typedef struct
ColorStopUniformValue color_stops;
IntUniformValue n_color_stops;
float center[2];
- float rotation;
+ float angle;
} OpConicGradient;
typedef struct
diff --git a/gsk/gskrendernode.h b/gsk/gskrendernode.h
index 99af00286f..5ae6bf2f89 100644
--- a/gsk/gskrendernode.h
+++ b/gsk/gskrendernode.h
@@ -257,6 +257,8 @@ const graphene_point_t * gsk_conic_gradient_node_get_center (GskRenderNo
GDK_AVAILABLE_IN_ALL
float gsk_conic_gradient_node_get_rotation (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
+float gsk_conic_gradient_node_get_angle (GskRenderNode *node);
+GDK_AVAILABLE_IN_ALL
gsize gsk_conic_gradient_node_get_n_color_stops (GskRenderNode *node);
GDK_AVAILABLE_IN_ALL
const GskColorStop * gsk_conic_gradient_node_get_color_stops (GskRenderNode *node,
diff --git a/gsk/gskrendernodeimpl.c b/gsk/gskrendernodeimpl.c
index fb6c659baf..f932c38185 100644
--- a/gsk/gskrendernodeimpl.c
+++ b/gsk/gskrendernodeimpl.c
@@ -782,6 +782,7 @@ struct _GskConicGradientNode
graphene_point_t center;
float rotation;
+ float angle;
gsize n_stops;
GskColorStop *stops;
@@ -1023,6 +1024,12 @@ gsk_conic_gradient_node_new (const graphene_rect_t *bounds,
self->stops = g_malloc_n (n_color_stops, sizeof (GskColorStop));
memcpy (self->stops, color_stops, n_color_stops * sizeof (GskColorStop));
+ self->angle = 90.f - self->rotation;
+ self->angle = G_PI * self->angle / 180.f;
+ self->angle = fmodf (self->angle, 2.f * G_PI);
+ if (self->angle < 0.f)
+ self->angle += 2.f * G_PI;
+
return node;
}
@@ -1095,6 +1102,26 @@ gsk_conic_gradient_node_get_rotation (GskRenderNode *node)
return self->rotation;
}
+/**
+ * gsk_conic_gradient_node_get_angle:
+ * @node: (type GskConicGradientNode): a #GskRenderNode for a conic gradient
+ *
+ * Retrieves the angle for the gradient in radians, normalized in [0, 2 * PI]
+ *
+ * The angle is starting at the top and going clockwise, as expressed
+ * in the css specification:
+ * angle = 90 - gsk_conic_gradient_node_get_rotation()
+ *
+ * Returns: the angle for the gradient
+ */
+float
+gsk_conic_gradient_node_get_angle (GskRenderNode *node)
+{
+ GskConicGradientNode *self = (GskConicGradientNode *) node;
+
+ return self->angle;
+}
+
/*** GSK_BORDER_NODE ***/
/**
diff --git a/gsk/resources/glsl/conic_gradient.glsl b/gsk/resources/glsl/conic_gradient.glsl
index 7f73508a3f..630a42c5e6 100644
--- a/gsk/resources/glsl/conic_gradient.glsl
+++ b/gsk/resources/glsl/conic_gradient.glsl
@@ -1,34 +1,17 @@
// VERTEX_SHADER
-uniform vec2 u_center;
-uniform float u_rotation;
-uniform float u_color_stops[6 * 5];
-uniform int u_num_color_stops;
-
-const float PI = 3.1415926535897932384626433832795;
+uniform vec4 u_geometry;
-_OUT_ vec2 center;
-_OUT_ float rotation;
-_OUT_ vec4 color_stops[6];
-_OUT_ float color_offsets[6];
+_NOPERSPECTIVE_ _OUT_ vec2 coord;
void main() {
- gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
-
- // The -90 is because conics point to the top by default
- rotation = mod (u_rotation - 90.0, 360.0);
- if (rotation < 0.0)
- rotation += 360.0;
- rotation = PI / 180.0 * rotation;
-
- center = (u_modelview * vec4(u_center, 0, 1)).xy;
-
- for (int i = 0; i < u_num_color_stops; i ++) {
- color_offsets[i] = u_color_stops[(i * 5) + 0];
- color_stops[i] = gsk_premultiply(vec4(u_color_stops[(i * 5) + 1],
- u_color_stops[(i * 5) + 2],
- u_color_stops[(i * 5) + 3],
- u_color_stops[(i * 5) + 4]));
- }
+ gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
+
+ vec2 mv0 = u_modelview[0].xy;
+ vec2 mv1 = u_modelview[1].xy;
+ vec2 offset = aPosition - u_geometry.xy;
+
+ coord = vec2(dot(mv0, offset),
+ dot(mv1, offset));
}
// FRAGMENT_SHADER:
@@ -38,32 +21,53 @@ uniform int u_num_color_stops;
uniform highp int u_num_color_stops; // Why? Because it works like this.
#endif
-const float PI = 3.1415926535897932384626433832795;
+uniform vec4 u_geometry;
+uniform float u_color_stops[6 * 5];
-_IN_ vec2 center;
-_IN_ float rotation;
-_IN_ vec4 color_stops[6];
-_IN_ float color_offsets[6];
+_NOPERSPECTIVE_ _IN_ vec2 coord;
-void main() {
- // Position relative to center
- vec2 pos = gsk_get_frag_coord() - center;
+float get_offset(int index) {
+ return u_color_stops[5 * index];
+}
+vec4 get_color(int index) {
+ int base = 5 * index + 1;
+
+ return vec4(u_color_stops[base],
+ u_color_stops[base + 1],
+ u_color_stops[base + 2],
+ u_color_stops[base + 3]);
+}
+
+void main() {
// direction of point in range [-PI, PI]
- float angle = atan (pos.y, pos.x);
- // rotate, it's now [-2 * PI, PI]
- angle -= rotation;
+ vec2 pos = floor(coord);
+ float angle = atan(pos.y, pos.x);
+
// fract() does the modulo here, so now we have progress
// into the current conic
- float offset = fract (angle / 2.0 / PI + 2.0);
+ float offset = fract(angle * u_geometry.z + u_geometry.w);
+
+ if (offset < get_offset(0)) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
+ return;
+ }
+
+ int n = u_num_color_stops - 1;
+ for (int i = 0; i < n; i++) {
+ float curr_offset = get_offset(i);
+ float next_offset = get_offset(i + 1);
+
+ if (offset >= curr_offset && offset < next_offset) {
+ float f = (offset - curr_offset) / (next_offset - curr_offset);
+ vec4 curr_color = gsk_premultiply(get_color(i));
+ vec4 next_color = gsk_premultiply(get_color(i + 1));
+ vec4 color = mix(curr_color, next_color, f);
- vec4 color = color_stops[0];
- for (int i = 1; i < u_num_color_stops; i ++) {
- if (offset >= color_offsets[i - 1]) {
- float o = (offset - color_offsets[i - 1]) / (color_offsets[i] - color_offsets[i - 1]);
- color = mix(color_stops[i - 1], color_stops[i], clamp(o, 0.0, 1.0));
+ gskSetOutputColor(color * u_alpha);
+ return;
}
}
- gskSetOutputColor(color * u_alpha);
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(n), u_alpha));
}
diff --git a/gsk/resources/glsl/linear_gradient.glsl b/gsk/resources/glsl/linear_gradient.glsl
index 588d9ec744..cc90392c06 100644
--- a/gsk/resources/glsl/linear_gradient.glsl
+++ b/gsk/resources/glsl/linear_gradient.glsl
@@ -1,35 +1,41 @@
// VERTEX_SHADER
-uniform vec2 u_start_point;
-uniform vec2 u_end_point;
-uniform float u_color_stops[6 * 5];
-uniform int u_num_color_stops;
+uniform vec4 u_points;
-_OUT_ vec2 startPoint;
-_OUT_ vec2 endPoint;
-_OUT_ float maxDist;
-_OUT_ vec2 gradient;
-_OUT_ float gradientLength;
-_OUT_ vec4 color_stops[6];
-_OUT_ float color_offsets[6];
+_NOPERSPECTIVE_ _OUT_ vec4 info;
void main() {
- gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
-
- startPoint = (u_modelview * vec4(u_start_point, 0, 1)).xy;
- endPoint = (u_modelview * vec4(u_end_point, 0, 1)).xy;
- maxDist = length(endPoint - startPoint);
-
- // Gradient direction
- gradient = endPoint - startPoint;
- gradientLength = length(gradient);
-
- for (int i = 0; i < u_num_color_stops; i ++) {
- color_offsets[i] = u_color_stops[(i * 5) + 0];
- color_stops[i] = gsk_premultiply(vec4(u_color_stops[(i * 5) + 1],
- u_color_stops[(i * 5) + 2],
- u_color_stops[(i * 5) + 3],
- u_color_stops[(i * 5) + 4]));
- }
+ gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
+
+ vec2 mv0 = u_modelview[0].xy;
+ vec2 mv1 = u_modelview[1].xy;
+ vec2 offset = aPosition - u_points.xy;
+ vec2 coord = vec2(dot(mv0, offset),
+ dot(mv1, offset));
+
+ // Original equation:
+ // VS | maxDist = length(end - start);
+ // VS | gradient = end - start;
+ // VS | gradientLength = length(gradient);
+ // FS | pos = frag_coord - start
+ // FS | proj = (dot(gradient, pos) / (gradientLength * gradientLength)) * gradient
+ // FS | offset = length(proj) / maxDist
+
+ // Simplified formula derivation:
+ // 1. Notice that maxDist = gradientLength:
+ // offset = length(proj) / gradientLength
+ // 2. Let gnorm = gradient / gradientLength, then:
+ // proj = (dot(gnorm * gradientLength, pos) / (gradientLength * gradientLength)) * (gnorm * gradientLength) =
+ // = dot(gnorm, pos) * gnorm
+ // 3. Since gnorm is unit length then:
+ // length(proj) = length(dot(gnorm, pos) * gnorm) = dot(gnorm, pos)
+ // 4. We can avoid the FS division by passing a scaled pos from the VS:
+ // offset = dot(gnorm, pos) / gradientLength = dot(gnorm, pos / gradientLength)
+ // 5. 1.0 / length(gradient) is inversesqrt(dot(gradient, gradient)) in GLSL
+ vec2 gradient = vec2(dot(mv0, u_points.zw),
+ dot(mv1, u_points.zw));
+ float rcp_gradient_length = inversesqrt(dot(gradient, gradient));
+
+ info = rcp_gradient_length * vec4(coord, gradient);
}
// FRAGMENT_SHADER:
@@ -39,32 +45,51 @@ uniform int u_num_color_stops;
uniform highp int u_num_color_stops; // Why? Because it works like this.
#endif
-_IN_ vec2 startPoint;
-_IN_ vec2 endPoint;
-_IN_ float maxDist;
-_IN_ vec2 gradient;
-_IN_ float gradientLength;
-_IN_ vec4 color_stops[6];
-_IN_ float color_offsets[6];
+uniform float u_color_stops[6 * 5];
+uniform bool u_repeat;
+
+_NOPERSPECTIVE_ _IN_ vec4 info;
+
+float get_offset(int index) {
+ return u_color_stops[5 * index];
+}
+
+vec4 get_color(int index) {
+ int base = 5 * index + 1;
+
+ return vec4(u_color_stops[base],
+ u_color_stops[base + 1],
+ u_color_stops[base + 2],
+ u_color_stops[base + 3]);
+}
void main() {
- // Position relative to startPoint
- vec2 pos = gsk_get_frag_coord() - startPoint;
+ float offset = dot(info.xy, info.zw);
+
+ if (u_repeat) {
+ offset = fract(offset);
+ }
+
+ if (offset < get_offset(0)) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
+ return;
+ }
- // Current pixel, projected onto the line between the start point and the end point
- // The projection will be relative to the start point!
- vec2 proj = (dot(gradient, pos) / (gradientLength * gradientLength)) * gradient;
+ int n = u_num_color_stops - 1;
+ for (int i = 0; i < n; i++) {
+ float curr_offset = get_offset(i);
+ float next_offset = get_offset(i + 1);
- // Offset of the current pixel
- float offset = length(proj) / maxDist;
+ if (offset >= curr_offset && offset < next_offset) {
+ float f = (offset - curr_offset) / (next_offset - curr_offset);
+ vec4 curr_color = gsk_premultiply(get_color(i));
+ vec4 next_color = gsk_premultiply(get_color(i + 1));
+ vec4 color = mix(curr_color, next_color, f);
- vec4 color = color_stops[0];
- for (int i = 1; i < u_num_color_stops; i ++) {
- if (offset >= color_offsets[i - 1]) {
- float o = (offset - color_offsets[i - 1]) / (color_offsets[i] - color_offsets[i - 1]);
- color = mix(color_stops[i - 1], color_stops[i], clamp(o, 0.0, 1.0));
+ gskSetOutputColor(color * u_alpha);
+ return;
}
}
- gskSetOutputColor(color * u_alpha);
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(n), u_alpha));
}
diff --git a/gsk/resources/glsl/preamble.glsl b/gsk/resources/glsl/preamble.glsl
index 47cc21b004..8ca6469f6d 100644
--- a/gsk/resources/glsl/preamble.glsl
+++ b/gsk/resources/glsl/preamble.glsl
@@ -5,10 +5,12 @@ precision highp float;
#if defined(GSK_GLES) || defined(GSK_LEGACY)
#define _OUT_ varying
#define _IN_ varying
+#define _NOPERSPECTIVE_
#define _GSK_ROUNDED_RECT_UNIFORM_ vec4[3]
#else
#define _OUT_ out
#define _IN_ in
+#define _NOPERSPECTIVE_ noperspective
#define _GSK_ROUNDED_RECT_UNIFORM_ GskRoundedRect
#endif
@@ -39,3 +41,11 @@ gsk_create_rect(vec4[3] data)
vec4 gsk_premultiply(vec4 c) {
return vec4(c.rgb * c.a, c.a);
}
+
+vec4 gsk_scaled_premultiply(vec4 c, float s) {
+ // Fast version of gsk_premultiply(c) * s
+ // 4 muls instead of 7
+ float a = s * c.a;
+
+ return vec4(c.rgb * a, a);
+}
diff --git a/gsk/resources/glsl/radial_gradient.glsl b/gsk/resources/glsl/radial_gradient.glsl
index 178806f83c..0ab3fdf07a 100644
--- a/gsk/resources/glsl/radial_gradient.glsl
+++ b/gsk/resources/glsl/radial_gradient.glsl
@@ -1,31 +1,18 @@
// VERTEX_SHADER
-uniform float u_start;
-uniform float u_end;
-uniform float u_color_stops[6 * 5];
-uniform int u_num_color_stops;
-uniform vec2 u_radius;
-uniform vec2 u_center;
+uniform vec4 u_geometry;
-_OUT_ vec2 center;
-_OUT_ vec4 color_stops[6];
-_OUT_ float color_offsets[6];
-_OUT_ float start;
-_OUT_ float end;
+_NOPERSPECTIVE_ _OUT_ vec2 coord;
void main() {
- gl_Position = u_projection * u_modelview * vec4(aPosition, 0.0, 1.0);
-
- center = (u_modelview * vec4(u_center, 0, 1)).xy;
- start = u_start;
- end = u_end;
-
- for (int i = 0; i < u_num_color_stops; i ++) {
- color_offsets[i] = u_color_stops[(i * 5) + 0];
- color_stops[i] = gsk_premultiply(vec4(u_color_stops[(i * 5) + 1],
- u_color_stops[(i * 5) + 2],
- u_color_stops[(i * 5) + 3],
- u_color_stops[(i * 5) + 4]));
- }
+ gl_Position = u_projection * (u_modelview * vec4(aPosition, 0.0, 1.0));
+
+ vec2 mv0 = u_modelview[0].xy;
+ vec2 mv1 = u_modelview[1].xy;
+ vec2 offset = aPosition - u_geometry.xy;
+ vec2 dir = vec2(dot(mv0, offset),
+ dot(mv1, offset));
+
+ coord = dir * u_geometry.zw;
}
// FRAGMENT_SHADER:
@@ -35,50 +22,53 @@ uniform int u_num_color_stops;
uniform highp int u_num_color_stops;
#endif
-uniform vec2 u_radius;
-uniform float u_end;
+uniform bool u_repeat;
+uniform vec2 u_range;
+uniform float u_color_stops[6 * 5];
-_IN_ vec2 center;
-_IN_ vec4 color_stops[6];
-_IN_ float color_offsets[6];
-_IN_ float start;
-_IN_ float end;
+_NOPERSPECTIVE_ _IN_ vec2 coord;
-// The offsets in the color stops are relative to the
-// start and end values of the gradient.
-float abs_offset(float offset) {
- return start + ((end - start) * offset);
+float get_offset(int index) {
+ return u_color_stops[5 * index];
+}
+
+vec4 get_color(int index) {
+ int base = 5 * index + 1;
+
+ return vec4(u_color_stops[base],
+ u_color_stops[base + 1],
+ u_color_stops[base + 2],
+ u_color_stops[base + 3]);
}
void main() {
- vec2 pixel = gsk_get_frag_coord();
- vec2 rel = (center - pixel) / (u_radius);
- float d = sqrt(dot(rel, rel));
+ // Reverse scale
+ float offset = length(coord) * u_range.x + u_range.y;
- if (d < abs_offset (color_offsets[0])) {
- gskSetOutputColor(color_stops[0] * u_alpha);
- return;
+ if (u_repeat) {
+ offset = fract(offset);
}
- if (d > end) {
- gskSetOutputColor(color_stops[u_num_color_stops - 1] * u_alpha);
+ if (offset < get_offset(0)) {
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(0), u_alpha));
return;
}
- vec4 color = vec4(0, 0, 0, 0);
- for (int i = 1; i < u_num_color_stops; i++) {
- float last_offset = abs_offset(color_offsets[i - 1]);
- float this_offset = abs_offset(color_offsets[i]);
+ int n = u_num_color_stops - 1;
+ for (int i = 0; i < n; i++) {
+ float curr_offset = get_offset(i);
+ float next_offset = get_offset(i + 1);
- // We have color_stops[i - 1] at last_offset and color_stops[i] at this_offset.
- // We now need to map `d` between those two offsets and simply mix linearly between them
- if (d >= last_offset && d <= this_offset) {
- float f = (d - last_offset) / (this_offset - last_offset);
+ if (offset >= curr_offset && offset < next_offset) {
+ float f = (offset - curr_offset) / (next_offset - curr_offset);
+ vec4 curr_color = gsk_premultiply(get_color(i));
+ vec4 next_color = gsk_premultiply(get_color(i + 1));
+ vec4 color = mix(curr_color, next_color, f);
- color = mix(color_stops[i - 1], color_stops[i], f);
- break;
+ gskSetOutputColor(color * u_alpha);
+ return;
}
}
- gskSetOutputColor(color * u_alpha);
+ gskSetOutputColor(gsk_scaled_premultiply(get_color(n), u_alpha));
}