Added backdrop effects pipeline (blur)
This commit is contained in:
+84
-96
@@ -52,12 +52,14 @@ emit_rectangle :: proc(x, y, width, height: f32, color: Color, vertices: []Verte
|
||||
vertices[offset + 5] = solid_vertex({x, y + height}, color)
|
||||
}
|
||||
|
||||
// Internal
|
||||
prepare_sdf_primitive_textured :: proc(
|
||||
// Internal — submit an SDF primitive with optional texture binding.
|
||||
// Replaces the old prepare_sdf_primitive and prepare_sdf_primitive_textured.
|
||||
@(private)
|
||||
prepare_sdf_primitive_ex :: proc(
|
||||
layer: ^Layer,
|
||||
prim: Primitive,
|
||||
texture_id: Texture_Id,
|
||||
sampler: Sampler_Preset,
|
||||
prim: Base_2D_Primitive,
|
||||
texture_id: Texture_Id = INVALID_TEXTURE,
|
||||
sampler: Sampler_Preset = DFT_SAMPLER,
|
||||
) {
|
||||
offset := u32(len(GLOB.tmp_primitives))
|
||||
append(&GLOB.tmp_primitives, prim)
|
||||
@@ -65,6 +67,23 @@ prepare_sdf_primitive_textured :: proc(
|
||||
append_or_extend_sub_batch(scissor, layer, .SDF, offset, 1, texture_id, sampler)
|
||||
}
|
||||
|
||||
// Internal — resolve Texture_Fill zero-initialized fields to their defaults.
|
||||
// Odin structs zero-initialize; Color{} and Rectangle{} are all-zero which is not a
|
||||
// useful tint or UV rect. This proc substitutes sensible defaults for zero values.
|
||||
@(private)
|
||||
resolve_texture_defaults :: #force_inline proc(
|
||||
tf: Texture_Fill,
|
||||
) -> (
|
||||
tint: Color,
|
||||
uv: Rectangle,
|
||||
sampler: Sampler_Preset,
|
||||
) {
|
||||
tint = tf.tint == Color{} ? DFT_TINT : tf.tint
|
||||
uv = tf.uv_rect == Rectangle{} ? DFT_UV_RECT : tf.uv_rect
|
||||
sampler = tf.sampler
|
||||
return
|
||||
}
|
||||
|
||||
//Internal
|
||||
//
|
||||
// Compute the visual center of a center-parametrized shape after applying
|
||||
@@ -89,7 +108,7 @@ rotated_aabb_half_extents :: proc(half_width, half_height, cos_angle, sin_angle:
|
||||
return {half_width * cos_abs + half_height * sin_abs, half_width * sin_abs + half_height * cos_abs}
|
||||
}
|
||||
|
||||
// Pack sin/cos into the Primitive.rotation_sc field as two f16 values.
|
||||
// Pack sin/cos into the Base_2D_Primitive.rotation_sc field as two f16 values.
|
||||
pack_rotation_sc :: #force_inline proc(sin_angle, cos_angle: f32) -> u32 {
|
||||
return pack_f16_pair(f16(sin_angle), f16(cos_angle))
|
||||
}
|
||||
@@ -97,7 +116,7 @@ pack_rotation_sc :: #force_inline proc(sin_angle, cos_angle: f32) -> u32 {
|
||||
|
||||
// Internal
|
||||
//
|
||||
// Build an RRect Primitive with bounds, params, and rotation computed from rectangle geometry.
|
||||
// Build an RRect Base_2D_Primitive with bounds, params, and rotation computed from rectangle geometry.
|
||||
// The caller sets color, flags, and uv fields on the returned primitive before submitting.
|
||||
build_rrect_primitive :: proc(
|
||||
rect: Rectangle,
|
||||
@@ -105,7 +124,7 @@ build_rrect_primitive :: proc(
|
||||
origin: Vec2,
|
||||
rotation: f32,
|
||||
feather_px: f32,
|
||||
) -> Primitive {
|
||||
) -> Base_2D_Primitive {
|
||||
max_radius := min(rect.width, rect.height) * 0.5
|
||||
clamped_top_left := clamp(radii.top_left, 0, max_radius)
|
||||
clamped_top_right := clamp(radii.top_right, 0, max_radius)
|
||||
@@ -141,7 +160,7 @@ build_rrect_primitive :: proc(
|
||||
bounds_half_height = expanded.y
|
||||
}
|
||||
|
||||
prim := Primitive {
|
||||
prim := Base_2D_Primitive {
|
||||
bounds = {
|
||||
center_x - bounds_half_width - padding,
|
||||
center_y - bounds_half_height - padding,
|
||||
@@ -165,7 +184,7 @@ build_rrect_primitive :: proc(
|
||||
|
||||
// Internal
|
||||
//
|
||||
// Build an RRect Primitive for a circle (fully-rounded square RRect).
|
||||
// Build an RRect Base_2D_Primitive for a circle (fully-rounded square RRect).
|
||||
// The caller sets color, flags, and uv fields on the returned primitive before submitting.
|
||||
build_circle_primitive :: proc(
|
||||
center: Vec2,
|
||||
@@ -173,7 +192,7 @@ build_circle_primitive :: proc(
|
||||
origin: Vec2,
|
||||
rotation: f32,
|
||||
feather_px: f32,
|
||||
) -> Primitive {
|
||||
) -> Base_2D_Primitive {
|
||||
half_feather := feather_px * 0.5
|
||||
padding := half_feather / GLOB.dpi_scaling
|
||||
dpi_scale := GLOB.dpi_scaling
|
||||
@@ -184,7 +203,7 @@ build_circle_primitive :: proc(
|
||||
actual_center = compute_pivot_center(center, origin, sin_a, cos_a)
|
||||
}
|
||||
|
||||
prim := Primitive {
|
||||
prim := Base_2D_Primitive {
|
||||
bounds = {
|
||||
actual_center.x - radius - padding,
|
||||
actual_center.y - radius - padding,
|
||||
@@ -203,7 +222,7 @@ build_circle_primitive :: proc(
|
||||
|
||||
// Internal
|
||||
//
|
||||
// Build an Ellipse Primitive with bounds, params, and rotation computed from ellipse geometry.
|
||||
// Build an Ellipse Base_2D_Primitive with bounds, params, and rotation computed from ellipse geometry.
|
||||
// The caller sets color, flags, and uv fields on the returned primitive before submitting.
|
||||
build_ellipse_primitive :: proc(
|
||||
center: Vec2,
|
||||
@@ -211,7 +230,7 @@ build_ellipse_primitive :: proc(
|
||||
origin: Vec2,
|
||||
rotation: f32,
|
||||
feather_px: f32,
|
||||
) -> Primitive {
|
||||
) -> Base_2D_Primitive {
|
||||
half_feather := feather_px * 0.5
|
||||
padding := half_feather / GLOB.dpi_scaling
|
||||
dpi_scale := GLOB.dpi_scaling
|
||||
@@ -235,7 +254,7 @@ build_ellipse_primitive :: proc(
|
||||
bound_vertical = expanded.y
|
||||
}
|
||||
|
||||
prim := Primitive {
|
||||
prim := Base_2D_Primitive {
|
||||
bounds = {
|
||||
actual_center.x - bound_horizontal - padding,
|
||||
actual_center.y - bound_vertical - padding,
|
||||
@@ -253,7 +272,7 @@ build_ellipse_primitive :: proc(
|
||||
|
||||
// Internal
|
||||
//
|
||||
// Build an NGon Primitive with bounds, params, and rotation computed from polygon geometry.
|
||||
// Build an NGon Base_2D_Primitive with bounds, params, and rotation computed from polygon geometry.
|
||||
// The caller sets color, flags, and uv fields on the returned primitive before submitting.
|
||||
build_polygon_primitive :: proc(
|
||||
center: Vec2,
|
||||
@@ -262,7 +281,7 @@ build_polygon_primitive :: proc(
|
||||
origin: Vec2,
|
||||
rotation: f32,
|
||||
feather_px: f32,
|
||||
) -> Primitive {
|
||||
) -> Base_2D_Primitive {
|
||||
half_feather := feather_px * 0.5
|
||||
padding := half_feather / GLOB.dpi_scaling
|
||||
dpi_scale := GLOB.dpi_scaling
|
||||
@@ -276,7 +295,7 @@ build_polygon_primitive :: proc(
|
||||
rotation_radians := math.to_radians(rotation)
|
||||
sin_rot, cos_rot := math.sincos(rotation_radians)
|
||||
|
||||
prim := Primitive {
|
||||
prim := Base_2D_Primitive {
|
||||
bounds = {
|
||||
actual_center.x - radius - padding,
|
||||
actual_center.y - radius - padding,
|
||||
@@ -295,7 +314,7 @@ build_polygon_primitive :: proc(
|
||||
|
||||
// Internal
|
||||
//
|
||||
// Build a Ring_Arc Primitive with bounds and params computed from ring/arc geometry.
|
||||
// Build a Ring_Arc Base_2D_Primitive with bounds and params computed from ring/arc geometry.
|
||||
// Pre-computes the angular boundary normals on the CPU so the fragment shader needs
|
||||
// no per-pixel sin/cos. The radial SDF uses max(inner-r, r-outer) which correctly
|
||||
// handles pie slices (inner_radius = 0) and full rings.
|
||||
@@ -309,7 +328,7 @@ build_ring_arc_primitive :: proc(
|
||||
rotation: f32,
|
||||
feather_px: f32,
|
||||
) -> (
|
||||
Primitive,
|
||||
Base_2D_Primitive,
|
||||
Shape_Flags,
|
||||
) {
|
||||
half_feather := feather_px * 0.5
|
||||
@@ -347,7 +366,7 @@ build_ring_arc_primitive :: proc(
|
||||
arc_flags = arc_span <= math.PI ? {.Arc_Narrow} : {.Arc_Wide}
|
||||
}
|
||||
|
||||
prim := Primitive {
|
||||
prim := Base_2D_Primitive {
|
||||
bounds = {
|
||||
actual_center.x - outer_radius - padding,
|
||||
actual_center.y - outer_radius - padding,
|
||||
@@ -365,39 +384,53 @@ build_ring_arc_primitive :: proc(
|
||||
return prim, arc_flags
|
||||
}
|
||||
|
||||
// Apply gradient and outline effects to a primitive. Sets flags, uv.effects, and expands bounds.
|
||||
// Apply brush fill and outline to a primitive, then submit it.
|
||||
// Dispatches to the correct sub-batch based on the Brush variant.
|
||||
// All parameters (outline_width) are in logical pixels, matching the rest of the public API.
|
||||
// The helper converts to physical pixels for GPU packing internally.
|
||||
@(private)
|
||||
apply_shape_effects :: proc(
|
||||
prim: ^Primitive,
|
||||
apply_brush_and_outline :: proc(
|
||||
layer: ^Layer,
|
||||
prim: ^Base_2D_Primitive,
|
||||
kind: Shape_Kind,
|
||||
gradient: Gradient,
|
||||
brush: Brush,
|
||||
outline_color: Color,
|
||||
outline_width: f32,
|
||||
extra_flags: Shape_Flags = {},
|
||||
) {
|
||||
flags: Shape_Flags = extra_flags
|
||||
gradient_dir_sc: u32 = 0
|
||||
|
||||
switch g in gradient {
|
||||
// Fill — determined by the Brush variant.
|
||||
texture_id := INVALID_TEXTURE
|
||||
sampler := DFT_SAMPLER
|
||||
|
||||
switch b in brush {
|
||||
case Color: prim.color = b
|
||||
case Linear_Gradient:
|
||||
flags += {.Gradient}
|
||||
prim.uv.effects.gradient_color = g.end_color
|
||||
rad := math.to_radians(g.angle)
|
||||
prim.color = b.start_color
|
||||
prim.effects.gradient_color = b.end_color
|
||||
rad := math.to_radians(b.angle)
|
||||
sin_a, cos_a := math.sincos(rad)
|
||||
gradient_dir_sc = pack_f16_pair(f16(cos_a), f16(sin_a))
|
||||
prim.effects.gradient_dir_sc = pack_f16_pair(f16(cos_a), f16(sin_a))
|
||||
case Radial_Gradient:
|
||||
flags += {.Gradient, .Gradient_Radial}
|
||||
prim.uv.effects.gradient_color = g.outer_color
|
||||
case:
|
||||
prim.color = b.inner_color
|
||||
prim.effects.gradient_color = b.outer_color
|
||||
case Texture_Fill:
|
||||
flags += {.Textured}
|
||||
tint, uv, sam := resolve_texture_defaults(b)
|
||||
prim.color = tint
|
||||
prim.uv_rect = {uv.x, uv.y, uv.width, uv.height}
|
||||
texture_id = b.id
|
||||
sampler = sam
|
||||
}
|
||||
|
||||
outline_packed: u32 = 0
|
||||
// Outline — orthogonal to all Brush variants.
|
||||
if outline_width > 0 {
|
||||
flags += {.Outline}
|
||||
prim.uv.effects.outline_color = outline_color
|
||||
outline_packed = pack_f16_pair(f16(outline_width * GLOB.dpi_scaling), 0)
|
||||
prim.effects.outline_color = outline_color
|
||||
prim.effects.outline_packed = pack_f16_pair(f16(outline_width * GLOB.dpi_scaling), 0)
|
||||
// Expand bounds to contain the outline (bounds are in logical pixels)
|
||||
prim.bounds[0] -= outline_width
|
||||
prim.bounds[1] -= outline_width
|
||||
@@ -410,9 +443,8 @@ apply_shape_effects :: proc(
|
||||
flags += {.Rotated}
|
||||
}
|
||||
|
||||
prim.uv.effects.gradient_dir_sc = gradient_dir_sc
|
||||
prim.uv.effects.outline_packed = outline_packed
|
||||
prim.flags = pack_kind_flags(kind, flags)
|
||||
prepare_sdf_primitive_ex(layer, prim^, texture_id, sampler)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
@@ -430,8 +462,7 @@ apply_shape_effects :: proc(
|
||||
rectangle :: proc(
|
||||
layer: ^Layer,
|
||||
rect: Rectangle,
|
||||
color: Color,
|
||||
gradient: Gradient = nil,
|
||||
brush: Brush,
|
||||
outline_color: Color = {},
|
||||
outline_width: f32 = 0,
|
||||
radii: Rectangle_Radii = {},
|
||||
@@ -440,36 +471,7 @@ rectangle :: proc(
|
||||
feather_px: f32 = DFT_FEATHER_PX,
|
||||
) {
|
||||
prim := build_rrect_primitive(rect, radii, origin, rotation, feather_px)
|
||||
prim.color = color
|
||||
apply_shape_effects(&prim, .RRect, gradient, outline_color, outline_width)
|
||||
prepare_sdf_primitive(layer, prim)
|
||||
}
|
||||
|
||||
// Draw a rectangle with a texture fill via SDF with optional per-corner rounding radii.
|
||||
// Texture and gradient/outline are mutually exclusive (they share the same storage in the
|
||||
// primitive). To outline a textured rect, draw the texture first, then a stroke-only rect on top.
|
||||
// Origin semantics: see `rectangle`.
|
||||
rectangle_texture :: proc(
|
||||
layer: ^Layer,
|
||||
rect: Rectangle,
|
||||
id: Texture_Id,
|
||||
tint: Color = DFT_TINT,
|
||||
uv_rect: Rectangle = DFT_UV_RECT,
|
||||
sampler: Sampler_Preset = DFT_SAMPLER,
|
||||
radii: Rectangle_Radii = {},
|
||||
origin: Vec2 = {},
|
||||
rotation: f32 = 0,
|
||||
feather_px: f32 = DFT_FEATHER_PX,
|
||||
) {
|
||||
prim := build_rrect_primitive(rect, radii, origin, rotation, feather_px)
|
||||
prim.color = tint
|
||||
tex_flags: Shape_Flags = {.Textured}
|
||||
if prim.rotation_sc != 0 {
|
||||
tex_flags += {.Rotated}
|
||||
}
|
||||
prim.flags = pack_kind_flags(.RRect, tex_flags)
|
||||
prim.uv.uv_rect = {uv_rect.x, uv_rect.y, uv_rect.width, uv_rect.height}
|
||||
prepare_sdf_primitive_textured(layer, prim, id, sampler)
|
||||
apply_brush_and_outline(layer, &prim, .RRect, brush, outline_color, outline_width)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
@@ -488,8 +490,7 @@ circle :: proc(
|
||||
layer: ^Layer,
|
||||
center: Vec2,
|
||||
radius: f32,
|
||||
color: Color,
|
||||
gradient: Gradient = nil,
|
||||
brush: Brush,
|
||||
outline_color: Color = {},
|
||||
outline_width: f32 = 0,
|
||||
origin: Vec2 = {},
|
||||
@@ -497,9 +498,7 @@ circle :: proc(
|
||||
feather_px: f32 = DFT_FEATHER_PX,
|
||||
) {
|
||||
prim := build_circle_primitive(center, radius, origin, rotation, feather_px)
|
||||
prim.color = color
|
||||
apply_shape_effects(&prim, .RRect, gradient, outline_color, outline_width)
|
||||
prepare_sdf_primitive(layer, prim)
|
||||
apply_brush_and_outline(layer, &prim, .RRect, brush, outline_color, outline_width)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
@@ -512,8 +511,7 @@ ellipse :: proc(
|
||||
layer: ^Layer,
|
||||
center: Vec2,
|
||||
radius_horizontal, radius_vertical: f32,
|
||||
color: Color,
|
||||
gradient: Gradient = nil,
|
||||
brush: Brush,
|
||||
outline_color: Color = {},
|
||||
outline_width: f32 = 0,
|
||||
origin: Vec2 = {},
|
||||
@@ -521,9 +519,7 @@ ellipse :: proc(
|
||||
feather_px: f32 = DFT_FEATHER_PX,
|
||||
) {
|
||||
prim := build_ellipse_primitive(center, radius_horizontal, radius_vertical, origin, rotation, feather_px)
|
||||
prim.color = color
|
||||
apply_shape_effects(&prim, .Ellipse, gradient, outline_color, outline_width)
|
||||
prepare_sdf_primitive(layer, prim)
|
||||
apply_brush_and_outline(layer, &prim, .Ellipse, brush, outline_color, outline_width)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
@@ -538,8 +534,7 @@ polygon :: proc(
|
||||
center: Vec2,
|
||||
sides: int,
|
||||
radius: f32,
|
||||
color: Color,
|
||||
gradient: Gradient = nil,
|
||||
brush: Brush,
|
||||
outline_color: Color = {},
|
||||
outline_width: f32 = 0,
|
||||
origin: Vec2 = {},
|
||||
@@ -549,9 +544,7 @@ polygon :: proc(
|
||||
if sides < 3 do return
|
||||
|
||||
prim := build_polygon_primitive(center, sides, radius, origin, rotation, feather_px)
|
||||
prim.color = color
|
||||
apply_shape_effects(&prim, .NGon, gradient, outline_color, outline_width)
|
||||
prepare_sdf_primitive(layer, prim)
|
||||
apply_brush_and_outline(layer, &prim, .NGon, brush, outline_color, outline_width)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
@@ -566,8 +559,7 @@ ring :: proc(
|
||||
layer: ^Layer,
|
||||
center: Vec2,
|
||||
inner_radius, outer_radius: f32,
|
||||
color: Color,
|
||||
gradient: Gradient = nil,
|
||||
brush: Brush,
|
||||
outline_color: Color = {},
|
||||
outline_width: f32 = 0,
|
||||
start_angle: f32 = 0,
|
||||
@@ -586,9 +578,7 @@ ring :: proc(
|
||||
rotation,
|
||||
feather_px,
|
||||
)
|
||||
prim.color = color
|
||||
apply_shape_effects(&prim, .Ring_Arc, gradient, outline_color, outline_width, arc_flags)
|
||||
prepare_sdf_primitive(layer, prim)
|
||||
apply_brush_and_outline(layer, &prim, .Ring_Arc, brush, outline_color, outline_width, arc_flags)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
@@ -600,7 +590,7 @@ ring :: proc(
|
||||
line :: proc(
|
||||
layer: ^Layer,
|
||||
start_position, end_position: Vec2,
|
||||
color: Color,
|
||||
brush: Brush,
|
||||
thickness: f32 = DFT_STROKE_THICKNESS,
|
||||
outline_color: Color = {},
|
||||
outline_width: f32 = 0,
|
||||
@@ -627,14 +617,13 @@ line :: proc(
|
||||
// Expand bounds for rotation
|
||||
bounds_half := rotated_aabb_half_extents(half_length + cap_radius, half_thickness, cos_angle, sin_angle)
|
||||
|
||||
prim := Primitive {
|
||||
prim := Base_2D_Primitive {
|
||||
bounds = {
|
||||
center_x - bounds_half.x - padding,
|
||||
center_y - bounds_half.y - padding,
|
||||
center_x + bounds_half.x + padding,
|
||||
center_y + bounds_half.y + padding,
|
||||
},
|
||||
color = color,
|
||||
rotation_sc = pack_rotation_sc(sin_angle, cos_angle),
|
||||
}
|
||||
prim.params.rrect = RRect_Params {
|
||||
@@ -647,15 +636,14 @@ line :: proc(
|
||||
},
|
||||
half_feather = half_feather,
|
||||
}
|
||||
apply_shape_effects(&prim, .RRect, nil, outline_color, outline_width)
|
||||
prepare_sdf_primitive(layer, prim)
|
||||
apply_brush_and_outline(layer, &prim, .RRect, brush, outline_color, outline_width)
|
||||
}
|
||||
|
||||
// Draw a line strip via decomposed SDF line segments.
|
||||
line_strip :: proc(
|
||||
layer: ^Layer,
|
||||
points: []Vec2,
|
||||
color: Color,
|
||||
brush: Brush,
|
||||
thickness: f32 = DFT_STROKE_THICKNESS,
|
||||
outline_color: Color = {},
|
||||
outline_width: f32 = 0,
|
||||
@@ -663,7 +651,7 @@ line_strip :: proc(
|
||||
) {
|
||||
if len(points) < 2 do return
|
||||
for i in 0 ..< len(points) - 1 {
|
||||
line(layer, points[i], points[i + 1], color, thickness, outline_color, outline_width, feather_px)
|
||||
line(layer, points[i], points[i + 1], brush, thickness, outline_color, outline_width, feather_px)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user