Compare commits
No commits in common. "03579ecc25d05f241bc8a5d61d6dc38aae36a590" and "adfbe04d30a57f114a4d284b328053a8210a6650" have entirely different histories.
03579ecc25
...
adfbe04d30
16 changed files with 36 additions and 314 deletions
1
.dagger/.gitattributes
vendored
1
.dagger/.gitattributes
vendored
|
|
@ -1 +0,0 @@
|
||||||
/sdk/** linguist-generated
|
|
||||||
4
.dagger/.gitignore
vendored
4
.dagger/.gitignore
vendored
|
|
@ -1,4 +0,0 @@
|
||||||
/sdk
|
|
||||||
/**/node_modules/**
|
|
||||||
/**/.pnpm-store/**
|
|
||||||
/.env
|
|
||||||
25
.dagger/package-lock.json
generated
25
.dagger/package-lock.json
generated
|
|
@ -1,25 +0,0 @@
|
||||||
{
|
|
||||||
"name": ".dagger",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"dependencies": {
|
|
||||||
"typescript": "5.9.3"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/typescript": {
|
|
||||||
"version": "5.9.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
|
||||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"bin": {
|
|
||||||
"tsc": "bin/tsc",
|
|
||||||
"tsserver": "bin/tsserver"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.17"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"type": "module"
|
|
||||||
,"dependencies":{"typescript":"5.9.3"}}
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
import {
|
|
||||||
dag,
|
|
||||||
Container,
|
|
||||||
Directory,
|
|
||||||
File,
|
|
||||||
object,
|
|
||||||
func,
|
|
||||||
argument,
|
|
||||||
} from "@dagger.io/dagger";
|
|
||||||
|
|
||||||
const RUST_IMAGE = "rust:1.85";
|
|
||||||
const WORKDIR = "/workspace";
|
|
||||||
|
|
||||||
@object()
|
|
||||||
export class LineAndSurface {
|
|
||||||
private baseRust(source: Directory): Container {
|
|
||||||
return dag
|
|
||||||
.container()
|
|
||||||
.from(RUST_IMAGE)
|
|
||||||
.withEnvVariable("CARGO_HOME", "/root/.cargo")
|
|
||||||
.withMountedCache("/root/.cargo", dag.cacheVolume("cargo-cache"))
|
|
||||||
.withMountedCache(
|
|
||||||
`${WORKDIR}/target`,
|
|
||||||
dag.cacheVolume("cargo-target"),
|
|
||||||
)
|
|
||||||
.withWorkdir(WORKDIR)
|
|
||||||
.withDirectory(WORKDIR, source)
|
|
||||||
.withExec(["cargo", "fetch", "--locked"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the native desktop binary (release).
|
|
||||||
*/
|
|
||||||
@func()
|
|
||||||
async nativeBuild(
|
|
||||||
@argument({
|
|
||||||
defaultPath: "/",
|
|
||||||
ignore: [".git/**", ".dagger/**", "target/**", "node_modules/**"],
|
|
||||||
})
|
|
||||||
source: Directory,
|
|
||||||
): Promise<File> {
|
|
||||||
const ctr = this.baseRust(source)
|
|
||||||
.withExec(["cargo", "build", "--release"])
|
|
||||||
.withExec(["mkdir", "-p", "/out"])
|
|
||||||
.withExec(["cp", "-v", `${WORKDIR}/target/release/las`, "/out/las"]);
|
|
||||||
|
|
||||||
return ctr.file("/out/las");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build the web (wasm) bundle using Trunk.
|
|
||||||
*/
|
|
||||||
@func()
|
|
||||||
async webBuild(
|
|
||||||
@argument({
|
|
||||||
defaultPath: "/",
|
|
||||||
ignore: [".git/**", ".dagger/**", "target/**", "node_modules/**"],
|
|
||||||
})
|
|
||||||
source: Directory,
|
|
||||||
): Promise<Directory> {
|
|
||||||
const ctr = this.baseRust(source)
|
|
||||||
.withExec(["rustup", "target", "add", "wasm32-unknown-unknown"])
|
|
||||||
.withExec(["cargo", "install", "trunk"])
|
|
||||||
.withExec(["trunk", "build", "--release", "--dist", "/out"]);
|
|
||||||
|
|
||||||
return ctr.directory("/out");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2022",
|
|
||||||
"moduleResolution": "Node",
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"strict": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"paths": {
|
|
||||||
"@dagger.io/dagger": ["./sdk/index.ts"],
|
|
||||||
"@dagger.io/dagger/telemetry": ["./sdk/telemetry.ts"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
|
||||||
# yarn lockfile v1
|
|
||||||
|
|
||||||
|
|
||||||
typescript@5.9.3:
|
|
||||||
version "5.9.3"
|
|
||||||
resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz"
|
|
||||||
integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==
|
|
||||||
|
|
@ -35,7 +35,7 @@ env_logger = "0.11.8"
|
||||||
# web:
|
# web:
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
wasm-bindgen-futures = "0.4.50"
|
wasm-bindgen-futures = "0.4.50"
|
||||||
web-sys = { version = "0.3.70", features = ["Performance", "Response", "Window"] } # to access the DOM (to hide the loading text)
|
web-sys = "0.3.70" # to access the DOM (to hide the loading text)
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = 2 # fast and small wasm
|
opt-level = 2 # fast and small wasm
|
||||||
|
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
{
|
|
||||||
"name": "line-and-surface",
|
|
||||||
"engineVersion": "v0.19.10",
|
|
||||||
"sdk": {
|
|
||||||
"source": "typescript"
|
|
||||||
},
|
|
||||||
"source": ".dagger"
|
|
||||||
}
|
|
||||||
|
|
@ -23,7 +23,6 @@
|
||||||
<link data-trunk rel="copy-file" href="assets/icon-256.png" data-target-path="assets"/>
|
<link data-trunk rel="copy-file" href="assets/icon-256.png" data-target-path="assets"/>
|
||||||
<link data-trunk rel="copy-file" href="assets/icon_ios_touch_192.png" data-target-path="assets"/>
|
<link data-trunk rel="copy-file" href="assets/icon_ios_touch_192.png" data-target-path="assets"/>
|
||||||
<link data-trunk rel="copy-file" href="assets/maskable_icon_x512.png" data-target-path="assets"/>
|
<link data-trunk rel="copy-file" href="assets/maskable_icon_x512.png" data-target-path="assets"/>
|
||||||
<link data-trunk rel="copy-file" href="content/intro.svg" data-target-path="content"/>
|
|
||||||
|
|
||||||
|
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="manifest.json">
|
||||||
|
|
|
||||||
163
src/app/mod.rs
163
src/app/mod.rs
|
|
@ -1,11 +1,6 @@
|
||||||
use crate::svg::SvgContent;
|
use crate::svg::SvgContent;
|
||||||
use crate::text_cache::TextCache;
|
use crate::text_cache::TextCache;
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
use std::cell::RefCell;
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
mod animation;
|
mod animation;
|
||||||
mod input;
|
mod input;
|
||||||
mod render;
|
mod render;
|
||||||
|
|
@ -66,10 +61,6 @@ pub struct LasApp {
|
||||||
/// Text rendering cache for smooth scaling.
|
/// Text rendering cache for smooth scaling.
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
text_cache: Option<TextCache>,
|
text_cache: Option<TextCache>,
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
#[serde(skip)]
|
|
||||||
svg_loader: Option<Rc<RefCell<Option<Result<SvgContent, String>>>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for LasApp {
|
impl Default for LasApp {
|
||||||
|
|
@ -90,8 +81,6 @@ impl Default for LasApp {
|
||||||
last_cursor_pos: None,
|
last_cursor_pos: None,
|
||||||
reset_view_requested: false,
|
reset_view_requested: false,
|
||||||
view_animation: None,
|
view_animation: None,
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
svg_loader: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -112,60 +101,32 @@ impl LasApp {
|
||||||
.and_then(|s| eframe::get_value(s, eframe::APP_KEY))
|
.and_then(|s| eframe::get_value(s, eframe::APP_KEY))
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
// Load SVG content
|
||||||
{
|
let svg_path = "../line-and-surface/content/intro.svg";
|
||||||
// Load SVG content
|
log::info!("Loading SVG from: {svg_path}");
|
||||||
let svg_path = "../line-and-surface/content/intro.svg";
|
let start = std::time::Instant::now();
|
||||||
log::info!("Loading SVG from: {svg_path}");
|
|
||||||
let start = std::time::Instant::now();
|
|
||||||
|
|
||||||
match SvgContent::from_file(svg_path) {
|
match SvgContent::from_file(svg_path) {
|
||||||
Ok(content) => {
|
Ok(content) => {
|
||||||
let elapsed = start.elapsed();
|
let elapsed = start.elapsed();
|
||||||
log::info!(
|
log::info!(
|
||||||
"Loaded SVG in {:.2?}: {} video scrolls, {} audio areas, {} anchors, {} texts",
|
"Loaded SVG in {:.2?}: {} video scrolls, {} audio areas, {} anchors, {} texts",
|
||||||
elapsed,
|
elapsed,
|
||||||
content.video_scrolls.len(),
|
content.video_scrolls.len(),
|
||||||
content.audio_areas.len(),
|
content.audio_areas.len(),
|
||||||
content.anchors.len(),
|
content.anchors.len(),
|
||||||
content.texts.len()
|
content.texts.len()
|
||||||
);
|
);
|
||||||
if let Some((vb_x, vb_y, vb_w, vb_h)) = content.viewbox {
|
if let Some((vb_x, vb_y, vb_w, vb_h)) = content.viewbox {
|
||||||
log::debug!("SVG viewbox: ({vb_x}, {vb_y}, {vb_w}, {vb_h})");
|
log::debug!("SVG viewbox: ({vb_x}, {vb_y}, {vb_w}, {vb_h})");
|
||||||
app.pan_x = -vb_x;
|
app.pan_x = -vb_x;
|
||||||
app.pan_y = -vb_y;
|
app.pan_y = -vb_y;
|
||||||
}
|
|
||||||
app.svg_content = Some(content);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
log::error!("Failed to load SVG: {e}");
|
|
||||||
}
|
}
|
||||||
|
app.svg_content = Some(content);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to load SVG: {e}");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
{
|
|
||||||
let svg_path = "./content/intro.svg";
|
|
||||||
log::info!("Loading SVG from: {svg_path}");
|
|
||||||
let loader = Rc::new(RefCell::new(None));
|
|
||||||
let loader_handle = loader.clone();
|
|
||||||
wasm_bindgen_futures::spawn_local(async move {
|
|
||||||
let start = wasm_now();
|
|
||||||
let result = load_svg_from_url(svg_path).await;
|
|
||||||
if let Ok(ref content) = result {
|
|
||||||
let elapsed_ms = (wasm_now() - start).max(0.0);
|
|
||||||
log::info!(
|
|
||||||
"Loaded SVG in {:.2}ms: {} video scrolls, {} audio areas, {} anchors, {} texts",
|
|
||||||
elapsed_ms,
|
|
||||||
content.video_scrolls.len(),
|
|
||||||
content.audio_areas.len(),
|
|
||||||
content.anchors.len(),
|
|
||||||
content.texts.len()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
*loader_handle.borrow_mut() = Some(result);
|
|
||||||
});
|
|
||||||
app.svg_loader = Some(loader);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!("Application initialized");
|
log::info!("Application initialized");
|
||||||
|
|
@ -178,38 +139,6 @@ impl LasApp {
|
||||||
self.fps_ema = ALPHA * (1.0 / frame_time) + (1.0 - ALPHA) * self.fps_ema;
|
self.fps_ema = ALPHA * (1.0 / frame_time) + (1.0 - ALPHA) * self.fps_ema;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
fn poll_svg_loader(&mut self, ctx: &egui::Context) {
|
|
||||||
if self.svg_content.is_some() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(loader) = self.svg_loader.as_ref() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let Some(result) = loader.borrow_mut().take() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Ok(content) => {
|
|
||||||
if let Some((vb_x, vb_y, vb_w, vb_h)) = content.viewbox {
|
|
||||||
log::debug!("SVG viewbox: ({vb_x}, {vb_y}, {vb_w}, {vb_h})");
|
|
||||||
self.pan_x = -vb_x;
|
|
||||||
self.pan_y = -vb_y;
|
|
||||||
}
|
|
||||||
self.svg_content = Some(content);
|
|
||||||
ctx.request_repaint();
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
log::error!("Failed to load SVG: {err}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.svg_loader = None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl eframe::App for LasApp {
|
impl eframe::App for LasApp {
|
||||||
|
|
@ -218,9 +147,6 @@ impl eframe::App for LasApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
self.poll_svg_loader(ctx);
|
|
||||||
|
|
||||||
let input = InputSnapshot::collect(ctx);
|
let input = InputSnapshot::collect(ctx);
|
||||||
|
|
||||||
if input.primary_pressed
|
if input.primary_pressed
|
||||||
|
|
@ -258,46 +184,3 @@ impl eframe::App for LasApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
async fn load_svg_from_url(url: &str) -> Result<SvgContent, String> {
|
|
||||||
use wasm_bindgen_futures::JsFuture;
|
|
||||||
use web_sys::Response;
|
|
||||||
use web_sys::wasm_bindgen::JsCast;
|
|
||||||
|
|
||||||
let window = web_sys::window().ok_or("missing window".to_string())?;
|
|
||||||
let resp_value = JsFuture::from(window.fetch_with_str(url))
|
|
||||||
.await
|
|
||||||
.map_err(|err| format!("fetch failed: {err:?}"))?;
|
|
||||||
let response: Response = resp_value
|
|
||||||
.dyn_into()
|
|
||||||
.map_err(|_| "fetch response cast failed".to_string())?;
|
|
||||||
|
|
||||||
if !response.ok() {
|
|
||||||
return Err(format!(
|
|
||||||
"fetch failed: {} {}",
|
|
||||||
response.status(),
|
|
||||||
response.status_text()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let text_promise = response
|
|
||||||
.text()
|
|
||||||
.map_err(|err| format!("read text failed: {err:?}"))?;
|
|
||||||
let text_value = JsFuture::from(text_promise)
|
|
||||||
.await
|
|
||||||
.map_err(|err| format!("read text failed: {err:?}"))?;
|
|
||||||
let text = text_value
|
|
||||||
.as_string()
|
|
||||||
.ok_or("response text missing".to_string())?;
|
|
||||||
|
|
||||||
SvgContent::parse(&text).map_err(|err| format!("parse SVG failed: {err}"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
fn wasm_now() -> f64 {
|
|
||||||
web_sys::window()
|
|
||||||
.and_then(|window| window.performance())
|
|
||||||
.map(|performance| performance.now())
|
|
||||||
.unwrap_or(0.0)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -122,10 +122,10 @@ impl LasApp {
|
||||||
self.pan_y = pan_y;
|
self.pan_y = pan_y;
|
||||||
self.zoom = zoom;
|
self.zoom = zoom;
|
||||||
}
|
}
|
||||||
self.did_fit_start = true;
|
|
||||||
ctx.request_repaint();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.did_fit_start = true;
|
||||||
|
ctx.request_repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
if input.space_pressed {
|
if input.space_pressed {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#![warn(clippy::all, rust_2018_idioms)]
|
#![warn(clippy::all, rust_2018_idioms)]
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
|
|
||||||
// When compiling natively:
|
// When compiling natively:
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ use super::color::parse_background_color;
|
||||||
use super::types::{Anchor, AnchorLink, AudioArea, SvgContent, TextElement, TextLine, VideoScroll};
|
use super::types::{Anchor, AnchorLink, AudioArea, SvgContent, TextElement, TextLine, VideoScroll};
|
||||||
use quick_xml::Reader;
|
use quick_xml::Reader;
|
||||||
use quick_xml::events::Event;
|
use quick_xml::events::Event;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
/// State for tracking current element during parsing.
|
/// State for tracking current element during parsing.
|
||||||
|
|
@ -80,7 +79,6 @@ impl LinkAccumulator {
|
||||||
|
|
||||||
impl SvgContent {
|
impl SvgContent {
|
||||||
/// Parse an SVG file and extract special elements.
|
/// Parse an SVG file and extract special elements.
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
pub fn from_file(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
|
pub fn from_file(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
log::debug!("Reading SVG file: {path}");
|
log::debug!("Reading SVG file: {path}");
|
||||||
let content = fs::read_to_string(path)?;
|
let content = fs::read_to_string(path)?;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use super::{Renderable as _, SvgContent};
|
use super::{Renderable, SvgContent};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_video_scroll() {
|
fn test_parse_video_scroll() {
|
||||||
|
|
|
||||||
|
|
@ -193,10 +193,7 @@ impl TextCache {
|
||||||
size_key: u32,
|
size_key: u32,
|
||||||
render_scale: f32,
|
render_scale: f32,
|
||||||
) -> CachedText {
|
) -> CachedText {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
let start_time = std::time::Instant::now();
|
let start_time = std::time::Instant::now();
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
let start_time = wasm_now();
|
|
||||||
trace!(
|
trace!(
|
||||||
"Rendering text '{}' at size {}px (scale {})...",
|
"Rendering text '{}' at size {}px (scale {})...",
|
||||||
&text[..text.len().min(20)],
|
&text[..text.len().min(20)],
|
||||||
|
|
@ -325,31 +322,15 @@ impl TextCache {
|
||||||
TEXTURE_OPTIONS,
|
TEXTURE_OPTIONS,
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
let duration = start_time.elapsed();
|
||||||
{
|
trace!(
|
||||||
let duration = start_time.elapsed();
|
"Rendered text '{}' ({}x{} @{}) in {:.2?}",
|
||||||
trace!(
|
&text[..text.len().min(20)],
|
||||||
"Rendered text '{}' ({}x{} @{}) in {:.2?}",
|
img_width,
|
||||||
&text[..text.len().min(20)],
|
img_height,
|
||||||
img_width,
|
chosen_scale,
|
||||||
img_height,
|
duration
|
||||||
chosen_scale,
|
);
|
||||||
duration
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
{
|
|
||||||
let duration_ms = (wasm_now() - start_time).max(0.0);
|
|
||||||
trace!(
|
|
||||||
"Rendered text '{}' ({}x{} @{}) in {:.2}ms",
|
|
||||||
&text[..text.len().min(20)],
|
|
||||||
img_width,
|
|
||||||
img_height,
|
|
||||||
chosen_scale,
|
|
||||||
duration_ms
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
CachedText {
|
CachedText {
|
||||||
texture,
|
texture,
|
||||||
|
|
@ -487,14 +468,6 @@ impl TextCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
fn wasm_now() -> f64 {
|
|
||||||
web_sys::window()
|
|
||||||
.and_then(|window| window.performance())
|
|
||||||
.map(|performance| performance.now())
|
|
||||||
.unwrap_or(0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn texture_bytes(cached: &CachedText) -> usize {
|
fn texture_bytes(cached: &CachedText) -> usize {
|
||||||
cached.width as usize * cached.height as usize * 4
|
cached.width as usize * cached.height as usize * 4
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue