mirror of
https://github.com/italicsjenga/vello.git
synced 2025-01-10 12:41:30 +11:00
Get text working
This wires up the text rendering. WIP though, as it tends to get stuck. Not sure if that's text related or just being triggered by it.
This commit is contained in:
parent
3ee81ad5a9
commit
55395fa533
|
@ -31,6 +31,18 @@ pub struct Encoder {
|
||||||
n_pathseg: u32,
|
n_pathseg: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A scene fragment encoding a glyph.
|
||||||
|
///
|
||||||
|
/// This is a reduced version of the full encoder.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct GlyphEncoder {
|
||||||
|
tag_stream: Vec<u8>,
|
||||||
|
pathseg_stream: Vec<u8>,
|
||||||
|
drawobj_stream: Vec<u8>,
|
||||||
|
n_path: u32,
|
||||||
|
n_pathseg: u32,
|
||||||
|
}
|
||||||
|
|
||||||
// Currently same as Element, but may change - should become packed.
|
// Currently same as Element, but may change - should become packed.
|
||||||
const DRAWOBJ_SIZE: usize = 36;
|
const DRAWOBJ_SIZE: usize = 36;
|
||||||
const TRANSFORM_SIZE: usize = 24;
|
const TRANSFORM_SIZE: usize = 24;
|
||||||
|
@ -187,6 +199,14 @@ impl Encoder {
|
||||||
pub(crate) fn n_transform(&self) -> usize {
|
pub(crate) fn n_transform(&self) -> usize {
|
||||||
self.transform_stream.len()
|
self.transform_stream.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn encode_glyph(&mut self, glyph: &GlyphEncoder) {
|
||||||
|
self.tag_stream.extend(&glyph.tag_stream);
|
||||||
|
self.pathseg_stream.extend(&glyph.pathseg_stream);
|
||||||
|
self.drawobj_stream.extend(&glyph.drawobj_stream);
|
||||||
|
self.n_path += glyph.n_path;
|
||||||
|
self.n_pathseg += glyph.n_pathseg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn align_up(x: usize, align: usize) -> usize {
|
fn align_up(x: usize, align: usize) -> usize {
|
||||||
|
@ -197,3 +217,30 @@ fn align_up(x: usize, align: usize) -> usize {
|
||||||
fn padding(x: usize, align: usize) -> usize {
|
fn padding(x: usize, align: usize) -> usize {
|
||||||
x.wrapping_neg() & (align - 1)
|
x.wrapping_neg() & (align - 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GlyphEncoder {
|
||||||
|
pub(crate) fn path_encoder(&mut self) -> PathEncoder {
|
||||||
|
PathEncoder::new(&mut self.tag_stream, &mut self.pathseg_stream)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn finish_path(&mut self, n_pathseg: u32) {
|
||||||
|
self.n_path += 1;
|
||||||
|
self.n_pathseg += n_pathseg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Encode a fill color draw object.
|
||||||
|
///
|
||||||
|
/// This should be encoded after a path.
|
||||||
|
pub(crate) fn fill_color(&mut self, rgba_color: u32) {
|
||||||
|
let element = FillColor {
|
||||||
|
tag: ELEMENT_FILLCOLOR,
|
||||||
|
rgba_color,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
self.drawobj_stream.extend(bytemuck::bytes_of(&element));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_color(&self) -> bool {
|
||||||
|
!self.drawobj_stream.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::stages::Config;
|
use crate::encoder::GlyphEncoder;
|
||||||
|
use crate::stages::{Config, Transform};
|
||||||
use crate::MAX_BLEND_STACK;
|
use crate::MAX_BLEND_STACK;
|
||||||
use piet::kurbo::{Affine, Insets, PathEl, Point, Rect, Shape};
|
use piet::kurbo::{Affine, Insets, PathEl, Point, Rect, Shape};
|
||||||
use piet::{
|
use piet::{
|
||||||
|
@ -10,11 +11,11 @@ use piet::{
|
||||||
|
|
||||||
use piet_gpu_hal::BufWrite;
|
use piet_gpu_hal::BufWrite;
|
||||||
use piet_gpu_types::encoder::{Encode, Encoder};
|
use piet_gpu_types::encoder::{Encode, Encoder};
|
||||||
use piet_gpu_types::scene::{Clip, Element, FillColor, FillLinGradient, SetFillMode, Transform};
|
use piet_gpu_types::scene::{Clip, Element, FillLinGradient, SetFillMode};
|
||||||
|
|
||||||
use crate::gradient::{LinearGradient, RampCache};
|
use crate::gradient::{LinearGradient, RampCache};
|
||||||
use crate::text::Font;
|
use crate::text::Font;
|
||||||
pub use crate::text::{PathEncoder, PietGpuText, PietGpuTextLayout, PietGpuTextLayoutBuilder};
|
pub use crate::text::{PietGpuText, PietGpuTextLayout, PietGpuTextLayoutBuilder};
|
||||||
|
|
||||||
pub struct PietGpuImage;
|
pub struct PietGpuImage;
|
||||||
|
|
||||||
|
@ -262,6 +263,7 @@ impl RenderContext for PietGpuRenderContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_text(&mut self, layout: &Self::TextLayout, pos: impl Into<Point>) {
|
fn draw_text(&mut self, layout: &Self::TextLayout, pos: impl Into<Point>) {
|
||||||
|
self.encode_linewidth(-1.0);
|
||||||
layout.draw_text(self, pos.into());
|
layout.draw_text(self, pos.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,7 +280,7 @@ impl RenderContext for PietGpuRenderContext {
|
||||||
if let Some(state) = self.state_stack.pop() {
|
if let Some(state) = self.state_stack.pop() {
|
||||||
if state.rel_transform != Affine::default() {
|
if state.rel_transform != Affine::default() {
|
||||||
let a_inv = state.rel_transform.inverse();
|
let a_inv = state.rel_transform.inverse();
|
||||||
self.encode_transform(to_scene_transform(a_inv));
|
self.encode_transform(Transform::from_kurbo(a_inv));
|
||||||
}
|
}
|
||||||
self.cur_transform = state.transform;
|
self.cur_transform = state.transform;
|
||||||
for _ in 0..state.n_clip {
|
for _ in 0..state.n_clip {
|
||||||
|
@ -298,7 +300,7 @@ impl RenderContext for PietGpuRenderContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform(&mut self, transform: Affine) {
|
fn transform(&mut self, transform: Affine) {
|
||||||
self.encode_transform(to_scene_transform(transform));
|
self.encode_transform(Transform::from_kurbo(transform));
|
||||||
if let Some(tos) = self.state_stack.last_mut() {
|
if let Some(tos) = self.state_stack.last_mut() {
|
||||||
tos.rel_transform *= transform;
|
tos.rel_transform *= transform;
|
||||||
}
|
}
|
||||||
|
@ -437,26 +439,16 @@ impl PietGpuRenderContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn append_path_encoder(&mut self, path: &PathEncoder) {
|
pub(crate) fn encode_glyph(&mut self, glyph: &GlyphEncoder) {
|
||||||
let elements = path.elements();
|
self.new_encoder.encode_glyph(glyph);
|
||||||
self.elements.extend(elements.iter().cloned());
|
|
||||||
self.pathseg_count += path.n_segs();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn fill_glyph(&mut self, rgba_color: u32) {
|
pub(crate) fn fill_glyph(&mut self, rgba_color: u32) {
|
||||||
let fill = FillColor { rgba_color };
|
self.new_encoder.fill_color(rgba_color);
|
||||||
self.elements.push(Element::FillColor(fill));
|
|
||||||
self.path_count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Bump the path count when rendering a color emoji.
|
|
||||||
pub(crate) fn bump_n_paths(&mut self, n_paths: usize) {
|
|
||||||
self.path_count += n_paths;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn encode_transform(&mut self, transform: Transform) {
|
pub(crate) fn encode_transform(&mut self, transform: Transform) {
|
||||||
self.elements.push(Element::Transform(transform));
|
self.new_encoder.transform(transform);
|
||||||
self.trans_count += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_linewidth(&mut self, linewidth: f32) {
|
fn encode_linewidth(&mut self, linewidth: f32) {
|
||||||
|
@ -507,14 +499,6 @@ fn rect_to_f32_4(rect: Rect) -> [f32; 4] {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_scene_transform(transform: Affine) -> Transform {
|
|
||||||
let c = transform.as_coeffs();
|
|
||||||
Transform {
|
|
||||||
mat: [c[0] as f32, c[1] as f32, c[2] as f32, c[3] as f32],
|
|
||||||
translate: [c[4] as f32, c[5] as f32],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_srgb(f: f64) -> f64 {
|
fn to_srgb(f: f64) -> f64 {
|
||||||
if f <= 0.0031308 {
|
if f <= 0.0031308 {
|
||||||
f * 12.92
|
f * 12.92
|
||||||
|
|
|
@ -10,10 +10,10 @@ use piet::{
|
||||||
TextLayoutBuilder, TextStorage,
|
TextLayoutBuilder, TextStorage,
|
||||||
};
|
};
|
||||||
|
|
||||||
use piet_gpu_types::scene::{CubicSeg, Element, FillColor, LineSeg, QuadSeg, Transform};
|
use crate::encoder::GlyphEncoder;
|
||||||
|
|
||||||
use crate::render_ctx::{self, FillMode};
|
use crate::render_ctx::{self, FillMode};
|
||||||
use crate::PietGpuRenderContext;
|
use crate::PietGpuRenderContext;
|
||||||
|
use crate::stages::Transform;
|
||||||
|
|
||||||
// This is very much a hack to get things working.
|
// This is very much a hack to get things working.
|
||||||
// On Windows, can set this to "c:\\Windows\\Fonts\\seguiemj.ttf" to get color emoji
|
// On Windows, can set this to "c:\\Windows\\Fonts\\seguiemj.ttf" to get color emoji
|
||||||
|
@ -51,13 +51,6 @@ struct Glyph {
|
||||||
y: f32,
|
y: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct PathEncoder {
|
|
||||||
elements: Vec<Element>,
|
|
||||||
n_segs: usize,
|
|
||||||
// If this is zero, then it's a text glyph and should be followed by a fill
|
|
||||||
n_colr_layers: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TextRenderCtx<'a> {
|
struct TextRenderCtx<'a> {
|
||||||
scaler: Scaler<'a>,
|
scaler: Scaler<'a>,
|
||||||
|
@ -126,8 +119,8 @@ impl Font {
|
||||||
Font { font_ref }
|
Font { font_ref }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_path<'a>(&self, glyph_id: GlyphId, tc: &mut TextRenderCtx<'a>) -> PathEncoder {
|
fn make_path<'a>(&self, glyph_id: GlyphId, tc: &mut TextRenderCtx<'a>) -> GlyphEncoder {
|
||||||
let mut encoder = PathEncoder::default();
|
let mut encoder = GlyphEncoder::default();
|
||||||
if tc.scaler.has_color_outlines() {
|
if tc.scaler.has_color_outlines() {
|
||||||
if let Some(outline) = tc.scaler.scale_color_outline(glyph_id) {
|
if let Some(outline) = tc.scaler.scale_color_outline(glyph_id) {
|
||||||
// TODO: be more sophisticated choosing a palette
|
// TODO: be more sophisticated choosing a palette
|
||||||
|
@ -136,8 +129,8 @@ impl Font {
|
||||||
while let Some(layer) = outline.get(i) {
|
while let Some(layer) = outline.get(i) {
|
||||||
if let Some(color_ix) = layer.color_index() {
|
if let Some(color_ix) = layer.color_index() {
|
||||||
let color = palette.get(color_ix);
|
let color = palette.get(color_ix);
|
||||||
encoder.append_outline(layer.verbs(), layer.points());
|
append_outline(&mut encoder, layer.verbs(), layer.points());
|
||||||
encoder.append_solid_fill(color);
|
encoder.fill_color(*bytemuck::from_bytes(&color));
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
@ -145,7 +138,7 @@ impl Font {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(outline) = tc.scaler.scale_outline(glyph_id) {
|
if let Some(outline) = tc.scaler.scale_outline(glyph_id) {
|
||||||
encoder.append_outline(outline.verbs(), outline.points());
|
append_outline(&mut encoder, outline.verbs(), outline.points());
|
||||||
}
|
}
|
||||||
encoder
|
encoder
|
||||||
}
|
}
|
||||||
|
@ -212,12 +205,10 @@ impl PietGpuTextLayout {
|
||||||
last_x = glyph.x;
|
last_x = glyph.x;
|
||||||
//println!("{:?}, {:?}", transform.mat, transform.translate);
|
//println!("{:?}, {:?}", transform.mat, transform.translate);
|
||||||
ctx.encode_transform(transform);
|
ctx.encode_transform(transform);
|
||||||
let path = self.font.make_path(glyph.glyph_id, &mut tc);
|
let glyph = self.font.make_path(glyph.glyph_id, &mut tc);
|
||||||
ctx.append_path_encoder(&path);
|
ctx.encode_glyph(&glyph);
|
||||||
if path.n_colr_layers == 0 {
|
if !glyph.is_color() {
|
||||||
ctx.fill_glyph(0xff_ff_ff_ff);
|
ctx.fill_glyph(0xff_ff_ff_ff);
|
||||||
} else {
|
|
||||||
ctx.bump_n_paths(path.n_colr_layers);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(transform) = inv_transform {
|
if let Some(transform) = inv_transform {
|
||||||
|
@ -271,79 +262,38 @@ impl TextLayoutBuilder for PietGpuTextLayoutBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PathEncoder {
|
fn append_outline(encoder: &mut GlyphEncoder, verbs: &[Verb], points: &[Vector]) {
|
||||||
pub(crate) fn elements(&self) -> &[Element] {
|
let mut path_encoder = encoder.path_encoder();
|
||||||
&self.elements
|
let mut i = 0;
|
||||||
}
|
for verb in verbs {
|
||||||
|
match verb {
|
||||||
pub(crate) fn n_segs(&self) -> usize {
|
Verb::MoveTo => {
|
||||||
self.n_segs
|
let p = points[i];
|
||||||
}
|
path_encoder.move_to(p.x, p.y);
|
||||||
|
i += 1;
|
||||||
fn append_outline(&mut self, verbs: &[Verb], points: &[Vector]) {
|
|
||||||
let elements = &mut self.elements;
|
|
||||||
let old_len = elements.len();
|
|
||||||
let mut i = 0;
|
|
||||||
let mut start_pt = [0.0f32; 2];
|
|
||||||
let mut last_pt = [0.0f32; 2];
|
|
||||||
for verb in verbs {
|
|
||||||
match verb {
|
|
||||||
Verb::MoveTo => {
|
|
||||||
start_pt = convert_swash_point(points[i]);
|
|
||||||
last_pt = start_pt;
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
Verb::LineTo => {
|
|
||||||
let p1 = convert_swash_point(points[i]);
|
|
||||||
elements.push(Element::Line(LineSeg { p0: last_pt, p1 }));
|
|
||||||
last_pt = p1;
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
Verb::QuadTo => {
|
|
||||||
let p1 = convert_swash_point(points[i]);
|
|
||||||
let p2 = convert_swash_point(points[i + 1]);
|
|
||||||
elements.push(Element::Quad(QuadSeg {
|
|
||||||
p0: last_pt,
|
|
||||||
p1,
|
|
||||||
p2,
|
|
||||||
}));
|
|
||||||
last_pt = p2;
|
|
||||||
i += 2;
|
|
||||||
}
|
|
||||||
Verb::CurveTo => {
|
|
||||||
let p1 = convert_swash_point(points[i]);
|
|
||||||
let p2 = convert_swash_point(points[i + 1]);
|
|
||||||
let p3 = convert_swash_point(points[i + 2]);
|
|
||||||
elements.push(Element::Cubic(CubicSeg {
|
|
||||||
p0: last_pt,
|
|
||||||
p1,
|
|
||||||
p2,
|
|
||||||
p3,
|
|
||||||
}));
|
|
||||||
last_pt = p3;
|
|
||||||
i += 3;
|
|
||||||
}
|
|
||||||
Verb::Close => {
|
|
||||||
if start_pt != last_pt {
|
|
||||||
elements.push(Element::Line(LineSeg {
|
|
||||||
p0: last_pt,
|
|
||||||
p1: start_pt,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Verb::LineTo => {
|
||||||
|
let p = points[i];
|
||||||
|
path_encoder.line_to(p.x, p.y);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
Verb::QuadTo => {
|
||||||
|
let p1 = points[i];
|
||||||
|
let p2 = points[i + 1];
|
||||||
|
path_encoder.quad_to(p1.x, p1.y, p2.x, p2.y);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
Verb::CurveTo => {
|
||||||
|
let p1 = points[i];
|
||||||
|
let p2 = points[i + 1];
|
||||||
|
let p3 = points[i + 2];
|
||||||
|
path_encoder.cubic_to(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
|
||||||
|
i += 3;
|
||||||
|
}
|
||||||
|
Verb::Close => path_encoder.close_path(),
|
||||||
}
|
}
|
||||||
self.n_segs += elements.len() - old_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn append_solid_fill(&mut self, color: [u8; 4]) {
|
|
||||||
let rgba_color = u32::from_be_bytes(color);
|
|
||||||
self.elements
|
|
||||||
.push(Element::FillColor(FillColor { rgba_color }));
|
|
||||||
self.n_colr_layers += 1;
|
|
||||||
}
|
}
|
||||||
}
|
path_encoder.path();
|
||||||
|
let n_pathseg = path_encoder.n_pathseg();
|
||||||
fn convert_swash_point(v: Vector) -> [f32; 2] {
|
encoder.finish_path(n_pathseg);
|
||||||
[v.x, v.y]
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue