use valo_dl::{BlurStyle, ClipOp, ColorFilter, RuntimeShader};
use valo_geometry::{Matrix, Rect};
use crate::pipelines::{AdvancedBlend, Blend, PipelineBlend};
use super::draw_state::DrawState;
use super::drawing::Drawing;
use super::emit::{Cover, Entity, Role};
use super::shading::Shading;
use super::snapshot::Snapshot;
use super::source::Shape;
use super::Planner;
pub(super) enum FilterOutput<'a> {
Snapshot(Snapshot),
Recolour {
input: Snapshot,
filter: ColorFilter,
},
Merge {
first: Snapshot,
second: Snapshot,
merge: Merge,
},
Clipped { blur: Snapshot, clip: StyleClip<'a> },
Solid { sharp: Snapshot, blur: Snapshot },
Runtime(RuntimeOutput),
}
pub(super) struct RuntimeOutput {
pub shader: RuntimeShader,
pub source: Snapshot,
pub region: Rect,
pub effect_transform: Matrix,
}
impl RuntimeOutput {
pub fn tree_to_local(&self) -> Matrix {
self.effect_transform
.invert()
.unwrap_or(Matrix::from_affine(0.0, 0.0, 0.0, 0.0, 0.0, 0.0))
}
pub fn local_bounds(&self) -> Rect {
self.tree_to_local().map_rect(&self.region)
}
}
pub(super) struct StyleClip<'a> {
pub shape: Shape<'a>,
pub op: ClipOp,
pub transform: Matrix,
}
#[derive(Clone, Copy)]
pub(super) enum Merge {
DropShadow,
BlurStyle(BlurStyle),
}
impl FilterOutput<'_> {
pub fn coverage(&self) -> Rect {
match self {
FilterOutput::Snapshot(snapshot) => snapshot.placement.coverage(),
FilterOutput::Recolour { input, .. } => input.placement.coverage(),
FilterOutput::Merge { first, second, .. } => first
.placement
.coverage()
.union(&second.placement.coverage()),
FilterOutput::Clipped { blur, .. } => blur.placement.coverage(),
FilterOutput::Solid { sharp, blur } => {
sharp.placement.coverage().union(&blur.placement.coverage())
}
FilterOutput::Runtime(output) => output.region,
}
}
}
impl Planner<'_> {
pub(super) fn composite_output(
&mut self,
output: FilterOutput<'_>,
blend: Blend,
at: &DrawState,
) {
match blend {
Blend::Fixed(blend) => self.drawing().draw_filter_output(&output, blend, at),
Blend::ReadsDestination(mode) => self.composite_blended(output, mode, at),
}
}
fn composite_blended(&mut self, output: FilterOutput<'_>, mode: AdvancedBlend, at: &DrawState) {
let quad = output.coverage();
let (source, alpha) = self.materialize_for_blend(output, at.alpha);
let destination = self.split_for_copy(at.z);
let composite = DrawState { alpha, ..*at };
self.drawing()
.draw_blended_snapshot(&source, &quad, &destination.view, mode, &composite);
}
pub(super) fn snapshot_output(&mut self, output: FilterOutput<'_>) -> Snapshot {
match output {
FilterOutput::Snapshot(snapshot) => snapshot,
FilterOutput::Recolour { input, filter } => {
self.filter_passes().push_recolour(&input, filter, 1.0)
}
FilterOutput::Merge {
first,
second,
merge,
} => self.filter_passes().push_merge(&first, &second, merge),
FilterOutput::Runtime(output) => self.filter_passes().push_runtime(&output),
output @ (FilterOutput::Clipped { .. } | FilterOutput::Solid { .. }) => self
.render_to_snapshot(&output.coverage(), |drawing, depths| {
let at = DrawState::in_layer(Matrix::IDENTITY, depths.first);
drawing.draw_filter_output(&output, PipelineBlend::SrcOver, &at);
}),
}
}
fn materialize_for_blend(&mut self, output: FilterOutput<'_>, alpha: f32) -> (Snapshot, f32) {
match output {
FilterOutput::Recolour { input, filter } => (
self.filter_passes().push_recolour(&input, filter, alpha),
1.0,
),
output => (self.snapshot_output(output), alpha),
}
}
}
impl Drawing<'_, '_> {
pub fn draw_filter_output(
&mut self,
output: &FilterOutput<'_>,
blend: PipelineBlend,
at: &DrawState,
) {
match output {
FilterOutput::Snapshot(snapshot) => self.draw_snapshot(snapshot, blend, at),
FilterOutput::Recolour { input, filter } => {
let shading = self.emit.recoloured_shading(input, *filter, at.alpha);
self.draw_output_quad(&input.placement.coverage(), shading, blend, at);
}
FilterOutput::Merge {
first,
second,
merge,
} => {
let quad = first
.placement
.coverage()
.union(&second.placement.coverage());
let shading = self.emit.merged_shading(first, second, *merge, at.alpha);
self.draw_output_quad(&quad, shading, blend, at);
}
FilterOutput::Clipped { blur, clip } => {
let shape_at = at.with_transform(at.transform.then(&clip.transform));
self.clip_to_shape(&clip.shape, clip.op, &shape_at);
self.draw_snapshot(blur, blend, at);
}
FilterOutput::Solid { sharp, blur } => {
self.draw_snapshot(sharp, blend, at);
self.draw_snapshot(blur, blend, at);
}
FilterOutput::Runtime(output) => {
let shading = self.emit.runtime_filter_shading(
&output.shader,
&output.source,
&output.local_bounds(),
&output.tree_to_local(),
&output.effect_transform,
at.alpha,
);
self.draw_output_quad(&output.region, shading, blend, at);
}
}
}
fn draw_snapshot(&mut self, snapshot: &Snapshot, blend: PipelineBlend, at: &DrawState) {
let shading = self.emit.texture_shading(snapshot, at.alpha);
self.draw_output_quad(&snapshot.placement.coverage(), shading, blend, at);
}
fn draw_output_quad(
&mut self,
quad: &Rect,
shading: Shading,
blend: PipelineBlend,
at: &DrawState,
) {
let entity = Entity {
stencil: None,
cover: Cover::Quad {
transform: at.transform,
rect: *quad,
},
role: Role::Fill,
shading,
blend,
z: at.z,
};
self.emit.push(self.context, entity);
}
pub fn draw_blended_snapshot(
&mut self,
source: &Snapshot,
quad: &Rect,
destination: &wgpu::TextureView,
mode: AdvancedBlend,
at: &DrawState,
) {
let size = self.context.area.size();
let shading = self
.emit
.blended_texture_shading(source, at.alpha, mode, destination, size);
self.draw_output_quad(quad, shading, PipelineBlend::SrcOver, at);
}
}