zaniah 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/LICENSE.txt +18 -0
- data/README.md +14 -4
- data/assets/icons/check.svg +1 -0
- data/assets/icons/close.svg +1 -0
- data/assets/icons/info.svg +1 -0
- data/assets/icons/menu.svg +1 -0
- data/assets/icons/search.svg +1 -0
- data/assets/icons/warning.svg +1 -0
- data/docs/accessibility.md +61 -0
- data/docs/adr/004-style-resolution.md +17 -0
- data/docs/adr/005-instance-layout.md +43 -0
- data/docs/adr/006-component-model.md +17 -0
- data/docs/adr/007-text-buffer.md +17 -0
- data/docs/adr/008-accessibility-bridge.md +19 -0
- data/docs/adr/009-grid-scope.md +17 -0
- data/docs/animation.md +46 -0
- data/docs/components.md +109 -0
- data/docs/gallery/command-palette-dark.png +0 -0
- data/docs/gallery/command-palette-high_contrast.png +0 -0
- data/docs/gallery/command-palette-light.png +0 -0
- data/docs/gallery/components-dark.png +0 -0
- data/docs/gallery/components-high_contrast.png +0 -0
- data/docs/gallery/components-light.png +0 -0
- data/docs/gallery/context-menu-dark.png +0 -0
- data/docs/gallery/context-menu-high_contrast.png +0 -0
- data/docs/gallery/context-menu-light.png +0 -0
- data/docs/gallery/dialog-dark.png +0 -0
- data/docs/gallery/dialog-high_contrast.png +0 -0
- data/docs/gallery/dialog-light.png +0 -0
- data/docs/gallery/drawer-left-dark.png +0 -0
- data/docs/gallery/drawer-left-high_contrast.png +0 -0
- data/docs/gallery/drawer-left-light.png +0 -0
- data/docs/gallery/drawer-right-dark.png +0 -0
- data/docs/gallery/drawer-right-high_contrast.png +0 -0
- data/docs/gallery/drawer-right-light.png +0 -0
- data/docs/gallery/menu-dark.png +0 -0
- data/docs/gallery/menu-high_contrast.png +0 -0
- data/docs/gallery/menu-light.png +0 -0
- data/docs/gallery/modal-dark.png +0 -0
- data/docs/gallery/modal-high_contrast.png +0 -0
- data/docs/gallery/modal-light.png +0 -0
- data/docs/gallery/popover-bottom-dark.png +0 -0
- data/docs/gallery/popover-bottom-high_contrast.png +0 -0
- data/docs/gallery/popover-bottom-light.png +0 -0
- data/docs/gallery/popover-left-dark.png +0 -0
- data/docs/gallery/popover-left-high_contrast.png +0 -0
- data/docs/gallery/popover-left-light.png +0 -0
- data/docs/gallery/popover-right-dark.png +0 -0
- data/docs/gallery/popover-right-high_contrast.png +0 -0
- data/docs/gallery/popover-right-light.png +0 -0
- data/docs/gallery/popover-top-dark.png +0 -0
- data/docs/gallery/popover-top-high_contrast.png +0 -0
- data/docs/gallery/popover-top-light.png +0 -0
- data/docs/gallery/toast-danger-dark.png +0 -0
- data/docs/gallery/toast-danger-high_contrast.png +0 -0
- data/docs/gallery/toast-danger-light.png +0 -0
- data/docs/gallery/toast-info-dark.png +0 -0
- data/docs/gallery/toast-info-high_contrast.png +0 -0
- data/docs/gallery/toast-info-light.png +0 -0
- data/docs/gallery/toast-success-dark.png +0 -0
- data/docs/gallery/toast-success-high_contrast.png +0 -0
- data/docs/gallery/toast-success-light.png +0 -0
- data/docs/gallery/toast-warning-dark.png +0 -0
- data/docs/gallery/toast-warning-high_contrast.png +0 -0
- data/docs/gallery/toast-warning-light.png +0 -0
- data/docs/gallery/tooltip-bottom-dark.png +0 -0
- data/docs/gallery/tooltip-bottom-high_contrast.png +0 -0
- data/docs/gallery/tooltip-bottom-light.png +0 -0
- data/docs/gallery/tooltip-left-dark.png +0 -0
- data/docs/gallery/tooltip-left-high_contrast.png +0 -0
- data/docs/gallery/tooltip-left-light.png +0 -0
- data/docs/gallery/tooltip-right-dark.png +0 -0
- data/docs/gallery/tooltip-right-high_contrast.png +0 -0
- data/docs/gallery/tooltip-right-light.png +0 -0
- data/docs/gallery/tooltip-top-dark.png +0 -0
- data/docs/gallery/tooltip-top-high_contrast.png +0 -0
- data/docs/gallery/tooltip-top-light.png +0 -0
- data/docs/index.html +74 -0
- data/docs/layout.md +31 -0
- data/docs/native.md +7 -2
- data/docs/site.css +32 -0
- data/docs/text.md +35 -0
- data/docs/theme.md +50 -0
- data/docs/tui.md +36 -0
- data/examples/gallery.rb +155 -0
- data/examples/native_ime.rb +13 -6
- data/lib/zaniah/accessibility/linux/service.rb +361 -0
- data/lib/zaniah/accessibility/linux.rb +23 -0
- data/lib/zaniah/accessibility/mac.rb +136 -0
- data/lib/zaniah/accessibility/native_tree.rb +53 -0
- data/lib/zaniah/accessibility/node.rb +12 -0
- data/lib/zaniah/accessibility/tree.rb +61 -0
- data/lib/zaniah/accessibility/windows/provider.rb +273 -0
- data/lib/zaniah/accessibility/windows.rb +38 -0
- data/lib/zaniah/accessibility.rb +63 -0
- data/lib/zaniah/animation/animator.rb +95 -0
- data/lib/zaniah/animation/easing.rb +45 -0
- data/lib/zaniah/animation/spring.rb +36 -0
- data/lib/zaniah/animation/transition.rb +5 -0
- data/lib/zaniah/animation.rb +6 -0
- data/lib/zaniah/app.rb +22 -2
- data/lib/zaniah/color.rb +16 -0
- data/lib/zaniah/devtools/hot_reload.rb +40 -0
- data/lib/zaniah/devtools/inspector.rb +111 -0
- data/lib/zaniah/devtools/stats_overlay.rb +16 -0
- data/lib/zaniah/devtools.rb +13 -0
- data/lib/zaniah/element.rb +210 -15
- data/lib/zaniah/frame_context.rb +4 -1
- data/lib/zaniah/gpu/frame_encoder.rb +10 -1
- data/lib/zaniah/gpu/instance_packing.rb +33 -5
- data/lib/zaniah/gpu/metal.rb +26 -10
- data/lib/zaniah/gpu/open_gl.rb +35 -14
- data/lib/zaniah/gpu/software.rb +116 -14
- data/lib/zaniah/gpu/vulkan/scene.frag +51 -0
- data/lib/zaniah/gpu/vulkan/scene.frag.spv +0 -0
- data/lib/zaniah/gpu/vulkan/scene.vert +50 -0
- data/lib/zaniah/gpu/vulkan/scene.vert.spv +0 -0
- data/lib/zaniah/gpu/vulkan.rb +282 -36
- data/lib/zaniah/gpu.rb +8 -3
- data/lib/zaniah/input/dispatcher.rb +96 -12
- data/lib/zaniah/input/focus_handle.rb +13 -4
- data/lib/zaniah/input/focus_tree.rb +77 -0
- data/lib/zaniah/input/keymap.rb +77 -0
- data/lib/zaniah/input.rb +1 -0
- data/lib/zaniah/interactivity.rb +30 -0
- data/lib/zaniah/layout/engine.rb +173 -36
- data/lib/zaniah/layout/grid.rb +134 -0
- data/lib/zaniah/layout/style.rb +16 -1
- data/lib/zaniah/layout.rb +1 -0
- data/lib/zaniah/length.rb +3 -0
- data/lib/zaniah/length_units.rb +7 -0
- data/lib/zaniah/list.rb +27 -15
- data/lib/zaniah/platform/appearance.rb +11 -0
- data/lib/zaniah/platform/headless/window.rb +101 -9
- data/lib/zaniah/platform/linux/appearance_aware.rb +8 -0
- data/lib/zaniah/platform/linux/wayland_window.rb +3 -1
- data/lib/zaniah/platform/linux/window.rb +4 -2
- data/lib/zaniah/platform/mac/app.rb +1 -1
- data/lib/zaniah/platform/mac/core_text.rb +3 -0
- data/lib/zaniah/platform/mac/window.rb +9 -1
- data/lib/zaniah/platform/tui/window.rb +1 -1
- data/lib/zaniah/platform/windows/window.rb +12 -2
- data/lib/zaniah/platform.rb +1 -0
- data/lib/zaniah/scene.rb +137 -28
- data/lib/zaniah/scroll_state.rb +117 -0
- data/lib/zaniah/scroll_view.rb +94 -0
- data/lib/zaniah/style/gradient.rb +28 -0
- data/lib/zaniah/style/ring.rb +13 -0
- data/lib/zaniah/style/shadow.rb +13 -0
- data/lib/zaniah/style/style_set.rb +43 -0
- data/lib/zaniah/style/transform.rb +40 -0
- data/lib/zaniah/svg.rb +19 -0
- data/lib/zaniah/text.rb +261 -6
- data/lib/zaniah/text_buffer.rb +119 -0
- data/lib/zaniah/text_selection.rb +18 -0
- data/lib/zaniah/text_system/atlas_cache.rb +1 -1
- data/lib/zaniah/text_system/fallback.rb +24 -0
- data/lib/zaniah/text_system/font_db.rb +4 -1
- data/lib/zaniah/text_system/kinsoku.rb +28 -0
- data/lib/zaniah/text_system/line_breaker.rb +54 -0
- data/lib/zaniah/text_system/paragraph.rb +99 -0
- data/lib/zaniah/text_system/renderer.rb +13 -2
- data/lib/zaniah/text_system/typesetter.rb +4 -0
- data/lib/zaniah/text_system.rb +3 -0
- data/lib/zaniah/theme.rb +39 -0
- data/lib/zaniah/ui/charts.rb +165 -0
- data/lib/zaniah/ui/choice_fields.rb +134 -0
- data/lib/zaniah/ui/component.rb +73 -0
- data/lib/zaniah/ui/controls.rb +439 -0
- data/lib/zaniah/ui/data.rb +364 -0
- data/lib/zaniah/ui/editors.rb +67 -0
- data/lib/zaniah/ui/forms.rb +131 -0
- data/lib/zaniah/ui/layout_components.rb +205 -0
- data/lib/zaniah/ui/modal.rb +129 -0
- data/lib/zaniah/ui/overlays.rb +282 -0
- data/lib/zaniah/ui/pickers.rb +169 -0
- data/lib/zaniah/ui/primitives.rb +199 -0
- data/lib/zaniah/ui/scrollbar.rb +104 -0
- data/lib/zaniah/ui/structure.rb +224 -0
- data/lib/zaniah/ui/text_inputs.rb +169 -0
- data/lib/zaniah/ui/variants.rb +23 -0
- data/lib/zaniah/ui.rb +18 -0
- data/lib/zaniah/unicode.rb +42 -0
- data/lib/zaniah/uniform_list.rb +27 -6
- data/lib/zaniah/version.rb +1 -1
- data/lib/zaniah.rb +14 -0
- data/sig/accessibility.rbs +34 -0
- data/sig/animation.rbs +54 -0
- data/sig/app.rbs +2 -1
- data/sig/devtools.rbs +37 -0
- data/sig/elements.rbs +88 -6
- data/sig/gpu.rbs +9 -2
- data/sig/input.rbs +31 -5
- data/sig/layout.rbs +2 -1
- data/sig/platform.rbs +24 -4
- data/sig/scene.rbs +15 -2
- data/sig/style.rbs +82 -0
- data/sig/text.rbs +35 -0
- data/sig/theme.rbs +64 -0
- data/sig/ui.rbs +390 -0
- data/sig/zaniah.rbs +56 -3
- data/tools/generate_component_gallery.rb +48 -0
- metadata +147 -2
data/lib/zaniah/gpu/open_gl.rb
CHANGED
|
@@ -12,42 +12,63 @@ module Zaniah
|
|
|
12
12
|
#version 330 core
|
|
13
13
|
layout(location=0) in vec4 rect;
|
|
14
14
|
layout(location=1) in vec4 tint;
|
|
15
|
-
layout(location=2) in vec4
|
|
16
|
-
layout(location=3) in vec4
|
|
17
|
-
layout(location=4) in vec4
|
|
18
|
-
layout(location=5) in vec4
|
|
15
|
+
layout(location=2) in vec4 tint2;
|
|
16
|
+
layout(location=3) in vec4 corners;
|
|
17
|
+
layout(location=4) in vec4 border_color;
|
|
18
|
+
layout(location=5) in vec4 edges_source;
|
|
19
|
+
layout(location=6) in vec4 gradient_data;
|
|
20
|
+
layout(location=7) in vec4 gradient_center_kind;
|
|
21
|
+
layout(location=8) in vec4 matrix;
|
|
22
|
+
layout(location=9) in vec4 translation_flags;
|
|
19
23
|
uniform vec2 viewport;
|
|
20
24
|
out vec2 local_position; out vec2 size; out vec4 color; out vec4 radii;
|
|
21
|
-
out vec4 border; out vec4
|
|
25
|
+
out vec4 secondary; out vec4 border; out vec4 widths; out vec4 gradient;
|
|
26
|
+
out vec4 center_kind; out vec2 uv; out float dash;
|
|
22
27
|
void main() {
|
|
23
28
|
vec2 vertices[4] = vec2[4](vec2(0,0),vec2(1,0),vec2(0,1),vec2(1,1));
|
|
24
29
|
vec2 corner = vertices[gl_VertexID];
|
|
25
30
|
vec2 position = rect.xy + corner * rect.zw;
|
|
26
|
-
if (
|
|
31
|
+
if (gradient_center_kind.w == 3) position = gl_VertexID == 0 ? rect.xy : gl_VertexID == 1 ? rect.zw : corners.xy;
|
|
32
|
+
position = vec2(matrix.x * position.x + matrix.z * position.y + translation_flags.x,
|
|
33
|
+
matrix.y * position.x + matrix.w * position.y + translation_flags.y);
|
|
27
34
|
gl_Position = vec4(position.x / viewport.x * 2 - 1, 1 - position.y / viewport.y * 2, 0, 1);
|
|
28
35
|
local_position = corner * rect.zw; size = rect.zw; color = tint;
|
|
29
|
-
|
|
36
|
+
secondary = tint2; radii = corners; border = border_color; widths = edges_source;
|
|
37
|
+
gradient = gradient_data; center_kind = gradient_center_kind;
|
|
38
|
+
uv = edges_source.xy + corner * edges_source.zw; dash = translation_flags.w;
|
|
30
39
|
}
|
|
31
40
|
GLSL
|
|
32
41
|
FRAGMENT = <<~GLSL.freeze
|
|
33
42
|
#version 330 core
|
|
34
43
|
in vec2 local_position; in vec2 size; in vec4 color; in vec4 radii;
|
|
35
|
-
in vec4 border; in vec4
|
|
44
|
+
in vec4 secondary; in vec4 border; in vec4 widths; in vec4 gradient;
|
|
45
|
+
in vec4 center_kind; in vec2 uv; in float dash;
|
|
36
46
|
uniform sampler2D atlas;
|
|
37
47
|
out vec4 output_color;
|
|
38
48
|
void main() {
|
|
39
49
|
vec4 result = color;
|
|
40
|
-
if (
|
|
50
|
+
if (center_kind.w == 0) {
|
|
51
|
+
if (gradient.x > 0) {
|
|
52
|
+
vec2 normalized = local_position / size;
|
|
53
|
+
float raw = gradient.x == 1
|
|
54
|
+
? dot(normalized - 0.5, vec2(cos(radians(gradient.w)), sin(radians(gradient.w)))) + 0.5
|
|
55
|
+
: length(normalized - center_kind.xy) / center_kind.z;
|
|
56
|
+
result = mix(color, secondary, clamp((raw - gradient.y) / max(gradient.z - gradient.y, 0.000001), 0, 1));
|
|
57
|
+
}
|
|
41
58
|
float radius = local_position.y < size.y/2 ? (local_position.x < size.x/2 ? radii.x : radii.y) : (local_position.x < size.x/2 ? radii.w : radii.z);
|
|
42
59
|
radius = clamp(radius, 0, min(size.x,size.y)/2);
|
|
43
60
|
vec2 q = abs(local_position - size/2) - size/2 + radius;
|
|
44
61
|
float distance = length(max(q,0)) + min(max(q.x,q.y),0) - radius;
|
|
45
62
|
float coverage = clamp(0.5 - distance, 0, 1);
|
|
46
|
-
|
|
63
|
+
vec4 edge_distance = vec4(local_position.y, size.x-local_position.x, size.y-local_position.y, local_position.x);
|
|
64
|
+
float edge = min(min(edge_distance.x,edge_distance.y),min(edge_distance.z,edge_distance.w));
|
|
65
|
+
float border_width = edge == edge_distance.x ? widths.x : edge == edge_distance.y ? widths.y : edge == edge_distance.z ? widths.z : widths.w;
|
|
66
|
+
float coordinate = edge == edge_distance.x || edge == edge_distance.z ? local_position.x : local_position.y;
|
|
67
|
+
if (border_width > 0 && distance >= -border_width && !(dash == 1 && mod(coordinate,6) >= 3)) result = border;
|
|
47
68
|
result.a *= coverage;
|
|
48
|
-
} else if (
|
|
69
|
+
} else if (center_kind.w == 1 || center_kind.w == 2) {
|
|
49
70
|
vec4 sample_color = texture(atlas,uv);
|
|
50
|
-
if (
|
|
71
|
+
if (center_kind.w == 1) result.a *= sample_color.r; else result *= sample_color;
|
|
51
72
|
}
|
|
52
73
|
output_color = vec4(result.rgb * result.a, result.a);
|
|
53
74
|
}
|
|
@@ -62,7 +83,7 @@ module Zaniah
|
|
|
62
83
|
@vao, @buffer = generate(:glGenVertexArrays), generate(:glGenBuffers)
|
|
63
84
|
gl(:glBindVertexArray, [I], V, @vao)
|
|
64
85
|
gl(:glBindBuffer, [I, I], V, 0x8892, @buffer)
|
|
65
|
-
|
|
86
|
+
10.times do |location|
|
|
66
87
|
gl(:glEnableVertexAttribArray, [I], V, location)
|
|
67
88
|
gl(:glVertexAttribDivisor, [I, I], V, location, 1)
|
|
68
89
|
end
|
|
@@ -148,7 +169,7 @@ module Zaniah
|
|
|
148
169
|
clip = clip ? Bounds.new(0, 0, @width, @height).intersect(clip) : Bounds.new(0, 0, @width, @height)
|
|
149
170
|
next unless clip.width.positive? && clip.height.positive?
|
|
150
171
|
gl(:glScissor, [I] * 4, V, (clip.x * @scale).to_i, ((@height - clip.bottom) * @scale).to_i, (clip.width * @scale).to_i, (clip.height * @scale).to_i)
|
|
151
|
-
|
|
172
|
+
10.times do |location|
|
|
152
173
|
gl(:glVertexAttribPointer, [I, I, I, I, I, P], V, location, 4, 0x1406, 0, InstancePacking::STRIDE * 4, (first * InstancePacking::STRIDE + location * 4) * 4)
|
|
153
174
|
end
|
|
154
175
|
native_texture(texture || @white)
|
data/lib/zaniah/gpu/software.rb
CHANGED
|
@@ -14,6 +14,7 @@ module Zaniah
|
|
|
14
14
|
@width, @height = width.to_i, height.to_i
|
|
15
15
|
@viewport = Bounds.new(0, 0, @width, @height)
|
|
16
16
|
@pixels = "\0".b * (@width * @height * 4)
|
|
17
|
+
@gradient_cache ||= {}
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def create_texture(width, height, **options) = Texture.new(width, height, **options)
|
|
@@ -43,25 +44,48 @@ module Zaniah
|
|
|
43
44
|
private
|
|
44
45
|
|
|
45
46
|
def draw_quad(data, offset, clip)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
values = data.slice(offset, Scene::QUAD_STRIDE)
|
|
48
|
+
x, y, width, height = values.first(4)
|
|
49
|
+
color, secondary = values[4, 4], values[8, 4]
|
|
50
|
+
radii, border_color, borders = values[12, 4], values[16, 4], values[20, 4]
|
|
51
|
+
gradient = values[24, 7]
|
|
52
|
+
matrix = Transform.new(*values[32, 6])
|
|
53
|
+
bounds = transformed_bounds(x, y, width, height, matrix).intersect(clip)
|
|
54
|
+
if matrix.b.zero? && matrix.c.zero? && borders.all?(&:zero?) && color[3] == 1 &&
|
|
55
|
+
radii.all?(&:zero?) && gradient[0].zero? &&
|
|
56
|
+
[bounds.x, bounds.y, bounds.width, bounds.height].all? { |value| value == value.to_i }
|
|
57
|
+
fill_rect(bounds, *color.first(3))
|
|
58
|
+
return
|
|
59
|
+
end
|
|
60
|
+
gradient = gradient_parameters(gradient)
|
|
61
|
+
if matrix == Transform.identity && borders.all?(&:zero?) && radii.all?(&:zero?) &&
|
|
62
|
+
color[3] == 1 && secondary[3] == 1 && gradient[0].positive? &&
|
|
63
|
+
[x, y, width, height].all? { |value| value == value.to_i }
|
|
64
|
+
fill_cached_gradient(bounds, x.to_i, y.to_i, width.to_i, height.to_i, color, secondary, gradient)
|
|
65
|
+
return
|
|
66
|
+
end
|
|
67
|
+
inverse = matrix.inverse
|
|
49
68
|
top, bottom = [bounds.y.floor, 0].max, [bounds.bottom.ceil, @height].min
|
|
50
69
|
left, right = [bounds.x.floor, 0].max, [bounds.right.ceil, @width].min
|
|
51
70
|
pixel_y = top
|
|
52
71
|
while pixel_y < bottom
|
|
53
72
|
pixel_x = left
|
|
54
73
|
while pixel_x < right
|
|
55
|
-
|
|
56
|
-
|
|
74
|
+
point = inverse.apply(Point.new(pixel_x + 0.5, pixel_y + 0.5))
|
|
75
|
+
local_x, local_y = point.x - x, point.y - y
|
|
76
|
+
radius = local_y < height / 2.0 ? (local_x < width / 2.0 ? radii[0] : radii[1]) : (local_x < width / 2.0 ? radii[3] : radii[2])
|
|
57
77
|
radius = radius.clamp(0, [width, height].min / 2.0)
|
|
58
|
-
quad_x = (
|
|
59
|
-
quad_y = (
|
|
78
|
+
quad_x = (local_x - width / 2.0).abs - width / 2.0 + radius
|
|
79
|
+
quad_y = (local_y - height / 2.0).abs - height / 2.0 + radius
|
|
60
80
|
distance = Math.sqrt([quad_x, 0].max**2 + [quad_y, 0].max**2) + [[quad_x, quad_y].max, 0].min - radius
|
|
61
81
|
coverage = (0.5 - distance).clamp(0, 1)
|
|
62
82
|
if coverage.positive?
|
|
63
|
-
|
|
64
|
-
|
|
83
|
+
fill = gradient[0].zero? ? color : mix(color, secondary, gradient_offset(gradient, local_x, local_y, width, height))
|
|
84
|
+
edge = [local_y, width - local_x, height - local_y, local_x].each_with_index.min_by(&:first).last
|
|
85
|
+
border = borders[edge]
|
|
86
|
+
dashed = values[39] == 1 && ((edge.odd? ? local_y : local_x) % 6) >= 3
|
|
87
|
+
fill = border_color if border.positive? && distance >= -border && !dashed
|
|
88
|
+
blend(pixel_x, pixel_y, fill[0], fill[1], fill[2], fill[3] * coverage)
|
|
65
89
|
end
|
|
66
90
|
pixel_x += 1
|
|
67
91
|
end
|
|
@@ -69,15 +93,62 @@ module Zaniah
|
|
|
69
93
|
end
|
|
70
94
|
end
|
|
71
95
|
|
|
96
|
+
def fill_rect(bounds, red, green, blue)
|
|
97
|
+
left, right = [bounds.x.to_i, 0].max, [bounds.right.to_i, @width].min
|
|
98
|
+
top, bottom = [bounds.y.to_i, 0].max, [bounds.bottom.to_i, @height].min
|
|
99
|
+
return if left >= right || top >= bottom
|
|
100
|
+
row = [red, green, blue, 1].map { |value| (value.clamp(0, 1) * 255).round }.pack("C4") * (right - left)
|
|
101
|
+
(top...bottom).each { |pixel_y| @pixels[pixel_y * @width * 4 + left * 4, row.bytesize] = row }
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def fill_cached_gradient(bounds, x, y, width, height, color, secondary, gradient)
|
|
105
|
+
return if width <= 0 || height <= 0
|
|
106
|
+
@gradient_cache.clear if @gradient_cache.length >= 256
|
|
107
|
+
key = [width, height, color, secondary, gradient]
|
|
108
|
+
tile = @gradient_cache[key] ||= gradient_tile(width, height, color, secondary, gradient)
|
|
109
|
+
left, right = [bounds.x.ceil, 0].max, [bounds.right.floor, @width].min
|
|
110
|
+
top, bottom = [bounds.y.ceil, 0].max, [bounds.bottom.floor, @height].min
|
|
111
|
+
return if left >= right || top >= bottom
|
|
112
|
+
bytes = (right - left) * 4
|
|
113
|
+
(top...bottom).each do |pixel_y|
|
|
114
|
+
source = ((pixel_y - y) * width + left - x) * 4
|
|
115
|
+
@pixels[pixel_y * @width * 4 + left * 4, bytes] = tile.byteslice(source, bytes)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def gradient_tile(width, height, left, right, gradient)
|
|
120
|
+
kind, first, scale, cosine, sine, center_x, center_y, radius = gradient
|
|
121
|
+
starts = left.first(3).map { |value| value * 255 }
|
|
122
|
+
deltas = 3.times.map { |index| (right[index] - left[index]) * 255 }
|
|
123
|
+
bytes = String.new(capacity: width * height * 4, encoding: Encoding::BINARY)
|
|
124
|
+
height.times do |pixel_y|
|
|
125
|
+
ny = (pixel_y + 0.5) / height
|
|
126
|
+
width.times do |pixel_x|
|
|
127
|
+
nx = (pixel_x + 0.5) / width
|
|
128
|
+
raw = kind == 1 ? (nx - 0.5) * cosine + (ny - 0.5) * sine + 0.5 : Math.hypot(nx - center_x, ny - center_y) / radius
|
|
129
|
+
amount = ((raw - first) * scale).clamp(0, 1)
|
|
130
|
+
bytes << (starts[0] + deltas[0] * amount).round.clamp(0, 255)
|
|
131
|
+
bytes << (starts[1] + deltas[1] * amount).round.clamp(0, 255)
|
|
132
|
+
bytes << (starts[2] + deltas[2] * amount).round.clamp(0, 255)
|
|
133
|
+
bytes << 255
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
bytes.freeze
|
|
137
|
+
end
|
|
138
|
+
|
|
72
139
|
def draw_sprite(scene, offset, clip)
|
|
73
|
-
x, y, width, height, red, green, blue, alpha, id, source_x, source_y, source_width, source_height = scene.sprite_data.slice(offset,
|
|
140
|
+
x, y, width, height, red, green, blue, alpha, id, source_x, source_y, source_width, source_height = scene.sprite_data.slice(offset, Scene::SPRITE_STRIDE)
|
|
74
141
|
return if width <= 0 || height <= 0
|
|
75
142
|
texture = scene.textures.fetch(id)
|
|
76
|
-
|
|
143
|
+
matrix = scene.sprite_transform(offset)
|
|
144
|
+
inverse = matrix.inverse
|
|
145
|
+
bounds = transformed_bounds(x, y, width, height, matrix).intersect(clip)
|
|
77
146
|
([bounds.y.ceil, 0].max...[bounds.bottom.ceil, @height].min).each do |pixel_y|
|
|
78
147
|
([bounds.x.ceil, 0].max...[bounds.right.ceil, @width].min).each do |pixel_x|
|
|
79
|
-
|
|
80
|
-
|
|
148
|
+
point = inverse.apply(Point.new(pixel_x + 0.5, pixel_y + 0.5))
|
|
149
|
+
next unless point.x >= x && point.y >= y && point.x < x + width && point.y < y + height
|
|
150
|
+
texture_x = (source_x + (point.x - x) * source_width / width).floor.clamp(0, texture.width - 1)
|
|
151
|
+
texture_y = (source_y + (point.y - y) * source_height / height).floor.clamp(0, texture.height - 1)
|
|
81
152
|
index = texture_y * texture.width + texture_x
|
|
82
153
|
if texture.format == :r8
|
|
83
154
|
blend(pixel_x, pixel_y, red, green, blue, alpha * texture.data.getbyte(index) / 255.0)
|
|
@@ -93,7 +164,12 @@ module Zaniah
|
|
|
93
164
|
end
|
|
94
165
|
|
|
95
166
|
def draw_triangle(data, offset, clip)
|
|
96
|
-
x0, y0, x1, y1, x2, y2, red, green, blue, alpha = data.slice(offset,
|
|
167
|
+
x0, y0, x1, y1, x2, y2, red, green, blue, alpha, *transform = data.slice(offset, 16)
|
|
168
|
+
matrix = Transform.new(*transform)
|
|
169
|
+
x0, y0, x1, y1, x2, y2 = [[x0, y0], [x1, y1], [x2, y2]].flat_map do |x, y|
|
|
170
|
+
point = matrix.apply(Point.new(x, y))
|
|
171
|
+
[point.x, point.y]
|
|
172
|
+
end
|
|
97
173
|
area = (x1 - x0) * (y2 - y0) - (y1 - y0) * (x2 - x0)
|
|
98
174
|
return if area.zero?
|
|
99
175
|
bounds = Bounds.new([x0, x1, x2].min, [y0, y1, y2].min,
|
|
@@ -121,6 +197,32 @@ module Zaniah
|
|
|
121
197
|
end
|
|
122
198
|
@pixels.setbyte(index + 3, (combined_alpha * 255).round)
|
|
123
199
|
end
|
|
200
|
+
|
|
201
|
+
def transformed_bounds(x, y, width, height, matrix)
|
|
202
|
+
points = [[x, y], [x + width, y], [x, y + height], [x + width, y + height]].map do |px, py|
|
|
203
|
+
matrix.apply(Point.new(px, py))
|
|
204
|
+
end
|
|
205
|
+
left, right = points.map(&:x).minmax
|
|
206
|
+
top, bottom = points.map(&:y).minmax
|
|
207
|
+
Bounds.new(left, top, right - left, bottom - top)
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def gradient_parameters(values)
|
|
211
|
+
kind, first, last, angle, center_x, center_y, radius = values
|
|
212
|
+
radians = angle * Math::PI / 180
|
|
213
|
+
[kind, first, 1.0 / [last - first, Float::EPSILON].max, Math.cos(radians), Math.sin(radians), center_x, center_y, radius]
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def gradient_offset(gradient, x, y, width, height)
|
|
217
|
+
kind, first, scale, cosine, sine, center_x, center_y, radius = gradient
|
|
218
|
+
nx, ny = x / width, y / height
|
|
219
|
+
raw = kind == 1 ? (nx - 0.5) * cosine + (ny - 0.5) * sine + 0.5 : Math.hypot(nx - center_x, ny - center_y) / radius
|
|
220
|
+
((raw - first) * scale).clamp(0, 1)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def mix(left, right, amount)
|
|
224
|
+
4.times.map { |index| left[index] + (right[index] - left[index]) * amount }
|
|
225
|
+
end
|
|
124
226
|
end
|
|
125
227
|
end
|
|
126
228
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#version 450
|
|
2
|
+
|
|
3
|
+
layout(location = 0) in vec2 local_position;
|
|
4
|
+
layout(location = 1) in vec2 primitive_size;
|
|
5
|
+
layout(location = 2) in vec4 color;
|
|
6
|
+
layout(location = 3) in vec4 secondary;
|
|
7
|
+
layout(location = 4) in vec4 radii;
|
|
8
|
+
layout(location = 5) in vec4 border;
|
|
9
|
+
layout(location = 6) in vec4 widths;
|
|
10
|
+
layout(location = 7) in vec4 gradient;
|
|
11
|
+
layout(location = 8) in vec4 center_kind;
|
|
12
|
+
layout(location = 9) in float dash;
|
|
13
|
+
|
|
14
|
+
layout(set = 0, binding = 0) uniform sampler2D atlas;
|
|
15
|
+
layout(location = 0) out vec4 fragment_color;
|
|
16
|
+
|
|
17
|
+
void main() {
|
|
18
|
+
vec4 fill = color;
|
|
19
|
+
if (center_kind.w == 0.0) {
|
|
20
|
+
if (gradient.x > 0.0) {
|
|
21
|
+
vec2 normalized = local_position / primitive_size;
|
|
22
|
+
float raw = gradient.x == 1.0
|
|
23
|
+
? dot(normalized - 0.5, vec2(cos(radians(gradient.w)), sin(radians(gradient.w)))) + 0.5
|
|
24
|
+
: length(normalized - center_kind.xy) / center_kind.z;
|
|
25
|
+
fill = mix(color, secondary,
|
|
26
|
+
clamp((raw - gradient.y) / max(gradient.z - gradient.y, 0.000001), 0.0, 1.0));
|
|
27
|
+
}
|
|
28
|
+
float radius = local_position.y < primitive_size.y / 2.0
|
|
29
|
+
? (local_position.x < primitive_size.x / 2.0 ? radii.x : radii.y)
|
|
30
|
+
: (local_position.x < primitive_size.x / 2.0 ? radii.w : radii.z);
|
|
31
|
+
radius = clamp(radius, 0.0, min(primitive_size.x, primitive_size.y) / 2.0);
|
|
32
|
+
vec2 q = abs(local_position - primitive_size / 2.0) - primitive_size / 2.0 + radius;
|
|
33
|
+
float distance = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - radius;
|
|
34
|
+
float coverage = clamp(0.5 - distance, 0.0, 1.0);
|
|
35
|
+
vec4 edge_distance = vec4(local_position.y, primitive_size.x - local_position.x,
|
|
36
|
+
primitive_size.y - local_position.y, local_position.x);
|
|
37
|
+
float edge = min(min(edge_distance.x, edge_distance.y), min(edge_distance.z, edge_distance.w));
|
|
38
|
+
float border_width = edge == edge_distance.x ? widths.x
|
|
39
|
+
: edge == edge_distance.y ? widths.y : edge == edge_distance.z ? widths.z : widths.w;
|
|
40
|
+
float coordinate = edge == edge_distance.x || edge == edge_distance.z ? local_position.x : local_position.y;
|
|
41
|
+
if (border_width > 0.0 && distance >= -border_width &&
|
|
42
|
+
!(dash == 1.0 && mod(coordinate, 6.0) >= 3.0)) fill = border;
|
|
43
|
+
fill.a *= coverage;
|
|
44
|
+
} else if (center_kind.w == 1.0 || center_kind.w == 2.0) {
|
|
45
|
+
vec2 uv = widths.xy + local_position / primitive_size * widths.zw;
|
|
46
|
+
vec4 sample_color = texture(atlas, uv);
|
|
47
|
+
if (center_kind.w == 1.0) fill.a *= sample_color.r;
|
|
48
|
+
else fill *= sample_color;
|
|
49
|
+
}
|
|
50
|
+
fragment_color = vec4(fill.rgb * fill.a, fill.a);
|
|
51
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#version 450
|
|
2
|
+
|
|
3
|
+
layout(location = 0) in vec4 instance_bounds;
|
|
4
|
+
layout(location = 1) in vec4 instance_color;
|
|
5
|
+
layout(location = 2) in vec4 instance_secondary;
|
|
6
|
+
layout(location = 3) in vec4 instance_radii;
|
|
7
|
+
layout(location = 4) in vec4 instance_border;
|
|
8
|
+
layout(location = 5) in vec4 instance_widths;
|
|
9
|
+
layout(location = 6) in vec4 instance_gradient;
|
|
10
|
+
layout(location = 7) in vec4 instance_center_kind;
|
|
11
|
+
layout(location = 8) in vec4 instance_matrix;
|
|
12
|
+
layout(location = 9) in vec4 instance_translation;
|
|
13
|
+
|
|
14
|
+
layout(push_constant) uniform Viewport { vec2 size; } viewport;
|
|
15
|
+
|
|
16
|
+
layout(location = 0) out vec2 local_position;
|
|
17
|
+
layout(location = 1) out vec2 primitive_size;
|
|
18
|
+
layout(location = 2) out vec4 color;
|
|
19
|
+
layout(location = 3) out vec4 secondary;
|
|
20
|
+
layout(location = 4) out vec4 radii;
|
|
21
|
+
layout(location = 5) out vec4 border;
|
|
22
|
+
layout(location = 6) out vec4 widths;
|
|
23
|
+
layout(location = 7) out vec4 gradient;
|
|
24
|
+
layout(location = 8) out vec4 center_kind;
|
|
25
|
+
layout(location = 9) out float dash;
|
|
26
|
+
|
|
27
|
+
void main() {
|
|
28
|
+
vec2 corners[4] = vec2[4](vec2(0, 0), vec2(1, 0), vec2(0, 1), vec2(1, 1));
|
|
29
|
+
vec2 corner = corners[gl_VertexIndex];
|
|
30
|
+
vec2 size = instance_bounds.zw;
|
|
31
|
+
vec2 position = instance_bounds.xy + corner * size;
|
|
32
|
+
if (instance_center_kind.w == 3.0) {
|
|
33
|
+
position = gl_VertexIndex == 0 ? instance_bounds.xy
|
|
34
|
+
: gl_VertexIndex == 1 ? instance_bounds.zw : instance_radii.xy;
|
|
35
|
+
}
|
|
36
|
+
position = vec2(instance_matrix.x * position.x + instance_matrix.z * position.y + instance_translation.x,
|
|
37
|
+
instance_matrix.y * position.x + instance_matrix.w * position.y + instance_translation.y);
|
|
38
|
+
gl_Position = vec4(position.x / viewport.size.x * 2.0 - 1.0,
|
|
39
|
+
position.y / viewport.size.y * 2.0 - 1.0, 0.0, 1.0);
|
|
40
|
+
local_position = corner * size;
|
|
41
|
+
primitive_size = size;
|
|
42
|
+
color = instance_color;
|
|
43
|
+
secondary = instance_secondary;
|
|
44
|
+
radii = instance_radii;
|
|
45
|
+
border = instance_border;
|
|
46
|
+
widths = instance_widths;
|
|
47
|
+
gradient = instance_gradient;
|
|
48
|
+
center_kind = instance_center_kind;
|
|
49
|
+
dash = instance_translation.w;
|
|
50
|
+
}
|
|
Binary file
|