mirror of
https://github.com/penpot/penpot.git
synced 2026-10-03 01:06:14 +00:00
🐛 Keep adjacent tile compose origins abutted (#11968)
Round the view offset once and place tiles at origin + k * TILE_SIZE so half-pixel pans do not open a 1px background seam between neighbors.
This commit is contained in:
parent
be63107ed6
commit
cba51cfb7e
@ -1852,14 +1852,8 @@ impl TileTextureCache {
|
||||
continue;
|
||||
}
|
||||
|
||||
self.transforms[index] = skia::RSXform::new(
|
||||
dest_scale,
|
||||
0.0,
|
||||
(
|
||||
(x as f32 * self.tile_size - offset.x).round(),
|
||||
(y as f32 * self.tile_size - offset.y).round(),
|
||||
),
|
||||
);
|
||||
let (tx, ty) = tiles::tile_screen_xy(Tile(x, y), self.tile_size, offset);
|
||||
self.transforms[index] = skia::RSXform::new(dest_scale, 0.0, (tx, ty));
|
||||
|
||||
let src = tiles::tile_atlas_content_rect(tile_ref.rect, self.slot_size);
|
||||
self.textures[index].set_ltrb(src.left, src.top, src.right, src.bottom);
|
||||
@ -1903,8 +1897,7 @@ impl TileTextureCache {
|
||||
|
||||
let src = tiles::tile_atlas_content_rect(tile_ref.rect, self.slot_size);
|
||||
let scos = doc_rect.width() * s / src.width();
|
||||
let tx = ((doc_rect.left + viewbox.pan.x) * s).round();
|
||||
let ty = ((doc_rect.top + viewbox.pan.y) * s).round();
|
||||
let (tx, ty) = tiles::doc_rect_screen_xy(doc_rect, viewbox.pan, s);
|
||||
|
||||
transforms.push(skia::RSXform::new(scos, 0.0, (tx, ty)));
|
||||
textures.push(src);
|
||||
@ -1932,8 +1925,7 @@ impl TileTextureCache {
|
||||
}
|
||||
|
||||
let src = tiles::tile_atlas_content_rect(tile_ref.rect, self.slot_size);
|
||||
let tx = ((doc_rect.left + viewbox.pan.x) * s).round();
|
||||
let ty = ((doc_rect.top + viewbox.pan.y) * s).round();
|
||||
let (tx, ty) = tiles::doc_rect_screen_xy(doc_rect, viewbox.pan, s);
|
||||
let scos = doc_rect.width() * s / src.width();
|
||||
|
||||
transforms.push(skia::RSXform::new(scos, 0.0, (tx, ty)));
|
||||
|
||||
@ -211,6 +211,22 @@ impl TileViewbox {
|
||||
|
||||
pub const TILE_SIZE: f32 = 512.;
|
||||
|
||||
/// Screen-space top-left of `tile`: `round(-offset) + tile * tile_size`.
|
||||
pub fn tile_screen_xy(tile: Tile, tile_size: f32, offset: skia::Point) -> (f32, f32) {
|
||||
(
|
||||
(-offset.x).round() + tile.x() as f32 * tile_size,
|
||||
(-offset.y).round() + tile.y() as f32 * tile_size,
|
||||
)
|
||||
}
|
||||
|
||||
/// Screen-space top-left of `doc_rect`: `doc * scale + round(pan * scale)`.
|
||||
pub fn doc_rect_screen_xy(doc_rect: skia::Rect, pan: skia::Point, scale: f32) -> (f32, f32) {
|
||||
(
|
||||
doc_rect.left * scale + (pan.x * scale).round(),
|
||||
doc_rect.top * scale + (pan.y * scale).round(),
|
||||
)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_tile_dimensions() -> skia::ISize {
|
||||
(TILE_SIZE as i32, TILE_SIZE as i32).into()
|
||||
@ -501,6 +517,29 @@ mod tests {
|
||||
use super::*;
|
||||
use skia_safe as skia;
|
||||
|
||||
#[test]
|
||||
fn tile_screen_xy_keeps_neighbors_abutted_at_half_pixel_offset() {
|
||||
let offset = skia::Point::new(103.5, 0.0);
|
||||
let (x0, y0) = tile_screen_xy(Tile(0, 0), TILE_SIZE, offset);
|
||||
let (x1, y1) = tile_screen_xy(Tile(1, 0), TILE_SIZE, offset);
|
||||
let (x2, y2) = tile_screen_xy(Tile(0, 1), TILE_SIZE, offset);
|
||||
assert_eq!(x1 - x0, TILE_SIZE);
|
||||
assert_eq!(y2 - y0, TILE_SIZE);
|
||||
assert_eq!(y1, y0);
|
||||
assert_eq!(x2, x0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doc_rect_screen_xy_keeps_neighbors_abutted_at_half_pixel_pan() {
|
||||
let pan = skia::Point::new(-103.5, 0.0);
|
||||
let scale = 1.0_f32;
|
||||
let a = skia::Rect::from_xywh(0.0, 0.0, TILE_SIZE, TILE_SIZE);
|
||||
let b = skia::Rect::from_xywh(TILE_SIZE, 0.0, TILE_SIZE, TILE_SIZE);
|
||||
let (x0, _) = doc_rect_screen_xy(a, pan, scale);
|
||||
let (x1, _) = doc_rect_screen_xy(b, pan, scale);
|
||||
assert_eq!(x1 - x0, TILE_SIZE);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn atlas_slot_is_full_size_when_tiles_fit() {
|
||||
assert_eq!(tile_atlas_slot_size(64, 4096), 512);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user