From 02012431cac55749bc069f5006abffedc7be6504 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 25 Feb 2020 11:37:42 -0800 Subject: [PATCH] Implement color dodge and color burn blend modes --- canvas/src/lib.rs | 4 + content/src/effects.rs | 8 +- renderer/src/gpu/renderer.rs | 56 +++++++-- renderer/src/gpu/shaders.rs | 16 +++ .../shaders/gl3/tile_alpha_dodgeburn.fs.glsl | 86 +++++++++++++ .../metal/tile_alpha_dodgeburn.fs.metal | 114 ++++++++++++++++++ shaders/Makefile | 1 + shaders/tile_alpha_dodgeburn.fs.glsl | 41 +++++++ 8 files changed, 317 insertions(+), 9 deletions(-) create mode 100644 resources/shaders/gl3/tile_alpha_dodgeburn.fs.glsl create mode 100644 resources/shaders/metal/tile_alpha_dodgeburn.fs.metal create mode 100644 shaders/tile_alpha_dodgeburn.fs.glsl diff --git a/canvas/src/lib.rs b/canvas/src/lib.rs index b7975f26..f4ba5aeb 100644 --- a/canvas/src/lib.rs +++ b/canvas/src/lib.rs @@ -534,6 +534,8 @@ pub enum CompositeOperation { Overlay, Darken, Lighten, + ColorDodge, + ColorBurn, HardLight, Hue, Saturation, @@ -555,6 +557,8 @@ impl CompositeOperation { CompositeOperation::Overlay => BlendMode::Overlay, CompositeOperation::Darken => BlendMode::Darken, CompositeOperation::Lighten => BlendMode::Lighten, + CompositeOperation::ColorDodge => BlendMode::ColorDodge, + CompositeOperation::ColorBurn => BlendMode::ColorBurn, CompositeOperation::HardLight => BlendMode::HardLight, CompositeOperation::Hue => BlendMode::Hue, CompositeOperation::Saturation => BlendMode::Saturation, diff --git a/content/src/effects.rs b/content/src/effects.rs index 394a0e86..f985db3f 100644 --- a/content/src/effects.rs +++ b/content/src/effects.rs @@ -81,6 +81,10 @@ pub enum BlendMode { HardLight, Overlay, + // Dodge/burn + ColorDodge, + ColorBurn, + // HSL Hue, Saturation, @@ -118,11 +122,13 @@ impl BlendMode { BlendMode::Xor | BlendMode::Lighter | BlendMode::Lighten | + BlendMode::Darken | BlendMode::Multiply | BlendMode::Screen | BlendMode::HardLight | BlendMode::Overlay | - BlendMode::Darken | + BlendMode::ColorDodge | + BlendMode::ColorBurn | BlendMode::Hue | BlendMode::Saturation | BlendMode::Color | diff --git a/renderer/src/gpu/renderer.rs b/renderer/src/gpu/renderer.rs index b17c7500..125ea1cd 100644 --- a/renderer/src/gpu/renderer.rs +++ b/renderer/src/gpu/renderer.rs @@ -10,13 +10,13 @@ use crate::gpu::debug::DebugUIPresenter; use crate::gpu::options::{DestFramebuffer, RendererOptions}; -use crate::gpu::shaders::{AlphaTileHSLProgram, AlphaTileOverlayProgram, AlphaTileProgram}; -use crate::gpu::shaders::{AlphaTileVertexArray, CopyTileProgram, CopyTileVertexArray, FillProgram}; -use crate::gpu::shaders::{FillVertexArray, FilterBasicProgram, FilterBasicVertexArray}; -use crate::gpu::shaders::{FilterTextProgram, FilterTextVertexArray, MAX_FILLS_PER_BATCH}; -use crate::gpu::shaders::{MaskTileProgram, MaskTileVertexArray, ReprojectionProgram}; -use crate::gpu::shaders::{ReprojectionVertexArray, SolidTileProgram, SolidTileVertexArray}; -use crate::gpu::shaders::{StencilProgram, StencilVertexArray}; +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_data::{AlphaTile, FillBatchPrimitive, MaskTile, PaintData, PaintPageContents}; use crate::gpu_data::{PaintPageId, RenderCommand, SolidTileVertex}; use crate::options::BoundingQuad; @@ -78,6 +78,7 @@ where solid_tile_program: SolidTileProgram, alpha_tile_program: AlphaTileProgram, alpha_tile_overlay_program: AlphaTileOverlayProgram, + alpha_tile_dodgeburn_program: AlphaTileDodgeBurnProgram, alpha_tile_hsl_program: AlphaTileHSLProgram, mask_winding_tile_vertex_array: MaskTileVertexArray, mask_evenodd_tile_vertex_array: MaskTileVertexArray, @@ -85,6 +86,7 @@ where solid_tile_vertex_array: SolidTileVertexArray, alpha_tile_vertex_array: AlphaTileVertexArray, alpha_tile_overlay_vertex_array: AlphaTileVertexArray, + alpha_tile_dodgeburn_vertex_array: AlphaTileVertexArray, alpha_tile_hsl_vertex_array: AlphaTileVertexArray, area_lut_texture: D::Texture, alpha_tile_vertex_buffer: D::Buffer, @@ -157,6 +159,7 @@ where let solid_tile_program = SolidTileProgram::new(&device, resources); let alpha_tile_program = AlphaTileProgram::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); let filter_basic_program = FilterBasicProgram::new(&device, resources); let filter_text_program = FilterTextProgram::new(&device, resources); @@ -217,6 +220,12 @@ where &alpha_tile_vertex_buffer, &quads_vertex_indices_buffer, ); + let alpha_tile_dodgeburn_vertex_array = AlphaTileVertexArray::new( + &device, + &alpha_tile_dodgeburn_program.alpha_tile_program, + &alpha_tile_vertex_buffer, + &quads_vertex_indices_buffer, + ); let alpha_tile_hsl_vertex_array = AlphaTileVertexArray::new( &device, &alpha_tile_hsl_program.alpha_tile_program, @@ -285,6 +294,7 @@ where solid_tile_program, alpha_tile_program, alpha_tile_overlay_program, + alpha_tile_dodgeburn_program, alpha_tile_hsl_program, mask_winding_tile_vertex_array, mask_evenodd_tile_vertex_array, @@ -292,6 +302,7 @@ where solid_tile_vertex_array, alpha_tile_vertex_array, alpha_tile_overlay_vertex_array, + alpha_tile_dodgeburn_vertex_array, alpha_tile_hsl_vertex_array, area_lut_texture, alpha_tile_vertex_buffer, @@ -728,6 +739,10 @@ where (&self.alpha_tile_overlay_program.alpha_tile_program, &self.alpha_tile_overlay_vertex_array) } + BlendModeProgram::DodgeBurn => { + (&self.alpha_tile_dodgeburn_program.alpha_tile_program, + &self.alpha_tile_dodgeburn_vertex_array) + } BlendModeProgram::HSL => { (&self.alpha_tile_hsl_program.alpha_tile_program, &self.alpha_tile_hsl_vertex_array) @@ -765,6 +780,11 @@ where BlendModeProgram::Overlay => { self.set_uniforms_for_overlay_blend_mode(&mut textures, &mut uniforms, blend_mode); } + BlendModeProgram::DodgeBurn => { + self.set_uniforms_for_dodge_burn_blend_mode(&mut textures, + &mut uniforms, + blend_mode); + } BlendModeProgram::HSL => { self.set_uniforms_for_hsl_blend_mode(&mut textures, &mut uniforms, blend_mode); } @@ -809,6 +829,19 @@ where UniformData::TextureUnit(textures.len() as u32 - 1))); } + fn set_uniforms_for_dodge_burn_blend_mode<'a>( + &'a self, + textures: &mut Vec<&'a D::Texture>, + uniforms: &mut Vec<(&'a D::Uniform, UniformData)>, + blend_mode: BlendMode) { + uniforms.push((&self.alpha_tile_dodgeburn_program.burn_uniform, + UniformData::Int(if blend_mode == BlendMode::ColorBurn { 1 } else { 0 }))); + + textures.push(self.device.framebuffer_texture(&self.dest_blend_framebuffer)); + uniforms.push((&self.alpha_tile_dodgeburn_program.dest_uniform, + UniformData::TextureUnit(textures.len() as u32 - 1))); + } + fn set_uniforms_for_hsl_blend_mode<'a>(&'a self, textures: &mut Vec<&'a D::Texture>, uniforms: &mut Vec<(&'a D::Uniform, UniformData)>, @@ -1448,6 +1481,8 @@ impl BlendModeExt for BlendMode { BlendMode::Screen | BlendMode::HardLight | BlendMode::Overlay | + BlendMode::ColorDodge | + BlendMode::ColorBurn | BlendMode::Hue | BlendMode::Saturation | BlendMode::Color | @@ -1463,6 +1498,7 @@ impl BlendModeExt for BlendMode { pub(crate) enum BlendModeProgram { Regular, Overlay, + DodgeBurn, HSL, } @@ -1482,6 +1518,8 @@ impl BlendModeProgram { BlendMode::Screen | BlendMode::HardLight | BlendMode::Overlay => BlendModeProgram::Overlay, + BlendMode::ColorDodge | + BlendMode::ColorBurn => BlendModeProgram::DodgeBurn, BlendMode::Hue | BlendMode::Saturation | BlendMode::Color | @@ -1492,7 +1530,9 @@ impl BlendModeProgram { pub(crate) fn needs_readable_framebuffer(self) -> bool { match self { BlendModeProgram::Regular => false, - BlendModeProgram::Overlay | BlendModeProgram::HSL => true, + BlendModeProgram::Overlay | + BlendModeProgram::DodgeBurn | + BlendModeProgram::HSL => true, } } } diff --git a/renderer/src/gpu/shaders.rs b/renderer/src/gpu/shaders.rs index 838be97b..e06c8fd0 100644 --- a/renderer/src/gpu/shaders.rs +++ b/renderer/src/gpu/shaders.rs @@ -484,6 +484,22 @@ impl AlphaTileOverlayProgram where D: Device { } } +pub struct AlphaTileDodgeBurnProgram where D: Device { + pub alpha_tile_program: AlphaTileProgram, + pub dest_uniform: D::Uniform, + pub burn_uniform: D::Uniform, +} + +impl AlphaTileDodgeBurnProgram where D: Device { + pub fn new(device: &D, resources: &dyn ResourceLoader) -> AlphaTileDodgeBurnProgram { + let alpha_tile_program = + AlphaTileProgram::from_fragment_shader_name(device, resources, "tile_alpha_dodgeburn"); + let dest_uniform = device.get_uniform(&alpha_tile_program.program, "Dest"); + let burn_uniform = device.get_uniform(&alpha_tile_program.program, "Burn"); + AlphaTileDodgeBurnProgram { alpha_tile_program, dest_uniform, burn_uniform } + } +} + pub struct FilterBasicProgram where D: Device { pub program: D::Program, pub source_uniform: D::Uniform, diff --git a/resources/shaders/gl3/tile_alpha_dodgeburn.fs.glsl b/resources/shaders/gl3/tile_alpha_dodgeburn.fs.glsl new file mode 100644 index 00000000..d8501bde --- /dev/null +++ b/resources/shaders/gl3/tile_alpha_dodgeburn.fs.glsl @@ -0,0 +1,86 @@ +#version {{version}} +// Automatically generated from files in pathfinder/shaders/. Do not edit! + + + + + + + + + + + + + + +#extension GL_GOOGLE_include_directive : enable + +precision highp float; + +uniform int uBurn; + +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); +} + + +void main(){ + vec4 srcRGBA = sampleSrcColor(); + vec4 destRGBA = sampleDestColor(); + + vec3 dest = uBurn == 0 ? destRGBA . rgb : vec3(1.0)- destRGBA . rgb; + vec3 src = uBurn == 0 ? vec3(1.0)- srcRGBA . rgb : srcRGBA . rgb; + + bvec3 srcNonzero = notEqual(src, vec3(0.0)); + vec3 blended = min(vec3(srcNonzero . x ? dest . x / src . x : 1.0, + srcNonzero . y ? dest . y / src . y : 1.0, + srcNonzero . z ? dest . z / src . z : 1.0), + vec3(1.0)); + if(uBurn != 0) + blended = vec3(1.0)- blended; + + oFragColor = blendColors(destRGBA, srcRGBA, blended); +} + diff --git a/resources/shaders/metal/tile_alpha_dodgeburn.fs.metal b/resources/shaders/metal/tile_alpha_dodgeburn.fs.metal new file mode 100644 index 00000000..b165485e --- /dev/null +++ b/resources/shaders/metal/tile_alpha_dodgeburn.fs.metal @@ -0,0 +1,114 @@ +// 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* uBurn [[id(7)]]; +}; + +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 blendColors(thread const float4& destRGBA, thread const float4& srcRGBA, thread const float3& blendedRGB) +{ + return float4(((srcRGBA.xyz * (srcRGBA.w * (1.0 - destRGBA.w))) + (blendedRGB * (srcRGBA.w * destRGBA.w))) + (destRGBA.xyz * ((1.0 - srcRGBA.w) * destRGBA.w)), 1.0); +} + +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); + float3 _118; + if ((*spvDescriptorSet0.uBurn) == 0) + { + _118 = destRGBA.xyz; + } + else + { + _118 = float3(1.0) - destRGBA.xyz; + } + float3 dest = _118; + float3 _132; + if ((*spvDescriptorSet0.uBurn) == 0) + { + _132 = float3(1.0) - srcRGBA.xyz; + } + else + { + _132 = srcRGBA.xyz; + } + float3 src = _132; + bool3 srcNonzero = src != float3(0.0); + float _153; + if (srcNonzero.x) + { + _153 = dest.x / src.x; + } + else + { + _153 = 1.0; + } + float _166; + if (srcNonzero.y) + { + _166 = dest.y / src.y; + } + else + { + _166 = 1.0; + } + float _179; + if (srcNonzero.z) + { + _179 = dest.z / src.z; + } + else + { + _179 = 1.0; + } + float3 blended = fast::min(float3(_153, _166, _179), float3(1.0)); + if ((*spvDescriptorSet0.uBurn) != 0) + { + blended = float3(1.0) - blended; + } + float4 param = destRGBA; + float4 param_1 = srcRGBA; + float3 param_2 = blended; + out.oFragColor = blendColors(param, param_1, param_2); + return out; +} + diff --git a/shaders/Makefile b/shaders/Makefile index a842b02f..2f438e67 100644 --- a/shaders/Makefile +++ b/shaders/Makefile @@ -23,6 +23,7 @@ SHADERS=\ stencil.vs.glsl \ tile_alpha.fs.glsl \ tile_alpha.vs.glsl \ + tile_alpha_dodgeburn.fs.glsl \ tile_alpha_hsl.fs.glsl \ tile_alpha_overlay.fs.glsl \ tile_copy.fs.glsl \ diff --git a/shaders/tile_alpha_dodgeburn.fs.glsl b/shaders/tile_alpha_dodgeburn.fs.glsl new file mode 100644 index 00000000..568f6979 --- /dev/null +++ b/shaders/tile_alpha_dodgeburn.fs.glsl @@ -0,0 +1,41 @@ +#version 330 + +// pathfinder/shaders/tile_alpha_dodgeburn.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. + +// Color dodge and color burn blend modes. + +#extension GL_GOOGLE_include_directive : enable + +precision highp float; + +uniform int uBurn; + +out vec4 oFragColor; + +#include "tile_alpha_sample.inc.glsl" + +void main() { + vec4 srcRGBA = sampleSrcColor(); + vec4 destRGBA = sampleDestColor(); + + vec3 dest = uBurn == 0 ? destRGBA.rgb : vec3(1.0) - destRGBA.rgb; + vec3 src = uBurn == 0 ? vec3(1.0) - srcRGBA.rgb : srcRGBA.rgb; + + bvec3 srcNonzero = notEqual(src, vec3(0.0)); + vec3 blended = min(vec3(srcNonzero.x ? dest.x / src.x : 1.0, + srcNonzero.y ? dest.y / src.y : 1.0, + srcNonzero.z ? dest.z / src.z : 1.0), + vec3(1.0)); + if (uBurn != 0) + blended = vec3(1.0) - blended; + + oFragColor = blendColors(destRGBA, srcRGBA, blended); +}