@@ -179,7 +179,7 @@ static std::optional<Entity> AdvancedBlend(
179179 entity.GetBlendMode (), entity.GetStencilDepth ());
180180}
181181
182- std::optional<Entity> BlendFilterContents::CreateForegroundBlend (
182+ std::optional<Entity> BlendFilterContents::CreateForegroundAdvancedBlend (
183183 const std::shared_ptr<FilterInput>& input,
184184 const ContentContext& renderer,
185185 const Entity& entity,
@@ -318,21 +318,145 @@ std::optional<Entity> BlendFilterContents::CreateForegroundBlend(
318318
319319 auto contents = AnonymousContents::Make (render_proc, coverage_proc);
320320
321- // If there is pending opacity but it was not absorbed by this entity, we have
322- // to convert this back to a snapshot so it can be passed on. This generally
323- // implies that there is another filter about to run, so we'd perform this
324- // operation anyway.
325- auto potential_opacity = alpha.value_or (1.0 ) * dst_snapshot->opacity ;
326- if (!absorb_opacity && potential_opacity < 1.0 ) {
327- auto result_snapshot = contents->RenderToSnapshot (renderer, entity);
328- if (!result_snapshot.has_value ()) {
329- return std::nullopt ;
330- }
331- result_snapshot->opacity = potential_opacity;
332- return Entity::FromSnapshot (result_snapshot.value (), entity.GetBlendMode (),
321+ Entity sub_entity;
322+ sub_entity.SetContents (std::move (contents));
323+ sub_entity.SetStencilDepth (entity.GetStencilDepth ());
324+ sub_entity.SetTransformation (entity.GetTransformation ());
325+
326+ return sub_entity;
327+ }
328+
329+ constexpr std::array<std::array<Scalar, 5 >, 15 > kPorterDuffCoefficients = {{
330+ {0 , 0 , 0 , 0 , 0 }, // Clear
331+ {1 , 0 , 0 , 0 , 0 }, // Source
332+ {0 , 0 , 1 , 0 , 0 }, // Destination
333+ {1 , 0 , 1 , -1 , 0 }, // SourceOver
334+ {1 , -1 , 1 , 0 , 0 }, // DestinationOver
335+ {0 , 1 , 0 , 0 , 0 }, // SourceIn
336+ {0 , 0 , 0 , 1 , 0 }, // DestinationIn
337+ {1 , -1 , 0 , 0 , 0 }, // SourceOut
338+ {0 , 0 , 1 , -1 , 0 }, // DestinationOut
339+ {0 , 1 , 1 , -1 , 0 }, // SourceATop
340+ {1 , -1 , 0 , 1 , 0 }, // DestinationATop
341+ {1 , -1 , 1 , -1 , 0 }, // Xor
342+ {1 , 0 , 1 , 0 , 0 }, // Plus
343+ {0 , 0 , 0 , 0 , 1 }, // Modulate
344+ {0 , 0 , 1 , 0 , -1 }, // Screen
345+ }};
346+
347+ std::optional<Entity> BlendFilterContents::CreateForegroundPorterDuffBlend (
348+ const std::shared_ptr<FilterInput>& input,
349+ const ContentContext& renderer,
350+ const Entity& entity,
351+ const Rect& coverage,
352+ Color foreground_color,
353+ BlendMode blend_mode,
354+ std::optional<Scalar> alpha,
355+ bool absorb_opacity) const {
356+ auto dst_snapshot = input->GetSnapshot (renderer, entity);
357+ if (!dst_snapshot.has_value ()) {
358+ return std::nullopt ;
359+ }
360+
361+ if (blend_mode == BlendMode::kClear ) {
362+ return std::nullopt ;
363+ }
364+
365+ if (blend_mode == BlendMode::kDestination ) {
366+ return Entity::FromSnapshot (dst_snapshot, entity.GetBlendMode (),
333367 entity.GetStencilDepth ());
334368 }
335369
370+ if (blend_mode == BlendMode::kSource ) {
371+ auto contents = std::make_shared<SolidColorContents>();
372+ contents->SetGeometry (Geometry::MakeRect (coverage));
373+ contents->SetColor (foreground_color);
374+
375+ Entity foreground_entity;
376+ foreground_entity.SetBlendMode (entity.GetBlendMode ());
377+ foreground_entity.SetStencilDepth (entity.GetStencilDepth ());
378+ foreground_entity.SetContents (std::move (contents));
379+ return foreground_entity;
380+ }
381+
382+ RenderProc render_proc = [foreground_color, coverage, dst_snapshot,
383+ blend_mode, absorb_opacity, alpha](
384+ const ContentContext& renderer,
385+ const Entity& entity, RenderPass& pass) -> bool {
386+ using VS = PorterDuffBlendPipeline::VertexShader;
387+ using FS = PorterDuffBlendPipeline::FragmentShader;
388+
389+ auto & host_buffer = pass.GetTransientsBuffer ();
390+
391+ auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs (coverage);
392+ if (!maybe_dst_uvs.has_value ()) {
393+ return false ;
394+ }
395+ auto dst_uvs = maybe_dst_uvs.value ();
396+
397+ auto size = coverage.size ;
398+ auto origin = coverage.origin ;
399+ VertexBufferBuilder<VS::PerVertexData> vtx_builder;
400+ vtx_builder.AddVertices ({
401+ {origin, dst_uvs[0 ]},
402+ {Point (origin.x + size.width , origin.y ), dst_uvs[1 ]},
403+ {Point (origin.x + size.width , origin.y + size.height ), dst_uvs[3 ]},
404+ {origin, dst_uvs[0 ]},
405+ {Point (origin.x + size.width , origin.y + size.height ), dst_uvs[3 ]},
406+ {Point (origin.x , origin.y + size.height ), dst_uvs[2 ]},
407+ });
408+ auto vtx_buffer = vtx_builder.CreateVertexBuffer (host_buffer);
409+
410+ Command cmd;
411+ cmd.label = " Foreground PorterDuff Blend Filter" ;
412+ cmd.BindVertices (vtx_buffer);
413+ cmd.stencil_reference = entity.GetStencilDepth ();
414+ auto options = OptionsFromPass (pass);
415+ cmd.pipeline = renderer.GetPorterDuffBlendPipeline (options);
416+
417+ FS::FragInfo frag_info;
418+ VS::FrameInfo frame_info;
419+
420+ auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor ;
421+ if (renderer.GetDeviceCapabilities ().SupportsDecalTileMode ()) {
422+ dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal ;
423+ dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal ;
424+ }
425+ auto dst_sampler = renderer.GetContext ()->GetSamplerLibrary ()->GetSampler (
426+ dst_sampler_descriptor);
427+ FS::BindTextureSamplerDst (cmd, dst_snapshot->texture , dst_sampler);
428+ frame_info.texture_sampler_y_coord_scale =
429+ dst_snapshot->texture ->GetYCoordScale ();
430+
431+ frag_info.color = foreground_color.Premultiply ();
432+ frag_info.input_alpha =
433+ absorb_opacity ? dst_snapshot->opacity * alpha.value_or (1.0 ) : 1.0 ;
434+
435+ auto blend_coefficients =
436+ kPorterDuffCoefficients [static_cast <int >(blend_mode)];
437+ frag_info.src_coeff = blend_coefficients[0 ];
438+ frag_info.src_coeff_dst_alpha = blend_coefficients[1 ];
439+ frag_info.dst_coeff = blend_coefficients[2 ];
440+ frag_info.dst_coeff_src_alpha = blend_coefficients[3 ];
441+ frag_info.dst_coeff_src_color = blend_coefficients[4 ];
442+
443+ FS::BindFragInfo (cmd, host_buffer.EmplaceUniform (frag_info));
444+
445+ frame_info.mvp = Matrix::MakeOrthographic (pass.GetRenderTargetSize ());
446+
447+ auto uniform_view = host_buffer.EmplaceUniform (frame_info);
448+ VS::BindFrameInfo (cmd, uniform_view);
449+
450+ return pass.AddCommand (cmd);
451+ };
452+
453+ CoverageProc coverage_proc =
454+ [coverage](const Entity& entity) -> std::optional<Rect> {
455+ return coverage;
456+ };
457+
458+ auto contents = AnonymousContents::Make (render_proc, coverage_proc);
459+
336460 Entity sub_entity;
337461 sub_entity.SetContents (std::move (contents));
338462 sub_entity.SetStencilDepth (entity.GetStencilDepth ());
@@ -531,17 +655,23 @@ std::optional<Entity> BlendFilterContents::RenderFilter(
531655 }
532656
533657 if (blend_mode_ <= Entity::kLastPipelineBlendMode ) {
658+ if (inputs.size () == 1 && foreground_color_.has_value () &&
659+ GetAbsorbOpacity ()) {
660+ return CreateForegroundPorterDuffBlend (
661+ inputs[0 ], renderer, entity, coverage, foreground_color_.value (),
662+ blend_mode_, GetAlpha (), GetAbsorbOpacity ());
663+ }
534664 return PipelineBlend (inputs, renderer, entity, coverage, blend_mode_,
535665 foreground_color_, GetAbsorbOpacity (), GetAlpha ());
536666 }
537667
538668 if (blend_mode_ <= Entity::kLastAdvancedBlendMode ) {
539- if (inputs.size () == 1 && foreground_color_.has_value ()) {
540- return CreateForegroundBlend (inputs[0 ], renderer, entity, coverage,
541- foreground_color_.value (), blend_mode_,
542- GetAlpha (), GetAbsorbOpacity ());
669+ if (inputs.size () == 1 && foreground_color_.has_value () &&
670+ GetAbsorbOpacity ()) {
671+ return CreateForegroundAdvancedBlend (
672+ inputs[0 ], renderer, entity, coverage, foreground_color_.value (),
673+ blend_mode_, GetAlpha (), GetAbsorbOpacity ());
543674 }
544-
545675 return advanced_blend_proc_ (inputs, renderer, entity, coverage,
546676 foreground_color_, GetAbsorbOpacity (),
547677 GetAlpha ());
0 commit comments