Support cubic Béziers for fonts with CFF outlines
This commit is contained in:
parent
a508322f34
commit
6ac37c5085
|
@ -22,10 +22,13 @@ flat in int vVertexID[];
|
|||
|
||||
// The starting point of the segment.
|
||||
out vec2 vpP0[];
|
||||
// The control point, if this is a curve. If this is a line, this value must be ignored.
|
||||
// The first control point, if this is a curve. If this is a line, this value must be ignored.
|
||||
out vec2 vpP1[];
|
||||
// The endpoint of this segment.
|
||||
// The second control point, if this is a curve. If this is a line, this value must be ignored.
|
||||
// If this curve is quadratic, this will be the same as `vpP1`.
|
||||
out vec2 vpP2[];
|
||||
// The endpoint of this segment.
|
||||
out vec2 vpP3[];
|
||||
// The tessellation level.
|
||||
//
|
||||
// This is passed along explicitly instead of having the TES read it from `gl_TessLevelInner` in
|
||||
|
@ -36,15 +39,19 @@ void main() {
|
|||
vec2 p0 = gl_in[0].gl_Position.xy;
|
||||
vec2 p1 = gl_in[1].gl_Position.xy;
|
||||
vec2 p2 = gl_in[2].gl_Position.xy;
|
||||
vec2 p3 = gl_in[3].gl_Position.xy;
|
||||
|
||||
// Divide into lines.
|
||||
float lineCount = 1.0f;
|
||||
if (vVertexID[1] > 0) {
|
||||
// Quadratic curve.
|
||||
vec2 dev = p0 - 2.0f * p1 + p2;
|
||||
// A curve.
|
||||
//
|
||||
// FIXME(pcwalton): Is this formula good for cubic curves?
|
||||
vec2 dev = p0 - 2.0f * mix(p1, p2, 0.5) + p3;
|
||||
float devSq = dot(dev, dev);
|
||||
if (devSq >= CURVE_THRESHOLD) {
|
||||
// Inverse square root is likely no slower and may be faster than regular square root
|
||||
// Inverse square root is likely no slower and may be faster than regular square
|
||||
// root
|
||||
// (e.g. on x86).
|
||||
lineCount += floor(inversesqrt(inversesqrt(CURVE_TOLERANCE * devSq)));
|
||||
}
|
||||
|
@ -92,7 +99,7 @@ void main() {
|
|||
// so we're in the clear: the rasterizer will always discard the unshaded areas and render only
|
||||
// the shaded ones.
|
||||
|
||||
float tessLevel = min(p0.x == p2.x ? 0.0f : (lineCount * 2.0f - 1.0f), 31.0f);
|
||||
float tessLevel = min(p0.x == p3.x ? 0.0f : (lineCount * 2.0f - 1.0f), 31.0f);
|
||||
gl_TessLevelInner[0] = tessLevel;
|
||||
gl_TessLevelInner[1] = 1.0f;
|
||||
gl_TessLevelOuter[0] = 1.0f;
|
||||
|
@ -105,6 +112,7 @@ void main() {
|
|||
vpP0[gl_InvocationID] = p0;
|
||||
vpP1[gl_InvocationID] = p1;
|
||||
vpP2[gl_InvocationID] = p2;
|
||||
vpP3[gl_InvocationID] = p3;
|
||||
vpTessLevel[gl_InvocationID] = tessLevel;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,10 +17,13 @@ uniform uvec2 uAtlasSize;
|
|||
|
||||
// The starting point of the segment.
|
||||
in vec2 vpP0[];
|
||||
// The control point, if this is a curve. If this is a line, this value must be ignored.
|
||||
// The first control point, if this is a curve. If this is a line, this value must be ignored.
|
||||
in vec2 vpP1[];
|
||||
// The endpoint of this segment.
|
||||
// The second control point, if this is a cubic curve. If this is a quadratic curve or a line, this
|
||||
// is equal to `vpP1`.
|
||||
in vec2 vpP2[];
|
||||
// The endpoint of this segment.
|
||||
in vec2 vpP3[];
|
||||
// The tessellation level.
|
||||
//
|
||||
// This is passed along explicitly instead of having the TES read it from `gl_TessLevelInner` in
|
||||
|
@ -40,7 +43,7 @@ flat out vec2 vYMinMax;
|
|||
|
||||
void main() {
|
||||
// Read in curve points.
|
||||
vec2 cP0 = vpP0[0], cP1 = vpP1[0], cP2 = vpP2[0];
|
||||
vec2 cP0 = vpP0[0], cP1 = vpP1[0], cP2 = vpP2[0], cP3 = vpP3[0];
|
||||
|
||||
// Work out how many lines made up this segment, which line we're working on, and which
|
||||
// endpoint of that line we're looking at.
|
||||
|
@ -53,12 +56,25 @@ void main() {
|
|||
vec2 p0, p1;
|
||||
if (lineCount == 1) {
|
||||
p0 = cP0;
|
||||
p1 = cP2;
|
||||
p1 = cP3;
|
||||
} else {
|
||||
float t0 = float(lineIndex + 0) / float(lineCount);
|
||||
float t1 = float(lineIndex + 1) / float(lineCount);
|
||||
p0 = mix(mix(cP0, cP1, t0), mix(cP1, cP2, t0), t0);
|
||||
p1 = mix(mix(cP0, cP1, t1), mix(cP1, cP2, t1), t1);
|
||||
|
||||
// These lerps are needed both for quadratic and cubic Béziers.
|
||||
vec2 pP0P1T0 = mix(cP0, cP1, t0), pP0P1T1 = mix(cP0, cP1, t1);
|
||||
vec2 pP2P3T0 = mix(cP2, cP3, t0), pP2P3T1 = mix(cP2, cP3, t1);
|
||||
|
||||
if (cP1 == cP2) {
|
||||
// Quadratic Bézier.
|
||||
p0 = mix(pP0P1T0, pP2P3T0, t0);
|
||||
p1 = mix(pP0P1T1, pP2P3T1, t1);
|
||||
} else {
|
||||
// Cubic Bézier.
|
||||
vec2 pP1P2T0 = mix(cP1, cP2, t0), pP1P2T1 = mix(cP1, cP2, t1);
|
||||
p0 = mix(mix(pP0P1T0, pP1P2T0, t0), mix(pP1P2T0, pP2P3T0, t0), t0);
|
||||
p1 = mix(mix(pP0P1T1, pP1P2T1, t1), mix(pP1P2T1, pP2P3T1, t1), t1);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute direction. Flip the two points around so that p0 is on the left and p1 is on the
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
use byteorder::{BigEndian, LittleEndian, ReadBytesExt};
|
||||
use euclid::Point2D;
|
||||
use otf::glyf::Point;
|
||||
use otf::glyf::{Point, PointKind};
|
||||
use otf::head::HeadTable;
|
||||
use otf::{Error, FontTable};
|
||||
use outline::GlyphBounds;
|
||||
|
@ -112,7 +112,7 @@ impl<'a> CffTable<'a> {
|
|||
callback(&Point {
|
||||
position: pos,
|
||||
index_in_contour: 0,
|
||||
on_curve: true,
|
||||
kind: PointKind::OnCurve,
|
||||
});
|
||||
start = pos;
|
||||
index_in_contour = 1;
|
||||
|
@ -125,7 +125,7 @@ impl<'a> CffTable<'a> {
|
|||
callback(&Point {
|
||||
position: pos,
|
||||
index_in_contour: index_in_contour,
|
||||
on_curve: true,
|
||||
kind: PointKind::OnCurve,
|
||||
});
|
||||
index_in_contour += 1
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ impl<'a> CffTable<'a> {
|
|||
callback(&Point {
|
||||
position: pos,
|
||||
index_in_contour: index_in_contour,
|
||||
on_curve: true,
|
||||
kind: PointKind::OnCurve,
|
||||
});
|
||||
index_in_contour += 1
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ impl<'a> CffTable<'a> {
|
|||
callback(&Point {
|
||||
position: pos,
|
||||
index_in_contour: index_in_contour,
|
||||
on_curve: true,
|
||||
kind: PointKind::OnCurve,
|
||||
});
|
||||
index_in_contour += 1
|
||||
}
|
||||
|
@ -174,21 +174,21 @@ impl<'a> CffTable<'a> {
|
|||
callback(&Point {
|
||||
position: pos,
|
||||
index_in_contour: index_in_contour + 0,
|
||||
on_curve: true,
|
||||
kind: PointKind::FirstCubicControl,
|
||||
});
|
||||
|
||||
pos = pos + Point2D::new(chunk[2] as i16, chunk[3] as i16);
|
||||
callback(&Point {
|
||||
position: pos,
|
||||
index_in_contour: index_in_contour + 1,
|
||||
on_curve: true,
|
||||
kind: PointKind::SecondCubicControl,
|
||||
});
|
||||
|
||||
pos = pos + Point2D::new(chunk[4] as i16, chunk[5] as i16);
|
||||
callback(&Point {
|
||||
position: pos,
|
||||
index_in_contour: index_in_contour + 2,
|
||||
on_curve: true,
|
||||
kind: PointKind::OnCurve,
|
||||
});
|
||||
|
||||
index_in_contour += 3
|
||||
|
@ -272,7 +272,7 @@ impl<'a> CffTable<'a> {
|
|||
callback(&Point {
|
||||
position: pos,
|
||||
index_in_contour: index_in_contour,
|
||||
on_curve: true,
|
||||
kind: PointKind::FirstCubicControl,
|
||||
});
|
||||
|
||||
pos.x += chunk[1] as i16;
|
||||
|
@ -280,14 +280,14 @@ impl<'a> CffTable<'a> {
|
|||
callback(&Point {
|
||||
position: pos,
|
||||
index_in_contour: index_in_contour + 1,
|
||||
on_curve: true,
|
||||
kind: PointKind::SecondCubicControl,
|
||||
});
|
||||
|
||||
pos.y += chunk[3] as i16;
|
||||
callback(&Point {
|
||||
position: pos,
|
||||
index_in_contour: index_in_contour + 2,
|
||||
on_curve: true,
|
||||
kind: PointKind::OnCurve,
|
||||
});
|
||||
|
||||
index_in_contour += 3
|
||||
|
@ -311,7 +311,7 @@ impl<'a> CffTable<'a> {
|
|||
callback(&Point {
|
||||
position: pos,
|
||||
index_in_contour: index_in_contour,
|
||||
on_curve: true,
|
||||
kind: PointKind::FirstCubicControl,
|
||||
});
|
||||
|
||||
pos.x += chunk[1] as i16;
|
||||
|
@ -319,14 +319,14 @@ impl<'a> CffTable<'a> {
|
|||
callback(&Point {
|
||||
position: pos,
|
||||
index_in_contour: index_in_contour + 1,
|
||||
on_curve: true,
|
||||
kind: PointKind::SecondCubicControl,
|
||||
});
|
||||
|
||||
pos.x += chunk[3] as i16;
|
||||
callback(&Point {
|
||||
position: pos,
|
||||
index_in_contour: index_in_contour + 2,
|
||||
on_curve: true,
|
||||
kind: PointKind::OnCurve,
|
||||
});
|
||||
|
||||
index_in_contour += 3
|
||||
|
@ -367,7 +367,7 @@ impl<'a> CffTable<'a> {
|
|||
callback(&Point {
|
||||
position: pos,
|
||||
index_in_contour: 0,
|
||||
on_curve: true,
|
||||
kind: PointKind::OnCurve,
|
||||
});
|
||||
start = pos;
|
||||
index_in_contour = 1;
|
||||
|
@ -380,7 +380,7 @@ impl<'a> CffTable<'a> {
|
|||
callback(&Point {
|
||||
position: pos,
|
||||
index_in_contour: 0,
|
||||
on_curve: true,
|
||||
kind: PointKind::OnCurve,
|
||||
});
|
||||
start = pos;
|
||||
index_in_contour = 1;
|
||||
|
@ -558,7 +558,7 @@ fn close_path_if_necessary<F>(pos: &mut Point2D<i16>,
|
|||
callback(&Point {
|
||||
position: *start,
|
||||
index_in_contour: index_in_contour,
|
||||
on_curve: true,
|
||||
kind: PointKind::OnCurve,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -572,7 +572,7 @@ fn process_hvcurveto_h<F>(chunk: &[i32],
|
|||
callback(&Point {
|
||||
position: *pos,
|
||||
index_in_contour: *index_in_contour + 0,
|
||||
on_curve: true,
|
||||
kind: PointKind::FirstCubicControl,
|
||||
});
|
||||
|
||||
pos.x += chunk[1] as i16;
|
||||
|
@ -580,7 +580,7 @@ fn process_hvcurveto_h<F>(chunk: &[i32],
|
|||
callback(&Point {
|
||||
position: *pos,
|
||||
index_in_contour: *index_in_contour + 1,
|
||||
on_curve: true,
|
||||
kind: PointKind::SecondCubicControl,
|
||||
});
|
||||
|
||||
pos.x += dxf as i16;
|
||||
|
@ -588,7 +588,7 @@ fn process_hvcurveto_h<F>(chunk: &[i32],
|
|||
callback(&Point {
|
||||
position: *pos,
|
||||
index_in_contour: *index_in_contour + 2,
|
||||
on_curve: true,
|
||||
kind: PointKind::OnCurve,
|
||||
});
|
||||
|
||||
*index_in_contour += 3
|
||||
|
@ -604,7 +604,7 @@ fn process_hvcurveto_v<F>(chunk: &[i32],
|
|||
callback(&Point {
|
||||
position: *pos,
|
||||
index_in_contour: *index_in_contour + 0,
|
||||
on_curve: true,
|
||||
kind: PointKind::FirstCubicControl,
|
||||
});
|
||||
|
||||
pos.x += chunk[1] as i16;
|
||||
|
@ -612,7 +612,7 @@ fn process_hvcurveto_v<F>(chunk: &[i32],
|
|||
callback(&Point {
|
||||
position: *pos,
|
||||
index_in_contour: *index_in_contour + 1,
|
||||
on_curve: true,
|
||||
kind: PointKind::SecondCubicControl,
|
||||
});
|
||||
|
||||
pos.x += chunk[3] as i16;
|
||||
|
@ -620,7 +620,7 @@ fn process_hvcurveto_v<F>(chunk: &[i32],
|
|||
callback(&Point {
|
||||
position: *pos,
|
||||
index_in_contour: *index_in_contour + 2,
|
||||
on_curve: true,
|
||||
kind: PointKind::OnCurve,
|
||||
});
|
||||
|
||||
*index_in_contour += 3
|
||||
|
|
|
@ -46,9 +46,30 @@ bitflags! {
|
|||
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub struct Point {
|
||||
/// Where the point is located in glyph space.
|
||||
pub position: Point2D<i16>,
|
||||
|
||||
/// The index of the point in this contour.
|
||||
///
|
||||
/// When iterating over points via `for_each_point`, a value of 0 here indicates that a new
|
||||
/// contour begins.
|
||||
pub index_in_contour: u16,
|
||||
pub on_curve: bool,
|
||||
|
||||
/// The kind of point this is.
|
||||
pub kind: PointKind,
|
||||
}
|
||||
|
||||
/// The type of point.
|
||||
#[derive(Clone, Copy, PartialEq, Debug)]
|
||||
pub enum PointKind {
|
||||
/// The point is on the curve.
|
||||
OnCurve,
|
||||
/// The point is a quadratic control point.
|
||||
QuadControl,
|
||||
/// The point is the first cubic control point.
|
||||
FirstCubicControl,
|
||||
/// The point is the second cubic control point.
|
||||
SecondCubicControl,
|
||||
}
|
||||
|
||||
/// TODO(pcwalton): Add some caching so we don't keep going to the `loca` table all the time.
|
||||
|
@ -166,7 +187,7 @@ impl<'a> GlyfTable<'a> {
|
|||
callback(&Point {
|
||||
position: position,
|
||||
index_in_contour: point_index_in_contour,
|
||||
on_curve: true,
|
||||
kind: PointKind::OnCurve,
|
||||
});
|
||||
point_index_in_contour += 1
|
||||
}
|
||||
|
@ -185,7 +206,11 @@ impl<'a> GlyfTable<'a> {
|
|||
} else {
|
||||
callback(&Point {
|
||||
position: position,
|
||||
on_curve: flags.contains(ON_CURVE),
|
||||
kind: if flags.contains(ON_CURVE) {
|
||||
PointKind::OnCurve
|
||||
} else {
|
||||
PointKind::QuadControl
|
||||
},
|
||||
index_in_contour: point_index_in_contour,
|
||||
});
|
||||
point_index_in_contour += 1
|
||||
|
@ -203,14 +228,14 @@ impl<'a> GlyfTable<'a> {
|
|||
callback(&Point {
|
||||
position: position,
|
||||
index_in_contour: point_index_in_contour,
|
||||
on_curve: true,
|
||||
kind: PointKind::OnCurve,
|
||||
});
|
||||
point_index_in_contour += 1
|
||||
}
|
||||
|
||||
callback(&Point {
|
||||
position: initial_off_curve_point,
|
||||
on_curve: false,
|
||||
kind: PointKind::QuadControl,
|
||||
index_in_contour: point_index_in_contour,
|
||||
});
|
||||
point_index_in_contour += 1
|
||||
|
@ -220,7 +245,7 @@ impl<'a> GlyfTable<'a> {
|
|||
if let Some(first_on_curve_point) = first_on_curve_point {
|
||||
callback(&Point {
|
||||
position: first_on_curve_point,
|
||||
on_curve: true,
|
||||
kind: PointKind::OnCurve,
|
||||
index_in_contour: point_index_in_contour,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -10,11 +10,13 @@
|
|||
|
||||
//! OpenType fonts.
|
||||
|
||||
pub use otf::glyf::{Point, PointKind};
|
||||
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use charmap::{CodepointRange, GlyphMapping};
|
||||
use otf::cff::CffTable;
|
||||
use otf::cmap::CmapTable;
|
||||
use otf::glyf::{GlyfTable, Point};
|
||||
use otf::glyf::GlyfTable;
|
||||
use otf::head::HeadTable;
|
||||
use otf::hhea::HheaTable;
|
||||
use otf::hmtx::{HmtxTable, HorizontalMetrics};
|
||||
|
|
|
@ -14,7 +14,7 @@ use error::GlError;
|
|||
use euclid::Size2D;
|
||||
use gl::types::{GLsizeiptr, GLuint};
|
||||
use gl;
|
||||
use otf::{self, Font};
|
||||
use otf::{self, Font, PointKind};
|
||||
use std::mem;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
|
@ -50,7 +50,7 @@ impl OutlineBuilder {
|
|||
let mut point_index = self.vertices.len() as u32;
|
||||
let start_index = self.indices.len() as u32;
|
||||
let start_point = point_index;
|
||||
let mut last_point_on_curve = true;
|
||||
let mut last_point_kind = PointKind::OnCurve;
|
||||
|
||||
try!(font.for_each_point(glyph_id, |point| {
|
||||
self.vertices.push(Vertex {
|
||||
|
@ -59,17 +59,22 @@ impl OutlineBuilder {
|
|||
glyph_index: glyph_index,
|
||||
});
|
||||
|
||||
if point.index_in_contour > 0 && point.on_curve {
|
||||
let indices = if !last_point_on_curve {
|
||||
[point_index - 2, point_index - 1, point_index]
|
||||
} else {
|
||||
[point_index - 1, 0, point_index]
|
||||
if point.index_in_contour > 0 && point.kind == PointKind::OnCurve {
|
||||
let indices = match last_point_kind {
|
||||
PointKind::FirstCubicControl => [0, 0, 0, 0],
|
||||
PointKind::SecondCubicControl => {
|
||||
[point_index - 3, point_index - 2, point_index - 1, point_index]
|
||||
}
|
||||
PointKind::QuadControl => {
|
||||
[point_index - 2, point_index - 1, point_index - 1, point_index]
|
||||
}
|
||||
PointKind::OnCurve => [point_index - 1, 0, 0, point_index],
|
||||
};
|
||||
self.indices.extend(indices.iter().cloned());
|
||||
}
|
||||
|
||||
point_index += 1;
|
||||
last_point_on_curve = point.on_curve
|
||||
last_point_kind = point.kind
|
||||
}));
|
||||
|
||||
// Add a glyph descriptor.
|
||||
|
|
|
@ -273,7 +273,7 @@ impl Rasterizer {
|
|||
|
||||
gl::Uniform2ui(self.draw_atlas_size_uniform, rect.size.width, rect.size.height);
|
||||
|
||||
gl::PatchParameteri(gl::PATCH_VERTICES, 3);
|
||||
gl::PatchParameteri(gl::PATCH_VERTICES, 4);
|
||||
|
||||
// Use blending on our floating point framebuffer to accumulate coverage.
|
||||
gl::Enable(gl::BLEND);
|
||||
|
|
Loading…
Reference in New Issue