///|
fn append_visual_item(
vertices : Array[VisualVertex],
items : Array[NativeDrawItem],
rect : @core.Rect,
radius~ : Double,
brush~ : @core.Brush,
mode~ : Double,
stroke_width~ : Double,
blur_radius~ : Double,
state~ : NativePaintState,
size~ : @core.Size,
scale_factor~ : Double,
) -> Unit {
if !native_paint_state_visible(state) {
return
}
if rect.size.width <= 0.0 || rect.size.height <= 0.0 {
return
}
if mode == visual_mode_stroke && stroke_width <= 0.0 {
return
}
let scissor = native_scissor_for_state(state, size, scale_factor)
if !native_scissor_visible(scissor) {
return
}
let start = vertices.length()
append_visual_quad(
vertices,
rect,
radius~,
brush~,
mode~,
stroke_width~,
blur_radius~,
state~,
size~,
)
items.push(
NativeDrawItem::NativeVisual(start, vertices.length() - start, scissor),
)
}
///|
fn append_shadow_item(
vertices : Array[VisualVertex],
items : Array[NativeDrawItem],
shadow : @core.ShadowSpec,
state : NativePaintState,
size : @core.Size,
scale_factor : Double,
) -> Unit {
let blur = shadow.blur_radius.max(0.0)
let spread = shadow.spread.max(0.0)
let grow = blur * 2.0 + spread
let rect = @core.Rect::new(
x=shadow.rect.rect.origin.x + shadow.offset.x - grow,
y=shadow.rect.rect.origin.y + shadow.offset.y - grow,
width=shadow.rect.rect.size.width + grow * 2.0,
height=shadow.rect.rect.size.height + grow * 2.0,
)
append_visual_item(
vertices,
items,
rect,
radius=shadow.rect.radius + grow,
brush=@core.Brush::solid(shadow.color),
mode=visual_mode_shadow,
stroke_width=0.0,
blur_radius=blur,
state~,
size~,
scale_factor~,
)
}
///|
fn append_image_item(
vertices : Array[ImageVertex],
items : Array[NativeDrawItem],
run : @core.ImageRun,
state : NativePaintState,
size : @core.Size,
scale_factor : Double,
) -> Unit {
if !native_paint_state_visible(state) {
return
}
if run.frame.size.width <= 0.0 || run.frame.size.height <= 0.0 {
return
}
let scissor = native_scissor_for_state(state, size, scale_factor)
if !native_scissor_visible(scissor) {
return
}
let start = vertices.length()
items.push(
NativeDrawItem::NativeImage(start, vertices.length() - start, scissor, {
source: run.source,
frame: run.frame,
opacity: clamp_opacity(run.opacity) * state.opacity,
fit: run.fit,
transform: state.transform,
}),
)
}
///|
fn append_path_item(
vertices : Array[VisualVertex],
items : Array[NativeDrawItem],
path : @core.PathSpec,
state : NativePaintState,
size : @core.Size,
scale_factor : Double,
) -> Unit {
if !native_paint_state_visible(state) {
return
}
let scissor = native_scissor_for_state(state, size, scale_factor)
if !native_scissor_visible(scissor) {
return
}
let mesh = @render_common.tessellate_path(path)
let start = vertices.length()
for vertex in mesh.vertices() {
push_path_visual_vertex(vertices, vertex, state, size)
}
let count = vertices.length() - start
if count > 0 {
items.push(NativeDrawItem::NativeVisual(start, count, scissor))
}
}
///|
fn append_visual_quad(
vertices : Array[VisualVertex],
rect : @core.Rect,
radius~ : Double,
brush~ : @core.Brush,
mode~ : Double,
stroke_width~ : Double,
blur_radius~ : Double,
state~ : NativePaintState,
size~ : @core.Size,
) -> Unit {
let x0 = rect.origin.x
let y0 = rect.origin.y
let x1 = rect.origin.x + rect.size.width
let y1 = rect.origin.y + rect.size.height
push_visual_vertex(
vertices,
rect,
x0,
y0,
radius~,
brush~,
mode~,
stroke_width~,
blur_radius~,
state~,
size~,
)
push_visual_vertex(
vertices,
rect,
x0,
y1,
radius~,
brush~,
mode~,
stroke_width~,
blur_radius~,
state~,
size~,
)
push_visual_vertex(
vertices,
rect,
x1,
y1,
radius~,
brush~,
mode~,
stroke_width~,
blur_radius~,
state~,
size~,
)
push_visual_vertex(
vertices,
rect,
x0,
y0,
radius~,
brush~,
mode~,
stroke_width~,
blur_radius~,
state~,
size~,
)
push_visual_vertex(
vertices,
rect,
x1,
y1,
radius~,
brush~,
mode~,
stroke_width~,
blur_radius~,
state~,
size~,
)
push_visual_vertex(
vertices,
rect,
x1,
y0,
radius~,
brush~,
mode~,
stroke_width~,
blur_radius~,
state~,
size~,
)
}
///|
fn push_visual_vertex(
vertices : Array[VisualVertex],
rect : @core.Rect,
x : Double,
y : Double,
radius~ : Double,
brush~ : @core.Brush,
mode~ : Double,
stroke_width~ : Double,
blur_radius~ : Double,
state~ : NativePaintState,
size~ : @core.Size,
) -> Unit {
let gradient = visual_gradient(brush)
let shader_brush_kind = if mode < visual_mode_shadow {
visual_brush_kind(brush)
} else {
blur_radius
}
let start_x = visual_brush_start_x(brush, gradient, rect)
let start_y = visual_brush_start_y(brush, gradient, rect)
let end_x = visual_brush_end_x(brush, gradient, rect)
let end_y = visual_brush_end_y(brush, gradient, rect)
let transformed = transform_point(state.transform, x, y)
vertices.push({
x: visual_pixel_x_to_ndc(transformed.x, size.width),
y: visual_pixel_y_to_ndc(transformed.y, size.height),
local_x: x - rect.origin.x,
local_y: y - rect.origin.y,
width: rect.size.width,
height: rect.size.height,
radius: radius.min(rect.size.width.min(rect.size.height) / 2.0).max(0.0),
mode,
stroke_width,
blur_radius: shader_brush_kind,
start_x,
start_y,
end_x,
end_y,
r0: gradient.start_color.r,
g0: gradient.start_color.g,
b0: gradient.start_color.b,
a0: gradient.start_color.a * state.opacity,
r1: gradient.end_color.r,
g1: gradient.end_color.g,
b1: gradient.end_color.b,
a1: gradient.end_color.a * state.opacity,
})
}
///|
fn push_path_visual_vertex(
vertices : Array[VisualVertex],
vertex : @render_common.PathVertex,
state : NativePaintState,
size : @core.Size,
) -> Unit {
let components = vertex.components()
let x = components[0]
let y = components[1]
let transformed = transform_point(state.transform, x, y)
vertices.push({
x: visual_pixel_x_to_ndc(transformed.x, size.width),
y: visual_pixel_y_to_ndc(transformed.y, size.height),
local_x: x,
local_y: y,
width: 0.0,
height: 0.0,
radius: 0.0,
mode: visual_mode_path,
stroke_width: 0.0,
blur_radius: components[2],
start_x: components[3],
start_y: components[4],
end_x: components[5],
end_y: components[6],
r0: components[7],
g0: components[8],
b0: components[9],
a0: components[10] * state.opacity,
r1: components[11],
g1: components[12],
b1: components[13],
a1: components[14] * state.opacity,
})
}
///|
fn visual_gradient(brush : @core.Brush) -> @core.LinearGradientSpec {
match brush {
@core.Brush::Solid(color) =>
@core.LinearGradientSpec::new(
start=@core.Point::new(x=0.0, y=0.0),
end=@core.Point::new(x=1.0, y=0.0),
start_color=color,
end_color=color,
)
@core.Brush::LinearGradient(gradient) => gradient
@core.Brush::RadialGradient(gradient) =>
@core.LinearGradientSpec::new(
start=gradient.center,
end=@core.Point::new(
x=gradient.center.x + gradient.radius.max(1.0),
y=gradient.center.y,
),
start_color=gradient.center_color,
end_color=gradient.edge_color,
)
}
}
///|
fn visual_brush_kind(brush : @core.Brush) -> Double {
match brush {
@core.Brush::RadialGradient(_) => 1.0
_ => 0.0
}
}
///|
fn visual_brush_start_x(
brush : @core.Brush,
gradient : @core.LinearGradientSpec,
rect : @core.Rect,
) -> Double {
match brush {
@core.Brush::RadialGradient(radial) => radial.center.x - rect.origin.x
_ => gradient.start.x - rect.origin.x
}
}
///|
fn visual_brush_start_y(
brush : @core.Brush,
gradient : @core.LinearGradientSpec,
rect : @core.Rect,
) -> Double {
match brush {
@core.Brush::RadialGradient(radial) => radial.center.y - rect.origin.y
_ => gradient.start.y - rect.origin.y
}
}
///|
fn visual_brush_end_x(
brush : @core.Brush,
gradient : @core.LinearGradientSpec,
rect : @core.Rect,
) -> Double {
match brush {
@core.Brush::RadialGradient(radial) => radial.radius.max(1.0)
_ => gradient.end.x - rect.origin.x
}
}
///|
fn visual_brush_end_y(
brush : @core.Brush,
gradient : @core.LinearGradientSpec,
rect : @core.Rect,
) -> Double {
match brush {
@core.Brush::RadialGradient(_) => 0.0
_ => gradient.end.y - rect.origin.y
}
}