blob: 9e4fb8fc9b352bfa129e6a447dadcefd26a91354 [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
61 void computeOverscrollStart(
62 out float outPos,
63 float inPos,
64 float overscroll,
65 float uStretchAffectedDist,
66 float distanceStretched
67 ) {
68 float offsetPos = uStretchAffectedDist - inPos;
69 float posBasedVariation = smoothstep(0., uStretchAffectedDist, offsetPos);
70 float stretchIntensity = overscroll * posBasedVariation;
71 outPos = distanceStretched - (offsetPos / (1. + stretchIntensity));
72 }
73
74 void computeOverscrollEnd(
75 out float outPos,
76 float inPos,
77 float overscroll,
78 float reverseStretchDist,
79 float uStretchAffectedDist,
80 float distanceStretched
81 ) {
82 float offsetPos = inPos - reverseStretchDist;
83 float posBasedVariation = (smoothstep(0., uStretchAffectedDist, offsetPos));
84 float stretchIntensity = (-overscroll) * posBasedVariation;
85 outPos = 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
86 }
87
88 void computeOverscroll(
89 out float outPos,
90 float inPos,
91 float overscroll,
92 float uStretchAffectedDist,
93 float distanceStretched,
94 float distanceDiff
95 ) {
96 if (overscroll > 0) {
97 if (inPos <= uStretchAffectedDist) {
98 computeOverscrollStart(
99 outPos,
100 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) {
112 computeOverscrollEnd(
113 outPos,
114 inPos,
115 overscroll,
116 stretchAffectedDist,
117 uStretchAffectedDist,
118 distanceStretched
119 );
120 } else if (inPos < stretchAffectedDist) {
121 outPos = -distanceDiff + inPos;
122 }
123 }
124 }
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;
138 computeOverscroll(
139 outU,
140 inU,
141 uOverscrollX,
Nader Jawada225d212021-03-17 15:12:58 -0700142 uStretchAffectedDistX,
Nader Jawad6701a602021-02-23 18:14:22 -0800143 uDistanceStretchedX,
144 uDistDiffX
145 );
146 computeOverscroll(
147 outV,
148 inV,
149 uOverscrollY,
Nader Jawada225d212021-03-17 15:12:58 -0700150 uStretchAffectedDistY,
Nader Jawad6701a602021-02-23 18:14:22 -0800151 uDistanceStretchedY,
152 uDistDiffY
153 );
154 coord.x = outU * viewportWidth;
155 coord.y = outV * viewportHeight;
156 return sample(uContentTexture, coord);
157 })");
158
159static const float ZERO = 0.f;
160
161sk_sp<SkImageFilter> StretchEffect::getImageFilter(const sk_sp<SkImage>& snapshotImage) const {
162 if (isEmpty()) {
163 return nullptr;
164 }
165
166 if (mStretchFilter != nullptr) {
167 return mStretchFilter;
168 }
169
Nader Jawad6701a602021-02-23 18:14:22 -0800170 float viewportWidth = stretchArea.width();
171 float viewportHeight = stretchArea.height();
Nader Jawada225d212021-03-17 15:12:58 -0700172 float normOverScrollDistX = mStretchDirection.x();
173 float normOverScrollDistY = mStretchDirection.y();
174 float distanceStretchedX = maxStretchAmountX / (1 + abs(normOverScrollDistX));
175 float distanceStretchedY = maxStretchAmountY / (1 + abs(normOverScrollDistY));
176 float diffX = distanceStretchedX;
177 float diffY = distanceStretchedY;
Nader Jawad6701a602021-02-23 18:14:22 -0800178
179 if (mBuilder == nullptr) {
180 mBuilder = std::make_unique<SkRuntimeShaderBuilder>(getStretchEffect());
181 }
182
183 mBuilder->child("uContentTexture") = snapshotImage->makeShader(
184 SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions(SkFilterMode::kLinear));
Nader Jawada225d212021-03-17 15:12:58 -0700185 mBuilder->uniform("uStretchAffectedDistX").set(&maxStretchAmountX, 1);
186 mBuilder->uniform("uStretchAffectedDistY").set(&maxStretchAmountY, 1);
Nader Jawad6701a602021-02-23 18:14:22 -0800187 mBuilder->uniform("uDistanceStretchedX").set(&distanceStretchedX, 1);
188 mBuilder->uniform("uDistanceStretchedY").set(&distanceStretchedY, 1);
189 mBuilder->uniform("uDistDiffX").set(&diffX, 1);
190 mBuilder->uniform("uDistDiffY").set(&diffY, 1);
191 mBuilder->uniform("uOverscrollX").set(&normOverScrollDistX, 1);
192 mBuilder->uniform("uOverscrollY").set(&normOverScrollDistY, 1);
193 mBuilder->uniform("uScrollX").set(&ZERO, 1);
194 mBuilder->uniform("uScrollY").set(&ZERO, 1);
195 mBuilder->uniform("viewportWidth").set(&viewportWidth, 1);
196 mBuilder->uniform("viewportHeight").set(&viewportHeight, 1);
197
198 mStretchFilter = SkImageFilters::Shader(mBuilder->makeShader(nullptr, false),
199 SkRect{0, 0, viewportWidth, viewportHeight});
200
201 return mStretchFilter;
202}
203
204sk_sp<SkRuntimeEffect> StretchEffect::getStretchEffect() {
205 const static SkRuntimeEffect::Result instance = SkRuntimeEffect::Make(stretchShader);
206 return instance.effect;
John Reck5cb290b2021-02-01 13:47:31 -0500207}
208
209} // namespace android::uirenderer