diff --git a/canvas/src/lib.rs b/canvas/src/lib.rs index f4ba5aeb..5962b86a 100644 --- a/canvas/src/lib.rs +++ b/canvas/src/lib.rs @@ -524,11 +524,15 @@ pub enum LineJoin { #[derive(Clone, Copy, Debug, PartialEq)] pub enum CompositeOperation { SourceOver, - DestinationOver, - DestinationOut, + SourceIn, + SourceOut, SourceAtop, - Xor, + DestinationOver, + DestinationIn, + DestinationOut, + DestinationAtop, Lighter, + Xor, Multiply, Screen, Overlay, @@ -547,9 +551,13 @@ impl CompositeOperation { fn to_blend_mode(self) -> BlendMode { match self { CompositeOperation::SourceOver => BlendMode::SrcOver, - CompositeOperation::DestinationOver => BlendMode::DestOver, - CompositeOperation::DestinationOut => BlendMode::DestOut, + CompositeOperation::SourceIn => BlendMode::SrcIn, + CompositeOperation::SourceOut => BlendMode::SrcOut, CompositeOperation::SourceAtop => BlendMode::SrcAtop, + CompositeOperation::DestinationOver => BlendMode::DestOver, + CompositeOperation::DestinationIn => BlendMode::DestIn, + CompositeOperation::DestinationOut => BlendMode::DestOut, + CompositeOperation::DestinationAtop => BlendMode::DestAtop, CompositeOperation::Xor => BlendMode::Xor, CompositeOperation::Lighter => BlendMode::Lighter, CompositeOperation::Multiply => BlendMode::Multiply, diff --git a/content/src/effects.rs b/content/src/effects.rs index f985db3f..3f0ed9b0 100644 --- a/content/src/effects.rs +++ b/content/src/effects.rs @@ -75,6 +75,12 @@ pub enum BlendMode { Lighten, Darken, + // Porter-Duff + SrcIn, + DestIn, + SrcOut, + DestAtop, + // Overlay Multiply, Screen, @@ -123,6 +129,10 @@ impl BlendMode { BlendMode::Lighter | BlendMode::Lighten | BlendMode::Darken | + BlendMode::SrcIn | + BlendMode::DestIn | + BlendMode::SrcOut | + BlendMode::DestAtop | BlendMode::Multiply | BlendMode::Screen | BlendMode::HardLight | diff --git a/renderer/src/gpu/renderer.rs b/renderer/src/gpu/renderer.rs index 125ea1cd..d3f4cff8 100644 --- a/renderer/src/gpu/renderer.rs +++ b/renderer/src/gpu/renderer.rs @@ -11,12 +11,13 @@ use crate::gpu::debug::DebugUIPresenter; use crate::gpu::options::{DestFramebuffer, RendererOptions}; use crate::gpu::shaders::{AlphaTileDodgeBurnProgram, AlphaTileHSLProgram, AlphaTileOverlayProgram}; -use crate::gpu::shaders::{AlphaTileProgram, AlphaTileVertexArray, CopyTileProgram}; -use crate::gpu::shaders::{CopyTileVertexArray, FillProgram, FillVertexArray, FilterBasicProgram}; -use crate::gpu::shaders::{FilterBasicVertexArray, FilterTextProgram, FilterTextVertexArray}; -use crate::gpu::shaders::{MAX_FILLS_PER_BATCH, MaskTileProgram, MaskTileVertexArray}; -use crate::gpu::shaders::{ReprojectionProgram, ReprojectionVertexArray, SolidTileProgram}; -use crate::gpu::shaders::{SolidTileVertexArray, StencilProgram, StencilVertexArray}; +use crate::gpu::shaders::{AlphaTilePorterDuffProgram, AlphaTileProgram, AlphaTileVertexArray}; +use crate::gpu::shaders::{CopyTileProgram, CopyTileVertexArray, FillProgram, FillVertexArray}; +use crate::gpu::shaders::{FilterBasicProgram, FilterBasicVertexArray, FilterTextProgram}; +use crate::gpu::shaders::{FilterTextVertexArray, MAX_FILLS_PER_BATCH, MaskTileProgram}; +use crate::gpu::shaders::{MaskTileVertexArray, ReprojectionProgram, ReprojectionVertexArray}; +use crate::gpu::shaders::{SolidTileProgram, SolidTileVertexArray}; +use crate::gpu::shaders::{StencilProgram, StencilVertexArray}; use crate::gpu_data::{AlphaTile, FillBatchPrimitive, MaskTile, PaintData, PaintPageContents}; use crate::gpu_data::{PaintPageId, RenderCommand, SolidTileVertex}; use crate::options::BoundingQuad; @@ -61,6 +62,11 @@ const OVERLAY_BLEND_MODE_SCREEN: i32 = 1; const OVERLAY_BLEND_MODE_HARD_LIGHT: i32 = 2; const OVERLAY_BLEND_MODE_OVERLAY: i32 = 3; +const PORTER_DUFF_FACTOR_ZERO: i32 = 0; +const PORTER_DUFF_FACTOR_DEST_ALPHA: i32 = 1; +const PORTER_DUFF_FACTOR_SRC_ALPHA: i32 = 2; +const PORTER_DUFF_FACTOR_ONE_MINUS_DEST_ALPHA: i32 = 3; + pub struct Renderer where D: Device, @@ -77,6 +83,7 @@ where copy_tile_program: CopyTileProgram, solid_tile_program: SolidTileProgram, alpha_tile_program: AlphaTileProgram, + alpha_tile_porterduff_program: AlphaTilePorterDuffProgram, alpha_tile_overlay_program: AlphaTileOverlayProgram, alpha_tile_dodgeburn_program: AlphaTileDodgeBurnProgram, alpha_tile_hsl_program: AlphaTileHSLProgram, @@ -85,6 +92,7 @@ where copy_tile_vertex_array: CopyTileVertexArray, solid_tile_vertex_array: SolidTileVertexArray, alpha_tile_vertex_array: AlphaTileVertexArray, + alpha_tile_porterduff_vertex_array: AlphaTileVertexArray, alpha_tile_overlay_vertex_array: AlphaTileVertexArray, alpha_tile_dodgeburn_vertex_array: AlphaTileVertexArray, alpha_tile_hsl_vertex_array: AlphaTileVertexArray, @@ -158,6 +166,7 @@ where let copy_tile_program = CopyTileProgram::new(&device, resources); let solid_tile_program = SolidTileProgram::new(&device, resources); let alpha_tile_program = AlphaTileProgram::new(&device, resources); + let alpha_tile_porterduff_program = AlphaTilePorterDuffProgram::new(&device, resources); let alpha_tile_overlay_program = AlphaTileOverlayProgram::new(&device, resources); let alpha_tile_dodgeburn_program = AlphaTileDodgeBurnProgram::new(&device, resources); let alpha_tile_hsl_program = AlphaTileHSLProgram::new(&device, resources); @@ -214,6 +223,12 @@ where &alpha_tile_vertex_buffer, &quads_vertex_indices_buffer, ); + let alpha_tile_porterduff_vertex_array = AlphaTileVertexArray::new( + &device, + &alpha_tile_porterduff_program.alpha_tile_program, + &alpha_tile_vertex_buffer, + &quads_vertex_indices_buffer, + ); let alpha_tile_overlay_vertex_array = AlphaTileVertexArray::new( &device, &alpha_tile_overlay_program.alpha_tile_program, @@ -293,6 +308,7 @@ where copy_tile_program, solid_tile_program, alpha_tile_program, + alpha_tile_porterduff_program, alpha_tile_overlay_program, alpha_tile_dodgeburn_program, alpha_tile_hsl_program, @@ -301,6 +317,7 @@ where copy_tile_vertex_array, solid_tile_vertex_array, alpha_tile_vertex_array, + alpha_tile_porterduff_vertex_array, alpha_tile_overlay_vertex_array, alpha_tile_dodgeburn_vertex_array, alpha_tile_hsl_vertex_array, @@ -735,6 +752,10 @@ where let (alpha_tile_program, alpha_tile_vertex_array) = match blend_mode_program { BlendModeProgram::Regular => (&self.alpha_tile_program, &self.alpha_tile_vertex_array), + BlendModeProgram::PorterDuff => { + (&self.alpha_tile_porterduff_program.alpha_tile_program, + &self.alpha_tile_porterduff_vertex_array) + } BlendModeProgram::Overlay => { (&self.alpha_tile_overlay_program.alpha_tile_program, &self.alpha_tile_overlay_vertex_array) @@ -777,6 +798,11 @@ where match blend_mode_program { BlendModeProgram::Regular => {} + BlendModeProgram::PorterDuff => { + self.set_uniforms_for_porter_duff_blend_mode(&mut textures, + &mut uniforms, + blend_mode); + } BlendModeProgram::Overlay => { self.set_uniforms_for_overlay_blend_mode(&mut textures, &mut uniforms, blend_mode); } @@ -809,6 +835,37 @@ where self.preserve_draw_framebuffer(); } + fn set_uniforms_for_porter_duff_blend_mode<'a>( + &'a self, + textures: &mut Vec<&'a D::Texture>, + uniforms: &mut Vec<(&'a D::Uniform, UniformData)>, + blend_mode: BlendMode) { + let (src_factor, dest_factor) = match blend_mode { + BlendMode::SrcIn => { + (PORTER_DUFF_FACTOR_DEST_ALPHA, PORTER_DUFF_FACTOR_ZERO) + } + BlendMode::DestIn => { + (PORTER_DUFF_FACTOR_ZERO, PORTER_DUFF_FACTOR_SRC_ALPHA) + } + BlendMode::SrcOut => { + (PORTER_DUFF_FACTOR_ONE_MINUS_DEST_ALPHA, PORTER_DUFF_FACTOR_ZERO) + } + BlendMode::DestAtop => { + (PORTER_DUFF_FACTOR_ONE_MINUS_DEST_ALPHA, PORTER_DUFF_FACTOR_SRC_ALPHA) + } + _ => unreachable!(), + }; + + uniforms.push((&self.alpha_tile_porterduff_program.src_factor_uniform, + UniformData::Int(src_factor))); + uniforms.push((&self.alpha_tile_porterduff_program.dest_factor_uniform, + UniformData::Int(dest_factor))); + + textures.push(self.device.framebuffer_texture(&self.dest_blend_framebuffer)); + uniforms.push((&self.alpha_tile_porterduff_program.dest_uniform, + UniformData::TextureUnit(textures.len() as u32 - 1))); + } + fn set_uniforms_for_overlay_blend_mode<'a>(&'a self, textures: &mut Vec<&'a D::Texture>, uniforms: &mut Vec<(&'a D::Uniform, UniformData)>, @@ -1477,6 +1534,10 @@ impl BlendModeExt for BlendMode { op: BlendOp::Min, }) } + BlendMode::SrcIn | + BlendMode::DestIn | + BlendMode::SrcOut | + BlendMode::DestAtop | BlendMode::Multiply | BlendMode::Screen | BlendMode::HardLight | @@ -1497,6 +1558,7 @@ impl BlendModeExt for BlendMode { #[derive(Clone, Copy, Debug, PartialEq)] pub(crate) enum BlendModeProgram { Regular, + PorterDuff, Overlay, DodgeBurn, HSL, @@ -1514,6 +1576,10 @@ impl BlendModeProgram { BlendMode::Lighter | BlendMode::Lighten | BlendMode::Darken => BlendModeProgram::Regular, + BlendMode::SrcIn | + BlendMode::DestIn | + BlendMode::SrcOut | + BlendMode::DestAtop => BlendModeProgram::PorterDuff, BlendMode::Multiply | BlendMode::Screen | BlendMode::HardLight | @@ -1530,6 +1596,7 @@ impl BlendModeProgram { pub(crate) fn needs_readable_framebuffer(self) -> bool { match self { BlendModeProgram::Regular => false, + BlendModeProgram::PorterDuff | BlendModeProgram::Overlay | BlendModeProgram::DodgeBurn | BlendModeProgram::HSL => true, diff --git a/renderer/src/gpu/shaders.rs b/renderer/src/gpu/shaders.rs index e06c8fd0..efc1b377 100644 --- a/renderer/src/gpu/shaders.rs +++ b/renderer/src/gpu/shaders.rs @@ -450,6 +450,31 @@ impl CopyTileProgram where D: Device { } } +pub struct AlphaTilePorterDuffProgram where D: Device { + pub alpha_tile_program: AlphaTileProgram, + pub dest_uniform: D::Uniform, + pub dest_factor_uniform: D::Uniform, + pub src_factor_uniform: D::Uniform, +} + +impl AlphaTilePorterDuffProgram where D: Device { + pub fn new(device: &D, resources: &dyn ResourceLoader) -> AlphaTilePorterDuffProgram { + let alpha_tile_program = + AlphaTileProgram::from_fragment_shader_name(device, + resources, + "tile_alpha_porterduff"); + let dest_uniform = device.get_uniform(&alpha_tile_program.program, "Dest"); + let dest_factor_uniform = device.get_uniform(&alpha_tile_program.program, "DestFactor"); + let src_factor_uniform = device.get_uniform(&alpha_tile_program.program, "SrcFactor"); + AlphaTilePorterDuffProgram { + alpha_tile_program, + dest_uniform, + dest_factor_uniform, + src_factor_uniform, + } + } +} + pub struct AlphaTileHSLProgram where D: Device { pub alpha_tile_program: AlphaTileProgram, pub dest_uniform: D::Uniform, diff --git a/resources/shaders/gl3/tile_alpha_porterduff.fs.glsl b/resources/shaders/gl3/tile_alpha_porterduff.fs.glsl new file mode 100644 index 00000000..d44ae323 --- /dev/null +++ b/resources/shaders/gl3/tile_alpha_porterduff.fs.glsl @@ -0,0 +1,96 @@ +#version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + + + + + + + + + + + + + + +#extension GL_GOOGLE_include_directive : enable + + + + + + +precision highp float; + +uniform int uDestFactor; +uniform int uSrcFactor; + +out vec4 oFragColor; + + + + + + + + + + + + +uniform sampler2D uStencilTexture; +uniform sampler2D uPaintTexture; +uniform sampler2D uDest; +uniform vec2 uFramebufferSize; + +in vec2 vColorTexCoord; +in vec2 vMaskTexCoord; + + +vec4 sampleSrcColor(){ + float coverage = texture(uStencilTexture, vMaskTexCoord). r; + vec4 srcRGBA = texture(uPaintTexture, vColorTexCoord); + return vec4(srcRGBA . rgb, srcRGBA . a * coverage); +} + +vec4 sampleDestColor(){ + vec2 destTexCoord = gl_FragCoord . xy / uFramebufferSize; + return texture(uDest, destTexCoord); +} + + +vec4 blendColors(vec4 destRGBA, vec4 srcRGBA, vec3 blendedRGB){ + return vec4(srcRGBA . a *(1.0 - destRGBA . a)* srcRGBA . rgb + + srcRGBA . a * destRGBA . a * blendedRGB + + (1.0 - srcRGBA . a)* destRGBA . a * destRGBA . rgb, + 1.0); +} + +vec3 select3(bvec3 cond, vec3 a, vec3 b){ + return vec3(cond . x ? a . x : b . x, cond . y ? a . y : b . y, cond . z ? a . z : b . z); +} + + +vec4 getFactor(int factor, vec4 destRGBA, vec4 srcRGBA){ + if(factor == 0) + return vec4(0.0); + if(factor == 1) + return vec4(destRGBA . a); + if(factor == 2) + return vec4(srcRGBA . a); + return vec4(1.0 - destRGBA . a); +} + +void main(){ + vec4 srcRGBA = sampleSrcColor(); + vec4 destRGBA = sampleDestColor(); + + vec4 destFactor = getFactor(uDestFactor, destRGBA, srcRGBA); + vec4 srcFactor = getFactor(uSrcFactor, destRGBA, srcRGBA); + + vec4 blended = destFactor * destRGBA * vec4(destRGBA . aaa, 1.0)+ + srcFactor * srcRGBA * vec4(srcRGBA . aaa, 1.0); + oFragColor = blended; +} + diff --git a/resources/shaders/metal/tile_alpha_porterduff.fs.metal b/resources/shaders/metal/tile_alpha_porterduff.fs.metal new file mode 100644 index 00000000..cd414300 --- /dev/null +++ b/resources/shaders/metal/tile_alpha_porterduff.fs.metal @@ -0,0 +1,80 @@ +// Automatically generated from files in pathfinder/shaders/. Do not edit! +#pragma clang diagnostic ignored "-Wmissing-prototypes" + +#include +#include + +using namespace metal; + +struct spvDescriptorSetBuffer0 +{ + texture2d uStencilTexture [[id(0)]]; + sampler uStencilTextureSmplr [[id(1)]]; + texture2d uPaintTexture [[id(2)]]; + sampler uPaintTextureSmplr [[id(3)]]; + constant float2* uFramebufferSize [[id(4)]]; + texture2d uDest [[id(5)]]; + sampler uDestSmplr [[id(6)]]; + constant int* uDestFactor [[id(7)]]; + constant int* uSrcFactor [[id(8)]]; +}; + +struct main0_out +{ + float4 oFragColor [[color(0)]]; +}; + +struct main0_in +{ + float2 vColorTexCoord [[user(locn0)]]; + float2 vMaskTexCoord [[user(locn1)]]; +}; + +float4 sampleSrcColor(thread texture2d uStencilTexture, thread const sampler uStencilTextureSmplr, thread float2& vMaskTexCoord, thread texture2d uPaintTexture, thread const sampler uPaintTextureSmplr, thread float2& vColorTexCoord) +{ + float coverage = uStencilTexture.sample(uStencilTextureSmplr, vMaskTexCoord).x; + float4 srcRGBA = uPaintTexture.sample(uPaintTextureSmplr, vColorTexCoord); + return float4(srcRGBA.xyz, srcRGBA.w * coverage); +} + +float4 sampleDestColor(thread float4& gl_FragCoord, thread float2 uFramebufferSize, thread texture2d uDest, thread const sampler uDestSmplr) +{ + float2 destTexCoord = gl_FragCoord.xy / uFramebufferSize; + return uDest.sample(uDestSmplr, destTexCoord); +} + +float4 getFactor(thread const int& factor, thread const float4& destRGBA, thread const float4& srcRGBA) +{ + if (factor == 0) + { + return float4(0.0); + } + if (factor == 1) + { + return float4(destRGBA.w); + } + if (factor == 2) + { + return float4(srcRGBA.w); + } + return float4(1.0 - destRGBA.w); +} + +fragment main0_out main0(main0_in in [[stage_in]], constant spvDescriptorSetBuffer0& spvDescriptorSet0 [[buffer(0)]], float4 gl_FragCoord [[position]]) +{ + main0_out out = {}; + float4 srcRGBA = sampleSrcColor(spvDescriptorSet0.uStencilTexture, spvDescriptorSet0.uStencilTextureSmplr, in.vMaskTexCoord, spvDescriptorSet0.uPaintTexture, spvDescriptorSet0.uPaintTextureSmplr, in.vColorTexCoord); + float4 destRGBA = sampleDestColor(gl_FragCoord, (*spvDescriptorSet0.uFramebufferSize), spvDescriptorSet0.uDest, spvDescriptorSet0.uDestSmplr); + int param = (*spvDescriptorSet0.uDestFactor); + float4 param_1 = destRGBA; + float4 param_2 = srcRGBA; + float4 destFactor = getFactor(param, param_1, param_2); + int param_3 = (*spvDescriptorSet0.uSrcFactor); + float4 param_4 = destRGBA; + float4 param_5 = srcRGBA; + float4 srcFactor = getFactor(param_3, param_4, param_5); + float4 blended = ((destFactor * destRGBA) * float4(destRGBA.www, 1.0)) + ((srcFactor * srcRGBA) * float4(srcRGBA.www, 1.0)); + out.oFragColor = blended; + return out; +} + diff --git a/shaders/Makefile b/shaders/Makefile index 2f438e67..ce19feba 100644 --- a/shaders/Makefile +++ b/shaders/Makefile @@ -26,6 +26,7 @@ SHADERS=\ tile_alpha_dodgeburn.fs.glsl \ tile_alpha_hsl.fs.glsl \ tile_alpha_overlay.fs.glsl \ + tile_alpha_porterduff.fs.glsl \ tile_copy.fs.glsl \ tile_copy.vs.glsl \ tile_solid.fs.glsl \ diff --git a/shaders/tile_alpha_porterduff.fs.glsl b/shaders/tile_alpha_porterduff.fs.glsl new file mode 100644 index 00000000..a5011b67 --- /dev/null +++ b/shaders/tile_alpha_porterduff.fs.glsl @@ -0,0 +1,51 @@ +#version 330 + +// pathfinder/shaders/tile_alpha_porterduff.fs.glsl +// +// Copyright © 2020 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Porter-Duff blend modes not supported by the standard GPU blender. + +#extension GL_GOOGLE_include_directive : enable + +#define PORTER_DUFF_FACTOR_ZERO 0 +#define PORTER_DUFF_FACTOR_DEST_ALPHA 1 +#define PORTER_DUFF_FACTOR_SRC_ALPHA 2 +#define PORTER_DUFF_FACTOR_ONE_MINUS_DEST_ALPHA 3 + +precision highp float; + +uniform int uDestFactor; +uniform int uSrcFactor; + +out vec4 oFragColor; + +#include "tile_alpha_sample.inc.glsl" + +vec4 getFactor(int factor, vec4 destRGBA, vec4 srcRGBA) { + if (factor == PORTER_DUFF_FACTOR_ZERO) + return vec4(0.0); + if (factor == PORTER_DUFF_FACTOR_DEST_ALPHA) + return vec4(destRGBA.a); + if (factor == PORTER_DUFF_FACTOR_SRC_ALPHA) + return vec4(srcRGBA.a); + return vec4(1.0 - destRGBA.a); +} + +void main() { + vec4 srcRGBA = sampleSrcColor(); + vec4 destRGBA = sampleDestColor(); + + vec4 destFactor = getFactor(uDestFactor, destRGBA, srcRGBA); + vec4 srcFactor = getFactor(uSrcFactor, destRGBA, srcRGBA); + + vec4 blended = destFactor * destRGBA * vec4(destRGBA.aaa, 1.0) + + srcFactor * srcRGBA * vec4(srcRGBA.aaa, 1.0); + oFragColor = blended; +}