diff --git a/engine/src/flutter/impeller/display_list/aiks_dl_runtime_effect_unittests.cc b/engine/src/flutter/impeller/display_list/aiks_dl_runtime_effect_unittests.cc index 9a55d9f6ea2..c24d7386482 100644 --- a/engine/src/flutter/impeller/display_list/aiks_dl_runtime_effect_unittests.cc +++ b/engine/src/flutter/impeller/display_list/aiks_dl_runtime_effect_unittests.cc @@ -473,5 +473,56 @@ TEST_P(AiksTest, ClippedBackdropFilterWithShader) { ASSERT_TRUE(OpenPlaygroundHere(builder.Build())); } +TEST_P(AiksTest, RuntimeEffectImageFilterRotated) { + auto image = DlImageImpeller::Make(CreateTextureForFixture("kalimba.jpg")); + auto size = image->GetBounds().GetSize(); + + struct FragUniforms { + Size size; + } frag_uniforms = {.size = Size(size.width, size.height)}; + auto uniform_data = std::make_shared>(); + uniform_data->resize(sizeof(FragUniforms)); + memcpy(uniform_data->data(), &frag_uniforms, sizeof(FragUniforms)); + + auto runtime_stages_result = OpenAssetAsRuntimeStage("gradient.frag.iplr"); + ABSL_ASSERT_OK(runtime_stages_result); + std::shared_ptr runtime_stage = + runtime_stages_result + .value()[PlaygroundBackendToRuntimeStageBackend(GetBackend())]; + ASSERT_TRUE(runtime_stage); + ASSERT_TRUE(runtime_stage->IsDirty()); + + std::vector> sampler_inputs = { + nullptr, + }; + + auto runtime_filter = DlImageFilter::MakeRuntimeEffect( + DlRuntimeEffectImpeller::Make(runtime_stage), sampler_inputs, + uniform_data); + + Scalar rotation = 45; + + auto callback = [&]() -> sk_sp { + if (AiksTest::ImGuiBegin("Controls", nullptr, + ImGuiWindowFlags_AlwaysAutoResize)) { + ImGui::SliderFloat("rotation", &rotation, 0, 360); + ImGui::End(); + } + DisplayListBuilder builder; + builder.Translate(size.width * 0.5, size.height * 0.5); + builder.Rotate(rotation); + builder.Translate(-size.width * 0.5, -size.height * 0.5); + + DlPaint paint; + paint.setImageFilter(runtime_filter); + builder.DrawImage(image, DlPoint(0.0, 0.0), + DlImageSampling::kNearestNeighbor, &paint); + + return builder.Build(); + }; + + ASSERT_TRUE(OpenPlaygroundHere(callback)); +} + } // namespace testing } // namespace impeller diff --git a/engine/src/flutter/impeller/renderer/snapshot.h b/engine/src/flutter/impeller/renderer/snapshot.h index 36d3091e3eb..07f101a6f1e 100644 --- a/engine/src/flutter/impeller/renderer/snapshot.h +++ b/engine/src/flutter/impeller/renderer/snapshot.h @@ -45,11 +45,15 @@ struct Snapshot { /// capture the padding. bool needs_rasterization_for_runtime_effects = false; - /// Any snapshot that is scaled should rerasterize because we should be + /// Any snapshot that is scaled should re-rasterize because we should be /// performing the RuntimeEffect at the resolution of the screen, not the /// scaled up or scaled down version of the snapshot. bool ShouldRasterizeForRuntimeEffects() const { - return !transform.IsTranslationOnly() || + // If the transform has a rotation we don't re-rasterize because we'll lose + // the rotation. + // TODO(tbd): We should re-rasterize scaled and rotated snapshots. + return (!transform.IsTranslationOnly() && + transform.IsTranslationScaleOnly()) || needs_rasterization_for_runtime_effects; }