///|
priv struct ImageVertices2D {
vertices : Array[Double]
sampler_code : Int
}
///|
fn pack_rect_fill_vertices2d(
rect : @smath.Rect,
transform : @smath.Transform,
color : @render.Color,
) -> Array[Double] {
let (p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) = rect_points2d(rect, transform)
let vertices : Array[Double] = []
let (r, g, b, a) = color_rgba01(color)
push_color_vertex2d(vertices, p0x, p0y, r, g, b, a)
push_color_vertex2d(vertices, p1x, p1y, r, g, b, a)
push_color_vertex2d(vertices, p2x, p2y, r, g, b, a)
push_color_vertex2d(vertices, p0x, p0y, r, g, b, a)
push_color_vertex2d(vertices, p2x, p2y, r, g, b, a)
push_color_vertex2d(vertices, p3x, p3y, r, g, b, a)
vertices
}
///|
fn pack_rect_stroke_vertices2d(
rect : @smath.Rect,
transform : @smath.Transform,
color : @render.Color,
) -> Array[Double] {
let (p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) = rect_points2d(rect, transform)
let vertices : Array[Double] = []
let (r, g, b, a) = color_rgba01(color)
push_color_vertex2d(vertices, p0x, p0y, r, g, b, a)
push_color_vertex2d(vertices, p1x, p1y, r, g, b, a)
push_color_vertex2d(vertices, p1x, p1y, r, g, b, a)
push_color_vertex2d(vertices, p2x, p2y, r, g, b, a)
push_color_vertex2d(vertices, p2x, p2y, r, g, b, a)
push_color_vertex2d(vertices, p3x, p3y, r, g, b, a)
push_color_vertex2d(vertices, p3x, p3y, r, g, b, a)
push_color_vertex2d(vertices, p0x, p0y, r, g, b, a)
vertices
}
///|
fn pack_gradient_rect_vertices2d(
command : @render2d_types.GradientRectDrawCommand2D,
) -> Array[Double] {
let vertices : Array[Double] = []
for triangle in @render2d_types.tessellate_gradient_rect(command) {
for point in [triangle.a, triangle.b, triangle.c] {
let (r, g, b, a) = color_rgba01(point.color)
push_color_vertex2d(
vertices,
point.position[X],
point.position[Y],
r,
g,
b,
a,
)
}
}
vertices
}
///|
fn pack_rounded_chrome_vertices2d(
command : @render2d_types.RoundedChromeDrawCommand2D,
) -> Array[Double] {
let vertices : Array[Double] = []
for triangle in @render2d_types.tessellate_rounded_chrome(command) {
for point in [triangle.a, triangle.b, triangle.c] {
let (r, g, b, a) = color_rgba01(point.color)
push_color_vertex2d(
vertices,
point.position[X],
point.position[Y],
r,
g,
b,
a,
)
}
}
vertices
}
///|
priv struct RoundedChromeVertexCacheEntry {
cache_id : Int
vertices : Array[Double]
mut last_used : Int
}
///|
let rounded_chrome_vertex_cache : Map[
@render2d_types.RoundedChromeDrawCommand2D,
RoundedChromeVertexCacheEntry,
] = Map([])
///|
let rounded_chrome_vertex_cache_tick : Ref[Int] = { val: 0 }
///|
let rounded_chrome_vertex_cache_next_id : Ref[Int] = { val: 1 }
///|
fn cached_rounded_chrome_vertices2d(
command : @render2d_types.RoundedChromeDrawCommand2D,
) -> RoundedChromeVertexCacheEntry {
rounded_chrome_vertex_cache_tick.val += 1
if rounded_chrome_vertex_cache.get(command) is Some(entry) {
entry.last_used = rounded_chrome_vertex_cache_tick.val
return entry
}
if rounded_chrome_vertex_cache.length() >= 4096 {
let mut oldest_command : @render2d_types.RoundedChromeDrawCommand2D? = None
let mut oldest_use = rounded_chrome_vertex_cache_tick.val
for cached_command, entry in rounded_chrome_vertex_cache {
if entry.last_used <= oldest_use {
oldest_command = Some(cached_command)
oldest_use = entry.last_used
}
}
if oldest_command is Some(cached_command) {
if rounded_chrome_vertex_cache.get(cached_command) is Some(entry) {
webgpu_release_cached_color_vertices_2d(entry.cache_id)
}
rounded_chrome_vertex_cache.remove(cached_command)
}
}
let entry = {
cache_id: rounded_chrome_vertex_cache_next_id.val,
vertices: pack_rounded_chrome_vertices2d(command),
last_used: rounded_chrome_vertex_cache_tick.val,
}
rounded_chrome_vertex_cache_next_id.val += 1
rounded_chrome_vertex_cache.set(command, entry)
entry
}
///|
fn pack_colored_geometry_vertices2d(
command : @render2d_types.ColoredGeometryDrawCommand2D,
) -> Array[Double] {
let vertices : Array[Double] = []
for triangle in command.triangles {
for point in [triangle.a, triangle.b, triangle.c] {
let (r, g, b, a) = color_rgba01(point.color)
push_color_vertex2d(
vertices,
point.position[X],
point.position[Y],
r,
g,
b,
a,
)
}
}
vertices
}
///|
fn pack_image_vertices2d(
command : @render2d_types.ImageDrawCommand2D,
image_width : Double,
image_height : Double,
) -> ImageVertices2D {
pack_image_vertices2d_raw(
command.destination,
command.transform,
command.source,
command.uv_transform,
command.repeat,
command.color,
image_width,
image_height,
)
}
///|
fn pack_image_material_vertices2d(
command : @render2d_types.ImageMaterialDrawCommand2D,
image_width : Double,
image_height : Double,
) -> Array[Double] {
let image = command.image
let destination = image.destination
let shadow = command.drop_shadow
let offset = shadow
.map(fn(value) { value.offset })
.unwrap_or(@smath.Vec2::zero())
let blur = shadow.map(fn(value) { value.blur_radius }).unwrap_or(0.0)
let left = blur * 2.0 + (-offset[X]).max(0.0)
let top = blur * 2.0 + (-offset[Y]).max(0.0)
let right = blur * 2.0 + offset[X].max(0.0)
let bottom = blur * 2.0 + offset[Y].max(0.0)
let expanded = @smath.Rect::{
position: destination.position - Vec2(left, top),
size: destination.size + Vec2(left + right, top + bottom),
}
let (p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) = rect_points2d(
expanded,
image.transform,
)
let (u0, v0, u1, v1) = match image.source {
Some(source) =>
(
source.position[X] / image_width,
source.position[Y] / image_height,
(source.position[X] + source.size[X]) / image_width,
(source.position[Y] + source.size[Y]) / image_height,
)
None => (0.0, 0.0, 1.0, 1.0)
}
let uv_width = u1 - u0
let uv_height = v1 - v0
let dest_width = destination.size[X].max(0.000001)
let dest_height = destination.size[Y].max(0.000001)
let eu0 = u0 - left / dest_width * uv_width
let ev0 = v0 - top / dest_height * uv_height
let eu1 = u1 + right / dest_width * uv_width
let ev1 = v1 + bottom / dest_height * uv_height
let (bu0, bv0) = transform_uv2d(image.uv_transform, u0, v0)
let (bu1, bv1) = transform_uv2d(image.uv_transform, u1, v1)
let (tu0, tv0) = transform_uv2d(image.uv_transform, eu0, ev0)
let (tu1, tv1) = transform_uv2d(image.uv_transform, eu1, ev1)
let (r, g, b, a) = color_rgba01(image.color)
let shadow_color = shadow
.map(fn(value) { value.color })
.unwrap_or(@render.Color::{ r: 0U, g: 0U, b: 0U, a: 0.0 })
let (sr, sg, sb, sa) = color_rgba01(shadow_color)
let effect = [
command.opacity,
command.brightness,
command.saturation,
if shadow is Some(_) {
1.0
} else {
0.0
},
]
let shadow_data = [
offset[X] / dest_width * uv_width,
offset[Y] / dest_height * uv_height,
blur / dest_width * uv_width,
blur / dest_height * uv_height,
]
let bounds = [bu0.min(bu1), bv0.min(bv1), bu0.max(bu1), bv0.max(bv1)]
let vertices : Array[Double] = []
for
vertex in [
[p0x, p0y, tu0, tv0],
[p1x, p1y, tu1, tv0],
[p2x, p2y, tu1, tv1],
[p0x, p0y, tu0, tv0],
[p2x, p2y, tu1, tv1],
[p3x, p3y, tu0, tv1],
] {
for value in vertex {
vertices.push(value)
}
for value in [r, g, b, a] + effect + [sr, sg, sb, sa] + shadow_data + bounds {
vertices.push(value)
}
}
vertices
}
///|
fn pack_image_vertices2d_raw(
destination : @smath.Rect,
transform : @smath.Transform,
source : @render2d_types.ImageRegion2D?,
uv_transform : @smath.Transform,
repeat : @smath.RepeatMode,
color : @render.Color,
image_width : Double,
image_height : Double,
) -> ImageVertices2D {
let (p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) = rect_points2d(
destination, transform,
)
let mut u0 = 0.0
let mut v0 = 0.0
let mut u1 = 1.0
let mut v1 = 1.0
let mut sampler_code = 0
match source {
Some(source) => {
u0 = source.position[X] / image_width
v0 = source.position[Y] / image_height
u1 = (source.position[X] + source.size[X]) / image_width
v1 = (source.position[Y] + source.size[Y]) / image_height
}
None => {
let scale_u = destination.size[X] / image_width
let scale_v = destination.size[Y] / image_height
match repeat {
Repeat => {
u1 = scale_u
v1 = scale_v
sampler_code = 3
}
RepeatX => {
u1 = scale_u
sampler_code = 1
}
RepeatY => {
v1 = scale_v
sampler_code = 2
}
NoRepeat => ()
}
}
}
let (tu0, tv0) = transform_uv2d(uv_transform, u0, v0)
let (tu1, tv0b) = transform_uv2d(uv_transform, u1, v0)
let (tu1b, tv1) = transform_uv2d(uv_transform, u1, v1)
let (tu0b, tv1b) = transform_uv2d(uv_transform, u0, v1)
let (r, g, b, a) = color_rgba01(color)
let vertices : Array[Double] = []
push_tex_vertex2d(vertices, p0x, p0y, tu0, tv0, r, g, b, a)
push_tex_vertex2d(vertices, p1x, p1y, tu1, tv0b, r, g, b, a)
push_tex_vertex2d(vertices, p2x, p2y, tu1b, tv1, r, g, b, a)
push_tex_vertex2d(vertices, p0x, p0y, tu0, tv0, r, g, b, a)
push_tex_vertex2d(vertices, p2x, p2y, tu1b, tv1, r, g, b, a)
push_tex_vertex2d(vertices, p3x, p3y, tu0b, tv1b, r, g, b, a)
{ vertices, sampler_code }
}
///|
fn pack_text_vertices2d(
command : @render2d_types.TextDrawCommand2D,
texture_width : Double,
texture_height : Double,
) -> Array[Double] {
let mut x = command.position[X]
let mut y = command.position[Y]
match command.style.align {
Center => x = x - texture_width / 2.0
Right => x = x - texture_width
Left => ()
}
match command.style.baseline {
Center => y = y - texture_height / 2.0
Bottom => y = y - texture_height
Top => ()
}
pack_text_vertices2d_raw(
{ position: Vec2(x, y), size: Vec2(texture_width, texture_height) },
command.transform,
)
}
///|
fn pack_text_vertices2d_raw(
rect : @smath.Rect,
transform : @smath.Transform,
) -> Array[Double] {
let (p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y) = rect_points2d(rect, transform)
let vertices : Array[Double] = []
push_tex_vertex2d(vertices, p0x, p0y, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0)
push_tex_vertex2d(vertices, p1x, p1y, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0)
push_tex_vertex2d(vertices, p2x, p2y, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
push_tex_vertex2d(vertices, p0x, p0y, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0)
push_tex_vertex2d(vertices, p2x, p2y, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0)
push_tex_vertex2d(vertices, p3x, p3y, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0)
vertices
}
///|
fn pack_circle_fill_vertices2d(
center : @smath.Vec2,
radius : Double,
transform : @smath.Transform,
color : @render.Color,
) -> Array[Double] {
let vertices : Array[Double] = []
let (r, g, b, a) = color_rgba01(color)
let center_x = center[X] + transform.tx
let center_y = center[Y] + transform.ty
for index in 0..<40 {
let angle0 = index.to_double() / 40.0 * @math.PI * 2.0
let angle1 = (index + 1).to_double() / 40.0 * @math.PI * 2.0
let (x0, y0) = transformed_local_point2d(
center,
transform,
@math.cos(angle0) * radius,
@math.sin(angle0) * radius,
)
let (x1, y1) = transformed_local_point2d(
center,
transform,
@math.cos(angle1) * radius,
@math.sin(angle1) * radius,
)
push_color_vertex2d(vertices, center_x, center_y, r, g, b, a)
push_color_vertex2d(vertices, x0, y0, r, g, b, a)
push_color_vertex2d(vertices, x1, y1, r, g, b, a)
}
vertices
}
///|
fn pack_circle_stroke_vertices2d(
center : @smath.Vec2,
radius : Double,
transform : @smath.Transform,
color : @render.Color,
) -> Array[Double] {
let vertices : Array[Double] = []
let (r, g, b, a) = color_rgba01(color)
for index in 0..<40 {
let angle0 = index.to_double() / 40.0 * @math.PI * 2.0
let angle1 = (index + 1).to_double() / 40.0 * @math.PI * 2.0
let (x0, y0) = transformed_local_point2d(
center,
transform,
@math.cos(angle0) * radius,
@math.sin(angle0) * radius,
)
let (x1, y1) = transformed_local_point2d(
center,
transform,
@math.cos(angle1) * radius,
@math.sin(angle1) * radius,
)
push_color_vertex2d(vertices, x0, y0, r, g, b, a)
push_color_vertex2d(vertices, x1, y1, r, g, b, a)
}
vertices
}
///|
fn pack_geometry_vertices2d(
command : @render2d_types.GeometryDrawCommand2D,
) -> Array[Double] {
let vertices : Array[Double] = []
let (r, g, b, a) = color_rgba01(command.color)
for point in command.vertices {
let (x, y) = transformed_local_point2d(
command.origin,
command.transform,
point[X],
point[Y],
)
push_color_vertex2d(vertices, x, y, r, g, b, a)
}
vertices
}
///|
fn rect_points2d(
rect : @smath.Rect,
transform : @smath.Transform,
) -> (Double, Double, Double, Double, Double, Double, Double, Double) {
let origin = rect.position
let width = rect.size[X]
let height = rect.size[Y]
let (p0x, p0y) = transformed_local_point2d(origin, transform, 0.0, 0.0)
let (p1x, p1y) = transformed_local_point2d(origin, transform, width, 0.0)
let (p2x, p2y) = transformed_local_point2d(origin, transform, width, height)
let (p3x, p3y) = transformed_local_point2d(origin, transform, 0.0, height)
(p0x, p0y, p1x, p1y, p2x, p2y, p3x, p3y)
}
///|
fn transformed_local_point2d(
origin : @smath.Vec2,
transform : @smath.Transform,
local_x : Double,
local_y : Double,
) -> (Double, Double) {
(
transform.a * local_x + transform.c * local_y + transform.tx + origin[X],
transform.b * local_x + transform.d * local_y + transform.ty + origin[Y],
)
}
///|
fn transform_uv2d(
transform : @smath.Transform,
u : Double,
v : Double,
) -> (Double, Double) {
(
transform.a * u + transform.c * v + transform.tx,
transform.b * u + transform.d * v + transform.ty,
)
}
///|
fn color_rgba01(color : @render.Color) -> (Double, Double, Double, Double) {
(
color.r.reinterpret_as_int().to_double() / 255.0,
color.g.reinterpret_as_int().to_double() / 255.0,
color.b.reinterpret_as_int().to_double() / 255.0,
color.a,
)
}
///|
fn push_color_vertex2d(
vertices : Array[Double],
x : Double,
y : Double,
r : Double,
g : Double,
b : Double,
a : Double,
) -> Unit {
vertices.push(x)
vertices.push(y)
vertices.push(r)
vertices.push(g)
vertices.push(b)
vertices.push(a)
}
///|
fn push_tex_vertex2d(
vertices : Array[Double],
x : Double,
y : Double,
u : Double,
v : Double,
r : Double,
g : Double,
b : Double,
a : Double,
) -> Unit {
vertices.push(x)
vertices.push(y)
vertices.push(u)
vertices.push(v)
vertices.push(r)
vertices.push(g)
vertices.push(b)
vertices.push(a)
}