Move radial gradients from the CPU to the GPU

This commit is contained in:
Patrick Walton 2020-03-26 20:01:17 -07:00
parent 0c24619dd0
commit 55df287fec
14 changed files with 558 additions and 318 deletions

1
Cargo.lock generated
View File

@ -295,6 +295,7 @@ dependencies = [
"pathfinder_gpu 0.1.0", "pathfinder_gpu 0.1.0",
"pathfinder_renderer 0.1.0", "pathfinder_renderer 0.1.0",
"pathfinder_resources 0.1.0", "pathfinder_resources 0.1.0",
"pathfinder_simd 0.4.0",
"sdl2 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "sdl2 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"sdl2-sys 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "sdl2-sys 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
] ]

View File

@ -98,7 +98,7 @@ impl Debug for ColorU {
} }
} }
#[derive(Clone, Copy, Default)] #[derive(Clone, Copy, PartialEq, Default)]
pub struct ColorF(pub F32x4); pub struct ColorF(pub F32x4);
impl ColorF { impl ColorF {

View File

@ -11,6 +11,9 @@
//! Special effects that can be applied to layers. //! Special effects that can be applied to layers.
use pathfinder_color::ColorF; use pathfinder_color::ColorF;
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::vector::Vector2F;
use pathfinder_simd::default::F32x2;
/// This intentionally does not precisely match what Core Graphics does (a /// This intentionally does not precisely match what Core Graphics does (a
/// Lanczos function), because we don't want any ringing artefacts. /// Lanczos function), because we don't want any ringing artefacts.
@ -29,18 +32,28 @@ pub const MAX_STEM_DARKENING_AMOUNT: [f32; 2] = [0.3, 0.3];
pub const MAX_STEM_DARKENING_PIXELS_PER_EM: f32 = 72.0; pub const MAX_STEM_DARKENING_PIXELS_PER_EM: f32 = 72.0;
/// Effects that can be applied to a layer. /// Effects that can be applied to a layer.
#[derive(Clone, Copy, Debug, Default)] #[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct Effects { pub struct Effects {
/// The shader that should be used when compositing this layer onto its destination. /// The shader that should be used when compositing this layer onto its destination.
pub filter: Filter, pub filter: Filter,
} }
/// The shader that should be used when compositing this layer onto its destination. /// The shader that should be used when compositing this layer onto its destination.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
pub enum Filter { pub enum Filter {
/// No special filter. /// No special filter.
None, None,
/// Converts a linear gradient to a radial one.
RadialGradient {
/// The line that the circles lie along.
line: LineSegment2F,
/// The radii of the circles at the two endpoints.
radii: F32x2,
/// The origin of the linearized gradient in the texture.
uv_origin: Vector2F,
},
/// Performs postprocessing operations useful for monochrome text. /// Performs postprocessing operations useful for monochrome text.
Text { Text {
/// The foreground color of the text. /// The foreground color of the text.

View File

@ -13,6 +13,7 @@ use crate::util;
use pathfinder_color::ColorU; use pathfinder_color::ColorU;
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::util as geometry_util; use pathfinder_geometry::util as geometry_util;
use pathfinder_simd::default::F32x2;
use std::cmp::{Ordering, PartialOrd}; use std::cmp::{Ordering, PartialOrd};
use std::convert; use std::convert;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
@ -20,20 +21,17 @@ use std::mem;
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub struct Gradient { pub struct Gradient {
pub geometry: GradientGeometry, /// The line this gradient runs along.
///
/// If this is a radial gradient, this is the line that connects the two circles. It may have
/// zero-length in the case of simple radial gradients.
pub line: LineSegment2F,
/// For radial gradients, the radii of the start and endpoints respectively. If this is a
/// linear gradient, this is `None`.
pub radii: Option<F32x2>,
stops: SortedVector<ColorStop>, stops: SortedVector<ColorStop>,
} }
#[derive(Clone, Copy, PartialEq, Debug)]
pub enum GradientGeometry {
Linear(LineSegment2F),
Radial {
line: LineSegment2F,
start_radius: f32,
end_radius: f32,
}
}
#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)] #[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
pub struct ColorStop { pub struct ColorStop {
pub offset: f32, pub offset: f32,
@ -44,19 +42,15 @@ impl Eq for Gradient {}
impl Hash for Gradient { impl Hash for Gradient {
fn hash<H>(&self, state: &mut H) where H: Hasher { fn hash<H>(&self, state: &mut H) where H: Hasher {
match self.geometry { util::hash_line_segment(self.line, state);
GradientGeometry::Linear(line) => { match self.radii {
(0).hash(state); None => (0).hash(state),
util::hash_line_segment(line, state); Some(radii) => {
}
GradientGeometry::Radial { line, start_radius, end_radius } => {
(1).hash(state); (1).hash(state);
util::hash_line_segment(line, state); util::hash_f32(radii.x(), state);
util::hash_f32(start_radius, state); util::hash_f32(radii.y(), state);
util::hash_f32(end_radius, state);
} }
} }
self.stops.hash(state); self.stops.hash(state);
} }
} }
@ -74,19 +68,14 @@ impl Hash for ColorStop {
} }
impl Gradient { impl Gradient {
#[inline]
pub fn new(geometry: GradientGeometry) -> Gradient {
Gradient { geometry, stops: SortedVector::new() }
}
#[inline] #[inline]
pub fn linear(line: LineSegment2F) -> Gradient { pub fn linear(line: LineSegment2F) -> Gradient {
Gradient::new(GradientGeometry::Linear(line)) Gradient { line, radii: None, stops: SortedVector::new() }
} }
#[inline] #[inline]
pub fn radial(line: LineSegment2F, start_radius: f32, end_radius: f32) -> Gradient { pub fn radial(line: LineSegment2F, radii: F32x2) -> Gradient {
Gradient::new(GradientGeometry::Radial { line, start_radius, end_radius }) Gradient { line, radii: Some(radii), stops: SortedVector::new() }
} }
#[inline] #[inline]
@ -95,13 +84,23 @@ impl Gradient {
} }
#[inline] #[inline]
pub fn geometry(&self) -> &GradientGeometry { pub fn line(&self) -> LineSegment2F {
&self.geometry self.line
} }
#[inline] #[inline]
pub fn geometry_mut(&mut self) -> &mut GradientGeometry { pub fn set_line(&mut self, line: LineSegment2F) {
&mut self.geometry self.line = line
}
#[inline]
pub fn radii(&self) -> Option<F32x2> {
self.radii
}
#[inline]
pub fn set_radii(&mut self, radii: Option<F32x2>) {
self.radii = radii
} }
#[inline] #[inline]

View File

@ -34,3 +34,6 @@ path = "../../renderer"
[dependencies.pathfinder_resources] [dependencies.pathfinder_resources]
path = "../../resources" path = "../../resources"
[dependencies.pathfinder_simd]
path = "../../simd"

View File

@ -29,6 +29,7 @@ use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions};
use pathfinder_renderer::gpu::renderer::Renderer; use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::BuildOptions; use pathfinder_renderer::options::BuildOptions;
use pathfinder_resources::fs::FilesystemResourceLoader; use pathfinder_resources::fs::FilesystemResourceLoader;
use pathfinder_simd::default::F32x2;
use sdl2::event::Event; use sdl2::event::Event;
use sdl2::keyboard::Keycode; use sdl2::keyboard::Keycode;
use sdl2::video::GLProfile; use sdl2::video::GLProfile;
@ -134,9 +135,9 @@ fn draw_eyes(canvas: &mut CanvasRenderingContext2D,
canvas.fill_path(path, FillRule::Winding); canvas.fill_path(path, FillRule::Winding);
let gloss_position = eyes_left_position - eyes_radii.scale_xy(Vector2F::new(0.25, 0.5)); let gloss_position = eyes_left_position - eyes_radii.scale_xy(Vector2F::new(0.25, 0.5));
let gloss_radii = F32x2::new(0.1, 0.75) * F32x2::splat(eyes_radii.x());
let mut gloss = Gradient::radial(LineSegment2F::new(gloss_position, gloss_position), let mut gloss = Gradient::radial(LineSegment2F::new(gloss_position, gloss_position),
eyes_radii.x() * 0.1, gloss_radii);
eyes_radii.x() * 0.75);
gloss.add_color_stop(ColorStop::new(ColorU::new(255, 255, 255, 128), 0.0)); gloss.add_color_stop(ColorStop::new(ColorU::new(255, 255, 255, 128), 0.0));
gloss.add_color_stop(ColorStop::new(ColorU::new(255, 255, 255, 0), 1.0)); gloss.add_color_stop(ColorStop::new(ColorU::new(255, 255, 255, 0), 1.0));
canvas.set_fill_style(FillStyle::Gradient(gloss)); canvas.set_fill_style(FillStyle::Gradient(gloss));
@ -146,8 +147,7 @@ fn draw_eyes(canvas: &mut CanvasRenderingContext2D,
let gloss_position = eyes_right_position - eyes_radii.scale_xy(Vector2F::new(0.25, 0.5)); let gloss_position = eyes_right_position - eyes_radii.scale_xy(Vector2F::new(0.25, 0.5));
let mut gloss = Gradient::radial(LineSegment2F::new(gloss_position, gloss_position), let mut gloss = Gradient::radial(LineSegment2F::new(gloss_position, gloss_position),
eyes_radii.x() * 0.1, gloss_radii);
eyes_radii.x() * 0.75);
gloss.add_color_stop(ColorStop::new(ColorU::new(255, 255, 255, 128), 0.0)); gloss.add_color_stop(ColorStop::new(ColorU::new(255, 255, 255, 128), 0.0));
gloss.add_color_stop(ColorStop::new(ColorU::new(255, 255, 255, 0), 1.0)); gloss.add_color_stop(ColorStop::new(ColorU::new(255, 255, 255, 0), 1.0));
canvas.set_fill_style(FillStyle::Gradient(gloss)); canvas.set_fill_style(FillStyle::Gradient(gloss));
@ -251,8 +251,7 @@ fn draw_graph(canvas: &mut CanvasRenderingContext2D, rect: RectF, time: f32) {
for &sample_point in &sample_points { for &sample_point in &sample_points {
let gradient_center = sample_point + Vector2F::new(0.0, 2.0); let gradient_center = sample_point + Vector2F::new(0.0, 2.0);
let mut background = Gradient::radial(LineSegment2F::new(gradient_center, gradient_center), let mut background = Gradient::radial(LineSegment2F::new(gradient_center, gradient_center),
3.0, F32x2::new(3.0, 8.0));
8.0);
background.add_color_stop(ColorStop::new(ColorU::new(0, 0, 0, 32), 0.0)); background.add_color_stop(ColorStop::new(ColorU::new(0, 0, 0, 32), 0.0));
background.add_color_stop(ColorStop::new(ColorU::transparent_black(), 1.0)); background.add_color_stop(ColorStop::new(ColorU::transparent_black(), 1.0));
canvas.set_fill_style(FillStyle::Gradient(background)); canvas.set_fill_style(FillStyle::Gradient(background));
@ -372,8 +371,7 @@ fn draw_color_wheel(canvas: &mut CanvasRenderingContext2D, rect: RectF, time: f3
// Fill the selection circle. // Fill the selection circle.
let mut gradient = Gradient::radial(LineSegment2F::new(selection_circle_center, let mut gradient = Gradient::radial(LineSegment2F::new(selection_circle_center,
selection_circle_center), selection_circle_center),
7.0, F32x2::new(7.0, 9.0));
9.0);
gradient.add_color_stop(ColorStop::new(ColorU::new(0, 0, 0, 64), 0.0)); gradient.add_color_stop(ColorStop::new(ColorU::new(0, 0, 0, 64), 0.0));
gradient.add_color_stop(ColorStop::new(ColorU::transparent_black(), 1.0)); gradient.add_color_stop(ColorStop::new(ColorU::transparent_black(), 1.0));
canvas.set_fill_style(FillStyle::Gradient(gradient)); canvas.set_fill_style(FillStyle::Gradient(gradient));

View File

@ -21,7 +21,7 @@ use crate::tile_map::DenseTileMap;
use crate::tiles::{self, DrawTilingPathInfo, PackedTile, TILE_HEIGHT, TILE_WIDTH}; use crate::tiles::{self, DrawTilingPathInfo, PackedTile, TILE_HEIGHT, TILE_WIDTH};
use crate::tiles::{Tiler, TilingPathInfo}; use crate::tiles::{Tiler, TilingPathInfo};
use crate::z_buffer::{DepthMetadata, ZBuffer}; use crate::z_buffer::{DepthMetadata, ZBuffer};
use pathfinder_content::effects::{BlendMode, Effects, Filter}; use pathfinder_content::effects::{BlendMode, Effects};
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_content::render_target::RenderTargetId; use pathfinder_content::render_target::RenderTargetId;
use pathfinder_geometry::line_segment::{LineSegment2F, LineSegmentU4, LineSegmentU8}; use pathfinder_geometry::line_segment::{LineSegment2F, LineSegmentU4, LineSegmentU8};
@ -55,6 +55,7 @@ pub(crate) struct ObjectBuilder {
struct BuiltDrawPath { struct BuiltDrawPath {
path: BuiltPath, path: BuiltPath,
blend_mode: BlendMode, blend_mode: BlendMode,
effects: Effects,
color_texture_page_0: TexturePageId, color_texture_page_0: TexturePageId,
color_texture_page_1: TexturePageId, color_texture_page_1: TexturePageId,
sampling_flags_0: TextureSamplingFlags, sampling_flags_0: TextureSamplingFlags,
@ -191,8 +192,9 @@ impl<'a> SceneBuilder<'a> {
let paint_id = path_object.paint(); let paint_id = path_object.paint();
let paint_metadata = &paint_metadata[paint_id.0 as usize]; let paint_metadata = &paint_metadata[paint_id.0 as usize];
let built_clip_path = let built_clip_path = path_object.clip_path().map(|clip_path_id| {
path_object.clip_path().map(|clip_path_id| &built_clip_paths[clip_path_id.0 as usize]); &built_clip_paths[clip_path_id.0 as usize]
});
let mut tiler = Tiler::new(self, let mut tiler = Tiler::new(self,
&outline, &outline,
@ -213,6 +215,7 @@ impl<'a> SceneBuilder<'a> {
BuiltDrawPath { BuiltDrawPath {
path: tiler.object_builder.built_path, path: tiler.object_builder.built_path,
blend_mode: path_object.blend_mode(), blend_mode: path_object.blend_mode(),
effects: paint_metadata.effects(),
color_texture_page_0: paint_metadata.location.page, color_texture_page_0: paint_metadata.location.page,
sampling_flags_0: paint_metadata.sampling_flags, sampling_flags_0: paint_metadata.sampling_flags,
color_texture_page_1: opacity_tile_page, color_texture_page_1: opacity_tile_page,
@ -322,6 +325,7 @@ impl<'a> SceneBuilder<'a> {
None, None,
None, None,
built_draw_path.blend_mode, built_draw_path.blend_mode,
built_draw_path.effects,
None, None,
None); None);
@ -332,6 +336,7 @@ impl<'a> SceneBuilder<'a> {
color_texture_0, color_texture_0,
color_texture_1, color_texture_1,
built_draw_path.blend_mode, built_draw_path.blend_mode,
built_draw_path.effects,
Some(built_draw_path.mask_0_fill_rule), Some(built_draw_path.mask_0_fill_rule),
None); None);
@ -343,6 +348,7 @@ impl<'a> SceneBuilder<'a> {
color_texture_0, color_texture_0,
color_texture_1, color_texture_1,
built_draw_path.blend_mode, built_draw_path.blend_mode,
built_draw_path.effects,
Some(built_draw_path.mask_0_fill_rule), Some(built_draw_path.mask_0_fill_rule),
Some(mask_1_fill_rule)); Some(mask_1_fill_rule));
} }
@ -356,6 +362,7 @@ impl<'a> SceneBuilder<'a> {
color_texture_0, color_texture_0,
color_texture_1, color_texture_1,
built_draw_path.blend_mode, built_draw_path.blend_mode,
built_draw_path.effects,
None, None,
built_draw_path.mask_1_fill_rule); built_draw_path.mask_1_fill_rule);
} }
@ -425,6 +432,7 @@ impl<'a> SceneBuilder<'a> {
color_texture_0: Option<TileBatchTexture>, color_texture_0: Option<TileBatchTexture>,
color_texture_1: Option<TileBatchTexture>, color_texture_1: Option<TileBatchTexture>,
blend_mode: BlendMode, blend_mode: BlendMode,
effects: Effects,
mask_0_fill_rule: Option<FillRule>, mask_0_fill_rule: Option<FillRule>,
mask_1_fill_rule: Option<FillRule>) { mask_1_fill_rule: Option<FillRule>) {
if alpha_tiles.is_empty() { if alpha_tiles.is_empty() {
@ -443,12 +451,13 @@ impl<'a> SceneBuilder<'a> {
color_texture_0: ref batch_color_texture_0, color_texture_0: ref batch_color_texture_0,
color_texture_1: ref batch_color_texture_1, color_texture_1: ref batch_color_texture_1,
blend_mode: batch_blend_mode, blend_mode: batch_blend_mode,
effects: Effects { filter: Filter::None }, effects: batch_effects,
mask_0_fill_rule: batch_mask_0_fill_rule, mask_0_fill_rule: batch_mask_0_fill_rule,
mask_1_fill_rule: batch_mask_1_fill_rule, mask_1_fill_rule: batch_mask_1_fill_rule,
})) if *batch_color_texture_0 == color_texture_0 && })) if *batch_color_texture_0 == color_texture_0 &&
*batch_color_texture_1 == color_texture_1 && *batch_color_texture_1 == color_texture_1 &&
batch_blend_mode == blend_mode && batch_blend_mode == blend_mode &&
batch_effects == effects &&
batch_mask_0_fill_rule == mask_0_fill_rule && batch_mask_0_fill_rule == mask_0_fill_rule &&
batch_mask_1_fill_rule == mask_1_fill_rule && batch_mask_1_fill_rule == mask_1_fill_rule &&
!batch_blend_mode.needs_readable_framebuffer() => {} !batch_blend_mode.needs_readable_framebuffer() => {}
@ -458,7 +467,7 @@ impl<'a> SceneBuilder<'a> {
color_texture_0, color_texture_0,
color_texture_1, color_texture_1,
blend_mode, blend_mode,
effects: Effects::default(), effects,
mask_0_fill_rule, mask_0_fill_rule,
mask_1_fill_rule, mask_1_fill_rule,
}; };

View File

@ -22,6 +22,7 @@ use pathfinder_color::{self as color, ColorF, ColorU};
use pathfinder_content::effects::{BlendMode, BlurDirection, DefringingKernel, Effects, Filter}; use pathfinder_content::effects::{BlendMode, BlurDirection, DefringingKernel, Effects, Filter};
use pathfinder_content::fill::FillRule; use pathfinder_content::fill::FillRule;
use pathfinder_content::render_target::RenderTargetId; use pathfinder_content::render_target::RenderTargetId;
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::RectI; use pathfinder_geometry::rect::RectI;
use pathfinder_geometry::transform3d::Transform4F; use pathfinder_geometry::transform3d::Transform4F;
use pathfinder_geometry::vector::{Vector2F, Vector2I, Vector4F}; use pathfinder_geometry::vector::{Vector2F, Vector2I, Vector4F};
@ -59,6 +60,7 @@ const COMBINER_CTRL_MASK_EVEN_ODD: i32 = 0x2;
const COMBINER_CTRL_COLOR_ENABLE_MASK: i32 = 0x1; const COMBINER_CTRL_COLOR_ENABLE_MASK: i32 = 0x1;
const COMBINER_CTRL_FILTER_RADIAL_GRADIENT: i32 = 0x1;
const COMBINER_CTRL_FILTER_TEXT: i32 = 0x2; const COMBINER_CTRL_FILTER_TEXT: i32 = 0x2;
const COMBINER_CTRL_FILTER_BLUR: i32 = 0x3; const COMBINER_CTRL_FILTER_BLUR: i32 = 0x3;
@ -611,13 +613,13 @@ where
UniformData::Mat4(self.tile_transform().to_columns())), UniformData::Mat4(self.tile_transform().to_columns())),
(&self.tile_program.tile_size_uniform, (&self.tile_program.tile_size_uniform,
UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))), UniformData::Vec2(F32x2::new(TILE_WIDTH as f32, TILE_HEIGHT as f32))),
(&self.tile_program.framebuffer_size_uniform,
UniformData::Vec2(draw_viewport.size().to_f32().0)),
]; ];
if needs_readable_framebuffer { if needs_readable_framebuffer {
uniforms.push((&self.tile_program.dest_texture_uniform, uniforms.push((&self.tile_program.dest_texture_uniform,
UniformData::TextureUnit(textures.len() as u32))); UniformData::TextureUnit(textures.len() as u32)));
uniforms.push((&self.tile_program.dest_texture_size_uniform,
UniformData::Vec2(draw_viewport.size().to_f32().0)));
textures.push(self.device.framebuffer_texture(&self.dest_blend_framebuffer)); textures.push(self.device.framebuffer_texture(&self.dest_blend_framebuffer));
} }
@ -659,6 +661,10 @@ where
match effects.filter { match effects.filter {
Filter::None => {} Filter::None => {}
Filter::RadialGradient { line, radii, uv_origin } => {
ctrl |= COMBINER_CTRL_FILTER_RADIAL_GRADIENT << COMBINER_CTRL_COLOR_0_FILTER_SHIFT;
self.set_uniforms_for_radial_gradient_filter(&mut uniforms, line, radii, uv_origin)
}
Filter::Text { fg_color, bg_color, defringing_kernel, gamma_correction } => { Filter::Text { fg_color, bg_color, defringing_kernel, gamma_correction } => {
ctrl |= COMBINER_CTRL_FILTER_TEXT << COMBINER_CTRL_COLOR_0_FILTER_SHIFT; ctrl |= COMBINER_CTRL_FILTER_TEXT << COMBINER_CTRL_COLOR_0_FILTER_SHIFT;
self.set_uniforms_for_text_filter(&mut textures, self.set_uniforms_for_text_filter(&mut textures,
@ -844,6 +850,20 @@ where
self.render_target_stack.pop().expect("Render target stack underflow!"); self.render_target_stack.pop().expect("Render target stack underflow!");
} }
fn set_uniforms_for_radial_gradient_filter<'a>(
&'a self,
uniforms: &mut Vec<(&'a D::Uniform, UniformData)>,
line: LineSegment2F,
radii: F32x2,
uv_origin: Vector2F) {
uniforms.extend_from_slice(&[
(&self.tile_program.filter_params_0_uniform,
UniformData::Vec4(line.from().0.concat_xy_xy(line.vector().0))),
(&self.tile_program.filter_params_1_uniform,
UniformData::Vec4(radii.concat_xy_xy(uv_origin.0))),
]);
}
fn set_uniforms_for_text_filter<'a>(&'a self, fn set_uniforms_for_text_filter<'a>(&'a self,
textures: &mut Vec<&'a D::Texture>, textures: &mut Vec<&'a D::Texture>,
uniforms: &mut Vec<(&'a D::Uniform, UniformData)>, uniforms: &mut Vec<(&'a D::Uniform, UniformData)>,

View File

@ -324,7 +324,7 @@ pub struct TileProgram<D> where D: Device {
pub filter_params_0_uniform: D::Uniform, pub filter_params_0_uniform: D::Uniform,
pub filter_params_1_uniform: D::Uniform, pub filter_params_1_uniform: D::Uniform,
pub filter_params_2_uniform: D::Uniform, pub filter_params_2_uniform: D::Uniform,
pub dest_texture_size_uniform: D::Uniform, pub framebuffer_size_uniform: D::Uniform,
pub ctrl_uniform: D::Uniform, pub ctrl_uniform: D::Uniform,
} }
@ -343,7 +343,7 @@ impl<D> TileProgram<D> where D: Device {
let filter_params_0_uniform = device.get_uniform(&program, "FilterParams0"); let filter_params_0_uniform = device.get_uniform(&program, "FilterParams0");
let filter_params_1_uniform = device.get_uniform(&program, "FilterParams1"); let filter_params_1_uniform = device.get_uniform(&program, "FilterParams1");
let filter_params_2_uniform = device.get_uniform(&program, "FilterParams2"); let filter_params_2_uniform = device.get_uniform(&program, "FilterParams2");
let dest_texture_size_uniform = device.get_uniform(&program, "DestTextureSize"); let framebuffer_size_uniform = device.get_uniform(&program, "FramebufferSize");
let ctrl_uniform = device.get_uniform(&program, "Ctrl"); let ctrl_uniform = device.get_uniform(&program, "Ctrl");
TileProgram { TileProgram {
program, program,
@ -359,7 +359,7 @@ impl<D> TileProgram<D> where D: Device {
filter_params_0_uniform, filter_params_0_uniform,
filter_params_1_uniform, filter_params_1_uniform,
filter_params_2_uniform, filter_params_2_uniform,
dest_texture_size_uniform, framebuffer_size_uniform,
ctrl_uniform, ctrl_uniform,
} }
} }

View File

@ -14,9 +14,11 @@ use crate::scene::RenderTarget;
use crate::tiles::{TILE_HEIGHT, TILE_WIDTH}; use crate::tiles::{TILE_HEIGHT, TILE_WIDTH};
use hashbrown::HashMap; use hashbrown::HashMap;
use pathfinder_color::ColorU; use pathfinder_color::ColorU;
use pathfinder_content::gradient::{Gradient, GradientGeometry}; use pathfinder_content::effects::{Effects, Filter};
use pathfinder_content::gradient::Gradient;
use pathfinder_content::pattern::{Image, Pattern, PatternFlags, PatternSource}; use pathfinder_content::pattern::{Image, Pattern, PatternFlags, PatternSource};
use pathfinder_content::render_target::RenderTargetId; use pathfinder_content::render_target::RenderTargetId;
use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::rect::{RectF, RectI}; use pathfinder_geometry::rect::{RectF, RectI};
use pathfinder_geometry::transform2d::{Matrix2x2F, Transform2F}; use pathfinder_geometry::transform2d::{Matrix2x2F, Transform2F};
use pathfinder_geometry::util; use pathfinder_geometry::util;
@ -123,26 +125,11 @@ impl Paint {
match *self { match *self {
Paint::Color(_) => {} Paint::Color(_) => {}
Paint::Gradient(ref mut gradient) => { Paint::Gradient(ref mut gradient) => {
match *gradient.geometry_mut() { gradient.set_line(*transform * gradient.line());
GradientGeometry::Linear(ref mut line) => { if let Some(radii) = gradient.radii() {
*line = *transform * *line; gradient.set_radii(Some(radii * F32x2::splat(util::lerp(transform.matrix.m11(),
} transform.matrix.m22(),
GradientGeometry::Radial { 0.5))));
ref mut line,
ref mut start_radius,
ref mut end_radius,
} => {
*line = *transform * *line;
// FIXME(pcwalton): This is wrong; I think the transform can make the
// radial gradient into an ellipse.
*start_radius *= util::lerp(transform.matrix.m11(),
transform.matrix.m22(),
0.5);
*end_radius *= util::lerp(transform.matrix.m11(),
transform.matrix.m22(),
0.5);
}
} }
} }
Paint::Pattern(ref mut pattern) => pattern.transform = *transform * pattern.transform, Paint::Pattern(ref mut pattern) => pattern.transform = *transform * pattern.transform,
@ -177,6 +164,16 @@ pub struct PaintMetadata {
pub sampling_flags: TextureSamplingFlags, pub sampling_flags: TextureSamplingFlags,
/// True if this paint is fully opaque. /// True if this paint is fully opaque.
pub is_opaque: bool, pub is_opaque: bool,
/// The radial gradient for this paint, if applicable.
pub radial_gradient: Option<RadialGradientMetadata>,
}
#[derive(Clone, Copy, Debug)]
pub struct RadialGradientMetadata {
/// The line segment that connects the two circles.
pub line: LineSegment2F,
/// The radii of the two circles.
pub radii: F32x2,
} }
#[derive(Debug)] #[derive(Debug)]
@ -204,7 +201,7 @@ impl Palette {
id id
} }
pub fn build_paint_info(&self, view_box_size: Vector2I) -> PaintInfo { pub fn build_paint_info(&self) -> PaintInfo {
let mut allocator = TextureAllocator::new(); let mut allocator = TextureAllocator::new();
let (mut paint_metadata, mut render_target_metadata) = (vec![], vec![]); let (mut paint_metadata, mut render_target_metadata) = (vec![], vec![]);
@ -220,26 +217,20 @@ impl Palette {
let mut solid_color_tile_builder = SolidColorTileBuilder::new(); let mut solid_color_tile_builder = SolidColorTileBuilder::new();
let mut gradient_tile_builder = GradientTileBuilder::new(); let mut gradient_tile_builder = GradientTileBuilder::new();
for paint in &self.paints { for paint in &self.paints {
let (texture_location, mut sampling_flags); let (texture_location, mut sampling_flags, radial_gradient);
match paint { match paint {
Paint::Color(_) => { Paint::Color(_) => {
texture_location = solid_color_tile_builder.allocate(&mut allocator); texture_location = solid_color_tile_builder.allocate(&mut allocator);
sampling_flags = TextureSamplingFlags::empty(); sampling_flags = TextureSamplingFlags::empty();
radial_gradient = None;
} }
Paint::Gradient(Gradient { geometry: GradientGeometry::Linear(_), .. }) => { Paint::Gradient(ref gradient) => {
// FIXME(pcwalton): The gradient size might not be big enough. Detect this. // FIXME(pcwalton): The gradient size might not be big enough. Detect this.
texture_location = gradient_tile_builder.allocate(&mut allocator); texture_location = gradient_tile_builder.allocate(&mut allocator);
sampling_flags = TextureSamplingFlags::empty(); sampling_flags = TextureSamplingFlags::empty();
} radial_gradient = gradient.radii().map(|radii| {
Paint::Gradient(Gradient { geometry: GradientGeometry::Radial { .. }, .. }) => { RadialGradientMetadata { line: gradient.line(), radii }
// TODO(pcwalton): Optimize this: });
// 1. Use repeating/clamp on the sides.
// 2. Choose an optimal size for the gradient that minimizes memory usage while
// retaining quality.
texture_location =
allocator.allocate(Vector2I::splat(GRADIENT_TILE_LENGTH as i32),
AllocationMode::Atlas);
sampling_flags = TextureSamplingFlags::empty();
} }
Paint::Pattern(ref pattern) => { Paint::Pattern(ref pattern) => {
match pattern.source { match pattern.source {
@ -271,6 +262,8 @@ impl Palette {
sampling_flags.insert(TextureSamplingFlags::NEAREST_MIN | sampling_flags.insert(TextureSamplingFlags::NEAREST_MIN |
TextureSamplingFlags::NEAREST_MAG); TextureSamplingFlags::NEAREST_MAG);
} }
radial_gradient = None;
} }
}; };
@ -279,6 +272,7 @@ impl Palette {
texture_transform: Transform2F::default(), texture_transform: Transform2F::default(),
sampling_flags, sampling_flags,
is_opaque: paint.is_opaque(), is_opaque: paint.is_opaque(),
radial_gradient,
}); });
} }
@ -290,10 +284,7 @@ impl Palette {
let vector = rect_to_inset_uv(metadata.location.rect, texture_scale).origin(); let vector = rect_to_inset_uv(metadata.location.rect, texture_scale).origin();
Transform2F { matrix: Matrix2x2F(F32x4::default()), vector } Transform2F { matrix: Matrix2x2F(F32x4::default()), vector }
} }
Paint::Gradient(Gradient { Paint::Gradient(Gradient { line: gradient_line, radii: None, .. }) => {
geometry: GradientGeometry::Linear(gradient_line),
..
}) => {
let v0 = metadata.location.rect.to_f32().center().y() * texture_scale.y(); let v0 = metadata.location.rect.to_f32().center().y() * texture_scale.y();
let length_inv = 1.0 / gradient_line.square_length(); let length_inv = 1.0 / gradient_line.square_length();
let (p0, d) = (gradient_line.from(), gradient_line.vector()); let (p0, d) = (gradient_line.from(), gradient_line.vector());
@ -302,12 +293,15 @@ impl Palette {
vector: Vector2F::new(-p0.dot(d) * length_inv, v0), vector: Vector2F::new(-p0.dot(d) * length_inv, v0),
} }
} }
Paint::Gradient(Gradient { geometry: GradientGeometry::Radial { .. }, .. }) => { Paint::Gradient(Gradient { radii: Some(_), .. }) => {
let texture_origin_uv = let texture_origin_uv =
rect_to_uv(metadata.location.rect, texture_scale).origin(); rect_to_inset_uv(metadata.location.rect, texture_scale).origin();
let gradient_tile_scale = texture_scale.scale(GRADIENT_TILE_LENGTH as f32); let gradient_tile_scale =
Transform2F::from_translation(texture_origin_uv) * texture_scale.scale((GRADIENT_TILE_LENGTH - 1) as f32);
Transform2F::from_scale(gradient_tile_scale / view_box_size.to_f32()) Transform2F {
matrix: Matrix2x2F::from_scale(gradient_tile_scale),
vector: texture_origin_uv,
}
} }
Paint::Pattern(Pattern { source: PatternSource::Image(_), transform, .. }) => { Paint::Pattern(Pattern { source: PatternSource::Image(_), transform, .. }) => {
let texture_origin_uv = let texture_origin_uv =
@ -359,10 +353,7 @@ impl Palette {
texels.put_texel(metadata.location.rect.origin(), *color); texels.put_texel(metadata.location.rect.origin(), *color);
} }
Paint::Gradient(ref gradient) => { Paint::Gradient(ref gradient) => {
self.render_gradient(gradient, self.render_gradient(gradient, metadata.location.rect, texels);
metadata.location.rect,
&metadata.texture_transform,
texels);
} }
Paint::Pattern(ref pattern) => { Paint::Pattern(ref pattern) => {
match pattern.source { match pattern.source {
@ -408,144 +399,15 @@ impl Palette {
} }
// TODO(pcwalton): This is slow. Do on GPU instead. // TODO(pcwalton): This is slow. Do on GPU instead.
fn render_gradient(&self, fn render_gradient(&self, gradient: &Gradient, tex_rect: RectI, texels: &mut Texels) {
gradient: &Gradient, // FIXME(pcwalton): Paint transparent if gradient line has zero size, per spec.
tex_rect: RectI, // TODO(pcwalton): Optimize this:
tex_transform: &Transform2F, // 1. Calculate ∇t up front and use differencing in the inner loop.
texels: &mut Texels) { // 2. Go four pixels at a time with SIMD.
match *gradient.geometry() { for x in 0..(GRADIENT_TILE_LENGTH as i32) {
GradientGeometry::Linear(_) => { let point = tex_rect.origin() + Vector2I::new(x, 0);
// FIXME(pcwalton): Paint transparent if gradient line has zero size, per spec. let t = (x as f32 + 0.5) / GRADIENT_TILE_LENGTH as f32;
// TODO(pcwalton): Optimize this: texels.put_texel(point, gradient.sample(t));
// 1. Calculate ∇t up front and use differencing in the inner loop.
// 2. Go four pixels at a time with SIMD.
for x in 0..(GRADIENT_TILE_LENGTH as i32) {
let point = tex_rect.origin() + Vector2I::new(x, 0);
let t = (x as f32 + 0.5) / GRADIENT_TILE_LENGTH as f32;
texels.put_texel(point, gradient.sample(t));
}
}
GradientGeometry::Radial { line, start_radius: r0, end_radius: r1 } => {
// FIXME(pcwalton): Paint transparent if line has zero size and radii are equal,
// per spec.
let line = *tex_transform * line;
// This is based on Pixman (MIT license). Copy and pasting the excellent comment
// from there:
// Implementation of radial gradients following the PDF specification.
// See section 8.7.4.5.4 Type 3 (Radial) Shadings of the PDF Reference
// Manual (PDF 32000-1:2008 at the time of this writing).
//
// In the radial gradient problem we are given two circles (c₁,r₁) and
// (c₂,r₂) that define the gradient itself.
//
// Mathematically the gradient can be defined as the family of circles
//
// ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂)
//
// excluding those circles whose radius would be < 0. When a point
// belongs to more than one circle, the one with a bigger t is the only
// one that contributes to its color. When a point does not belong
// to any of the circles, it is transparent black, i.e. RGBA (0, 0, 0, 0).
// Further limitations on the range of values for t are imposed when
// the gradient is not repeated, namely t must belong to [0,1].
//
// The graphical result is the same as drawing the valid (radius > 0)
// circles with increasing t in [-inf, +inf] (or in [0,1] if the gradient
// is not repeated) using SOURCE operator composition.
//
// It looks like a cone pointing towards the viewer if the ending circle
// is smaller than the starting one, a cone pointing inside the page if
// the starting circle is the smaller one and like a cylinder if they
// have the same radius.
//
// What we actually do is, given the point whose color we are interested
// in, compute the t values for that point, solving for t in:
//
// length((1-t)·c₁ + t·(c₂) - p) = (1-t)·r₁ + t·r₂
//
// Let's rewrite it in a simpler way, by defining some auxiliary
// variables:
//
// cd = c₂ - c₁
// pd = p - c₁
// dr = r₂ - r₁
// length(t·cd - pd) = r₁ + t·dr
//
// which actually means
//
// hypot(t·cdx - pdx, t·cdy - pdy) = r₁ + t·dr
//
// or
//
// ⎷((t·cdx - pdx)² + (t·cdy - pdy)²) = r₁ + t·dr.
//
// If we impose (as stated earlier) that r₁ + t·dr >= 0, it becomes:
//
// (t·cdx - pdx)² + (t·cdy - pdy)² = (r₁ + t·dr)²
//
// where we can actually expand the squares and solve for t:
//
// t²cdx² - 2t·cdx·pdx + pdx² + t²cdy² - 2t·cdy·pdy + pdy² =
// = r₁² + 2·r₁·t·dr + t²·dr²
//
// (cdx² + cdy² - dr²)t² - 2(cdx·pdx + cdy·pdy + r₁·dr)t +
// (pdx² + pdy² - r₁²) = 0
//
// A = cdx² + cdy² - dr²
// B = pdx·cdx + pdy·cdy + r₁·dr
// C = pdx² + pdy² - r₁²
// At² - 2Bt + C = 0
//
// The solutions (unless the equation degenerates because of A = 0) are:
//
// t = (B ± ⎷(B² - A·C)) / A
//
// The solution we are going to prefer is the bigger one, unless the
// radius associated to it is negative (or it falls outside the valid t
// range).
//
// Additional observations (useful for optimizations):
// A does not depend on p
//
// A < 0 <=> one of the two circles completely contains the other one
// <=> for every p, the radiuses associated with the two t solutions
// have opposite sign
let cd = line.vector();
let dr = r1 - r0;
let a = cd.square_length() - dr * dr;
let a_inv = 1.0 / a;
for y in 0..(GRADIENT_TILE_LENGTH as i32) {
for x in 0..(GRADIENT_TILE_LENGTH as i32) {
let point = tex_rect.origin() + Vector2I::new(x, y);
let point_f = point.to_f32();
let pd = point_f - line.from();
let b = pd.dot(cd) + r0 * dr;
let c = pd.square_length() - r0 * r0;
let discrim = b * b - a * c;
let mut color = ColorU::transparent_black();
if !util::approx_eq(discrim, 0.0) {
let discrim_sqrt = f32::sqrt(discrim);
let discrim_sqrts = F32x2::new(discrim_sqrt, -discrim_sqrt);
let ts = (discrim_sqrts + F32x2::splat(b)) * F32x2::splat(a_inv);
let t_min = f32::min(ts.x(), ts.y());
let t_max = f32::max(ts.x(), ts.y());
let t = if t_max <= 1.0 { t_max } else { t_min };
if t >= 0.0 {
color = gradient.sample(t);
}
};
texels.put_texel(point, color);
}
}
}
} }
} }
@ -568,6 +430,21 @@ impl PaintMetadata {
let tex_coords = self.texture_transform * position; let tex_coords = self.texture_transform * position;
tex_coords tex_coords
} }
pub(crate) fn effects(&self) -> Effects {
Effects {
filter: match self.radial_gradient {
None => Filter::None,
Some(gradient) => {
Filter::RadialGradient {
line: gradient.line,
radii: gradient.radii,
uv_origin: self.texture_transform.vector,
}
}
},
}
}
} }
struct Texels { struct Texels {

View File

@ -87,7 +87,7 @@ impl Scene {
#[inline] #[inline]
pub fn build_paint_info(&self) -> PaintInfo { pub fn build_paint_info(&self) -> PaintInfo {
self.palette.build_paint_info(self.view_box.size().to_i32()) self.palette.build_paint_info()
} }
#[allow(clippy::trivially_copy_pass_by_ref)] #[allow(clippy::trivially_copy_pass_by_ref)]

View File

@ -73,6 +73,8 @@ precision highp float;
@ -85,7 +87,7 @@ uniform sampler2D uGammaLUT;
uniform vec4 uFilterParams0; uniform vec4 uFilterParams0;
uniform vec4 uFilterParams1; uniform vec4 uFilterParams1;
uniform vec4 uFilterParams2; uniform vec4 uFilterParams2;
uniform vec2 uDestTextureSize; uniform vec2 uFramebufferSize;
uniform vec2 uColorTexture0Size; uniform vec2 uColorTexture0Size;
uniform int uCtrl; uniform int uCtrl;
@ -202,6 +204,128 @@ vec4 filterText(vec2 colorTexCoord,
vec4 filterRadialGradient(vec2 colorTexCoord,
sampler2D colorTexture,
vec2 colorTextureSize,
vec2 fragCoord,
vec2 framebufferSize,
vec4 filterParams0,
vec4 filterParams1){
vec2 lineFrom = filterParams0 . xy, lineVector = filterParams0 . zw;
vec2 radii = filterParams1 . xy, uvOrigin = filterParams1 . zw;
fragCoord . y = framebufferSize . y - fragCoord . y;
vec2 dP = fragCoord - lineFrom, dC = lineVector;
float dR = radii . y - radii . x;
float a = dot(dC, dC)- dR * dR;
float b = dot(dP, dC)+ radii . x * dR;
float c = dot(dP, dP)- radii . x * radii . x;
float discrim = b * b - a * c;
vec4 color = vec4(0.0);
if(abs(discrim)>= 0.00001){
vec2 ts = vec2(sqrt(discrim)* vec2(1.0, - 1.0)+ vec2(b))/ vec2(a);
float tMax = max(ts . x, ts . y);
float t = tMax <= 1.0 ? tMax : min(ts . x, ts . y);
if(t >= 0.0)
color = texture(colorTexture, uvOrigin + vec2(t, 0.0));
}
return color;
}
vec4 filterBlur(vec2 colorTexCoord, vec4 filterBlur(vec2 colorTexCoord,
sampler2D colorTexture, sampler2D colorTexture,
vec2 colorTextureSize, vec2 colorTextureSize,
@ -250,11 +374,21 @@ vec4 filterColor(vec2 colorTexCoord,
sampler2D colorTexture, sampler2D colorTexture,
sampler2D gammaLUT, sampler2D gammaLUT,
vec2 colorTextureSize, vec2 colorTextureSize,
vec2 fragCoord,
vec2 framebufferSize,
vec4 filterParams0, vec4 filterParams0,
vec4 filterParams1, vec4 filterParams1,
vec4 filterParams2, vec4 filterParams2,
int colorFilter){ int colorFilter){
switch(colorFilter){ switch(colorFilter){
case 0x1 :
return filterRadialGradient(colorTexCoord,
colorTexture,
colorTextureSize,
fragCoord,
framebufferSize,
filterParams0,
filterParams1);
case 0x3 : case 0x3 :
return filterBlur(colorTexCoord, return filterBlur(colorTexCoord,
colorTexture, colorTexture,
@ -433,6 +567,8 @@ void calculateColor(int ctrl){
uColorTexture0, uColorTexture0,
uGammaLUT, uGammaLUT,
uColorTexture0Size, uColorTexture0Size,
gl_FragCoord . xy,
uFramebufferSize,
uFilterParams0, uFilterParams0,
uFilterParams1, uFilterParams1,
uFilterParams2, uFilterParams2,
@ -446,7 +582,7 @@ void calculateColor(int ctrl){
int compositeOp =(ctrl >> 8)& 0xf; int compositeOp =(ctrl >> 8)& 0xf;
color = composite(color, uDestTexture, uDestTextureSize, gl_FragCoord . xy, compositeOp); color = composite(color, uDestTexture, uFramebufferSize, gl_FragCoord . xy, compositeOp);
color . rgb *= color . a; color . rgb *= color . a;

View File

@ -17,18 +17,18 @@ struct spvDescriptorSetBuffer0
texture2d<float> uGammaLUT [[id(6)]]; texture2d<float> uGammaLUT [[id(6)]];
sampler uGammaLUTSmplr [[id(7)]]; sampler uGammaLUTSmplr [[id(7)]];
constant float2* uColorTexture0Size [[id(8)]]; constant float2* uColorTexture0Size [[id(8)]];
constant float4* uFilterParams0 [[id(9)]]; constant float2* uFramebufferSize [[id(9)]];
constant float4* uFilterParams1 [[id(10)]]; constant float4* uFilterParams0 [[id(10)]];
constant float4* uFilterParams2 [[id(11)]]; constant float4* uFilterParams1 [[id(11)]];
texture2d<float> uColorTexture1 [[id(12)]]; constant float4* uFilterParams2 [[id(12)]];
sampler uColorTexture1Smplr [[id(13)]]; texture2d<float> uColorTexture1 [[id(13)]];
texture2d<float> uDestTexture [[id(14)]]; sampler uColorTexture1Smplr [[id(14)]];
sampler uDestTextureSmplr [[id(15)]]; texture2d<float> uDestTexture [[id(15)]];
constant float2* uDestTextureSize [[id(16)]]; sampler uDestTextureSmplr [[id(16)]];
constant int* uCtrl [[id(17)]]; constant int* uCtrl [[id(17)]];
}; };
constant float3 _862 = {}; constant float3 _1003 = {};
struct main0_out struct main0_out
{ {
@ -68,6 +68,42 @@ float sampleMask(thread const float& maskAlpha, thread const texture2d<float> ma
return fast::min(maskAlpha, coverage); return fast::min(maskAlpha, coverage);
} }
float4 filterRadialGradient(thread const float2& colorTexCoord, thread const texture2d<float> colorTexture, thread const sampler colorTextureSmplr, thread const float2& colorTextureSize, thread const float2& fragCoord, thread const float2& framebufferSize, thread const float4& filterParams0, thread const float4& filterParams1)
{
float2 lineFrom = filterParams0.xy;
float2 lineVector = filterParams0.zw;
float2 radii = filterParams1.xy;
float2 uvOrigin = filterParams1.zw;
float2 dP = fragCoord - lineFrom;
float2 dC = lineVector;
float dR = radii.y - radii.x;
float a = dot(dC, dC) - (dR * dR);
float b = dot(dP, dC) + (radii.x * dR);
float c = dot(dP, dP) - (radii.x * radii.x);
float discrim = (b * b) - (a * c);
float4 color = float4(0.0);
if (abs(discrim) >= 9.9999997473787516355514526367188e-06)
{
float2 ts = float2((float2(1.0, -1.0) * sqrt(discrim)) + float2(b)) / float2(a);
float tMax = fast::max(ts.x, ts.y);
float _511;
if (tMax <= 1.0)
{
_511 = tMax;
}
else
{
_511 = fast::min(ts.x, ts.y);
}
float t = _511;
if (t >= 0.0)
{
color = colorTexture.sample(colorTextureSmplr, (uvOrigin + float2(t, 0.0)));
}
}
return color;
}
float4 filterBlur(thread const float2& colorTexCoord, thread const texture2d<float> colorTexture, thread const sampler colorTextureSmplr, thread const float2& colorTextureSize, thread const float4& filterParams0, thread const float4& filterParams1) float4 filterBlur(thread const float2& colorTexCoord, thread const texture2d<float> colorTexture, thread const sampler colorTextureSmplr, thread const float2& colorTextureSize, thread const float4& filterParams0, thread const float4& filterParams1)
{ {
float2 srcOffsetScale = filterParams0.xy / colorTextureSize; float2 srcOffsetScale = filterParams0.xy / colorTextureSize;
@ -75,19 +111,19 @@ float4 filterBlur(thread const float2& colorTexCoord, thread const texture2d<flo
float3 gaussCoeff = filterParams1.xyz; float3 gaussCoeff = filterParams1.xyz;
float gaussSum = gaussCoeff.x; float gaussSum = gaussCoeff.x;
float4 color = colorTexture.sample(colorTextureSmplr, colorTexCoord) * gaussCoeff.x; float4 color = colorTexture.sample(colorTextureSmplr, colorTexCoord) * gaussCoeff.x;
float2 _435 = gaussCoeff.xy * gaussCoeff.yz; float2 _561 = gaussCoeff.xy * gaussCoeff.yz;
gaussCoeff = float3(_435.x, _435.y, gaussCoeff.z); gaussCoeff = float3(_561.x, _561.y, gaussCoeff.z);
for (int i = 1; i <= support; i += 2) for (int i = 1; i <= support; i += 2)
{ {
float gaussPartialSum = gaussCoeff.x; float gaussPartialSum = gaussCoeff.x;
float2 _455 = gaussCoeff.xy * gaussCoeff.yz; float2 _581 = gaussCoeff.xy * gaussCoeff.yz;
gaussCoeff = float3(_455.x, _455.y, gaussCoeff.z); gaussCoeff = float3(_581.x, _581.y, gaussCoeff.z);
gaussPartialSum += gaussCoeff.x; gaussPartialSum += gaussCoeff.x;
float2 srcOffset = srcOffsetScale * (float(i) + (gaussCoeff.x / gaussPartialSum)); float2 srcOffset = srcOffsetScale * (float(i) + (gaussCoeff.x / gaussPartialSum));
color += ((colorTexture.sample(colorTextureSmplr, (colorTexCoord - srcOffset)) + colorTexture.sample(colorTextureSmplr, (colorTexCoord + srcOffset))) * gaussPartialSum); color += ((colorTexture.sample(colorTextureSmplr, (colorTexCoord - srcOffset)) + colorTexture.sample(colorTextureSmplr, (colorTexCoord + srcOffset))) * gaussPartialSum);
gaussSum += (2.0 * gaussPartialSum); gaussSum += (2.0 * gaussPartialSum);
float2 _495 = gaussCoeff.xy * gaussCoeff.yz; float2 _621 = gaussCoeff.xy * gaussCoeff.yz;
gaussCoeff = float3(_495.x, _495.y, gaussCoeff.z); gaussCoeff = float3(_621.x, _621.y, gaussCoeff.z);
} }
return color / float4(gaussSum); return color / float4(gaussSum);
} }
@ -100,16 +136,16 @@ float filterTextSample1Tap(thread const float& offset, thread const texture2d<fl
void filterTextSample9Tap(thread float4& outAlphaLeft, thread float& outAlphaCenter, thread float4& outAlphaRight, thread const texture2d<float> colorTexture, thread const sampler colorTextureSmplr, thread const float2& colorTexCoord, thread const float4& kernel0, thread const float& onePixel) void filterTextSample9Tap(thread float4& outAlphaLeft, thread float& outAlphaCenter, thread float4& outAlphaRight, thread const texture2d<float> colorTexture, thread const sampler colorTextureSmplr, thread const float2& colorTexCoord, thread const float4& kernel0, thread const float& onePixel)
{ {
bool wide = kernel0.x > 0.0; bool wide = kernel0.x > 0.0;
float _183; float _195;
if (wide) if (wide)
{ {
float param = (-4.0) * onePixel; float param = (-4.0) * onePixel;
float2 param_1 = colorTexCoord; float2 param_1 = colorTexCoord;
_183 = filterTextSample1Tap(param, colorTexture, colorTextureSmplr, param_1); _195 = filterTextSample1Tap(param, colorTexture, colorTextureSmplr, param_1);
} }
else else
{ {
_183 = 0.0; _195 = 0.0;
} }
float param_2 = (-3.0) * onePixel; float param_2 = (-3.0) * onePixel;
float2 param_3 = colorTexCoord; float2 param_3 = colorTexCoord;
@ -117,7 +153,7 @@ void filterTextSample9Tap(thread float4& outAlphaLeft, thread float& outAlphaCen
float2 param_5 = colorTexCoord; float2 param_5 = colorTexCoord;
float param_6 = (-1.0) * onePixel; float param_6 = (-1.0) * onePixel;
float2 param_7 = colorTexCoord; float2 param_7 = colorTexCoord;
outAlphaLeft = float4(_183, filterTextSample1Tap(param_2, colorTexture, colorTextureSmplr, param_3), filterTextSample1Tap(param_4, colorTexture, colorTextureSmplr, param_5), filterTextSample1Tap(param_6, colorTexture, colorTextureSmplr, param_7)); outAlphaLeft = float4(_195, filterTextSample1Tap(param_2, colorTexture, colorTextureSmplr, param_3), filterTextSample1Tap(param_4, colorTexture, colorTextureSmplr, param_5), filterTextSample1Tap(param_6, colorTexture, colorTextureSmplr, param_7));
float param_8 = 0.0; float param_8 = 0.0;
float2 param_9 = colorTexCoord; float2 param_9 = colorTexCoord;
outAlphaCenter = filterTextSample1Tap(param_8, colorTexture, colorTextureSmplr, param_9); outAlphaCenter = filterTextSample1Tap(param_8, colorTexture, colorTextureSmplr, param_9);
@ -127,18 +163,18 @@ void filterTextSample9Tap(thread float4& outAlphaLeft, thread float& outAlphaCen
float2 param_13 = colorTexCoord; float2 param_13 = colorTexCoord;
float param_14 = 3.0 * onePixel; float param_14 = 3.0 * onePixel;
float2 param_15 = colorTexCoord; float2 param_15 = colorTexCoord;
float _243; float _255;
if (wide) if (wide)
{ {
float param_16 = 4.0 * onePixel; float param_16 = 4.0 * onePixel;
float2 param_17 = colorTexCoord; float2 param_17 = colorTexCoord;
_243 = filterTextSample1Tap(param_16, colorTexture, colorTextureSmplr, param_17); _255 = filterTextSample1Tap(param_16, colorTexture, colorTextureSmplr, param_17);
} }
else else
{ {
_243 = 0.0; _255 = 0.0;
} }
outAlphaRight = float4(filterTextSample1Tap(param_10, colorTexture, colorTextureSmplr, param_11), filterTextSample1Tap(param_12, colorTexture, colorTextureSmplr, param_13), filterTextSample1Tap(param_14, colorTexture, colorTextureSmplr, param_15), _243); outAlphaRight = float4(filterTextSample1Tap(param_10, colorTexture, colorTextureSmplr, param_11), filterTextSample1Tap(param_12, colorTexture, colorTextureSmplr, param_13), filterTextSample1Tap(param_14, colorTexture, colorTextureSmplr, param_15), _255);
} }
float filterTextConvolve7Tap(thread const float4& alpha0, thread const float3& alpha1, thread const float4& kernel0) float filterTextConvolve7Tap(thread const float4& alpha0, thread const float3& alpha1, thread const float4& kernel0)
@ -219,30 +255,40 @@ float4 filterNone(thread const float2& colorTexCoord, thread const texture2d<flo
return sampleColor(colorTexture, colorTextureSmplr, param); return sampleColor(colorTexture, colorTextureSmplr, param);
} }
float4 filterColor(thread const float2& colorTexCoord, thread const texture2d<float> colorTexture, thread const sampler colorTextureSmplr, thread const texture2d<float> gammaLUT, thread const sampler gammaLUTSmplr, thread const float2& colorTextureSize, thread const float4& filterParams0, thread const float4& filterParams1, thread const float4& filterParams2, thread const int& colorFilter) float4 filterColor(thread const float2& colorTexCoord, thread const texture2d<float> colorTexture, thread const sampler colorTextureSmplr, thread const texture2d<float> gammaLUT, thread const sampler gammaLUTSmplr, thread const float2& colorTextureSize, thread const float2& fragCoord, thread const float2& framebufferSize, thread const float4& filterParams0, thread const float4& filterParams1, thread const float4& filterParams2, thread const int& colorFilter)
{ {
switch (colorFilter) switch (colorFilter)
{ {
case 3: case 1:
{ {
float2 param = colorTexCoord; float2 param = colorTexCoord;
float2 param_1 = colorTextureSize; float2 param_1 = colorTextureSize;
float4 param_2 = filterParams0; float2 param_2 = fragCoord;
float4 param_3 = filterParams1; float2 param_3 = framebufferSize;
return filterBlur(param, colorTexture, colorTextureSmplr, param_1, param_2, param_3); float4 param_4 = filterParams0;
float4 param_5 = filterParams1;
return filterRadialGradient(param, colorTexture, colorTextureSmplr, param_1, param_2, param_3, param_4, param_5);
}
case 3:
{
float2 param_6 = colorTexCoord;
float2 param_7 = colorTextureSize;
float4 param_8 = filterParams0;
float4 param_9 = filterParams1;
return filterBlur(param_6, colorTexture, colorTextureSmplr, param_7, param_8, param_9);
} }
case 2: case 2:
{ {
float2 param_4 = colorTexCoord; float2 param_10 = colorTexCoord;
float2 param_5 = colorTextureSize; float2 param_11 = colorTextureSize;
float4 param_6 = filterParams0; float4 param_12 = filterParams0;
float4 param_7 = filterParams1; float4 param_13 = filterParams1;
float4 param_8 = filterParams2; float4 param_14 = filterParams2;
return filterText(param_4, colorTexture, colorTextureSmplr, gammaLUT, gammaLUTSmplr, param_5, param_6, param_7, param_8); return filterText(param_10, colorTexture, colorTextureSmplr, gammaLUT, gammaLUTSmplr, param_11, param_12, param_13, param_14);
} }
} }
float2 param_9 = colorTexCoord; float2 param_15 = colorTexCoord;
return filterNone(param_9, colorTexture, colorTextureSmplr); return filterNone(param_15, colorTexture, colorTextureSmplr);
} }
float3 compositeScreen(thread const float3& destColor, thread const float3& srcColor) float3 compositeScreen(thread const float3& destColor, thread const float3& srcColor)
@ -252,34 +298,34 @@ float3 compositeScreen(thread const float3& destColor, thread const float3& srcC
float3 compositeSelect(thread const bool3& cond, thread const float3& ifTrue, thread const float3& ifFalse) float3 compositeSelect(thread const bool3& cond, thread const float3& ifTrue, thread const float3& ifFalse)
{ {
float _546; float _687;
if (cond.x) if (cond.x)
{ {
_546 = ifTrue.x; _687 = ifTrue.x;
} }
else else
{ {
_546 = ifFalse.x; _687 = ifFalse.x;
} }
float _557; float _698;
if (cond.y) if (cond.y)
{ {
_557 = ifTrue.y; _698 = ifTrue.y;
} }
else else
{ {
_557 = ifFalse.y; _698 = ifFalse.y;
} }
float _568; float _709;
if (cond.z) if (cond.z)
{ {
_568 = ifTrue.z; _709 = ifTrue.z;
} }
else else
{ {
_568 = ifFalse.z; _709 = ifFalse.z;
} }
return float3(_546, _557, _568); return float3(_687, _698, _709);
} }
float3 compositeHardLight(thread const float3& destColor, thread const float3& srcColor) float3 compositeHardLight(thread const float3& destColor, thread const float3& srcColor)
@ -320,16 +366,16 @@ float3 compositeSoftLight(thread const float3& destColor, thread const float3& s
float compositeDivide(thread const float& num, thread const float& denom) float compositeDivide(thread const float& num, thread const float& denom)
{ {
float _582; float _723;
if (denom != 0.0) if (denom != 0.0)
{ {
_582 = num / denom; _723 = num / denom;
} }
else else
{ {
_582 = 0.0; _723 = 0.0;
} }
return _582; return _723;
} }
float3 compositeRGBToHSL(thread const float3& rgb) float3 compositeRGBToHSL(thread const float3& rgb)
@ -338,25 +384,25 @@ float3 compositeRGBToHSL(thread const float3& rgb)
float xMin = fast::min(fast::min(rgb.x, rgb.y), rgb.z); float xMin = fast::min(fast::min(rgb.x, rgb.y), rgb.z);
float c = v - xMin; float c = v - xMin;
float l = mix(xMin, v, 0.5); float l = mix(xMin, v, 0.5);
float3 _688; float3 _829;
if (rgb.x == v) if (rgb.x == v)
{ {
_688 = float3(0.0, rgb.yz); _829 = float3(0.0, rgb.yz);
} }
else else
{ {
float3 _701; float3 _842;
if (rgb.y == v) if (rgb.y == v)
{ {
_701 = float3(2.0, rgb.zx); _842 = float3(2.0, rgb.zx);
} }
else else
{ {
_701 = float3(4.0, rgb.xy); _842 = float3(4.0, rgb.xy);
} }
_688 = _701; _829 = _842;
} }
float3 terms = _688; float3 terms = _829;
float param = ((terms.x * c) + terms.y) - terms.z; float param = ((terms.x * c) + terms.y) - terms.z;
float param_1 = c; float param_1 = c;
float h = 1.0471975803375244140625 * compositeDivide(param, param_1); float h = 1.0471975803375244140625 * compositeDivide(param, param_1);
@ -488,7 +534,7 @@ float4 composite(thread const float4& srcColor, thread const texture2d<float> de
return float4(((srcColor.xyz * (srcColor.w * (1.0 - destColor.w))) + (blendedRGB * (srcColor.w * destColor.w))) + (destColor.xyz * (1.0 - srcColor.w)), 1.0); return float4(((srcColor.xyz * (srcColor.w * (1.0 - destColor.w))) + (blendedRGB * (srcColor.w * destColor.w))) + (destColor.xyz * (1.0 - srcColor.w)), 1.0);
} }
void calculateColor(thread const int& ctrl, thread texture2d<float> uMaskTexture0, thread const sampler uMaskTexture0Smplr, thread float3& vMaskTexCoord0, thread texture2d<float> uMaskTexture1, thread const sampler uMaskTexture1Smplr, thread float3& vMaskTexCoord1, thread float2& vColorTexCoord0, thread texture2d<float> uColorTexture0, thread const sampler uColorTexture0Smplr, thread texture2d<float> uGammaLUT, thread const sampler uGammaLUTSmplr, thread float2 uColorTexture0Size, thread float4 uFilterParams0, thread float4 uFilterParams1, thread float4 uFilterParams2, thread texture2d<float> uColorTexture1, thread const sampler uColorTexture1Smplr, thread float2& vColorTexCoord1, thread texture2d<float> uDestTexture, thread const sampler uDestTextureSmplr, thread float2 uDestTextureSize, thread float4& gl_FragCoord, thread float4& oFragColor) void calculateColor(thread const int& ctrl, thread texture2d<float> uMaskTexture0, thread const sampler uMaskTexture0Smplr, thread float3& vMaskTexCoord0, thread texture2d<float> uMaskTexture1, thread const sampler uMaskTexture1Smplr, thread float3& vMaskTexCoord1, thread float2& vColorTexCoord0, thread texture2d<float> uColorTexture0, thread const sampler uColorTexture0Smplr, thread texture2d<float> uGammaLUT, thread const sampler uGammaLUTSmplr, thread float2 uColorTexture0Size, thread float4& gl_FragCoord, thread float2 uFramebufferSize, thread float4 uFilterParams0, thread float4 uFilterParams1, thread float4 uFilterParams2, thread texture2d<float> uColorTexture1, thread const sampler uColorTexture1Smplr, thread float2& vColorTexCoord1, thread texture2d<float> uDestTexture, thread const sampler uDestTextureSmplr, thread float4& oFragColor)
{ {
int maskCtrl0 = (ctrl >> 0) & 3; int maskCtrl0 = (ctrl >> 0) & 3;
int maskCtrl1 = (ctrl >> 2) & 3; int maskCtrl1 = (ctrl >> 2) & 3;
@ -507,26 +553,28 @@ void calculateColor(thread const int& ctrl, thread texture2d<float> uMaskTexture
int color0Filter = (ctrl >> 4) & 3; int color0Filter = (ctrl >> 4) & 3;
float2 param_6 = vColorTexCoord0; float2 param_6 = vColorTexCoord0;
float2 param_7 = uColorTexture0Size; float2 param_7 = uColorTexture0Size;
float4 param_8 = uFilterParams0; float2 param_8 = gl_FragCoord.xy;
float4 param_9 = uFilterParams1; float2 param_9 = uFramebufferSize;
float4 param_10 = uFilterParams2; float4 param_10 = uFilterParams0;
int param_11 = color0Filter; float4 param_11 = uFilterParams1;
color += filterColor(param_6, uColorTexture0, uColorTexture0Smplr, uGammaLUT, uGammaLUTSmplr, param_7, param_8, param_9, param_10, param_11); float4 param_12 = uFilterParams2;
int param_13 = color0Filter;
color += filterColor(param_6, uColorTexture0, uColorTexture0Smplr, uGammaLUT, uGammaLUTSmplr, param_7, param_8, param_9, param_10, param_11, param_12, param_13);
} }
if (((ctrl >> 7) & 1) != 0) if (((ctrl >> 7) & 1) != 0)
{ {
float2 param_12 = vColorTexCoord1; float2 param_14 = vColorTexCoord1;
color *= sampleColor(uColorTexture1, uColorTexture1Smplr, param_12); color *= sampleColor(uColorTexture1, uColorTexture1Smplr, param_14);
} }
color.w *= maskAlpha; color.w *= maskAlpha;
int compositeOp = (ctrl >> 8) & 15; int compositeOp = (ctrl >> 8) & 15;
float4 param_13 = color; float4 param_15 = color;
float2 param_14 = uDestTextureSize; float2 param_16 = uFramebufferSize;
float2 param_15 = gl_FragCoord.xy; float2 param_17 = gl_FragCoord.xy;
int param_16 = compositeOp; int param_18 = compositeOp;
color = composite(param_13, uDestTexture, uDestTextureSmplr, param_14, param_15, param_16); color = composite(param_15, uDestTexture, uDestTextureSmplr, param_16, param_17, param_18);
float3 _1159 = color.xyz * color.w; float3 _1304 = color.xyz * color.w;
color = float4(_1159.x, _1159.y, _1159.z, color.w); color = float4(_1304.x, _1304.y, _1304.z, color.w);
oFragColor = color; oFragColor = color;
} }
@ -534,7 +582,7 @@ fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuff
{ {
main0_out out = {}; main0_out out = {};
int param = (*spvDescriptorSet0.uCtrl); int param = (*spvDescriptorSet0.uCtrl);
calculateColor(param, spvDescriptorSet0.uMaskTexture0, spvDescriptorSet0.uMaskTexture0Smplr, in.vMaskTexCoord0, spvDescriptorSet0.uMaskTexture1, spvDescriptorSet0.uMaskTexture1Smplr, in.vMaskTexCoord1, in.vColorTexCoord0, spvDescriptorSet0.uColorTexture0, spvDescriptorSet0.uColorTexture0Smplr, spvDescriptorSet0.uGammaLUT, spvDescriptorSet0.uGammaLUTSmplr, (*spvDescriptorSet0.uColorTexture0Size), (*spvDescriptorSet0.uFilterParams0), (*spvDescriptorSet0.uFilterParams1), (*spvDescriptorSet0.uFilterParams2), spvDescriptorSet0.uColorTexture1, spvDescriptorSet0.uColorTexture1Smplr, in.vColorTexCoord1, spvDescriptorSet0.uDestTexture, spvDescriptorSet0.uDestTextureSmplr, (*spvDescriptorSet0.uDestTextureSize), gl_FragCoord, out.oFragColor); calculateColor(param, spvDescriptorSet0.uMaskTexture0, spvDescriptorSet0.uMaskTexture0Smplr, in.vMaskTexCoord0, spvDescriptorSet0.uMaskTexture1, spvDescriptorSet0.uMaskTexture1Smplr, in.vMaskTexCoord1, in.vColorTexCoord0, spvDescriptorSet0.uColorTexture0, spvDescriptorSet0.uColorTexture0Smplr, spvDescriptorSet0.uGammaLUT, spvDescriptorSet0.uGammaLUTSmplr, (*spvDescriptorSet0.uColorTexture0Size), gl_FragCoord, (*spvDescriptorSet0.uFramebufferSize), (*spvDescriptorSet0.uFilterParams0), (*spvDescriptorSet0.uFilterParams1), (*spvDescriptorSet0.uFilterParams2), spvDescriptorSet0.uColorTexture1, spvDescriptorSet0.uColorTexture1Smplr, in.vColorTexCoord1, spvDescriptorSet0.uDestTexture, spvDescriptorSet0.uDestTextureSmplr, out.oFragColor);
return out; return out;
} }

View File

@ -35,6 +35,8 @@
precision highp float; precision highp float;
#define EPSILON 0.00001
#define FRAC_6_PI 1.9098593171027443 #define FRAC_6_PI 1.9098593171027443
#define FRAC_PI_3 1.0471975511965976 #define FRAC_PI_3 1.0471975511965976
@ -83,7 +85,7 @@ uniform sampler2D uGammaLUT;
uniform vec4 uFilterParams0; uniform vec4 uFilterParams0;
uniform vec4 uFilterParams1; uniform vec4 uFilterParams1;
uniform vec4 uFilterParams2; uniform vec4 uFilterParams2;
uniform vec2 uDestTextureSize; uniform vec2 uFramebufferSize;
uniform vec2 uColorTexture0Size; uniform vec2 uColorTexture0Size;
uniform int uCtrl; uniform int uCtrl;
@ -193,7 +195,129 @@ vec4 filterText(vec2 colorTexCoord,
return vec4(mix(bgColor, fgColor, alpha), 1.0); return vec4(mix(bgColor, fgColor, alpha), 1.0);
} }
// Filters // Other filters
// This is based on Pixman (MIT license). Copy and pasting the excellent comment
// from there:
// Implementation of radial gradients following the PDF specification.
// See section 8.7.4.5.4 Type 3 (Radial) Shadings of the PDF Reference
// Manual (PDF 32000-1:2008 at the time of this writing).
//
// In the radial gradient problem we are given two circles (c₁,r₁) and
// (c₂,r₂) that define the gradient itself.
//
// Mathematically the gradient can be defined as the family of circles
//
// ((1-t)·c₁ + t·(c₂), (1-t)·r₁ + t·r₂)
//
// excluding those circles whose radius would be < 0. When a point
// belongs to more than one circle, the one with a bigger t is the only
// one that contributes to its color. When a point does not belong
// to any of the circles, it is transparent black, i.e. RGBA (0, 0, 0, 0).
// Further limitations on the range of values for t are imposed when
// the gradient is not repeated, namely t must belong to [0,1].
//
// The graphical result is the same as drawing the valid (radius > 0)
// circles with increasing t in [-∞, +∞] (or in [0,1] if the gradient
// is not repeated) using SOURCE operator composition.
//
// It looks like a cone pointing towards the viewer if the ending circle
// is smaller than the starting one, a cone pointing inside the page if
// the starting circle is the smaller one and like a cylinder if they
// have the same radius.
//
// What we actually do is, given the point whose color we are interested
// in, compute the t values for that point, solving for t in:
//
// length((1-t)·c₁ + t·(c₂) - p) = (1-t)·r₁ + t·r₂
//
// Let's rewrite it in a simpler way, by defining some auxiliary
// variables:
//
// cd = c₂ - c₁
// pd = p - c₁
// dr = r₂ - r₁
// length(t·cd - pd) = r₁ + t·dr
//
// which actually means
//
// hypot(t·cdx - pdx, t·cdy - pdy) = r₁ + t·dr
//
// or
//
// ⎷((t·cdx - pdx)² + (t·cdy - pdy)²) = r₁ + t·dr.
//
// If we impose (as stated earlier) that r₁ + t·dr ≥ 0, it becomes:
//
// (t·cdx - pdx)² + (t·cdy - pdy)² = (r₁ + t·dr)²
//
// where we can actually expand the squares and solve for t:
//
// t²cdx² - 2t·cdx·pdx + pdx² + t²cdy² - 2t·cdy·pdy + pdy² =
// = r₁² + 2·r₁·t·dr + t²·dr²
//
// (cdx² + cdy² - dr²)t² - 2(cdx·pdx + cdy·pdy + r₁·dr)t +
// (pdx² + pdy² - r₁²) = 0
//
// A = cdx² + cdy² - dr²
// B = pdx·cdx + pdy·cdy + r₁·dr
// C = pdx² + pdy² - r₁²
// At² - 2Bt + C = 0
//
// The solutions (unless the equation degenerates because of A = 0) are:
//
// t = (B ± ⎷(B² - A·C)) / A
//
// The solution we are going to prefer is the bigger one, unless the
// radius associated to it is negative (or it falls outside the valid t
// range).
//
// Additional observations (useful for optimizations):
// A does not depend on p
//
// A < 0 ⟺ one of the two circles completely contains the other one
// ⟺ for every p, the radii associated with the two t solutions have
// opposite sign
//
// | x y z w
// --------------+-------------------------------------------
// filterParams0 | lineFrom.x lineFrom.y lineVector.x lineVector.y
// filterParams1 | radii.x radii.y uvOrigin.x uvOrigin.y
// filterParams2 | - - - -
vec4 filterRadialGradient(vec2 colorTexCoord,
sampler2D colorTexture,
vec2 colorTextureSize,
vec2 fragCoord,
vec2 framebufferSize,
vec4 filterParams0,
vec4 filterParams1) {
vec2 lineFrom = filterParams0.xy, lineVector = filterParams0.zw;
vec2 radii = filterParams1.xy, uvOrigin = filterParams1.zw;
#ifndef PF_ORIGIN_UPPER_LEFT
fragCoord.y = framebufferSize.y - fragCoord.y;
#endif
vec2 dP = fragCoord - lineFrom, dC = lineVector;
float dR = radii.y - radii.x;
float a = dot(dC, dC) - dR * dR;
float b = dot(dP, dC) + radii.x * dR;
float c = dot(dP, dP) - radii.x * radii.x;
float discrim = b * b - a * c;
vec4 color = vec4(0.0);
if (abs(discrim) >= EPSILON) {
vec2 ts = vec2(sqrt(discrim) * vec2(1.0, -1.0) + vec2(b)) / vec2(a);
float tMax = max(ts.x, ts.y);
float t = tMax <= 1.0 ? tMax : min(ts.x, ts.y);
if (t >= 0.0)
color = texture(colorTexture, uvOrigin + vec2(t, 0.0));
}
return color;
}
// | x y z w // | x y z w
// --------------+---------------------------------------------------- // --------------+----------------------------------------------------
@ -248,11 +372,21 @@ vec4 filterColor(vec2 colorTexCoord,
sampler2D colorTexture, sampler2D colorTexture,
sampler2D gammaLUT, sampler2D gammaLUT,
vec2 colorTextureSize, vec2 colorTextureSize,
vec2 fragCoord,
vec2 framebufferSize,
vec4 filterParams0, vec4 filterParams0,
vec4 filterParams1, vec4 filterParams1,
vec4 filterParams2, vec4 filterParams2,
int colorFilter) { int colorFilter) {
switch (colorFilter) { switch (colorFilter) {
case COMBINER_CTRL_FILTER_RADIAL_GRADIENT:
return filterRadialGradient(colorTexCoord,
colorTexture,
colorTextureSize,
fragCoord,
framebufferSize,
filterParams0,
filterParams1);
case COMBINER_CTRL_FILTER_BLUR: case COMBINER_CTRL_FILTER_BLUR:
return filterBlur(colorTexCoord, return filterBlur(colorTexCoord,
colorTexture, colorTexture,
@ -431,6 +565,8 @@ void calculateColor(int ctrl) {
uColorTexture0, uColorTexture0,
uGammaLUT, uGammaLUT,
uColorTexture0Size, uColorTexture0Size,
gl_FragCoord.xy,
uFramebufferSize,
uFilterParams0, uFilterParams0,
uFilterParams1, uFilterParams1,
uFilterParams2, uFilterParams2,
@ -444,7 +580,7 @@ void calculateColor(int ctrl) {
// Apply composite. // Apply composite.
int compositeOp = (ctrl >> COMBINER_CTRL_COMPOSITE_SHIFT) & COMBINER_CTRL_COMPOSITE_MASK; int compositeOp = (ctrl >> COMBINER_CTRL_COMPOSITE_SHIFT) & COMBINER_CTRL_COMPOSITE_MASK;
color = composite(color, uDestTexture, uDestTextureSize, gl_FragCoord.xy, compositeOp); color = composite(color, uDestTexture, uFramebufferSize, gl_FragCoord.xy, compositeOp);
// Premultiply alpha. // Premultiply alpha.
color.rgb *= color.a; color.rgb *= color.a;