Add a defringing shader for subpixel antialiasing, untested as of yet
This commit is contained in:
parent
a5234e6695
commit
f25682f0f1
|
@ -0,0 +1,59 @@
|
||||||
|
#version 330
|
||||||
|
|
||||||
|
// pathfinder/demo3/shaders/defringe.fs.glsl
|
||||||
|
//
|
||||||
|
// Copyright © 2019 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
// TODO(pcwalton): This could be significantly optimized by operating on a
|
||||||
|
// sparse per-tile basis.
|
||||||
|
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
uniform sampler2D uSource;
|
||||||
|
uniform vec2 uFramebufferSize;
|
||||||
|
uniform vec4 uKernel;
|
||||||
|
|
||||||
|
in vec2 vTexCoord;
|
||||||
|
|
||||||
|
out vec4 oFragColor;
|
||||||
|
|
||||||
|
float sample1Tap(float offset) {
|
||||||
|
return texture(uSource, vec2(vTexCoord.x + offset, vTexCoord.y)).r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sample9Tap(out vec4 outAlphaLeft,
|
||||||
|
out float outAlphaCenter,
|
||||||
|
out vec4 outAlphaRight,
|
||||||
|
float onePixel) {
|
||||||
|
outAlphaLeft = vec4(uKernel.x > 0.0 ? sample1Tap(-4.0 * onePixel) : 0.0,
|
||||||
|
sample1Tap(-3.0 * onePixel),
|
||||||
|
sample1Tap(-2.0 * onePixel),
|
||||||
|
sample1Tap(-1.0 * onePixel));
|
||||||
|
outAlphaCenter = sample1Tap(0.0);
|
||||||
|
outAlphaRight = vec4(sample1Tap(1.0 * onePixel),
|
||||||
|
sample1Tap(2.0 * onePixel),
|
||||||
|
sample1Tap(3.0 * onePixel),
|
||||||
|
uKernel.x > 0.0 ? sample1Tap(4.0 * onePixel) : 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
float convolve7Tap(vec4 alpha0, vec3 alpha1) {
|
||||||
|
return dot(alpha0, uKernel) + dot(alpha1, uKernel.zyx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 alphaLeft, alphaRight;
|
||||||
|
float alphaCenter;
|
||||||
|
sample9Tap(alphaLeft, alphaCenter, alphaRight, 1.0 / uFramebufferSize.x);
|
||||||
|
|
||||||
|
vec3 alpha = vec3(convolve7Tap(alphaLeft, vec3(alphaCenter, alphaRight.xy)),
|
||||||
|
convolve7Tap(vec4(alphaLeft.yzw, alphaCenter), alphaRight.xyz),
|
||||||
|
convolve7Tap(vec4(alphaLeft.zw, alphaCenter, alphaRight.x), alphaRight.yzw));
|
||||||
|
|
||||||
|
oFragColor = vec4(alpha, 1.0);
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
#version 330
|
||||||
|
|
||||||
|
// pathfinder/demo3/shaders/defringe.vs.glsl
|
||||||
|
//
|
||||||
|
// Copyright © 2019 The Pathfinder Project Developers.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
precision highp float;
|
||||||
|
|
||||||
|
uniform vec2 uFramebufferSize;
|
||||||
|
|
||||||
|
in vec2 aPosition;
|
||||||
|
|
||||||
|
out vec2 vTexCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vTexCoord = aPosition;
|
||||||
|
gl_Position = vec4(aPosition / uFramebufferSize * 2.0 - 1.0, 0.0, 1.0);
|
||||||
|
}
|
|
@ -343,11 +343,11 @@ trait ContourClipper where Self::Edge: TEdge {
|
||||||
if let Some(last_position) = contour.last_position() {
|
if let Some(last_position) = contour.last_position() {
|
||||||
if last_position != segment.baseline.from() {
|
if last_position != segment.baseline.from() {
|
||||||
// Add a line to join up segments.
|
// Add a line to join up segments.
|
||||||
contour.push_point(segment.baseline.from(), PointFlags::empty());
|
contour.push_point(segment.baseline.from(), PointFlags::empty(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contour.push_segment(*segment);
|
contour.push_segment(*segment, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_for_fast_clip(&mut self, edge: &Self::Edge) -> FastClipResult {
|
fn check_for_fast_clip(&mut self, edge: &Self::Edge) -> FastClipResult {
|
||||||
|
|
|
@ -17,6 +17,7 @@ use crate::point::Point2DF32;
|
||||||
use crate::segment::{Segment, SegmentFlags, SegmentKind};
|
use crate::segment::{Segment, SegmentFlags, SegmentKind};
|
||||||
use crate::transform3d::Perspective;
|
use crate::transform3d::Perspective;
|
||||||
use crate::transform::Transform2DF32;
|
use crate::transform::Transform2DF32;
|
||||||
|
use crate::util;
|
||||||
use euclid::{Point2D, Rect, Size2D};
|
use euclid::{Point2D, Rect, Size2D};
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -66,7 +67,7 @@ impl Outline {
|
||||||
.contours
|
.contours
|
||||||
.push(mem::replace(&mut current_contour, Contour::new()));
|
.push(mem::replace(&mut current_contour, Contour::new()));
|
||||||
}
|
}
|
||||||
current_contour.push_point(segment.baseline.from(), PointFlags::empty());
|
current_contour.push_point(segment.baseline.from(), PointFlags::empty(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if segment.flags.contains(SegmentFlags::CLOSES_SUBPATH) {
|
if segment.flags.contains(SegmentFlags::CLOSES_SUBPATH) {
|
||||||
|
@ -83,13 +84,15 @@ impl Outline {
|
||||||
}
|
}
|
||||||
|
|
||||||
if !segment.is_line() {
|
if !segment.is_line() {
|
||||||
current_contour.push_point(segment.ctrl.from(), PointFlags::CONTROL_POINT_0);
|
current_contour.push_point(segment.ctrl.from(), PointFlags::CONTROL_POINT_0, true);
|
||||||
if !segment.is_quadratic() {
|
if !segment.is_quadratic() {
|
||||||
current_contour.push_point(segment.ctrl.to(), PointFlags::CONTROL_POINT_1);
|
current_contour.push_point(segment.ctrl.to(),
|
||||||
|
PointFlags::CONTROL_POINT_1,
|
||||||
|
true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
current_contour.push_point(segment.baseline.to(), PointFlags::empty());
|
current_contour.push_point(segment.baseline.to(), PointFlags::empty(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !current_contour.is_empty() {
|
if !current_contour.is_empty() {
|
||||||
|
@ -109,12 +112,6 @@ impl Outline {
|
||||||
&self.bounds
|
&self.bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn make_monotonic(&mut self) {
|
|
||||||
self.contours.iter_mut().for_each(|contour| contour.make_monotonic());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn transform(&mut self, transform: &Transform2DF32) {
|
pub fn transform(&mut self, transform: &Transform2DF32) {
|
||||||
let mut new_bounds = None;
|
let mut new_bounds = None;
|
||||||
for contour in &mut self.contours {
|
for contour in &mut self.contours {
|
||||||
|
@ -124,7 +121,6 @@ impl Outline {
|
||||||
self.bounds = new_bounds.unwrap_or_else(|| Rect::zero());
|
self.bounds = new_bounds.unwrap_or_else(|| Rect::zero());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn apply_perspective(&mut self, perspective: &Perspective) {
|
pub fn apply_perspective(&mut self, perspective: &Perspective) {
|
||||||
let mut new_bounds = None;
|
let mut new_bounds = None;
|
||||||
for contour in &mut self.contours {
|
for contour in &mut self.contours {
|
||||||
|
@ -134,7 +130,13 @@ impl Outline {
|
||||||
self.bounds = new_bounds.unwrap_or_else(|| Rect::zero());
|
self.bounds = new_bounds.unwrap_or_else(|| Rect::zero());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
pub fn prepare_for_tiling(&mut self, view_box: &Rect<f32>) {
|
||||||
|
for contour in &mut self.contours {
|
||||||
|
contour.prepare_for_tiling(view_box);
|
||||||
|
}
|
||||||
|
self.bounds = self.bounds.intersection(view_box).unwrap_or_else(|| Rect::zero());
|
||||||
|
}
|
||||||
|
|
||||||
pub fn clip_against_polygon(&mut self, clip_polygon: &[Point2DF32]) {
|
pub fn clip_against_polygon(&mut self, clip_polygon: &[Point2DF32]) {
|
||||||
let mut new_bounds = None;
|
let mut new_bounds = None;
|
||||||
for contour in mem::replace(&mut self.contours, vec![]) {
|
for contour in mem::replace(&mut self.contours, vec![]) {
|
||||||
|
@ -148,6 +150,10 @@ impl Outline {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clip_against_rect(&mut self, clip_rect: &Rect<f32>) {
|
pub fn clip_against_rect(&mut self, clip_rect: &Rect<f32>) {
|
||||||
|
if clip_rect.contains_rect(&self.bounds) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let mut new_bounds = None;
|
let mut new_bounds = None;
|
||||||
for contour in mem::replace(&mut self.contours, vec![]) {
|
for contour in mem::replace(&mut self.contours, vec![]) {
|
||||||
let contour = ContourRectClipper::new(clip_rect, contour).clip();
|
let contour = ContourRectClipper::new(clip_rect, contour).clip();
|
||||||
|
@ -221,31 +227,36 @@ impl Contour {
|
||||||
|
|
||||||
// TODO(pcwalton): SIMD.
|
// TODO(pcwalton): SIMD.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn push_point(&mut self, point: Point2DF32, flags: PointFlags) {
|
pub(crate) fn push_point(&mut self,
|
||||||
|
point: Point2DF32,
|
||||||
|
flags: PointFlags,
|
||||||
|
update_bounds: bool) {
|
||||||
|
if update_bounds {
|
||||||
let first = self.is_empty();
|
let first = self.is_empty();
|
||||||
union_rect(&mut self.bounds, point, first);
|
union_rect(&mut self.bounds, point, first);
|
||||||
|
}
|
||||||
|
|
||||||
self.points.push(point);
|
self.points.push(point);
|
||||||
self.flags.push(flags);
|
self.flags.push(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn push_segment(&mut self, segment: Segment) {
|
pub(crate) fn push_segment(&mut self, segment: Segment, update_bounds: bool) {
|
||||||
if segment.is_none() {
|
if segment.is_none() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.is_empty() {
|
if self.is_empty() {
|
||||||
self.push_point(segment.baseline.from(), PointFlags::empty());
|
self.push_point(segment.baseline.from(), PointFlags::empty(), update_bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !segment.is_line() {
|
if !segment.is_line() {
|
||||||
self.push_point(segment.ctrl.from(), PointFlags::CONTROL_POINT_0);
|
self.push_point(segment.ctrl.from(), PointFlags::CONTROL_POINT_0, update_bounds);
|
||||||
if !segment.is_quadratic() {
|
if !segment.is_quadratic() {
|
||||||
self.push_point(segment.ctrl.to(), PointFlags::CONTROL_POINT_1);
|
self.push_point(segment.ctrl.to(), PointFlags::CONTROL_POINT_1, update_bounds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.push_point(segment.baseline.to(), PointFlags::empty());
|
self.push_point(segment.baseline.to(), PointFlags::empty(), update_bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -338,7 +349,6 @@ impl Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn transform(&mut self, transform: &Transform2DF32) {
|
pub fn transform(&mut self, transform: &Transform2DF32) {
|
||||||
for (point_index, point) in self.points.iter_mut().enumerate() {
|
for (point_index, point) in self.points.iter_mut().enumerate() {
|
||||||
*point = transform.transform_point(point);
|
*point = transform.transform_point(point);
|
||||||
|
@ -346,7 +356,6 @@ impl Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn apply_perspective(&mut self, perspective: &Perspective) {
|
pub fn apply_perspective(&mut self, perspective: &Perspective) {
|
||||||
for (point_index, point) in self.points.iter_mut().enumerate() {
|
for (point_index, point) in self.points.iter_mut().enumerate() {
|
||||||
*point = perspective.transform_point_2d(point);
|
*point = perspective.transform_point_2d(point);
|
||||||
|
@ -354,20 +363,82 @@ impl Contour {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
fn prepare_for_tiling(&mut self, view_box: &Rect<f32>) {
|
||||||
pub fn make_monotonic(&mut self) {
|
// Snap points to the view box bounds. This mops up floating point error from the clipping
|
||||||
// Fast path.
|
// process.
|
||||||
if self.iter().all(|segment| segment.is_monotonic()) {
|
let origin_upper_left = Point2DF32::from_euclid(view_box.origin);
|
||||||
return;
|
let origin_lower_right = Point2DF32::from_euclid(view_box.bottom_right());
|
||||||
|
let (mut last_endpoint_index, mut contour_is_monotonic) = (None, true);
|
||||||
|
for point_index in 0..(self.points.len() as u32) {
|
||||||
|
let position = &mut self.points[point_index as usize];
|
||||||
|
*position = position.clamp(origin_upper_left, origin_lower_right);
|
||||||
|
|
||||||
|
if contour_is_monotonic {
|
||||||
|
if self.point_is_endpoint(point_index) {
|
||||||
|
if let Some(last_endpoint_index) = last_endpoint_index {
|
||||||
|
if !self.curve_with_endpoints_is_monotonic(last_endpoint_index,
|
||||||
|
point_index) {
|
||||||
|
contour_is_monotonic = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last_endpoint_index = Some(point_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Slow path.
|
// Convert to monotonic, if necessary.
|
||||||
|
if !contour_is_monotonic {
|
||||||
let contour = self.take();
|
let contour = self.take();
|
||||||
|
self.bounds = contour.bounds;
|
||||||
for segment in MonotonicConversionIter::new(contour.iter()) {
|
for segment in MonotonicConversionIter::new(contour.iter()) {
|
||||||
self.push_segment(segment);
|
self.push_segment(segment, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update bounds.
|
||||||
|
self.bounds = self.bounds.intersection(view_box).unwrap_or_else(|| Rect::zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn curve_with_endpoints_is_monotonic(&self, start_endpoint_index: u32, end_endpoint_index: u32)
|
||||||
|
-> bool {
|
||||||
|
let start_position = self.points[start_endpoint_index as usize];
|
||||||
|
let end_position = self.points[end_endpoint_index as usize];
|
||||||
|
|
||||||
|
if start_position.x() <= end_position.x() {
|
||||||
|
for point_index in start_endpoint_index..end_endpoint_index {
|
||||||
|
if self.points[point_index as usize].x() >
|
||||||
|
self.points[point_index as usize + 1].x() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for point_index in start_endpoint_index..end_endpoint_index {
|
||||||
|
if self.points[point_index as usize].x() <
|
||||||
|
self.points[point_index as usize + 1].x() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if start_position.y() <= end_position.y() {
|
||||||
|
for point_index in start_endpoint_index..end_endpoint_index {
|
||||||
|
if self.points[point_index as usize].y() >
|
||||||
|
self.points[point_index as usize + 1].y() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for point_index in start_endpoint_index..end_endpoint_index {
|
||||||
|
if self.points[point_index as usize].y() <
|
||||||
|
self.points[point_index as usize + 1].y() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn update_bounds(&self, bounds: &mut Option<Rect<f32>>) {
|
fn update_bounds(&self, bounds: &mut Option<Rect<f32>>) {
|
||||||
*bounds = Some(match *bounds {
|
*bounds = Some(match *bounds {
|
||||||
None => self.bounds,
|
None => self.bounds,
|
||||||
|
|
|
@ -76,6 +76,11 @@ impl Point2DF32 {
|
||||||
Point2DF32(self.0.max(other.0))
|
Point2DF32(self.0.max(other.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn clamp(&self, min_val: Point2DF32, max_val: Point2DF32) -> Point2DF32 {
|
||||||
|
self.max(min_val).min(max_val)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn det(&self, other: Point2DF32) -> f32 {
|
pub fn det(&self, other: Point2DF32) -> f32 {
|
||||||
self.x() * other.y() - self.y() * other.x()
|
self.x() * other.y() - self.y() * other.x()
|
||||||
|
|
|
@ -10,12 +10,20 @@
|
||||||
|
|
||||||
//! Various utilities.
|
//! Various utilities.
|
||||||
|
|
||||||
|
use std::f32;
|
||||||
|
|
||||||
/// Linear interpolation.
|
/// Linear interpolation.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
|
pub fn lerp(a: f32, b: f32, t: f32) -> f32 {
|
||||||
a + (b - a) * t
|
a + (b - a) * t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clamping.
|
||||||
|
#[inline]
|
||||||
|
pub fn clamp(x: f32, min_val: f32, max_val: f32) -> f32 {
|
||||||
|
f32::min(max_val, f32::max(min_val, x))
|
||||||
|
}
|
||||||
|
|
||||||
/// Divides `a` by `b`, rounding up.
|
/// Divides `a` by `b`, rounding up.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn alignup_i32(a: i32, b: i32) -> i32 {
|
pub fn alignup_i32(a: i32, b: i32) -> i32 {
|
||||||
|
|
|
@ -33,6 +33,7 @@ const FILL_COLORS_TEXTURE_WIDTH: u32 = 256;
|
||||||
const FILL_COLORS_TEXTURE_HEIGHT: u32 = 256;
|
const FILL_COLORS_TEXTURE_HEIGHT: u32 = 256;
|
||||||
|
|
||||||
pub struct Renderer {
|
pub struct Renderer {
|
||||||
|
// Core shaders
|
||||||
fill_program: FillProgram,
|
fill_program: FillProgram,
|
||||||
solid_tile_program: SolidTileProgram,
|
solid_tile_program: SolidTileProgram,
|
||||||
mask_tile_program: MaskTileProgram,
|
mask_tile_program: MaskTileProgram,
|
||||||
|
@ -45,11 +46,16 @@ pub struct Renderer {
|
||||||
mask_framebuffer: Framebuffer,
|
mask_framebuffer: Framebuffer,
|
||||||
fill_colors_texture: Texture,
|
fill_colors_texture: Texture,
|
||||||
|
|
||||||
|
// Postprocessing shaders
|
||||||
|
defringe_program: DefringeProgram,
|
||||||
|
defringe_vertex_array: DefringeVertexArray,
|
||||||
|
|
||||||
|
// Debug
|
||||||
pending_timer_queries: VecDeque<TimerQuery>,
|
pending_timer_queries: VecDeque<TimerQuery>,
|
||||||
free_timer_queries: Vec<TimerQuery>,
|
free_timer_queries: Vec<TimerQuery>,
|
||||||
|
|
||||||
pub debug_renderer: DebugRenderer,
|
pub debug_renderer: DebugRenderer,
|
||||||
|
|
||||||
|
// Extra info
|
||||||
main_framebuffer_size: Size2D<u32>,
|
main_framebuffer_size: Size2D<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,6 +65,8 @@ impl Renderer {
|
||||||
let solid_tile_program = SolidTileProgram::new();
|
let solid_tile_program = SolidTileProgram::new();
|
||||||
let mask_tile_program = MaskTileProgram::new();
|
let mask_tile_program = MaskTileProgram::new();
|
||||||
|
|
||||||
|
let defringe_program = DefringeProgram::new();
|
||||||
|
|
||||||
let area_lut_texture = Texture::from_png("area-lut");
|
let area_lut_texture = Texture::from_png("area-lut");
|
||||||
|
|
||||||
let quad_vertex_positions_buffer = Buffer::new();
|
let quad_vertex_positions_buffer = Buffer::new();
|
||||||
|
@ -72,6 +80,9 @@ impl Renderer {
|
||||||
let solid_tile_vertex_array = SolidTileVertexArray::new(&solid_tile_program,
|
let solid_tile_vertex_array = SolidTileVertexArray::new(&solid_tile_program,
|
||||||
&quad_vertex_positions_buffer);
|
&quad_vertex_positions_buffer);
|
||||||
|
|
||||||
|
let defringe_vertex_array = DefringeVertexArray::new(&defringe_program,
|
||||||
|
&quad_vertex_positions_buffer);
|
||||||
|
|
||||||
let mask_framebuffer = Framebuffer::new(&Size2D::new(MASK_FRAMEBUFFER_WIDTH,
|
let mask_framebuffer = Framebuffer::new(&Size2D::new(MASK_FRAMEBUFFER_WIDTH,
|
||||||
MASK_FRAMEBUFFER_HEIGHT));
|
MASK_FRAMEBUFFER_HEIGHT));
|
||||||
|
|
||||||
|
@ -92,6 +103,9 @@ impl Renderer {
|
||||||
mask_framebuffer,
|
mask_framebuffer,
|
||||||
fill_colors_texture,
|
fill_colors_texture,
|
||||||
|
|
||||||
|
defringe_program,
|
||||||
|
defringe_vertex_array,
|
||||||
|
|
||||||
pending_timer_queries: VecDeque::new(),
|
pending_timer_queries: VecDeque::new(),
|
||||||
free_timer_queries: vec![],
|
free_timer_queries: vec![],
|
||||||
|
|
||||||
|
@ -452,3 +466,51 @@ impl MaskTileProgram {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct DefringeProgram {
|
||||||
|
program: Program,
|
||||||
|
source_uniform: Uniform,
|
||||||
|
framebuffer_size_uniform: Uniform,
|
||||||
|
kernel_uniform: Uniform,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DefringeProgram {
|
||||||
|
fn new() -> DefringeProgram {
|
||||||
|
let program = Program::new("defringe");
|
||||||
|
let source_uniform = Uniform::new(&program, "Source");
|
||||||
|
let framebuffer_size_uniform = Uniform::new(&program, "FramebufferSize");
|
||||||
|
let kernel_uniform = Uniform::new(&program, "Kernel");
|
||||||
|
DefringeProgram { program, source_uniform, framebuffer_size_uniform, kernel_uniform }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DefringeVertexArray {
|
||||||
|
gl_vertex_array: GLuint,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DefringeVertexArray {
|
||||||
|
fn new(defringe_program: &DefringeProgram, quad_vertex_positions_buffer: &Buffer)
|
||||||
|
-> DefringeVertexArray {
|
||||||
|
let mut gl_vertex_array = 0;
|
||||||
|
unsafe {
|
||||||
|
let position_attr = VertexAttr::new(&defringe_program.program, "Position");
|
||||||
|
|
||||||
|
gl::GenVertexArrays(1, &mut gl_vertex_array);
|
||||||
|
gl::BindVertexArray(gl_vertex_array);
|
||||||
|
gl::UseProgram(defringe_program.program.gl_program);
|
||||||
|
gl::BindBuffer(gl::ARRAY_BUFFER, quad_vertex_positions_buffer.gl_buffer);
|
||||||
|
position_attr.configure_float(2, gl::UNSIGNED_BYTE, false, 0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
DefringeVertexArray { gl_vertex_array }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for DefringeVertexArray {
|
||||||
|
#[inline]
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
gl::DeleteVertexArrays(1, &mut self.gl_vertex_array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -116,14 +116,17 @@ impl Scene {
|
||||||
PreparedBuildTransform::Perspective(ref perspective, ref quad) => {
|
PreparedBuildTransform::Perspective(ref perspective, ref quad) => {
|
||||||
outline.clip_against_polygon(quad);
|
outline.clip_against_polygon(quad);
|
||||||
outline.apply_perspective(perspective);
|
outline.apply_perspective(perspective);
|
||||||
|
outline.prepare_for_tiling(&self.view_box);
|
||||||
}
|
}
|
||||||
PreparedBuildTransform::Transform2D(ref transform) => {
|
PreparedBuildTransform::Transform2D(ref transform) => {
|
||||||
outline.transform(transform);
|
outline.transform(transform);
|
||||||
}
|
|
||||||
PreparedBuildTransform::None => {}
|
|
||||||
}
|
|
||||||
outline.clip_against_rect(&self.view_box);
|
outline.clip_against_rect(&self.view_box);
|
||||||
outline.make_monotonic();
|
}
|
||||||
|
PreparedBuildTransform::None => {
|
||||||
|
outline.clip_against_rect(&self.view_box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
outline.prepare_for_tiling(&self.view_box);
|
||||||
outline
|
outline
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,10 +91,10 @@ impl<'o, 'z> Tiler<'o, 'z> {
|
||||||
// Add new active edges.
|
// Add new active edges.
|
||||||
let strip_max_y = ((i32::from(strip_origin_y) + 1) * TILE_HEIGHT as i32) as f32;
|
let strip_max_y = ((i32::from(strip_origin_y) + 1) * TILE_HEIGHT as i32) as f32;
|
||||||
while let Some(queued_endpoint) = self.point_queue.peek() {
|
while let Some(queued_endpoint) = self.point_queue.peek() {
|
||||||
// We're done when we see an endpoint that belongs to the next tile.
|
// We're done when we see an endpoint that belongs to the next tile strip.
|
||||||
//
|
//
|
||||||
// Note that this test must be `>`, not `>=`, in order to make sure we don't miss
|
// Note that this test must be `>`, not `>=`, in order to make sure we don't miss
|
||||||
// active edges that lie precisely on the tile boundary.
|
// active edges that lie precisely on the tile strip boundary.
|
||||||
if queued_endpoint.y > strip_max_y {
|
if queued_endpoint.y > strip_max_y {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue