1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
use crate::Pt;
use crate::ShaderOpts;
use crate::Text;
#[cfg(feature = "model-3d")]
pub(crate) use crate::drawable_3d::DrawCommand3D;
use crate::image_shader::ImageShaderBindings;
/// Trait for objects that can be drawn into an [`Image`][crate::Image].
pub trait Drawable {
/// Associated options for configuring the draw call (e.g., [`DrawOption`][crate::DrawOption]).
type Options;
/// Renders the object into the specified target image.
///
/// This is the low-level drawing interface. Users should typically call
/// `target.draw(ctx, drawable, options)` instead.
fn draw_to(self, ctx: &mut crate::Context, target: crate::Image, options: Self::Options);
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct ImageCommand {
pub id: u32,
pub target_texture_id: u32,
pub opts: DrawOption,
pub shader_id: u32,
pub shader_opts: ShaderOpts,
pub shader_bindings: ImageShaderBindings,
pub size: [Pt; 2],
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct TextCommand {
pub target_texture_id: u32,
pub text: Box<Text>,
pub opts: DrawOption,
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) enum DrawCommand {
Image(Box<ImageCommand>),
Text(Box<TextCommand>),
}
/// Controls how an image is sampled when the drawn quad is larger than one tile.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ImageRepeat {
/// Scale the image to fill the drawn quad.
#[default]
Stretch,
/// Repeat the image horizontally and vertically.
Repeat,
/// Repeat horizontally and keep a single tile vertically.
RepeatX,
/// Repeat vertically and keep a single tile horizontally.
RepeatY,
/// Repeat horizontally and stretch vertically.
RepeatXStretchY,
/// Repeat vertically and stretch horizontally.
RepeatYStretchX,
/// Draw one tile and leave the rest transparent.
NoRepeat,
}
/// Unified options for drawing images and text.
///
/// Controls the position, rotation, and scale of drawn items.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct DrawOption {
/// Position in logical units relative to the target's top-left corner.
position: [Pt; 2],
/// Rotation in radians.
rotation: f32,
/// Scale factors (x, y). Applied after size.
scale: [f32; 2],
opacity: f32,
repeat: ImageRepeat,
tile_size: Option<[Pt; 2]>,
}
impl Default for DrawOption {
fn default() -> Self {
Self {
position: [Pt(0.0), Pt(0.0)],
scale: [1.0, 1.0],
rotation: 0.0,
opacity: 1.0,
repeat: ImageRepeat::Stretch,
tile_size: None,
}
}
}
impl DrawOption {
/// Creates a new DrawOption with position, rotation, and scale.
pub fn new(position: [Pt; 2], rotation: f32, scale: [f32; 2]) -> Self {
Self {
position,
rotation,
scale,
opacity: 1.0,
repeat: ImageRepeat::Stretch,
tile_size: None,
}
}
pub fn position(&self) -> [Pt; 2] {
self.position
}
/// Sets the drawing position relative to the current target's top-left corner.
pub fn with_position(mut self, position: [Pt; 2]) -> Self {
self.position = position;
self
}
pub fn set_position(&mut self, x: Pt, y: Pt) {
self.position = [x, y];
}
pub fn rotation(&self) -> f32 {
self.rotation
}
/// Sets the rotation in radians.
pub fn with_rotation(mut self, rotation: f32) -> Self {
self.rotation = rotation;
self
}
pub fn scale(&self) -> [f32; 2] {
self.scale
}
/// Sets the scale multiplier (e.g., [2.0, 2.0] for double size).
pub fn with_scale(mut self, scale: [f32; 2]) -> Self {
self.scale = scale;
self
}
pub fn opacity(&self) -> f32 {
self.opacity
}
/// Sets the opacity (alpha multiplier), from 0.0 to 1.0.
pub fn with_opacity(mut self, opacity: f32) -> Self {
self.opacity = opacity.clamp(0.0, 1.0);
self
}
pub fn repeat(&self) -> ImageRepeat {
self.repeat
}
pub fn with_repeat(mut self, repeat: ImageRepeat) -> Self {
self.repeat = repeat;
self
}
pub fn tile_size(&self) -> Option<[Pt; 2]> {
self.tile_size
}
pub fn with_tile_size(mut self, tile_size: [Pt; 2]) -> Self {
self.tile_size = Some(tile_size);
self
}
pub fn clear_tile_size(mut self) -> Self {
self.tile_size = None;
self
}
}