1use oxiui_core::Color;
25
26#[repr(C)]
30#[derive(Clone, Copy, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
31pub struct Vertex {
32 pub position: [f32; 2],
34 pub color: [f32; 4],
36 pub local: [f32; 2],
38 pub shape_xy: [f32; 2],
41 pub shape_r: f32,
45 pub kind: f32,
49 pub extra: [f32; 2],
56}
57
58pub const KIND_RECT: f32 = 0.0;
62pub const KIND_CIRCLE: f32 = 1.0;
64pub const KIND_ROUNDED_RECT: f32 = 2.0;
66pub const KIND_ROUNDED_RECT_PC: f32 = 3.0;
68pub const KIND_ELLIPSE: f32 = 4.0;
70pub const KIND_LINE_SEG: f32 = 5.0;
72
73const _: () = assert!(core::mem::size_of::<Vertex>() == 56);
75const _: () = assert!(core::mem::align_of::<Vertex>() == 4);
76
77impl Vertex {
78 #[inline]
80 pub fn color_to_f32(color: Color) -> [f32; 4] {
81 [
82 color.0 as f32 / 255.0,
83 color.1 as f32 / 255.0,
84 color.2 as f32 / 255.0,
85 color.3 as f32 / 255.0,
86 ]
87 }
88}
89
90#[repr(C)]
96#[derive(Clone, Copy, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
97pub struct Globals {
98 pub viewport: [f32; 2],
100 pub _pad: [f32; 2],
102}
103
104const _: () = assert!(core::mem::size_of::<Globals>() == 16);
105
106impl Globals {
107 #[inline]
109 pub fn new(width: u32, height: u32) -> Self {
110 Self {
111 viewport: [width as f32, height as f32],
112 _pad: [0.0, 0.0],
113 }
114 }
115}
116
117#[repr(C)]
121#[derive(Clone, Copy, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
122pub struct GradientVertex {
123 pub position: [f32; 2],
125 pub local: [f32; 2],
128}
129
130const _: () = assert!(core::mem::size_of::<GradientVertex>() == 16);
131
132pub const MAX_GRADIENT_STOPS: usize = 8;
134
135#[repr(C)]
140#[derive(Clone, Copy, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
141pub struct GradientUniforms {
142 pub p0: [f32; 2],
145 pub p1: [f32; 2],
148 pub radius: f32,
150 pub gradient_type: u32,
152 pub stop_count: u32,
154 pub _pad: u32,
156 pub stop_offsets: [[f32; 4]; MAX_GRADIENT_STOPS],
158 pub stop_colors: [[f32; 4]; MAX_GRADIENT_STOPS],
160}
161
162const _: () = assert!(core::mem::size_of::<GradientUniforms>() == 288);
165
166#[inline]
173pub fn pack_u16_pair(hi: u16, lo: u16) -> f32 {
174 f32::from_bits(((hi as u32) << 16) | (lo as u32))
175}
176
177pub fn push_rect_quad(out: &mut Vec<Vertex>, x: f32, y: f32, w: f32, h: f32, color: Color) {
182 let rgba = Vertex::color_to_f32(color);
183 let x1 = x + w;
184 let y1 = y + h;
185 let corners = [[x, y], [x, y1], [x1, y1], [x, y], [x1, y1], [x1, y]];
186 for c in corners {
187 out.push(Vertex {
188 position: c,
189 color: rgba,
190 local: c,
191 shape_xy: [0.0, 0.0],
192 shape_r: 0.0,
193 kind: KIND_RECT,
194 extra: [0.0, 0.0],
195 });
196 }
197}
198
199pub fn push_circle_quad(out: &mut Vec<Vertex>, cx: f32, cy: f32, radius: f32, color: Color) {
202 let rgba = Vertex::color_to_f32(color);
203 let r = radius + 1.0;
204 let x0 = cx - r;
205 let y0 = cy - r;
206 let x1 = cx + r;
207 let y1 = cy + r;
208 let corners = [[x0, y0], [x0, y1], [x1, y1], [x0, y0], [x1, y1], [x1, y0]];
209 for c in corners {
210 out.push(Vertex {
211 position: c,
212 color: rgba,
213 local: c,
214 shape_xy: [cx, cy],
215 shape_r: radius,
216 kind: KIND_CIRCLE,
217 extra: [0.0, 0.0],
218 });
219 }
220}
221
222pub fn push_rounded_rect_quad(
226 out: &mut Vec<Vertex>,
227 x: f32,
228 y: f32,
229 w: f32,
230 h: f32,
231 radius: f32,
232 color: Color,
233) {
234 let rgba = Vertex::color_to_f32(color);
235 let r = radius.min(w * 0.5).min(h * 0.5).max(0.0);
236 let cx = x + w * 0.5;
237 let cy = y + h * 0.5;
238 let hw = w * 0.5;
239 let hh = h * 0.5;
240 let pad = 1.0_f32;
241 let x0 = x - pad;
242 let y0 = y - pad;
243 let x1 = x + w + pad;
244 let y1 = y + h + pad;
245 let corners = [[x0, y0], [x0, y1], [x1, y1], [x0, y0], [x1, y1], [x1, y0]];
246 for c in corners {
247 out.push(Vertex {
248 position: c,
249 color: rgba,
250 local: c,
251 shape_xy: [cx, cy],
252 shape_r: r,
253 kind: KIND_ROUNDED_RECT,
254 extra: [hw, hh],
255 });
256 }
257}
258
259pub fn push_rounded_rect_per_corner_quad(
273 out: &mut Vec<Vertex>,
274 x: f32,
275 y: f32,
276 w: f32,
277 h: f32,
278 radii: [f32; 4],
279 color: Color,
280) {
281 let rgba = Vertex::color_to_f32(color);
282 let [tl, tr, br, bl] = radii;
283 let hw = w * 0.5;
284 let hh = h * 0.5;
285 let cx = x + hw;
286 let cy = y + hh;
287 let clamp_r = |r: f32| r.clamp(0.0, hw.min(hh).min(255.0));
288 let tl = clamp_r(tl);
289 let tr = clamp_r(tr);
290 let br = clamp_r(br);
291 let bl = clamp_r(bl);
292 let hw_c = hw.clamp(0.0, 4095.0);
293 let hh_c = hh.clamp(0.0, 4095.0);
294 let r_packed = tl.floor() * 256.0 + tr.floor();
296 let brbl_packed = br.floor() * 256.0 + bl.floor();
297 let hwhh_packed = hw_c.floor() * 4096.0 + hh_c.floor();
298 let pad = 1.0_f32;
299 let x0 = x - pad;
300 let y0 = y - pad;
301 let x1 = x + w + pad;
302 let y1 = y + h + pad;
303 let corners = [[x0, y0], [x0, y1], [x1, y1], [x0, y0], [x1, y1], [x1, y0]];
304 for c in corners {
305 out.push(Vertex {
306 position: c,
307 color: rgba,
308 local: c,
309 shape_xy: [cx, cy],
310 shape_r: r_packed,
311 kind: KIND_ROUNDED_RECT_PC,
312 extra: [brbl_packed, hwhh_packed],
313 });
314 }
315}
316
317pub fn push_ellipse_quad(out: &mut Vec<Vertex>, cx: f32, cy: f32, rx: f32, ry: f32, color: Color) {
320 let rgba = Vertex::color_to_f32(color);
321 let pad = 1.0_f32;
322 let x0 = cx - rx - pad;
323 let y0 = cy - ry - pad;
324 let x1 = cx + rx + pad;
325 let y1 = cy + ry + pad;
326 let corners = [[x0, y0], [x0, y1], [x1, y1], [x0, y0], [x1, y1], [x1, y0]];
327 for c in corners {
328 out.push(Vertex {
329 position: c,
330 color: rgba,
331 local: c,
332 shape_xy: [cx, cy],
333 shape_r: 0.0,
334 kind: KIND_ELLIPSE,
335 extra: [rx, ry],
336 });
337 }
338}
339
340pub struct LineQuadParams {
342 pub from_x: f32,
344 pub from_y: f32,
346 pub to_x: f32,
348 pub to_y: f32,
350 pub half_width: f32,
352 pub color: Color,
354 pub aa_smooth: bool,
356}
357
358pub fn push_line_quad(out: &mut Vec<Vertex>, params: LineQuadParams) {
366 let LineQuadParams {
367 from_x,
368 from_y,
369 to_x,
370 to_y,
371 half_width,
372 color,
373 aa_smooth,
374 } = params;
375 let rgba = Vertex::color_to_f32(color);
376 let dx = to_x - from_x;
377 let dy = to_y - from_y;
378 let len = (dx * dx + dy * dy).sqrt().max(1e-6);
379 let nx = -dy / len;
381 let ny = dx / len;
382 let lx = dx / len;
384 let ly = dy / len;
385 let expand = half_width + 1.0;
386 let cap = half_width + 1.0;
387 let ax = from_x - lx * cap + nx * expand;
390 let ay = from_y - ly * cap + ny * expand;
391 let bx = from_x - lx * cap - nx * expand;
392 let by = from_y - ly * cap - ny * expand;
393 let cx = to_x + lx * cap - nx * expand;
394 let cy_v = to_y + ly * cap - ny * expand;
395 let dxp = to_x + lx * cap + nx * expand;
396 let dyp = to_y + ly * cap + ny * expand;
397 let corners = [
399 [ax, ay],
400 [bx, by],
401 [cx, cy_v],
402 [ax, ay],
403 [cx, cy_v],
404 [dxp, dyp],
405 ];
406 let shape_r_val = if aa_smooth {
409 half_width + 0.5
410 } else {
411 half_width
412 };
413 for c in corners {
414 out.push(Vertex {
415 position: c,
416 color: rgba,
417 local: c,
418 shape_xy: [from_x, from_y],
419 shape_r: shape_r_val,
420 kind: KIND_LINE_SEG,
421 extra: [to_x, to_y],
422 });
423 }
424}
425
426pub fn push_triangle(
428 out: &mut Vec<Vertex>,
429 p0: [f32; 2],
430 p1: [f32; 2],
431 p2: [f32; 2],
432 color: Color,
433) {
434 let rgba = Vertex::color_to_f32(color);
435 for c in [p0, p1, p2] {
436 out.push(Vertex {
437 position: c,
438 color: rgba,
439 local: c,
440 shape_xy: [0.0, 0.0],
441 shape_r: 0.0,
442 kind: KIND_RECT,
443 extra: [0.0, 0.0],
444 });
445 }
446}
447
448#[repr(C)]
454#[derive(Clone, Copy, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
455pub struct TexVertex {
456 pub position: [f32; 2],
458 pub uv: [f32; 2],
460 pub tint: [f32; 4],
462}
463
464const _: () = assert!(core::mem::size_of::<TexVertex>() == 32);
465
466pub struct TexQuadParams {
470 pub x: f32,
472 pub y: f32,
474 pub w: f32,
476 pub h: f32,
478 pub u0: f32,
480 pub v0: f32,
482 pub u1: f32,
484 pub v1: f32,
486 pub tint: [f32; 4],
488}
489
490pub fn push_textured_quad(out: &mut Vec<TexVertex>, p: TexQuadParams) {
495 let TexQuadParams {
496 x,
497 y,
498 w,
499 h,
500 u0,
501 v0,
502 u1,
503 v1,
504 tint,
505 } = p;
506 let x1 = x + w;
507 let y1 = y + h;
508 let corners = [
510 ([x, y], [u0, v0]),
511 ([x, y1], [u0, v1]),
512 ([x1, y1], [u1, v1]),
513 ([x, y], [u0, v0]),
514 ([x1, y1], [u1, v1]),
515 ([x1, y], [u1, v0]),
516 ];
517 for (pos, uv) in corners {
518 out.push(TexVertex {
519 position: pos,
520 uv,
521 tint,
522 });
523 }
524}
525
526pub fn push_nine_slice_quads(
541 out: &mut Vec<TexVertex>,
542 dest: [f32; 4],
543 img_w: u32,
544 img_h: u32,
545 insets: [u32; 4],
546 tint: [f32; 4],
547) {
548 let [dst_x, dst_y, dst_w, dst_h] = dest;
549
550 let iw = img_w as f32;
552 let ih = img_h as f32;
553
554 let raw_top = insets[0] as f32;
555 let raw_right = insets[1] as f32;
556 let raw_bottom = insets[2] as f32;
557 let raw_left = insets[3] as f32;
558
559 let lr_sum = raw_left + raw_right;
561 let tb_sum = raw_top + raw_bottom;
562 let (src_left, src_right) = if lr_sum > iw && lr_sum > 0.0 {
563 let scale = iw / lr_sum;
564 (raw_left * scale, raw_right * scale)
565 } else {
566 (raw_left, raw_right)
567 };
568 let (src_top, src_bottom) = if tb_sum > ih && tb_sum > 0.0 {
569 let scale = ih / tb_sum;
570 (raw_top * scale, raw_bottom * scale)
571 } else {
572 (raw_top, raw_bottom)
573 };
574
575 let dst_left = src_left.min(dst_w * 0.5);
579 let dst_right = src_right.min(dst_w - dst_left);
580 let dst_top = src_top.min(dst_h * 0.5);
581 let dst_bottom = src_bottom.min(dst_h - dst_top);
582
583 let u0 = 0.0_f32;
585 let u1 = src_left / iw;
586 let u2 = (iw - src_right) / iw;
587 let u3 = 1.0_f32;
588
589 let v0 = 0.0_f32;
590 let v1 = src_top / ih;
591 let v2 = (ih - src_bottom) / ih;
592 let v3 = 1.0_f32;
593
594 let dx0 = dst_x;
596 let dx1 = dst_x + dst_left;
597 let dx2 = dst_x + dst_w - dst_right;
598 let dx3 = dst_x + dst_w;
599
600 let dy0 = dst_y;
601 let dy1 = dst_y + dst_top;
602 let dy2 = dst_y + dst_h - dst_bottom;
603 let dy3 = dst_y + dst_h;
604
605 let regions: [([f32; 4], [f32; 4]); 9] = [
611 ([dx0, dy0, dx1 - dx0, dy1 - dy0], [u0, v0, u1, v1]), ([dx1, dy0, dx2 - dx1, dy1 - dy0], [u1, v0, u2, v1]), ([dx2, dy0, dx3 - dx2, dy1 - dy0], [u2, v0, u3, v1]), ([dx0, dy1, dx1 - dx0, dy2 - dy1], [u0, v1, u1, v2]), ([dx1, dy1, dx2 - dx1, dy2 - dy1], [u1, v1, u2, v2]), ([dx2, dy1, dx3 - dx2, dy2 - dy1], [u2, v1, u3, v2]), ([dx0, dy2, dx1 - dx0, dy3 - dy2], [u0, v2, u1, v3]), ([dx1, dy2, dx2 - dx1, dy3 - dy2], [u1, v2, u2, v3]), ([dx2, dy2, dx3 - dx2, dy3 - dy2], [u2, v2, u3, v3]), ];
622
623 for ([rx, ry, rw, rh], [ru0, rv0, ru1, rv1]) in regions {
624 if rw <= 0.0 || rh <= 0.0 {
626 continue;
627 }
628 if (ru1 - ru0).abs() <= 0.0 || (rv1 - rv0).abs() <= 0.0 {
629 continue;
630 }
631 push_textured_quad(
632 out,
633 TexQuadParams {
634 x: rx,
635 y: ry,
636 w: rw,
637 h: rh,
638 u0: ru0,
639 v0: rv0,
640 u1: ru1,
641 v1: rv1,
642 tint,
643 },
644 );
645 }
646}
647
648pub fn push_gradient_quad(out: &mut Vec<GradientVertex>, x: f32, y: f32, w: f32, h: f32) {
650 let x1 = x + w;
651 let y1 = y + h;
652 let corners = [[x, y], [x, y1], [x1, y1], [x, y], [x1, y1], [x1, y]];
653 for c in corners {
654 out.push(GradientVertex {
655 position: c,
656 local: c,
657 });
658 }
659}
660
661#[repr(C)]
667#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
668pub struct BlurUniforms {
669 pub direction: [f32; 2],
671 pub texel_size: [f32; 2],
673 pub radius: f32,
675 pub sigma: f32,
677 pub _pad: [f32; 2],
679}
680
681const _: () = assert!(core::mem::size_of::<BlurUniforms>() == 32);
682
683#[repr(C)]
689#[derive(Clone, Copy, Debug, bytemuck::Pod, bytemuck::Zeroable)]
690pub struct CompUniforms {
691 pub tint: [f32; 4],
693 pub texel_size: [f32; 2],
695 pub _pad: [f32; 2],
697}
698
699const _: () = assert!(core::mem::size_of::<CompUniforms>() == 32);
700
701pub fn push_fullscreen_quad(out: &mut Vec<GradientVertex>, w: f32, h: f32) {
710 let corners = [
712 ([0.0_f32, 0.0_f32], [0.0_f32, 0.0_f32]), ([w, 0.0], [w, 0.0]), ([0.0, h], [0.0, h]), ([w, h], [w, h]), ];
717 out.push(GradientVertex {
719 position: corners[0].0,
720 local: corners[0].1,
721 });
722 out.push(GradientVertex {
723 position: corners[1].0,
724 local: corners[1].1,
725 });
726 out.push(GradientVertex {
727 position: corners[2].0,
728 local: corners[2].1,
729 });
730 out.push(GradientVertex {
732 position: corners[1].0,
733 local: corners[1].1,
734 });
735 out.push(GradientVertex {
736 position: corners[3].0,
737 local: corners[3].1,
738 });
739 out.push(GradientVertex {
740 position: corners[2].0,
741 local: corners[2].1,
742 });
743}
744
745#[cfg(test)]
748mod tests {
749 use super::*;
750
751 #[test]
752 fn vertex_size_is_56_bytes() {
753 assert_eq!(core::mem::size_of::<Vertex>(), 56);
754 }
755
756 #[test]
757 fn globals_size_is_16_bytes() {
758 assert_eq!(core::mem::size_of::<Globals>(), 16);
759 }
760
761 #[test]
762 fn gradient_vertex_size_is_16_bytes() {
763 assert_eq!(core::mem::size_of::<GradientVertex>(), 16);
764 }
765
766 #[test]
767 fn gradient_uniforms_size_is_288_bytes() {
768 assert_eq!(core::mem::size_of::<GradientUniforms>(), 288);
769 }
770
771 #[test]
772 fn color_to_f32_maps_full_range() {
773 let white = Vertex::color_to_f32(Color(255, 255, 255, 255));
774 assert!((white[0] - 1.0).abs() < 1e-6);
775 assert!((white[3] - 1.0).abs() < 1e-6);
776 let black = Vertex::color_to_f32(Color(0, 0, 0, 0));
777 assert_eq!(black, [0.0, 0.0, 0.0, 0.0]);
778 }
779
780 #[test]
781 fn rect_quad_emits_six_vertices() {
782 let mut v = Vec::new();
783 push_rect_quad(&mut v, 1.0, 2.0, 3.0, 4.0, Color(255, 0, 0, 255));
784 assert_eq!(v.len(), 6);
785 for vert in &v {
786 assert_eq!(vert.kind, KIND_RECT);
787 }
788 let xs: Vec<f32> = v.iter().map(|vt| vt.position[0]).collect();
789 assert!(xs.contains(&1.0));
790 assert!(xs.contains(&4.0));
791 }
792
793 #[test]
794 fn circle_quad_emits_six_vertices_with_center() {
795 let mut v = Vec::new();
796 push_circle_quad(&mut v, 10.0, 10.0, 5.0, Color(0, 255, 0, 255));
797 assert_eq!(v.len(), 6);
798 for vert in &v {
799 assert_eq!(vert.kind, KIND_CIRCLE);
800 assert_eq!(vert.shape_xy, [10.0, 10.0]);
801 assert!((vert.shape_r - 5.0).abs() < 1e-6);
802 }
803 }
804
805 #[test]
806 fn vertices_are_pod_castable() {
807 let mut v = Vec::new();
808 push_rect_quad(&mut v, 0.0, 0.0, 1.0, 1.0, Color(1, 2, 3, 4));
809 let bytes: &[u8] = bytemuck::cast_slice(&v);
810 assert_eq!(bytes.len(), 6 * 56);
811 }
812
813 #[test]
814 fn rounded_rect_quad_emits_six_vertices() {
815 let mut v = Vec::new();
816 push_rounded_rect_quad(&mut v, 10.0, 10.0, 80.0, 40.0, 8.0, Color(0, 0, 255, 255));
817 assert_eq!(v.len(), 6);
818 for vert in &v {
819 assert_eq!(vert.kind, KIND_ROUNDED_RECT);
820 }
821 }
822
823 #[test]
824 fn rounded_rect_pc_quad_emits_six_vertices() {
825 let mut v = Vec::new();
826 push_rounded_rect_per_corner_quad(
827 &mut v,
828 10.0,
829 10.0,
830 80.0,
831 40.0,
832 [4.0, 8.0, 4.0, 8.0],
833 Color(0, 0, 255, 255),
834 );
835 assert_eq!(v.len(), 6);
836 for vert in &v {
837 assert_eq!(vert.kind, KIND_ROUNDED_RECT_PC);
838 }
839 }
840
841 #[test]
842 fn ellipse_quad_emits_six_vertices() {
843 let mut v = Vec::new();
844 push_ellipse_quad(&mut v, 50.0, 50.0, 30.0, 20.0, Color(255, 255, 0, 255));
845 assert_eq!(v.len(), 6);
846 for vert in &v {
847 assert_eq!(vert.kind, KIND_ELLIPSE);
848 assert!((vert.extra[0] - 30.0).abs() < 1e-4);
849 assert!((vert.extra[1] - 20.0).abs() < 1e-4);
850 }
851 }
852
853 #[test]
854 fn line_quad_emits_six_vertices() {
855 let mut v = Vec::new();
856 push_line_quad(
857 &mut v,
858 LineQuadParams {
859 from_x: 0.0,
860 from_y: 0.0,
861 to_x: 100.0,
862 to_y: 0.0,
863 half_width: 2.0,
864 color: Color(255, 0, 0, 255),
865 aa_smooth: true,
866 },
867 );
868 assert_eq!(v.len(), 6);
869 for vert in &v {
870 assert_eq!(vert.kind, KIND_LINE_SEG);
871 }
872 }
873
874 #[test]
875 fn push_triangle_emits_three_vertices() {
876 let mut v = Vec::new();
877 push_triangle(
878 &mut v,
879 [0.0, 0.0],
880 [10.0, 0.0],
881 [5.0, 8.0],
882 Color(255, 255, 255, 255),
883 );
884 assert_eq!(v.len(), 3);
885 for vert in &v {
886 assert_eq!(vert.kind, KIND_RECT);
887 }
888 }
889
890 #[test]
891 fn gradient_quad_emits_six_vertices() {
892 let mut v = Vec::new();
893 push_gradient_quad(&mut v, 0.0, 0.0, 100.0, 50.0);
894 assert_eq!(v.len(), 6);
895 }
896
897 #[test]
898 fn pack_u16_pair_round_trips() {
899 let packed = pack_u16_pair(42, 1000);
900 let bits = packed.to_bits();
901 let hi = (bits >> 16) as u16;
902 let lo = (bits & 0xffff) as u16;
903 assert_eq!(hi, 42);
904 assert_eq!(lo, 1000);
905 }
906
907 #[test]
908 fn tex_vertex_size_is_32_bytes() {
909 assert_eq!(core::mem::size_of::<TexVertex>(), 32);
910 }
911
912 #[test]
913 fn textured_quad_emits_six_vertices() {
914 let mut v = Vec::new();
915 push_textured_quad(
916 &mut v,
917 TexQuadParams {
918 x: 0.0,
919 y: 0.0,
920 w: 100.0,
921 h: 50.0,
922 u0: 0.0,
923 v0: 0.0,
924 u1: 1.0,
925 v1: 1.0,
926 tint: [1.0, 1.0, 1.0, 1.0],
927 },
928 );
929 assert_eq!(v.len(), 6);
930 for vert in &v {
932 assert_eq!(vert.tint, [1.0, 1.0, 1.0, 1.0]);
933 }
934 let us: Vec<f32> = v.iter().map(|vt| vt.uv[0]).collect();
936 assert!(us.contains(&0.0_f32));
937 assert!(us.contains(&1.0_f32));
938 }
939
940 #[test]
941 fn nine_slice_quads_emit_at_most_54_vertices() {
942 let mut v = Vec::new();
943 push_nine_slice_quads(
945 &mut v,
946 [0.0, 0.0, 64.0, 64.0],
947 12,
948 12,
949 [4, 4, 4, 4],
950 [1.0, 1.0, 1.0, 1.0],
951 );
952 assert_eq!(v.len(), 54);
954 }
955
956 #[test]
957 fn blur_uniforms_size_is_32_bytes() {
958 assert_eq!(core::mem::size_of::<BlurUniforms>(), 32);
959 }
960
961 #[test]
962 fn comp_uniforms_size_is_32_bytes() {
963 assert_eq!(core::mem::size_of::<CompUniforms>(), 32);
964 }
965
966 #[test]
967 fn fullscreen_quad_emits_six_vertices() {
968 let mut v = Vec::new();
969 push_fullscreen_quad(&mut v, 100.0, 200.0);
970 assert_eq!(v.len(), 6);
971 let xs: Vec<f32> = v.iter().map(|vt| vt.position[0]).collect();
973 let ys: Vec<f32> = v.iter().map(|vt| vt.position[1]).collect();
974 assert!(xs.contains(&0.0_f32));
975 assert!(xs.contains(&100.0_f32));
976 assert!(ys.contains(&0.0_f32));
977 assert!(ys.contains(&200.0_f32));
978 }
979
980 #[test]
981 fn nine_slice_degenerate_insets_skip_regions() {
982 let mut v = Vec::new();
983 push_nine_slice_quads(
986 &mut v,
987 [0.0, 0.0, 64.0, 64.0],
988 12,
989 12,
990 [0, 0, 0, 0],
991 [1.0, 1.0, 1.0, 1.0],
992 );
993 assert_eq!(v.len(), 6);
995 }
996}