Get subpixel AA and gamma correction working in 2D

This commit is contained in:
Patrick Walton 2019-03-25 16:13:56 -07:00
parent 29cedb96ef
commit db3851d754
8 changed files with 163 additions and 117 deletions

View File

@ -116,7 +116,7 @@ impl<W> DemoApp<W> where W: Window {
let resources = window.resource_loader(); let resources = window.resource_loader();
let options = Options::get(); let options = Options::get();
let view_box_size = view_box_size(options.mode, &window_size, false); let view_box_size = view_box_size(options.mode, &window_size);
let built_svg = load_scene(resources, &options.input_path); let built_svg = load_scene(resources, &options.input_path);
let message = get_svg_building_message(&built_svg); let message = get_svg_building_message(&built_svg);
@ -199,9 +199,7 @@ impl<W> DemoApp<W> where W: Window {
} }
fn build_scene(&mut self) { fn build_scene(&mut self) {
let view_box_size = view_box_size(self.ui.mode, let view_box_size = view_box_size(self.ui.mode, &self.window_size);
&self.window_size,
self.ui.subpixel_aa_effect_enabled);
let render_transform = match self.camera { let render_transform = match self.camera {
Camera::ThreeD { ref mut transform, ref mut velocity } => { Camera::ThreeD { ref mut transform, ref mut velocity } => {
@ -232,6 +230,7 @@ impl<W> DemoApp<W> where W: Window {
} else { } else {
None None
}, },
subpixel_aa_enabled: self.ui.subpixel_aa_effect_enabled,
barrel_distortion, barrel_distortion,
})).unwrap(); })).unwrap();
} }
@ -254,9 +253,7 @@ impl<W> DemoApp<W> where W: Window {
} }
Event::WindowResized(new_size) => { Event::WindowResized(new_size) => {
self.window_size = new_size; self.window_size = new_size;
let view_box_size = view_box_size(self.ui.mode, let view_box_size = view_box_size(self.ui.mode, &self.window_size);
&self.window_size,
self.ui.subpixel_aa_effect_enabled);
self.scene_thread_proxy.set_drawable_size(view_box_size); self.scene_thread_proxy.set_drawable_size(view_box_size);
self.renderer.set_main_framebuffer_size(self.window_size.device_size()); self.renderer.set_main_framebuffer_size(self.window_size.device_size());
self.dirty = true; self.dirty = true;
@ -343,9 +340,7 @@ impl<W> DemoApp<W> where W: Window {
let built_svg = load_scene(self.window.resource_loader(), svg_path); let built_svg = load_scene(self.window.resource_loader(), svg_path);
self.ui.message = get_svg_building_message(&built_svg); self.ui.message = get_svg_building_message(&built_svg);
let view_box_size = view_box_size(self.ui.mode, let view_box_size = view_box_size(self.ui.mode, &self.window_size);
&self.window_size,
self.ui.subpixel_aa_effect_enabled);
self.scene_view_box = built_svg.scene.view_box; self.scene_view_box = built_svg.scene.view_box;
self.monochrome_scene_color = built_svg.scene.monochrome_color(); self.monochrome_scene_color = built_svg.scene.monochrome_color();
@ -539,24 +534,20 @@ impl<W> DemoApp<W> where W: Window {
let render_msg = &self.current_frame.as_ref().unwrap().render_msg; let render_msg = &self.current_frame.as_ref().unwrap().render_msg;
let built_scene = &render_msg.render_scenes[viewport_index as usize].built_scene; let built_scene = &render_msg.render_scenes[viewport_index as usize].built_scene;
let view_box_size = view_box_size(self.ui.mode, let view_box_size = view_box_size(self.ui.mode, &self.window_size);
&self.window_size,
self.ui.subpixel_aa_effect_enabled);
let viewport_origin_x = viewport_index as i32 * view_box_size.x(); let viewport_origin_x = viewport_index as i32 * view_box_size.x();
let viewport = RectI32::new(Point2DI32::new(viewport_origin_x, 0), view_box_size); let viewport = RectI32::new(Point2DI32::new(viewport_origin_x, 0), view_box_size);
self.renderer.set_viewport(viewport); self.renderer.set_viewport(viewport);
match self.monochrome_scene_color { match self.monochrome_scene_color {
None => self.renderer.set_render_mode(RenderMode::Multicolor), None => self.renderer.set_render_mode(RenderMode::Multicolor),
Some(fill_color) => { Some(fg_color) => {
self.renderer.set_render_mode(RenderMode::Monochrome { self.renderer.set_render_mode(RenderMode::Monochrome {
fill_color: fill_color.to_f32(), fg_color: fg_color.to_f32(),
gamma_correction_bg_color: if self.ui.gamma_correction_effect_enabled { bg_color: self.background_color().to_f32(),
Some(self.background_color()) gamma_correction: self.ui.gamma_correction_effect_enabled,
} else {
None
},
defringing_kernel: if self.ui.subpixel_aa_effect_enabled { defringing_kernel: if self.ui.subpixel_aa_effect_enabled {
// TODO(pcwalton): Select FreeType defringing kernel as necessary.
Some(DEFRINGING_KERNEL_CORE_GRAPHICS) Some(DEFRINGING_KERNEL_CORE_GRAPHICS)
} else { } else {
None None
@ -709,6 +700,7 @@ struct BuildOptions {
render_transforms: Vec<RenderTransform>, render_transforms: Vec<RenderTransform>,
stem_darkening_font_size: Option<f32>, stem_darkening_font_size: Option<f32>,
barrel_distortion: Option<BarrelDistortionCoefficients>, barrel_distortion: Option<BarrelDistortionCoefficients>,
subpixel_aa_enabled: bool,
} }
struct SceneToMainMsg { struct SceneToMainMsg {
@ -810,8 +802,6 @@ fn build_scene(scene: &Scene,
render_transform: RenderTransform, render_transform: RenderTransform,
jobs: Option<usize>) jobs: Option<usize>)
-> BuiltScene { -> BuiltScene {
let z_buffer = ZBuffer::new(scene.view_box);
let render_options = RenderOptions { let render_options = RenderOptions {
transform: render_transform, transform: render_transform,
dilation: match build_options.stem_darkening_font_size { dilation: match build_options.stem_darkening_font_size {
@ -822,9 +812,13 @@ fn build_scene(scene: &Scene,
} }
}, },
barrel_distortion: build_options.barrel_distortion, barrel_distortion: build_options.barrel_distortion,
subpixel_aa_enabled: build_options.subpixel_aa_enabled,
}; };
let built_options = render_options.prepare(scene.bounds); let built_options = render_options.prepare(scene.bounds);
let effective_view_box = scene.effective_view_box(&built_options);
let z_buffer = ZBuffer::new(effective_view_box);
let quad = built_options.quad(); let quad = built_options.quad();
let built_objects = panic::catch_unwind(|| { let built_objects = panic::catch_unwind(|| {
@ -846,7 +840,7 @@ fn build_scene(scene: &Scene,
let mut built_scene = BuiltScene::new(scene.view_box, &quad, scene.objects.len() as u32); let mut built_scene = BuiltScene::new(scene.view_box, &quad, scene.objects.len() as u32);
built_scene.shaders = scene.build_shaders(); built_scene.shaders = scene.build_shaders();
let mut scene_builder = SceneBuilder::new(built_objects, z_buffer, scene.view_box); let mut scene_builder = SceneBuilder::new(built_objects, z_buffer, effective_view_box);
built_scene.solid_tiles = scene_builder.build_solid_tiles(); built_scene.solid_tiles = scene_builder.build_solid_tiles();
while let Some(batch) = scene_builder.build_batch() { while let Some(batch) = scene_builder.build_batch() {
built_scene.batches.push(batch); built_scene.batches.push(batch);
@ -966,13 +960,12 @@ fn emit_message<W>(ui: &mut DemoUI<GLDevice>,
}); });
} }
fn view_box_size(mode: Mode, window_size: &WindowSize, use_subpixel_aa: bool) -> Point2DI32 { fn view_box_size(mode: Mode, window_size: &WindowSize) -> Point2DI32 {
let window_drawable_size = window_size.device_size(); let window_drawable_size = window_size.device_size();
let initial_size = match mode { match mode {
Mode::TwoD | Mode::ThreeD => window_drawable_size, Mode::TwoD | Mode::ThreeD => window_drawable_size,
Mode::VR => Point2DI32::new(window_drawable_size.x() / 2, window_drawable_size.y()), Mode::VR => Point2DI32::new(window_drawable_size.x() / 2, window_drawable_size.y()),
}; }
if use_subpixel_aa { initial_size.scale_xy(Point2DI32::new(3, 1)) } else { initial_size }
} }
struct Frame { struct Frame {

View File

@ -364,6 +364,9 @@ impl Device for GLDevice {
fn set_uniform(&self, uniform: &Self::Uniform, data: UniformData) { fn set_uniform(&self, uniform: &Self::Uniform, data: UniformData) {
unsafe { unsafe {
match data { match data {
UniformData::Int(value) => {
gl::Uniform1i(uniform.location, value); ck();
}
UniformData::Mat4(data) => { UniformData::Mat4(data) => {
assert_eq!(mem::size_of::<[F32x4; 4]>(), 4 * 4 * 4); assert_eq!(mem::size_of::<[F32x4; 4]>(), 4 * 4 * 4);
let data_ptr: *const F32x4 = data.as_ptr(); let data_ptr: *const F32x4 = data.as_ptr();

View File

@ -191,6 +191,7 @@ pub enum ShaderKind {
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum UniformData { pub enum UniformData {
Int(i32),
Mat4([F32x4; 4]), Mat4([F32x4; 4]),
Vec2(F32x4), Vec2(F32x4),
Vec4(F32x4), Vec4(F32x4),

View File

@ -128,6 +128,7 @@ pub struct RenderOptions {
pub transform: RenderTransform, pub transform: RenderTransform,
pub dilation: Point2DF32, pub dilation: Point2DF32,
pub barrel_distortion: Option<BarrelDistortionCoefficients>, pub barrel_distortion: Option<BarrelDistortionCoefficients>,
pub subpixel_aa_enabled: bool,
} }
impl RenderOptions { impl RenderOptions {
@ -136,6 +137,7 @@ impl RenderOptions {
transform: self.transform.prepare(bounds), transform: self.transform.prepare(bounds),
dilation: self.dilation, dilation: self.dilation,
barrel_distortion: self.barrel_distortion, barrel_distortion: self.barrel_distortion,
subpixel_aa_enabled: self.subpixel_aa_enabled,
} }
} }
} }
@ -205,6 +207,7 @@ pub struct PreparedRenderOptions {
pub transform: PreparedRenderTransform, pub transform: PreparedRenderTransform,
pub dilation: Point2DF32, pub dilation: Point2DF32,
pub barrel_distortion: Option<BarrelDistortionCoefficients>, pub barrel_distortion: Option<BarrelDistortionCoefficients>,
pub subpixel_aa_enabled: bool,
} }
impl PreparedRenderOptions { impl PreparedRenderOptions {
@ -222,3 +225,13 @@ pub enum PreparedRenderTransform {
Transform2D(Transform2DF32), Transform2D(Transform2DF32),
Perspective { perspective: Perspective, clip_polygon: Vec<Point2DF32>, quad: [Point3DF32; 4] } Perspective { perspective: Perspective, clip_polygon: Vec<Point2DF32>, quad: [Point3DF32; 4] }
} }
impl PreparedRenderTransform {
#[inline]
pub fn is_2d(&self) -> bool {
match *self {
PreparedRenderTransform::Transform2D(_) => true,
_ => false,
}
}
}

View File

@ -15,7 +15,7 @@ use crate::scene::ObjectShader;
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
use pathfinder_geometry::basic::point::{Point2DI32, Point3DF32}; use pathfinder_geometry::basic::point::{Point2DI32, Point3DF32};
use pathfinder_geometry::basic::rect::RectI32; use pathfinder_geometry::basic::rect::RectI32;
use pathfinder_geometry::color::{ColorF, ColorU}; use pathfinder_geometry::color::ColorF;
use pathfinder_gpu::resources::ResourceLoader; use pathfinder_gpu::resources::ResourceLoader;
use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, DepthFunc, DepthState, Device}; use pathfinder_gpu::{BlendState, BufferTarget, BufferUploadMode, DepthFunc, DepthState, Device};
use pathfinder_gpu::{Primitive, RenderState, StencilFunc, StencilState, TextureFormat}; use pathfinder_gpu::{Primitive, RenderState, StencilFunc, StencilState, TextureFormat};
@ -321,7 +321,7 @@ impl<D> Renderer<D> where D: Device {
self.device.bind_vertex_array(&alpha_tile_vertex_array.vertex_array); self.device.bind_vertex_array(&alpha_tile_vertex_array.vertex_array);
self.device.use_program(&alpha_tile_program.program); self.device.use_program(&alpha_tile_program.program);
self.device.set_uniform(&alpha_tile_program.framebuffer_size_uniform, self.device.set_uniform(&alpha_tile_program.framebuffer_size_uniform,
UniformData::Vec2(self.viewport.size().to_f32().0)); UniformData::Vec2(self.draw_viewport().size().to_f32().0));
self.device.set_uniform(&alpha_tile_program.tile_size_uniform, self.device.set_uniform(&alpha_tile_program.tile_size_uniform,
UniformData::Vec2(I32x4::new(TILE_WIDTH as i32, UniformData::Vec2(I32x4::new(TILE_WIDTH as i32,
TILE_HEIGHT as i32, TILE_HEIGHT as i32,
@ -349,9 +349,13 @@ impl<D> Renderer<D> where D: Device {
0, 0,
0).to_f32x4())); 0).to_f32x4()));
} }
RenderMode::Monochrome { fill_color, .. } => { RenderMode::Monochrome { .. } if self.postprocessing_needed() => {
self.device.set_uniform(&self.alpha_monochrome_tile_program.fill_color_uniform, self.device.set_uniform(&self.alpha_monochrome_tile_program.fill_color_uniform,
UniformData::Vec4(fill_color.0)); UniformData::Vec4(F32x4::splat(1.0)));
}
RenderMode::Monochrome { fg_color, .. } => {
self.device.set_uniform(&self.alpha_monochrome_tile_program.fill_color_uniform,
UniformData::Vec4(fg_color.0));
} }
} }
@ -370,13 +374,15 @@ impl<D> Renderer<D> where D: Device {
} }
fn draw_solid_tiles(&mut self, built_scene: &BuiltScene) { fn draw_solid_tiles(&mut self, built_scene: &BuiltScene) {
self.bind_draw_framebuffer();
let solid_tile_vertex_array = self.solid_tile_vertex_array(); let solid_tile_vertex_array = self.solid_tile_vertex_array();
let solid_tile_program = self.solid_tile_program(); let solid_tile_program = self.solid_tile_program();
self.device.bind_vertex_array(&solid_tile_vertex_array.vertex_array); self.device.bind_vertex_array(&solid_tile_vertex_array.vertex_array);
self.device.use_program(&solid_tile_program.program); self.device.use_program(&solid_tile_program.program);
self.device.set_uniform(&solid_tile_program.framebuffer_size_uniform, self.device.set_uniform(&solid_tile_program.framebuffer_size_uniform,
UniformData::Vec2(self.viewport.size().0.to_f32x4())); UniformData::Vec2(self.draw_viewport().size().0.to_f32x4()));
self.device.set_uniform(&solid_tile_program.tile_size_uniform, self.device.set_uniform(&solid_tile_program.tile_size_uniform,
UniformData::Vec2(I32x4::new(TILE_WIDTH as i32, UniformData::Vec2(I32x4::new(TILE_WIDTH as i32,
TILE_HEIGHT as i32, TILE_HEIGHT as i32,
@ -396,9 +402,13 @@ impl<D> Renderer<D> where D: Device {
0, 0,
0).to_f32x4())); 0).to_f32x4()));
} }
RenderMode::Monochrome { fill_color, .. } => { RenderMode::Monochrome { .. } if self.postprocessing_needed() => {
self.device.set_uniform(&self.solid_monochrome_tile_program.fill_color_uniform, self.device.set_uniform(&self.solid_monochrome_tile_program.fill_color_uniform,
UniformData::Vec4(fill_color.0)); UniformData::Vec4(F32x4::splat(1.0)));
}
RenderMode::Monochrome { fg_color, .. } => {
self.device.set_uniform(&self.solid_monochrome_tile_program.fill_color_uniform,
UniformData::Vec4(fg_color.0));
} }
} }
@ -414,17 +424,19 @@ impl<D> Renderer<D> where D: Device {
} }
fn postprocess(&mut self) { fn postprocess(&mut self) {
let (fill_color, defringing_kernel, gamma_correction_bg_color); let (fg_color, bg_color, defringing_kernel, gamma_correction_enabled);
match self.render_mode { match self.render_mode {
RenderMode::Multicolor => return, RenderMode::Multicolor => return,
RenderMode::Monochrome { RenderMode::Monochrome {
fill_color: fc, fg_color: fg,
defringing_kernel: dk, bg_color: bg,
gamma_correction_bg_color: gcbc, defringing_kernel: kernel,
gamma_correction,
} => { } => {
fill_color = fc; fg_color = fg;
defringing_kernel = dk; bg_color = bg;
gamma_correction_bg_color = gcbc; defringing_kernel = kernel;
gamma_correction_enabled = gamma_correction;
} }
} }
@ -456,22 +468,13 @@ impl<D> Renderer<D> where D: Device {
self.device.bind_texture(&self.gamma_lut_texture, 1); self.device.bind_texture(&self.gamma_lut_texture, 1);
self.device.set_uniform(&self.postprocess_program.gamma_lut_uniform, self.device.set_uniform(&self.postprocess_program.gamma_lut_uniform,
UniformData::TextureUnit(1)); UniformData::TextureUnit(1));
let gamma_correction_bg_color_uniform = &self.postprocess_program self.device.set_uniform(&self.postprocess_program.fg_color_uniform,
.gamma_correction_bg_color_uniform; UniformData::Vec4(fg_color.0));
match gamma_correction_bg_color { self.device.set_uniform(&self.postprocess_program.bg_color_uniform,
None => { UniformData::Vec4(bg_color.0));
self.device.set_uniform(gamma_correction_bg_color_uniform, self.device.set_uniform(&self.postprocess_program.gamma_correction_enabled_uniform,
UniformData::Vec4(F32x4::default())); UniformData::Int(gamma_correction_enabled as i32));
} self.device.draw_arrays(Primitive::TriangleFan, 4, &RenderState::default());
Some(color) => {
self.device.set_uniform(gamma_correction_bg_color_uniform,
UniformData::Vec4(color.to_f32().0));
}
}
self.device.draw_arrays(Primitive::TriangleFan, 4, &RenderState {
blend: BlendState::RGBOneAlphaOne,
..RenderState::default()
});
} }
fn solid_tile_program(&self) -> &SolidTileProgram<D> { fn solid_tile_program(&self) -> &SolidTileProgram<D> {
@ -543,12 +546,14 @@ impl<D> Renderer<D> where D: Device {
return; return;
} }
let source_framebuffer_size = self.draw_viewport().size();
match self.postprocess_source_framebuffer { match self.postprocess_source_framebuffer {
Some(ref framebuffer) if Some(ref framebuffer) if
self.device.texture_size(self.device.framebuffer_texture(framebuffer)) == self.device.texture_size(self.device.framebuffer_texture(framebuffer)) ==
self.viewport.size() => {} source_framebuffer_size => {}
_ => { _ => {
let texture = self.device.create_texture(TextureFormat::R8, self.viewport.size()); let texture = self.device.create_texture(TextureFormat::R8,
source_framebuffer_size);
self.postprocess_source_framebuffer = Some(self.device.create_framebuffer(texture)) self.postprocess_source_framebuffer = Some(self.device.create_framebuffer(texture))
} }
}; };
@ -559,8 +564,8 @@ impl<D> Renderer<D> where D: Device {
fn postprocessing_needed(&self) -> bool { fn postprocessing_needed(&self) -> bool {
match self.render_mode { match self.render_mode {
RenderMode::Monochrome { ref defringing_kernel, gamma_correction_bg_color, .. } => { RenderMode::Monochrome { ref defringing_kernel, gamma_correction, .. } => {
defringing_kernel.is_some() || gamma_correction_bg_color.is_some() defringing_kernel.is_some() || gamma_correction
} }
_ => false, _ => false,
} }
@ -573,6 +578,16 @@ impl<D> Renderer<D> where D: Device {
Some(StencilState { func: StencilFunc::Equal, reference: 1, mask: 1, write: false }) Some(StencilState { func: StencilFunc::Equal, reference: 1, mask: 1, write: false })
} }
fn draw_viewport(&self) -> RectI32 {
match self.render_mode {
RenderMode::Monochrome { defringing_kernel: Some(..), .. } => {
RectI32::new(Point2DI32::default(),
self.viewport.size().scale_xy(Point2DI32::new(3, 1)))
}
_ => self.viewport
}
}
} }
struct FillVertexArray<D> where D: Device { struct FillVertexArray<D> where D: Device {
@ -717,26 +732,26 @@ impl<D> SolidTileVertexArray<D> where D: Device {
device.use_program(&solid_tile_program.program); device.use_program(&solid_tile_program.program);
device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex); device.bind_buffer(quad_vertex_positions_buffer, BufferTarget::Vertex);
device.configure_float_vertex_attr(&tess_coord_attr, device.configure_float_vertex_attr(&tess_coord_attr,
2, 2,
VertexAttrType::U8, VertexAttrType::U8,
false, false,
0, 0,
0, 0,
0); 0);
device.bind_buffer(&vertex_buffer, BufferTarget::Vertex); device.bind_buffer(&vertex_buffer, BufferTarget::Vertex);
device.configure_float_vertex_attr(&tile_origin_attr, device.configure_float_vertex_attr(&tile_origin_attr,
2, 2,
VertexAttrType::I16, VertexAttrType::I16,
false, false,
SOLID_TILE_INSTANCE_SIZE, SOLID_TILE_INSTANCE_SIZE,
0, 0,
1); 1);
device.configure_int_vertex_attr(&object_attr, device.configure_int_vertex_attr(&object_attr,
1, 1,
VertexAttrType::I16, VertexAttrType::I16,
SOLID_TILE_INSTANCE_SIZE, SOLID_TILE_INSTANCE_SIZE,
4, 4,
1); 1);
SolidTileVertexArray { vertex_array, vertex_buffer } SolidTileVertexArray { vertex_array, vertex_buffer }
} }
@ -824,8 +839,6 @@ struct AlphaTileProgram<D> where D: Device {
tile_size_uniform: D::Uniform, tile_size_uniform: D::Uniform,
stencil_texture_uniform: D::Uniform, stencil_texture_uniform: D::Uniform,
stencil_texture_size_uniform: D::Uniform, stencil_texture_size_uniform: D::Uniform,
fill_colors_texture_uniform: D::Uniform,
fill_colors_texture_size_uniform: D::Uniform,
view_box_origin_uniform: D::Uniform, view_box_origin_uniform: D::Uniform,
} }
@ -839,9 +852,6 @@ impl<D> AlphaTileProgram<D> where D: Device {
let tile_size_uniform = device.get_uniform(&program, "TileSize"); let tile_size_uniform = device.get_uniform(&program, "TileSize");
let stencil_texture_uniform = device.get_uniform(&program, "StencilTexture"); let stencil_texture_uniform = device.get_uniform(&program, "StencilTexture");
let stencil_texture_size_uniform = device.get_uniform(&program, "StencilTextureSize"); let stencil_texture_size_uniform = device.get_uniform(&program, "StencilTextureSize");
let fill_colors_texture_uniform = device.get_uniform(&program, "FillColorsTexture");
let fill_colors_texture_size_uniform = device.get_uniform(&program,
"FillColorsTextureSize");
let view_box_origin_uniform = device.get_uniform(&program, "ViewBoxOrigin"); let view_box_origin_uniform = device.get_uniform(&program, "ViewBoxOrigin");
AlphaTileProgram { AlphaTileProgram {
program, program,
@ -849,8 +859,6 @@ impl<D> AlphaTileProgram<D> where D: Device {
tile_size_uniform, tile_size_uniform,
stencil_texture_uniform, stencil_texture_uniform,
stencil_texture_size_uniform, stencil_texture_size_uniform,
fill_colors_texture_uniform,
fill_colors_texture_size_uniform,
view_box_origin_uniform, view_box_origin_uniform,
} }
} }
@ -897,7 +905,9 @@ struct PostprocessProgram<D> where D: Device {
framebuffer_size_uniform: D::Uniform, framebuffer_size_uniform: D::Uniform,
kernel_uniform: D::Uniform, kernel_uniform: D::Uniform,
gamma_lut_uniform: D::Uniform, gamma_lut_uniform: D::Uniform,
gamma_correction_bg_color_uniform: D::Uniform, gamma_correction_enabled_uniform: D::Uniform,
fg_color_uniform: D::Uniform,
bg_color_uniform: D::Uniform,
} }
impl<D> PostprocessProgram<D> where D: Device { impl<D> PostprocessProgram<D> where D: Device {
@ -908,8 +918,10 @@ impl<D> PostprocessProgram<D> where D: Device {
let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize"); let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
let kernel_uniform = device.get_uniform(&program, "Kernel"); let kernel_uniform = device.get_uniform(&program, "Kernel");
let gamma_lut_uniform = device.get_uniform(&program, "GammaLUT"); let gamma_lut_uniform = device.get_uniform(&program, "GammaLUT");
let gamma_correction_bg_color_uniform = device.get_uniform(&program, let gamma_correction_enabled_uniform = device.get_uniform(&program,
"GammaCorrectionBGColor"); "GammaCorrectionEnabled");
let fg_color_uniform = device.get_uniform(&program, "FGColor");
let bg_color_uniform = device.get_uniform(&program, "BGColor");
PostprocessProgram { PostprocessProgram {
program, program,
source_uniform, source_uniform,
@ -917,7 +929,9 @@ impl<D> PostprocessProgram<D> where D: Device {
framebuffer_size_uniform, framebuffer_size_uniform,
kernel_uniform, kernel_uniform,
gamma_lut_uniform, gamma_lut_uniform,
gamma_correction_bg_color_uniform, gamma_correction_enabled_uniform,
fg_color_uniform,
bg_color_uniform,
} }
} }
} }
@ -990,9 +1004,10 @@ impl<D> StencilVertexArray<D> where D: Device {
pub enum RenderMode { pub enum RenderMode {
Multicolor, Multicolor,
Monochrome { Monochrome {
fill_color: ColorF, fg_color: ColorF,
bg_color: ColorF,
defringing_kernel: Option<DefringingKernel>, defringing_kernel: Option<DefringingKernel>,
gamma_correction_bg_color: Option<ColorU>, gamma_correction: bool,
}, },
} }

View File

@ -15,7 +15,9 @@ use crate::gpu_data::BuiltObject;
use crate::tiles::Tiler; use crate::tiles::Tiler;
use crate::z_buffer::ZBuffer; use crate::z_buffer::ZBuffer;
use hashbrown::HashMap; use hashbrown::HashMap;
use pathfinder_geometry::basic::point::Point2DF32;
use pathfinder_geometry::basic::rect::{RectF32, RectI32}; use pathfinder_geometry::basic::rect::{RectF32, RectI32};
use pathfinder_geometry::basic::transform2d::Transform2DF32;
use pathfinder_geometry::color::ColorU; use pathfinder_geometry::color::ColorU;
use pathfinder_geometry::outline::Outline; use pathfinder_geometry::outline::Outline;
use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
@ -74,7 +76,7 @@ impl Scene {
let outline = self.apply_render_options(&object.outline, &built_options); let outline = self.apply_render_options(&object.outline, &built_options);
let mut tiler = Tiler::new( let mut tiler = Tiler::new(
&outline, &outline,
self.view_box, self.effective_view_box(&built_options),
object_index as u16, object_index as u16,
ShaderId(object.paint.0), ShaderId(object.paint.0),
z_buffer, z_buffer,
@ -94,7 +96,7 @@ impl Scene {
let outline = self.apply_render_options(&object.outline, &built_options); let outline = self.apply_render_options(&object.outline, &built_options);
let mut tiler = Tiler::new( let mut tiler = Tiler::new(
&outline, &outline,
self.view_box, self.effective_view_box(&built_options),
object_index as u16, object_index as u16,
ShaderId(object.paint.0), ShaderId(object.paint.0),
z_buffer, z_buffer,
@ -107,6 +109,8 @@ impl Scene {
fn apply_render_options(&self, original_outline: &Outline, options: &PreparedRenderOptions) fn apply_render_options(&self, original_outline: &Outline, options: &PreparedRenderOptions)
-> Outline { -> Outline {
let effective_view_box = self.effective_view_box(options);
let mut outline; let mut outline;
match options.transform { match options.transform {
PreparedRenderTransform::Perspective { ref perspective, ref clip_polygon, .. } => { PreparedRenderTransform::Perspective { ref perspective, ref clip_polygon, .. } => {
@ -121,17 +125,26 @@ impl Scene {
if let Some(barrel_distortion) = options.barrel_distortion { if let Some(barrel_distortion) = options.barrel_distortion {
outline.barrel_distort(barrel_distortion, perspective.window_size); outline.barrel_distort(barrel_distortion, perspective.window_size);
} }
// TODO(pcwalton): Support subpixel AA in 3D.
} }
} }
PreparedRenderTransform::Transform2D(ref transform) => { _ => {
// TODO(pcwalton): Short circuit. // TODO(pcwalton): Short circuit.
outline = (*original_outline).clone(); outline = (*original_outline).clone();
outline.transform(transform); if options.transform.is_2d() || options.subpixel_aa_enabled {
outline.clip_against_rect(self.view_box); let mut transform = match options.transform {
} PreparedRenderTransform::Transform2D(transform) => transform,
PreparedRenderTransform::None => { PreparedRenderTransform::None => Transform2DF32::default(),
outline = (*original_outline).clone(); PreparedRenderTransform::Perspective { .. } => unreachable!(),
outline.clip_against_rect(self.view_box); };
if options.subpixel_aa_enabled {
transform = transform.post_mul(&Transform2DF32::from_scale(
&Point2DF32::new(3.0, 1.0)))
}
outline.transform(&transform);
}
outline.clip_against_rect(effective_view_box);
} }
} }
@ -141,7 +154,7 @@ impl Scene {
// TODO(pcwalton): Fold this into previous passes to avoid unnecessary clones during // TODO(pcwalton): Fold this into previous passes to avoid unnecessary clones during
// monotonic conversion. // monotonic conversion.
outline.prepare_for_tiling(self.view_box); outline.prepare_for_tiling(self.effective_view_box(options));
outline outline
} }
@ -155,6 +168,15 @@ impl Scene {
} }
Some(self.paints[first_paint_id.0 as usize].color) Some(self.paints[first_paint_id.0 as usize].color)
} }
#[inline]
pub fn effective_view_box(&self, render_options: &PreparedRenderOptions) -> RectF32 {
if render_options.subpixel_aa_enabled {
self.view_box.scale_xy(Point2DF32::new(3.0, 1.0))
} else {
self.view_box
}
}
} }
impl Debug for Scene { impl Debug for Scene {

View File

@ -17,6 +17,9 @@ precision highp float;
uniform sampler2D uSource; uniform sampler2D uSource;
uniform vec2 uSourceSize; uniform vec2 uSourceSize;
uniform vec4 uFGColor;
uniform vec4 uBGColor;
uniform int uGammaCorrectionEnabled;
in vec2 vTexCoord; in vec2 vTexCoord;
@ -32,9 +35,9 @@ float sample1Tap(float offset) {
void main() { void main() {
// Apply defringing if necessary. // Apply defringing if necessary.
vec4 fgColor; vec3 alpha;
if (uKernel.w == 0.0) { if (uKernel.w == 0.0) {
fgColor = texture(uSource, vTexCoord); alpha = texture(uSource, vTexCoord).rrr;
} else { } else {
vec4 alphaLeft, alphaRight; vec4 alphaLeft, alphaRight;
float alphaCenter; float alphaCenter;
@ -44,13 +47,13 @@ void main() {
float g = convolve7Tap(vec4(alphaLeft.yzw, alphaCenter), alphaRight.xyz); float g = convolve7Tap(vec4(alphaLeft.yzw, alphaCenter), alphaRight.xyz);
float b = convolve7Tap(vec4(alphaLeft.zw, alphaCenter, alphaRight.x), alphaRight.yzw); float b = convolve7Tap(vec4(alphaLeft.zw, alphaCenter, alphaRight.x), alphaRight.yzw);
fgColor = vec4(r); alpha = vec3(r, g, b);
} }
// Apply gamma correction if necessary. // Apply gamma correction if necessary.
/*if (uGammaCorrectionBGColor.a > 0.0) if (uGammaCorrectionEnabled != 0)
fgColor.rgb = gammaCorrect(fgColor.rgb);*/ alpha = gammaCorrect(uBGColor.rgb, alpha);
// Finish. // Finish.
oFragColor = fgColor; oFragColor = vec4(mix(uBGColor.rgb, uFGColor.rgb, alpha), 1.0);
} }

View File

@ -12,17 +12,13 @@
// expects. // expects.
uniform sampler2D uGammaLUT; uniform sampler2D uGammaLUT;
// The background color to blend against. Zero if no gamma correction is to be float gammaCorrectChannel(float bgColor, float fgColor) {
// performed. return texture(uGammaLUT, vec2(fgColor, 1.0 - bgColor)).r;
uniform vec4 uGammaCorrectionBGColor;
float gammaCorrectChannel(float fgColor) {
return texture(uGammaLUT, vec2(fgColor, 1.0 - uGammaCorrectionBGColor)).r;
} }
// `fgColor` is in linear space. // `fgColor` is in linear space.
vec3 gammaCorrect(vec3 fgColor) { vec3 gammaCorrect(vec3 bgColor, vec3 fgColor) {
return vec3(gammaCorrectChannel(fgColor.r), return vec3(gammaCorrectChannel(bgColor.r, fgColor.r),
gammaCorrectChannel(fgColor.g), gammaCorrectChannel(bgColor.g, fgColor.g),
gammaCorrectChannel(fgColor.b)); gammaCorrectChannel(bgColor.b, fgColor.b));
} }