1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <SkBitmap.h> 18 #include <SkBlendMode.h> 19 #include <SkCanvas.h> 20 #include <SkPaint.h> 21 #include <SkRefCnt.h> 22 #include <SkRuntimeEffect.h> 23 #include <SkSurface.h> 24 #include <include/gpu/ganesh/SkSurfaceGanesh.h> 25 #include <math.h> 26 27 #include "SkImageFilters.h" 28 #include "TestSceneBase.h" 29 #include "include/gpu/GpuTypes.h" // from Skia 30 #include "tests/common/BitmapAllocationTestUtils.h" 31 #include "utils/Color.h" 32 33 class WindowBlurKawase; 34 35 static TestScene::Registrar _WindowBlurKawase(TestScene::Info{ 36 "windowblurkawase", "Draws window Kawase blur", 37 TestScene::simpleCreateScene<WindowBlurKawase>}); 38 39 /** 40 * Simulates the multi-pass Kawase blur algorithm in 41 * frameworks/native/libs/renderengine/skia/filters/WindowBlurKawaseFilter.cpp 42 */ 43 class WindowBlurKawase : public TestScene { 44 private: 45 // Keep in sync with 46 // frameworks/native/libs/renderengine/skia/filters/KawaseBlurFilter.h 47 static constexpr uint32_t kMaxPasses = 4; 48 // Keep in sync with frameworks/native/libs/renderengine/skia/filters/BlurFilter.h 49 static constexpr float kInputScale = 0.25f; 50 51 static constexpr uint32_t kLoopLength = 500; 52 static constexpr uint32_t kMaxBlurRadius = 300; 53 sk_sp<SkRuntimeEffect> mBlurEffect; 54 55 sp<RenderNode> card; 56 sp<RenderNode> contentNode; 57 58 public: WindowBlurKawase()59 explicit WindowBlurKawase() { 60 SkString blurString( 61 "uniform shader child;" 62 "uniform float in_blurOffset;" 63 64 "half4 main(float2 xy) {" 65 "half4 c = child.eval(xy);" 66 "c += child.eval(xy + float2(+in_blurOffset, +in_blurOffset));" 67 "c += child.eval(xy + float2(+in_blurOffset, -in_blurOffset));" 68 "c += child.eval(xy + float2(-in_blurOffset, -in_blurOffset));" 69 "c += child.eval(xy + float2(-in_blurOffset, +in_blurOffset));" 70 "return half4(c.rgb * 0.2, 1.0);" 71 "}"); 72 73 auto [blurEffect, error] = SkRuntimeEffect::MakeForShader(blurString); 74 if (!blurEffect) { 75 LOG_ALWAYS_FATAL("RuntimeShader error: %s", error.c_str()); 76 } 77 mBlurEffect = std::move(blurEffect); 78 } 79 createContent(int width,int height,Canvas & canvas)80 void createContent(int width, int height, Canvas& canvas) override { 81 contentNode = TestUtils::createNode( 82 0, 0, width, height, [width, height](RenderProperties& props, Canvas& canvas) { 83 canvas.drawColor(Color::White, SkBlendMode::kSrcOver); 84 Paint paint; 85 paint.setColor(Color::Red_500); 86 canvas.drawRect(0, 0, width / 2, height / 2, paint); 87 paint.setColor(Color::Blue_500); 88 canvas.drawRect(width / 2, height / 2, width, height, paint); 89 }); 90 91 card = TestUtils::createNode( 92 0, 0, width, height, 93 [this](RenderProperties& props, Canvas& canvas) { blurFrame(canvas, 0); }); 94 canvas.drawRenderNode(card.get()); 95 } 96 doFrame(int frameNr)97 void doFrame(int frameNr) override { 98 int curFrame = frameNr % kLoopLength; 99 float blurRadius = 100 (sin((float)curFrame / kLoopLength * M_PI * 2) + 1) * 0.5 * kMaxBlurRadius; 101 TestUtils::recordNode( 102 *card, [this, blurRadius](Canvas& canvas) { blurFrame(canvas, blurRadius); }); 103 } 104 blurFrame(Canvas & canvas,float blurRadius)105 void blurFrame(Canvas& canvas, float blurRadius) { 106 if (blurRadius == 0) { 107 canvas.drawRenderNode(contentNode.get()); 108 return; 109 } 110 111 int width = canvas.width(); 112 int height = canvas.height(); 113 float tmpRadius = (float)blurRadius / 2.0f; 114 uint32_t numberOfPasses = std::min(kMaxPasses, (uint32_t)ceil(tmpRadius)); 115 float radiusByPasses = tmpRadius / (float)numberOfPasses; 116 117 SkRuntimeShaderBuilder blurBuilder(mBlurEffect); 118 119 sp<RenderNode> node = contentNode; 120 for (int i = 0; i < numberOfPasses; i++) { 121 blurBuilder.uniform("in_blurOffset") = radiusByPasses * kInputScale * (i + 1); 122 sk_sp<SkImageFilter> blurFilter = 123 SkImageFilters::RuntimeShader(blurBuilder, radiusByPasses, "child", nullptr); 124 // Also downsample the image in the first pass. 125 float canvasScale = i == 0 ? kInputScale : 1; 126 127 // Apply the blur effect as an image filter. 128 node = TestUtils::createNode( 129 0, 0, width * kInputScale, height * kInputScale, 130 [node, blurFilter, canvasScale](RenderProperties& props, Canvas& canvas) { 131 props.mutateLayerProperties().setImageFilter(blurFilter.get()); 132 canvas.scale(canvasScale, canvasScale); 133 canvas.drawRenderNode(node.get()); 134 }); 135 } 136 137 // Finally upsample the image to its original size. 138 canvas.scale(1 / kInputScale, 1 / kInputScale); 139 canvas.drawRenderNode(node.get()); 140 } 141 }; 142