mirror of
https://github.com/penpot/penpot.git
synced 2026-08-11 23:38:45 +00:00
Keep the shape-to-tile grid zoom-only while scaling GPU paint by DPR (capped at 1024px). Fix fractional browser zoom and pan, cap atlas slots with eviction instead of panic, and split paint vs view scale with explicit paint/atlas/screen tile sizes.
313 lines
9.4 KiB
Rust
313 lines
9.4 KiB
Rust
#![allow(dead_code)]
|
|
use super::{tiles, RenderState, SurfaceId};
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
use macros::wasm_error;
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
use crate::get_render_state;
|
|
use crate::get_resources;
|
|
|
|
use skia_safe::{self as skia, Rect};
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
use crate::run_script;
|
|
|
|
const DEBUG_SCALE: f32 = 0.2;
|
|
|
|
fn get_debug_rect(rect: Rect) -> Rect {
|
|
skia::Rect::from_xywh(
|
|
100. + rect.x() * DEBUG_SCALE,
|
|
100. + rect.y() * DEBUG_SCALE,
|
|
rect.width() * DEBUG_SCALE,
|
|
rect.height() * DEBUG_SCALE,
|
|
)
|
|
}
|
|
|
|
fn render_debug_view(render_state: &mut RenderState) {
|
|
let mut paint = skia::Paint::default();
|
|
paint.set_style(skia::PaintStyle::Stroke);
|
|
paint.set_color(skia::Color::GREEN);
|
|
paint.set_stroke_width(1.);
|
|
|
|
let rect = get_debug_rect(render_state.viewbox.area);
|
|
render_state
|
|
.surfaces
|
|
.canvas(SurfaceId::Debug)
|
|
.draw_rect(rect, &paint);
|
|
}
|
|
|
|
pub fn render_debug_cache_surface(render_state: &mut RenderState) {
|
|
let canvas = render_state.surfaces.canvas(SurfaceId::Debug);
|
|
canvas.save();
|
|
canvas.scale((0.1, 0.1));
|
|
render_state
|
|
.surfaces
|
|
.draw_into(SurfaceId::Cache, SurfaceId::Debug, None);
|
|
render_state.surfaces.canvas(SurfaceId::Debug).restore();
|
|
}
|
|
|
|
pub fn render_wasm_label(render_state: &mut RenderState) {
|
|
if render_state.preview_mode || !render_state.options.show_wasm_info() {
|
|
return;
|
|
}
|
|
|
|
let canvas = render_state.surfaces.canvas(SurfaceId::Target);
|
|
let canvas_width = canvas.base_layer_size().width as f32;
|
|
let mut paint = skia::Paint::default();
|
|
paint.set_color(skia::Color::GRAY);
|
|
|
|
let mut str = if render_state.options.is_debug_visible() {
|
|
"WebGL rendering (debug)"
|
|
} else {
|
|
"WebGL rendering"
|
|
};
|
|
let dpr = render_state.options.dpr;
|
|
let (scalar, _) = get_resources().fonts.debug_font().measure_str(str, None);
|
|
let mut p = skia::Point::new(canvas_width - (20.0 * dpr) - scalar, 50.0 * dpr);
|
|
|
|
let debug_font = get_resources().fonts.debug_font();
|
|
canvas.draw_str(str, p, debug_font, &paint);
|
|
|
|
if render_state.options.is_text_editor_v3() {
|
|
str = "Text Editor v3";
|
|
|
|
let (scalar, _) = get_resources().fonts.debug_font().measure_str(str, None);
|
|
p.x = canvas_width - (20.0 * dpr) - scalar;
|
|
p.y += 15.0 * dpr;
|
|
canvas.draw_str(str, p, debug_font, &paint);
|
|
}
|
|
}
|
|
|
|
pub fn render_debug_tiles_for_viewbox(render_state: &mut RenderState) {
|
|
let tiles::TileRect(sx, sy, ex, ey) = render_state.tile_viewbox.interest_rect;
|
|
let canvas = render_state.surfaces.canvas(SurfaceId::Debug);
|
|
let mut paint = skia::Paint::default();
|
|
paint.set_color(skia::Color::RED);
|
|
let str_rect = format!("{} {} {} {}", sx, sy, ex, ey);
|
|
|
|
let debug_font = get_resources().fonts.debug_font();
|
|
canvas.draw_str(str_rect, skia::Point::new(100.0, 150.0), debug_font, &paint);
|
|
}
|
|
|
|
// Renders the tiles in the viewbox
|
|
pub fn render_debug_viewbox_tiles(render_state: &mut RenderState) {
|
|
let zoom = render_state.viewbox.zoom();
|
|
let canvas = render_state.surfaces.canvas(SurfaceId::Debug);
|
|
let mut paint = skia::Paint::default();
|
|
paint.set_style(skia::PaintStyle::Stroke);
|
|
paint.set_color(skia::Color::MAGENTA);
|
|
paint.set_stroke_width(1.);
|
|
|
|
let tile_size = tiles::get_tile_size(zoom);
|
|
let tile_rect = tiles::get_tiles_for_rect(render_state.viewbox.area, tile_size);
|
|
let tiles::TileRect(sx, sy, ex, ey) = tile_rect;
|
|
|
|
let str_rect = format!("{} {} {} {}", sx, sy, ex, ey);
|
|
|
|
let debug_font = get_resources().fonts.debug_font();
|
|
canvas.draw_str(str_rect, skia::Point::new(100.0, 100.0), debug_font, &paint);
|
|
|
|
let tile_size = tiles::get_tile_size(zoom);
|
|
for tile in tile_rect.iter(true) {
|
|
let tiles::Tile(x, y) = tile;
|
|
let rect = tile.get_rect_with_size(tile_size);
|
|
let debug_rect = get_debug_rect(rect);
|
|
let p = skia::Point::new(debug_rect.x(), debug_rect.y() - 1.);
|
|
let str = format!("{}:{}", x, y);
|
|
let debug_font = get_resources().fonts.debug_font();
|
|
paint.set_style(skia::PaintStyle::Fill);
|
|
canvas.draw_str(str, p, debug_font, &paint);
|
|
canvas.draw_rect(debug_rect, &paint);
|
|
}
|
|
}
|
|
|
|
pub fn render(render_state: &mut RenderState) {
|
|
// DEBUG VIEWBOX - green rect - buggy?
|
|
// render_debug_view(render_state);
|
|
|
|
// DEBUG VIEWBOX TILES - magenta - buggy?
|
|
// render_debug_viewbox_tiles(render_state);
|
|
|
|
// DEBUG CACHE SURFACE - noisy - ?
|
|
// render_debug_cache_surface(render_state);
|
|
|
|
render_state.surfaces.draw_into(
|
|
SurfaceId::Debug,
|
|
SurfaceId::Target,
|
|
Some(&skia::Paint::default()),
|
|
);
|
|
}
|
|
|
|
pub fn render_workspace_current_tile(
|
|
render_state: &mut RenderState,
|
|
prefix: String,
|
|
tile: tiles::Tile,
|
|
rect: skia::Rect,
|
|
) {
|
|
let canvas = render_state.surfaces.canvas(SurfaceId::Debug);
|
|
let mut p = skia::Paint::default();
|
|
p.set_stroke_width(2.);
|
|
p.set_style(skia::PaintStyle::Stroke);
|
|
canvas.draw_rect(rect, &p);
|
|
|
|
let tile_position_origin = skia::Point::new(rect.x() + 10., rect.y() + 20.);
|
|
p.set_style(skia::PaintStyle::Fill);
|
|
let str = format!("{prefix} {}:{}", tile.x(), tile.y());
|
|
let mut debug_font = get_resources().fonts.debug_font().clone();
|
|
debug_font.set_size(16.);
|
|
canvas.draw_str(str, tile_position_origin, &debug_font, &p);
|
|
}
|
|
|
|
pub fn render_debug_shape(
|
|
render_state: &mut RenderState,
|
|
shape_selrect: Option<skia::Rect>,
|
|
shape_extrect: Option<skia::Rect>,
|
|
) {
|
|
let canvas = render_state.surfaces.canvas(SurfaceId::Debug);
|
|
|
|
let mut paint = skia::Paint::default();
|
|
paint.set_style(skia::PaintStyle::Stroke);
|
|
paint.set_color(skia::Color::RED);
|
|
paint.set_stroke_width(1.);
|
|
|
|
if let Some(rect) = shape_selrect {
|
|
canvas.draw_rect(rect, &paint);
|
|
}
|
|
|
|
if let Some(rect) = shape_extrect {
|
|
paint.set_color(skia::Color::BLUE);
|
|
canvas.draw_rect(rect, &paint);
|
|
}
|
|
}
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
pub fn trap() {
|
|
run_script!("debugger");
|
|
}
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
#[derive(Debug, PartialEq)]
|
|
pub enum SurfaceBackendKind {
|
|
BackendTexture, // GPU Framebuffer (Texture)
|
|
BackendRenderTarget, // GPU Framebuffer (Renderbuffer)
|
|
Raster, // CPU
|
|
Unknown,
|
|
}
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
pub fn classify_surface_backend(surface: &mut skia::Surface) -> SurfaceBackendKind {
|
|
if skia::gpu::surfaces::get_backend_texture(
|
|
surface,
|
|
skia_safe::surface::BackendHandleAccess::FlushRead,
|
|
)
|
|
.is_some()
|
|
{
|
|
return SurfaceBackendKind::BackendTexture;
|
|
}
|
|
|
|
if skia::gpu::surfaces::get_backend_render_target(
|
|
surface,
|
|
skia_safe::surface::BackendHandleAccess::FlushRead,
|
|
)
|
|
.is_some()
|
|
{
|
|
return SurfaceBackendKind::BackendRenderTarget;
|
|
}
|
|
|
|
if surface.peek_pixels().is_some() {
|
|
return SurfaceBackendKind::Raster;
|
|
}
|
|
|
|
SurfaceBackendKind::Unknown
|
|
}
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
pub fn console_debug_surface(render_state: &mut RenderState, id: SurfaceId) {
|
|
let base64_image = render_state
|
|
.surfaces
|
|
.base64_snapshot(id)
|
|
.expect("Failed to get base64 image");
|
|
|
|
run_script!(format!("console.log('%c ', 'font-size: 1px; background: url(data:image/png;base64,{base64_image}) no-repeat; padding: 100px; background-size: contain;')"));
|
|
}
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
pub fn console_debug_surface_base64(render_state: &mut RenderState, id: SurfaceId) {
|
|
let base64_image = render_state
|
|
.surfaces
|
|
.base64_snapshot(id)
|
|
.expect("Failed to get base64 image");
|
|
|
|
println!("{}", base64_image);
|
|
}
|
|
|
|
#[cfg(target_arch = "wasm32")]
|
|
pub fn console_debug_surface_rect(render_state: &mut RenderState, id: SurfaceId, rect: skia::Rect) {
|
|
let int_rect = skia::IRect::from_ltrb(
|
|
rect.left as i32,
|
|
rect.top as i32,
|
|
rect.right as i32,
|
|
rect.bottom as i32,
|
|
);
|
|
|
|
let base64_image = render_state
|
|
.surfaces
|
|
.base64_snapshot_rect(id, int_rect)
|
|
.expect("Failed to get base64 image");
|
|
|
|
if let Some(base64_image) = base64_image {
|
|
run_script!(format!("console.log('%c ', 'font-size: 1px; background: url(data:image/png;base64,{base64_image}) no-repeat; padding: 100px; background-size: contain;')"))
|
|
}
|
|
}
|
|
|
|
#[no_mangle]
|
|
#[wasm_error]
|
|
#[cfg(target_arch = "wasm32")]
|
|
pub extern "C" fn capture_frames(capture_frames: i32) -> Result<()> {
|
|
get_render_state()
|
|
.options
|
|
.set_capture_frames(capture_frames);
|
|
Ok(())
|
|
}
|
|
|
|
#[no_mangle]
|
|
#[wasm_error]
|
|
#[cfg(target_arch = "wasm32")]
|
|
pub extern "C" fn debug_cache_console() -> Result<()> {
|
|
console_debug_surface(get_render_state(), SurfaceId::Cache);
|
|
Ok(())
|
|
}
|
|
|
|
#[no_mangle]
|
|
#[wasm_error]
|
|
#[cfg(target_arch = "wasm32")]
|
|
pub extern "C" fn debug_cache_base64() -> Result<()> {
|
|
console_debug_surface_base64(get_render_state(), SurfaceId::Cache);
|
|
Ok(())
|
|
}
|
|
|
|
#[no_mangle]
|
|
#[wasm_error]
|
|
#[cfg(target_arch = "wasm32")]
|
|
pub extern "C" fn debug_atlas_console() -> Result<()> {
|
|
console_debug_surface(get_render_state(), SurfaceId::Atlas);
|
|
Ok(())
|
|
}
|
|
|
|
#[no_mangle]
|
|
#[wasm_error]
|
|
#[cfg(target_arch = "wasm32")]
|
|
pub extern "C" fn debug_atlas_base64() -> Result<()> {
|
|
console_debug_surface_base64(get_render_state(), SurfaceId::Atlas);
|
|
Ok(())
|
|
}
|
|
|
|
#[no_mangle]
|
|
#[wasm_error]
|
|
#[cfg(target_arch = "wasm32")]
|
|
pub extern "C" fn debug_surface_console(id: SurfaceId) -> Result<()> {
|
|
console_debug_surface(get_render_state(), id);
|
|
Ok(())
|
|
}
|