Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support glyph transforms for writing modes #2288

Merged
merged 2 commits into from Jan 15, 2018
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -1283,8 +1283,10 @@ impl FrameBuilder {
let mut render_mode = self.config
.default_font_render_mode
.limit_by(font.render_mode);
let mut flags = font.flags;
if let Some(options) = glyph_options {
render_mode = render_mode.limit_by(options.render_mode);
flags |= options.flags;
}

// There are some conditions under which we can't use
@@ -1309,7 +1311,7 @@ impl FrameBuilder {
font.bg_color,
render_mode,
font.subpx_dir,
font.flags,
flags,
font.platform_options,
font.variations.clone(),
);
@@ -114,6 +114,18 @@ impl FontTransform {
self.scale_y - self.skew_y * skew_factor,
)
}

pub fn swap_xy(&self) -> Self {
FontTransform::new(self.skew_x, self.scale_x, self.scale_y, self.skew_y)
}

pub fn flip_x(&self) -> Self {
FontTransform::new(-self.scale_x, self.skew_x, -self.skew_y, self.scale_y)
}

pub fn flip_y(&self) -> Self {
FontTransform::new(self.scale_x, -self.skew_y, self.skew_y, -self.scale_y)
}
}

impl<'a> From<&'a LayerToWorldTransform> for FontTransform {
@@ -117,7 +117,6 @@ fn get_glyph_metrics(

if let Some(transform) = transform {
bounds = bounds.apply_transform(transform);
advance = advance.apply_transform(transform);
}

// First round out to pixel boundaries
@@ -361,8 +360,23 @@ impl FontContext {
let glyph = key.index as CGGlyph;
let bitmap = is_bitmap_font(ct_font);
let (x_offset, y_offset) = if bitmap { (0.0, 0.0) } else { font.get_subpx_offset(key) };
let transform = if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
let shape = FontTransform::identity().synthesize_italics(OBLIQUE_SKEW_FACTOR);
let transform = if font.flags.intersects(FontInstanceFlags::SYNTHETIC_ITALICS |
FontInstanceFlags::TRANSPOSE |
FontInstanceFlags::FLIP_X |
FontInstanceFlags::FLIP_Y) {
let mut shape = FontTransform::identity();
if font.flags.contains(FontInstanceFlags::FLIP_X) {
shape = shape.flip_x();
}
if font.flags.contains(FontInstanceFlags::FLIP_Y) {
shape = shape.flip_y();
}
if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
shape = shape.swap_xy();
}
if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
shape = shape.synthesize_italics(OBLIQUE_SKEW_FACTOR);
}
Some(CGAffineTransform {
a: shape.scale_x as f64,
b: -shape.skew_y as f64,
@@ -483,6 +497,15 @@ impl FontContext {
} else {
(font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key))
};
if font.flags.contains(FontInstanceFlags::FLIP_X) {
shape = shape.flip_x();
}
if font.flags.contains(FontInstanceFlags::FLIP_Y) {
shape = shape.flip_y();
}
if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
shape = shape.swap_xy();
}
if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
shape = shape.synthesize_italics(OBLIQUE_SKEW_FACTOR);
}
@@ -105,6 +105,38 @@ fn skew_bitmap(bitmap: &[u8], width: usize, height: usize, left: i32, top: i32)
(skew_buffer, skew_width, left + skew_min as i32)
}

fn transpose_bitmap(bitmap: &[u8], width: usize, height: usize) -> Vec<u8> {
let mut transposed = vec![0u8; width * height * 4];
for (y, row) in bitmap.chunks(width * 4).enumerate() {
let mut offset = y * 4;
for src in row.chunks(4) {
transposed[offset .. offset + 4].copy_from_slice(src);
offset += height * 4;
}
}
transposed
}

fn flip_bitmap_x(bitmap: &mut [u8], width: usize, height: usize) {
assert!(bitmap.len() == width * height * 4);
let pixels = unsafe { slice::from_raw_parts_mut(bitmap.as_mut_ptr() as *mut u32, width * height) };
for row in pixels.chunks_mut(width) {
row.reverse();
}
}

fn flip_bitmap_y(bitmap: &mut [u8], width: usize, height: usize) {
assert!(bitmap.len() == width * height * 4);
let pixels = unsafe { slice::from_raw_parts_mut(bitmap.as_mut_ptr() as *mut u32, width * height) };
for y in 0 .. height / 2 {
let low_row = y * width;
let high_row = (height - 1 - y) * width;
for x in 0 .. width {
pixels.swap(low_row + x, high_row + x);
}
}
}

impl FontContext {
pub fn new() -> FontContext {
let mut lib: FT_Library = ptr::null_mut();
@@ -250,6 +282,15 @@ impl FontContext {
self.choose_bitmap_size(face.face, req_size * y_scale)
} else {
let mut shape = font.transform.invert_scale(x_scale, y_scale);
if font.flags.contains(FontInstanceFlags::FLIP_X) {
shape = shape.flip_x();
}
if font.flags.contains(FontInstanceFlags::FLIP_Y) {
shape = shape.flip_y();
}
if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
shape = shape.swap_xy();
}
if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
shape = shape.synthesize_italics(OBLIQUE_SKEW_FACTOR);
};
@@ -391,6 +432,18 @@ impl FontContext {
left += skew_min as i32;
width += (skew_max - skew_min) as u32;
}
if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
mem::swap(&mut width, &mut height);
mem::swap(&mut left, &mut top);
left -= width as i32;
top += height as i32;
}
if font.flags.contains(FontInstanceFlags::FLIP_X) {
left = -(left + width as i32);
}
if font.flags.contains(FontInstanceFlags::FLIP_Y) {
top = -(top - height as i32);
}
}
Some(GlyphDimensions {
left,
@@ -570,7 +623,7 @@ impl FontContext {

let bitmap = unsafe { &(*slot).bitmap };
let pixel_mode = unsafe { mem::transmute(bitmap.pixel_mode as u32) };
let (mut actual_width, actual_height) = match pixel_mode {
let (mut actual_width, mut actual_height) = match pixel_mode {
FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
assert!(bitmap.width % 3 == 0);
((bitmap.width / 3) as usize, bitmap.rows as usize)
@@ -677,6 +730,21 @@ impl FontContext {
actual_width = skew_width;
left = skew_left;
}
if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
final_buffer = transpose_bitmap(&final_buffer, actual_width, actual_height);
mem::swap(&mut actual_width, &mut actual_height);
mem::swap(&mut left, &mut top);
left -= actual_width as i32;
top += actual_height as i32;
}
if font.flags.contains(FontInstanceFlags::FLIP_X) {
flip_bitmap_x(&mut final_buffer, actual_width, actual_height);
left = -(left + actual_width as i32);
}
if font.flags.contains(FontInstanceFlags::FLIP_Y) {
flip_bitmap_y(&mut final_buffer, actual_width, actual_height);
top = -(top - actual_height as i32);
}
}
FT_Glyph_Format::FT_GLYPH_FORMAT_OUTLINE => {
unsafe {
@@ -236,8 +236,23 @@ impl FontContext {
) -> Option<GlyphDimensions> {
let size = font.size.to_f32_px();
let bitmaps = is_bitmap_font(font);
let transform = if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
let shape = FontTransform::identity().synthesize_italics(OBLIQUE_SKEW_FACTOR);
let transform = if font.flags.intersects(FontInstanceFlags::SYNTHETIC_ITALICS |
FontInstanceFlags::TRANSPOSE |
FontInstanceFlags::FLIP_X |
FontInstanceFlags::FLIP_Y) {
let mut shape = FontTransform::identity();
if font.flags.contains(FontInstanceFlags::FLIP_X) {
shape = shape.flip_x();
}
if font.flags.contains(FontInstanceFlags::FLIP_Y) {
shape = shape.flip_y();
}
if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
shape = shape.swap_xy();
}
if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
shape = shape.synthesize_italics(OBLIQUE_SKEW_FACTOR);
}
Some(dwrote::DWRITE_MATRIX {
m11: shape.scale_x,
m12: shape.skew_y,
@@ -359,6 +374,15 @@ impl FontContext {
} else {
(font.transform.invert_scale(y_scale, y_scale), font.get_subpx_offset(key))
};
if font.flags.contains(FontInstanceFlags::FLIP_X) {
shape = shape.flip_x();
}
if font.flags.contains(FontInstanceFlags::FLIP_Y) {
shape = shape.flip_y();
}
if font.flags.contains(FontInstanceFlags::TRANSPOSE) {
shape = shape.swap_xy();
}
if font.flags.contains(FontInstanceFlags::SYNTHETIC_ITALICS) {
shape = shape.synthesize_italics(OBLIQUE_SKEW_FACTOR);
}
@@ -199,6 +199,16 @@ impl Hash for FontVariation {
#[derive(Clone, Copy, Debug, Deserialize, Hash, Eq, PartialEq, PartialOrd, Ord, Serialize)]
pub struct GlyphOptions {
pub render_mode: FontRenderMode,
pub flags: FontInstanceFlags,
}

impl Default for GlyphOptions {
fn default() -> GlyphOptions {
GlyphOptions {
render_mode: FontRenderMode::Subpixel,
flags: FontInstanceFlags::empty(),
}
}
}

bitflags! {
@@ -210,6 +220,9 @@ bitflags! {
const SYNTHETIC_BOLD = 1 << 1;
const EMBEDDED_BITMAPS = 1 << 2;
const SUBPIXEL_BGR = 1 << 3;
const TRANSPOSE = 1 << 4;
const FLIP_X = 1 << 5;
const FLIP_Y = 1 << 6;

// Windows flags
const FORCE_GDI = 1 << 16;
@@ -49,3 +49,4 @@ platform(linux) == subpixel-skew.yaml subpixel-skew.png
platform(linux) == embedded-bitmaps.yaml embedded-bitmaps.png
platform(linux) == clipped-transform.yaml clipped-transform.png
platform(mac) == color-bitmap-shadow.yaml color-bitmap-shadow-ref.yaml
platform(linux) == writing-modes.yaml writing-modes-ref.yaml
@@ -0,0 +1,19 @@
root:
items:
- type: stacking-context
bounds: [0, 0, 300, 60]
transform: rotate(90) translate(-120, 160)
items:
- text: "This is sideways-left"
origin: 0 40
size: 20
font: "FreeSans.ttf"
- type: stacking-context
bounds: [0, 0, 300, 60]
transform: rotate(-90) translate(-90, 120)
items:
- text: "This is sideways-right"
origin: 0 40
size: 20
font: "FreeSans.ttf"

@@ -0,0 +1,15 @@
root:
items:
- text: "This is sideways-left"
origin: 20 40
size: 20
transpose: true
flip-x: true
font: "FreeSans.ttf"
- text: "This is sideways-right"
origin: 70 300
size: 20
transpose: true
flip-y: true
font: "FreeSans.ttf"

@@ -260,6 +260,7 @@ impl Wrench {
text: &str,
size: Au,
origin: LayerPoint,
flags: FontInstanceFlags,
) -> (Vec<u32>, Vec<LayerPoint>, LayoutRect) {
// Map the string codepoints to glyph indices in this font.
// Just drop any glyph that isn't present in this font.
@@ -287,26 +288,36 @@ impl Wrench {
let mut bounding_rect = LayoutRect::zero();
let mut positions = Vec::new();

let mut x = origin.x;
let y = origin.y;
let mut cursor = origin;
let direction = if flags.contains(FontInstanceFlags::TRANSPOSE) {
LayerVector2D::new(
0.0,
if flags.contains(FontInstanceFlags::FLIP_Y) { -1.0 } else { 1.0 },
)
} else {
LayerVector2D::new(
if flags.contains(FontInstanceFlags::FLIP_X) { -1.0 } else { 1.0 },
0.0,
)
};
for metric in metrics {
positions.push(LayerPoint::new(x, y));
positions.push(cursor);

match metric {
Some(metric) => {
let glyph_rect = LayoutRect::new(
LayoutPoint::new(x + metric.left as f32, y - metric.top as f32),
LayoutPoint::new(cursor.x + metric.left as f32, cursor.y - metric.top as f32),
LayoutSize::new(metric.width as f32, metric.height as f32)
);
bounding_rect = bounding_rect.union(&glyph_rect);
x += metric.advance;
cursor += direction * metric.advance;
}
None => {
// Extract the advances from the metrics. The get_glyph_dimensions API
// has a limitation that it can't currently get dimensions for non-renderable
// glyphs (e.g. spaces), so just use a rough estimate in that case.
let space_advance = size.to_f32_px() / 3.0;
x += space_advance;
cursor += direction * space_advance;
}
}
}
@@ -195,7 +195,7 @@ pub struct YamlFrameReader {
image_map: HashMap<(PathBuf, Option<i64>), (ImageKey, LayoutSize)>,

fonts: HashMap<FontDescriptor, FontKey>,
font_instances: HashMap<(FontKey, Au), FontInstanceKey>,
font_instances: HashMap<(FontKey, Au, FontInstanceFlags), FontInstanceKey>,
font_render_mode: Option<FontRenderMode>,
}

@@ -461,7 +461,7 @@ impl YamlFrameReader {
let font_render_mode = self.font_render_mode;

*self.font_instances
.entry((font_key, size))
.entry((font_key, size, flags))
.or_insert_with(|| {
wrench.add_font_instance(
font_key,
@@ -1023,6 +1023,15 @@ impl YamlFrameReader {
if item["embedded-bitmaps"].as_bool().unwrap_or(false) {
flags |= FontInstanceFlags::EMBEDDED_BITMAPS;
}
if item["transpose"].as_bool().unwrap_or(false) {
flags |= FontInstanceFlags::TRANSPOSE;
}
if item["flip-x"].as_bool().unwrap_or(false) {
flags |= FontInstanceFlags::FLIP_X;
}
if item["flip-y"].as_bool().unwrap_or(false) {
flags |= FontInstanceFlags::FLIP_Y;
}

assert!(
item["blur-radius"].is_badvalue(),
@@ -1082,6 +1091,7 @@ impl YamlFrameReader {
text,
size,
origin,
flags,
);

let glyphs = glyph_indices
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.