1pub use glow;
24pub use piet_hardware::piet;
25
26use glow::HasContext;
27
28use piet::{kurbo, Error as Pierror, IntoBrush};
29use piet_hardware::gpu_types::{AreaCapture, BufferPush, SubtextureWrite, TextureWrite};
30
31use std::borrow::Cow;
32use std::cell::Cell;
33use std::fmt;
34use std::mem;
35use std::rc::Rc;
36
37macro_rules! c {
38 ($e:expr) => {{
39 ($e) as f32
40 }};
41}
42
43const VERTEX_SHADER: &str = include_str!("./shaders/glow.v.glsl");
44const FRAGMENT_SHADER: &str = include_str!("./shaders/glow.f.glsl");
45
46#[derive(Debug, Clone, Copy)]
47enum Uniforms {
48 Transform = 0,
49 ViewportSize = 1,
50 ImageTexture = 2,
51 MaskTexture = 3,
52}
53
54impl Uniforms {
55 fn as_index(self) -> usize {
56 self as usize
57 }
58
59 fn as_name(self) -> &'static str {
60 match self {
61 Uniforms::Transform => "uTransform",
62 Uniforms::ViewportSize => "uViewportSize",
63 Uniforms::ImageTexture => "uImage",
64 Uniforms::MaskTexture => "uMask",
65 }
66 }
67}
68
69const UNIFORM_COUNT: usize = 4;
70const UNIFORMS: [Uniforms; UNIFORM_COUNT] = [
71 Uniforms::Transform,
72 Uniforms::ViewportSize,
73 Uniforms::ImageTexture,
74 Uniforms::MaskTexture,
75];
76
77use Uniforms::*;
78
79struct GpuContext<H: HasContext + ?Sized> {
81 render_program: H::Program,
83
84 uniforms: Box<[H::UniformLocation]>,
86
87 check_indices: bool,
89
90 context: Rc<H>,
92}
93
94impl<H: HasContext + ?Sized> fmt::Debug for GpuContext<H> {
95 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
96 f.debug_struct("GpuContext")
97 .field("robust", &!self.check_indices)
98 .finish_non_exhaustive()
99 }
100}
101
102impl<H: HasContext + ?Sized> GpuContext<H> {
103 fn uniform(&self, uniform: Uniforms) -> &H::UniformLocation {
104 self.uniforms.get(uniform.as_index()).unwrap()
105 }
106}
107
108impl<H: HasContext + ?Sized> Drop for GpuContext<H> {
109 fn drop(&mut self) {
110 unsafe {
111 self.context.delete_program(self.render_program);
112 }
113 }
114}
115
116struct GlTexture<H: HasContext + ?Sized> {
118 context: Rc<H>,
120
121 texture: H::Texture,
123}
124
125impl<H: HasContext + ?Sized> Drop for GlTexture<H> {
126 fn drop(&mut self) {
127 unsafe {
128 self.context.delete_texture(self.texture);
129 }
130 }
131}
132
133struct GlVertexBuffer<H: HasContext + ?Sized> {
135 context: Rc<H>,
137
138 vbo: H::Buffer,
140
141 ebo: H::Buffer,
143
144 vao: H::VertexArray,
146
147 num_indices: Cell<usize>,
149}
150
151impl<H: HasContext + ?Sized> Drop for GlVertexBuffer<H> {
152 fn drop(&mut self) {
153 unsafe {
154 self.context.delete_buffer(self.vbo);
155 self.context.delete_buffer(self.ebo);
156 self.context.delete_vertex_array(self.vao);
157 }
158 }
159}
160
161#[derive(Debug)]
162struct GlError(String);
163
164impl From<String> for GlError {
165 fn from(s: String) -> Self {
166 GlError(s)
167 }
168}
169
170impl fmt::Display for GlError {
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 write!(f, "gl error: {}", self.0)
173 }
174}
175
176impl std::error::Error for GlError {}
177
178impl<H: HasContext + ?Sized> piet_hardware::GpuContext for GpuContext<H> {
179 type Device = ();
180 type Queue = ();
181 type Texture = GlTexture<H>;
182 type VertexBuffer = GlVertexBuffer<H>;
183 type Error = GlError;
184
185 fn clear(&mut self, _: &(), _: &(), color: piet_hardware::piet::Color) {
186 let (r, g, b, a) = color.as_rgba();
187
188 unsafe {
189 self.context.disable(glow::SCISSOR_TEST);
190 self.context.clear_color(c!(r), c!(g), c!(b), c!(a));
191 self.context.clear(glow::COLOR_BUFFER_BIT);
192 }
193 }
194
195 fn flush(&mut self) -> Result<(), Self::Error> {
196 unsafe {
197 self.context.flush();
198 }
199
200 Ok(())
201 }
202
203 fn create_texture(
204 &mut self,
205 _: &(),
206 interpolation: piet_hardware::piet::InterpolationMode,
207 repeat: piet_hardware::RepeatStrategy,
208 ) -> Result<Self::Texture, Self::Error> {
209 unsafe {
210 let texture = self.context.create_texture().gl_err()?;
211
212 self.context.bind_texture(glow::TEXTURE_2D, Some(texture));
214 let _guard = CallOnDrop(|| {
215 self.context.bind_texture(glow::TEXTURE_2D, None);
216 });
217
218 let (min_filter, mag_filter) = match interpolation {
219 piet::InterpolationMode::NearestNeighbor => (glow::NEAREST, glow::NEAREST),
220 piet::InterpolationMode::Bilinear => (glow::LINEAR, glow::LINEAR),
221 };
222
223 self.context.tex_parameter_i32(
224 glow::TEXTURE_2D,
225 glow::TEXTURE_MIN_FILTER,
226 min_filter as i32,
227 );
228 self.context.tex_parameter_i32(
229 glow::TEXTURE_2D,
230 glow::TEXTURE_MAG_FILTER,
231 mag_filter as i32,
232 );
233
234 let (wrap_s, wrap_t) = match repeat {
235 piet_hardware::RepeatStrategy::Color(_color) => {
236 #[cfg(not(any(target_arch = "wasm32", target_arch = "wasm32")))]
237 {
238 let (r, g, b, a) = _color.as_rgba();
239 self.context.tex_parameter_f32_slice(
240 glow::TEXTURE_2D,
241 glow::TEXTURE_BORDER_COLOR,
242 &[c!(r), c!(g), c!(b), c!(a)],
243 );
244 }
245
246 (glow::CLAMP_TO_BORDER, glow::CLAMP_TO_BORDER)
247 }
248 piet_hardware::RepeatStrategy::Repeat => (glow::REPEAT, glow::REPEAT),
249 piet_hardware::RepeatStrategy::Clamp => (glow::CLAMP_TO_EDGE, glow::CLAMP_TO_EDGE),
250 _ => panic!("unsupported repeat strategy: {repeat:?}"),
251 };
252
253 self.context
254 .tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_S, wrap_s as i32);
255 self.context
256 .tex_parameter_i32(glow::TEXTURE_2D, glow::TEXTURE_WRAP_T, wrap_t as i32);
257
258 gl_error(&*self.context);
259
260 Ok(GlTexture {
261 context: self.context.clone(),
262 texture,
263 })
264 }
265 }
266
267 fn write_texture(
268 &mut self,
269 TextureWrite {
270 device: (),
271 queue: (),
272 texture,
273 size: (width, height),
274 format,
275 data,
276 }: TextureWrite<'_, Self>,
277 ) {
278 let data_width = match format {
279 piet::ImageFormat::Grayscale => 1,
280 piet::ImageFormat::Rgb => 3,
281 piet::ImageFormat::RgbaSeparate | piet::ImageFormat::RgbaPremul => 4,
282 _ => panic!("unsupported image format: {format:?}"),
283 };
284
285 if let Some(data) = data {
286 let total_len = usize::try_from(width)
287 .ok()
288 .and_then(|width| width.checked_mul(height.try_into().ok()?))
289 .and_then(|total| total.checked_mul(data_width.try_into().ok()?))
290 .expect("image data too large");
291 assert_eq!(data.len(), total_len);
292 }
293
294 let grayscale_data;
295 let mut data = data;
296
297 unsafe {
298 self.context
299 .bind_texture(glow::TEXTURE_2D, Some(texture.texture));
300 let _guard = CallOnDrop(|| {
301 self.context.bind_texture(glow::TEXTURE_2D, None);
302 });
303
304 let (internal_format, format, data_type) = match format {
305 piet::ImageFormat::Grayscale => {
306 grayscale_data =
308 data.map(|data| data.iter().flat_map(|&v| [v, v, v]).collect::<Vec<_>>());
309 data = grayscale_data.as_deref();
310
311 (glow::RGB8, glow::RGB, glow::UNSIGNED_BYTE)
312 }
313 piet::ImageFormat::Rgb => (glow::RGB8, glow::RGB, glow::UNSIGNED_BYTE),
314 piet::ImageFormat::RgbaPremul => (glow::RGBA8, glow::RGBA, glow::UNSIGNED_BYTE),
315 piet::ImageFormat::RgbaSeparate => (glow::RGBA8, glow::RGBA, glow::UNSIGNED_BYTE),
316 _ => panic!("unsupported image format: {format:?}"),
317 };
318
319 self.context.pixel_store_i32(glow::UNPACK_ALIGNMENT, 1);
321
322 self.context.tex_image_2d(
323 glow::TEXTURE_2D,
324 0,
325 internal_format as i32,
326 width as i32,
327 height as i32,
328 0,
329 format,
330 data_type,
331 data,
332 );
333 }
334
335 gl_error(&*self.context);
336 }
337
338 fn write_subtexture(
339 &mut self,
340 SubtextureWrite {
341 device: (),
342 queue: (),
343 texture,
344 offset: (x, y),
345 size: (width, height),
346 format,
347 data,
348 }: SubtextureWrite<'_, Self>,
349 ) {
350 let data_width = match format {
351 piet::ImageFormat::Grayscale => 1,
352 piet::ImageFormat::Rgb => 3,
353 piet::ImageFormat::RgbaSeparate | piet::ImageFormat::RgbaPremul => 4,
354 _ => panic!("unsupported image format: {format:?}"),
355 };
356
357 let total_len = (width * height * data_width) as usize;
358 assert_eq!(data.len(), total_len);
359
360 unsafe {
361 self.context
362 .bind_texture(glow::TEXTURE_2D, Some(texture.texture));
363 let _guard = CallOnDrop(|| {
364 self.context.bind_texture(glow::TEXTURE_2D, None);
365 });
366
367 let (format, data_type) = match format {
368 piet::ImageFormat::Grayscale => (glow::RED, glow::UNSIGNED_BYTE),
369 piet::ImageFormat::Rgb => (glow::RGB, glow::UNSIGNED_BYTE),
370 piet::ImageFormat::RgbaPremul => (glow::RGBA, glow::UNSIGNED_BYTE),
371 piet::ImageFormat::RgbaSeparate => (glow::RGBA, glow::UNSIGNED_BYTE),
372 _ => panic!("unsupported image format: {format:?}"),
373 };
374
375 self.context.tex_sub_image_2d(
376 glow::TEXTURE_2D,
377 0,
378 x as i32,
379 y as i32,
380 width as i32,
381 height as i32,
382 format,
383 data_type,
384 glow::PixelUnpackData::Slice(data),
385 );
386 }
387
388 gl_error(&*self.context);
389 }
390
391 fn set_texture_interpolation(
392 &mut self,
393 _: &(),
394 texture: &Self::Texture,
395 interpolation: piet_hardware::piet::InterpolationMode,
396 ) {
397 unsafe {
398 self.context
399 .bind_texture(glow::TEXTURE_2D, Some(texture.texture));
400 let _guard = CallOnDrop(|| {
401 self.context.bind_texture(glow::TEXTURE_2D, None);
402 });
403
404 let (min_filter, mag_filter) = match interpolation {
405 piet::InterpolationMode::NearestNeighbor => (glow::NEAREST, glow::NEAREST),
406 piet::InterpolationMode::Bilinear => (glow::LINEAR, glow::LINEAR),
407 };
408
409 self.context.tex_parameter_i32(
410 glow::TEXTURE_2D,
411 glow::TEXTURE_MIN_FILTER,
412 min_filter as i32,
413 );
414 self.context.tex_parameter_i32(
415 glow::TEXTURE_2D,
416 glow::TEXTURE_MAG_FILTER,
417 mag_filter as i32,
418 );
419 }
420 }
421
422 fn capture_area(
423 &mut self,
424 AreaCapture {
425 device: (),
426 queue: (),
427 texture,
428 offset,
429 size,
430 bitmap_scale: scale,
431 }: AreaCapture<'_, Self>,
432 ) -> Result<(), Self::Error> {
433 let (x, y) = offset;
434 let (width, height) = size;
435
436 let (x, y, width, height) = (
438 (x as f64 * scale).floor() as u32,
439 (y as f64 * scale).floor() as u32,
440 (width as f64 * scale).ceil() as u32,
441 (height as f64 * scale).ceil() as u32,
442 );
443
444 let buffer_size = (width * (height + 1) * 4) as usize;
447 let mut pixels = vec![0u8; buffer_size];
448 let scratch_start = pixels.len() - (width * 4) as usize;
449
450 unsafe {
452 self.context
453 .bind_texture(glow::TEXTURE_2D, Some(texture.texture));
454 let _guard = CallOnDrop(|| {
455 self.context.bind_texture(glow::TEXTURE_2D, None);
456 });
457
458 self.context.pixel_store_i32(glow::PACK_ALIGNMENT, 1);
459 self.context.read_pixels(
460 x as i32,
461 y as i32,
462 width as i32,
463 height as i32,
464 glow::RGBA,
465 glow::UNSIGNED_BYTE,
466 glow::PixelPackData::Slice(&mut pixels),
467 );
468 }
469
470 if unsafe { self.context.get_error() } != glow::NO_ERROR {
472 gl_error(&*self.context);
474 return Err(GlError("failed to read pixels".into()));
475 }
476
477 let stride = width as usize * 4;
480 for row in 0..(height / 2) as usize {
481 let top = row * stride;
482 let bottom = (height as usize - row - 1) * stride;
483
484 pixels.copy_within(top..top + stride, scratch_start);
485 pixels.copy_within(bottom..bottom + stride, top);
486 pixels.copy_within(scratch_start..scratch_start + stride, bottom);
487 }
488
489 unsafe {
491 self.context
492 .bind_texture(glow::TEXTURE_2D, Some(texture.texture));
493 let _guard = CallOnDrop(|| {
494 self.context.bind_texture(glow::TEXTURE_2D, None);
495 });
496
497 self.context.tex_image_2d(
498 glow::TEXTURE_2D,
499 0,
500 glow::RGBA8 as _,
501 width as i32,
502 height as i32,
503 0,
504 glow::RGBA,
505 glow::UNSIGNED_BYTE,
506 Some(&pixels),
507 );
508 }
509
510 Ok(())
511 }
512
513 fn max_texture_size(&mut self, _: &()) -> (u32, u32) {
514 unsafe {
515 let size = self.context.get_parameter_i32(glow::MAX_TEXTURE_SIZE);
516 (size as u32, size as u32)
517 }
518 }
519
520 fn create_vertex_buffer(&mut self, _: &()) -> Result<Self::VertexBuffer, Self::Error> {
521 use piet_hardware::Vertex;
522
523 unsafe {
524 let vbo = self.context.create_buffer().gl_err()?;
525 let ebo = self.context.create_buffer().gl_err()?;
526 let vao = self.context.create_vertex_array().gl_err()?;
527
528 self.context.bind_vertex_array(Some(vao));
530 let _guard = CallOnDrop(|| {
531 self.context.bind_vertex_array(None);
532 });
533 self.context.bind_buffer(glow::ARRAY_BUFFER, Some(vbo));
534 self.context
535 .bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(ebo));
536
537 let vertex_attributes = [
539 (
540 "aPosition",
541 2,
542 glow::FLOAT,
543 bytemuck::offset_of!(Vertex, pos),
544 ),
545 ("aUv", 2, glow::FLOAT, bytemuck::offset_of!(Vertex, uv)),
546 (
547 "aColor",
548 4,
549 glow::UNSIGNED_BYTE,
550 bytemuck::offset_of!(Vertex, color),
551 ),
552 ];
553
554 let stride = std::mem::size_of::<Vertex>() as i32;
555 for (name, size, data_type, offset) in vertex_attributes {
556 let location = self
557 .context
558 .get_attrib_location(self.render_program, name)
559 .ok_or_else(|| {
560 GlError(format!("failed to get attribute location for {name}"))
561 })?;
562
563 self.context.enable_vertex_attrib_array(location);
564 self.context.vertex_attrib_pointer_f32(
565 location,
566 size,
567 data_type,
568 false,
569 stride,
570 offset as i32,
571 );
572 }
573
574 gl_error(&*self.context);
575
576 Ok(GlVertexBuffer {
577 context: self.context.clone(),
578 vbo,
579 ebo,
580 vao,
581 num_indices: Cell::new(0),
582 })
583 }
584 }
585
586 fn write_vertices(
587 &mut self,
588 _: &(),
589 _: &(),
590 buffer: &Self::VertexBuffer,
591 vertices: &[piet_hardware::Vertex],
592 indices: &[u32],
593 ) {
594 if self.check_indices {
596 assert!(indices.iter().all(|&i| i < vertices.len() as u32));
597 } else {
598 debug_assert!(indices.iter().all(|&i| i < vertices.len() as u32));
599 }
600
601 unsafe {
602 self.context.bind_vertex_array(Some(buffer.vao));
603 let _guard = CallOnDrop(|| {
604 self.context.bind_vertex_array(None);
605 });
606
607 self.context.buffer_data_u8_slice(
608 glow::ARRAY_BUFFER,
609 bytemuck::cast_slice(vertices),
610 glow::DYNAMIC_DRAW,
611 );
612
613 self.context.buffer_data_u8_slice(
614 glow::ELEMENT_ARRAY_BUFFER,
615 bytemuck::cast_slice(indices),
616 glow::DYNAMIC_DRAW,
617 );
618
619 gl_error(&*self.context);
620
621 buffer.num_indices.set(indices.len());
622 }
623 }
624
625 fn push_buffers(
626 &mut self,
627 BufferPush {
628 device: (),
629 queue: (),
630 vertex_buffer,
631 current_texture,
632 mask_texture,
633 transform,
634 viewport_size: size,
635 clip,
636 }: BufferPush<'_, Self>,
637 ) -> Result<(), Self::Error> {
638 unsafe {
639 self.context.use_program(Some(self.render_program));
641 let _unbind_program = CallOnDrop(|| {
642 self.context.use_program(None);
643 });
644
645 self.context.viewport(0, 0, size.0 as i32, size.1 as i32);
647 self.context.uniform_2_f32(
648 Some(self.uniform(ViewportSize)),
649 size.0 as f32,
650 size.1 as f32,
651 );
652
653 match clip {
655 Some(clip) => {
656 self.context.enable(glow::SCISSOR_TEST);
657 self.context.scissor(
658 clip.x0 as i32,
659 size.1 as i32 - clip.y1 as i32,
660 clip.width() as i32,
661 clip.height() as i32,
662 )
663 }
664 None => self.context.disable(glow::SCISSOR_TEST),
665 }
666
667 let [a, b, c, d, e, f] = transform.as_coeffs();
669 let transform = [
670 c!(a),
671 c!(b),
672 c!(0.0),
673 c!(c),
674 c!(d),
675 c!(0.0),
676 c!(e),
677 c!(f),
678 c!(1.0),
679 ];
680 self.context.uniform_matrix_3_f32_slice(
681 Some(self.uniform(Transform)),
682 false,
683 &transform,
684 );
685
686 self.context.active_texture(glow::TEXTURE1);
688 self.context
689 .bind_texture(glow::TEXTURE_2D, Some(current_texture.texture));
690 self.context
691 .uniform_1_i32(Some(self.uniform(ImageTexture)), 1);
692
693 self.context.active_texture(glow::TEXTURE0);
695 self.context
696 .bind_texture(glow::TEXTURE_2D, Some(mask_texture.texture));
697 self.context
698 .uniform_1_i32(Some(self.uniform(MaskTexture)), 0);
699
700 self.context.enable(glow::BLEND);
702 self.context
703 .blend_equation_separate(glow::FUNC_ADD, glow::FUNC_ADD);
704 self.context.blend_func_separate(
705 glow::SRC_ALPHA,
706 glow::ONE_MINUS_SRC_ALPHA,
707 glow::ONE,
708 glow::ONE_MINUS_SRC_ALPHA,
709 );
710 self.context.enable(glow::MULTISAMPLE);
711
712 self.context.bind_vertex_array(Some(vertex_buffer.vao));
714 let _unbind_vao = CallOnDrop(|| {
715 self.context.bind_vertex_array(None);
716 });
717
718 self.context.draw_elements(
720 glow::TRIANGLES,
721 vertex_buffer.num_indices.get() as i32,
722 glow::UNSIGNED_INT,
723 0,
724 );
725
726 gl_error(&*self.context);
727
728 Ok(())
729 }
730 }
731}
732
733#[derive(Debug)]
735pub struct GlContext<H: HasContext + ?Sized> {
736 text: Text,
737 source: piet_hardware::Source<GpuContext<H>>,
738}
739
740impl<H: HasContext + ?Sized> GlContext<H> {
741 pub unsafe fn new(context: H) -> Result<Self, Pierror>
748 where
749 H: Sized,
750 {
751 let version = context.version();
753
754 let has_supported_version = if version.is_embedded {
756 version.major >= 3
757 } else {
758 version.major >= 4 || (version.major >= 3 && version.minor >= 3)
759 };
760 if !has_supported_version {
761 return Err(Pierror::BackendError(
762 "OpenGL version 3.3 (or 3.0 ES) or higher is required".into(),
763 ));
764 }
765
766 let shader_header = if version.is_embedded {
767 "#version 300 es"
768 } else {
769 "#version 330 core"
770 };
771
772 let format_shader = |shader| format!("{shader_header}\n{shader}");
773
774 let program = compile_program(
776 &context,
777 &format_shader(VERTEX_SHADER),
778 &format_shader(FRAGMENT_SHADER),
779 )
780 .map_err(|e| Pierror::BackendError(e.into()))?;
781
782 let uniforms = UNIFORMS
784 .iter()
785 .map(|uniform| {
786 context
787 .get_uniform_location(program, uniform.as_name())
788 .ok_or_else(|| {
789 Pierror::BackendError(
790 format!("failed to get uniform location for {}", uniform.as_name())
791 .into(),
792 )
793 })
794 })
795 .collect::<Result<Box<[_]>, _>>()?;
796
797 let robust_buffer = context
798 .supported_extensions()
799 .contains("GL_ARB_robust_buffer_access_behavior")
800 || context
801 .supported_extensions()
802 .contains("GL_KHR_robust_buffer_access_behavior");
803
804 piet_hardware::Source::new(
805 GpuContext {
806 context: Rc::new(context),
807 uniforms,
808 check_indices: !robust_buffer,
809 render_program: program,
810 },
811 &(),
812 &(),
813 )
814 .map(|source| GlContext {
815 text: Text(source.text().clone()),
816 source,
817 })
818 }
819
820 pub fn context(&self) -> &H {
822 &self.source.context().context
823 }
824
825 pub unsafe fn render_context(&mut self, width: u32, height: u32) -> RenderContext<'_, H> {
832 RenderContext {
833 context: self.source.render_context(&(), &(), width, height),
834 text: &mut self.text,
835 }
836 }
837}
838
839pub struct RenderContext<'a, H: HasContext + ?Sized> {
841 context: piet_hardware::RenderContext<'a, 'static, 'static, GpuContext<H>>,
842 text: &'a mut Text,
843}
844
845impl<'a, H: HasContext + ?Sized> RenderContext<'a, H> {
846 #[inline]
848 pub fn tolerance(&self) -> f64 {
849 self.context.tolerance()
850 }
851
852 #[inline]
854 pub fn set_tolerance(&mut self, tolerance: f64) {
855 self.context.set_tolerance(tolerance)
856 }
857
858 #[inline]
860 pub fn bitmap_scale(&self) -> f64 {
861 self.context.bitmap_scale()
862 }
863
864 #[inline]
866 pub fn set_bitmap_scale(&mut self, scale: f64) {
867 self.context.set_bitmap_scale(scale)
868 }
869}
870
871impl<H: HasContext + ?Sized> piet::RenderContext for RenderContext<'_, H> {
872 type Brush = Brush<H>;
873
874 type Text = Text;
875
876 type TextLayout = TextLayout;
877
878 type Image = Image<H>;
879
880 fn status(&mut self) -> Result<(), Pierror> {
881 self.context.status()
882 }
883
884 fn solid_brush(&mut self, color: piet::Color) -> Self::Brush {
885 Brush(self.context.solid_brush(color))
886 }
887
888 fn gradient(
889 &mut self,
890 gradient: impl Into<piet::FixedGradient>,
891 ) -> Result<Self::Brush, Pierror> {
892 self.context.gradient(gradient).map(Brush)
893 }
894
895 fn clear(&mut self, region: impl Into<Option<kurbo::Rect>>, color: piet::Color) {
896 self.context.clear(region, color)
897 }
898
899 fn stroke(&mut self, shape: impl kurbo::Shape, brush: &impl IntoBrush<Self>, width: f64) {
900 let brush = brush.make_brush(self, || shape.bounding_box());
901 self.context.stroke(shape, &brush.as_ref().0, width)
902 }
903
904 fn stroke_styled(
905 &mut self,
906 shape: impl kurbo::Shape,
907 brush: &impl IntoBrush<Self>,
908 width: f64,
909 style: &piet::StrokeStyle,
910 ) {
911 let brush = brush.make_brush(self, || shape.bounding_box());
912 self.context
913 .stroke_styled(shape, &brush.as_ref().0, width, style)
914 }
915
916 fn fill(&mut self, shape: impl kurbo::Shape, brush: &impl IntoBrush<Self>) {
917 let brush = brush.make_brush(self, || shape.bounding_box());
918 self.context.fill(shape, &brush.as_ref().0)
919 }
920
921 fn fill_even_odd(&mut self, shape: impl kurbo::Shape, brush: &impl IntoBrush<Self>) {
922 let brush = brush.make_brush(self, || shape.bounding_box());
923 self.context.fill_even_odd(shape, &brush.as_ref().0)
924 }
925
926 fn clip(&mut self, shape: impl kurbo::Shape) {
927 self.context.clip(shape)
928 }
929
930 fn text(&mut self) -> &mut Self::Text {
931 self.text
932 }
933
934 fn draw_text(&mut self, layout: &Self::TextLayout, pos: impl Into<kurbo::Point>) {
935 self.context.draw_text(&layout.0, pos)
936 }
937
938 fn save(&mut self) -> Result<(), Pierror> {
939 self.context.save()
940 }
941
942 fn restore(&mut self) -> Result<(), Pierror> {
943 self.context.restore()
944 }
945
946 fn finish(&mut self) -> Result<(), Pierror> {
947 self.context.finish()?;
948
949 self.context.source_mut().gpu_flushed();
951
952 Ok(())
953 }
954
955 fn transform(&mut self, transform: kurbo::Affine) {
956 self.context.transform(transform)
957 }
958
959 fn make_image(
960 &mut self,
961 width: usize,
962 height: usize,
963 buf: &[u8],
964 format: piet::ImageFormat,
965 ) -> Result<Self::Image, Pierror> {
966 self.context
967 .make_image(width, height, buf, format)
968 .map(Image)
969 }
970
971 fn draw_image(
972 &mut self,
973 image: &Self::Image,
974 dst_rect: impl Into<kurbo::Rect>,
975 interp: piet::InterpolationMode,
976 ) {
977 self.context.draw_image(&image.0, dst_rect, interp)
978 }
979
980 fn draw_image_area(
981 &mut self,
982 image: &Self::Image,
983 src_rect: impl Into<kurbo::Rect>,
984 dst_rect: impl Into<kurbo::Rect>,
985 interp: piet::InterpolationMode,
986 ) {
987 self.context
988 .draw_image_area(&image.0, src_rect, dst_rect, interp)
989 }
990
991 fn capture_image_area(
992 &mut self,
993 src_rect: impl Into<kurbo::Rect>,
994 ) -> Result<Self::Image, Pierror> {
995 self.context.capture_image_area(src_rect).map(Image)
996 }
997
998 fn blurred_rect(&mut self, rect: kurbo::Rect, blur_radius: f64, brush: &impl IntoBrush<Self>) {
999 let brush = brush.make_brush(self, || rect);
1000 self.context
1001 .blurred_rect(rect, blur_radius, &brush.as_ref().0)
1002 }
1003
1004 fn current_transform(&self) -> kurbo::Affine {
1005 self.context.current_transform()
1006 }
1007}
1008
1009#[derive(Debug)]
1011pub struct Brush<H: HasContext + ?Sized>(piet_hardware::Brush<GpuContext<H>>);
1012
1013impl<H: HasContext + ?Sized> Clone for Brush<H> {
1014 fn clone(&self) -> Self {
1015 Brush(self.0.clone())
1016 }
1017}
1018
1019impl<H: HasContext + ?Sized> IntoBrush<RenderContext<'_, H>> for Brush<H> {
1020 fn make_brush<'a>(
1021 &'a self,
1022 _piet: &mut RenderContext<'_, H>,
1023 _bbox: impl FnOnce() -> kurbo::Rect,
1024 ) -> Cow<'a, Brush<H>> {
1025 Cow::Borrowed(self)
1026 }
1027}
1028
1029#[derive(Debug)]
1031pub struct Image<H: HasContext + ?Sized>(piet_hardware::Image<GpuContext<H>>);
1032
1033impl<H: HasContext + ?Sized> Clone for Image<H> {
1034 fn clone(&self) -> Self {
1035 Image(self.0.clone())
1036 }
1037}
1038
1039impl<H: HasContext + ?Sized> piet::Image for Image<H> {
1040 fn size(&self) -> kurbo::Size {
1041 self.0.size()
1042 }
1043}
1044
1045#[derive(Debug, Clone)]
1047pub struct TextLayout(piet_hardware::TextLayout);
1048
1049impl piet::TextLayout for TextLayout {
1050 fn size(&self) -> kurbo::Size {
1051 self.0.size()
1052 }
1053
1054 fn line_text(&self, line_number: usize) -> Option<&str> {
1055 self.0.line_text(line_number)
1056 }
1057
1058 fn line_metric(&self, line_number: usize) -> Option<piet::LineMetric> {
1059 self.0.line_metric(line_number)
1060 }
1061
1062 fn line_count(&self) -> usize {
1063 self.0.line_count()
1064 }
1065
1066 fn hit_test_point(&self, point: kurbo::Point) -> piet::HitTestPoint {
1067 self.0.hit_test_point(point)
1068 }
1069
1070 fn trailing_whitespace_width(&self) -> f64 {
1071 self.0.trailing_whitespace_width()
1072 }
1073
1074 fn image_bounds(&self) -> kurbo::Rect {
1075 self.0.image_bounds()
1076 }
1077
1078 fn text(&self) -> &str {
1079 self.0.text()
1080 }
1081
1082 fn hit_test_text_position(&self, idx: usize) -> piet::HitTestPosition {
1083 self.0.hit_test_text_position(idx)
1084 }
1085}
1086
1087#[derive(Debug)]
1089pub struct TextLayoutBuilder(piet_hardware::TextLayoutBuilder);
1090
1091impl piet::TextLayoutBuilder for TextLayoutBuilder {
1092 type Out = TextLayout;
1093
1094 fn max_width(self, width: f64) -> Self {
1095 Self(self.0.max_width(width))
1096 }
1097
1098 fn alignment(self, alignment: piet::TextAlignment) -> Self {
1099 Self(self.0.alignment(alignment))
1100 }
1101
1102 fn default_attribute(self, attribute: impl Into<piet::TextAttribute>) -> Self {
1103 Self(self.0.default_attribute(attribute))
1104 }
1105
1106 fn range_attribute(
1107 self,
1108 range: impl std::ops::RangeBounds<usize>,
1109 attribute: impl Into<piet::TextAttribute>,
1110 ) -> Self {
1111 Self(self.0.range_attribute(range, attribute))
1112 }
1113
1114 fn build(self) -> Result<Self::Out, Pierror> {
1115 Ok(TextLayout(self.0.build()?))
1116 }
1117}
1118
1119#[derive(Debug, Clone)]
1121pub struct Text(piet_hardware::Text);
1122
1123impl Text {
1124 pub fn dpi(&self) -> f64 {
1126 self.0.dpi()
1127 }
1128
1129 pub fn set_dpi(&mut self, dpi: f64) {
1131 self.0.set_dpi(dpi)
1132 }
1133}
1134
1135impl piet::Text for Text {
1136 type TextLayoutBuilder = TextLayoutBuilder;
1137 type TextLayout = TextLayout;
1138
1139 fn font_family(&mut self, family_name: &str) -> Option<piet::FontFamily> {
1140 self.0.font_family(family_name)
1141 }
1142
1143 fn load_font(&mut self, data: &[u8]) -> Result<piet::FontFamily, Pierror> {
1144 self.0.load_font(data)
1145 }
1146
1147 fn new_text_layout(&mut self, text: impl piet::TextStorage) -> Self::TextLayoutBuilder {
1148 TextLayoutBuilder(self.0.new_text_layout(text))
1149 }
1150}
1151
1152fn compile_program<H: HasContext + ?Sized>(
1153 context: &H,
1154 vertex_shader: &str,
1155 fragment_shader: &str,
1156) -> Result<H::Program, GlError> {
1157 unsafe {
1158 let vertex_shader = compile_shader(context, glow::VERTEX_SHADER, vertex_shader)?;
1159 let fragment_shader = compile_shader(context, glow::FRAGMENT_SHADER, fragment_shader)?;
1160
1161 let program = context.create_program().gl_err()?;
1162 let _call_on_drop = CallOnDrop(|| context.delete_program(program));
1163
1164 context.attach_shader(program, vertex_shader);
1165 context.attach_shader(program, fragment_shader);
1166 let _unlink_shaders = CallOnDrop(|| {
1167 context.detach_shader(program, vertex_shader);
1168 context.detach_shader(program, fragment_shader);
1169 context.delete_shader(vertex_shader);
1170 context.delete_shader(fragment_shader);
1171 });
1172 context.link_program(program);
1173
1174 if !context.get_program_link_status(program) {
1175 let log = context.get_program_info_log(program);
1176 return Err(GlError(log));
1177 }
1178
1179 mem::forget(_call_on_drop);
1180 Ok(program)
1181 }
1182}
1183
1184unsafe fn compile_shader<H: HasContext + ?Sized>(
1185 context: &H,
1186 shader_type: u32,
1187 source: &str,
1188) -> Result<H::Shader, GlError> {
1189 let shader = context.create_shader(shader_type).gl_err()?;
1190 let _call_on_drop = CallOnDrop(|| context.delete_shader(shader));
1191
1192 context.shader_source(shader, source);
1193 context.compile_shader(shader);
1194
1195 if !context.get_shader_compile_status(shader) {
1196 let log = context.get_shader_info_log(shader);
1197 return Err(GlError(log));
1198 }
1199
1200 mem::forget(_call_on_drop);
1201 Ok(shader)
1202}
1203
1204fn gl_error(h: &(impl HasContext + ?Sized)) {
1205 let err = unsafe { h.get_error() };
1206
1207 if err != glow::NO_ERROR {
1208 let error_str = match err {
1209 glow::INVALID_ENUM => "GL_INVALID_ENUM",
1210 glow::INVALID_VALUE => "GL_INVALID_VALUE",
1211 glow::INVALID_OPERATION => "GL_INVALID_OPERATION",
1212 glow::STACK_OVERFLOW => "GL_STACK_OVERFLOW",
1213 glow::STACK_UNDERFLOW => "GL_STACK_UNDERFLOW",
1214 glow::OUT_OF_MEMORY => "GL_OUT_OF_MEMORY",
1215 glow::INVALID_FRAMEBUFFER_OPERATION => "GL_INVALID_FRAMEBUFFER_OPERATION",
1216 glow::CONTEXT_LOST => "GL_CONTEXT_LOST",
1217 _ => "Unknown GL error",
1218 };
1219
1220 tracing::error!("GL error: {}", error_str)
1221 }
1222}
1223
1224trait ResultExt<T, E> {
1225 fn gl_err(self) -> Result<T, GlError>;
1226}
1227
1228impl<T, E: Into<GlError>> ResultExt<T, E> for Result<T, E> {
1229 fn gl_err(self) -> Result<T, GlError> {
1230 self.map_err(Into::into)
1231 }
1232}
1233
1234struct CallOnDrop<F: FnMut()>(F);
1235
1236impl<F: FnMut()> Drop for CallOnDrop<F> {
1237 fn drop(&mut self) {
1238 (self.0)();
1239 }
1240}