#[cfg(feature = "nostd")]
use alloc::{vec, vec::Vec};
#[cfg(not(feature = "nostd"))]
use std::vec::Vec;
use tiny_skia::Transform;
#[cfg(not(feature = "nostd"))]
fn emit_cached(
cached: &super::CachedCoverage,
anchor: (i32, i32),
colors: super::super::LayerColors,
karaoke_sweep: Option<(f32, [u8; 4])>,
) -> Vec<crate::backends::coverage::RenderBitmap> {
use crate::backends::coverage::RenderBitmap;
let (anchor_x, anchor_y) = anchor;
let (outline_color, shadow, fill_color) = colors;
let mut out = Vec::new();
let bitmap =
|tile: &crate::backends::coverage::CoverageTile, x: i32, y: i32, color: [u8; 4]| {
RenderBitmap::Coverage {
width: tile.width,
height: tile.height,
coverage: tile.data.clone(),
x,
y,
color,
}
};
if let (Some((color, (dx, dy))), Some((tile, ox, oy))) = (shadow, &cached.fill) {
out.push(bitmap(tile, anchor_x + ox + dx, anchor_y + oy + dy, color));
}
if let (Some(color), Some((tile, ox, oy))) = (outline_color, &cached.outline) {
out.push(bitmap(tile, anchor_x + ox, anchor_y + oy, color));
}
if let Some((tile, ox, oy)) = &cached.fill {
let (x, y) = (anchor_x + ox, anchor_y + oy);
match karaoke_sweep {
Some((progress, secondary)) if progress <= 0.0 => {
out.push(bitmap(tile, x, y, secondary));
}
Some((progress, _)) if progress >= 1.0 => {
out.push(bitmap(tile, x, y, fill_color));
}
Some((progress, secondary)) => {
out.push(bitmap(tile, x, y, secondary));
let cols = (1.0 + progress * (tile.width as f32 - 2.0))
.round()
.clamp(0.0, tile.width as f32) as u32;
if cols > 0 {
out.push(RenderBitmap::Coverage {
width: cols,
height: tile.height,
coverage: crop_coverage_columns(tile, cols),
x,
y,
color: fill_color,
});
}
}
None => out.push(bitmap(tile, x, y, fill_color)),
}
}
out
}
#[cfg(not(feature = "nostd"))]
fn crop_coverage_columns(
tile: &crate::backends::coverage::CoverageTile,
cols: u32,
) -> std::sync::Arc<Vec<u8>> {
let cols = cols.min(tile.width) as usize;
let width = tile.width as usize;
let mut data = vec![0u8; cols * tile.height as usize];
for y in 0..tile.height as usize {
let src = y * width;
let dst = y * cols;
data[dst..dst + cols].copy_from_slice(&tile.data[src..src + cols]);
}
std::sync::Arc::new(data)
}
#[cfg(not(feature = "nostd"))]
pub(super) fn composite_cached(
dst: &mut [u8],
pixmap_w: u32,
pixmap_h: u32,
cached: &super::CachedCoverage,
anchor: (i32, i32),
colors: super::super::LayerColors,
karaoke_sweep: Option<(f32, [u8; 4])>,
) {
use crate::backends::coverage::composite_bitmap;
let bitmaps = emit_cached(cached, anchor, colors, karaoke_sweep);
super::EMIT_SINK.with(|sink| {
let mut sink = sink.borrow_mut();
if let Some(sink) = sink.as_mut() {
sink.extend(bitmaps);
} else {
for bitmap in &bitmaps {
composite_bitmap(dst, pixmap_w, pixmap_h, bitmap);
}
}
});
}
impl super::super::SoftwareBackend {
#[cfg(not(feature = "nostd"))]
pub(in crate::backends::software) fn blur_tile_hit(
&mut self,
data: &crate::pipeline::TextData,
bold: bool,
italic: bool,
baseline_y: f32,
ascent: f32,
) -> bool {
use crate::pipeline::TextEffect;
let eligible = data.effects.iter().all(|e| {
matches!(
e,
TextEffect::Blur { .. }
| TextEffect::Outline { .. }
| TextEffect::Shadow { .. }
| TextEffect::Bold
| TextEffect::Italic
| TextEffect::Rotation { .. }
| TextEffect::Scale { .. }
| TextEffect::Shear { .. }
)
});
if !eligible {
return false;
}
let Some(radius) = data.effects.iter().find_map(|e| match e {
TextEffect::Blur { radius } => Some(*radius),
_ => None,
}) else {
return false;
};
let outline_info = data.effects.iter().find_map(|e| match e {
TextEffect::Outline {
color,
width_x,
width_y,
} => Some((*color, *width_x, *width_y)),
_ => None,
});
let shadow_info = data.effects.iter().find_map(|e| match e {
TextEffect::Shadow {
color,
x_offset,
y_offset,
} => Some((*color, *x_offset, *y_offset)),
_ => None,
});
let key = super::BlurTileKey {
text: data.text.clone(),
font: data.font_family.clone(),
size: data.font_size.to_bits(),
spacing: data.spacing.to_bits(),
bold,
italic,
blur: radius.to_bits(),
fill: data.color,
outline: outline_info.map(|(c, wx, wy)| (wx.to_bits(), wy.to_bits(), c)),
shadow: shadow_info.map(|(c, x, y)| (c, x.to_bits(), y.to_bits())),
};
let Some(tile) = super::BLUR_TILES.with(|c| c.borrow().get(&key).cloned()) else {
return false;
};
let blur_size = (radius * 3.0).ceil();
let x = data.x - blur_size;
let y = baseline_y - blur_size - ascent;
let emitted = super::EMIT_SINK.with(|sink| {
if let Some(list) = sink.borrow_mut().as_mut() {
list.push(crate::backends::coverage::RenderBitmap::Rgba {
width: tile.width,
height: tile.height,
pixels: tile.data.clone(),
x: x.round() as i32,
y: y.round() as i32,
});
true
} else {
false
}
});
if emitted {
return true;
}
if let Some(pixref) =
tiny_skia::PixmapRef::from_bytes(tile.data.as_slice(), tile.width, tile.height)
{
let paint = tiny_skia::PixmapPaint {
blend_mode: tiny_skia::BlendMode::SourceOver,
..Default::default()
};
self.pixmap
.draw_pixmap(0, 0, pixref, &paint, Transform::from_translate(x, y), None);
}
true
}
}