gpu: Add linear gradients to pattern shader

This copy/pastes the gist of the Vulkan gradient renderer.
This commit is contained in:
Benjamin Otte
2023-09-06 19:24:14 +02:00
parent 7d43e4e56a
commit 7473617ffb
8 changed files with 263 additions and 13 deletions

View File

@@ -105,6 +105,19 @@ gsk_gpu_buffer_writer_append_vec4 (GskGpuBufferWriter *self,
gsk_gpu_buffer_writer_append (self, G_ALIGNOF (float), (guchar *) f, sizeof (f));
}
void
gsk_gpu_buffer_writer_append_point (GskGpuBufferWriter *self,
const graphene_point_t *point,
const graphene_point_t *offset)
{
float f[2];
f[0] = point->x + offset->x;
f[1] = point->y + offset->y;
gsk_gpu_buffer_writer_append (self, G_ALIGNOF (float), (guchar *) f, sizeof (f));
}
void
gsk_gpu_buffer_writer_append_rect (GskGpuBufferWriter *self,
const graphene_rect_t *rect,
@@ -125,3 +138,13 @@ gsk_gpu_buffer_writer_append_rgba (GskGpuBufferWriter *self,
gsk_gpu_buffer_writer_append (self, G_ALIGNOF (float), (guchar *) f, sizeof (f));
}
void
gsk_gpu_buffer_writer_append_color_stops (GskGpuBufferWriter *self,
const GskColorStop *stops,
gsize n_stops)
{
gsk_gpu_buffer_writer_append_uint (self, n_stops);
gsk_gpu_buffer_writer_append (self, G_ALIGNOF (float), (guchar *) stops, sizeof (GskColorStop) * n_stops);
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "gskgputypesprivate.h"
#include "gskrendernode.h"
#include <graphene.h>
@@ -44,10 +45,16 @@ void gsk_gpu_buffer_writer_append_matrix (GskGpuB
const graphene_matrix_t *matrix);
void gsk_gpu_buffer_writer_append_vec4 (GskGpuBufferWriter *self,
const graphene_vec4_t *vec4);
void gsk_gpu_buffer_writer_append_point (GskGpuBufferWriter *self,
const graphene_point_t *point,
const graphene_point_t *offset);
void gsk_gpu_buffer_writer_append_rect (GskGpuBufferWriter *self,
const graphene_rect_t *rect,
const graphene_point_t *offset);
void gsk_gpu_buffer_writer_append_rgba (GskGpuBufferWriter *self,
const GdkRGBA *rgba);
void gsk_gpu_buffer_writer_append_color_stops (GskGpuBufferWriter *self,
const GskColorStop *stops,
gsize n_stops);
G_END_DECLS

View File

@@ -766,6 +766,32 @@ gsk_gpu_node_processor_create_texture_pattern (GskGpuNodeProcessor *self,
return TRUE;
}
static gboolean
gsk_gpu_node_processor_create_linear_gradient_pattern (GskGpuNodeProcessor *self,
GskGpuBufferWriter *writer,
GskRenderNode *node,
GskGpuShaderImage *images,
gsize n_images,
gsize *out_n_images)
{
if (gsk_render_node_get_node_type (node) == GSK_REPEATING_LINEAR_GRADIENT_NODE)
gsk_gpu_buffer_writer_append_uint (writer, GSK_GPU_PATTERN_REPEATING_LINEAR_GRADIENT);
else
gsk_gpu_buffer_writer_append_uint (writer, GSK_GPU_PATTERN_LINEAR_GRADIENT);
gsk_gpu_buffer_writer_append_point (writer,
gsk_linear_gradient_node_get_start (node),
&self->offset);
gsk_gpu_buffer_writer_append_point (writer,
gsk_linear_gradient_node_get_end (node),
&self->offset);
gsk_gpu_buffer_writer_append_color_stops (writer,
gsk_linear_gradient_node_get_color_stops (node, NULL),
gsk_linear_gradient_node_get_n_color_stops (node));
return TRUE;
}
static gboolean
gsk_gpu_node_processor_create_glyph_pattern (GskGpuNodeProcessor *self,
GskGpuBufferWriter *writer,
@@ -944,13 +970,13 @@ static const struct
},
[GSK_LINEAR_GRADIENT_NODE] = {
0,
NULL,
NULL,
gsk_gpu_node_processor_add_node_as_pattern,
gsk_gpu_node_processor_create_linear_gradient_pattern,
},
[GSK_REPEATING_LINEAR_GRADIENT_NODE] = {
0,
NULL,
NULL,
gsk_gpu_node_processor_add_node_as_pattern,
gsk_gpu_node_processor_create_linear_gradient_pattern,
},
[GSK_RADIAL_GRADIENT_NODE] = {
0,

View File

@@ -34,5 +34,7 @@ typedef enum {
GSK_GPU_PATTERN_TEXTURE,
GSK_GPU_PATTERN_COLOR_MATRIX,
GSK_GPU_PATTERN_GLYPHS,
GSK_GPU_PATTERN_LINEAR_GRADIENT,
GSK_GPU_PATTERN_REPEATING_LINEAR_GRADIENT,
} GskGpuPatternType;

View File

@@ -11,5 +11,7 @@
#define GSK_GPU_PATTERN_TEXTURE 3u
#define GSK_GPU_PATTERN_COLOR_MATRIX 4u
#define GSK_GPU_PATTERN_GLYPHS 5u
#define GSK_GPU_PATTERN_LINEAR_GRADIENT 6u
#define GSK_GPU_PATTERN_REPEATING_LINEAR_GRADIENT 7u
#endif

View File

@@ -0,0 +1,147 @@
#ifndef _GRADIENT_
#define _GRADIENT_
#ifdef GSK_FRAGMENT_SHADER
#include "common.glsl"
struct Gradient
{
int n_stops;
uint offset;
};
float
gradient_read_offset (Gradient self,
int i)
{
return gsk_get_float (self.offset + uint(i) * 5u);
}
vec4
gradient_read_color (Gradient self,
int i)
{
uint u = uint (clamp (i, 0, self.n_stops - 1));
return vec4 (gsk_get_float (self.offset + u * 5u + 1u),
gsk_get_float (self.offset + u * 5u + 2u),
gsk_get_float (self.offset + u * 5u + 3u),
gsk_get_float (self.offset + u * 5u + 4u));
}
vec4
gradient_get_color_for_range_unscaled (Gradient self,
float start,
float end)
{
vec4 result = vec4 (0.0);
float offset;
int i;
for (i = 0; i < self.n_stops; i++)
{
offset = gradient_read_offset (self, i);
if (offset >= start)
break;
}
if (i == self.n_stops)
offset = 1.0;
float last_offset = i > 0 ? gradient_read_offset (self, i - 1) : 0.0;
vec4 last_color = gradient_read_color (self, i - 1);
vec4 color = gradient_read_color (self, i);
if (last_offset < start)
{
last_color = mix (last_color, color, (start - last_offset) / (offset - last_offset));
last_offset = start;
}
if (end <= start)
return last_color;
for (; i < self.n_stops; i++)
{
offset = gradient_read_offset (self, i);
color = gradient_read_color (self, i);
if (offset >= end)
break;
result += 0.5 * (color + last_color) * (offset - last_offset);
last_offset = offset;
last_color = color;
}
if (i == self.n_stops)
{
offset = 1.0;
color = gradient_read_color (self, i);
}
if (offset > end)
{
color = mix (last_color, color, (end - last_offset) / (offset - last_offset));
offset = end;
}
result += 0.5 * (color + last_color) * (offset - last_offset);
return result;
}
vec4
gradient_get_color_repeating (Gradient self,
float start,
float end)
{
vec4 c;
if (floor (end) > floor (start))
{
float fract_end = fract(end);
float fract_start = fract(start);
float n = floor (end) - floor (start);
if (fract_end > fract_start + 0.01)
c = gradient_get_color_for_range_unscaled (self, fract_start, fract_end);
else if (fract_start > fract_end + 0.01)
c = - gradient_get_color_for_range_unscaled (self, fract_end, fract_start);
c += gradient_get_color_for_range_unscaled (self, 0.0, 1.0) * n;
}
else
{
start = fract (start);
end = fract (end);
c = gradient_get_color_for_range_unscaled (self, start, end);
}
c /= end - start;
return color_premultiply (c);
}
vec4
gradient_get_color (Gradient self,
float start,
float end)
{
vec4 c;
start = clamp (start, 0.0, 1.0);
end = clamp (end, 0.0, 1.0);
c = gradient_get_color_for_range_unscaled (self, start, end);
if (end > start)
c /= end - start;
return color_premultiply (c);
}
uint
gradient_get_size (Gradient self)
{
return uint (self.n_stops) * 5u + 1u;
}
Gradient
gradient_new (uint offset)
{
Gradient self = Gradient (gsk_get_int (offset),
offset + 1u);
return self;
}
#endif /* GSK_FRAGMENT_SHADER */
#endif /* __GRADIENT__ */

View File

@@ -5,6 +5,7 @@ gsk_private_gpu_include_shaders = files([
'common-vulkan.glsl',
'ellipse.glsl',
'enums.glsl',
'gradient.glsl',
'pattern.glsl',
'rect.glsl',
'roundedrect.glsl',

View File

@@ -2,6 +2,7 @@
#define _PATTERN_
#include "common.glsl"
#include "gradient.glsl"
#include "rect.glsl"
#ifdef GSK_FRAGMENT_SHADER
@@ -22,6 +23,12 @@ read_float (inout uint reader)
return result;
}
vec2
read_vec2 (inout uint reader)
{
return vec2 (read_float (reader), read_float (reader));
}
vec4
read_vec4 (inout uint reader)
{
@@ -37,18 +44,27 @@ read_rect (inout uint reader)
mat4
read_mat4 (inout uint reader)
{
mat4 result = mat4 (gsk_get_float (reader + 0), gsk_get_float (reader + 1),
gsk_get_float (reader + 2), gsk_get_float (reader + 3),
gsk_get_float (reader + 4), gsk_get_float (reader + 5),
gsk_get_float (reader + 6), gsk_get_float (reader + 7),
gsk_get_float (reader + 8), gsk_get_float (reader + 9),
gsk_get_float (reader + 10), gsk_get_float (reader + 11),
gsk_get_float (reader + 12), gsk_get_float (reader + 13),
gsk_get_float (reader + 14), gsk_get_float (reader + 15));
reader += 16;
mat4 result = mat4 (gsk_get_float (reader + 0u), gsk_get_float (reader + 1u),
gsk_get_float (reader + 2u), gsk_get_float (reader + 3u),
gsk_get_float (reader + 4u), gsk_get_float (reader + 5u),
gsk_get_float (reader + 6u), gsk_get_float (reader + 7u),
gsk_get_float (reader + 8u), gsk_get_float (reader + 9u),
gsk_get_float (reader + 10u), gsk_get_float (reader + 11u),
gsk_get_float (reader + 12u), gsk_get_float (reader + 13u),
gsk_get_float (reader + 14u), gsk_get_float (reader + 15u));
reader += 16u;
return result;
}
Gradient
read_gradient (inout uint reader)
{
Gradient gradient = gradient_new (reader);
reader += gradient_get_size (gradient);
return gradient;
}
void
opacity_pattern (inout uint reader,
inout vec4 color,
@@ -108,6 +124,26 @@ texture_pattern (inout uint reader,
return texture (gsk_get_texture (tex_id), (pos - push.scale * tex_rect.xy) / (push.scale * tex_rect.zw));
}
vec4
linear_gradient_pattern (inout uint reader,
vec2 pos,
bool repeating)
{
vec2 start = read_vec2 (reader) * push.scale;
vec2 end = read_vec2 (reader) * push.scale;
Gradient gradient = read_gradient (reader);
vec2 line = end - start;
float line_length = dot (line, line);
float offset = dot (pos - start, line) / line_length;
float d_offset = 0.5 * fwidth (offset);
if (repeating)
return gradient_get_color_repeating (gradient, offset - d_offset, offset + d_offset);
else
return gradient_get_color (gradient, offset - d_offset, offset + d_offset);
}
vec4
color_pattern (inout uint reader)
{
@@ -145,6 +181,12 @@ pattern (uint reader,
case GSK_GPU_PATTERN_OPACITY:
opacity_pattern (reader, color, pos);
break;
case GSK_GPU_PATTERN_LINEAR_GRADIENT:
color = linear_gradient_pattern (reader, pos, false);
break;
case GSK_GPU_PATTERN_REPEATING_LINEAR_GRADIENT:
color = linear_gradient_pattern (reader, pos, true);
break;
}
}
}