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

cleanup of subpixel glyph format #1969

Merged
merged 1 commit into from Nov 1, 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

@@ -295,82 +295,41 @@ impl GammaLut {
table
}

// Skia normally preblends based on what the text color is.
// If we can't do that, use Skia default colors.
pub fn preblend_default_colors_bgra(&self, pixels: &mut [u8], width: usize, height: usize) {
let preblend_color = ColorU::new(0x7f, 0x80, 0x7f, 0xff);
self.preblend_bgra(pixels, width, height, preblend_color);
}

fn replace_pixels_bgra(&self, pixels: &mut [u8], width: usize, height: usize,
table_r: &[u8; 256], table_g: &[u8; 256], table_b: &[u8; 256]) {
for y in 0..height {
let current_height = y * width * 4;

for pixel in pixels[current_height..current_height + (width * 4)].chunks_mut(4) {
pixel[0] = table_b[pixel[0] as usize];
pixel[1] = table_g[pixel[1] as usize];
pixel[2] = table_r[pixel[2] as usize];
// Don't touch alpha
}
}
}

// Mostly used by windows and GlyphRunAnalysis::GetAlphaTexture
fn replace_pixels_rgb(&self, pixels: &mut [u8], width: usize, height: usize,
table_r: &[u8; 256], table_g: &[u8; 256], table_b: &[u8; 256]) {
for y in 0..height {
let current_height = y * width * 3;

for pixel in pixels[current_height..current_height + (width * 3)].chunks_mut(3) {
pixel[0] = table_r[pixel[0] as usize];
pixel[1] = table_g[pixel[1] as usize];
pixel[2] = table_b[pixel[2] as usize];
}
}
}

// Assumes pixels are in BGRA format. Assumes pixel values are in linear space already.
pub fn preblend_bgra(&self, pixels: &mut [u8], width: usize, height: usize, color: ColorU) {
pub fn preblend(&self, pixels: &mut [u8], color: ColorU) {
let table_r = self.get_table(color.r);
let table_g = self.get_table(color.g);
let table_b = self.get_table(color.b);

self.replace_pixels_bgra(pixels, width, height, table_r, table_g, table_b);
}

// Assumes pixels are in RGB format. Assumes pixel values are in linear space already. NOTE:
// there is no alpha here.
pub fn preblend_rgb(&self, pixels: &mut [u8], width: usize, height: usize, color: ColorU) {
let table_r = self.get_table(color.r);
let table_g = self.get_table(color.g);
let table_b = self.get_table(color.b);

self.replace_pixels_rgb(pixels, width, height, table_r, table_g, table_b);
for pixel in pixels.chunks_mut(4) {
pixel[0] = table_b[pixel[0] as usize];
pixel[1] = table_g[pixel[1] as usize];
pixel[2] = table_r[pixel[2] as usize];
// Use green channel as a cheap grayscale approximation that won't disturb preblending.
pixel[3] = pixel[1];
}
}

#[cfg(target_os="macos")]
pub fn coregraphics_convert_to_linear_bgra(&self, pixels: &mut [u8], width: usize, height: usize) {
self.replace_pixels_bgra(pixels, width, height,
&self.cg_inverse_gamma,
&self.cg_inverse_gamma,
&self.cg_inverse_gamma);
pub fn coregraphics_convert_to_linear(&self, pixels: &mut [u8]) {
for pixel in pixels.chunks_mut(4) {
pixel[0] = self.cg_inverse_gamma[pixel[0] as usize];
pixel[1] = self.cg_inverse_gamma[pixel[1] as usize];
pixel[2] = self.cg_inverse_gamma[pixel[2] as usize];
}
}

// Assumes pixels are in BGRA format. Assumes pixel values are in linear space already.
pub fn preblend_grayscale_bgra(&self, pixels: &mut [u8], width: usize, height: usize, color: ColorU) {
pub fn preblend_grayscale(&self, pixels: &mut [u8], color: ColorU) {
let table_g = self.get_table(color.g);

for y in 0..height {
let current_height = y * width * 4;

for pixel in pixels[current_height..current_height + (width * 4)].chunks_mut(4) {
let luminance = compute_luminance(pixel[2], pixel[1], pixel[0]);
pixel[0] = table_g[luminance as usize];
pixel[1] = table_g[luminance as usize];
pixel[2] = table_g[luminance as usize];
pixel[3] = table_g[luminance as usize];
}
for pixel in pixels.chunks_mut(4) {
let luminance = compute_luminance(pixel[2], pixel[1], pixel[0]);
let alpha = table_g[luminance as usize];
pixel[0] = alpha;
pixel[1] = alpha;
pixel[2] = alpha;
pixel[3] = alpha;
}
}

@@ -371,20 +371,16 @@ impl FontContext {
fn gamma_correct_pixels(
&self,
pixels: &mut Vec<u8>,
width: usize,
height: usize,
render_mode: FontRenderMode,
color: ColorU,
) {
// Then convert back to gamma corrected values.
match render_mode {
FontRenderMode::Alpha => {
self.gamma_lut
.preblend_grayscale_bgra(pixels, width, height, color);
self.gamma_lut.preblend_grayscale(pixels, color);
}
FontRenderMode::Subpixel => {
self.gamma_lut
.preblend_bgra(pixels, width, height, color);
self.gamma_lut.preblend(pixels, color);
}
_ => {} // Again, give mono untouched since only the alpha matters.
}
@@ -595,35 +591,28 @@ impl FontContext {
// We explicitly do not do this for grayscale AA ("Alpha without
// smoothing" or Mono) because those rendering modes are not
// gamma-aware in CoreGraphics.
self.gamma_lut.coregraphics_convert_to_linear_bgra(
self.gamma_lut.coregraphics_convert_to_linear(
&mut rasterized_pixels,
metrics.rasterized_width as usize,
metrics.rasterized_height as usize,
);
}

for i in 0 .. metrics.rasterized_height {
let current_height = (i * metrics.rasterized_width * 4) as usize;
let end_row = current_height + (metrics.rasterized_width as usize * 4);

for pixel in rasterized_pixels[current_height .. end_row].chunks_mut(4) {
if invert {
pixel[0] = 255 - pixel[0];
pixel[1] = 255 - pixel[1];
pixel[2] = 255 - pixel[2];
}
for pixel in rasterized_pixels.chunks_mut(4) {
if invert {
pixel[0] = 255 - pixel[0];
pixel[1] = 255 - pixel[1];
pixel[2] = 255 - pixel[2];
}

// Set alpha to the value of the green channel. For grayscale
// text, all three channels have the same value anyway.
// For subpixel text, the mask's alpha only makes a difference
// when computing the destination alpha on destination pixels
// that are not completely opaque. Picking an alpha value
// that's somehow based on the mask at least ensures that text
// blending doesn't modify the destination alpha on pixels where
// the mask is entirely zero.
pixel[3] = pixel[1];
} // end row
} // end height
// Set alpha to the value of the green channel. For grayscale
// text, all three channels have the same value anyway.
// For subpixel text, the mask's alpha only makes a difference
// when computing the destination alpha on destination pixels
// that are not completely opaque. Picking an alpha value
// that's somehow based on the mask at least ensures that text
// blending doesn't modify the destination alpha on pixels where
// the mask is entirely zero.
pixel[3] = pixel[1];
}

if smooth {
// Convert back from linear space into device space, and perform
@@ -632,8 +621,6 @@ impl FontContext {
// into grayscale AA.
self.gamma_correct_pixels(
&mut rasterized_pixels,
metrics.rasterized_width as usize,
metrics.rasterized_height as usize,
font.render_mode,
font.color,
);
@@ -554,45 +554,34 @@ impl FontContext {
}
}
FT_Pixel_Mode::FT_PIXEL_MODE_LCD => {
if subpixel_bgr {
while dest < row_end {
final_buffer[dest + 0] = unsafe { *src };
final_buffer[dest + 1] = unsafe { *src.offset(1) };
final_buffer[dest + 2] = unsafe { *src.offset(2) };
final_buffer[dest + 3] = 0xff;
src = unsafe { src.offset(3) };
dest += 4;
}
} else {
while dest < row_end {
final_buffer[dest + 2] = unsafe { *src };
final_buffer[dest + 1] = unsafe { *src.offset(1) };
final_buffer[dest + 0] = unsafe { *src.offset(2) };
final_buffer[dest + 3] = 0xff;
src = unsafe { src.offset(3) };
dest += 4;
while dest < row_end {
let (mut r, g, mut b) = unsafe { (*src, *src.offset(1), *src.offset(2)) };
if subpixel_bgr {
mem::swap(&mut r, &mut b);
}
final_buffer[dest + 0] = b;
final_buffer[dest + 1] = g;
final_buffer[dest + 2] = r;
// Use green as a cheap grayscale approximation.
final_buffer[dest + 3] = g;
src = unsafe { src.offset(3) };
dest += 4;
}
}
FT_Pixel_Mode::FT_PIXEL_MODE_LCD_V => {
if subpixel_bgr {
while dest < row_end {
final_buffer[dest + 0] = unsafe { *src };
final_buffer[dest + 1] = unsafe { *src.offset(bitmap.pitch as isize) };
final_buffer[dest + 2] = unsafe { *src.offset((2 * bitmap.pitch) as isize) };
final_buffer[dest + 3] = 0xff;
src = unsafe { src.offset(1) };
dest += 4;
}
} else {
while dest < row_end {
final_buffer[dest + 2] = unsafe { *src };
final_buffer[dest + 1] = unsafe { *src.offset(bitmap.pitch as isize) };
final_buffer[dest + 0] = unsafe { *src.offset((2 * bitmap.pitch) as isize) };
final_buffer[dest + 3] = 0xff;
src = unsafe { src.offset(1) };
dest += 4;
while dest < row_end {
let (mut r, g, mut b) =
unsafe { (*src, *src.offset(bitmap.pitch as isize), *src.offset((2 * bitmap.pitch) as isize)) };
if subpixel_bgr {
mem::swap(&mut r, &mut b);
}
final_buffer[dest + 0] = b;
final_buffer[dest + 1] = g;
final_buffer[dest + 2] = r;
// Use green as a cheap grayscale approximation.
final_buffer[dest + 3] = g;
src = unsafe { src.offset(1) };
dest += 4;
}
src_row = unsafe { src_row.offset((2 * bitmap.pitch) as isize) };
}
@@ -605,6 +594,7 @@ impl FontContext {
_ => panic!("Unsupported {:?}", pixel_mode),
}
src_row = unsafe { src_row.offset(bitmap.pitch as isize) };
dest = row_end;
}

Some(RasterizedGlyph {
@@ -253,46 +253,46 @@ impl FontContext {
})
}

// 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: &[u8], render_mode: FontRenderMode) -> Vec<u8> {
// DWrite ClearType gives us values in RGB, but WR expects BGRA.
fn convert_to_bgra(&self, pixels: &[u8], render_mode: FontRenderMode) -> Vec<u8> {
match render_mode {
FontRenderMode::Bitmap => {
unreachable!("TODO: bitmap fonts");
}
FontRenderMode::Mono => {
let mut rgba_pixels: Vec<u8> = vec![0; pixels.len() * 4];
let mut bgra_pixels: Vec<u8> = vec![0; pixels.len() * 4];
for i in 0 .. pixels.len() {
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];
let alpha = pixels[i];
bgra_pixels[i * 4 + 0] = alpha;
bgra_pixels[i * 4 + 1] = alpha;
bgra_pixels[i * 4 + 2] = alpha;
bgra_pixels[i * 4 + 3] = alpha;
}
rgba_pixels
bgra_pixels
}
FontRenderMode::Alpha => {
let length = pixels.len() / 3;
let mut rgba_pixels: Vec<u8> = vec![0; length * 4];
let mut bgra_pixels: Vec<u8> = vec![0; length * 4];
for i in 0 .. length {
// Only take the G channel, as its closest to D2D
let alpha = pixels[i * 3 + 1] 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;
bgra_pixels[i * 4 + 0] = alpha;
bgra_pixels[i * 4 + 1] = alpha;
bgra_pixels[i * 4 + 2] = alpha;
bgra_pixels[i * 4 + 3] = alpha;
}
rgba_pixels
bgra_pixels
}
FontRenderMode::Subpixel => {
let length = pixels.len() / 3;
let mut rgba_pixels: Vec<u8> = vec![0; length * 4];
let mut bgra_pixels: Vec<u8> = vec![0; 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;
bgra_pixels[i * 4 + 0] = pixels[i * 3 + 0];
bgra_pixels[i * 4 + 1] = pixels[i * 3 + 1];
bgra_pixels[i * 4 + 2] = pixels[i * 3 + 2];
bgra_pixels[i * 4 + 3] = 0xff;

This comment has been minimized.

@mstange

mstange Oct 31, 2017

Contributor

Should this be changed to use green as well?

This comment has been minimized.

@lsalzman

lsalzman Oct 31, 2017

Author Contributor

The gamma-lut now does that during the preblend (for mac and windows). This Windows code now converts to BGRA before the preblend happens, rather than after, so that value on input is just useless for the most part.

This comment has been minimized.

@mstange

mstange Oct 31, 2017

Contributor

oh I see.

}
rgba_pixels
bgra_pixels
}
}
}
@@ -328,16 +328,17 @@ impl FontContext {
let texture_type = dwrite_texture_type(font.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 width = (bounds.right - bounds.left) as u32;
let height = (bounds.bottom - bounds.top) as u32;

// Alpha texture bounds can sometimes return an empty rect
// Such as for spaces
if width == 0 || height == 0 {
return None;
}

let mut pixels = analysis.create_alpha_texture(texture_type, bounds);
let pixels = analysis.create_alpha_texture(texture_type, bounds);
let mut bgra_pixels = self.convert_to_bgra(&pixels, font.render_mode);

match font.render_mode {
FontRenderMode::Mono | FontRenderMode::Bitmap => {}
@@ -351,25 +352,18 @@ impl FontContext {
None => &self.gamma_lut,
};

lut_correction.preblend_rgb(
&mut pixels,
width,
height,
font.color,
);
lut_correction.preblend(&mut bgra_pixels, font.color);
}
}

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

Some(RasterizedGlyph {
left: bounds.left as f32,
top: -bounds.top as f32,
width: width as u32,
height: height as u32,
width,
height,
scale: 1.0,
format: GlyphFormat::from(font.render_mode),
bytes: rgba_pixels,
bytes: bgra_pixels,
})
}
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.