1use std::{
2 sync::{mpsc, Arc, Mutex},
3 thread,
4};
5
6use crate::{platform, utils::*, PipelineTrait, RenderTarget, TextMetrics};
7use platform::Image;
8
9#[derive(Clone)]
10struct PipelineWrapper(pub Box<dyn PipelineTrait>);
11
12impl PartialEq for PipelineWrapper {
13 fn eq(&self, _: &Self) -> bool {
14 true
15 }
16}
17
18#[derive(Clone, PartialEq)]
20enum RenderTask {
21 Start(),
23 SetBackground(Color),
24 Resize {
25 width: f64,
26 height: f64,
27 },
28 RegisterFont {
29 family: String,
30 font_file: &'static [u8],
31 },
32
33 FillRect {
35 x: f64,
36 y: f64,
37 width: f64,
38 height: f64,
39 },
40 StrokeRect {
41 x: f64,
42 y: f64,
43 width: f64,
44 height: f64,
45 },
46 FillText {
47 text: String,
48 x: f64,
49 y: f64,
50 },
51 Fill(),
52 Stroke(),
53 BeginPath(),
54 ClosePath(),
55 Rectangle {
56 x: f64,
57 y: f64,
58 width: f64,
59 height: f64,
60 },
61 Arc {
62 x: f64,
63 y: f64,
64 radius: f64,
65 start_angle: f64,
66 end_angle: f64,
67 },
68 MoveTo {
69 x: f64,
70 y: f64,
71 },
72 LineTo {
73 x: f64,
74 y: f64,
75 },
76 QuadraticCurveTo {
77 cpx: f64,
78 cpy: f64,
79 x: f64,
80 y: f64,
81 },
82 BesierCurveTo {
83 cp1x: f64,
84 cp1y: f64,
85 cp2x: f64,
86 cp2y: f64,
87 x: f64,
88 y: f64,
89 },
90 DrawRenderTarget {
91 render_target: RenderTarget,
92 x: f64,
93 y: f64,
94 },
95 DrawImage {
96 image: Image,
97 x: f64,
98 y: f64,
99 },
100 DrawImageWithClip {
101 image: Image,
102 clip: Rectangle,
103 x: f64,
104 y: f64,
105 },
106 DrawPipeline {
107 x: f64,
108 y: f64,
109 width: f64,
110 height: f64,
111 pipeline: PipelineWrapper,
112 },
113 Clip(),
114 SetLineWidth {
115 line_width: f64,
116 },
117 SetAlpha {
118 alpha: f32,
119 },
120 SetFontFamily {
121 family: String,
122 },
123 SetFontSize {
124 size: f64,
125 },
126 SetFillStyle {
127 fill_style: Brush,
128 },
129 SetStrokeStyle {
130 stroke_style: Brush,
131 },
132 Save(),
133 Restore(),
134 Clear {
135 brush: Brush,
136 },
137 SetTransform {
138 h_scaling: f64,
139 h_skewing: f64,
140 v_skewing: f64,
141 v_scaling: f64,
142 h_moving: f64,
143 v_moving: f64,
144 },
145 Finish(),
146 Terminate(),
147}
148
149enum RenderResult {
151 Finish { data: Vec<u32> },
152}
153
154struct RenderWorker {
156 render_thread: Option<thread::JoinHandle<()>>,
157}
158
159fn is_single_tasks(task: &RenderTask) -> bool {
160 match task {
161 RenderTask::Start() => true,
162 RenderTask::SetBackground(_) => true,
163 RenderTask::Resize { .. } => true,
164 RenderTask::RegisterFont { .. } => true,
165 RenderTask::DrawRenderTarget { .. } => true,
166 RenderTask::DrawImage { .. } => true,
167 RenderTask::DrawImageWithClip { .. } => true,
168 RenderTask::DrawPipeline { .. } => true,
169 RenderTask::SetTransform { .. } => true,
170 RenderTask::Terminate { .. } => true,
171 _ => false,
172 }
173}
174
175impl RenderWorker {
176 fn new(
177 width: f64,
178 height: f64,
179 finish_sender: mpsc::Sender<bool>,
180 receiver: Arc<Mutex<mpsc::Receiver<Vec<RenderTask>>>>,
181 sender: Arc<Mutex<mpsc::Sender<RenderResult>>>,
182 ) -> Self {
183 let render_thread = thread::spawn(move || {
184 let mut tasks_collection = vec![];
185
186 let mut render_context_2_d = platform::RenderContext2D::new(width, height);
187
188 loop {
189 let mut tasks = receiver.lock().unwrap().recv().unwrap();
190
191 if tasks.len() == 1 && is_single_tasks(tasks.get(0).unwrap()) {
193 match tasks.remove(0) {
194 RenderTask::Start() => {
195 tasks_collection.clear();
196 render_context_2_d.start();
197 continue;
198 }
199 RenderTask::SetBackground(background) => {
200 render_context_2_d.set_background(background);
201 continue;
202 }
203 RenderTask::Resize { width, height } => {
204 render_context_2_d.resize(width, height);
205 continue;
206 }
207 RenderTask::RegisterFont { family, font_file } => {
208 render_context_2_d.register_font(family.as_str(), font_file);
209 continue;
210 }
211 RenderTask::DrawRenderTarget {
212 render_target,
213 x,
214 y,
215 } => {
216 render_context_2_d.draw_render_target(&render_target, x, y);
217 }
218 RenderTask::DrawImage { image, x, y } => {
219 render_context_2_d.draw_image(&image, x, y);
220 }
221 RenderTask::DrawImageWithClip { image, clip, x, y } => {
222 render_context_2_d.draw_image_with_clip(&image, clip, x, y);
223 }
224 RenderTask::DrawPipeline {
225 x,
226 y,
227 width,
228 height,
229 pipeline,
230 } => {
231 render_context_2_d.draw_pipeline(x, y, width, height, pipeline.0);
232 }
233 RenderTask::SetTransform {
234 h_scaling,
235 h_skewing,
236 v_skewing,
237 v_scaling,
238 h_moving,
239 v_moving,
240 } => {
241 render_context_2_d.set_transform(
242 h_scaling, h_skewing, v_skewing, v_scaling, h_moving, v_moving,
243 );
244 }
245 RenderTask::Terminate() => {
246 return;
247 }
248 _ => {}
249 };
250 }
251
252 tasks_collection.push(tasks);
253
254 if !tasks_collection.is_empty() {
255 for task in tasks_collection.remove(0) {
256 match task {
257 RenderTask::FillRect {
258 x,
259 y,
260 width,
261 height,
262 } => {
263 render_context_2_d.fill_rect(x, y, width, height);
264 }
265 RenderTask::StrokeRect {
266 x,
267 y,
268 width,
269 height,
270 } => {
271 render_context_2_d.stroke_rect(x, y, width, height);
272 }
273 RenderTask::FillText { text, x, y } => {
274 render_context_2_d.fill_text(text.as_str(), x, y);
275 }
276 RenderTask::Fill() => {
277 render_context_2_d.fill();
278 }
279 RenderTask::Stroke() => {
280 render_context_2_d.stroke();
281 }
282 RenderTask::BeginPath() => {
283 render_context_2_d.begin_path();
284 }
285 RenderTask::ClosePath() => {
286 render_context_2_d.close_path();
287 }
288 RenderTask::Rectangle {
289 x,
290 y,
291 width,
292 height,
293 } => {
294 render_context_2_d.rect(x, y, width, height);
295 }
296 RenderTask::Arc {
297 x,
298 y,
299 radius,
300 start_angle,
301 end_angle,
302 } => {
303 render_context_2_d.arc(x, y, radius, start_angle, end_angle);
304 }
305 RenderTask::MoveTo { x, y } => {
306 render_context_2_d.move_to(x, y);
307 }
308 RenderTask::LineTo { x, y } => {
309 render_context_2_d.line_to(x, y);
310 }
311 RenderTask::QuadraticCurveTo { cpx, cpy, x, y } => {
312 render_context_2_d.quadratic_curve_to(cpx, cpy, x, y);
313 }
314 RenderTask::BesierCurveTo {
315 cp1x,
316 cp1y,
317 cp2x,
318 cp2y,
319 x,
320 y,
321 } => {
322 render_context_2_d.bezier_curve_to(cp1x, cp1y, cp2x, cp2y, x, y);
323 }
324 RenderTask::SetLineWidth { line_width } => {
325 render_context_2_d.set_line_width(line_width);
326 }
327 RenderTask::SetAlpha { alpha } => {
328 render_context_2_d.set_alpha(alpha);
329 }
330 RenderTask::Clip() => {
331 render_context_2_d.clip();
332 }
333 RenderTask::SetFontFamily { family } => {
334 render_context_2_d.set_font_family(family);
335 }
336 RenderTask::SetFontSize { size } => {
337 render_context_2_d.set_font_size(size);
338 }
339 RenderTask::SetFillStyle { fill_style } => {
340 render_context_2_d.set_fill_style(fill_style);
341 }
342 RenderTask::SetStrokeStyle { stroke_style } => {
343 render_context_2_d.set_stroke_style(stroke_style);
344 }
345 RenderTask::Save() => {
346 render_context_2_d.save();
347 }
348 RenderTask::Restore() => {
349 render_context_2_d.restore();
350 }
351 RenderTask::Clear { brush } => {
352 render_context_2_d.clear(&brush);
353 }
354 RenderTask::Finish() => {
355 sender
356 .lock()
357 .unwrap()
358 .send(RenderResult::Finish {
359 data: render_context_2_d.data().iter().copied().collect(),
360 })
361 .expect("Could not send render result to main thread.");
362 finish_sender
363 .send(true)
364 .expect("Could not send render result to main thread.");
365 }
366 _ => {}
367 };
368 }
369 }
370 }
371 });
372
373 RenderWorker {
374 render_thread: Some(render_thread),
375 }
376 }
377}
378
379pub struct RenderContext2D {
381 output: Vec<u32>,
382 worker: RenderWorker,
383 sender: mpsc::Sender<Vec<RenderTask>>,
384 result_receiver: mpsc::Receiver<RenderResult>,
385 finish_receiver: mpsc::Receiver<bool>,
386 tasks: Vec<RenderTask>,
387 measure_context: platform::RenderContext2D,
388}
389
390impl Drop for RenderContext2D {
391 fn drop(&mut self) {
392 self.sender
393 .send(vec![RenderTask::Terminate()])
394 .expect("Could not send terminate to render thread.");
395 if let Some(thread) = self.worker.render_thread.take() {
396 thread.join().unwrap();
397 }
398 }
399}
400
401impl RenderContext2D {
402 pub fn new(width: f64, height: f64) -> Self {
404 let (sender, receiver) = mpsc::channel();
405
406 let (finish_sender, finish_receiver) = mpsc::channel();
407 let (result_sender, result_receiver) = mpsc::channel();
408
409 let receiver = Arc::new(Mutex::new(receiver));
410 let result_sender = Arc::new(Mutex::new(result_sender));
411
412 let worker = RenderWorker::new(width, height, finish_sender, receiver, result_sender);
413
414 RenderContext2D {
415 output: vec![0; width as usize * height as usize],
416 worker,
417 sender,
418 result_receiver,
419 finish_receiver,
420 tasks: vec![],
421 measure_context: platform::RenderContext2D::new(width, height),
422 }
423 }
424
425 pub fn finish_receiver(&self) -> &mpsc::Receiver<bool> {
426 &self.finish_receiver
427 }
428
429 pub fn set_background(&mut self, background: Color) {
430 self.sender
431 .send(vec![RenderTask::SetBackground(background)])
432 .expect("Could not send set background to render thread.");
433 }
434
435 fn send_tasks(&mut self) {
437 if !self.tasks.is_empty() {
438 self.sender
439 .send(self.tasks.to_vec())
440 .expect("Could not send render task.");
441 self.tasks.clear();
442 }
443 }
444
445 pub fn start(&mut self) {
447 self.sender
448 .send(vec![RenderTask::Start()])
449 .expect("Could not send start to render thread.");
450 }
451
452 pub fn finish(&mut self) {
454 self.tasks.push(RenderTask::Finish());
455 self.send_tasks();
456 }
457
458 pub fn resize(&mut self, width: f64, height: f64) {
460 self.sender
461 .send(vec![RenderTask::Resize { width, height }])
462 .expect("Could not send resize to render thread.");
463 }
464
465 pub fn register_font(&mut self, family: &str, font_file: &'static [u8]) {
467 self.measure_context.register_font(family, font_file);
468 self.sender
469 .send(vec![RenderTask::RegisterFont {
470 family: family.to_string(),
471 font_file,
472 }])
473 .expect("Could not send register font to render thread.");
474 }
475
476 pub fn fill_rect(&mut self, x: f64, y: f64, width: f64, height: f64) {
481 self.tasks.push(RenderTask::FillRect {
482 x,
483 y,
484 width,
485 height,
486 });
487 }
488
489 pub fn stroke_rect(&mut self, x: f64, y: f64, width: f64, height: f64) {
491 self.tasks.push(RenderTask::StrokeRect {
492 x,
493 y,
494 width,
495 height,
496 });
497 }
498
499 pub fn fill_text(&mut self, text: &str, x: f64, y: f64) {
503 self.tasks.push(RenderTask::FillText {
504 text: text.to_string(),
505 x,
506 y,
507 });
508 }
509
510 pub fn measure(
511 &mut self,
512 text: &str,
513 font_size: f64,
514 family: impl Into<String>,
515 ) -> TextMetrics {
516 self.measure_context.set_font_family(family);
517 self.measure_context.set_font_size(font_size);
518 self.measure_text(text)
519 }
520
521 pub fn measure_text(&mut self, text: &str) -> TextMetrics {
523 self.measure_context.measure_text(text)
524 }
525
526 pub fn fill(&mut self) {
528 self.tasks.push(RenderTask::Fill());
529 }
530
531 pub fn stroke(&mut self) {
533 self.tasks.push(RenderTask::Stroke());
534 }
535
536 pub fn begin_path(&mut self) {
538 self.send_tasks();
539 self.tasks.push(RenderTask::BeginPath());
540 }
541
542 pub fn close_path(&mut self) {
545 self.tasks.push(RenderTask::ClosePath());
546 self.send_tasks();
547 }
548 pub fn rect(&mut self, x: f64, y: f64, width: f64, height: f64) {
550 self.tasks.push(RenderTask::Rectangle {
551 x,
552 y,
553 width,
554 height,
555 });
556 }
557
558 pub fn arc(&mut self, x: f64, y: f64, radius: f64, start_angle: f64, end_angle: f64) {
561 self.tasks.push(RenderTask::Arc {
562 x,
563 y,
564 radius,
565 start_angle,
566 end_angle,
567 });
568 }
569
570 pub fn move_to(&mut self, x: f64, y: f64) {
573 self.tasks.push(RenderTask::MoveTo { x, y });
574 }
575
576 pub fn line_to(&mut self, x: f64, y: f64) {
579 self.tasks.push(RenderTask::LineTo { x, y });
580 }
581
582 pub fn quadratic_curve_to(&mut self, cpx: f64, cpy: f64, x: f64, y: f64) {
584 self.tasks
585 .push(RenderTask::QuadraticCurveTo { cpx, cpy, x, y });
586 }
587
588 pub fn bezier_curve_to(&mut self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) {
593 self.tasks.push(RenderTask::BesierCurveTo {
594 cp1x,
595 cp1y,
596 cp2x,
597 cp2y,
598 x,
599 y,
600 });
601 }
602
603 pub fn draw_render_target(&mut self, render_target: &RenderTarget, x: f64, y: f64) {
606 self.sender
607 .send(vec![RenderTask::DrawRenderTarget {
608 render_target: render_target.clone(),
609 x,
610 y,
611 }])
612 .expect("Could not send render target to render thread.");
613 }
614
615 pub fn draw_image(&mut self, image: &mut Image, x: f64, y: f64) {
617 self.sender
618 .send(vec![RenderTask::DrawImage {
619 image: image.clone(),
620 x,
621 y,
622 }])
623 .expect("Could not send image to render thread.");
624 }
625
626 pub fn draw_image_with_clip(&mut self, image: &mut Image, clip: Rectangle, x: f64, y: f64) {
628 self.sender
629 .send(vec![RenderTask::DrawImageWithClip {
630 image: image.clone(),
631 clip,
632 x,
633 y,
634 }])
635 .expect("Could not send clipped image to render thread.");
636 }
637
638 pub fn draw_pipeline(
639 &mut self,
640 x: f64,
641 y: f64,
642 width: f64,
643 height: f64,
644 pipeline: Box<dyn PipelineTrait>,
645 ) {
646 self.sender
647 .send(vec![RenderTask::DrawPipeline {
648 x,
649 y,
650 width,
651 height,
652 pipeline: PipelineWrapper(pipeline),
653 }])
654 .expect("Could not send draw_pipeline to render thread.");
655 }
656
657 pub fn clip(&mut self) {
660 self.tasks.push(RenderTask::Clip());
661 }
662
663 pub fn set_line_width(&mut self, line_width: f64) {
667 self.tasks.push(RenderTask::SetLineWidth { line_width });
668 }
669
670 pub fn set_alpha(&mut self, alpha: f32) {
672 self.tasks.push(RenderTask::SetAlpha { alpha });
673 }
674
675 pub fn set_font_family(&mut self, family: impl Into<String>) {
677 let family = family.into();
678 self.tasks.push(RenderTask::SetFontFamily { family });
679 }
680
681 pub fn set_font_size(&mut self, size: f64) {
683 self.tasks.push(RenderTask::SetFontSize { size });
684 }
685
686 pub fn set_fill_style(&mut self, fill_style: Brush) {
690 self.tasks.push(RenderTask::SetFillStyle { fill_style });
691 }
692
693 pub fn set_stroke_style(&mut self, stroke_style: Brush) {
695 self.tasks.push(RenderTask::SetStrokeStyle { stroke_style });
696 }
697
698 pub fn set_transform(
702 &mut self,
703 h_scaling: f64,
704 h_skewing: f64,
705 v_skewing: f64,
706 v_scaling: f64,
707 h_moving: f64,
708 v_moving: f64,
709 ) {
710 self.tasks.push(RenderTask::SetTransform {
711 h_scaling,
712 h_skewing,
713 v_skewing,
714 v_scaling,
715 h_moving,
716 v_moving,
717 });
718 }
719
720 pub fn save(&mut self) {
724 self.tasks.push(RenderTask::Save());
725 }
726
727 pub fn restore(&mut self) {
730 self.tasks.push(RenderTask::Restore());
731 }
732
733 pub fn clear(&mut self, brush: &Brush) {
734 let brush = brush.clone();
735 self.tasks.push(RenderTask::Clear { brush });
736 }
737
738 pub fn data(&mut self) -> Option<&[u32]> {
739 if let Ok(RenderResult::Finish { data }) = self.result_receiver.try_recv() {
740 self.output = data;
741 Some(&self.output)
742 } else {
743 None
744 }
745 }
746
747 pub fn data_mut(&mut self) -> &mut [u32] {
748 &mut self.output
749 }
750
751 pub fn data_u8_mut(&mut self) -> &mut [u8] {
752 let p = self.output[..].as_mut_ptr();
753 let len = self.output[..].len();
754 unsafe { std::slice::from_raw_parts_mut(p as *mut u8, len * std::mem::size_of::<u32>()) }
757 }
758}