1use std::{cmp, collections::HashMap};
2
3use crate::{utils::*, PipelineTrait, RenderConfig, RenderTarget, TextMetrics};
4
5pub use self::font::*;
6pub use self::image::Image;
7
8mod font;
9mod image;
10
11pub struct RenderContext2D {
13 draw_target: raqote::DrawTarget,
14 path: raqote::Path,
15 config: RenderConfig,
16 saved_config: Option<RenderConfig>,
17 fonts: HashMap<String, Font>,
18
19 clip: bool,
21 last_rect: Rectangle,
22 clip_rect: Option<Rectangle>,
23
24 background: Color,
25}
26
27impl RenderContext2D {
28 pub fn new(width: f64, height: f64) -> Self {
30 RenderContext2D {
31 draw_target: raqote::DrawTarget::new(width as i32, height as i32),
32 path: raqote::Path {
33 ops: Vec::new(),
34 winding: raqote::Winding::NonZero,
35 },
36 config: RenderConfig::default(),
37 saved_config: None,
38 fonts: HashMap::new(),
39 clip: false,
40 last_rect: Rectangle::new((0.0, 0.0), (width, height)),
41 clip_rect: None,
42 background: Color::default(),
43 }
44 }
45
46 pub fn set_background(&mut self, background: Color) {
48 self.background = background;
49 }
50
51 pub fn resize(&mut self, width: f64, height: f64) {
52 self.draw_target = raqote::DrawTarget::new(width as i32, height as i32);
53 }
54
55 pub fn register_font(&mut self, family: &str, font_file: &'static [u8]) {
57 if self.fonts.contains_key(family) {
58 return;
59 }
60
61 if let Ok(font) = Font::from_bytes(font_file) {
62 self.fonts.insert(family.to_string(), font);
63 }
64 }
65
66 pub fn fill_rect(&mut self, x: f64, y: f64, width: f64, height: f64) {
70 self.draw_target.fill_rect(
71 x as f32,
72 y as f32,
73 width as f32,
74 height as f32,
75 &brush_to_source(&self.config.fill_style),
76 &raqote::DrawOptions {
77 alpha: self.config.alpha,
78 ..Default::default()
79 },
80 );
81 }
82
83 pub fn stroke_rect(&mut self, x: f64, y: f64, width: f64, height: f64) {
85 self.rect(x, y, width, height);
86 self.stroke();
87 }
88
89 pub fn fill_text(&mut self, text: &str, x: f64, y: f64) {
93 if text.is_empty() {
94 return;
95 }
96
97 let color = match self.config.fill_style {
98 Brush::SolidColor(color) => color,
99 _ => Color::from("#000000"),
100 };
101
102 if color.a() == 0 || self.config.alpha == 0.0 {
103 return;
104 }
105
106 if let Some(font) = self.fonts.get(&self.config.font_config.family) {
107 let width = self.draw_target.width() as f64;
108
109 if self.clip {
110 if let Some(rect) = self.clip_rect {
111 font.render_text_clipped(
112 text,
113 self.draw_target.get_data_mut(),
114 width,
115 (self.config.font_config.font_size, color, self.config.alpha),
116 (x, y),
117 rect,
118 );
119 } else {
120 font.render_text(
121 text,
122 self.draw_target.get_data_mut(),
123 width,
124 (self.config.font_config.font_size, color, self.config.alpha),
125 (x, y),
126 );
127 }
128 } else {
129 font.render_text(
130 text,
131 self.draw_target.get_data_mut(),
132 width,
133 (self.config.font_config.font_size, color, self.config.alpha),
134 (x, y),
135 );
136 }
137 }
138 }
139
140 pub fn measure_text(&mut self, text: &str) -> TextMetrics {
142 let mut text_metrics = TextMetrics::default();
143
144 if text.is_empty() {
145 return text_metrics;
146 }
147
148 if let Some(font) = self.fonts.get(&self.config.font_config.family) {
149 let (width, height) = font.measure_text(text, self.config.font_config.font_size);
150
151 text_metrics.width = width;
152 text_metrics.height = height;
153 }
154
155 text_metrics
156 }
157
158 pub fn fill(&mut self) {
160 self.draw_target.fill(
161 &self.path,
162 &brush_to_source(&self.config.fill_style),
163 &raqote::DrawOptions {
164 alpha: self.config.alpha,
165 ..Default::default()
166 },
167 );
168 }
169
170 pub fn stroke(&mut self) {
172 self.draw_target.stroke(
173 &self.path,
174 &brush_to_source(&self.config.stroke_style),
175 &raqote::StrokeStyle {
176 width: self.config.line_width as f32,
177 ..Default::default()
178 },
179 &raqote::DrawOptions {
180 alpha: self.config.alpha,
181 ..Default::default()
182 },
183 );
184 }
185
186 pub fn begin_path(&mut self) {
188 self.path = raqote::Path {
189 ops: Vec::new(),
190 winding: raqote::Winding::NonZero,
191 };
192 }
193
194 pub fn close_path(&mut self) {
196 let mut path_builder = raqote::PathBuilder::from(self.path.clone());
197 path_builder.close();
198 self.path = path_builder.finish();
199 }
200
201 pub fn rect(&mut self, x: f64, y: f64, width: f64, height: f64) {
203 self.last_rect = Rectangle::new((x, y), (width, height));
204 let mut path_builder = raqote::PathBuilder::from(self.path.clone());
205 path_builder.rect(x as f32, y as f32, width as f32, height as f32);
206 self.path = path_builder.finish();
207 }
208
209 pub fn arc(&mut self, x: f64, y: f64, radius: f64, start_angle: f64, end_angle: f64) {
211 let mut path_builder = raqote::PathBuilder::from(self.path.clone());
212 path_builder.arc(
213 x as f32,
214 y as f32,
215 radius as f32,
216 start_angle as f32,
217 end_angle as f32,
218 );
219 self.path = path_builder.finish();
220 }
221
222 pub fn move_to(&mut self, x: f64, y: f64) {
225 let mut path_builder = raqote::PathBuilder::from(self.path.clone());
226 path_builder.move_to(x as f32, y as f32);
227 self.path = path_builder.finish();
228 }
229
230 pub fn line_to(&mut self, x: f64, y: f64) {
232 let mut path_builder = raqote::PathBuilder::from(self.path.clone());
233 path_builder.line_to(x as f32, y as f32);
234 self.path = path_builder.finish();
235 }
236
237 pub fn quadratic_curve_to(&mut self, cpx: f64, cpy: f64, x: f64, y: f64) {
239 let mut path_builder = raqote::PathBuilder::from(self.path.clone());
240 path_builder.quad_to(cpx as f32, cpy as f32, x as f32, y as f32);
241 self.path = path_builder.finish();
242 }
243
244 pub fn bezier_curve_to(&mut self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
248 let mut path_builder = raqote::PathBuilder::from(self.path.clone());
249 path_builder.cubic_to(
250 cp1x as f32,
251 cp1y as f32,
252 cp2x as f32,
253 cp2y as f32,
254 x as f32,
255 y as f32,
256 );
257 }
258
259 pub fn draw_render_target(&mut self, render_target: &RenderTarget, x: f64, y: f64) {
261 self.draw_target.draw_image_at(
262 x as f32,
263 y as f32,
264 &raqote::Image {
265 data: &render_target.data(),
266 width: render_target.width() as i32,
267 height: render_target.height() as i32,
268 },
269 &raqote::DrawOptions {
270 alpha: self.config.alpha,
271 ..Default::default()
272 },
273 );
274 }
275
276 pub fn draw_image(&mut self, image: &Image, x: f64, y: f64) {
278 self.draw_target.draw_image_at(
279 x as f32,
280 y as f32,
281 &raqote::Image {
282 data: &image.data(),
283 width: image.width() as i32,
284 height: image.height() as i32,
285 },
286 &raqote::DrawOptions {
287 alpha: self.config.alpha,
288 ..Default::default()
289 },
290 );
291 }
292
293 pub fn draw_image_with_clip(&mut self, image: &Image, clip: Rectangle, x: f64, y: f64) {
295 let mut y = y as i32;
296 let stride = image.width();
297 let mut offset = clip.y().mul_add(stride, clip.x()) as usize;
298 let last_offset = cmp::min(
299 ((clip.y() + clip.height()).mul_add(stride, clip.x())) as usize,
300 image.data().len(),
301 );
302 while offset < last_offset {
303 let next_offset = offset + stride as usize;
304
305 self.draw_target.draw_image_at(
306 x as f32,
307 y as f32,
308 &raqote::Image {
309 data: &image.data()[offset..],
310 width: clip.width() as i32,
311 height: 1,
312 },
313 &raqote::DrawOptions {
314 alpha: self.config.alpha,
315 ..Default::default()
316 },
317 );
318 offset = next_offset;
319 y += 1;
320 }
321 }
322
323 pub fn draw_pipeline(
324 &mut self,
325 x: f64,
326 y: f64,
327 width: f64,
328 height: f64,
329 pipeline: Box<dyn PipelineTrait>,
330 ) {
331 let mut render_target = RenderTarget::new(width as u32, height as u32);
332 pipeline.draw_pipeline(&mut render_target);
333 self.draw_render_target(&render_target, x, y);
334 }
335
336 pub fn clip(&mut self) {
338 self.clip_rect = Some(self.last_rect);
339 self.clip = true;
340 self.draw_target.push_clip(&self.path);
341 }
342
343 pub fn set_line_width(&mut self, line_width: f64) {
347 self.config.line_width = line_width;
348 }
349
350 pub fn set_alpha(&mut self, alpha: f32) {
352 self.config.alpha = alpha;
353 }
354
355 pub fn set_font_family(&mut self, family: impl Into<String>) {
357 self.config.font_config.family = family.into();
358 }
359
360 pub fn set_font_size(&mut self, size: f64) {
362 self.config.font_config.font_size = size + 4.0;
363 }
364
365 pub fn set_fill_style(&mut self, fill_style: Brush) {
369 self.config.fill_style = fill_style;
370 }
371
372 pub fn set_stroke_style(&mut self, stroke_style: Brush) {
374 self.config.stroke_style = stroke_style;
375 }
376
377 pub fn set_transform(
381 &mut self,
382 h_scaling: f64,
383 h_skewing: f64,
384 v_skewing: f64,
385 v_scaling: f64,
386 h_moving: f64,
387 v_moving: f64,
388 ) {
389 self.draw_target
390 .set_transform(&raqote::Transform::row_major(
391 h_scaling as f32,
392 h_skewing as f32,
393 v_skewing as f32,
394 v_scaling as f32,
395 h_moving as f32,
396 v_moving as f32,
397 ));
398 }
399
400 pub fn save(&mut self) {
404 self.saved_config = Some(self.config.clone());
405 }
406
407 pub fn restore(&mut self) {
410 self.clip = false;
411 self.clip_rect = None;
412 self.draw_target.pop_clip();
413 if let Some(config) = &self.saved_config {
414 self.config = config.clone();
415 }
416
417 self.saved_config = None;
418 }
419
420 pub fn clear(&mut self, brush: &Brush) {
421 let solid = match *brush {
422 Brush::SolidColor(color) => raqote::SolidSource {
423 r: color.r(),
424 g: color.g(),
425 b: color.b(),
426 a: color.a(),
427 },
428
429 _ => raqote::SolidSource {
430 r: 0x0,
431 g: 0x0,
432 b: 0x80,
433 a: 0x80,
434 },
435 };
436
437 self.draw_target.clear(solid);
438 }
439
440 pub fn data(&self) -> &[u32] {
441 self.draw_target.get_data()
442 }
443
444 pub fn data_mut(&mut self) -> &mut [u32] {
445 self.draw_target.get_data_mut()
446 }
447
448 pub fn data_u8_mut(&mut self) -> &mut [u8] {
449 self.draw_target.get_data_u8_mut()
450 }
451
452 pub fn start(&mut self) {
453 self.clear(&Brush::from(self.background));
454 }
455 pub fn finish(&mut self) {}
456}
457
458fn brush_to_source<'a>(brush: &Brush) -> raqote::Source<'a> {
459 match brush {
460 Brush::SolidColor(color) => raqote::Source::Solid(raqote::SolidSource {
461 r: color.r(),
462 g: color.g(),
463 b: color.b(),
464 a: color.a(),
465 }),
466 Brush::LinearGradient { start, end, stops } => {
467 let g_stops = stops
468 .iter()
469 .map(|stop| raqote::GradientStop {
470 position: stop.position as f32,
471 color: raqote::Color::new(
472 stop.color.a(),
473 stop.color.r(),
474 stop.color.g(),
475 stop.color.b(),
476 ),
477 })
478 .collect();
479
480 raqote::Source::new_linear_gradient(
481 raqote::Gradient { stops: g_stops },
482 raqote::Point::new(start.x() as f32, start.y() as f32),
483 raqote::Point::new(end.x() as f32, start.y() as f32),
484 raqote::Spread::Pad,
485 )
486 }
487 }
488}