floem_vger/
lib.rs

1use cosmic_text::{SubpixelBin, SwashImage};
2use std::sync::Arc;
3
4mod path;
5use path::*;
6
7mod scene;
8use scene::*;
9
10mod prim;
11use prim::*;
12
13pub mod defs;
14use defs::*;
15
16mod paint;
17use paint::*;
18
19mod gpu_vec;
20use gpu_vec::*;
21
22pub mod color;
23pub use color::Color;
24
25pub mod atlas;
26
27mod glyphs;
28
29use glyphs::GlyphCache;
30pub use glyphs::{Image, PixelFormat};
31
32use wgpu::util::DeviceExt;
33
34#[allow(dead_code)]
35#[derive(Copy, Clone, Debug)]
36struct Uniforms {
37    size: [f32; 2],
38    atlas_size: [f32; 2],
39}
40
41#[derive(Copy, Clone, Debug)]
42pub struct PaintIndex {
43    index: usize,
44}
45
46#[derive(Copy, Clone, Debug)]
47pub struct ImageIndex {
48    index: usize,
49}
50
51#[derive(Copy, Clone, Debug)]
52pub struct LineMetrics {
53    pub glyph_start: usize,
54    pub glyph_end: usize,
55    pub bounds: LocalRect,
56}
57
58#[derive(Copy, Clone, Debug)]
59#[repr(C)]
60pub(crate) struct Scissor {
61    pub xform: WorldToLocal,
62    pub origin: [f32; 2],
63    pub size: [f32; 2],
64    pub radius: f32,
65    pad: f32,
66}
67
68impl Scissor {
69    fn new() -> Self {
70        Self {
71            xform: WorldToLocal::identity(),
72            origin: [-10000.0, -10000.0],
73            size: [20000.0, 20000.0],
74            radius: 0.0,
75            pad: 0.0,
76        }
77    }
78}
79
80pub struct Vger {
81    device: Arc<wgpu::Device>,
82    queue: Arc<wgpu::Queue>,
83    scenes: [Scene; 3],
84    cur_scene: usize,
85    cur_layer: usize,
86    cur_z_index: i32,
87    tx_stack: Vec<LocalToWorld>,
88    scissor_stack: Vec<Scissor>,
89    device_px_ratio: f32,
90    screen_size: ScreenSize,
91    paint_count: usize,
92    pipeline: wgpu::RenderPipeline,
93    uniform_bind_group: wgpu::BindGroup,
94    uniforms: GPUVec<Uniforms>,
95    xform_count: usize,
96    scissor_count: usize,
97    path_scanner: PathScanner,
98    pen: LocalPoint,
99    pub glyph_cache: GlyphCache,
100    images: Vec<Option<wgpu::Texture>>,
101    image_bind_groups: Vec<Option<wgpu::BindGroup>>,
102    cache_bind_group_layout: wgpu::BindGroupLayout,
103    cache_bind_group: wgpu::BindGroup,
104}
105
106impl Vger {
107    /// Create a new renderer given a device and output pixel format.
108    pub fn new(
109        device: Arc<wgpu::Device>,
110        queue: Arc<wgpu::Queue>,
111        texture_format: wgpu::TextureFormat,
112    ) -> Self {
113        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
114            label: None,
115            source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!(
116                "shader.wgsl"
117            ))),
118        });
119
120        let scenes = [
121            Scene::new(&device),
122            Scene::new(&device),
123            Scene::new(&device),
124        ];
125
126        let uniform_bind_group_layout =
127            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
128                entries: &[
129                    wgpu::BindGroupLayoutEntry {
130                        binding: 0,
131                        visibility: wgpu::ShaderStages::VERTEX,
132                        ty: wgpu::BindingType::Buffer {
133                            ty: wgpu::BufferBindingType::Uniform,
134                            has_dynamic_offset: false,
135                            min_binding_size: None,
136                        },
137                        count: None,
138                    },
139                    wgpu::BindGroupLayoutEntry {
140                        binding: 1,
141                        visibility: wgpu::ShaderStages::FRAGMENT,
142                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
143                        count: None,
144                    },
145                    wgpu::BindGroupLayoutEntry {
146                        binding: 2,
147                        visibility: wgpu::ShaderStages::FRAGMENT,
148                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
149                        count: None,
150                    },
151                ],
152                label: Some("uniform_bind_group_layout"),
153            });
154
155        let cache_bind_group_layout =
156            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
157                entries: &[
158                    wgpu::BindGroupLayoutEntry {
159                        binding: 0,
160                        visibility: wgpu::ShaderStages::FRAGMENT,
161                        ty: wgpu::BindingType::Texture {
162                            multisampled: false,
163                            sample_type: wgpu::TextureSampleType::Float { filterable: true },
164                            view_dimension: wgpu::TextureViewDimension::D2,
165                        },
166                        count: None,
167                    },
168                    wgpu::BindGroupLayoutEntry {
169                        binding: 1,
170                        visibility: wgpu::ShaderStages::FRAGMENT,
171                        ty: wgpu::BindingType::Texture {
172                            multisampled: false,
173                            sample_type: wgpu::TextureSampleType::Float { filterable: true },
174                            view_dimension: wgpu::TextureViewDimension::D2,
175                        },
176                        count: None,
177                    },
178                ],
179                label: Some("image_bind_group_layout"),
180            });
181
182        let glyph_cache = GlyphCache::new(&device);
183
184        let uniforms = GPUVec::new_uniforms(&device, "uniforms");
185
186        let glyph_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
187            label: Some("glyph"),
188            mag_filter: wgpu::FilterMode::Linear,
189            min_filter: wgpu::FilterMode::Linear,
190            mipmap_filter: wgpu::FilterMode::Linear,
191            ..Default::default()
192        });
193
194        let color_glyph_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
195            label: Some("color_glyph"),
196            mag_filter: wgpu::FilterMode::Linear,
197            min_filter: wgpu::FilterMode::Linear,
198            mipmap_filter: wgpu::FilterMode::Linear,
199            ..Default::default()
200        });
201
202        let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
203            layout: &uniform_bind_group_layout,
204            entries: &[
205                uniforms.bind_group_entry(0),
206                wgpu::BindGroupEntry {
207                    binding: 1,
208                    resource: wgpu::BindingResource::Sampler(&glyph_sampler),
209                },
210                wgpu::BindGroupEntry {
211                    binding: 2,
212                    resource: wgpu::BindingResource::Sampler(&color_glyph_sampler),
213                },
214            ],
215            label: Some("vger bind group"),
216        });
217
218        let cache_bind_group =
219            Self::get_cache_bind_group(&device, &glyph_cache, &cache_bind_group_layout);
220
221        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
222            label: None,
223            bind_group_layouts: &[
224                &Scene::bind_group_layout(&device),
225                &uniform_bind_group_layout,
226                &cache_bind_group_layout,
227            ],
228            push_constant_ranges: &[],
229        });
230
231        let blend_comp = wgpu::BlendComponent {
232            operation: wgpu::BlendOperation::Add,
233            src_factor: wgpu::BlendFactor::SrcAlpha,
234            dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
235        };
236
237        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
238            label: None,
239            layout: Some(&pipeline_layout),
240            vertex: wgpu::VertexState {
241                module: &shader,
242                entry_point: "vs_main",
243                buffers: &[],
244                compilation_options: Default::default(),
245            },
246            fragment: Some(wgpu::FragmentState {
247                module: &shader,
248                entry_point: "fs_main",
249                targets: &[Some(wgpu::ColorTargetState {
250                    format: texture_format,
251                    blend: Some(wgpu::BlendState {
252                        color: blend_comp,
253                        alpha: blend_comp,
254                    }),
255                    write_mask: wgpu::ColorWrites::ALL,
256                })],
257                compilation_options: Default::default(),
258            }),
259            primitive: wgpu::PrimitiveState {
260                cull_mode: None,
261                topology: wgpu::PrimitiveTopology::TriangleStrip,
262                ..Default::default()
263            },
264            depth_stencil: None,
265            multisample: wgpu::MultisampleState::default(),
266            multiview: None,
267            cache: None,
268        });
269
270        Self {
271            device,
272            queue,
273            scenes,
274            cur_scene: 0,
275            cur_layer: 0,
276            cur_z_index: 0,
277            tx_stack: vec![],
278            scissor_stack: vec![],
279            device_px_ratio: 1.0,
280            screen_size: ScreenSize::new(512.0, 512.0),
281            paint_count: 0,
282            pipeline,
283            uniforms,
284            uniform_bind_group,
285            xform_count: 0,
286            scissor_count: 0,
287            path_scanner: PathScanner::new(),
288            pen: LocalPoint::zero(),
289            glyph_cache,
290            images: vec![],
291            image_bind_groups: vec![],
292            cache_bind_group_layout,
293            cache_bind_group,
294        }
295    }
296
297    fn get_cache_bind_group(
298        device: &wgpu::Device,
299        glyph_cache: &GlyphCache,
300        bind_group_layout: &wgpu::BindGroupLayout,
301    ) -> wgpu::BindGroup {
302        let mask_texture_view = glyph_cache.mask_atlas.create_view();
303        let color_texture_view = glyph_cache.color_atlas.create_view();
304
305        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
306            layout: bind_group_layout,
307            entries: &[
308                wgpu::BindGroupEntry {
309                    binding: 0,
310                    resource: wgpu::BindingResource::TextureView(&mask_texture_view),
311                },
312                wgpu::BindGroupEntry {
313                    binding: 1,
314                    resource: wgpu::BindingResource::TextureView(&color_texture_view),
315                },
316            ],
317            label: Some("vger cache bind group"),
318        });
319
320        bind_group
321    }
322
323    /// Begin rendering.
324    pub fn begin(&mut self, window_width: f32, window_height: f32, device_px_ratio: f32) {
325        self.device_px_ratio = device_px_ratio;
326        self.cur_layer = 0;
327        self.screen_size = ScreenSize::new(window_width, window_height);
328        self.cur_scene = (self.cur_scene + 1) % 3;
329        self.scenes[self.cur_scene].clear();
330        self.tx_stack.clear();
331        self.tx_stack.push(LocalToWorld::identity());
332        self.scissor_stack.clear();
333        self.scissor_stack.push(Scissor::new());
334        self.paint_count = 0;
335        self.xform_count = 0;
336        self.add_xform();
337        self.scissor_count = 0;
338        self.pen = LocalPoint::zero();
339
340        // If we're getting close to full, reset the glyph cache.
341        if self.glyph_cache.check_usage(&self.device) {
342            // if resized, we need to get new bind group
343            self.cache_bind_group = Self::get_cache_bind_group(
344                &self.device,
345                &self.glyph_cache,
346                &self.cache_bind_group_layout,
347            )
348        }
349
350        self.uniforms.clear();
351        self.uniforms.push(Uniforms {
352            size: [window_width, window_height],
353            atlas_size: [self.glyph_cache.size as f32, self.glyph_cache.size as f32],
354        });
355    }
356
357    /// Saves rendering state (transform and scissor rect).
358    pub fn save(&mut self) {
359        self.tx_stack.push(*self.tx_stack.last().unwrap());
360        self.scissor_stack.push(*self.scissor_stack.last().unwrap());
361    }
362
363    /// Restores rendering state (transform and scissor rect).
364    pub fn restore(&mut self) {
365        self.tx_stack.pop();
366        self.scissor_stack.pop();
367    }
368
369    /// Encode all rendering to a command buffer.
370    pub fn encode(&mut self, render_pass: &wgpu::RenderPassDescriptor) {
371        let device = &self.device;
372        let queue = &self.queue;
373        self.scenes[self.cur_scene].update(device, queue);
374        self.uniforms.update(device, queue);
375        let mut current_texture = -1;
376
377        let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
378            label: Some("vger encoder"),
379        });
380
381        self.glyph_cache.update(device, &mut encoder);
382
383        {
384            let mut rpass = encoder.begin_render_pass(render_pass);
385
386            rpass.set_pipeline(&self.pipeline);
387
388            rpass.set_bind_group(
389                0,
390                &self.scenes[self.cur_scene].bind_groups[self.cur_layer],
391                &[], // dynamic offsets
392            );
393
394            rpass.set_bind_group(1, &self.uniform_bind_group, &[]);
395            rpass.set_bind_group(2, &self.cache_bind_group, &[]);
396
397            let scene = &self.scenes[self.cur_scene];
398            let n = scene.prims[self.cur_layer].len();
399            let mut m: u32 = 0;
400            let mut start: u32 = 0;
401
402            for i in 0..n {
403                let prim = &scene.prims[self.cur_layer][i];
404                let image_id = scene.paints[prim.paint as usize].image;
405
406                // Image changed, render.
407                if image_id >= 0 && image_id != current_texture {
408                    // println!("image changed: encoding {:?} prims", m);
409                    if m > 0 {
410                        rpass.draw(
411                            /*vertices*/ 0..4,
412                            /*instances*/ start..(start + m),
413                        );
414                    }
415
416                    current_texture = image_id;
417                    rpass.set_bind_group(
418                        2,
419                        self.image_bind_groups[image_id as usize].as_ref().unwrap(),
420                        &[],
421                    );
422
423                    start += m;
424                    m = 0;
425                }
426
427                m += 1;
428            }
429
430            // println!("encoding {:?} prims", m);
431
432            if m > 0 {
433                rpass.draw(
434                    /*vertices*/ 0..4,
435                    /*instances*/ start..(start + m),
436                )
437            }
438        }
439        queue.submit(Some(encoder.finish()));
440    }
441
442    fn render(&mut self, prim: Prim) {
443        let prims = self.scenes[self.cur_scene]
444            .depthed_prims
445            .entry(self.cur_z_index)
446            .or_default();
447        prims.push(prim);
448    }
449
450    /// Fills a circle.
451    pub fn fill_circle<Pt: Into<LocalPoint>>(
452        &mut self,
453        center: Pt,
454        radius: f32,
455        paint_index: PaintIndex,
456    ) {
457        let mut prim = Prim::default();
458        prim.prim_type = PrimType::Circle as u32;
459        let c: LocalPoint = center.into();
460        prim.cvs[0] = c.x;
461        prim.cvs[1] = c.y;
462        prim.radius = radius;
463        prim.paint = paint_index.index as u32;
464        prim.quad_bounds = [c.x - radius, c.y - radius, c.x + radius, c.y + radius];
465        prim.tex_bounds = prim.quad_bounds;
466        prim.scissor = self.add_scissor() as u32;
467
468        self.render(prim);
469    }
470
471    /// Strokes an arc.
472    pub fn stroke_arc<Pt: Into<LocalPoint>>(
473        &mut self,
474        center: Pt,
475        radius: f32,
476        width: f32,
477        rotation: f32,
478        aperture: f32,
479        paint_index: PaintIndex,
480    ) {
481        let mut prim = Prim::default();
482        prim.prim_type = PrimType::Arc as u32;
483        prim.radius = radius;
484        let c: LocalPoint = center.into();
485        prim.cvs = [
486            c.x,
487            c.y,
488            rotation.sin(),
489            rotation.cos(),
490            aperture.sin(),
491            aperture.cos(),
492        ];
493        prim.width = width;
494        prim.paint = paint_index.index as u32;
495        prim.quad_bounds = [
496            c.x - radius - width,
497            c.y - radius - width,
498            c.x + radius + width,
499            c.y + radius + width,
500        ];
501        prim.tex_bounds = prim.quad_bounds;
502        prim.scissor = self.add_scissor() as u32;
503
504        self.render(prim);
505    }
506
507    /// Fills a rectangle.
508    pub fn fill_rect<Rect: Into<LocalRect>>(
509        &mut self,
510        rect: Rect,
511        radius: f32,
512        paint_index: PaintIndex,
513        blur_radius: f32,
514    ) {
515        let mut prim = Prim::default();
516        prim.prim_type = PrimType::Rect as u32;
517        let r: LocalRect = rect.into();
518        let min = r.min();
519        let max = r.max();
520        prim.cvs[0] = min.x;
521        prim.cvs[1] = min.y;
522        prim.cvs[2] = max.x;
523        prim.cvs[3] = max.y;
524        prim.cvs[4] = blur_radius;
525        prim.radius = radius;
526        prim.paint = paint_index.index as u32;
527        prim.quad_bounds = [
528            min.x - blur_radius * 3.0,
529            min.y - blur_radius * 3.0,
530            max.x + blur_radius * 3.0,
531            max.y + blur_radius * 3.0,
532        ];
533        prim.tex_bounds = prim.quad_bounds;
534        prim.scissor = self.add_scissor() as u32;
535
536        self.render(prim);
537    }
538
539    /// Strokes a rectangle.
540    pub fn stroke_rect(
541        &mut self,
542        min: LocalPoint,
543        max: LocalPoint,
544        radius: f32,
545        width: f32,
546        paint_index: PaintIndex,
547    ) {
548        let mut prim = Prim::default();
549        prim.prim_type = PrimType::RectStroke as u32;
550        prim.cvs[0] = min.x;
551        prim.cvs[1] = min.y;
552        prim.cvs[2] = max.x;
553        prim.cvs[3] = max.y;
554        prim.radius = radius;
555        prim.width = width;
556        prim.paint = paint_index.index as u32;
557        prim.quad_bounds = [min.x - width, min.y - width, max.x + width, max.y + width];
558        prim.tex_bounds = prim.quad_bounds;
559        prim.scissor = self.add_scissor() as u32;
560
561        self.render(prim);
562    }
563
564    /// Strokes a line segment.
565    pub fn stroke_segment<Pt: Into<LocalPoint>>(
566        &mut self,
567        a: Pt,
568        b: Pt,
569        width: f32,
570        paint_index: PaintIndex,
571    ) {
572        let mut prim = Prim::default();
573        prim.prim_type = PrimType::Segment as u32;
574        let ap: LocalPoint = a.into();
575        let bp: LocalPoint = b.into();
576        prim.cvs[0] = ap.x;
577        prim.cvs[1] = ap.y;
578        prim.cvs[2] = bp.x;
579        prim.cvs[3] = bp.y;
580        prim.width = width;
581        prim.paint = paint_index.index as u32;
582        prim.quad_bounds = [
583            ap.x.min(bp.x) - width * 2.0,
584            ap.y.min(bp.y) - width * 2.0,
585            ap.x.max(bp.x) + width * 2.0,
586            ap.y.max(bp.y) + width * 2.0,
587        ];
588        prim.tex_bounds = prim.quad_bounds;
589        prim.scissor = self.add_scissor() as u32;
590
591        self.render(prim);
592    }
593
594    /// Strokes a quadratic bezier segment.
595    pub fn stroke_bezier<Pt: Into<LocalPoint>>(
596        &mut self,
597        a: Pt,
598        b: Pt,
599        c: Pt,
600        width: f32,
601        paint_index: PaintIndex,
602    ) {
603        let mut prim = Prim::default();
604        prim.prim_type = PrimType::Bezier as u32;
605        let ap: LocalPoint = a.into();
606        let bp: LocalPoint = b.into();
607        let cp: LocalPoint = c.into();
608        prim.cvs[0] = ap.x;
609        prim.cvs[1] = ap.y;
610        prim.cvs[2] = bp.x;
611        prim.cvs[3] = bp.y;
612        prim.cvs[4] = cp.x;
613        prim.cvs[5] = cp.y;
614        prim.width = width;
615        prim.paint = paint_index.index as u32;
616        prim.quad_bounds = [
617            ap.x.min(bp.x).min(cp.x) - width,
618            ap.y.min(bp.y).min(cp.y) - width,
619            ap.x.max(bp.x).max(cp.x) + width,
620            ap.y.max(bp.y).max(cp.y) + width,
621        ];
622        prim.tex_bounds = prim.quad_bounds;
623        prim.scissor = self.add_scissor() as u32;
624
625        self.render(prim);
626    }
627
628    /// Move the pen to a point (path fills only)
629    pub fn move_to<Pt: Into<LocalPoint>>(&mut self, p: Pt) {
630        self.pen = p.into();
631    }
632
633    /// Makes a quadratic curve to a point (path fills only)
634    pub fn quad_to<Pt: Into<LocalPoint>>(&mut self, b: Pt, c: Pt) {
635        let cp: LocalPoint = c.into();
636        self.path_scanner
637            .segments
638            .push(PathSegment::new(self.pen, b.into(), cp));
639        self.pen = cp;
640    }
641
642    fn add_cv<Pt: Into<LocalPoint>>(&mut self, p: Pt) {
643        self.scenes[self.cur_scene].cvs.push(p.into())
644    }
645
646    /// Fills a path.
647    pub fn fill(&mut self, paint_index: PaintIndex) {
648        let scissor = self.add_scissor();
649
650        self.path_scanner.init();
651
652        while self.path_scanner.next() {
653            let mut prim = Prim::default();
654            prim.prim_type = PrimType::PathFill as u32;
655            prim.paint = paint_index.index as u32;
656            prim.scissor = scissor as u32;
657            prim.start = self.scenes[self.cur_scene].cvs.len() as u32;
658
659            let mut x_interval = Interval {
660                a: std::f32::MAX,
661                b: std::f32::MIN,
662            };
663
664            let mut index = self.path_scanner.first;
665            while let Some(a) = index {
666                for i in 0..3 {
667                    let p = self.path_scanner.segments[a].cvs[i];
668                    self.add_cv(p);
669                    x_interval.a = x_interval.a.min(p.x);
670                    x_interval.b = x_interval.b.max(p.x);
671                }
672                prim.count += 1;
673
674                index = self.path_scanner.segments[a].next;
675            }
676
677            prim.quad_bounds[0] = x_interval.a;
678            prim.quad_bounds[1] = self.path_scanner.interval.a;
679            prim.quad_bounds[2] = x_interval.b;
680            prim.quad_bounds[3] = self.path_scanner.interval.b;
681            prim.tex_bounds = prim.quad_bounds;
682
683            self.render(prim);
684        }
685
686        self.path_scanner.segments.clear();
687    }
688
689    pub fn render_glyph(
690        &mut self,
691        x: f32,
692        y: f32,
693        font_id: cosmic_text::fontdb::ID,
694        glyph_id: u16,
695        size: u32,
696        subpx: (SubpixelBin, SubpixelBin),
697        image: impl FnOnce() -> SwashImage,
698        paint_index: PaintIndex,
699    ) {
700        let info = self
701            .glyph_cache
702            .get_glyph_mask(font_id, glyph_id, size, subpx, image);
703        if let Some(rect) = info.rect {
704            let mut prim = Prim::default();
705            prim.prim_type = if info.colored {
706                PrimType::ColorGlyph
707            } else {
708                PrimType::Glyph
709            } as u32;
710
711            let x = x + info.left as f32;
712            let y = y - info.top as f32;
713            prim.quad_bounds = [x, y, x + rect.width as f32, y + rect.height as f32];
714
715            prim.tex_bounds = [
716                rect.x as f32,
717                rect.y as f32,
718                (rect.x + rect.width) as f32,
719                (rect.y + rect.height) as f32,
720            ];
721            prim.paint = paint_index.index as u32;
722            prim.scissor = self.add_scissor() as u32;
723
724            self.render(prim);
725        }
726    }
727
728    pub fn render_image(
729        &mut self,
730        x: f32,
731        y: f32,
732        hash: &[u8],
733        width: u32,
734        height: u32,
735        image_fn: impl FnOnce() -> Image,
736    ) {
737        let info = self.glyph_cache.get_image_mask(hash, image_fn);
738        if let Some(rect) = info.rect {
739            let mut prim = Prim::default();
740            prim.prim_type = PrimType::ColorGlyph as u32;
741
742            let x = x + info.left as f32;
743            let y = y - info.top as f32;
744            prim.quad_bounds = [x, y, x + width as f32, y + height as f32];
745
746            prim.tex_bounds = [
747                rect.x as f32,
748                rect.y as f32,
749                (rect.x + rect.width) as f32,
750                (rect.y + rect.height) as f32,
751            ];
752            prim.scissor = self.add_scissor() as u32;
753
754            self.render(prim);
755        }
756    }
757
758    pub fn render_svg(
759        &mut self,
760        x: f32,
761        y: f32,
762        hash: &[u8],
763        width: u32,
764        height: u32,
765        image: impl FnOnce() -> Vec<u8>,
766        paint_index: Option<PaintIndex>,
767    ) {
768        let info = self.glyph_cache.get_svg_mask(hash, width, height, image);
769        if let Some(rect) = info.rect {
770            let mut prim = Prim::default();
771            prim.prim_type = if paint_index.is_some() {
772                PrimType::OverrideColorSvg
773            } else {
774                PrimType::ColorGlyph
775            } as u32;
776
777            let x = x + info.left as f32;
778            let y = y - info.top as f32;
779            prim.quad_bounds = [x, y, x + rect.width as f32, y + rect.height as f32];
780
781            prim.tex_bounds = [
782                rect.x as f32,
783                rect.y as f32,
784                (rect.x + rect.width) as f32,
785                (rect.y + rect.height) as f32,
786            ];
787            if let Some(paint_index) = paint_index {
788                prim.paint = paint_index.index as u32;
789            }
790            prim.scissor = self.add_scissor() as u32;
791
792            self.render(prim);
793        }
794    }
795
796    fn add_xform(&mut self) -> usize {
797        if self.xform_count < MAX_PRIMS {
798            let m = *self.tx_stack.last().unwrap();
799            self.scenes[self.cur_scene]
800                .xforms
801                .push(m.to_3d().to_array());
802            let n = self.xform_count;
803            self.xform_count += 1;
804            return n;
805        }
806        0
807    }
808
809    fn add_scissor(&mut self) -> usize {
810        if self.scissor_count < MAX_PRIMS {
811            let scissor = *self.scissor_stack.last().unwrap();
812            self.scenes[self.cur_scene].scissors.push(scissor);
813            let n = self.scissor_count;
814            self.scissor_count += 1;
815            return n;
816        }
817        0
818    }
819
820    /// Translates the coordinate system.
821    pub fn translate<Vec: Into<LocalVector>>(&mut self, offset: Vec) {
822        if let Some(m) = self.tx_stack.last_mut() {
823            *m = (*m).pre_translate(offset.into());
824        }
825    }
826
827    /// Scales the coordinate system.
828    pub fn scale<Vec: Into<LocalVector>>(&mut self, scale: Vec) {
829        if let Some(m) = self.tx_stack.last_mut() {
830            let s: LocalVector = scale.into();
831            *m = (*m).pre_scale(s.x, s.y);
832        }
833    }
834
835    /// Rotates the coordinate system.
836    pub fn rotate(&mut self, theta: f32) {
837        if let Some(m) = self.tx_stack.last_mut() {
838            *m = m.pre_rotate(euclid::Angle::<f32>::radians(theta));
839        }
840    }
841
842    pub fn set_z_index(&mut self, z_index: i32) {
843        self.cur_z_index = z_index;
844    }
845
846    /// Gets the current transform.
847    pub fn current_transform(&self) -> LocalToWorld {
848        *self.tx_stack.last().unwrap()
849    }
850
851    /// Sets the current scissor rect.
852    pub fn scissor(&mut self, rect: LocalRect, radius: f32) {
853        if let Some(m) = self.scissor_stack.last_mut() {
854            *m = Scissor::new();
855            if let Some(xform) = self.tx_stack.last().unwrap().inverse() {
856                m.xform = xform;
857                m.origin = rect.origin.to_array();
858                m.size = rect.size.to_array();
859                m.radius = radius;
860            }
861        }
862    }
863
864    /// Resets the current scissor rect.
865    pub fn reset_scissor(&mut self) {
866        if let Some(m) = self.scissor_stack.last_mut() {
867            *m = Scissor::new();
868        }
869    }
870
871    fn add_paint(&mut self, paint: Paint) -> PaintIndex {
872        if self.paint_count < MAX_PRIMS {
873            self.scenes[self.cur_scene].paints.push(paint);
874            self.paint_count += 1;
875            return PaintIndex {
876                index: self.paint_count - 1,
877            };
878        }
879        PaintIndex { index: 0 }
880    }
881
882    /// Solid color paint.
883    pub fn color_paint(&mut self, color: Color) -> PaintIndex {
884        self.add_paint(Paint::solid_color(color))
885    }
886
887    /// Linear gradient paint.
888    pub fn linear_gradient<Pt: Into<LocalPoint>>(
889        &mut self,
890        start: Pt,
891        end: Pt,
892        inner_color: Color,
893        outer_color: Color,
894        glow: f32,
895    ) -> PaintIndex {
896        self.add_paint(Paint::linear_gradient(
897            start.into(),
898            end.into(),
899            inner_color,
900            outer_color,
901            glow,
902        ))
903    }
904
905    /// Create an image from pixel data in memory.
906    /// Must be RGBA8.
907    pub fn create_image_pixels(&mut self, data: &[u8], width: u32, height: u32) -> ImageIndex {
908        let texture_size = wgpu::Extent3d {
909            width,
910            height,
911            depth_or_array_layers: 1,
912        };
913
914        let texture_desc = wgpu::TextureDescriptor {
915            size: texture_size,
916            mip_level_count: 1,
917            sample_count: 1,
918            dimension: wgpu::TextureDimension::D2,
919            format: wgpu::TextureFormat::Rgba8UnormSrgb,
920            usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING,
921            label: Some("lyte image"),
922            view_formats: &[wgpu::TextureFormat::Rgba8UnormSrgb],
923        };
924
925        let texture = self.device.create_texture(&texture_desc);
926
927        let buffer = self
928            .device
929            .create_buffer_init(&wgpu::util::BufferInitDescriptor {
930                label: Some("Temp Buffer"),
931                contents: data,
932                usage: wgpu::BufferUsages::COPY_SRC,
933            });
934
935        let mut encoder = self
936            .device
937            .create_command_encoder(&wgpu::CommandEncoderDescriptor {
938                label: Some("texture_buffer_copy_encoder"),
939            });
940
941        let image_size = wgpu::Extent3d {
942            width,
943            height,
944            depth_or_array_layers: 1,
945        };
946
947        encoder.copy_buffer_to_texture(
948            wgpu::ImageCopyBuffer {
949                buffer: &buffer,
950                layout: wgpu::ImageDataLayout {
951                    offset: 0,
952                    bytes_per_row: Some(width * 4),
953                    rows_per_image: Some(height),
954                },
955            },
956            wgpu::ImageCopyTexture {
957                texture: &texture,
958                mip_level: 0,
959                aspect: wgpu::TextureAspect::All,
960                origin: wgpu::Origin3d::ZERO,
961            },
962            image_size,
963        );
964
965        self.queue.submit(std::iter::once(encoder.finish()));
966
967        let index = ImageIndex {
968            index: self.images.len(),
969        };
970
971        let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
972
973        self.images.push(Some(texture));
974
975        let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
976            layout: &self.cache_bind_group_layout,
977            entries: &[wgpu::BindGroupEntry {
978                binding: 1,
979                resource: wgpu::BindingResource::TextureView(&texture_view),
980            }],
981            label: Some("vger bind group"),
982        });
983
984        self.image_bind_groups.push(Some(bind_group));
985
986        index
987    }
988
989    pub fn delete_image(&mut self, image: ImageIndex) {
990        self.images[image.index] = None;
991        self.image_bind_groups[image.index] = None;
992    }
993}
994
995#[derive(Hash, Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
996#[repr(u8)]
997pub enum SubpixelOffset {
998    Zero = 0,
999    Quarter = 1,
1000    Half = 2,
1001    ThreeQuarters = 3,
1002}
1003
1004impl Default for SubpixelOffset {
1005    fn default() -> Self {
1006        SubpixelOffset::Zero
1007    }
1008}
1009
1010impl SubpixelOffset {
1011    // Skia quantizes subpixel offsets into 1/4 increments.
1012    // Given the absolute position, return the quantized increment
1013    pub fn quantize(pos: f32) -> Self {
1014        // Following the conventions of Gecko and Skia, we want
1015        // to quantize the subpixel position, such that abs(pos) gives:
1016        // [0.0, 0.125) -> Zero
1017        // [0.125, 0.375) -> Quarter
1018        // [0.375, 0.625) -> Half
1019        // [0.625, 0.875) -> ThreeQuarters,
1020        // [0.875, 1.0) -> Zero
1021        // The unit tests below check for this.
1022        let apos = ((pos - pos.floor()) * 8.0) as i32;
1023        match apos {
1024            1..=2 => SubpixelOffset::Quarter,
1025            3..=4 => SubpixelOffset::Half,
1026            5..=6 => SubpixelOffset::ThreeQuarters,
1027            _ => SubpixelOffset::Zero,
1028        }
1029    }
1030
1031    pub fn to_f32(self) -> f32 {
1032        match self {
1033            SubpixelOffset::Zero => 0.0,
1034            SubpixelOffset::Quarter => 0.25,
1035            SubpixelOffset::Half => 0.5,
1036            SubpixelOffset::ThreeQuarters => 0.75,
1037        }
1038    }
1039}