Use subpixel font rendering

OpenGL only supports shared alpha blending. Subpixel font rendering
requires using the font RGB values as alpha masks for the corresponding
RGB channels. To support this, blending is implemented in the fragment
shader.
This commit is contained in:
Joe Wilm 2016-04-11 08:05:19 -07:00
parent b84eb9e921
commit e794bc11b9
No known key found for this signature in database
GPG Key ID: 39B57C6972F518DA
5 changed files with 93 additions and 22 deletions

View File

@ -3,9 +3,28 @@ in vec2 TexCoords;
uniform sampler2D mask;
uniform vec3 textColor;
uniform vec3 bgColor;
// SRC = SRC_ALPHA; DST = 1 - SRC_ALPHA
void MyBlend(in vec3 srcValue,
in vec3 dstValue,
in vec3 srcAlpha,
out vec3 blended)
{
vec3 dstAlpha = vec3(1.0, 1.0, 1.0) - srcAlpha;
vec3 preBlended = (srcValue * srcAlpha + dstValue * dstAlpha);
blended = vec3(min(1.0, preBlended.x),
min(1.0, preBlended.y),
min(1.0, preBlended.z));
}
void main()
{
vec4 sampled = vec4(1.0, 1.0, 1.0, texture(mask, TexCoords).r);
gl_FragColor = vec4(textColor, 1.0) * sampled;
// vec4 red = vec4(sampled.rgb, sampled.r * sampled.g * sampled.b);
// vec4 sampled = vec4(1.0, 1.0, 1.0, texture(mask, TexCoords));
vec3 blended = vec3(1.0, 1.0, 1.0);
MyBlend(textColor, bgColor, texture(mask, TexCoords).rgb, blended);
gl_FragColor = vec4(blended, 1.0);
}

View File

@ -26,7 +26,7 @@ pub struct Grid {
raw: Vec<Row>,
/// Number of columns
_cols: usize,
cols: usize,
/// Number of rows.
///
@ -43,7 +43,7 @@ impl Grid {
Grid {
raw: raw,
_cols: cols,
cols: cols,
rows: rows,
}
}
@ -51,6 +51,10 @@ impl Grid {
pub fn rows(&self) -> usize {
self.rows
}
pub fn cols(&self) -> usize {
self.cols
}
}
impl Index<usize> for Grid {

View File

@ -20,7 +20,7 @@ use grid::Grid;
static INIT_LIST: &'static str = "abcdefghijklmnopqrstuvwxyz\
ABCDEFGHIJKLMNOPQRSTUVWXYZ\
01234567890\
~`!@#$%^&*()[]{}-_=+\\|\"/?.,<>";
~`!@#$%^&*()[]{}-_=+\\|\"/?.,<>;:";
fn main() {
let window = glutin::Window::new().unwrap();
@ -37,12 +37,12 @@ fn main() {
let (dpi_x, dpi_y) = window.get_dpi().unwrap();
let dpr = window.hidpi_factor();
let font_size = 12.0;
let font_size = 11.0;
let sep_x = 2;
let sep_y = 2;
let sep_y = 5;
let desc = FontDesc::new("Ubuntu Mono", "Regular");
let desc = FontDesc::new("DejaVu Sans Mono", "Book");
let mut rasterizer = text::Rasterizer::new(dpi_x, dpi_y, dpr);
let (cell_width, cell_height) = rasterizer.box_size_for_font(&desc, font_size);
@ -54,10 +54,35 @@ fn main() {
let mut grid = Grid::new(num_rows as usize, num_cols as usize);
grid[0][0] = grid::Cell::new(Some(String::from("R")));
grid[0][1] = grid::Cell::new(Some(String::from("u")));
grid[0][2] = grid::Cell::new(Some(String::from("s")));
grid[0][3] = grid::Cell::new(Some(String::from("t")));
// let contents = [
// "for (row, line) in contents.iter().enumerate() {",
// " for (i, c) in line.chars().enumerate() {",
// " grid[row][i] = grid::Cell::new(Some(c.escape_default().collect()));",
// " }",
// "}"];
let contents = include_str!("grid.rs");
let mut row = 0usize;
let mut col = 0;
for (i, c) in contents.chars().enumerate() {
if c == '\n' {
row += 1;
col = 0;
continue;
}
if row >= (num_rows as usize) {
break;
}
if col >= grid.cols() {
continue;
}
grid[row][col] = grid::Cell::new(Some(c.escape_default().collect()));
col += 1;
}
let mut glyph_cache = HashMap::new();
for c in INIT_LIST.chars() {
@ -67,8 +92,8 @@ fn main() {
}
unsafe {
gl::Enable(gl::BLEND);
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
// gl::Enable(gl::BLEND);
// gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
gl::Enable(gl::MULTISAMPLE);
}

View File

@ -97,6 +97,7 @@ impl QuadRenderer {
unsafe {
// set color
gl::Uniform3f(self.program.u_color, 1., 1., 0.5);
gl::Uniform3f(self.program.u_bg_color, 0.08, 0.08, 0.08);
}
let rect = get_rect(glyph, x, y);
@ -134,7 +135,7 @@ impl QuadRenderer {
fn get_rect(glyph: &Glyph, x: f32, y: f32) -> Rect<f32> {
Rect::new(
Point2D::new(x, y),
Point2D::new(x + glyph.left as f32, y - (glyph.height - glyph.top) as f32),
Size2D::new(glyph.width as f32, glyph.height as f32)
)
}
@ -152,6 +153,8 @@ pub struct ShaderProgram {
u_projection: GLint,
/// color uniform
u_color: GLint,
/// background color uniform
u_bg_color: GLint,
}
impl ShaderProgram {
@ -180,11 +183,13 @@ impl ShaderProgram {
// get uniform locations
let projection_str = CString::new("projection").unwrap();
let color_str = CString::new("textColor").unwrap();
let bg_color_str = CString::new("bgColor").unwrap();
let (projection, color) = unsafe {
let (projection, color, bg_color) = unsafe {
(
gl::GetUniformLocation(program, projection_str.as_ptr()),
gl::GetUniformLocation(program, color_str.as_ptr())
gl::GetUniformLocation(program, color_str.as_ptr()),
gl::GetUniformLocation(program, bg_color_str.as_ptr()),
)
};
@ -192,11 +197,14 @@ impl ShaderProgram {
assert!(projection != gl::INVALID_OPERATION as i32);
assert!(color != gl::INVALID_VALUE as i32);
assert!(color != gl::INVALID_OPERATION as i32);
assert!(bg_color != gl::INVALID_VALUE as i32);
assert!(bg_color != gl::INVALID_OPERATION as i32);
let shader = ShaderProgram {
id: program,
u_projection: projection,
u_color: color,
u_bg_color: bg_color,
};
// set projection uniform
@ -287,11 +295,11 @@ impl Glyph {
gl::TexImage2D(
gl::TEXTURE_2D,
0,
gl::RED as i32,
gl::RGB as i32,
rasterized.width as i32,
rasterized.height as i32,
0,
gl::RED,
gl::RGB,
gl::UNSIGNED_BYTE,
rasterized.buf.as_ptr() as *const _
);

View File

@ -87,15 +87,30 @@ impl Rasterizer {
pub fn get_glyph(&mut self, desc: &FontDesc, size: f32, c: char) -> RasterizedGlyph {
let face = self.get_face(desc).expect("TODO handle get_face error");
face.set_char_size(to_freetype_26_6(size * self.dpr), 0, self.dpi_x, self.dpi_y).unwrap();
face.load_char(c as usize, freetype::face::RENDER).unwrap();
face.load_char(c as usize, freetype::face::TARGET_LIGHT).unwrap();
let glyph = face.glyph();
glyph.render_glyph(freetype::render_mode::RenderMode::Lcd).unwrap();
// FIXME need LCD filtering to reduce color fringes with subpixel rendering. The freetype
// bindings don't currently expose this!
let bitmap = glyph.bitmap();
let buf = bitmap.buffer();
let pitch = bitmap.pitch() as usize;
let mut packed = Vec::with_capacity((bitmap.rows() * bitmap.width()) as usize);
for i in 0..bitmap.rows() {
let start = (i as usize) * pitch;
let stop = start + bitmap.width() as usize;
packed.extend_from_slice(&buf[start..stop]);
}
RasterizedGlyph {
top: glyph.bitmap_top() as usize,
left: glyph.bitmap_left() as usize,
width: glyph.bitmap().width() as usize,
width: glyph.bitmap().width() as usize / 3,
height: glyph.bitmap().rows() as usize,
buf: glyph.bitmap().buffer().to_vec(),
buf: packed,
}
}
}