First commit. Version 0.1.0
This commit is contained in:
124
src/render/setup.rs
Normal file
124
src/render/setup.rs
Normal file
@@ -0,0 +1,124 @@
|
||||
// SPDX-License-Identifier: LGPL-2.1-only
|
||||
// Copyright (C) 2026 Liberux Labs, S. L. <info@liberux.net>
|
||||
|
||||
//! Construction + accessors for [`SoftwareCanvas`]: new / sub_canvas
|
||||
//! / resize plus the font-registry installer and `blit`.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{ Arc, OnceLock };
|
||||
|
||||
use fontdue::{ Font, FontSettings };
|
||||
use tiny_skia::{ Pixmap, PixmapPaint, Transform };
|
||||
|
||||
use crate::theme::{ FontRegistry, FontStyle };
|
||||
|
||||
use super::helpers::load_default_font_bytes;
|
||||
use super::SoftwareCanvas;
|
||||
|
||||
/// Process-wide cache of the default font face. Avoids re-reading +
|
||||
/// re-parsing the file on every surface bring-up. Sora is small
|
||||
/// (~50 KB) so the cost was minor in absolute terms — but a layer
|
||||
/// shell that brings up a launcher overlay, a QS panel, a calendar
|
||||
/// popup and a handful of toast surfaces would still pay the parse
|
||||
/// cost a dozen times in a single session, all of which is wasted
|
||||
/// work.
|
||||
static DEFAULT_FONT: OnceLock<Arc<Font>> = OnceLock::new();
|
||||
|
||||
fn default_font() -> Arc<Font>
|
||||
{
|
||||
Arc::clone( DEFAULT_FONT.get_or_init( ||
|
||||
{
|
||||
let bytes = load_default_font_bytes();
|
||||
let font = Font::from_bytes( bytes.as_slice(), FontSettings::default() )
|
||||
.expect( "bad font" );
|
||||
Arc::new( font )
|
||||
} ) )
|
||||
}
|
||||
|
||||
impl SoftwareCanvas
|
||||
{
|
||||
/// Create a canvas of the given pixel dimensions, loading a system font.
|
||||
///
|
||||
/// Fallback fonts (Noto Sans / CJK / Devanagari / …) are NOT loaded
|
||||
/// here — they are owned by the crate-private system-fonts chain
|
||||
/// and loaded lazily per codepoint. A canvas that only ever paints
|
||||
/// Latin text will never touch those files; the first non-Latin
|
||||
/// glyph triggers a single targeted load and the rest of the
|
||||
/// process reuses the cached `Arc<Font>`.
|
||||
pub fn new( width: u32, height: u32 ) -> Self
|
||||
{
|
||||
Self
|
||||
{
|
||||
pixmap: Pixmap::new( width, height ).expect( "pixmap" ),
|
||||
font: default_font(),
|
||||
font_registry: None,
|
||||
dpi_scale: 1.0,
|
||||
global_alpha: 1.0,
|
||||
glyph_cache: HashMap::new(),
|
||||
clip_mask: None,
|
||||
clip_bounds: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a blank sub-canvas sharing the same font and DPI scale.
|
||||
pub fn sub_canvas( &self, width: u32, height: u32 ) -> SoftwareCanvas
|
||||
{
|
||||
SoftwareCanvas
|
||||
{
|
||||
pixmap: Pixmap::new( width, height ).expect( "pixmap" ),
|
||||
font: Arc::clone( &self.font ),
|
||||
font_registry: self.font_registry.as_ref().map( Arc::clone ),
|
||||
dpi_scale: self.dpi_scale,
|
||||
global_alpha: self.global_alpha,
|
||||
glyph_cache: HashMap::new(),
|
||||
clip_mask: None,
|
||||
clip_bounds: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Install a theme font registry so [`Self::font_for`] can
|
||||
/// resolve family+weight+style triples declared by the theme's
|
||||
/// `fonts` block. The default [`Self::font`] stays in place as a
|
||||
/// fallback.
|
||||
pub fn set_font_registry( &mut self, registry: Arc<FontRegistry> )
|
||||
{
|
||||
self.font_registry = Some( registry );
|
||||
}
|
||||
|
||||
/// Resolve a specific font from the theme registry, falling back
|
||||
/// to the canvas' default [`Self::font`] when no registry is
|
||||
/// installed or the triple cannot be satisfied.
|
||||
pub fn font_for( &self, family: &str, weight: u16, style: FontStyle ) -> Arc<Font>
|
||||
{
|
||||
self.font_registry
|
||||
.as_ref()
|
||||
.and_then( |r| r.resolve( family, weight, style ) )
|
||||
.unwrap_or_else( || Arc::clone( &self.font ) )
|
||||
}
|
||||
|
||||
/// Pick the right font for `ch`. Tries the primary [`Self::font`]
|
||||
/// first; on a miss, delegates to the crate-private system-fonts
|
||||
/// fallback chain (lazy load of the relevant Noto pack). Falls
|
||||
/// back to the primary (which paints a `.notdef` box) when no
|
||||
/// installed fallback covers the codepoint.
|
||||
pub fn font_for_char( &self, ch: char ) -> Arc<Font>
|
||||
{
|
||||
if self.font.lookup_glyph_index( ch ) != 0
|
||||
{
|
||||
return Arc::clone( &self.font );
|
||||
}
|
||||
crate::system_fonts::lookup( ch ).unwrap_or_else( || Arc::clone( &self.font ) )
|
||||
}
|
||||
|
||||
pub fn blit( &mut self, src: &SoftwareCanvas, dest_x: i32, dest_y: i32 )
|
||||
{
|
||||
let paint = PixmapPaint::default();
|
||||
let t = Transform::from_translate( dest_x as f32, dest_y as f32 );
|
||||
self.pixmap.draw_pixmap( 0, 0, src.pixmap.as_ref(), &paint, t, self.clip_mask.as_ref() );
|
||||
}
|
||||
|
||||
pub fn resize( &mut self, width: u32, height: u32 )
|
||||
{
|
||||
self.pixmap = Pixmap::new( width, height ).expect( "pixmap" );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user