blob: 0804e0aec2789ad24e2de3d2dbd726938d4c92f8 [file] [log] [blame]
John Reck5cb290b2021-02-01 13:47:31 -05001/*
2 * Copyright (C) 2021 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 "StretchEffect.h"
Nader Jawad6701a602021-02-23 18:14:22 -080018#include <SkImageFilter.h>
19#include <SkRefCnt.h>
20#include <SkRuntimeEffect.h>
21#include <SkString.h>
22#include <SkSurface.h>
23#include <include/effects/SkImageFilters.h>
24
25#include <memory>
John Reck5cb290b2021-02-01 13:47:31 -050026
27namespace android::uirenderer {
28
Nader Jawad6701a602021-02-23 18:14:22 -080029static const SkString stretchShader = SkString(R"(
30 uniform shader uContentTexture;
31
32 // multiplier to apply to scale effect
33 uniform float uMaxStretchIntensity;
34
35 // Maximum percentage to stretch beyond bounds of target
Nader Jawada225d212021-03-17 15:12:58 -070036 uniform float uStretchAffectedDistX;
37 uniform float uStretchAffectedDistY;
Nader Jawad6701a602021-02-23 18:14:22 -080038
39 // Distance stretched as a function of the normalized overscroll times
40 // scale intensity
41 uniform float uDistanceStretchedX;
42 uniform float uDistanceStretchedY;
43 uniform float uDistDiffX;
44
45 // Difference between the peak stretch amount and overscroll amount normalized
46 uniform float uDistDiffY;
47
48 // Horizontal offset represented as a ratio of pixels divided by the target width
49 uniform float uScrollX;
50 // Vertical offset represented as a ratio of pixels divided by the target height
51 uniform float uScrollY;
52
53 // Normalized overscroll amount in the horizontal direction
54 uniform float uOverscrollX;
55
56 // Normalized overscroll amount in the vertical direction
57 uniform float uOverscrollY;
58 uniform float viewportWidth; // target height in pixels
59 uniform float viewportHeight; // target width in pixels
60
Nader Jawad7148aa72021-03-31 13:11:57 -070061 float computeOverscrollStart(
Nader Jawad6701a602021-02-23 18:14:22 -080062 float inPos,
63 float overscroll,
64 float uStretchAffectedDist,
65 float distanceStretched
66 ) {
67 float offsetPos = uStretchAffectedDist - inPos;
68 float posBasedVariation = smoothstep(0., uStretchAffectedDist, offsetPos);
69 float stretchIntensity = overscroll * posBasedVariation;
Nader Jawad7148aa72021-03-31 13:11:57 -070070 return distanceStretched - (offsetPos / (1. + stretchIntensity));
Nader Jawad6701a602021-02-23 18:14:22 -080071 }
72
Nader Jawad7148aa72021-03-31 13:11:57 -070073 float computeOverscrollEnd(
Nader Jawad6701a602021-02-23 18:14:22 -080074 float inPos,
75 float overscroll,
76 float reverseStretchDist,
77 float uStretchAffectedDist,
78 float distanceStretched
79 ) {
80 float offsetPos = inPos - reverseStretchDist;
81 float posBasedVariation = (smoothstep(0., uStretchAffectedDist, offsetPos));
82 float stretchIntensity = (-overscroll) * posBasedVariation;
Nader Jawad7148aa72021-03-31 13:11:57 -070083 return 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
Nader Jawad6701a602021-02-23 18:14:22 -080084 }
85
Nader Jawad7148aa72021-03-31 13:11:57 -070086 // Prefer usage of return values over out parameters as it enables
87 // SKSL to properly inline method calls and works around potential GPU
88 // driver issues on Wembly. See b/182566543 for details
89 float computeOverscroll(
Nader Jawad6701a602021-02-23 18:14:22 -080090 float inPos,
91 float overscroll,
92 float uStretchAffectedDist,
93 float distanceStretched,
94 float distanceDiff
95 ) {
Nader Jawad7148aa72021-03-31 13:11:57 -070096 float outPos = inPos;
97 if (overscroll > 0) {
Nader Jawad6701a602021-02-23 18:14:22 -080098 if (inPos <= uStretchAffectedDist) {
Nader Jawad7148aa72021-03-31 13:11:57 -070099 outPos = computeOverscrollStart(
Nader Jawad6701a602021-02-23 18:14:22 -0800100 inPos,
101 overscroll,
102 uStretchAffectedDist,
103 distanceStretched
104 );
105 } else if (inPos >= distanceStretched) {
106 outPos = distanceDiff + inPos;
107 }
108 }
109 if (overscroll < 0) {
110 float stretchAffectedDist = 1. - uStretchAffectedDist;
111 if (inPos >= stretchAffectedDist) {
Nader Jawad7148aa72021-03-31 13:11:57 -0700112 outPos = computeOverscrollEnd(
Nader Jawad6701a602021-02-23 18:14:22 -0800113 inPos,
114 overscroll,
115 stretchAffectedDist,
116 uStretchAffectedDist,
117 distanceStretched
118 );
119 } else if (inPos < stretchAffectedDist) {
120 outPos = -distanceDiff + inPos;
121 }
122 }
Nader Jawad7148aa72021-03-31 13:11:57 -0700123 return outPos;
Nader Jawad6701a602021-02-23 18:14:22 -0800124 }
125
126 vec4 main(vec2 coord) {
127 // Normalize SKSL pixel coordinate into a unit vector
128 float inU = coord.x / viewportWidth;
129 float inV = coord.y / viewportHeight;
130 float outU;
131 float outV;
132 float stretchIntensity;
133 // Add the normalized scroll position within scrolling list
134 inU += uScrollX;
135 inV += uScrollY;
136 outU = inU;
137 outV = inV;
Nader Jawad7148aa72021-03-31 13:11:57 -0700138 outU = computeOverscroll(
Nader Jawad6701a602021-02-23 18:14:22 -0800139 inU,
140 uOverscrollX,
Nader Jawada225d212021-03-17 15:12:58 -0700141 uStretchAffectedDistX,
Nader Jawad6701a602021-02-23 18:14:22 -0800142 uDistanceStretchedX,
143 uDistDiffX
144 );
Nader Jawad7148aa72021-03-31 13:11:57 -0700145 outV = computeOverscroll(
Nader Jawad6701a602021-02-23 18:14:22 -0800146 inV,
147 uOverscrollY,
Nader Jawada225d212021-03-17 15:12:58 -0700148 uStretchAffectedDistY,
Nader Jawad6701a602021-02-23 18:14:22 -0800149 uDistanceStretchedY,
150 uDistDiffY
151 );
152 coord.x = outU * viewportWidth;
153 coord.y = outV * viewportHeight;
154 return sample(uContentTexture, coord);
155 })");
156
157static const float ZERO = 0.f;
158
159sk_sp<SkImageFilter> StretchEffect::getImageFilter(const sk_sp<SkImage>& snapshotImage) const {
160 if (isEmpty()) {
161 return nullptr;
162 }
163
164 if (mStretchFilter != nullptr) {
165 return mStretchFilter;
166 }
167
Nader Jawad6701a602021-02-23 18:14:22 -0800168 float viewportWidth = stretchArea.width();
169 float viewportHeight = stretchArea.height();
Nader Jawada225d212021-03-17 15:12:58 -0700170 float normOverScrollDistX = mStretchDirection.x();
171 float normOverScrollDistY = mStretchDirection.y();
172 float distanceStretchedX = maxStretchAmountX / (1 + abs(normOverScrollDistX));
173 float distanceStretchedY = maxStretchAmountY / (1 + abs(normOverScrollDistY));
174 float diffX = distanceStretchedX;
175 float diffY = distanceStretchedY;
Nader Jawad6701a602021-02-23 18:14:22 -0800176
177 if (mBuilder == nullptr) {
178 mBuilder = std::make_unique<SkRuntimeShaderBuilder>(getStretchEffect());
179 }
180
181 mBuilder->child("uContentTexture") = snapshotImage->makeShader(
182 SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions(SkFilterMode::kLinear));
Nader Jawada225d212021-03-17 15:12:58 -0700183 mBuilder->uniform("uStretchAffectedDistX").set(&maxStretchAmountX, 1);
184 mBuilder->uniform("uStretchAffectedDistY").set(&maxStretchAmountY, 1);
Nader Jawad6701a602021-02-23 18:14:22 -0800185 mBuilder->uniform("uDistanceStretchedX").set(&distanceStretchedX, 1);
186 mBuilder->uniform("uDistanceStretchedY").set(&distanceStretchedY, 1);
187 mBuilder->uniform("uDistDiffX").set(&diffX, 1);
188 mBuilder->uniform("uDistDiffY").set(&diffY, 1);
189 mBuilder->uniform("uOverscrollX").set(&normOverScrollDistX, 1);
190 mBuilder->uniform("uOverscrollY").set(&normOverScrollDistY, 1);
191 mBuilder->uniform("uScrollX").set(&ZERO, 1);
192 mBuilder->uniform("uScrollY").set(&ZERO, 1);
193 mBuilder->uniform("viewportWidth").set(&viewportWidth, 1);
194 mBuilder->uniform("viewportHeight").set(&viewportHeight, 1);
195
196 mStretchFilter = SkImageFilters::Shader(mBuilder->makeShader(nullptr, false),
197 SkRect{0, 0, viewportWidth, viewportHeight});
198
199 return mStretchFilter;
200}
201
202sk_sp<SkRuntimeEffect> StretchEffect::getStretchEffect() {
203 const static SkRuntimeEffect::Result instance = SkRuntimeEffect::Make(stretchShader);
204 return instance.effect;
John Reck5cb290b2021-02-01 13:47:31 -0500205}
206
207} // namespace android::uirenderer