mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-11-08 17:50:10 +00:00
gpu: Add a box shadow shader
Code was inspired mainly by https://madebyevan.com/shaders/fast-rounded-rectangle-shadows/ and https://pcwalton.github.io/_posts/2015-12-21-drawing-css-box-shadows-in-webrender.html So far the results aren't cached, that's the task of future commits.
This commit is contained in:
parent
268ad54c6a
commit
d8db673fb7
91
gsk/gpu/gskgpuboxshadowop.c
Normal file
91
gsk/gpu/gskgpuboxshadowop.c
Normal file
@ -0,0 +1,91 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "gskgpuboxshadowopprivate.h"
|
||||
|
||||
#include "gskgpuframeprivate.h"
|
||||
#include "gskgpuprintprivate.h"
|
||||
#include "gskgpushaderopprivate.h"
|
||||
#include "gskrectprivate.h"
|
||||
#include "gsk/gskroundedrectprivate.h"
|
||||
|
||||
#include "gpu/shaders/gskgpuboxshadowinstance.h"
|
||||
|
||||
typedef struct _GskGpuBoxShadowOp GskGpuBoxShadowOp;
|
||||
|
||||
struct _GskGpuBoxShadowOp
|
||||
{
|
||||
GskGpuShaderOp op;
|
||||
};
|
||||
|
||||
static void
|
||||
gsk_gpu_box_shadow_op_print (GskGpuOp *op,
|
||||
GskGpuFrame *frame,
|
||||
GString *string,
|
||||
guint indent)
|
||||
{
|
||||
GskGpuShaderOp *shader = (GskGpuShaderOp *) op;
|
||||
GskGpuBoxshadowInstance *instance;
|
||||
|
||||
instance = (GskGpuBoxshadowInstance *) gsk_gpu_frame_get_vertex_data (frame, shader->vertex_offset);
|
||||
|
||||
gsk_gpu_print_op (string, indent, instance->inset ? "inset-shadow" : "outset-shadow");
|
||||
gsk_gpu_print_rounded_rect (string, instance->outline);
|
||||
gsk_gpu_print_rgba (string, instance->color);
|
||||
g_string_append_printf (string, "%g %g %g %g ",
|
||||
instance->shadow_offset[0], instance->shadow_offset[1],
|
||||
instance->blur_radius, instance->shadow_spread);
|
||||
gsk_gpu_print_newline (string);
|
||||
}
|
||||
|
||||
static const GskGpuShaderOpClass GSK_GPU_BOX_SHADOW_OP_CLASS = {
|
||||
{
|
||||
GSK_GPU_OP_SIZE (GskGpuBoxShadowOp),
|
||||
GSK_GPU_STAGE_SHADER,
|
||||
gsk_gpu_shader_op_finish,
|
||||
gsk_gpu_box_shadow_op_print,
|
||||
#ifdef GDK_RENDERING_VULKAN
|
||||
gsk_gpu_shader_op_vk_command,
|
||||
#endif
|
||||
gsk_gpu_shader_op_gl_command
|
||||
},
|
||||
"gskgpuboxshadow",
|
||||
sizeof (GskGpuBoxshadowInstance),
|
||||
#ifdef GDK_RENDERING_VULKAN
|
||||
&gsk_gpu_boxshadow_info,
|
||||
#endif
|
||||
gsk_gpu_boxshadow_setup_vao
|
||||
};
|
||||
|
||||
void
|
||||
gsk_gpu_box_shadow_op (GskGpuFrame *frame,
|
||||
GskGpuShaderClip clip,
|
||||
gboolean inset,
|
||||
const graphene_rect_t *bounds,
|
||||
const GskRoundedRect *outline,
|
||||
const graphene_point_t *shadow_offset,
|
||||
float spread,
|
||||
float blur_radius,
|
||||
const graphene_point_t *offset,
|
||||
const GdkRGBA *color)
|
||||
{
|
||||
GskGpuBoxshadowInstance *instance;
|
||||
|
||||
/* Use border shader for no blurring */
|
||||
g_return_if_fail (blur_radius > 0.0f);
|
||||
|
||||
gsk_gpu_shader_op_alloc (frame,
|
||||
&GSK_GPU_BOX_SHADOW_OP_CLASS,
|
||||
clip,
|
||||
NULL,
|
||||
&instance);
|
||||
|
||||
gsk_gpu_rect_to_float (bounds, offset, instance->bounds);
|
||||
gsk_rounded_rect_to_float (outline, offset, instance->outline);
|
||||
gsk_gpu_rgba_to_float (color, instance->color);
|
||||
instance->shadow_offset[0] = shadow_offset->x;
|
||||
instance->shadow_offset[1] = shadow_offset->y;
|
||||
instance->shadow_spread = spread;
|
||||
instance->blur_radius = blur_radius;
|
||||
instance->inset = inset ? 1 : 0;
|
||||
}
|
||||
|
23
gsk/gpu/gskgpuboxshadowopprivate.h
Normal file
23
gsk/gpu/gskgpuboxshadowopprivate.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "gskgputypesprivate.h"
|
||||
#include "gsktypes.h"
|
||||
|
||||
#include <graphene.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
void gsk_gpu_box_shadow_op (GskGpuFrame *frame,
|
||||
GskGpuShaderClip clip,
|
||||
gboolean inset,
|
||||
const graphene_rect_t *bounds,
|
||||
const GskRoundedRect *outline,
|
||||
const graphene_point_t *shadow_offset,
|
||||
float spread,
|
||||
float blur_radius,
|
||||
const graphene_point_t *offset,
|
||||
const GdkRGBA *color);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "gskgpunodeprocessorprivate.h"
|
||||
|
||||
#include "gskgpuborderopprivate.h"
|
||||
#include "gskgpuboxshadowopprivate.h"
|
||||
#include "gskgpubluropprivate.h"
|
||||
#include "gskgpuclearopprivate.h"
|
||||
#include "gskgpuclipprivate.h"
|
||||
@ -1195,18 +1196,27 @@ gsk_gpu_node_processor_add_inset_shadow_node (GskGpuNodeProcessor *self,
|
||||
gsk_inset_shadow_node_get_dy (node)),
|
||||
(float[4]) { spread, spread, spread, spread },
|
||||
(GdkRGBA[4]) { *color, *color, *color, *color });
|
||||
return;
|
||||
}
|
||||
|
||||
GSK_DEBUG (FALLBACK, "No blurring for inset shadows");
|
||||
gsk_gpu_node_processor_add_fallback_node (self, node);
|
||||
else
|
||||
{
|
||||
gsk_gpu_box_shadow_op (self->frame,
|
||||
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
|
||||
TRUE,
|
||||
&node->bounds,
|
||||
gsk_inset_shadow_node_get_outline (node),
|
||||
&GRAPHENE_POINT_INIT (gsk_inset_shadow_node_get_dx (node),
|
||||
gsk_inset_shadow_node_get_dy (node)),
|
||||
spread,
|
||||
blur_radius,
|
||||
&self->offset,
|
||||
color);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gsk_gpu_node_processor_add_outset_shadow_node (GskGpuNodeProcessor *self,
|
||||
GskRenderNode *node)
|
||||
{
|
||||
GskRoundedRect outline;
|
||||
const GdkRGBA *color;
|
||||
float spread, blur_radius, dx, dy;
|
||||
|
||||
@ -1216,12 +1226,14 @@ gsk_gpu_node_processor_add_outset_shadow_node (GskGpuNodeProcessor *self,
|
||||
dx = gsk_outset_shadow_node_get_dx (node);
|
||||
dy = gsk_outset_shadow_node_get_dy (node);
|
||||
|
||||
gsk_rounded_rect_init_copy (&outline, gsk_outset_shadow_node_get_outline (node));
|
||||
gsk_rounded_rect_shrink (&outline, -spread, -spread, -spread, -spread);
|
||||
graphene_rect_offset (&outline.bounds, dx, dy);
|
||||
|
||||
if (blur_radius == 0)
|
||||
{
|
||||
GskRoundedRect outline;
|
||||
|
||||
gsk_rounded_rect_init_copy (&outline, gsk_outset_shadow_node_get_outline (node));
|
||||
gsk_rounded_rect_shrink (&outline, -spread, -spread, -spread, -spread);
|
||||
graphene_rect_offset (&outline.bounds, dx, dy);
|
||||
|
||||
gsk_gpu_border_op (self->frame,
|
||||
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
|
||||
&outline,
|
||||
@ -1229,11 +1241,20 @@ gsk_gpu_node_processor_add_outset_shadow_node (GskGpuNodeProcessor *self,
|
||||
&GRAPHENE_POINT_INIT (-dx, -dy),
|
||||
(float[4]) { spread, spread, spread, spread },
|
||||
(GdkRGBA[4]) { *color, *color, *color, *color });
|
||||
return;
|
||||
}
|
||||
|
||||
GSK_DEBUG (FALLBACK, "No blurring for outset shadows");
|
||||
gsk_gpu_node_processor_add_fallback_node (self, node);
|
||||
else
|
||||
{
|
||||
gsk_gpu_box_shadow_op (self->frame,
|
||||
gsk_gpu_clip_get_shader_clip (&self->clip, &self->offset, &node->bounds),
|
||||
FALSE,
|
||||
&node->bounds,
|
||||
gsk_outset_shadow_node_get_outline (node),
|
||||
&GRAPHENE_POINT_INIT (dx, dy),
|
||||
spread,
|
||||
blur_radius,
|
||||
&self->offset,
|
||||
color);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
|
@ -18,6 +18,7 @@ void main_clip_rounded (void);
|
||||
#include "roundedrect.glsl"
|
||||
|
||||
#define PI 3.1415926535897932384626433832795
|
||||
#define SQRT1_2 1.4142135623730951
|
||||
|
||||
Rect
|
||||
rect_clip (Rect r)
|
||||
|
145
gsk/gpu/shaders/gskgpuboxshadow.glsl
Normal file
145
gsk/gpu/shaders/gskgpuboxshadow.glsl
Normal file
@ -0,0 +1,145 @@
|
||||
#include "common.glsl"
|
||||
|
||||
/* blur radius (aka in_blur_direction) 0 is NOT supported and MUST be caught before */
|
||||
|
||||
PASS(0) vec2 _pos;
|
||||
PASS_FLAT(1) RoundedRect _shadow_outline;
|
||||
PASS_FLAT(4) RoundedRect _clip_outline;
|
||||
PASS_FLAT(7) vec4 _color;
|
||||
PASS_FLAT(8) vec2 _sigma;
|
||||
PASS_FLAT(9) uint _inset;
|
||||
|
||||
#ifdef GSK_VERTEX_SHADER
|
||||
|
||||
IN(0) mat3x4 in_outline;
|
||||
IN(3) vec4 in_bounds;
|
||||
IN(4) vec4 in_color;
|
||||
IN(5) vec2 in_shadow_offset;
|
||||
IN(6) float in_shadow_spread;
|
||||
IN(7) float in_blur_radius;
|
||||
IN(8) uint in_inset;
|
||||
|
||||
void
|
||||
run (out vec2 pos)
|
||||
{
|
||||
RoundedRect outline = rounded_rect_from_gsk (in_outline);
|
||||
|
||||
pos = rect_get_position (rect_from_gsk (in_bounds));
|
||||
|
||||
_pos = pos;
|
||||
_clip_outline = outline;
|
||||
vec2 spread = GSK_GLOBAL_SCALE * in_shadow_spread;
|
||||
if (in_inset == 0u)
|
||||
spread = -spread;
|
||||
outline = rounded_rect_shrink (outline, spread.yxyx);
|
||||
rounded_rect_offset (outline, GSK_GLOBAL_SCALE * in_shadow_offset);
|
||||
_shadow_outline = outline;
|
||||
_color = in_color;
|
||||
_sigma = GSK_GLOBAL_SCALE * 0.5 * in_blur_radius;
|
||||
_inset = in_inset;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef GSK_FRAGMENT_SHADER
|
||||
|
||||
/* A standard gaussian function, used for weighting samples */
|
||||
float
|
||||
gauss (float x,
|
||||
float sigma)
|
||||
{
|
||||
float sigma_2 = sigma * sigma;
|
||||
return 1.0 / sqrt (2.0 * PI * sigma_2) * exp (-(x * x) / (2.0 * sigma_2));
|
||||
}
|
||||
|
||||
/* This approximates the error function, needed for the gaussian integral */
|
||||
vec2
|
||||
erf (vec2 x)
|
||||
{
|
||||
vec2 s = sign(x), a = abs(x);
|
||||
x = 1.0 + (0.278393 + (0.230389 + 0.078108 * (a * a)) * a) * a;
|
||||
x *= x;
|
||||
return s - s / (x * x);
|
||||
}
|
||||
|
||||
float
|
||||
erf_range (vec2 x,
|
||||
float sigma)
|
||||
{
|
||||
vec2 from_to = 0.5 - 0.5 * erf (x / (sigma * SQRT1_2));
|
||||
return from_to.y - from_to.x;
|
||||
}
|
||||
|
||||
float
|
||||
ellipse_x (vec2 ellipse,
|
||||
float y)
|
||||
{
|
||||
float y_scaled = y / ellipse.y;
|
||||
return ellipse.x * sqrt (1.0 - y_scaled * y_scaled);
|
||||
}
|
||||
|
||||
float
|
||||
blur_rect (Rect r,
|
||||
vec2 pos)
|
||||
{
|
||||
return erf_range (r.bounds.xz - pos.x, _sigma.x) * erf_range (r.bounds.yw - pos.y, _sigma.y);
|
||||
}
|
||||
|
||||
float
|
||||
blur_corner (vec2 p,
|
||||
vec2 r)
|
||||
{
|
||||
if (min (r.x, r.y) <= 0.0)
|
||||
return 0.0;
|
||||
|
||||
float result = 0.0;
|
||||
float step = 1.0;
|
||||
for (float y = 0.5 * step; y <= r.y; y += step)
|
||||
{
|
||||
float x = r.x - ellipse_x (r, r.y - y);
|
||||
result -= gauss (p.y - y, _sigma.y) * erf_range (vec2 (- p.x, x - p.x), _sigma.x);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
float
|
||||
blur_rounded_rect (RoundedRect r,
|
||||
vec2 p)
|
||||
{
|
||||
float result = blur_rect (Rect (r.bounds), _pos);
|
||||
|
||||
result -= blur_corner (p - r.bounds.xy, vec2 (r.corner_widths[TOP_LEFT], r.corner_heights[TOP_LEFT]));
|
||||
result -= blur_corner (vec2 (r.bounds.z - p.x, p.y - r.bounds.y), vec2 (r.corner_widths[TOP_RIGHT], r.corner_heights[TOP_RIGHT]));
|
||||
result -= blur_corner (r.bounds.zw - p, vec2 (r.corner_widths[BOTTOM_RIGHT], r.corner_heights[BOTTOM_RIGHT]));
|
||||
result -= blur_corner (vec2 (p.x - r.bounds.x, r.bounds.w - p.y), vec2 (r.corner_widths[BOTTOM_LEFT], r.corner_heights[BOTTOM_LEFT]));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
run (out vec4 color,
|
||||
out vec2 position)
|
||||
{
|
||||
float clip_alpha = rounded_rect_coverage (_clip_outline, _pos);
|
||||
|
||||
if (_inset == 0u)
|
||||
clip_alpha = 1.0 - clip_alpha;
|
||||
|
||||
if (clip_alpha == 0.0)
|
||||
{
|
||||
color = vec4 (0.0);
|
||||
position = _pos;
|
||||
return;
|
||||
}
|
||||
|
||||
float blur_alpha = blur_rounded_rect (_shadow_outline, _pos);
|
||||
if (_inset == 1u)
|
||||
blur_alpha = 1.0 - blur_alpha;
|
||||
|
||||
color = clip_alpha * _color * blur_alpha;
|
||||
position = _pos;
|
||||
}
|
||||
|
||||
#endif
|
@ -14,6 +14,7 @@ gsk_private_gpu_include_shaders = files([
|
||||
gsk_private_gpu_shaders = files([
|
||||
'gskgpublur.glsl',
|
||||
'gskgpuborder.glsl',
|
||||
'gskgpuboxshadow.glsl',
|
||||
'gskgpucolor.glsl',
|
||||
'gskgpucolorize.glsl',
|
||||
'gskgpuroundedcolor.glsl',
|
||||
|
@ -75,6 +75,7 @@ gsk_private_sources = files([
|
||||
'gpu/gskgpublitop.c',
|
||||
'gpu/gskgpublurop.c',
|
||||
'gpu/gskgpuborderop.c',
|
||||
'gpu/gskgpuboxshadowop.c',
|
||||
'gpu/gskgpubuffer.c',
|
||||
'gpu/gskgpubufferwriter.c',
|
||||
'gpu/gskgpuclearop.c',
|
||||
|
Loading…
Reference in New Issue
Block a user