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 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 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 self.glyph_cache.check_usage(&self.device) {
342 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 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 pub fn restore(&mut self) {
365 self.tx_stack.pop();
366 self.scissor_stack.pop();
367 }
368
369 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 &[], );
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 if image_id >= 0 && image_id != current_texture {
408 if m > 0 {
410 rpass.draw(
411 0..4,
412 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 if m > 0 {
433 rpass.draw(
434 0..4,
435 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 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 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 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 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 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 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 pub fn move_to<Pt: Into<LocalPoint>>(&mut self, p: Pt) {
630 self.pen = p.into();
631 }
632
633 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 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 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 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 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 pub fn current_transform(&self) -> LocalToWorld {
848 *self.tx_stack.last().unwrap()
849 }
850
851 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 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 pub fn color_paint(&mut self, color: Color) -> PaintIndex {
884 self.add_paint(Paint::solid_color(color))
885 }
886
887 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 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 pub fn quantize(pos: f32) -> Self {
1014 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}