Implement color dodge and color burn blend modes

This commit is contained in:
Patrick Walton 2020-02-25 11:37:42 -08:00
parent 5b228ed825
commit 02012431ca
8 changed files with 317 additions and 9 deletions

View File

@ -534,6 +534,8 @@ pub enum CompositeOperation {
Overlay, Overlay,
Darken, Darken,
Lighten, Lighten,
ColorDodge,
ColorBurn,
HardLight, HardLight,
Hue, Hue,
Saturation, Saturation,
@ -555,6 +557,8 @@ impl CompositeOperation {
CompositeOperation::Overlay => BlendMode::Overlay, CompositeOperation::Overlay => BlendMode::Overlay,
CompositeOperation::Darken => BlendMode::Darken, CompositeOperation::Darken => BlendMode::Darken,
CompositeOperation::Lighten => BlendMode::Lighten, CompositeOperation::Lighten => BlendMode::Lighten,
CompositeOperation::ColorDodge => BlendMode::ColorDodge,
CompositeOperation::ColorBurn => BlendMode::ColorBurn,
CompositeOperation::HardLight => BlendMode::HardLight, CompositeOperation::HardLight => BlendMode::HardLight,
CompositeOperation::Hue => BlendMode::Hue, CompositeOperation::Hue => BlendMode::Hue,
CompositeOperation::Saturation => BlendMode::Saturation, CompositeOperation::Saturation => BlendMode::Saturation,

View File

@ -81,6 +81,10 @@ pub enum BlendMode {
HardLight, HardLight,
Overlay, Overlay,
// Dodge/burn
ColorDodge,
ColorBurn,
// HSL // HSL
Hue, Hue,
Saturation, Saturation,
@ -118,11 +122,13 @@ impl BlendMode {
BlendMode::Xor | BlendMode::Xor |
BlendMode::Lighter | BlendMode::Lighter |
BlendMode::Lighten | BlendMode::Lighten |
BlendMode::Darken |
BlendMode::Multiply | BlendMode::Multiply |
BlendMode::Screen | BlendMode::Screen |
BlendMode::HardLight | BlendMode::HardLight |
BlendMode::Overlay | BlendMode::Overlay |
BlendMode::Darken | BlendMode::ColorDodge |
BlendMode::ColorBurn |
BlendMode::Hue | BlendMode::Hue |
BlendMode::Saturation | BlendMode::Saturation |
BlendMode::Color | BlendMode::Color |

View File

@ -10,13 +10,13 @@
use crate::gpu::debug::DebugUIPresenter; use crate::gpu::debug::DebugUIPresenter;
use crate::gpu::options::{DestFramebuffer, RendererOptions}; use crate::gpu::options::{DestFramebuffer, RendererOptions};
use crate::gpu::shaders::{AlphaTileHSLProgram, AlphaTileOverlayProgram, AlphaTileProgram}; use crate::gpu::shaders::{AlphaTileDodgeBurnProgram, AlphaTileHSLProgram, AlphaTileOverlayProgram};
use crate::gpu::shaders::{AlphaTileVertexArray, CopyTileProgram, CopyTileVertexArray, FillProgram}; use crate::gpu::shaders::{AlphaTileProgram, AlphaTileVertexArray, CopyTileProgram};
use crate::gpu::shaders::{FillVertexArray, FilterBasicProgram, FilterBasicVertexArray}; use crate::gpu::shaders::{CopyTileVertexArray, FillProgram, FillVertexArray, FilterBasicProgram};
use crate::gpu::shaders::{FilterTextProgram, FilterTextVertexArray, MAX_FILLS_PER_BATCH}; use crate::gpu::shaders::{FilterBasicVertexArray, FilterTextProgram, FilterTextVertexArray};
use crate::gpu::shaders::{MaskTileProgram, MaskTileVertexArray, ReprojectionProgram}; use crate::gpu::shaders::{MAX_FILLS_PER_BATCH, MaskTileProgram, MaskTileVertexArray};
use crate::gpu::shaders::{ReprojectionVertexArray, SolidTileProgram, SolidTileVertexArray}; use crate::gpu::shaders::{ReprojectionProgram, ReprojectionVertexArray, SolidTileProgram};
use crate::gpu::shaders::{StencilProgram, StencilVertexArray}; use crate::gpu::shaders::{SolidTileVertexArray, StencilProgram, StencilVertexArray};
use crate::gpu_data::{AlphaTile, FillBatchPrimitive, MaskTile, PaintData, PaintPageContents}; use crate::gpu_data::{AlphaTile, FillBatchPrimitive, MaskTile, PaintData, PaintPageContents};
use crate::gpu_data::{PaintPageId, RenderCommand, SolidTileVertex}; use crate::gpu_data::{PaintPageId, RenderCommand, SolidTileVertex};
use crate::options::BoundingQuad; use crate::options::BoundingQuad;
@ -78,6 +78,7 @@ where
solid_tile_program: SolidTileProgram<D>, solid_tile_program: SolidTileProgram<D>,
alpha_tile_program: AlphaTileProgram<D>, alpha_tile_program: AlphaTileProgram<D>,
alpha_tile_overlay_program: AlphaTileOverlayProgram<D>, alpha_tile_overlay_program: AlphaTileOverlayProgram<D>,
alpha_tile_dodgeburn_program: AlphaTileDodgeBurnProgram<D>,
alpha_tile_hsl_program: AlphaTileHSLProgram<D>, alpha_tile_hsl_program: AlphaTileHSLProgram<D>,
mask_winding_tile_vertex_array: MaskTileVertexArray<D>, mask_winding_tile_vertex_array: MaskTileVertexArray<D>,
mask_evenodd_tile_vertex_array: MaskTileVertexArray<D>, mask_evenodd_tile_vertex_array: MaskTileVertexArray<D>,
@ -85,6 +86,7 @@ where
solid_tile_vertex_array: SolidTileVertexArray<D>, solid_tile_vertex_array: SolidTileVertexArray<D>,
alpha_tile_vertex_array: AlphaTileVertexArray<D>, alpha_tile_vertex_array: AlphaTileVertexArray<D>,
alpha_tile_overlay_vertex_array: AlphaTileVertexArray<D>, alpha_tile_overlay_vertex_array: AlphaTileVertexArray<D>,
alpha_tile_dodgeburn_vertex_array: AlphaTileVertexArray<D>,
alpha_tile_hsl_vertex_array: AlphaTileVertexArray<D>, alpha_tile_hsl_vertex_array: AlphaTileVertexArray<D>,
area_lut_texture: D::Texture, area_lut_texture: D::Texture,
alpha_tile_vertex_buffer: D::Buffer, alpha_tile_vertex_buffer: D::Buffer,
@ -157,6 +159,7 @@ where
let solid_tile_program = SolidTileProgram::new(&device, resources); let solid_tile_program = SolidTileProgram::new(&device, resources);
let alpha_tile_program = AlphaTileProgram::new(&device, resources); let alpha_tile_program = AlphaTileProgram::new(&device, resources);
let alpha_tile_overlay_program = AlphaTileOverlayProgram::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 alpha_tile_hsl_program = AlphaTileHSLProgram::new(&device, resources);
let filter_basic_program = FilterBasicProgram::new(&device, resources); let filter_basic_program = FilterBasicProgram::new(&device, resources);
let filter_text_program = FilterTextProgram::new(&device, resources); let filter_text_program = FilterTextProgram::new(&device, resources);
@ -217,6 +220,12 @@ where
&alpha_tile_vertex_buffer, &alpha_tile_vertex_buffer,
&quads_vertex_indices_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( let alpha_tile_hsl_vertex_array = AlphaTileVertexArray::new(
&device, &device,
&alpha_tile_hsl_program.alpha_tile_program, &alpha_tile_hsl_program.alpha_tile_program,
@ -285,6 +294,7 @@ where
solid_tile_program, solid_tile_program,
alpha_tile_program, alpha_tile_program,
alpha_tile_overlay_program, alpha_tile_overlay_program,
alpha_tile_dodgeburn_program,
alpha_tile_hsl_program, alpha_tile_hsl_program,
mask_winding_tile_vertex_array, mask_winding_tile_vertex_array,
mask_evenodd_tile_vertex_array, mask_evenodd_tile_vertex_array,
@ -292,6 +302,7 @@ where
solid_tile_vertex_array, solid_tile_vertex_array,
alpha_tile_vertex_array, alpha_tile_vertex_array,
alpha_tile_overlay_vertex_array, alpha_tile_overlay_vertex_array,
alpha_tile_dodgeburn_vertex_array,
alpha_tile_hsl_vertex_array, alpha_tile_hsl_vertex_array,
area_lut_texture, area_lut_texture,
alpha_tile_vertex_buffer, alpha_tile_vertex_buffer,
@ -728,6 +739,10 @@ where
(&self.alpha_tile_overlay_program.alpha_tile_program, (&self.alpha_tile_overlay_program.alpha_tile_program,
&self.alpha_tile_overlay_vertex_array) &self.alpha_tile_overlay_vertex_array)
} }
BlendModeProgram::DodgeBurn => {
(&self.alpha_tile_dodgeburn_program.alpha_tile_program,
&self.alpha_tile_dodgeburn_vertex_array)
}
BlendModeProgram::HSL => { BlendModeProgram::HSL => {
(&self.alpha_tile_hsl_program.alpha_tile_program, (&self.alpha_tile_hsl_program.alpha_tile_program,
&self.alpha_tile_hsl_vertex_array) &self.alpha_tile_hsl_vertex_array)
@ -765,6 +780,11 @@ where
BlendModeProgram::Overlay => { BlendModeProgram::Overlay => {
self.set_uniforms_for_overlay_blend_mode(&mut textures, &mut uniforms, blend_mode); 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 => { BlendModeProgram::HSL => {
self.set_uniforms_for_hsl_blend_mode(&mut textures, &mut uniforms, blend_mode); 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))); 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, fn set_uniforms_for_hsl_blend_mode<'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)>,
@ -1448,6 +1481,8 @@ impl BlendModeExt for BlendMode {
BlendMode::Screen | BlendMode::Screen |
BlendMode::HardLight | BlendMode::HardLight |
BlendMode::Overlay | BlendMode::Overlay |
BlendMode::ColorDodge |
BlendMode::ColorBurn |
BlendMode::Hue | BlendMode::Hue |
BlendMode::Saturation | BlendMode::Saturation |
BlendMode::Color | BlendMode::Color |
@ -1463,6 +1498,7 @@ impl BlendModeExt for BlendMode {
pub(crate) enum BlendModeProgram { pub(crate) enum BlendModeProgram {
Regular, Regular,
Overlay, Overlay,
DodgeBurn,
HSL, HSL,
} }
@ -1482,6 +1518,8 @@ impl BlendModeProgram {
BlendMode::Screen | BlendMode::Screen |
BlendMode::HardLight | BlendMode::HardLight |
BlendMode::Overlay => BlendModeProgram::Overlay, BlendMode::Overlay => BlendModeProgram::Overlay,
BlendMode::ColorDodge |
BlendMode::ColorBurn => BlendModeProgram::DodgeBurn,
BlendMode::Hue | BlendMode::Hue |
BlendMode::Saturation | BlendMode::Saturation |
BlendMode::Color | BlendMode::Color |
@ -1492,7 +1530,9 @@ impl BlendModeProgram {
pub(crate) fn needs_readable_framebuffer(self) -> bool { pub(crate) fn needs_readable_framebuffer(self) -> bool {
match self { match self {
BlendModeProgram::Regular => false, BlendModeProgram::Regular => false,
BlendModeProgram::Overlay | BlendModeProgram::HSL => true, BlendModeProgram::Overlay |
BlendModeProgram::DodgeBurn |
BlendModeProgram::HSL => true,
} }
} }
} }

View File

@ -484,6 +484,22 @@ impl<D> AlphaTileOverlayProgram<D> where D: Device {
} }
} }
pub struct AlphaTileDodgeBurnProgram<D> where D: Device {
pub alpha_tile_program: AlphaTileProgram<D>,
pub dest_uniform: D::Uniform,
pub burn_uniform: D::Uniform,
}
impl<D> AlphaTileDodgeBurnProgram<D> where D: Device {
pub fn new(device: &D, resources: &dyn ResourceLoader) -> AlphaTileDodgeBurnProgram<D> {
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<D> where D: Device { pub struct FilterBasicProgram<D> where D: Device {
pub program: D::Program, pub program: D::Program,
pub source_uniform: D::Uniform, pub source_uniform: D::Uniform,

View File

@ -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);
}

View File

@ -0,0 +1,114 @@
// Automatically generated from files in pathfinder/shaders/. Do not edit!
#pragma clang diagnostic ignored "-Wmissing-prototypes"
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct spvDescriptorSetBuffer0
{
texture2d<float> uStencilTexture [[id(0)]];
sampler uStencilTextureSmplr [[id(1)]];
texture2d<float> uPaintTexture [[id(2)]];
sampler uPaintTextureSmplr [[id(3)]];
constant float2* uFramebufferSize [[id(4)]];
texture2d<float> 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<float> uStencilTexture, thread const sampler uStencilTextureSmplr, thread float2& vMaskTexCoord, thread texture2d<float> 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<float> 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;
}

View File

@ -23,6 +23,7 @@ SHADERS=\
stencil.vs.glsl \ stencil.vs.glsl \
tile_alpha.fs.glsl \ tile_alpha.fs.glsl \
tile_alpha.vs.glsl \ tile_alpha.vs.glsl \
tile_alpha_dodgeburn.fs.glsl \
tile_alpha_hsl.fs.glsl \ tile_alpha_hsl.fs.glsl \
tile_alpha_overlay.fs.glsl \ tile_alpha_overlay.fs.glsl \
tile_copy.fs.glsl \ tile_copy.fs.glsl \

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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);
}