blob: de14bebe5cbcd0972070a1fbdaa1fa7512d17520 [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;
Nader Jawadebb26b12021-04-01 15:54:09 -070043 uniform float uInverseDistanceStretchedX;
44 uniform float uInverseDistanceStretchedY;
Nader Jawad6701a602021-02-23 18:14:22 -080045 uniform float uDistDiffX;
46
47 // Difference between the peak stretch amount and overscroll amount normalized
48 uniform float uDistDiffY;
49
50 // Horizontal offset represented as a ratio of pixels divided by the target width
51 uniform float uScrollX;
52 // Vertical offset represented as a ratio of pixels divided by the target height
53 uniform float uScrollY;
54
55 // Normalized overscroll amount in the horizontal direction
56 uniform float uOverscrollX;
57
58 // Normalized overscroll amount in the vertical direction
59 uniform float uOverscrollY;
60 uniform float viewportWidth; // target height in pixels
61 uniform float viewportHeight; // target width in pixels
62
Nader Jawadebb26b12021-04-01 15:54:09 -070063 float easeInCubic(float t, float d) {
64 float tmp = t * d;
65 return tmp * tmp * tmp;
66 }
67
Nader Jawad7148aa72021-03-31 13:11:57 -070068 float computeOverscrollStart(
Nader Jawad6701a602021-02-23 18:14:22 -080069 float inPos,
70 float overscroll,
71 float uStretchAffectedDist,
Nader Jawadebb26b12021-04-01 15:54:09 -070072 float uInverseStretchAffectedDist,
Nader Jawad6701a602021-02-23 18:14:22 -080073 float distanceStretched
74 ) {
75 float offsetPos = uStretchAffectedDist - inPos;
Nader Jawadebb26b12021-04-01 15:54:09 -070076 float posBasedVariation = easeInCubic(offsetPos, uInverseStretchAffectedDist);
Nader Jawad6701a602021-02-23 18:14:22 -080077 float stretchIntensity = overscroll * posBasedVariation;
Nader Jawad7148aa72021-03-31 13:11:57 -070078 return distanceStretched - (offsetPos / (1. + stretchIntensity));
Nader Jawad6701a602021-02-23 18:14:22 -080079 }
80
Nader Jawad7148aa72021-03-31 13:11:57 -070081 float computeOverscrollEnd(
Nader Jawad6701a602021-02-23 18:14:22 -080082 float inPos,
83 float overscroll,
84 float reverseStretchDist,
85 float uStretchAffectedDist,
Nader Jawadebb26b12021-04-01 15:54:09 -070086 float uInverseStretchAffectedDist,
Nader Jawad6701a602021-02-23 18:14:22 -080087 float distanceStretched
88 ) {
89 float offsetPos = inPos - reverseStretchDist;
Nader Jawadebb26b12021-04-01 15:54:09 -070090 float posBasedVariation = easeInCubic(offsetPos, uInverseStretchAffectedDist);
Nader Jawad6701a602021-02-23 18:14:22 -080091 float stretchIntensity = (-overscroll) * posBasedVariation;
Nader Jawad7148aa72021-03-31 13:11:57 -070092 return 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
Nader Jawad6701a602021-02-23 18:14:22 -080093 }
94
Nader Jawad7148aa72021-03-31 13:11:57 -070095 // Prefer usage of return values over out parameters as it enables
96 // SKSL to properly inline method calls and works around potential GPU
97 // driver issues on Wembly. See b/182566543 for details
98 float computeOverscroll(
Nader Jawad6701a602021-02-23 18:14:22 -080099 float inPos,
100 float overscroll,
101 float uStretchAffectedDist,
Nader Jawadebb26b12021-04-01 15:54:09 -0700102 float uInverseStretchAffectedDist,
Nader Jawad6701a602021-02-23 18:14:22 -0800103 float distanceStretched,
104 float distanceDiff
105 ) {
Nader Jawad7148aa72021-03-31 13:11:57 -0700106 float outPos = inPos;
107 if (overscroll > 0) {
Nader Jawad6701a602021-02-23 18:14:22 -0800108 if (inPos <= uStretchAffectedDist) {
Nader Jawad7148aa72021-03-31 13:11:57 -0700109 outPos = computeOverscrollStart(
Nader Jawad6701a602021-02-23 18:14:22 -0800110 inPos,
111 overscroll,
112 uStretchAffectedDist,
Nader Jawadebb26b12021-04-01 15:54:09 -0700113 uInverseStretchAffectedDist,
Nader Jawad6701a602021-02-23 18:14:22 -0800114 distanceStretched
115 );
116 } else if (inPos >= distanceStretched) {
117 outPos = distanceDiff + inPos;
118 }
119 }
120 if (overscroll < 0) {
121 float stretchAffectedDist = 1. - uStretchAffectedDist;
122 if (inPos >= stretchAffectedDist) {
Nader Jawad7148aa72021-03-31 13:11:57 -0700123 outPos = computeOverscrollEnd(
Nader Jawad6701a602021-02-23 18:14:22 -0800124 inPos,
125 overscroll,
126 stretchAffectedDist,
127 uStretchAffectedDist,
Nader Jawadebb26b12021-04-01 15:54:09 -0700128 uInverseStretchAffectedDist,
Nader Jawad6701a602021-02-23 18:14:22 -0800129 distanceStretched
130 );
131 } else if (inPos < stretchAffectedDist) {
132 outPos = -distanceDiff + inPos;
133 }
134 }
Nader Jawad7148aa72021-03-31 13:11:57 -0700135 return outPos;
Nader Jawad6701a602021-02-23 18:14:22 -0800136 }
137
138 vec4 main(vec2 coord) {
139 // Normalize SKSL pixel coordinate into a unit vector
140 float inU = coord.x / viewportWidth;
141 float inV = coord.y / viewportHeight;
142 float outU;
143 float outV;
144 float stretchIntensity;
145 // Add the normalized scroll position within scrolling list
146 inU += uScrollX;
147 inV += uScrollY;
148 outU = inU;
149 outV = inV;
Nader Jawad7148aa72021-03-31 13:11:57 -0700150 outU = computeOverscroll(
Nader Jawad6701a602021-02-23 18:14:22 -0800151 inU,
152 uOverscrollX,
Nader Jawada225d212021-03-17 15:12:58 -0700153 uStretchAffectedDistX,
Nader Jawadebb26b12021-04-01 15:54:09 -0700154 uInverseDistanceStretchedX,
Nader Jawad6701a602021-02-23 18:14:22 -0800155 uDistanceStretchedX,
156 uDistDiffX
157 );
Nader Jawad7148aa72021-03-31 13:11:57 -0700158 outV = computeOverscroll(
Nader Jawad6701a602021-02-23 18:14:22 -0800159 inV,
160 uOverscrollY,
Nader Jawada225d212021-03-17 15:12:58 -0700161 uStretchAffectedDistY,
Nader Jawadebb26b12021-04-01 15:54:09 -0700162 uInverseDistanceStretchedY,
Nader Jawad6701a602021-02-23 18:14:22 -0800163 uDistanceStretchedY,
164 uDistDiffY
165 );
166 coord.x = outU * viewportWidth;
167 coord.y = outV * viewportHeight;
168 return sample(uContentTexture, coord);
169 })");
170
171static const float ZERO = 0.f;
Nader Jawadebb26b12021-04-01 15:54:09 -0700172static const float CONTENT_DISTANCE_STRETCHED = 1.f;
Nader Jawad6701a602021-02-23 18:14:22 -0800173
Nader Jawad8f5d66b2021-04-07 16:05:13 -0700174sk_sp<SkShader> StretchEffect::getShader(const sk_sp<SkImage>& snapshotImage) const {
Nader Jawad6701a602021-02-23 18:14:22 -0800175 if (isEmpty()) {
176 return nullptr;
177 }
178
Nader Jawad8f5d66b2021-04-07 16:05:13 -0700179 if (mStretchShader != nullptr) {
180 return mStretchShader;
Nader Jawad6701a602021-02-23 18:14:22 -0800181 }
182
Nader Jawad6701a602021-02-23 18:14:22 -0800183 float viewportWidth = stretchArea.width();
184 float viewportHeight = stretchArea.height();
Nader Jawada225d212021-03-17 15:12:58 -0700185 float normOverScrollDistX = mStretchDirection.x();
186 float normOverScrollDistY = mStretchDirection.y();
Nader Jawadebb26b12021-04-01 15:54:09 -0700187 float distanceStretchedX = CONTENT_DISTANCE_STRETCHED / (1 + abs(normOverScrollDistX));
188 float distanceStretchedY = CONTENT_DISTANCE_STRETCHED / (1 + abs(normOverScrollDistY));
Nader Jawaddbc85fc2021-04-02 22:47:27 -0700189 float inverseDistanceStretchedX = 1.f / CONTENT_DISTANCE_STRETCHED;
190 float inverseDistanceStretchedY = 1.f / CONTENT_DISTANCE_STRETCHED;
Nader Jawadebb26b12021-04-01 15:54:09 -0700191 float diffX = distanceStretchedX - CONTENT_DISTANCE_STRETCHED;
192 float diffY = distanceStretchedY - CONTENT_DISTANCE_STRETCHED;
Nader Jawad6701a602021-02-23 18:14:22 -0800193
194 if (mBuilder == nullptr) {
195 mBuilder = std::make_unique<SkRuntimeShaderBuilder>(getStretchEffect());
196 }
197
198 mBuilder->child("uContentTexture") = snapshotImage->makeShader(
199 SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions(SkFilterMode::kLinear));
Nader Jawadebb26b12021-04-01 15:54:09 -0700200 mBuilder->uniform("uStretchAffectedDistX").set(&CONTENT_DISTANCE_STRETCHED, 1);
201 mBuilder->uniform("uStretchAffectedDistY").set(&CONTENT_DISTANCE_STRETCHED, 1);
Nader Jawad6701a602021-02-23 18:14:22 -0800202 mBuilder->uniform("uDistanceStretchedX").set(&distanceStretchedX, 1);
203 mBuilder->uniform("uDistanceStretchedY").set(&distanceStretchedY, 1);
Nader Jawadebb26b12021-04-01 15:54:09 -0700204 mBuilder->uniform("uInverseDistanceStretchedX").set(&inverseDistanceStretchedX, 1);
205 mBuilder->uniform("uInverseDistanceStretchedY").set(&inverseDistanceStretchedY, 1);
Nader Jawad6701a602021-02-23 18:14:22 -0800206 mBuilder->uniform("uDistDiffX").set(&diffX, 1);
207 mBuilder->uniform("uDistDiffY").set(&diffY, 1);
208 mBuilder->uniform("uOverscrollX").set(&normOverScrollDistX, 1);
209 mBuilder->uniform("uOverscrollY").set(&normOverScrollDistY, 1);
210 mBuilder->uniform("uScrollX").set(&ZERO, 1);
211 mBuilder->uniform("uScrollY").set(&ZERO, 1);
212 mBuilder->uniform("viewportWidth").set(&viewportWidth, 1);
213 mBuilder->uniform("viewportHeight").set(&viewportHeight, 1);
214
Nader Jawad8f5d66b2021-04-07 16:05:13 -0700215 mStretchShader = mBuilder->makeShader(nullptr, false);
Nader Jawad6701a602021-02-23 18:14:22 -0800216
Nader Jawad8f5d66b2021-04-07 16:05:13 -0700217 return mStretchShader;
Nader Jawad6701a602021-02-23 18:14:22 -0800218}
219
220sk_sp<SkRuntimeEffect> StretchEffect::getStretchEffect() {
221 const static SkRuntimeEffect::Result instance = SkRuntimeEffect::Make(stretchShader);
222 return instance.effect;
John Reck5cb290b2021-02-01 13:47:31 -0500223}
224
225} // namespace android::uirenderer