use core::cell::Cell;
use core::ops::RangeInclusive;
use glifo::{
AtlasCacher, AtlasPaint, AtlasSlot, DrawSink, GLYPH_PADDING, Glyph, GlyphPrepCache,
GlyphRenderer, GlyphRun, GlyphRunBackend,
};
use kurbo::{Affine, BezPath, Rect, Shape};
use peniko::color::palette::css::BLACK;
use peniko::color::{AlphaColor, Srgb};
use peniko::{BlendMode, Extend, ImageQuality, ImageSampler};
use peniko::{Brush, Fill};
use vello_common::clip::PathDataRef;
use vello_common::encode::{EncodedImage, EncodedPaint};
use vello_common::geometry::RectU16;
use vello_common::paint::{Image, ImageSource, IndexedPaint, Paint, PaintType, Tint, TintMode};
use vello_common::strip_generator::{StripGenerator, StripStorage};
use crate::cache::images::AtlasRegion;
use crate::compile::clip::ClipStack;
use crate::compile::cull::generate_fill;
use crate::compile::paint::{encode_brush, encode_glyph_run_record};
use crate::compile::{
CompiledFrame, DepthCounter, EngineDraw, FLATTEN_TOLERANCE, GlyphQuad, GlyphSlot,
};
use crate::gpu::atlas::x_y_advances;
use crate::text::ResidentPlan;
use crate::text::atlas_policy::{AtlasPolicy, PlacedGlyph, ResidentAnswer, subpixel_bucket};
use crate::text::color::{ColorGlyph, ColorLayer, LayerShape};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub(crate) struct GlyphRunOutcome {
pub(crate) drawn: u32,
pub(crate) skipped: u32,
pub(crate) resident: bool,
}
#[derive(Debug, Clone)]
enum SinkPaint {
Run,
Layer(Brush),
Atlas(Image),
Unsupported,
}
#[derive(Debug, Clone)]
pub(crate) struct GlyphSinkState {
transform: Affine,
paint_transform: Affine,
paint: SinkPaint,
tint: Option<Tint>,
}
#[derive(Debug, Clone)]
pub(crate) struct PendingGlyphQuad {
image: Image,
area: Rect,
transform: Affine,
paint_transform: Affine,
tint: Option<Tint>,
slot: AtlasSlot,
}
impl PendingGlyphQuad {
fn quad_rect(&self) -> Option<[i64; 4]> {
if self.image.sampler.quality != ImageQuality::Low
|| self.paint_transform != Affine::IDENTITY
{
return None;
}
let [a, b, c, d, tx, ty] = self.transform.as_coeffs();
if a != 1.0 || b != 0.0 || c != 0.0 || d != 1.0 || !whole_pixel(tx) || !whole_pixel(ty) {
return None;
}
let width = f64::from(self.slot.width);
let height = f64::from(self.slot.height);
if self.area != Rect::new(0.0, 0.0, width, height) {
return None;
}
let (x0, y0) = (tx as i64, ty as i64);
Some([
x0,
y0,
x0 + i64::from(self.slot.width),
y0 + i64::from(self.slot.height),
])
}
}
fn whole_pixel(value: f64) -> bool {
value.fract() == 0.0 && on_quad_grid(value)
}
fn on_quad_grid(value: f64) -> bool {
value.is_finite() && value.abs() <= f64::from(1_u32 << 30)
}
#[derive(Debug, Clone, Copy)]
struct RunRecord {
page: u32,
tint: Option<Tint>,
paint_index: usize,
}
#[derive(Debug, Default)]
pub(crate) struct GlyphQuadScratch {
pending: Vec<PendingGlyphQuad>,
records: Vec<RunRecord>,
placed: Vec<PlacedGlyph>,
origins: Vec<(f64, f64)>,
slots: Vec<AtlasSlot>,
quads: Vec<([i64; 4], AtlasSlot)>,
}
#[derive(Debug)]
pub(crate) struct GlyphQuadTargets<'a> {
pub(crate) scratch: &'a mut GlyphQuadScratch,
pub(crate) window: RectU16,
}
#[derive(Debug)]
pub(crate) struct EngineGlyphSink<'a> {
generator: &'a mut StripGenerator,
clips: &'a mut ClipStack,
frame: &'a mut CompiledFrame,
depth: &'a mut DepthCounter,
run_paint: Paint,
context_paint: PaintType,
state: GlyphSinkState,
atlas_slot: Cell<Option<AtlasSlot>>,
color: ColorGlyph,
outcome: GlyphRunOutcome,
quads: Option<GlyphQuadTargets<'a>>,
collecting: bool,
}
impl<'a> EngineGlyphSink<'a> {
pub(crate) fn new(
generator: &'a mut StripGenerator,
clips: &'a mut ClipStack,
frame: &'a mut CompiledFrame,
depth: &'a mut DepthCounter,
run_paint: Paint,
context_paint: PaintType,
mut quads: Option<GlyphQuadTargets<'a>>,
) -> Self {
if let Some(targets) = quads.as_mut() {
targets.scratch.pending.clear();
targets.scratch.records.clear();
}
let collecting = quads.is_some();
Self {
generator,
clips,
frame,
depth,
run_paint,
context_paint,
state: GlyphSinkState {
transform: Affine::IDENTITY,
paint_transform: Affine::IDENTITY,
paint: SinkPaint::Run,
tint: None,
},
atlas_slot: Cell::new(None),
color: ColorGlyph::default(),
outcome: GlyphRunOutcome::default(),
quads,
collecting,
}
}
pub(crate) fn outcome(&mut self) -> GlyphRunOutcome {
if self.color.is_open() {
self.color.reset();
self.outcome.skipped = self.outcome.skipped.saturating_add(1);
}
self.outcome
}
fn record<F>(&mut self, generate: F)
where
F: FnOnce(&mut StripGenerator, &mut StripStorage, Option<PathDataRef<'_>>),
{
self.abandon_quads();
if !matches!(self.state.paint, SinkPaint::Run) {
self.outcome.skipped = self.outcome.skipped.saturating_add(1);
return;
}
let paint = self.run_paint.clone();
if self.record_draw(paint, generate) {
self.outcome.drawn = self.outcome.drawn.saturating_add(1);
}
}
fn record_draw<F>(&mut self, paint: Paint, generate: F) -> bool
where
F: FnOnce(&mut StripGenerator, &mut StripStorage, Option<PathDataRef<'_>>),
{
if self.clips.blocks_everything() {
return false;
}
let start = self.frame.strips.strips.len();
let alpha_start = self.frame.strips.alphas.len();
generate(self.generator, &mut self.frame.strips, self.clips.mask());
self.clips
.clip_run(&mut self.frame.strips, start, alpha_start);
let strip_range = start..self.frame.strips.strips.len();
if strip_range.is_empty() {
return false;
}
let draw = EngineDraw::new(paint, self.depth.advance(), strip_range.clone());
self.frame
.recorder
.push_draw(draw, &self.frame.strips.strips[strip_range]);
true
}
pub(crate) fn draw_resident(
&mut self,
policy: &mut AtlasPolicy,
plan: &ResidentPlan<'_>,
) -> ResidentAnswer {
if !self.collecting {
return ResidentAnswer::Bypassed;
}
let Some(targets) = self.quads.as_mut() else {
return ResidentAnswer::Bypassed;
};
let scratch = &mut *targets.scratch;
scratch.placed.clear();
scratch.origins.clear();
for glyph in plan.glyphs() {
let origin = plan.origin(glyph);
scratch.placed.push(PlacedGlyph {
id: glyph.id,
bucket: subpixel_bucket(origin.0.fract() as f32),
});
scratch.origins.push(origin);
}
let answer =
policy.resolve_resident_run(plan.identity(), &scratch.placed, &mut scratch.slots);
if answer != ResidentAnswer::Resolved {
return answer;
}
let tint = Some(Tint {
color: plan.color(),
mode: TintMode::AlphaMask,
});
let sampler = ImageSampler {
x_extend: Extend::Pad,
y_extend: Extend::Pad,
quality: ImageQuality::Low,
alpha: 1.0,
};
scratch.quads.clear();
for (&(x, y), &slot) in scratch.origins.iter().zip(&scratch.slots) {
if slot.width == 0 || slot.height == 0 {
continue;
}
let left = x.floor() + f64::from(slot.bearing_x);
let top = y.floor() + f64::from(slot.bearing_y);
if !on_quad_grid(left) || !on_quad_grid(top) {
return ResidentAnswer::Bypassed;
}
let (x0, y0) = (left as i64, top as i64);
scratch.quads.push((
[
x0,
y0,
x0 + i64::from(slot.width),
y0 + i64::from(slot.height),
],
slot,
));
}
let window = targets.window;
let quads = std::mem::take(&mut scratch.quads);
self.collecting = false;
for &(rect, slot) in &quads {
let source = ImageSource::opaque_id(slot.image_id);
self.emit_quad(rect, slot, tint, &source, sampler, window);
}
if let Some(targets) = self.quads.as_mut() {
targets.scratch.quads = quads;
}
self.outcome.resident = true;
ResidentAnswer::Resolved
}
fn draw_atlas_glyph(&mut self, image: &Image, area: Rect) {
let Some(slot) = self.atlas_slot.get() else {
self.outcome.skipped = self.outcome.skipped.saturating_add(1);
return;
};
if slot.width == 0 || slot.height == 0 {
return;
}
let glyph = PendingGlyphQuad {
image: image.clone(),
area,
transform: self.state.transform,
paint_transform: self.state.paint_transform,
tint: self.state.tint,
slot,
};
if self.collecting
&& glyph.quad_rect().is_some()
&& let Some(targets) = self.quads.as_mut()
{
targets.scratch.pending.push(glyph);
return;
}
self.abandon_quads();
self.draw_atlas_image(&glyph);
}
fn abandon_quads(&mut self) {
if self.collecting {
self.collecting = false;
self.flush_pending(false);
}
}
pub(crate) fn finish_quads(&mut self, all_resident: bool) {
if self.collecting {
self.collecting = false;
self.flush_pending(all_resident);
}
}
fn flush_pending(&mut self, as_quads: bool) {
let Some(targets) = self.quads.as_mut() else {
return;
};
let window = targets.window;
let mut pending = std::mem::take(&mut targets.scratch.pending);
for glyph in &pending {
if as_quads {
self.draw_quad(glyph, window);
} else {
self.draw_atlas_image(glyph);
}
}
pending.clear();
if let Some(targets) = self.quads.as_mut() {
targets.scratch.pending = pending;
}
}
fn draw_quad(&mut self, glyph: &PendingGlyphQuad, window: RectU16) {
let Some(rect) = glyph.quad_rect() else {
self.draw_atlas_image(glyph);
return;
};
self.emit_quad(
rect,
glyph.slot,
glyph.tint,
&glyph.image.image,
glyph.image.sampler,
window,
);
}
fn emit_quad(
&mut self,
[x0, y0, x1, y1]: [i64; 4],
slot: AtlasSlot,
tint: Option<Tint>,
source: &ImageSource,
sampler: ImageSampler,
window: RectU16,
) {
let left = x0.max(i64::from(window.x0));
let top = y0.max(i64::from(window.y0));
let right = x1.min(i64::from(window.x1));
let bottom = y1.min(i64::from(window.y1));
if left >= right || top >= bottom || self.clips.blocks_everything() {
return;
}
let narrow = |value: i64| u16::try_from(value).unwrap_or(u16::MAX);
let start = self.frame.strips.strips.len();
if self.frame.recorder.has_layers() {
let alpha_start = self.frame.strips.alphas.len();
let device = Rect::new(left as f64, top as f64, right as f64, bottom as f64);
self.generator
.generate_filled_rect_fast(&device, &mut self.frame.strips, None);
if self.frame.strips.strips.len() - start < 2 {
self.frame.strips.strips.truncate(start);
self.frame.strips.alphas.truncate(alpha_start);
return;
}
self.frame.fast_rect_draws = self.frame.fast_rect_draws.saturating_add(1);
}
let strip_range = start..self.frame.strips.strips.len();
let paint_index = self.run_record(slot.page_index, tint, source, sampler);
let depth = self.depth.advance();
let draw = EngineDraw::new(
Paint::Indexed(IndexedPaint::new(paint_index)),
depth,
strip_range.clone(),
);
self.frame
.recorder
.push_draw(draw, &self.frame.strips.strips[strip_range]);
self.frame.glyph_quads.push(GlyphQuad {
depth,
x: narrow(left),
y: narrow(top),
width: narrow(right - left),
height: narrow(bottom - top),
atlas_x: narrow(i64::from(slot.x) + (left - x0)),
atlas_y: narrow(i64::from(slot.y) + (top - y0)),
});
self.note_atlas_draw(slot);
}
fn run_record(
&mut self,
page: u32,
tint: Option<Tint>,
source: &ImageSource,
sampler: ImageSampler,
) -> usize {
if let Some(record) = self.quads.as_ref().and_then(|targets| {
targets
.scratch
.records
.iter()
.find(|record| record.page == page && record.tint == tint)
}) {
return record.paint_index;
}
let paint_index = encode_glyph_run_record(
source.clone(),
sampler,
tint,
&mut self.frame.encoded_paints,
);
self.frame.glyph_run_paints.push(paint_index);
if let Some(targets) = self.quads.as_mut() {
targets.scratch.records.push(RunRecord {
page,
tint,
paint_index,
});
}
paint_index
}
fn note_atlas_draw(&mut self, slot: AtlasSlot) {
self.frame.glyph_slots.push(GlyphSlot {
id: slot.image_id,
region: AtlasRegion {
layer: slot.page_index,
offset: [u32::from(slot.x), u32::from(slot.y)],
size: [u32::from(slot.width), u32::from(slot.height)],
},
padding: u32::from(GLYPH_PADDING),
});
self.frame.atlas_glyph_draws = self.frame.atlas_glyph_draws.saturating_add(1);
self.outcome.drawn = self.outcome.drawn.saturating_add(1);
}
fn draw_atlas_image(&mut self, glyph: &PendingGlyphQuad) {
let transform = glyph.transform;
let paint_transform = transform * glyph.paint_transform;
let inverse = paint_transform.inverse();
if !inverse.as_coeffs().iter().all(|coeff| coeff.is_finite()) {
self.outcome.skipped = self.outcome.skipped.saturating_add(1);
return;
}
let (x_advance, y_advance) = x_y_advances(inverse);
let paint_index = self.frame.encoded_paints.len();
self.frame
.encoded_paints
.push(EncodedPaint::Image(EncodedImage {
may_have_transparency: true,
source: glyph.image.image.clone(),
sampler: glyph.image.sampler,
transform: inverse,
x_advance,
y_advance,
tint: glyph.tint,
}));
let area = glyph.area;
let paint = Paint::Indexed(IndexedPaint::new(paint_index));
let drawn = self.record_draw(paint, |generator, storage, clip| {
generator.generate_filled_path(
area.path_elements(FLATTEN_TOLERANCE),
Fill::NonZero,
transform,
None,
storage,
clip,
);
});
if drawn {
self.note_atlas_draw(glyph.slot);
} else {
self.frame.encoded_paints.truncate(paint_index);
}
}
fn flush_color_glyph(&mut self) {
self.abandon_quads();
let transform = self.color.transform();
let Some(layers) = self.color.take() else {
self.outcome.skipped = self.outcome.skipped.saturating_add(1);
return;
};
let mut drew = false;
for layer in layers {
drew |= self.draw_color_layer(&layer, transform);
}
if drew {
self.outcome.drawn = self.outcome.drawn.saturating_add(1);
}
}
fn draw_color_layer(&mut self, layer: &ColorLayer, transform: Affine) -> bool {
let encoding = encode_brush(
&layer.brush,
transform * layer.paint_transform,
&mut self.frame.encoded_paints,
);
self.frame.lut_requests.extend(encoding.lut_request);
let clipped = layer.clip.is_some();
if let Some(clip) = layer.clip {
self.clips.push_rect(clip, transform, self.generator);
}
let drawn = match &layer.shape {
LayerShape::Rect(rect) => {
let rect = *rect;
self.record_draw(encoding.paint, |generator, storage, clip| {
generator.generate_filled_path(
rect.path_elements(FLATTEN_TOLERANCE),
Fill::NonZero,
transform,
None,
storage,
clip,
);
})
}
LayerShape::Path(path) => {
self.record_draw(encoding.paint, |generator, storage, clip| {
generate_fill(generator, path.iter(), transform, storage, clip);
})
}
};
if clipped {
self.clips.pop();
}
drawn
}
}
impl DrawSink for EngineGlyphSink<'_> {
fn set_transform(&mut self, t: Affine) {
self.state.transform = t;
}
fn set_paint(&mut self, paint: AtlasPaint) {
self.state.paint = SinkPaint::Layer(match paint {
AtlasPaint::Solid(color) => Brush::Solid(color),
AtlasPaint::Gradient(gradient) => Brush::Gradient(gradient),
});
}
fn set_paint_transform(&mut self, t: Affine) {
self.state.paint_transform = t;
}
fn fill_path(&mut self, path: &BezPath) {
if self.color.is_open() {
self.color.refuse();
return;
}
let transform = self.state.transform;
self.record(|generator, storage, clip| {
generate_fill(generator, path.iter(), transform, storage, clip);
});
}
fn fill_rect(&mut self, rect: &Rect) {
let rect = *rect;
if self.color.is_open() {
match &self.state.paint {
SinkPaint::Layer(brush) => {
let brush = brush.clone();
let paint_transform = self.state.paint_transform;
self.color.fill(rect, brush, paint_transform);
}
SinkPaint::Run | SinkPaint::Atlas(_) | SinkPaint::Unsupported => {
self.color.refuse();
}
}
return;
}
if let SinkPaint::Atlas(image) = &self.state.paint {
let image = image.clone();
self.draw_atlas_glyph(&image, rect);
return;
}
let transform = self.state.transform;
self.record(|generator, storage, clip| {
generator.generate_filled_path(
rect.path_elements(FLATTEN_TOLERANCE),
Fill::NonZero,
transform,
None,
storage,
clip,
);
});
}
fn push_clip_layer(&mut self, _clip: &BezPath) {
self.color.push_refused(self.state.transform);
}
fn push_clip_path(&mut self, clip: &BezPath) {
self.color.push_clip(clip, self.state.transform);
}
fn push_blend_layer(&mut self, _blend_mode: BlendMode) {
self.color.push_refused(self.state.transform);
}
fn pop_layer(&mut self) {
if self.color.pop() {
self.flush_color_glyph();
}
}
fn pop_clip_path(&mut self) {
if self.color.pop() {
self.flush_color_glyph();
}
}
fn width(&self) -> u16 {
self.generator.width()
}
fn height(&self) -> u16 {
self.generator.height()
}
}
impl GlyphRenderer for EngineGlyphSink<'_> {
type SavedState = GlyphSinkState;
fn save_state(&mut self) -> Self::SavedState {
self.state.clone()
}
fn restore_state(&mut self, state: Self::SavedState) {
self.state = state;
}
fn stroke_path(&mut self, _path: &BezPath) {
if self.color.is_open() {
self.color.refuse();
return;
}
self.outcome.skipped = self.outcome.skipped.saturating_add(1);
}
fn set_paint_image(&mut self, image: Image) {
self.state.paint = match &image.image {
ImageSource::OpaqueId { .. } => SinkPaint::Atlas(image),
ImageSource::Pixmap(_) => SinkPaint::Unsupported,
};
}
fn set_tint(&mut self, tint: Option<vello_common::paint::Tint>) {
self.state.tint = tint;
}
fn get_context_color(&self) -> AlphaColor<Srgb> {
match &self.context_paint {
PaintType::Solid(color) => *color,
_ => BLACK,
}
}
fn current_paint(&self) -> &PaintType {
&self.context_paint
}
fn atlas_image_source(&self, atlas_slot: &AtlasSlot) -> ImageSource {
self.atlas_slot.set(Some(*atlas_slot));
ImageSource::opaque_id(atlas_slot.image_id)
}
fn atlas_paint_transform(&self, _atlas_slot: &AtlasSlot) -> Affine {
Affine::IDENTITY
}
}
#[derive(Debug)]
pub(crate) struct EngineTextBackend<'a, 's> {
sink: &'a mut EngineGlyphSink<'s>,
prep: &'a mut GlyphPrepCache,
cacher: AtlasCacher<'a>,
}
impl<'a, 's> EngineTextBackend<'a, 's> {
pub(crate) fn new(
sink: &'a mut EngineGlyphSink<'s>,
prep: &'a mut GlyphPrepCache,
cacher: AtlasCacher<'a>,
) -> Self {
Self { sink, prep, cacher }
}
}
impl<'a> GlyphRunBackend<'a> for EngineTextBackend<'a, '_> {
fn atlas_cache(mut self, enabled: bool) -> Self {
if !enabled {
self.cacher = AtlasCacher::Disabled;
}
self
}
fn fill_glyphs<Glyphs>(self, run: GlyphRun<'a>, glyphs: Glyphs)
where
Glyphs: Iterator<Item = Glyph> + Clone,
{
let Self { sink, prep, cacher } = self;
let mut renderer = run.build(glyphs, prep.as_mut(), cacher);
renderer.fill_glyphs(sink);
}
fn stroke_glyphs<Glyphs>(self, run: GlyphRun<'a>, glyphs: Glyphs)
where
Glyphs: Iterator<Item = Glyph> + Clone,
{
let Self { sink, prep, cacher } = self;
let mut renderer = run.build(glyphs, prep.as_mut(), cacher);
renderer.stroke_glyphs(sink);
}
fn render_decoration<Glyphs>(
self,
run: GlyphRun<'a>,
glyphs: Glyphs,
x_range: RangeInclusive<f32>,
baseline_y: f32,
offset: f32,
size: f32,
buffer: f32,
) where
Glyphs: Iterator<Item = Glyph> + Clone,
{
let Self { sink, prep, cacher } = self;
let mut renderer = run.build(glyphs, prep.as_mut(), cacher);
renderer.render_decoration(x_range, baseline_y, offset, size, buffer, sink);
}
}
pub(crate) fn context_paint(brush: &Brush) -> PaintType {
match brush {
Brush::Solid(color) => PaintType::Solid(*color),
Brush::Gradient(gradient) => PaintType::Gradient(gradient.clone()),
Brush::Image(_) => PaintType::Solid(BLACK),
}
}