Render color emoji layers

A bit hacky still, but does render color in Segoe color emoji.
This commit is contained in:
Raph Levien 2021-08-25 13:59:26 -07:00
parent 02ee369428
commit ef5ef2745c
3 changed files with 101 additions and 60 deletions

View file

@ -1,7 +1,8 @@
use piet_gpu_derive::piet_gpu; use piet_gpu_derive::piet_gpu;
pub use self::scene::{ pub use self::scene::{
Clip, CubicSeg, Element, FillColor, FillLinGradient, LineSeg, QuadSeg, SetFillMode, SetLineWidth, Transform, Clip, CubicSeg, Element, FillColor, FillLinGradient, LineSeg, QuadSeg, SetFillMode,
SetLineWidth, Transform,
}; };
piet_gpu! { piet_gpu! {

View file

@ -501,6 +501,11 @@ impl PietGpuRenderContext {
self.path_count += 1; 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.elements.push(Element::Transform(transform));
self.trans_count += 1; self.trans_count += 1;

View file

@ -10,13 +10,14 @@ use piet::{
TextLayoutBuilder, TextStorage, TextLayoutBuilder, TextStorage,
}; };
use piet_gpu_types::scene::{CubicSeg, Element, LineSeg, QuadSeg, Transform}; use piet_gpu_types::scene::{CubicSeg, Element, FillColor, LineSeg, QuadSeg, Transform};
use crate::render_ctx::{self, FillMode}; use crate::render_ctx::{self, FillMode};
use crate::PietGpuRenderContext; use crate::PietGpuRenderContext;
// This is very much a hack to get things working. // This is very much a hack to get things working.
const FONT_DATA: &[u8] = include_bytes!("c:\\Windows\\Fonts\\seguiemj.ttf"); // On Windows, can set this to "c:\\Windows\\Fonts\\seguiemj.ttf" to get color emoji
const FONT_DATA: &[u8] = include_bytes!("../third-party/Roboto-Regular.ttf");
#[derive(Clone)] #[derive(Clone)]
pub struct Font { pub struct Font {
@ -118,6 +119,12 @@ impl TextLayout for PietGpuTextLayout {
impl Font { impl Font {
pub fn new() -> Font { pub fn new() -> Font {
let font_ref = FontRef::from_index(FONT_DATA, 0).expect("error parsing font"); let font_ref = FontRef::from_index(FONT_DATA, 0).expect("error parsing font");
for palette in font_ref.color_palettes() {
println!("palette, len={}", palette.len());
for i in 0..palette.len() {
println!("{}: {:?}", i, palette.get(i));
}
}
Font { font_ref } Font { font_ref }
} }
@ -131,66 +138,27 @@ impl Font {
let mut scale_context = ScaleContext::new(); let mut scale_context = ScaleContext::new();
let mut scaler = scale_context.builder(self.font_ref).size(2048.).build(); let mut scaler = scale_context.builder(self.font_ref).size(2048.).build();
let mut encoder = PathEncoder::default(); let mut encoder = PathEncoder::default();
println!("glyph {} has_color_outlines {}", glyph_id, scaler.has_color_outlines()); if scaler.has_color_outlines() {
if let Some(outline) = scaler.scale_outline(glyph_id) { if let Some(outline) = scaler.scale_color_outline(glyph_id) {
let verbs = outline.verbs(); // TODO: be more sophisticated choosing a palette
let points = outline.points(); let palette = self.font_ref.color_palettes().next().unwrap();
let elements = &mut encoder.elements; println!("is_color {}", outline.is_color());
let mut i = 0; let mut i = 0;
let mut start_pt = [0.0f32; 2]; while let Some(layer) = outline.get(i) {
let mut last_pt = [0.0f32; 2]; if let Some(color_ix) = layer.color_index() {
for verb in verbs { let color = palette.get(color_ix);
match verb { encoder.append_outline(layer.verbs(), layer.points());
Verb::MoveTo => { encoder.append_solid_fill(color);
start_pt = convert_swash_point(points[i]); println!("layer {} color {:?}", i, color);
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,
}));
}
} }
i += 1;
} }
return encoder;
} }
} }
encoder.n_segs = encoder.elements.len(); if let Some(outline) = scaler.scale_outline(glyph_id) {
encoder.append_outline(outline.verbs(), outline.points());
}
encoder encoder
} }
} }
@ -255,7 +223,11 @@ impl PietGpuTextLayout {
ctx.encode_transform(transform); ctx.encode_transform(transform);
let path = self.font.make_path(glyph.glyph_id); let path = self.font.make_path(glyph.glyph_id);
ctx.append_path_encoder(&path); ctx.append_path_encoder(&path);
ctx.fill_glyph(0xff_ff_ff_ff); if path.n_colr_layers == 0 {
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 {
ctx.encode_transform(transform); ctx.encode_transform(transform);
@ -316,6 +288,69 @@ impl PathEncoder {
pub(crate) fn n_segs(&self) -> usize { pub(crate) fn n_segs(&self) -> usize {
self.n_segs self.n_segs
} }
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,
}));
}
}
}
}
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;
}
} }
fn convert_swash_point(v: Vector) -> [f32; 2] { fn convert_swash_point(v: Vector) -> [f32; 2] {