#[cfg(feature = "nostd")]
use alloc::{sync::Arc, vec::Vec};
#[cfg(not(feature = "nostd"))]
use std::{sync::Arc, vec::Vec};
use tiny_skia::Transform;
mod blur_tile;
mod coverage;
#[cfg(not(feature = "nostd"))]
#[derive(Clone, PartialEq, Eq, Hash)]
struct RunCoverageKey {
text: String,
font: String,
size: u32,
spacing: u32,
bold: bool,
italic: bool,
outline: Option<(u32, u32)>,
shadow: Option<(u32, u32)>,
transform: [u32; 6],
}
#[cfg(not(feature = "nostd"))]
#[derive(Clone, PartialEq, Eq, Hash)]
pub(super) struct BlurTileKey {
pub(super) text: String,
pub(super) font: String,
pub(super) size: u32,
pub(super) spacing: u32,
pub(super) bold: bool,
pub(super) italic: bool,
pub(super) blur: u32,
pub(super) fill: [u8; 4],
pub(super) outline: Option<(u32, u32, [u8; 4])>,
pub(super) shadow: Option<([u8; 4], u32, u32)>,
}
#[cfg(not(feature = "nostd"))]
pub(super) struct BlurTile {
pub(super) data: Arc<Vec<u8>>,
pub(super) width: u32,
pub(super) height: u32,
}
#[cfg(not(feature = "nostd"))]
struct CachedCoverage {
fill: Option<(crate::backends::coverage::CoverageTile, i32, i32)>,
outline: Option<(crate::backends::coverage::CoverageTile, i32, i32)>,
}
#[cfg(not(feature = "nostd"))]
std::thread_local! {
static RUN_COVERAGE: std::cell::RefCell<std::collections::HashMap<RunCoverageKey, CachedCoverage>> =
std::cell::RefCell::new(std::collections::HashMap::new());
pub(super) static BLUR_TILES: std::cell::RefCell<std::collections::HashMap<BlurTileKey, std::sync::Arc<BlurTile>>> =
std::cell::RefCell::new(std::collections::HashMap::new());
pub(super) static EMIT_SINK: std::cell::RefCell<Option<Vec<crate::backends::coverage::RenderBitmap>>> =
const { std::cell::RefCell::new(None) };
pub(super) static DIRTY_BBOX: std::cell::RefCell<Option<(i32, i32, i32, i32)>> =
const { std::cell::RefCell::new(None) };
}
#[cfg(not(feature = "nostd"))]
#[allow(clippy::type_complexity)]
fn coverage_key(
data: &crate::pipeline::TextData,
base_transform: Transform,
baseline_y: f32,
) -> Option<(
RunCoverageKey,
Option<([u8; 4], f32, f32)>,
Option<([u8; 4], f32, f32)>,
Transform,
[u8; 4],
Option<(f32, [u8; 4])>,
)> {
use crate::pipeline::TextEffect;
let mut outline: Option<([u8; 4], f32, f32)> = None;
let mut shadow: Option<([u8; 4], f32, f32)> = None;
let mut bold = false;
let mut italic = false;
let mut fill_color = data.color;
let mut karaoke_sweep: Option<(f32, [u8; 4])> = None;
for effect in &data.effects {
match effect {
TextEffect::Outline {
color,
width_x,
width_y,
} => outline = Some((*color, *width_x, *width_y)),
TextEffect::Shadow {
color,
x_offset,
y_offset,
} => shadow = Some((*color, *x_offset, *y_offset)),
TextEffect::Bold => bold = true,
TextEffect::Italic => italic = true,
TextEffect::Rotation { .. } | TextEffect::Scale { .. } | TextEffect::Shear { .. } => {}
TextEffect::Karaoke {
progress,
style,
secondary,
} => {
if *style == 0 {
fill_color = if *progress > 0.0 {
data.color
} else {
*secondary
};
} else {
karaoke_sweep = Some((*progress, *secondary));
}
}
_ => return None,
}
}
let local = base_transform.post_translate(-data.x, -baseline_y);
let key = RunCoverageKey {
text: data.text.clone(),
font: data.font_family.clone(),
size: data.font_size.to_bits(),
spacing: data.spacing.to_bits(),
bold,
italic,
outline: outline.map(|(_, wx, wy)| (wx.to_bits(), wy.to_bits())),
shadow: shadow.map(|(_, x, y)| (x.to_bits(), y.to_bits())),
transform: [
local.sx.to_bits(),
local.kx.to_bits(),
local.ky.to_bits(),
local.sy.to_bits(),
local.tx.to_bits(),
local.ty.to_bits(),
],
};
Some((key, outline, shadow, local, fill_color, karaoke_sweep))
}