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::BlendMode;
use peniko::color::palette::css::BLACK;
use peniko::color::{AlphaColor, Srgb};
use peniko::{Brush, Fill};
use vello_common::clip::PathDataRef;
use vello_common::encode::{EncodedImage, EncodedPaint};
use vello_common::paint::{Image, ImageSource, IndexedPaint, Paint, PaintType, Tint};
use vello_common::strip_generator::{StripGenerator, StripStorage};
use crate::cache::images::AtlasRegion;
use crate::compile::clip::ClipStack;
use crate::compile::paint::encode_brush;
use crate::compile::{CompiledFrame, DepthCounter, EngineDraw, FLATTEN_TOLERANCE, GlyphSlot};
use crate::gpu::atlas::x_y_advances;
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,
}
#[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)]
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,
}
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,
) -> Self {
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(),
}
}
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<'_>>),
{
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
}
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 transform = self.state.transform;
let paint_transform = transform * self.state.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: image.image.clone(),
sampler: image.sampler,
transform: inverse,
x_advance,
y_advance,
tint: self.state.tint,
}));
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.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);
} else {
self.frame.encoded_paints.truncate(paint_index);
}
}
fn flush_color_glyph(&mut self) {
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| {
generator.generate_filled_path(
path.iter(),
Fill::NonZero,
transform,
None,
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| {
generator.generate_filled_path(
path.iter(),
Fill::NonZero,
transform,
None,
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),
}
}