Bump to 0.2.0: SW/GLES paint parity, shared font resolution, FrameState refactor, docs and packaging fixes
Rendering parity (software ↔ GLES). The software backend now rounds glyph pen positions and image destinations to the nearest integer pixel, matching what the GLES backend already did; previously it truncated, so text and 1:1 images could land up to half a pixel off between the two backends and the bilinear sample read ~1 px softer than the source. Gradients and shadows are deliberately left unimplemented on the software backend, and the GLES multi-rect `glScissor` clip is left coarse on purpose: making it exact would need stencil bits the EGL config does not carry, or routing the partial-redraw path through the offscreen clip layer, which would break its `fill` / `clear_rects_transparent` scissor semantics. Adds software-backend pixel tests covering the snapping. Font resolution unification. The system-font candidate chain, `find_font_opt` and `load_default_font_bytes` lived in two copies (`render/helpers` and `gles_render/helpers`) that had already diverged — one resolved through `find_font_opt`, the other inlined the candidate loop — and now live once in `system_fonts`. The two per-backend `OnceLock` default-font caches and `primary_handle` collapse into a single `system_fonts::default_handle`. Module docs and the `font_registry` caller are updated accordingly. Shared image validation and rect inflation. `draw_image_data`'s dimension check and its one-line warning were byte-duplicated across both backends and are now `render::helpers::validate_rgba_dims`. The six manual symmetric `Rect`-inflate literals in the GLES primitives reuse the existing `Rect::expand`. FrameState and DrawCtx de-duplication. The eleven `SurfaceState` fields the draw pass owns and threads through `DrawCtx` — `widget_rects`, the cursor / selection maps, the scroll state, `accessible_extras`, `prev_focused` / `prev_hovered` / `prev_pressed` — move into a `FrameState` sub-struct. The per-frame `build_draw_ctx` / `commit_draw_ctx` helpers can then borrow `&mut ss.frame`, disjoint from `ss.canvas` and `ss.pool`, so the four frame paths (software / GLES × full / partial) replace their duplicated `DrawCtx` construction and write-back with a single helper call each. A whole-`SurfaceState` borrow could not express this (partial borrows do not cross function boundaries), which is why the helpers take the sub-struct. `content_dirty` stays on `SurfaceState` — it is an invalidation flag, not frame state — and is reset at the call site. rich_text tests. Adds the previously-missing `tests.rs` for the `RichText` widget: one hit rect per visual line a link spans (the widget's core invariant), the single-line and no-link cases, preferred-size growth with hard line breaks, and `map_msg` range preservation — all headless against a software `Canvas`, with line counts forced by `\n` so they do not depend on any system font's measured width. Documentation. Fills the rustdoc gaps on the embedder-facing surface: `core::UiSurface` accessors, the `egl_context` public API, the `GlesCanvas` methods, `theme::typography` and `theme::error`, and the `RichText` / `Text` builders; adds a crate-level "Rendering backends" overview. `CHANGELOG.md` is added (0.2.0 / 0.1.0), and `docs/widgets.md` / `docs/cookbook.md` gain `rich_text`, `external`, and the CPU-draw / path-clip / externally-laid-out-tree recipes. `debian/changelog` gets the 0.2.0-1 entry. Private intra-doc links to `system_fonts` are demoted to code spans so `cargo doc` is warning-free. Packaging. The `libltk-dev` registry crate shipped a `Cargo.toml` declaring the `lookup` bench while the `Makefile` install copied only `src/`, so Cargo refused to parse the manifest over a missing `benches/lookup.rs`; the install now ships `benches/` as well (the file alone satisfies the parse — criterion is a dev-dependency and is not resolved when the crate is consumed as a library). `Cargo.toml` is bumped to 0.2.0 to match the package version and the `ltk-0.2.0` registry directory.
This commit is contained in:
@@ -19,6 +19,12 @@ use super::GlesCanvas;
|
||||
|
||||
impl GlesCanvas
|
||||
{
|
||||
/// Clip subsequent draws to `rects` via `glScissor`. The scissor is the
|
||||
/// bounding-box union of all rects clamped to the canvas — a coarse clip,
|
||||
/// unlike the software backend's exact per-rect mask, so pixels between
|
||||
/// disjoint rects are not culled. An empty slice clears the clip; an empty
|
||||
/// union installs a zero-area scissor so subsequent draws become no-ops.
|
||||
/// Replaces any active path clip, flushing its layer first.
|
||||
pub fn set_clip_rects( &mut self, rects: &[Rect] )
|
||||
{
|
||||
// A rect clip replaces any active path clip: flush its layer first.
|
||||
@@ -56,6 +62,8 @@ impl GlesCanvas
|
||||
self.set_scissor( Rect { x: x0, y: y0, width: x1 - x0, height: y1 - y0 } );
|
||||
}
|
||||
|
||||
/// Drop the active clip — disable the scissor test and flush any open path
|
||||
/// clip layer (compositing it back). Subsequent draws cover the whole canvas.
|
||||
pub fn clear_clip( &mut self )
|
||||
{
|
||||
// SAFETY: see `primitives.rs` module doc. `disable( SCISSOR_TEST )` is
|
||||
|
||||
@@ -20,6 +20,9 @@ use super::{ BorrowedGlesTexture, GlesCanvas };
|
||||
|
||||
impl GlesCanvas
|
||||
{
|
||||
/// Composite `src`'s FBO into this canvas at top-left `( dest_x, dest_y )`,
|
||||
/// premultiplied over. `src` must share this canvas's GL context (guaranteed
|
||||
/// for sub-canvases). Equivalent to [`Self::blit_fade_bottom`] with no fade.
|
||||
pub fn blit( &mut self, src: &GlesCanvas, dest_x: i32, dest_y: i32 )
|
||||
{
|
||||
self.blit_fade_bottom( src, dest_x, dest_y, 0.0 );
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>
|
||||
|
||||
//! Backend-neutral free helpers for the GLES renderer: MVP matrix
|
||||
//! construction, shader compilation, FBO / texture allocation,
|
||||
//! system-font lookup, small typed-handle extractors. Visible only
|
||||
//! within `crate::gles_render` — callers always go through
|
||||
//! `GlesCanvas`'s public methods.
|
||||
//! construction, shader compilation, FBO / texture allocation, small
|
||||
//! typed-handle extractors. Visible only within `crate::gles_render` —
|
||||
//! callers always go through `GlesCanvas`'s public methods. System-font
|
||||
//! resolution lives in `crate::system_fonts`.
|
||||
|
||||
use glow::HasContext;
|
||||
|
||||
@@ -218,40 +218,3 @@ pub( super ) fn native_framebuffer_id( framebuffer: glow::Framebuffer ) -> u32
|
||||
framebuffer.0.get()
|
||||
}
|
||||
|
||||
const SYSTEM_FONT_CANDIDATES: &[&str] =
|
||||
&[
|
||||
// Debian `fonts-sora` — the canonical path the `ltk-theme-default`
|
||||
// package depends on. Listed first so Sora wins as the default
|
||||
// font whenever that package is installed.
|
||||
"/usr/share/fonts/opentype/sora/Sora-Regular.otf",
|
||||
"/usr/share/fonts/truetype/sora/Sora-Regular.ttf",
|
||||
"/usr/share/fonts/sora/Sora-Regular.ttf",
|
||||
"/usr/share/fonts/TTF/Sora-Regular.ttf",
|
||||
"/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
|
||||
"/usr/share/fonts/liberation/LiberationSans-Regular.ttf",
|
||||
"/usr/share/fonts/truetype/freefont/FreeSans.ttf",
|
||||
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
|
||||
"/usr/share/fonts/dejavu/DejaVuSans.ttf",
|
||||
"/usr/share/fonts/TTF/DejaVuSans.ttf",
|
||||
];
|
||||
|
||||
/// Load the bytes of a default system font. Tries
|
||||
/// [`SYSTEM_FONT_CANDIDATES`] in order; falls back to the embedded
|
||||
/// [`crate::theme::fallback::FALLBACK_FONT`] (Sora Regular, ~50 KB,
|
||||
/// OFL 1.1) when nothing matches or the file cannot be read. Always
|
||||
/// returns usable bytes so canvas construction never panics on a
|
||||
/// system without the expected fonts.
|
||||
pub( super ) fn load_default_font_bytes() -> Vec<u8>
|
||||
{
|
||||
for path in SYSTEM_FONT_CANDIDATES.iter()
|
||||
{
|
||||
if std::path::Path::new( path ).exists()
|
||||
{
|
||||
if let Ok( bytes ) = std::fs::read( path )
|
||||
{
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
crate::theme::fallback::FALLBACK_FONT.to_vec()
|
||||
}
|
||||
|
||||
@@ -68,13 +68,8 @@ impl GlesCanvas
|
||||
/// level so the cache key is never seeded with a bogus mapping.
|
||||
pub fn draw_image_data( &mut self, rgba_data: &[u8], img_w: u32, img_h: u32, dest: Rect, opacity: f32 )
|
||||
{
|
||||
let expected = ( img_w as usize ).saturating_mul( img_h as usize ).saturating_mul( 4 );
|
||||
if img_w == 0 || img_h == 0 || rgba_data.len() != expected
|
||||
if !crate::render::helpers::validate_rgba_dims( "GlesCanvas", rgba_data, img_w, img_h )
|
||||
{
|
||||
eprintln!(
|
||||
"[ltk] GlesCanvas::draw_image_data: refusing draw — {}×{} declared, {} bytes provided, expected {}",
|
||||
img_w, img_h, rgba_data.len(), expected,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,8 @@
|
||||
//! * `image` — `GlesCanvas::draw_image_data`.
|
||||
//! * `shaders` — GLSL ES 1.00 shader sources (const strings).
|
||||
//! * `helpers` — free functions: `ortho_rect`, `compile_program`,
|
||||
//! `alloc_fbo_tex`, `upload_*_texture`, handle extractors,
|
||||
//! `find_font` + `SYSTEM_FONT_CANDIDATES`.
|
||||
//! `alloc_fbo_tex`, `upload_*_texture`, handle extractors.
|
||||
//! System-font resolution lives in `crate::system_fonts`.
|
||||
//! * `raii` — `FboBinding` / `ProgramBinding` scoped guards for the
|
||||
//! handful of operations that change global GL state for a scope
|
||||
//! and must guarantee restoration even on early return / panic
|
||||
@@ -135,7 +135,7 @@ pub struct GlesCanvas
|
||||
{
|
||||
pub gl: Arc<glow::Context>,
|
||||
pub version: GlesVersion,
|
||||
/// Default font loaded from the system via `helpers::find_font`.
|
||||
/// Default font loaded from the system via `system_fonts::default_handle`.
|
||||
/// Kept as a fallback for callers that do not route through the
|
||||
/// theme registry.
|
||||
pub font: Arc<Font>,
|
||||
|
||||
@@ -51,11 +51,17 @@ impl GlesCanvas
|
||||
r_x1 <= clip.x || c_x1 <= r_x0 || r_y1 <= clip.y || c_y1 <= r_y0
|
||||
}
|
||||
|
||||
/// Fill an arbitrary vector path (commands in surface coordinates) with a
|
||||
/// solid colour. CPU fallback: rasterised with tiny-skia into a bbox-sized
|
||||
/// pixmap and blitted as a transient texture — there is no GPU path shader.
|
||||
pub fn fill_path( &mut self, cmds: &[ PathCmd ], color: Color )
|
||||
{
|
||||
self.rasterise_path( cmds, color, None );
|
||||
}
|
||||
|
||||
/// Stroke an arbitrary vector path (commands in surface coordinates) with a
|
||||
/// centered stroke of `width` px. Same tiny-skia-into-texture CPU fallback as
|
||||
/// [`Self::fill_path`].
|
||||
pub fn stroke_path( &mut self, cmds: &[ PathCmd ], color: Color, width: f32 )
|
||||
{
|
||||
self.rasterise_path( cmds, color, Some( width ) );
|
||||
@@ -114,6 +120,10 @@ impl GlesCanvas
|
||||
unsafe { self.gl.delete_texture( tex ); }
|
||||
}
|
||||
|
||||
/// Fill `rect` with a solid colour, with per-corner rounding from `corners`.
|
||||
/// Coverage (including the rounded corners) comes from an SDF in the rect
|
||||
/// shader; `color.a` is multiplied by `global_alpha`. Culled early when the
|
||||
/// rect falls entirely outside the active scissor.
|
||||
pub fn fill_rect( &mut self, rect: Rect, color: Color, corners: Corners )
|
||||
{
|
||||
if self.rect_culled( rect, 1.0 ) { return; }
|
||||
@@ -124,13 +134,7 @@ impl GlesCanvas
|
||||
// stay anchored to the original rect — `u_pad` lets the shader
|
||||
// remap `v_uv` from the larger quad back into rect-local space.
|
||||
let pad = 1.0_f32;
|
||||
let expanded = Rect
|
||||
{
|
||||
x: rect.x - pad,
|
||||
y: rect.y - pad,
|
||||
width: rect.width + 2.0 * pad,
|
||||
height: rect.height + 2.0 * pad,
|
||||
};
|
||||
let expanded = rect.expand( pad );
|
||||
let mvp = ortho_rect( self.width, self.height, expanded );
|
||||
let alpha = color.a * self.global_alpha;
|
||||
// SAFETY: see module doc. `u_rect_stroke = 0.0` triggers the fill
|
||||
@@ -221,13 +225,7 @@ impl GlesCanvas
|
||||
self.activate_target();
|
||||
// See fill_rect for the rationale on the 1 px quad pad.
|
||||
let pad = 1.0_f32;
|
||||
let expanded = Rect
|
||||
{
|
||||
x: rect.x - pad,
|
||||
y: rect.y - pad,
|
||||
width: rect.width + 2.0 * pad,
|
||||
height: rect.height + 2.0 * pad,
|
||||
};
|
||||
let expanded = rect.expand( pad );
|
||||
let mvp = ortho_rect( self.width, self.height, expanded );
|
||||
// SAFETY: see module doc. `tex` is the cached LUT for `g.stops`
|
||||
// produced by `ensure_lut_texture` above (RGBA8, sampler unit 0);
|
||||
@@ -269,13 +267,7 @@ impl GlesCanvas
|
||||
self.activate_target();
|
||||
// See fill_rect for the rationale on the 1 px quad pad.
|
||||
let pad = 1.0_f32;
|
||||
let expanded = Rect
|
||||
{
|
||||
x: rect.x - pad,
|
||||
y: rect.y - pad,
|
||||
width: rect.width + 2.0 * pad,
|
||||
height: rect.height + 2.0 * pad,
|
||||
};
|
||||
let expanded = rect.expand( pad );
|
||||
let mvp = ortho_rect( self.width, self.height, expanded );
|
||||
// SAFETY: see module doc. Same LUT contract as the linear path
|
||||
// above. `g.center` and `g.radius` are finite fractional values
|
||||
@@ -422,22 +414,10 @@ impl GlesCanvas
|
||||
// active scissor is culled before the shader runs, so the
|
||||
// snapshot only needs to cover the intersection.
|
||||
let pad = 1.0_f32;
|
||||
let snap_rect = Rect
|
||||
{
|
||||
x: target.x - pad,
|
||||
y: target.y - pad,
|
||||
width: target.width + 2.0 * pad,
|
||||
height: target.height + 2.0 * pad,
|
||||
};
|
||||
let snap_rect = target.expand( pad );
|
||||
self.snapshot_fbo_region_tight( snap_rect );
|
||||
self.activate_target();
|
||||
let expanded = Rect
|
||||
{
|
||||
x: target.x - pad,
|
||||
y: target.y - pad,
|
||||
width: target.width + 2.0 * pad,
|
||||
height: target.height + 2.0 * pad,
|
||||
};
|
||||
let expanded = target.expand( pad );
|
||||
let mvp = ortho_rect( self.width, self.height, expanded );
|
||||
let aux_tex = self.aux_a.expect( "snapshotted" ).1;
|
||||
// SAFETY: see module doc. `aux_tex` was just populated by
|
||||
@@ -475,13 +455,7 @@ impl GlesCanvas
|
||||
// of terminating at the surface rect. Same rationale as
|
||||
// fill_rect. `u_size` / `u_radii` stay anchored to `target`.
|
||||
let pad = 1.0_f32;
|
||||
let expanded = Rect
|
||||
{
|
||||
x: target.x - pad,
|
||||
y: target.y - pad,
|
||||
width: target.width + 2.0 * pad,
|
||||
height: target.height + 2.0 * pad,
|
||||
};
|
||||
let expanded = target.expand( pad );
|
||||
let mvp = ortho_rect( self.width, self.height, expanded );
|
||||
// SAFETY: see module doc. We swap the global blend state for the
|
||||
// duration of one draw and restore the canvas-wide default
|
||||
@@ -550,13 +524,7 @@ impl GlesCanvas
|
||||
// would shift the zero-line outward and, in the circle case
|
||||
// (radius = size/2), turn the result into a rounded square.
|
||||
let pad = half + 1.0;
|
||||
let expanded = Rect
|
||||
{
|
||||
x: rect.x - pad,
|
||||
y: rect.y - pad,
|
||||
width: rect.width + 2.0 * pad,
|
||||
height: rect.height + 2.0 * pad,
|
||||
};
|
||||
let expanded = rect.expand( pad );
|
||||
let mvp = ortho_rect( self.width, self.height, expanded );
|
||||
let alpha = color.a * self.global_alpha;
|
||||
// SAFETY: see module doc. `u_rect_stroke = width > 0.0` triggers
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
//! elided (programs, VAO, default font all come from the parent).
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{ Arc, OnceLock };
|
||||
use std::sync::Arc;
|
||||
|
||||
use fontdue::{ Font, FontSettings, LineMetrics, Metrics };
|
||||
use fontdue::{ Font, LineMetrics, Metrics };
|
||||
use glow::HasContext;
|
||||
|
||||
use crate::theme::{ FontRegistry, FontStyle };
|
||||
|
||||
use super::helpers::{ alloc_fbo_tex, bytemuck_cast_slice, compile_program, compile_program_with_attribs, load_default_font_bytes };
|
||||
use super::helpers::{ alloc_fbo_tex, bytemuck_cast_slice, compile_program, compile_program_with_attribs };
|
||||
use super::shaders::
|
||||
{
|
||||
BACKDROP_BLUR_H_FRAG_SRC, BACKDROP_COMPOSITE_FRAG_SRC,
|
||||
@@ -35,34 +35,19 @@ use super::shaders::
|
||||
};
|
||||
use super::{ GlesCanvas, GlesVersion };
|
||||
|
||||
/// Process-wide cache of the GLES path's default font. Avoids
|
||||
/// re-reading + re-parsing the small Sora face on every surface
|
||||
/// bring-up. The fallback chain (Noto Sans / CJK / Devanagari / …)
|
||||
/// is owned by the crate-private system-fonts module and loaded
|
||||
/// lazily per codepoint, not per canvas.
|
||||
static DEFAULT_FONT_GLES: OnceLock<crate::system_fonts::FontHandle> = OnceLock::new();
|
||||
|
||||
fn default_handle_gles() -> crate::system_fonts::FontHandle
|
||||
{
|
||||
DEFAULT_FONT_GLES.get_or_init( ||
|
||||
{
|
||||
let bytes = load_default_font_bytes();
|
||||
let font = Font::from_bytes( bytes.as_slice(), FontSettings::default() )
|
||||
.expect( "bad font" );
|
||||
crate::system_fonts::FontHandle
|
||||
{
|
||||
font: Arc::new( font ),
|
||||
bytes: Arc::new( bytes ),
|
||||
face: 0,
|
||||
}
|
||||
} ).clone()
|
||||
}
|
||||
|
||||
impl GlesCanvas
|
||||
{
|
||||
/// Build a GPU canvas of `width × height` physical px on an already-current
|
||||
/// EGL/GLES context. Compiles every shader program, looks up its uniforms,
|
||||
/// uploads the shared quad geometry and the glyph atlas (format chosen per ES
|
||||
/// profile), then allocates the persistent shadow FBO and makes it the active
|
||||
/// draw target — every draw writes into the FBO, and [`Self::present`] is the
|
||||
/// only call that blits it to the default framebuffer. `dpi_scale` and
|
||||
/// `global_alpha` start at 1.0; the default font comes from the process-wide
|
||||
/// cached handle. Panics if the FBO is incomplete or a program fails to link.
|
||||
pub fn new( gl: Arc<glow::Context>, version: GlesVersion, width: u32, height: u32 ) -> Self
|
||||
{
|
||||
let font_handle = default_handle_gles();
|
||||
let font_handle = crate::system_fonts::default_handle();
|
||||
let font = font_handle.font.clone();
|
||||
let font_bytes = font_handle.bytes.clone();
|
||||
let font_face = font_handle.face;
|
||||
@@ -703,6 +688,7 @@ impl GlesCanvas
|
||||
}
|
||||
}
|
||||
|
||||
/// `( width, height )` of the FBO in physical px.
|
||||
pub fn size( &self ) -> ( u32, u32 ) { ( self.width, self.height ) }
|
||||
|
||||
/// Discard all cached gradient LUT textures. Call after a theme change
|
||||
@@ -723,14 +709,19 @@ impl GlesCanvas
|
||||
}
|
||||
}
|
||||
|
||||
/// DPI scale factor applied to font sizes before rasterisation.
|
||||
pub fn dpi_scale( &self ) -> f32 { self.dpi_scale }
|
||||
|
||||
/// Set the DPI scale factor applied to font sizes.
|
||||
pub fn set_dpi_scale( &mut self, s: f32 ) { self.dpi_scale = s; }
|
||||
|
||||
/// Global alpha multiplier applied to every draw (0.0 transparent, 1.0 opaque).
|
||||
pub fn global_alpha( &self ) -> f32 { self.global_alpha }
|
||||
|
||||
/// Set the global alpha multiplier applied to every draw.
|
||||
pub fn set_global_alpha( &mut self, a: f32 ) { self.global_alpha = a; }
|
||||
|
||||
/// The canvas default font, used when no specific face is resolved.
|
||||
pub fn font( &self ) -> &Font { &self.font }
|
||||
|
||||
/// Install a theme font registry so [`Self::font_for`] can resolve
|
||||
@@ -765,11 +756,15 @@ impl GlesCanvas
|
||||
crate::system_fonts::lookup( ch ).unwrap_or_else( || Arc::clone( &self.font ) )
|
||||
}
|
||||
|
||||
/// Glyph metrics for `ch` at `size` logical px, resolved through the fallback
|
||||
/// chain and pre-scaled by `dpi_scale`.
|
||||
pub fn font_metrics( &self, ch: char, size: f32 ) -> Metrics
|
||||
{
|
||||
self.font_for_char( ch ).metrics( ch, size * self.dpi_scale )
|
||||
}
|
||||
|
||||
/// Horizontal line metrics of the default font at `size` px (`None` if the
|
||||
/// font lacks them). Not pre-scaled by `dpi_scale`.
|
||||
pub fn font_line_metrics( &self, size: f32 ) -> Option<LineMetrics>
|
||||
{
|
||||
self.font.horizontal_line_metrics( size )
|
||||
|
||||
@@ -34,11 +34,19 @@ fn font_id( font: &Arc<Font> ) -> usize
|
||||
|
||||
impl GlesCanvas
|
||||
{
|
||||
/// Draw a single shaped line of `text` with the canvas default font and the
|
||||
/// system fallback chain, baseline at `( x, y )` in surface px. `size` is in
|
||||
/// logical px and scaled by `dpi_scale` before rasterisation. Glyphs are
|
||||
/// shelf-packed into the GPU atlas and the whole line is flushed in one batched
|
||||
/// draw call.
|
||||
pub fn draw_text( &mut self, text: &str, x: f32, y: f32, size: f32, color: Color )
|
||||
{
|
||||
self.draw_text_inner( text, x, y, size, color, None );
|
||||
}
|
||||
|
||||
/// Like [`Self::draw_text`] but leads the resolver with `font` instead of the
|
||||
/// canvas default, falling back to the system chain for codepoints it does not
|
||||
/// cover.
|
||||
pub fn draw_text_with_font( &mut self, text: &str, x: f32, y: f32, size: f32, color: Color, font: &Arc<Font> )
|
||||
{
|
||||
self.draw_text_inner( text, x, y, size, color, Some( font ) );
|
||||
@@ -287,11 +295,16 @@ impl GlesCanvas
|
||||
unsafe { self.gl.bind_texture( glow::TEXTURE_2D, None ); }
|
||||
}
|
||||
|
||||
/// Advance width of one shaped line of `text` in surface px, using the canvas
|
||||
/// default font and the system fallback chain. Shapes through the same path as
|
||||
/// [`Self::draw_text`] (so kerning and fallback advances match) without drawing.
|
||||
pub fn measure_text( &self, text: &str, size: f32 ) -> f32
|
||||
{
|
||||
self.measure_inner( text, size, None )
|
||||
}
|
||||
|
||||
/// Like [`Self::measure_text`] but measures with `font` leading the resolver,
|
||||
/// so text laid out at one weight and drawn at another stays aligned.
|
||||
pub fn measure_text_with_font( &self, text: &str, size: f32, font: &Arc<Font> ) -> f32
|
||||
{
|
||||
self.measure_inner( text, size, Some( font ) )
|
||||
|
||||
Reference in New Issue
Block a user