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