1use crate::api::TileSize;
6use crate::api::units::*;
7use crate::segment::EdgeMask;
8use euclid::{point2, size2};
9use std::i32;
10use std::ops::Range;
11
12pub fn simplify_repeated_primitive(
18 stretch_size: &LayoutSize,
19 tile_spacing: &mut LayoutSize,
20 prim_rect: &mut LayoutRect,
21) {
22 let stride = *stretch_size + *tile_spacing;
23
24 if stride.width >= prim_rect.width() {
25 tile_spacing.width = 0.0;
26 prim_rect.max.x = f32::min(prim_rect.min.x + stretch_size.width, prim_rect.max.x);
27 }
28 if stride.height >= prim_rect.height() {
29 tile_spacing.height = 0.0;
30 prim_rect.max.y = f32::min(prim_rect.min.y + stretch_size.height, prim_rect.max.y);
31 }
32}
33
34pub struct Repetition {
35 pub origin: LayoutPoint,
36 pub edge_flags: EdgeMask,
37}
38
39pub struct RepetitionIterator {
40 current_x: i32,
41 x_count: i32,
42 current_y: i32,
43 y_count: i32,
44 row_flags: EdgeMask,
45 current_origin: LayoutPoint,
46 initial_origin: LayoutPoint,
47 stride: LayoutSize,
48}
49
50impl Iterator for RepetitionIterator {
51 type Item = Repetition;
52
53 fn next(&mut self) -> Option<Self::Item> {
54 if self.current_x >= self.x_count {
55 self.current_y += 1;
56 if self.current_y >= self.y_count {
57 return None;
58 }
59 self.current_x = 0;
60
61 self.row_flags = EdgeMask::empty();
62 if self.current_y == self.y_count - 1 {
63 self.row_flags |= EdgeMask::BOTTOM;
64 }
65
66 self.current_origin.x = self.initial_origin.x;
67 self.current_origin.y += self.stride.height;
68 }
69
70 let mut edge_flags = self.row_flags;
71 if self.current_x == 0 {
72 edge_flags |= EdgeMask::LEFT;
73 }
74
75 if self.current_x == self.x_count - 1 {
76 edge_flags |= EdgeMask::RIGHT;
77 }
78
79 let repetition = Repetition {
80 origin: self.current_origin,
81 edge_flags,
82 };
83
84 self.current_origin.x += self.stride.width;
85 self.current_x += 1;
86
87 Some(repetition)
88 }
89}
90
91pub fn repetitions(
92 prim_rect: &LayoutRect,
93 visible_rect: &LayoutRect,
94 stride: LayoutSize,
95) -> RepetitionIterator {
96 let visible_rect = match prim_rect.intersection(&visible_rect) {
97 Some(rect) => rect,
98 None => {
99 return RepetitionIterator {
100 current_origin: LayoutPoint::zero(),
101 initial_origin: LayoutPoint::zero(),
102 current_x: 0,
103 current_y: 0,
104 x_count: 0,
105 y_count: 0,
106 stride,
107 row_flags: EdgeMask::empty(),
108 }
109 }
110 };
111
112 assert!(stride.width > 0.0);
113 assert!(stride.height > 0.0);
114
115 let nx = if visible_rect.min.x > prim_rect.min.x {
116 f32::floor((visible_rect.min.x - prim_rect.min.x) / stride.width)
117 } else {
118 0.0
119 };
120
121 let ny = if visible_rect.min.y > prim_rect.min.y {
122 f32::floor((visible_rect.min.y - prim_rect.min.y) / stride.height)
123 } else {
124 0.0
125 };
126
127 let x0 = prim_rect.min.x + nx * stride.width;
128 let y0 = prim_rect.min.y + ny * stride.height;
129
130 let x_most = visible_rect.max.x;
131 let y_most = visible_rect.max.y;
132
133 let mut x_count = f32::ceil((x_most - x0) / stride.width);
134 let mut y_count = f32::ceil((y_most - y0) / stride.height);
135
136 let valid = x_count.is_finite()
139 & y_count.is_finite()
140 & stride.is_finite();
141
142 if !valid {
143 x_count = 0.0;
144 y_count = 0.0;
145 }
146
147
148 let mut row_flags = EdgeMask::TOP;
149 if y_count as i32 == 1 {
150 row_flags |= EdgeMask::BOTTOM;
151 }
152
153
154 RepetitionIterator {
155 current_origin: LayoutPoint::new(x0, y0),
156 initial_origin: LayoutPoint::new(x0, y0),
157 current_x: 0,
158 current_y: 0,
159 x_count: x_count as i32,
160 y_count: y_count as i32,
161 row_flags,
162 stride,
163 }
164}
165
166#[derive(Debug)]
167pub struct Tile {
168 pub rect: LayoutRect,
169 pub offset: TileOffset,
170 pub edge_flags: EdgeMask,
171}
172
173#[derive(Debug)]
174pub struct TileIteratorExtent {
175 tile_range: Range<i32>,
177 image_tiles: Range<i32>,
179 first_tile_layout_size: f32,
181 last_tile_layout_size: f32,
183 layout_tiling_origin: f32,
185 layout_prim_start: f32,
187}
188
189#[derive(Debug)]
190pub struct TileIterator {
191 current_tile: TileOffset,
192 x: TileIteratorExtent,
193 y: TileIteratorExtent,
194 regular_tile_size: LayoutSize,
195}
196
197impl Iterator for TileIterator {
198 type Item = Tile;
199
200 fn next(&mut self) -> Option<Self::Item> {
201 if self.current_tile.x >= self.x.tile_range.end {
203 self.current_tile.y += 1;
204 self.current_tile.x = self.x.tile_range.start;
205 }
206
207 if self.current_tile.x >= self.x.tile_range.end || self.current_tile.y >= self.y.tile_range.end {
210 return None;
211 }
212
213 let tile_offset = self.current_tile;
214
215 let mut segment_rect = LayoutRect::from_origin_and_size(
216 LayoutPoint::new(
217 self.x.layout_tiling_origin + tile_offset.x as f32 * self.regular_tile_size.width,
218 self.y.layout_tiling_origin + tile_offset.y as f32 * self.regular_tile_size.height,
219 ),
220 self.regular_tile_size,
221 );
222
223 let mut edge_flags = EdgeMask::empty();
224
225 if tile_offset.x == self.x.image_tiles.start {
226 edge_flags |= EdgeMask::LEFT;
227 segment_rect.min.x = self.x.layout_prim_start;
228 segment_rect.max.x = segment_rect.min.x + self.x.first_tile_layout_size;
230 }
231 if tile_offset.x == self.x.image_tiles.end - 1 {
232 edge_flags |= EdgeMask::RIGHT;
233 segment_rect.max.x = segment_rect.min.x + self.x.last_tile_layout_size;
234 }
235
236 if tile_offset.y == self.y.image_tiles.start {
237 segment_rect.min.y = self.y.layout_prim_start;
238 segment_rect.max.y = segment_rect.min.y + self.y.first_tile_layout_size;
239 edge_flags |= EdgeMask::TOP;
240 }
241 if tile_offset.y == self.y.image_tiles.end - 1 {
242 segment_rect.max.y = segment_rect.min.y + self.y.last_tile_layout_size;
243 edge_flags |= EdgeMask::BOTTOM;
244 }
245
246 assert!(tile_offset.y < self.y.tile_range.end);
247 let tile = Tile {
248 rect: segment_rect,
249 offset: tile_offset,
250 edge_flags,
251 };
252
253 self.current_tile.x += 1;
254
255 Some(tile)
256 }
257}
258
259pub fn tiles(
260 prim_rect: &LayoutRect,
261 visible_rect: &LayoutRect,
262 image_rect: &DeviceIntRect,
263 device_tile_size: i32,
264) -> TileIterator {
265 let visible_rect = match prim_rect.intersection(&visible_rect) {
295 Some(rect) => rect,
296 None => {
297 return TileIterator {
298 current_tile: TileOffset::zero(),
299 x: TileIteratorExtent {
300 tile_range: 0..0,
301 image_tiles: 0..0,
302 first_tile_layout_size: 0.0,
303 last_tile_layout_size: 0.0,
304 layout_tiling_origin: 0.0,
305 layout_prim_start: prim_rect.min.x,
306 },
307 y: TileIteratorExtent {
308 tile_range: 0..0,
309 image_tiles: 0..0,
310 first_tile_layout_size: 0.0,
311 last_tile_layout_size: 0.0,
312 layout_tiling_origin: 0.0,
313 layout_prim_start: prim_rect.min.y,
314 },
315 regular_tile_size: LayoutSize::zero(),
316 }
317 }
318 };
319
320 let layout_tile_size = LayoutSize::new(
322 device_tile_size as f32 / image_rect.width() as f32 * prim_rect.width(),
323 device_tile_size as f32 / image_rect.height() as f32 * prim_rect.height(),
324 );
325
326 let x_extent = tiles_1d(
330 layout_tile_size.width,
331 visible_rect.x_range(),
332 prim_rect.min.x,
333 image_rect.x_range(),
334 device_tile_size,
335 );
336
337 let y_extent = tiles_1d(
338 layout_tile_size.height,
339 visible_rect.y_range(),
340 prim_rect.min.y,
341 image_rect.y_range(),
342 device_tile_size,
343 );
344
345 TileIterator {
346 current_tile: point2(
347 x_extent.tile_range.start,
348 y_extent.tile_range.start,
349 ),
350 x: x_extent,
351 y: y_extent,
352 regular_tile_size: layout_tile_size,
353 }
354}
355
356fn tiles_1d(
361 layout_tile_size: f32,
362 layout_visible_range: Range<f32>,
363 layout_prim_start: f32,
364 device_image_range: Range<i32>,
365 device_tile_size: i32,
366) -> TileIteratorExtent {
367 debug_assert!(layout_tile_size > 0.0);
369 debug_assert!(layout_visible_range.end >= layout_visible_range.start);
370 debug_assert!(device_image_range.end > device_image_range.start);
371 debug_assert!(device_tile_size > 0);
372
373 let first_tile_device_size = first_tile_size_1d(&device_image_range, device_tile_size);
375 let last_tile_device_size = last_tile_size_1d(&device_image_range, device_tile_size);
376
377 let image_tiles = tile_range_1d(&device_image_range, device_tile_size);
380
381 let layout_offset = device_image_range.start as f32 * layout_tile_size / device_tile_size as f32;
383 let layout_tiling_origin = layout_prim_start - layout_offset;
385
386 let visible_tiles_start = f32::floor((layout_visible_range.start - layout_tiling_origin) / layout_tile_size) as i32;
388 let visible_tiles_end = f32::ceil((layout_visible_range.end - layout_tiling_origin) / layout_tile_size) as i32;
389
390 let mut tiles_start = i32::max(image_tiles.start, visible_tiles_start);
392 let tiles_end = i32::min(image_tiles.end, visible_tiles_end);
393 if tiles_start > tiles_end {
394 tiles_start = tiles_end;
395 }
396
397 let first_tile_layout_size = if tiles_start == image_tiles.start {
399 first_tile_device_size as f32 * layout_tile_size / device_tile_size as f32
400 } else {
401 layout_tile_size
403 };
404
405 let last_tile_layout_size = if tiles_end == image_tiles.end {
407 last_tile_device_size as f32 * layout_tile_size / device_tile_size as f32
408 } else {
409 layout_tile_size
410 };
411
412 TileIteratorExtent {
413 tile_range: tiles_start..tiles_end,
414 image_tiles,
415 first_tile_layout_size,
416 last_tile_layout_size,
417 layout_tiling_origin,
418 layout_prim_start,
419 }
420}
421
422fn tile_range_1d(
439 image_range: &Range<i32>,
440 regular_tile_size: i32,
441) -> Range<i32> {
442 let mut start = image_range.start / regular_tile_size;
446 if image_range.start % regular_tile_size < 0 {
447 start -= 1;
448 }
449
450 let mut end = image_range.end / regular_tile_size;
451 if image_range.end % regular_tile_size > 0 {
452 end += 1;
453 }
454
455 start..end
456}
457
458fn first_tile_size_1d(
463 image_range: &Range<i32>,
464 regular_tile_size: i32,
465) -> i32 {
466 let image_size = image_range.end - image_range.start;
468 i32::min(
469 match image_range.start % regular_tile_size {
470 0 => regular_tile_size,
473 m if m > 0 => regular_tile_size - m,
477 m => -m,
481 },
482 image_size
483 )
484}
485
486fn last_tile_size_1d(
491 image_range: &Range<i32>,
492 regular_tile_size: i32,
493) -> i32 {
494 let image_size = image_range.end - image_range.start;
496 i32::min(
497 match image_range.end % regular_tile_size {
498 0 => regular_tile_size,
501 m if m < 0 => regular_tile_size + m,
505 m => m,
509 },
510 image_size,
511 )
512}
513
514pub fn compute_tile_rect(
515 image_rect: &DeviceIntRect,
516 regular_tile_size: TileSize,
517 tile: TileOffset,
518) -> DeviceIntRect {
519 let regular_tile_size = regular_tile_size as i32;
520 DeviceIntRect::from_origin_and_size(
521 point2(
522 compute_tile_origin_1d(image_rect.x_range(), regular_tile_size, tile.x as i32),
523 compute_tile_origin_1d(image_rect.y_range(), regular_tile_size, tile.y as i32),
524 ),
525 size2(
526 compute_tile_size_1d(image_rect.x_range(), regular_tile_size, tile.x as i32),
527 compute_tile_size_1d(image_rect.y_range(), regular_tile_size, tile.y as i32),
528 ),
529 )
530}
531
532fn compute_tile_origin_1d(
533 img_range: Range<i32>,
534 regular_tile_size: i32,
535 tile_offset: i32,
536) -> i32 {
537 let tile_range = tile_range_1d(&img_range, regular_tile_size);
538 if tile_offset == tile_range.start {
539 img_range.start
540 } else {
541 tile_offset * regular_tile_size
542 }
543}
544
545pub fn compute_tile_size(
547 image_rect: &DeviceIntRect,
548 regular_tile_size: TileSize,
549 tile: TileOffset,
550) -> DeviceIntSize {
551 let regular_tile_size = regular_tile_size as i32;
552 size2(
553 compute_tile_size_1d(image_rect.x_range(), regular_tile_size, tile.x as i32),
554 compute_tile_size_1d(image_rect.y_range(), regular_tile_size, tile.y as i32),
555 )
556}
557
558fn compute_tile_size_1d(
559 img_range: Range<i32>,
560 regular_tile_size: i32,
561 tile_offset: i32,
562) -> i32 {
563 let tile_range = tile_range_1d(&img_range, regular_tile_size);
564
565 let actual_size = if tile_offset == tile_range.start {
568 first_tile_size_1d(&img_range, regular_tile_size)
569 } else if tile_offset == tile_range.end - 1 {
570 last_tile_size_1d(&img_range, regular_tile_size)
571 } else {
572 regular_tile_size
573 };
574
575 assert!(actual_size > 0);
576
577 actual_size
578}
579
580pub fn compute_tile_range(
581 visible_area: &DeviceIntRect,
582 tile_size: u16,
583) -> TileRange {
584 let tile_size = tile_size as i32;
585 let x_range = tile_range_1d(&visible_area.x_range(), tile_size);
586 let y_range = tile_range_1d(&visible_area.y_range(), tile_size);
587
588 TileRange {
589 min: point2(x_range.start, y_range.start),
590 max: point2(x_range.end, y_range.end),
591 }
592}
593
594pub fn for_each_tile_in_range(
595 range: &TileRange,
596 mut callback: impl FnMut(TileOffset),
597) {
598 for y in range.y_range() {
599 for x in range.x_range() {
600 callback(point2(x, y));
601 }
602 }
603}
604
605pub fn compute_valid_tiles_if_bounds_change(
606 prev_rect: &DeviceIntRect,
607 new_rect: &DeviceIntRect,
608 tile_size: u16,
609) -> Option<TileRange> {
610 let intersection = match prev_rect.intersection(new_rect) {
611 Some(rect) => rect,
612 None => {
613 return Some(TileRange::zero());
614 }
615 };
616
617 let left = prev_rect.min.x != new_rect.min.x;
618 let right = prev_rect.max.x != new_rect.max.x;
619 let top = prev_rect.min.y != new_rect.min.y;
620 let bottom = prev_rect.max.y != new_rect.max.y;
621
622 if !left && !right && !top && !bottom {
623 return None;
625 }
626
627 let tw = 1.0 / (tile_size as f32);
628 let th = 1.0 / (tile_size as f32);
629
630 let tiles = intersection
631 .cast::<f32>()
632 .scale(tw, th);
633
634 let min_x = if left { f32::ceil(tiles.min.x) } else { f32::floor(tiles.min.x) };
635 let min_y = if top { f32::ceil(tiles.min.y) } else { f32::floor(tiles.min.y) };
636 let max_x = if right { f32::floor(tiles.max.x) } else { f32::ceil(tiles.max.x) };
637 let max_y = if bottom { f32::floor(tiles.max.y) } else { f32::ceil(tiles.max.y) };
638
639 Some(TileRange {
640 min: point2(min_x as i32, min_y as i32),
641 max: point2(max_x as i32, max_y as i32),
642 })
643}
644
645#[cfg(test)]
646mod tests {
647 use super::*;
648 use std::collections::HashSet;
649 use euclid::rect;
650
651 fn checked_for_each_tile(
653 prim_rect: &LayoutRect,
654 visible_rect: &LayoutRect,
655 device_image_rect: &DeviceIntRect,
656 device_tile_size: i32,
657 callback: &mut dyn FnMut(&LayoutRect, TileOffset, EdgeMask),
658 ) {
659 let mut coverage = LayoutRect::zero();
660 let mut seen_tiles = HashSet::new();
661 for tile in tiles(
662 prim_rect,
663 visible_rect,
664 device_image_rect,
665 device_tile_size,
666 ) {
667 assert!(!seen_tiles.contains(&tile.offset));
669 seen_tiles.insert(tile.offset);
670 coverage = coverage.union(&tile.rect);
671 assert!(prim_rect.contains_box(&tile.rect));
672 callback(&tile.rect, tile.offset, tile.edge_flags);
673 }
674 assert!(prim_rect.contains_box(&coverage));
675 assert!(coverage.contains_box(&visible_rect.intersection(&prim_rect).unwrap_or(LayoutRect::zero())));
676 }
677
678 #[test]
679 fn basic() {
680 let mut count = 0;
681 checked_for_each_tile(&rect(0., 0., 1000., 1000.).to_box2d(),
682 &rect(75., 75., 400., 400.).to_box2d(),
683 &rect(0, 0, 400, 400).to_box2d(),
684 36,
685 &mut |_tile_rect, _tile_offset, _tile_flags| {
686 count += 1;
687 },
688 );
689 assert_eq!(count, 36);
690 }
691
692 #[test]
693 fn empty() {
694 let mut count = 0;
695 checked_for_each_tile(&rect(0., 0., 74., 74.).to_box2d(),
696 &rect(75., 75., 400., 400.).to_box2d(),
697 &rect(0, 0, 400, 400).to_box2d(),
698 36,
699 &mut |_tile_rect, _tile_offset, _tile_flags| {
700 count += 1;
701 },
702 );
703 assert_eq!(count, 0);
704 }
705
706 #[test]
707 fn test_tiles_1d() {
708 let result = tiles_1d(64.0, -10000.0..10000.0, 0.0, 0..64, 64);
710 assert_eq!(result.tile_range.start, 0);
711 assert_eq!(result.tile_range.end, 1);
712 assert_eq!(result.first_tile_layout_size, 64.0);
713 assert_eq!(result.last_tile_layout_size, 64.0);
714
715 let result = tiles_1d(64.0, -10000.0..10000.0, -64.0, -64..0, 64);
717 assert_eq!(result.tile_range.start, -1);
718 assert_eq!(result.tile_range.end, 0);
719 assert_eq!(result.first_tile_layout_size, 64.0);
720 assert_eq!(result.last_tile_layout_size, 64.0);
721
722 let result = tiles_1d(64.0, -10000.0..10000.0, -64.0, -64..64, 64);
724 assert_eq!(result.tile_range.start, -1);
725 assert_eq!(result.tile_range.end, 1);
726 assert_eq!(result.first_tile_layout_size, 64.0);
727 assert_eq!(result.last_tile_layout_size, 64.0);
728
729 let result = tiles_1d(64.0, -100.0..10.0, 64.0, 64..310, 64);
731 assert_eq!(result.tile_range.start, result.tile_range.end);
732
733 let result = tiles_1d(64.0, 10.0..10000.0, -64.0, -64..64, 64);
736 assert_eq!(result.tile_range.start, 0);
737 assert_eq!(result.tile_range.end, 1);
738 assert_eq!(result.first_tile_layout_size, 64.0);
739 assert_eq!(result.last_tile_layout_size, 64.0);
740 let result = tiles_1d(64.0, -10000.0..-10.0, -64.0, -64..64, 64);
741 assert_eq!(result.tile_range.start, -1);
742 assert_eq!(result.tile_range.end, 0);
743 assert_eq!(result.first_tile_layout_size, 64.0);
744 assert_eq!(result.last_tile_layout_size, 64.0);
745
746 let result = tiles_1d(128.0, -10000.0..10000.0, -64.0, -64..32, 64);
749 assert_eq!(result.tile_range.start, -1);
750 assert_eq!(result.tile_range.end, 1);
751 assert_eq!(result.first_tile_layout_size, 128.0);
752 assert_eq!(result.last_tile_layout_size, 64.0);
753
754 let result = tiles_1d(10.0, 0.0..20.0, 0.0, 0..64, 64);
756 assert_eq!(result.tile_range.start, 0);
757 assert_eq!(result.tile_range.end, 1);
758 assert_eq!(result.first_tile_layout_size, 10.0);
759 assert_eq!(result.last_tile_layout_size, 10.0);
760
761 let result = tiles_1d(10.0, -20.0..0.0, -20.0, 0..64, 64);
763 assert_eq!(result.tile_range.start, 0);
764 assert_eq!(result.tile_range.end, 1);
765 assert_eq!(result.first_tile_layout_size, 10.0);
766 assert_eq!(result.last_tile_layout_size, 10.0);
767 }
768
769 #[test]
770 fn test_tile_range_1d() {
771 assert_eq!(tile_range_1d(&(0..256), 256), 0..1);
772 assert_eq!(tile_range_1d(&(0..257), 256), 0..2);
773 assert_eq!(tile_range_1d(&(-1..257), 256), -1..2);
774 assert_eq!(tile_range_1d(&(-256..256), 256), -1..1);
775 assert_eq!(tile_range_1d(&(-20..-10), 6), -4..-1);
776 assert_eq!(tile_range_1d(&(20..100), 256), 0..1);
777 }
778
779 #[test]
780 fn test_first_last_tile_size_1d() {
781 assert_eq!(first_tile_size_1d(&(0..10), 64), 10);
782 assert_eq!(first_tile_size_1d(&(-20..0), 64), 20);
783
784 assert_eq!(last_tile_size_1d(&(0..10), 64), 10);
785 assert_eq!(last_tile_size_1d(&(-20..0), 64), 20);
786 }
787
788 #[test]
789 fn doubly_partial_tiles() {
790 assert_eq!(first_tile_size_1d(&(300..310), 64), 10);
795 assert_eq!(first_tile_size_1d(&(-20..-10), 64), 10);
796
797 assert_eq!(last_tile_size_1d(&(300..310), 64), 10);
798 assert_eq!(last_tile_size_1d(&(-20..-10), 64), 10);
799
800
801 let result = tiles_1d(64.0, -10000.0..10000.0, 0.0, 300..310, 64);
803 assert_eq!(result.tile_range.start, 4);
804 assert_eq!(result.tile_range.end, 5);
805 assert_eq!(result.first_tile_layout_size, 10.0);
806 assert_eq!(result.last_tile_layout_size, 10.0);
807 }
808
809 #[test]
810 fn smaller_than_tile_size_at_origin() {
811 let r = compute_tile_rect(
812 &rect(0, 0, 80, 80).to_box2d(),
813 256,
814 point2(0, 0),
815 );
816
817 assert_eq!(r, rect(0, 0, 80, 80).to_box2d());
818 }
819
820 #[test]
821 fn smaller_than_tile_size_with_offset() {
822 let r = compute_tile_rect(
823 &rect(20, 20, 80, 80).to_box2d(),
824 256,
825 point2(0, 0),
826 );
827
828 assert_eq!(r, rect(20, 20, 80, 80).to_box2d());
829 }
830}