|
9 | 9 | #include <optional> |
10 | 10 |
|
11 | 11 | #include "impeller/core/formats.h" |
| 12 | +#include "impeller/entity/contents/anonymous_contents.h" |
12 | 13 | #include "impeller/entity/contents/content_context.h" |
13 | 14 | #include "impeller/entity/contents/contents.h" |
14 | 15 | #include "impeller/entity/contents/filters/inputs/filter_input.h" |
@@ -178,6 +179,168 @@ static std::optional<Entity> AdvancedBlend( |
178 | 179 | entity.GetBlendMode(), entity.GetStencilDepth()); |
179 | 180 | } |
180 | 181 |
|
| 182 | +std::optional<Entity> BlendFilterContents::CreateForegroundBlend( |
| 183 | + const std::shared_ptr<FilterInput>& input, |
| 184 | + const ContentContext& renderer, |
| 185 | + const Entity& entity, |
| 186 | + const Rect& coverage, |
| 187 | + Color foreground_color, |
| 188 | + BlendMode blend_mode, |
| 189 | + std::optional<Scalar> alpha, |
| 190 | + bool absorb_opacity) const { |
| 191 | + auto dst_snapshot = input->GetSnapshot(renderer, entity); |
| 192 | + if (!dst_snapshot.has_value()) { |
| 193 | + return std::nullopt; |
| 194 | + } |
| 195 | + |
| 196 | + RenderProc render_proc = [foreground_color, coverage, dst_snapshot, |
| 197 | + blend_mode, alpha, absorb_opacity]( |
| 198 | + const ContentContext& renderer, |
| 199 | + const Entity& entity, RenderPass& pass) -> bool { |
| 200 | + using VS = BlendScreenPipeline::VertexShader; |
| 201 | + using FS = BlendScreenPipeline::FragmentShader; |
| 202 | + |
| 203 | + auto& host_buffer = pass.GetTransientsBuffer(); |
| 204 | + |
| 205 | + auto maybe_dst_uvs = dst_snapshot->GetCoverageUVs(coverage); |
| 206 | + if (!maybe_dst_uvs.has_value()) { |
| 207 | + return false; |
| 208 | + } |
| 209 | + auto dst_uvs = maybe_dst_uvs.value(); |
| 210 | + |
| 211 | + auto size = coverage.size; |
| 212 | + auto origin = coverage.origin; |
| 213 | + VertexBufferBuilder<VS::PerVertexData> vtx_builder; |
| 214 | + vtx_builder.AddVertices({ |
| 215 | + {origin, dst_uvs[0], dst_uvs[0]}, |
| 216 | + {Point(origin.x + size.width, origin.y), dst_uvs[1], dst_uvs[1]}, |
| 217 | + {Point(origin.x + size.width, origin.y + size.height), dst_uvs[3], |
| 218 | + dst_uvs[3]}, |
| 219 | + {origin, dst_uvs[0], dst_uvs[0]}, |
| 220 | + {Point(origin.x + size.width, origin.y + size.height), dst_uvs[3], |
| 221 | + dst_uvs[3]}, |
| 222 | + {Point(origin.x, origin.y + size.height), dst_uvs[2], dst_uvs[2]}, |
| 223 | + }); |
| 224 | + auto vtx_buffer = vtx_builder.CreateVertexBuffer(host_buffer); |
| 225 | + |
| 226 | + Command cmd; |
| 227 | + cmd.label = "Foreground Advanced Blend Filter"; |
| 228 | + cmd.BindVertices(vtx_buffer); |
| 229 | + cmd.stencil_reference = entity.GetStencilDepth(); |
| 230 | + auto options = OptionsFromPass(pass); |
| 231 | + |
| 232 | + switch (blend_mode) { |
| 233 | + case BlendMode::kScreen: |
| 234 | + cmd.pipeline = renderer.GetBlendScreenPipeline(options); |
| 235 | + break; |
| 236 | + case BlendMode::kOverlay: |
| 237 | + cmd.pipeline = renderer.GetBlendOverlayPipeline(options); |
| 238 | + break; |
| 239 | + case BlendMode::kDarken: |
| 240 | + cmd.pipeline = renderer.GetBlendDarkenPipeline(options); |
| 241 | + break; |
| 242 | + case BlendMode::kLighten: |
| 243 | + cmd.pipeline = renderer.GetBlendLightenPipeline(options); |
| 244 | + break; |
| 245 | + case BlendMode::kColorDodge: |
| 246 | + cmd.pipeline = renderer.GetBlendColorDodgePipeline(options); |
| 247 | + break; |
| 248 | + case BlendMode::kColorBurn: |
| 249 | + cmd.pipeline = renderer.GetBlendColorBurnPipeline(options); |
| 250 | + break; |
| 251 | + case BlendMode::kHardLight: |
| 252 | + cmd.pipeline = renderer.GetBlendHardLightPipeline(options); |
| 253 | + break; |
| 254 | + case BlendMode::kSoftLight: |
| 255 | + cmd.pipeline = renderer.GetBlendSoftLightPipeline(options); |
| 256 | + break; |
| 257 | + case BlendMode::kDifference: |
| 258 | + cmd.pipeline = renderer.GetBlendDifferencePipeline(options); |
| 259 | + break; |
| 260 | + case BlendMode::kExclusion: |
| 261 | + cmd.pipeline = renderer.GetBlendExclusionPipeline(options); |
| 262 | + break; |
| 263 | + case BlendMode::kMultiply: |
| 264 | + cmd.pipeline = renderer.GetBlendMultiplyPipeline(options); |
| 265 | + break; |
| 266 | + case BlendMode::kHue: |
| 267 | + cmd.pipeline = renderer.GetBlendHuePipeline(options); |
| 268 | + break; |
| 269 | + case BlendMode::kSaturation: |
| 270 | + cmd.pipeline = renderer.GetBlendSaturationPipeline(options); |
| 271 | + break; |
| 272 | + case BlendMode::kColor: |
| 273 | + cmd.pipeline = renderer.GetBlendColorPipeline(options); |
| 274 | + break; |
| 275 | + case BlendMode::kLuminosity: |
| 276 | + cmd.pipeline = renderer.GetBlendLuminosityPipeline(options); |
| 277 | + break; |
| 278 | + default: |
| 279 | + return false; |
| 280 | + } |
| 281 | + |
| 282 | + FS::BlendInfo blend_info; |
| 283 | + VS::FrameInfo frame_info; |
| 284 | + |
| 285 | + auto dst_sampler_descriptor = dst_snapshot->sampler_descriptor; |
| 286 | + if (renderer.GetDeviceCapabilities().SupportsDecalTileMode()) { |
| 287 | + dst_sampler_descriptor.width_address_mode = SamplerAddressMode::kDecal; |
| 288 | + dst_sampler_descriptor.height_address_mode = SamplerAddressMode::kDecal; |
| 289 | + } |
| 290 | + auto dst_sampler = renderer.GetContext()->GetSamplerLibrary()->GetSampler( |
| 291 | + dst_sampler_descriptor); |
| 292 | + FS::BindTextureSamplerDst(cmd, dst_snapshot->texture, dst_sampler); |
| 293 | + frame_info.dst_y_coord_scale = dst_snapshot->texture->GetYCoordScale(); |
| 294 | + blend_info.dst_input_alpha = |
| 295 | + absorb_opacity ? dst_snapshot->opacity * alpha.value_or(1.0) : 1.0; |
| 296 | + |
| 297 | + blend_info.color_factor = 1; |
| 298 | + blend_info.color = foreground_color; |
| 299 | + // This texture will not be sampled from due to the color factor. But |
| 300 | + // this is present so that validation doesn't trip on a missing |
| 301 | + // binding. |
| 302 | + FS::BindTextureSamplerSrc(cmd, dst_snapshot->texture, dst_sampler); |
| 303 | + |
| 304 | + auto blend_uniform = host_buffer.EmplaceUniform(blend_info); |
| 305 | + FS::BindBlendInfo(cmd, blend_uniform); |
| 306 | + |
| 307 | + frame_info.mvp = Matrix::MakeOrthographic(pass.GetRenderTargetSize()); |
| 308 | + |
| 309 | + auto uniform_view = host_buffer.EmplaceUniform(frame_info); |
| 310 | + VS::BindFrameInfo(cmd, uniform_view); |
| 311 | + |
| 312 | + return pass.AddCommand(cmd); |
| 313 | + }; |
| 314 | + CoverageProc coverage_proc = |
| 315 | + [coverage](const Entity& entity) -> std::optional<Rect> { |
| 316 | + return coverage; |
| 317 | + }; |
| 318 | + |
| 319 | + auto contents = AnonymousContents::Make(render_proc, coverage_proc); |
| 320 | + |
| 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(), |
| 333 | + entity.GetStencilDepth()); |
| 334 | + } |
| 335 | + |
| 336 | + Entity sub_entity; |
| 337 | + sub_entity.SetContents(std::move(contents)); |
| 338 | + sub_entity.SetStencilDepth(entity.GetStencilDepth()); |
| 339 | + sub_entity.SetTransformation(entity.GetTransformation()); |
| 340 | + |
| 341 | + return sub_entity; |
| 342 | +} |
| 343 | + |
181 | 344 | static std::optional<Entity> PipelineBlend( |
182 | 345 | const FilterInput::Vector& inputs, |
183 | 346 | const ContentContext& renderer, |
@@ -373,6 +536,12 @@ std::optional<Entity> BlendFilterContents::RenderFilter( |
373 | 536 | } |
374 | 537 |
|
375 | 538 | 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()); |
| 543 | + } |
| 544 | + |
376 | 545 | return advanced_blend_proc_(inputs, renderer, entity, coverage, |
377 | 546 | foreground_color_, GetAbsorbOpacity(), |
378 | 547 | GetAlpha()); |
|
0 commit comments