mirror of
https://github.com/penpot/penpot.git
synced 2026-06-26 09:12:06 +00:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
2eb9423963
@ -78,6 +78,11 @@
|
||||
text-transform-attrs
|
||||
text-fills))
|
||||
|
||||
(def text-span-attrs
|
||||
"Inline text span attrs. Line-height is paragraph-level in the DOM editor;
|
||||
it may still be stored redundantly on span nodes."
|
||||
(vec (remove #{:line-height} text-node-attrs)))
|
||||
|
||||
(defn text-node-attr?
|
||||
[attr]
|
||||
(d/index-of text-node-attrs attr))
|
||||
@ -317,9 +322,16 @@
|
||||
"Given two content text structures, conformed by maps and vectors,
|
||||
compare them, and returns a set with the attributes that have changed.
|
||||
This is independent of the text structure, so if the structure changes
|
||||
but the attributes are the same, it will return an empty set."
|
||||
but the attributes are the same, it will return an empty set.
|
||||
|
||||
Line-height on text nodes is ignored: it is a paragraph-level attribute
|
||||
and may be stored redundantly on spans (e.g. after token apply)."
|
||||
[a b]
|
||||
(let [diff-attrs (compare-text-content a b
|
||||
(let [strip-span-line-height
|
||||
#(transform-nodes is-text-node? (fn [node] (dissoc node :line-height)) %)
|
||||
a (strip-span-line-height a)
|
||||
b (strip-span-line-height b)
|
||||
diff-attrs (compare-text-content a b
|
||||
{:text-cb identity
|
||||
:attribute-cb (fn [acc attr] (conj acc attr))})]
|
||||
(if-not (contains? diff-attrs :text-content-structure)
|
||||
|
||||
@ -76,8 +76,31 @@
|
||||
(assoc-in content-base [:children 0 :children 0 :children 0 :font-family] "Arial"))
|
||||
|
||||
(def content-changed-line-height
|
||||
(assoc-in content-base [:children 0 :children 0 :line-height] "1.5"))
|
||||
|
||||
(def content-redundant-span-line-height
|
||||
(assoc-in content-base [:children 0 :children 0 :children 0 :line-height] "1.5"))
|
||||
|
||||
;; Token apply may store line-height on paragraph and spans; after a DOM
|
||||
;; round-trip spans no longer carry it (paragraph-level in the editor).
|
||||
(def content-token-like-line-height
|
||||
(-> content-base
|
||||
(assoc-in [:children 0 :children 0 :line-height] 1.4)
|
||||
(assoc-in [:children 0 :children 0 :children 0 :line-height] 1.4)))
|
||||
|
||||
(def content-after-editor-roundtrip
|
||||
(update-in content-token-like-line-height
|
||||
[:children 0 :children 0 :children 0]
|
||||
dissoc :line-height))
|
||||
|
||||
;; from_dom used to merge default nil typography refs on import.
|
||||
(def content-explicit-nil-typography-refs
|
||||
(-> content-base
|
||||
(assoc-in [:children 0 :children 0 :typography-ref-id] nil)
|
||||
(assoc-in [:children 0 :children 0 :typography-ref-file] nil)
|
||||
(assoc-in [:children 0 :children 0 :children 0 :typography-ref-id] nil)
|
||||
(assoc-in [:children 0 :children 0 :children 0 :typography-ref-file] nil)))
|
||||
|
||||
(def content-changed-letter-spacing
|
||||
(assoc-in content-base [:children 0 :children 0 :children 0 :letter-spacing] "2"))
|
||||
|
||||
@ -185,6 +208,10 @@
|
||||
;; Other text-node-attr categories
|
||||
attrs-font-family (cttx/get-diff-attrs content-base content-changed-font-family)
|
||||
attrs-line-height (cttx/get-diff-attrs content-base content-changed-line-height)
|
||||
attrs-span-line-height (cttx/get-diff-attrs content-base content-redundant-span-line-height)
|
||||
attrs-roundtrip-line-height (cttx/get-diff-attrs content-token-like-line-height
|
||||
content-after-editor-roundtrip)
|
||||
attrs-nil-typography-refs (cttx/get-diff-attrs content-base content-explicit-nil-typography-refs)
|
||||
attrs-letter-spacing (cttx/get-diff-attrs content-base content-changed-letter-spacing)
|
||||
attrs-text-decoration (cttx/get-diff-attrs content-base content-changed-text-decoration)
|
||||
attrs-text-transform (cttx/get-diff-attrs content-base content-changed-text-transform)
|
||||
@ -215,6 +242,9 @@
|
||||
;; Each text-node-attr category reports correct attr key
|
||||
(t/is (= #{:font-family} attrs-font-family))
|
||||
(t/is (= #{:line-height} attrs-line-height))
|
||||
(t/is (= #{} attrs-span-line-height))
|
||||
(t/is (= #{} attrs-roundtrip-line-height))
|
||||
(t/is (= #{} attrs-nil-typography-refs))
|
||||
(t/is (= #{:letter-spacing} attrs-letter-spacing))
|
||||
(t/is (= #{:text-decoration} attrs-text-decoration))
|
||||
(t/is (= #{:text-transform} attrs-text-transform))
|
||||
|
||||
@ -51,13 +51,19 @@
|
||||
[_ style-decode] (get styles/mapping key)]
|
||||
(style-decode (.getPropertyValue style style-name)))
|
||||
(let [style-name (styles/get-style-name key)]
|
||||
(styles/normalize-attr-value key (.getPropertyValue style style-name))))]
|
||||
(assoc acc key (if (value-empty? value) (get defaults key) value))))
|
||||
(styles/normalize-attr-value key (.getPropertyValue style style-name))))
|
||||
default (get defaults key)
|
||||
final-value (if (value-empty? value) default value)]
|
||||
;; Omit attrs with no CSS value when the default is nil (e.g.
|
||||
;; typography-ref-id). Avoids polluting round-tripped content.
|
||||
(if (and (value-empty? value) (nil? default))
|
||||
acc
|
||||
(assoc acc key final-value))))
|
||||
{} attrs)))
|
||||
|
||||
(defn get-text-span-styles
|
||||
[element]
|
||||
(get-attrs-from-styles element txt/text-node-attrs (txt/get-default-text-attrs)))
|
||||
(get-attrs-from-styles element txt/text-span-attrs (txt/get-default-text-attrs)))
|
||||
|
||||
(defn get-paragraph-styles
|
||||
[element]
|
||||
|
||||
@ -94,9 +94,8 @@
|
||||
|
||||
(defn get-text-span-styles
|
||||
[inline paragraph]
|
||||
(let [node (if (= "" (:text inline)) paragraph inline)
|
||||
styles (get-styles-from-attrs node txt/text-node-attrs txt/default-text-attrs)]
|
||||
(dissoc styles :line-height)))
|
||||
(let [node (if (= "" (:text inline)) paragraph inline)]
|
||||
(get-styles-from-attrs node txt/text-span-attrs txt/default-text-attrs)))
|
||||
|
||||
(defn normalize-spaces
|
||||
"Add zero-width spaces after forward slashes to enable word breaking"
|
||||
|
||||
@ -2003,89 +2003,12 @@ impl RenderState {
|
||||
pub fn render_from_cache(&mut self, shapes: ShapesPoolRef) {
|
||||
let _start = performance::begin_timed_log!("render_from_cache");
|
||||
performance::begin_measure!("render_from_cache");
|
||||
let bg_color = self.background_color;
|
||||
|
||||
// During fast mode (pan/zoom), if a previous full-quality render still has pending tiles,
|
||||
// always prefer the persistent atlas. The atlas is incrementally updated as tiles finish,
|
||||
// and drawing from it avoids mixing a partially-updated Cache surface with missing tiles.
|
||||
if self.options.is_fast_mode() && !self.surfaces.atlas.is_empty() {
|
||||
self.surfaces
|
||||
.draw_atlas_to_backbuffer(self.viewbox, bg_color);
|
||||
|
||||
self.present_frame(shapes);
|
||||
performance::end_measure!("render_from_cache");
|
||||
performance::end_timed_log!("render_from_cache", _start);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we have a valid cached viewbox (non-zero dimensions indicate valid cache)
|
||||
if self.cached_viewbox.area.width() > 0.0 {
|
||||
// Scale and translate the target according to the cached data
|
||||
let navigate_zoom = self.viewbox.zoom / self.cached_viewbox.zoom;
|
||||
|
||||
let interest = self.options.dpr_viewport_interest_area_threshold;
|
||||
let TileRect(start_tile_x, start_tile_y, _, _) =
|
||||
tiles::get_tiles_for_viewbox_with_interest(&self.cached_viewbox, interest);
|
||||
let offset_x = self.viewbox.area.left * self.cached_viewbox.zoom * self.options.dpr;
|
||||
let offset_y = self.viewbox.area.top * self.cached_viewbox.zoom * self.options.dpr;
|
||||
let translate_x = (start_tile_x as f32 * tiles::TILE_SIZE) - offset_x;
|
||||
let translate_y = (start_tile_y as f32 * tiles::TILE_SIZE) - offset_y;
|
||||
|
||||
// For zoom-out, prefer cache only if it fully covers the viewport.
|
||||
// Otherwise, atlas will provide a more correct full-viewport preview.
|
||||
let zooming_out = self.viewbox.zoom < self.cached_viewbox.zoom;
|
||||
if zooming_out {
|
||||
let cache_dim = self.surfaces.cache_dimensions();
|
||||
let cache_w = cache_dim.width as f32;
|
||||
let cache_h = cache_dim.height as f32;
|
||||
|
||||
// Viewport in target pixels.
|
||||
let vw = self.viewbox.dpr_width().max(1.0);
|
||||
let vh = self.viewbox.dpr_height().max(1.0);
|
||||
|
||||
// Inverse-map viewport corners into cache coordinates.
|
||||
// target = (cache * navigate_zoom) translated by (translate_x, translate_y) (in cache coords).
|
||||
// => cache = (target / navigate_zoom) - translate
|
||||
let inv = if navigate_zoom.abs() > f32::EPSILON {
|
||||
1.0 / navigate_zoom
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
// let cx0 = (0.0 * inv) - translate_x;
|
||||
// let cy0 = (0.0 * inv) - translate_y;
|
||||
// NOTA: 0.0 * inv => siempre 0
|
||||
let cx0 = -translate_x;
|
||||
let cy0 = -translate_y;
|
||||
let cx1 = (vw * inv) - translate_x;
|
||||
let cy1 = (vh * inv) - translate_y;
|
||||
|
||||
let min_x = cx0.min(cx1);
|
||||
let min_y = cy0.min(cy1);
|
||||
let max_x = cx0.max(cx1);
|
||||
let max_y = cy0.max(cy1);
|
||||
|
||||
let cache_covers =
|
||||
min_x >= 0.0 && min_y >= 0.0 && max_x <= cache_w && max_y <= cache_h;
|
||||
if !cache_covers {
|
||||
// Early return only if atlas exists; otherwise keep cache path.
|
||||
if !self.surfaces.atlas.is_empty() {
|
||||
self.surfaces
|
||||
.draw_atlas_to_backbuffer(self.viewbox, bg_color);
|
||||
|
||||
self.present_frame(shapes);
|
||||
performance::end_measure!("render_from_cache");
|
||||
performance::end_timed_log!("render_from_cache", _start);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw directly from cache surface, avoiding snapshot overhead
|
||||
self.surfaces.draw_cache_to_backbuffer();
|
||||
|
||||
self.present_frame(shapes);
|
||||
}
|
||||
self.surfaces.draw_combined_atlas_to_backbuffer(
|
||||
&self.viewbox,
|
||||
&self.tile_viewbox,
|
||||
self.background_color,
|
||||
);
|
||||
self.present_frame(shapes);
|
||||
|
||||
performance::end_measure!("render_from_cache");
|
||||
performance::end_timed_log!("render_from_cache", _start);
|
||||
|
||||
@ -28,13 +28,17 @@ const CANVAS_BORDER_RADIUS: f32 = 12.0;
|
||||
// distinct offsets for the two and we mirror that.
|
||||
const SELECTION_LABEL_BASELINE: f32 = 13.6;
|
||||
|
||||
// Selection-label gradient mask: matches the SVG `selection-gradient-start`
|
||||
// and `selection-gradient-end` defs. The mask is `OVER_NUMBER_SIZE` screen
|
||||
// pixels long, with the opaque part starting `OVER_NUMBER_PERCENT` of the
|
||||
// way through the rect (40% from the outside edge, 60% from the inside).
|
||||
// Selection-label gradient mask: fades the tick labels behind the selection
|
||||
// band. The mask is `OVER_NUMBER_SIZE` screen pixels long. `OVER_NUMBER_PERCENT`
|
||||
// is the fraction of the mask that sits *outside* the band; at 1.0 the mask's
|
||||
// inner edge lands exactly on the band edge, so the dark shadow begins where
|
||||
// the green selection area ends and fades outward (it never bleeds under the
|
||||
// band). `GRADIENT_FADE_FRACTION` is the share of the mask spent on the
|
||||
// transparent→opaque ramp at the outer end.
|
||||
const OVER_NUMBER_SIZE: f32 = 100.0;
|
||||
const OVER_NUMBER_PERCENT: f32 = 0.75;
|
||||
const OVER_NUMBER_PERCENT: f32 = 1.0;
|
||||
const GRADIENT_FADE_FRACTION: f32 = 0.4;
|
||||
const OVER_NUMBER_OPACITY: f32 = 0.8;
|
||||
|
||||
fn calculate_step_size(zoom: f32) -> f32 {
|
||||
if zoom <= 0.0 {
|
||||
@ -308,23 +312,27 @@ fn draw_selection_x(ctx: &RenderCtx, sel: Rect, offset: f32) {
|
||||
);
|
||||
|
||||
let text_y = ctx.vy + SELECTION_LABEL_BASELINE * zi;
|
||||
let pad_x = 4.0 * zi;
|
||||
// Both labels use the same half-bar gap from the band so the start (left)
|
||||
// and end (right) are spaced symmetrically.
|
||||
let gap = (RULER_AREA_SIZE / 2.0) * zi;
|
||||
let left_label = format_label(sel.left - offset);
|
||||
let right_label = format_label(sel.right - offset);
|
||||
let (lw_font, _) = ctx.font.measure_str(&left_label, None);
|
||||
// The right label is anchored at its left edge, so we don't need its
|
||||
// measured width.
|
||||
let lx = sel.left - pad_x - lw_font * zi;
|
||||
let rx = sel.right + pad_x;
|
||||
let lx = sel.left - gap - lw_font * zi;
|
||||
let rx = sel.right + gap;
|
||||
|
||||
let mut text_paint = Paint::default();
|
||||
text_paint.set_color(ctx.state.accent_color);
|
||||
text_paint.set_anti_alias(true);
|
||||
|
||||
// 1. Left label
|
||||
canvas.save();
|
||||
canvas.translate((lx, text_y));
|
||||
canvas.scale((zi, zi));
|
||||
canvas.draw_str(&left_label, Point::new(0.0, 0.0), ctx.font, &text_paint);
|
||||
canvas.restore();
|
||||
|
||||
// 2. Right label
|
||||
canvas.save();
|
||||
canvas.translate((rx, text_y));
|
||||
canvas.scale((zi, zi));
|
||||
@ -341,7 +349,7 @@ enum MaskAxis {
|
||||
/// labels behind the selection band. `fade_to_end` flips it from
|
||||
/// transparent→opaque (before the band) to opaque→transparent (after).
|
||||
fn draw_mask(ctx: &RenderCtx, rect: Rect, axis: MaskAxis, fade_to_end: bool) {
|
||||
let opaque = ctx.state.bg_color;
|
||||
let opaque = with_alpha(ctx.state.bg_color, OVER_NUMBER_OPACITY);
|
||||
let transparent = with_alpha(ctx.state.bg_color, 0.0);
|
||||
let (colors, offsets): (&[skia::Color; 3], &[f32; 3]) = if fade_to_end {
|
||||
(
|
||||
@ -377,11 +385,10 @@ fn draw_selection_y(ctx: &RenderCtx, sel: Rect, offset: f32) {
|
||||
let canvas = ctx.canvas;
|
||||
let zi = ctx.zi;
|
||||
|
||||
let pad_y = 4.0 * zi;
|
||||
let top_label = format_label(sel.top - offset);
|
||||
let bottom_label = format_label(sel.bottom - offset);
|
||||
// Top label's draw position doesn't depend on its own width (LX is just
|
||||
// pad_y/zi), so we only need bw_font for the bottom label's right-anchor.
|
||||
// Both labels sit a half-bar gap from the band; only the bottom label's
|
||||
// origin depends on its own width (it reads toward the band).
|
||||
let (bw_font, _) = ctx.font.measure_str(&bottom_label, None);
|
||||
|
||||
// Mask first (gradient bg over tick labels behind), then band, then
|
||||
@ -416,33 +423,27 @@ fn draw_selection_y(ctx: &RenderCtx, sel: Rect, offset: f32) {
|
||||
let mut text_paint = Paint::default();
|
||||
text_paint.set_color(ctx.state.accent_color);
|
||||
text_paint.set_anti_alias(true);
|
||||
// Both labels read bottom-to-top on screen (after the -90° rotation
|
||||
// local +x points upward). With the transform stack
|
||||
// (translate→rotate→scale) and a draw at code-(LX, 0), the actual
|
||||
// origin in document coords is (text_x, pivot_y − LX·zi).
|
||||
//
|
||||
// Top label: want origin just above sel.top, reading upward from
|
||||
// there, so pivot_y − LX·zi = sel.top − pad_y ⇒ LX = pad_y/zi.
|
||||
// Both labels read bottom-to-top on screen
|
||||
// 1. Top label
|
||||
canvas.save();
|
||||
canvas.translate((text_x, sel.top));
|
||||
canvas.rotate(-90.0, None);
|
||||
canvas.scale((zi, zi));
|
||||
canvas.draw_str(
|
||||
&top_label,
|
||||
Point::new(pad_y / zi, 0.0),
|
||||
Point::new(RULER_AREA_SIZE / 2.0, 0.0),
|
||||
ctx.font,
|
||||
&text_paint,
|
||||
);
|
||||
canvas.restore();
|
||||
// Bottom label: want the text END at sel.bottom + pad_y and origin
|
||||
// at sel.bottom + pad_y + bw so it reads upward toward the band.
|
||||
// 2. Bottom label
|
||||
canvas.save();
|
||||
canvas.translate((text_x, sel.bottom));
|
||||
canvas.rotate(-90.0, None);
|
||||
canvas.scale((zi, zi));
|
||||
canvas.draw_str(
|
||||
&bottom_label,
|
||||
Point::new(-bw_font - pad_y / zi, 0.0),
|
||||
Point::new(-bw_font - RULER_AREA_SIZE / 2.0, 0.0),
|
||||
ctx.font,
|
||||
&text_paint,
|
||||
);
|
||||
|
||||
@ -590,6 +590,57 @@ impl Surfaces {
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
/// Fast pan/zoom preview: draw the doc atlas as backdrop, then overlay HQ
|
||||
/// cached tile textures placed via their stored document rects (pan + scale).
|
||||
pub fn draw_combined_atlas_to_backbuffer(
|
||||
&mut self,
|
||||
viewbox: &Viewbox,
|
||||
tile_viewbox: &TileViewbox,
|
||||
background: skia::Color,
|
||||
) {
|
||||
self.draw_atlas_to_backbuffer(*viewbox, background);
|
||||
|
||||
// Tile textures are keyed by grid index but positioned in document space.
|
||||
// Without `tile_doc_rects` we cannot displace/scale them correctly (e.g.
|
||||
// right after zoom invalidation); the atlas backdrop alone is enough.
|
||||
if self.atlas.tile_doc_rects.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let batch = self.tiles.build_atlas_draw_batch_for_doc_rects(
|
||||
viewbox,
|
||||
tile_viewbox,
|
||||
&self.atlas.tile_doc_rects,
|
||||
);
|
||||
if batch.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.tiles.needs_snapshot() || self.tile_atlas_image.is_none() {
|
||||
self.tile_atlas_image = Some(self.tile_atlas.image_snapshot());
|
||||
self.tiles.snapshot();
|
||||
}
|
||||
let Some(atlas_image) = self.tile_atlas_image.as_ref() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let canvas = self.backbuffer.canvas();
|
||||
canvas.save();
|
||||
canvas.reset_matrix();
|
||||
|
||||
canvas.draw_atlas(
|
||||
atlas_image,
|
||||
&batch.transforms,
|
||||
&batch.textures,
|
||||
None,
|
||||
skia::BlendMode::SrcOver,
|
||||
self.atlas_sampling_options,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
canvas.restore();
|
||||
}
|
||||
|
||||
pub fn margins(&self) -> skia::ISize {
|
||||
self.margins
|
||||
}
|
||||
@ -716,18 +767,6 @@ impl Surfaces {
|
||||
);
|
||||
}
|
||||
|
||||
/// Draws the cache surface directly to the backbuffer canvas.
|
||||
/// This avoids creating an intermediate snapshot, reducing GPU stalls.
|
||||
pub fn draw_cache_to_backbuffer(&mut self) {
|
||||
let sampling_options = self.sampling_options;
|
||||
self.cache.draw(
|
||||
self.backbuffer.canvas(),
|
||||
(0.0, 0.0),
|
||||
sampling_options,
|
||||
Some(&skia::Paint::default()),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn cache_dimensions(&self) -> skia::ISize {
|
||||
skia::ISize::new(self.cache.width(), self.cache.height())
|
||||
}
|
||||
@ -1516,6 +1555,17 @@ pub struct TileTextureCache {
|
||||
removed: HashSet<Tile>,
|
||||
}
|
||||
|
||||
pub struct AtlasDrawBatch {
|
||||
pub transforms: Vec<skia::RSXform>,
|
||||
pub textures: Vec<skia::Rect>,
|
||||
}
|
||||
|
||||
impl AtlasDrawBatch {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.transforms.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl TileTextureCache {
|
||||
pub fn new(texture_size: i32, capacity: usize) -> Self {
|
||||
Self {
|
||||
@ -1615,6 +1665,76 @@ impl TileTextureCache {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_atlas_draw_batch_for_doc_rects(
|
||||
&self,
|
||||
viewbox: &Viewbox,
|
||||
tile_viewbox: &TileViewbox,
|
||||
tile_doc_rects: &HashMap<Tile, skia::Rect>,
|
||||
) -> AtlasDrawBatch {
|
||||
let mut transforms = Vec::new();
|
||||
let mut textures = Vec::new();
|
||||
|
||||
let s = viewbox.get_scale();
|
||||
let view_doc = viewbox.area;
|
||||
|
||||
for y in tile_viewbox.visible_rect.top()..=tile_viewbox.visible_rect.bottom() {
|
||||
for x in tile_viewbox.visible_rect.left()..=tile_viewbox.visible_rect.right() {
|
||||
let tile = Tile(x, y);
|
||||
|
||||
let Some(tile_ref) = self.grid.get(&tile) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if self.removed.contains(&tile) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let doc_rect = tile_doc_rects
|
||||
.get(&tile)
|
||||
.copied()
|
||||
.unwrap_or_else(|| tiles::get_tile_rect(tile, s));
|
||||
if doc_rect.is_empty() || !doc_rect.intersects(view_doc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let scos = doc_rect.width() * s / self.tile_size;
|
||||
let tx = (doc_rect.left + viewbox.pan.x) * s;
|
||||
let ty = (doc_rect.top + viewbox.pan.y) * s;
|
||||
|
||||
transforms.push(skia::RSXform::new(scos, 0.0, (tx, ty)));
|
||||
textures.push(tile_ref.rect);
|
||||
}
|
||||
}
|
||||
|
||||
// Cached tiles from a previous zoom level use indices outside visible_rect;
|
||||
// place them via their stored document rect, not the current grid walk above.
|
||||
for (&tile, tile_ref) in &self.grid {
|
||||
if tile_viewbox.is_visible(&tile) || self.removed.contains(&tile) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let doc_rect = tile_doc_rects
|
||||
.get(&tile)
|
||||
.copied()
|
||||
.unwrap_or_else(|| tiles::get_tile_rect(tile, s));
|
||||
if doc_rect.is_empty() || !doc_rect.intersects(view_doc) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let tx = (doc_rect.left + viewbox.pan.x) * s;
|
||||
let ty = (doc_rect.top + viewbox.pan.y) * s;
|
||||
let scos = doc_rect.width() * s / self.tile_size;
|
||||
|
||||
transforms.push(skia::RSXform::new(scos, 0.0, (tx, ty)));
|
||||
textures.push(tile_ref.rect);
|
||||
}
|
||||
|
||||
AtlasDrawBatch {
|
||||
transforms,
|
||||
textures,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has(&self, tile: Tile) -> bool {
|
||||
self.grid.contains_key(&tile) && !self.removed.contains(&tile)
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user