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

Gamma correction for dwrite fonts #775

Merged
merged 1 commit into from Jan 26, 2017
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

gamma correction for dwrite fonts

  • Loading branch information
changm committed Jan 26, 2017
commit 41de1837593a69aa6d0b07078724fb5845b5ca1c

Some generated files are not rendered by default. Learn more.

@@ -29,6 +29,7 @@ time = "0.1"
threadpool = "1.3.2"
webrender_traits = {path = "../webrender_traits", default-features = false}
bitflags = "0.7"
gamma-lut = "0.1"

[dev-dependencies]
angle = {git = "https://github.com/servo/angle", branch = "servo"}
@@ -37,7 +38,7 @@ angle = {git = "https://github.com/servo/angle", branch = "servo"}
freetype = { version = "0.2", default-features = false }

[target.'cfg(target_os = "windows")'.dependencies]
dwrote = "0.1.5"
dwrote = "0.1.6"

[target.'cfg(target_os = "macos")'.dependencies]
core-graphics = "0.5.0"
@@ -126,6 +126,7 @@ extern crate webrender_traits;
extern crate offscreen_gl_context;
extern crate byteorder;
extern crate threadpool;
extern crate gamma_lut;

pub use renderer::{ExternalImage, ExternalImageSource, ExternalImageHandler};
pub use renderer::{Renderer, RendererOptions};
@@ -5,6 +5,7 @@
use app_units::Au;
use std::collections::HashMap;
use webrender_traits::{FontKey, ColorU, FontRenderMode, GlyphDimensions, GlyphOptions};
use gamma_lut::{GammaLut, Color as ColorLut};

use dwrote;

@@ -17,8 +18,11 @@ lazy_static! {
};
}

#[allow(dead_code)]

This comment has been minimized.

@vvuk

vvuk Jan 25, 2017

Contributor

dead_code here, why?

This comment has been minimized.

@changm

changm Jan 25, 2017

Author Contributor

We don't use the gdi_gamma_lut yet. We will once #770 lands.

pub struct FontContext {
fonts: HashMap<FontKey, dwrote::FontFace>,
gamma_lut: GammaLut,
gdi_gamma_lut: GammaLut,
}

pub struct RasterizedGlyph {
@@ -27,10 +31,60 @@ pub struct RasterizedGlyph {
pub bytes: Vec<u8>,
}

fn dwrite_texture_type(render_mode: FontRenderMode) ->
dwrote::DWRITE_TEXTURE_TYPE {
match render_mode {
FontRenderMode::Mono => dwrote::DWRITE_TEXTURE_ALIASED_1x1 ,
FontRenderMode::Alpha |
FontRenderMode::Subpixel => dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1,
}
}

fn dwrite_measure_mode(render_mode: FontRenderMode) ->
dwrote::DWRITE_MEASURING_MODE {
match render_mode {
FontRenderMode::Mono => dwrote::DWRITE_MEASURING_MODE_GDI_NATURAL,
FontRenderMode::Alpha => dwrote::DWRITE_MEASURING_MODE_GDI_NATURAL,
FontRenderMode::Subpixel => dwrote::DWRITE_MEASURING_MODE_GDI_CLASSIC,
}
}

fn dwrite_render_mode(render_mode: FontRenderMode) ->
dwrote::DWRITE_RENDERING_MODE {
match render_mode {
FontRenderMode::Mono => dwrote::DWRITE_RENDERING_MODE_ALIASED,
FontRenderMode::Alpha => dwrote::DWRITE_RENDERING_MODE_GDI_NATURAL,
FontRenderMode::Subpixel => dwrote::DWRITE_RENDERING_MODE_GDI_CLASSIC,
}
}

fn get_glyph_dimensions_with_analysis(analysis: dwrote::GlyphRunAnalysis,
texture_type: dwrote::DWRITE_TEXTURE_TYPE)
-> GlyphDimensions {
let bounds = analysis.get_alpha_texture_bounds(texture_type);

let width = (bounds.right - bounds.left) as u32;
let height = (bounds.bottom - bounds.top) as u32;
GlyphDimensions {
left: bounds.left,
top: -bounds.top,
width: width,
height: height,
}
}

impl FontContext {
pub fn new() -> FontContext {
// These are the default values we use in Gecko.
// We use a gamma value of 2.3 for gdi fonts
// TODO: Fetch this data from Gecko itself.
let contrast = 1.0;
let gamma = 1.8;
let gdi_gamma = 2.3;
FontContext {
fonts: HashMap::new(),
gamma_lut: GammaLut::new(contrast, gamma, gamma),
gdi_gamma_lut: GammaLut::new(contrast, gdi_gamma, gdi_gamma),
}
}

@@ -60,13 +114,28 @@ impl FontContext {
self.fonts.insert((*font_key).clone(), face);
}

fn get_glyph_dimensions_and_maybe_rasterize(&self,
font_key: FontKey,
size: Au,
glyph: u32,
render_mode: Option<FontRenderMode>)
-> (Option<GlyphDimensions>, Option<RasterizedGlyph>)
{
// Assumes RGB format from dwrite, which is 3 bytes per pixel as dwrite
// doesn't output an alpha value via GlyphRunAnalysis::CreateAlphaTexture
#[allow(dead_code)]
fn print_glyph_data(&self, data: &Vec<u8>, width: usize, height: usize) {
// Rust doesn't have step_by support on stable :(
for i in 0..height {
let current_height = i * width * 3;

for pixel in data[current_height .. current_height + (width * 3)].chunks(3) {
let r = pixel[0];
let g = pixel[1];
let b = pixel[2];
print!("({}, {}, {}) ", r, g, b, );
}
println!("");
}
}

fn create_glyph_analysis(&self, font_key: FontKey,
size: Au, glyph: u32,
render_mode: FontRenderMode) ->
dwrote::GlyphRunAnalysis {
let face = self.fonts.get(&font_key).unwrap();
let glyph = glyph as u16;

@@ -85,98 +154,73 @@ impl FontContext {
bidiLevel: 0,
};

// dwrite requires DWRITE_RENDERING_MODE_ALIASED if the texture
// type is DWRITE_TEXTURE_ALIASED_1x1. If CLEARTYPE_3x1,
// then the other modes can be used.

// TODO(vlad): get_glyph_dimensions needs to take the render mode into account
// but the API doesn't give it to us right now. Just assume subpixel.
let (r_mode, m_mode, tex_type) = match render_mode {
Some(FontRenderMode::Mono) => (dwrote::DWRITE_RENDERING_MODE_ALIASED,
dwrote::DWRITE_MEASURING_MODE_GDI_NATURAL,
dwrote::DWRITE_TEXTURE_ALIASED_1x1),
Some(FontRenderMode::Alpha) => (dwrote::DWRITE_RENDERING_MODE_GDI_NATURAL,
dwrote::DWRITE_MEASURING_MODE_GDI_NATURAL,
dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1),
Some(FontRenderMode::Subpixel) | None => (dwrote::DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL,
dwrote::DWRITE_MEASURING_MODE_GDI_NATURAL,
dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1),
};
let dwrite_render_mode = dwrite_render_mode(render_mode);
let dwrite_measure_mode = dwrite_measure_mode(render_mode);

// XX use the xform to handle subpixel positioning (what skia does), I believe that keeps
//let xform = dwrote::DWRITE_MATRIX { m11: 1.0, m12: 0.0, m21: 0.0, m22: 1.0, dx: 0.0, dy: 0.0 };
let analysis = dwrote::GlyphRunAnalysis::create(&glyph_run, 1.0, None, r_mode, m_mode, 0.0, 0.0);
let bounds = analysis.get_alpha_texture_bounds(tex_type);

let width = (bounds.right - bounds.left) as u32;
let height = (bounds.bottom - bounds.top) as u32;
let dims = GlyphDimensions {
left: bounds.left,
top: -bounds.top,
width: width,
height: height,
};
dwrote::GlyphRunAnalysis::create(&glyph_run, 1.0, None,
dwrite_render_mode,
dwrite_measure_mode,
0.0, 0.0)
}

// if empty, then nothing
if dims.width == 0 || dims.height == 0 {
return (None, None);
}
pub fn get_glyph_dimensions(&self,
font_key: FontKey,
size: Au,
glyph: u32) -> Option<GlyphDimensions> {
// Probably have to default to something else here.
let render_mode = FontRenderMode::Subpixel;
let analysis = self.create_glyph_analysis(font_key, size, glyph, render_mode);

// if we weren't asked to rasterize, we're done
if render_mode.is_none() {
return (Some(dims), None);
}
let texture_type = dwrite_texture_type(render_mode);
Some(get_glyph_dimensions_with_analysis(analysis, texture_type))
}

let pixels = analysis.create_alpha_texture(tex_type, bounds);
let rgba_pixels = match render_mode.unwrap() {
// DWRITE gives us values in RGB. WR doesn't really touch it after. Note, CG returns in BGR
// TODO: Decide whether all fonts should return RGB or BGR
fn convert_to_rgba(&self, pixels: &Vec<u8>, render_mode: FontRenderMode) -> Vec<u8> {
match render_mode {
FontRenderMode::Mono => {
let mut rgba_pixels = vec![0; pixels.len() * 4];
let mut rgba_pixels: Vec<u8> = Vec::with_capacity(pixels.len() * 4);
for i in 0..pixels.len() {
rgba_pixels[i*4+0] = 0xff;
rgba_pixels[i*4+1] = 0xff;
rgba_pixels[i*4+2] = 0xff;
rgba_pixels[i*4+0] = pixels[i];
rgba_pixels[i*4+1] = pixels[i];
rgba_pixels[i*4+2] = pixels[i];
rgba_pixels[i*4+3] = pixels[i];
}
rgba_pixels
}
FontRenderMode::Alpha => {
let mut rgba_pixels = vec![0; pixels.len()/3 * 4];
for i in 0..pixels.len()/3 {
let length = pixels.len() / 3;
let mut rgba_pixels: Vec<u8> = Vec::with_capacity(length * 4);
for i in 0..length {
// TODO(vlad): we likely need to do something smarter
let alpha = (pixels[i*3+0] as u32 + pixels[i*3+0] as u32 + pixels[i*3+0] as u32) / 3;
rgba_pixels[i*4+0] = 0xff;
rgba_pixels[i*4+1] = 0xff;
rgba_pixels[i*4+2] = 0xff;
rgba_pixels[i*4+3] = alpha as u8;
// This is what skia does
let alpha = ((pixels[i*3+0] as u32 +
pixels[i*3+1] as u32 +
pixels[i*3+2] as u32)
/ 3) as u8;

rgba_pixels[i*4+0] = alpha;
rgba_pixels[i*4+1] = alpha;
rgba_pixels[i*4+2] = alpha;
rgba_pixels[i*4+3] = alpha;
}
rgba_pixels
}
FontRenderMode::Subpixel => {
let mut rgba_pixels = vec![0; pixels.len()/3 * 4];
for i in 0..pixels.len()/3 {
let length = pixels.len() / 3;
let mut rgba_pixels: Vec<u8> = Vec::with_capacity(length * 4);
for i in 0..length {
rgba_pixels[i*4+0] = pixels[i*3+0];
rgba_pixels[i*4+1] = pixels[i*3+1];
rgba_pixels[i*4+2] = pixels[i*3+2];
rgba_pixels[i*4+3] = 0xff;
}
rgba_pixels
}
};

(Some(dims), Some(RasterizedGlyph {
width: dims.width,
height: dims.height,
bytes: rgba_pixels,
}))
}

pub fn get_glyph_dimensions(&self,
font_key: FontKey,
size: Au,
glyph: u32) -> Option<GlyphDimensions> {
let (maybe_dims, _) =
self.get_glyph_dimensions_and_maybe_rasterize(font_key, size, glyph, None);
maybe_dims
}
}

pub fn rasterize_glyph(&mut self,
@@ -186,8 +230,23 @@ impl FontContext {
glyph: u32,
render_mode: FontRenderMode,
glyph_options: Option<GlyphOptions>) -> Option<RasterizedGlyph> {
let (_, maybe_glyph) =
self.get_glyph_dimensions_and_maybe_rasterize(font_key, size, glyph, Some(render_mode));
maybe_glyph
let analysis = self.create_glyph_analysis(font_key, size, glyph, render_mode);
let texture_type = dwrite_texture_type(render_mode);

let bounds = analysis.get_alpha_texture_bounds(texture_type);
let width = (bounds.right - bounds.left) as usize;
let height = (bounds.bottom - bounds.top) as usize;

let mut pixels = analysis.create_alpha_texture(texture_type, bounds);
self.gamma_lut.preblend_rgb(&mut pixels, width, height,
ColorLut::new(color.r, color.g, color.b, color.a));

let rgba_pixels = self.convert_to_rgba(&mut pixels, render_mode);

Some(RasterizedGlyph {
width: width as u32,
height: height as u32,
bytes: rgba_pixels,
})
}
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.