Added dampening logic to caluclate the distance to overscroll. Updated StretchEffect to support independent maximum stretch amounts for the x/y axis Removed debugging parameters to configure stretch distance since they are no longer used. Removed hidden API calls to configure stretch distance in ScrollView/HorizontalScrollView Bug: 179047472 Test: re-ran EdgeEffectTests Change-Id: I4698669273d364695a21c2cce00ec2cfec41b2cc
209 lines
7.1 KiB
C++
209 lines
7.1 KiB
C++
/*
|
|
* Copyright (C) 2021 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "StretchEffect.h"
|
|
#include <SkImageFilter.h>
|
|
#include <SkRefCnt.h>
|
|
#include <SkRuntimeEffect.h>
|
|
#include <SkString.h>
|
|
#include <SkSurface.h>
|
|
#include <include/effects/SkImageFilters.h>
|
|
|
|
#include <memory>
|
|
|
|
namespace android::uirenderer {
|
|
|
|
static const SkString stretchShader = SkString(R"(
|
|
uniform shader uContentTexture;
|
|
|
|
// multiplier to apply to scale effect
|
|
uniform float uMaxStretchIntensity;
|
|
|
|
// Maximum percentage to stretch beyond bounds of target
|
|
uniform float uStretchAffectedDistX;
|
|
uniform float uStretchAffectedDistY;
|
|
|
|
// Distance stretched as a function of the normalized overscroll times
|
|
// scale intensity
|
|
uniform float uDistanceStretchedX;
|
|
uniform float uDistanceStretchedY;
|
|
uniform float uDistDiffX;
|
|
|
|
// Difference between the peak stretch amount and overscroll amount normalized
|
|
uniform float uDistDiffY;
|
|
|
|
// Horizontal offset represented as a ratio of pixels divided by the target width
|
|
uniform float uScrollX;
|
|
// Vertical offset represented as a ratio of pixels divided by the target height
|
|
uniform float uScrollY;
|
|
|
|
// Normalized overscroll amount in the horizontal direction
|
|
uniform float uOverscrollX;
|
|
|
|
// Normalized overscroll amount in the vertical direction
|
|
uniform float uOverscrollY;
|
|
uniform float viewportWidth; // target height in pixels
|
|
uniform float viewportHeight; // target width in pixels
|
|
|
|
void computeOverscrollStart(
|
|
out float outPos,
|
|
float inPos,
|
|
float overscroll,
|
|
float uStretchAffectedDist,
|
|
float distanceStretched
|
|
) {
|
|
float offsetPos = uStretchAffectedDist - inPos;
|
|
float posBasedVariation = smoothstep(0., uStretchAffectedDist, offsetPos);
|
|
float stretchIntensity = overscroll * posBasedVariation;
|
|
outPos = distanceStretched - (offsetPos / (1. + stretchIntensity));
|
|
}
|
|
|
|
void computeOverscrollEnd(
|
|
out float outPos,
|
|
float inPos,
|
|
float overscroll,
|
|
float reverseStretchDist,
|
|
float uStretchAffectedDist,
|
|
float distanceStretched
|
|
) {
|
|
float offsetPos = inPos - reverseStretchDist;
|
|
float posBasedVariation = (smoothstep(0., uStretchAffectedDist, offsetPos));
|
|
float stretchIntensity = (-overscroll) * posBasedVariation;
|
|
outPos = 1 - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
|
|
}
|
|
|
|
void computeOverscroll(
|
|
out float outPos,
|
|
float inPos,
|
|
float overscroll,
|
|
float uStretchAffectedDist,
|
|
float distanceStretched,
|
|
float distanceDiff
|
|
) {
|
|
if (overscroll > 0) {
|
|
if (inPos <= uStretchAffectedDist) {
|
|
computeOverscrollStart(
|
|
outPos,
|
|
inPos,
|
|
overscroll,
|
|
uStretchAffectedDist,
|
|
distanceStretched
|
|
);
|
|
} else if (inPos >= distanceStretched) {
|
|
outPos = distanceDiff + inPos;
|
|
}
|
|
}
|
|
if (overscroll < 0) {
|
|
float stretchAffectedDist = 1. - uStretchAffectedDist;
|
|
if (inPos >= stretchAffectedDist) {
|
|
computeOverscrollEnd(
|
|
outPos,
|
|
inPos,
|
|
overscroll,
|
|
stretchAffectedDist,
|
|
uStretchAffectedDist,
|
|
distanceStretched
|
|
);
|
|
} else if (inPos < stretchAffectedDist) {
|
|
outPos = -distanceDiff + inPos;
|
|
}
|
|
}
|
|
}
|
|
|
|
vec4 main(vec2 coord) {
|
|
// Normalize SKSL pixel coordinate into a unit vector
|
|
float inU = coord.x / viewportWidth;
|
|
float inV = coord.y / viewportHeight;
|
|
float outU;
|
|
float outV;
|
|
float stretchIntensity;
|
|
// Add the normalized scroll position within scrolling list
|
|
inU += uScrollX;
|
|
inV += uScrollY;
|
|
outU = inU;
|
|
outV = inV;
|
|
computeOverscroll(
|
|
outU,
|
|
inU,
|
|
uOverscrollX,
|
|
uStretchAffectedDistX,
|
|
uDistanceStretchedX,
|
|
uDistDiffX
|
|
);
|
|
computeOverscroll(
|
|
outV,
|
|
inV,
|
|
uOverscrollY,
|
|
uStretchAffectedDistY,
|
|
uDistanceStretchedY,
|
|
uDistDiffY
|
|
);
|
|
coord.x = outU * viewportWidth;
|
|
coord.y = outV * viewportHeight;
|
|
return sample(uContentTexture, coord);
|
|
})");
|
|
|
|
static const float ZERO = 0.f;
|
|
|
|
sk_sp<SkImageFilter> StretchEffect::getImageFilter(const sk_sp<SkImage>& snapshotImage) const {
|
|
if (isEmpty()) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (mStretchFilter != nullptr) {
|
|
return mStretchFilter;
|
|
}
|
|
|
|
float viewportWidth = stretchArea.width();
|
|
float viewportHeight = stretchArea.height();
|
|
float normOverScrollDistX = mStretchDirection.x();
|
|
float normOverScrollDistY = mStretchDirection.y();
|
|
float distanceStretchedX = maxStretchAmountX / (1 + abs(normOverScrollDistX));
|
|
float distanceStretchedY = maxStretchAmountY / (1 + abs(normOverScrollDistY));
|
|
float diffX = distanceStretchedX;
|
|
float diffY = distanceStretchedY;
|
|
|
|
if (mBuilder == nullptr) {
|
|
mBuilder = std::make_unique<SkRuntimeShaderBuilder>(getStretchEffect());
|
|
}
|
|
|
|
mBuilder->child("uContentTexture") = snapshotImage->makeShader(
|
|
SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions(SkFilterMode::kLinear));
|
|
mBuilder->uniform("uStretchAffectedDistX").set(&maxStretchAmountX, 1);
|
|
mBuilder->uniform("uStretchAffectedDistY").set(&maxStretchAmountY, 1);
|
|
mBuilder->uniform("uDistanceStretchedX").set(&distanceStretchedX, 1);
|
|
mBuilder->uniform("uDistanceStretchedY").set(&distanceStretchedY, 1);
|
|
mBuilder->uniform("uDistDiffX").set(&diffX, 1);
|
|
mBuilder->uniform("uDistDiffY").set(&diffY, 1);
|
|
mBuilder->uniform("uOverscrollX").set(&normOverScrollDistX, 1);
|
|
mBuilder->uniform("uOverscrollY").set(&normOverScrollDistY, 1);
|
|
mBuilder->uniform("uScrollX").set(&ZERO, 1);
|
|
mBuilder->uniform("uScrollY").set(&ZERO, 1);
|
|
mBuilder->uniform("viewportWidth").set(&viewportWidth, 1);
|
|
mBuilder->uniform("viewportHeight").set(&viewportHeight, 1);
|
|
|
|
mStretchFilter = SkImageFilters::Shader(mBuilder->makeShader(nullptr, false),
|
|
SkRect{0, 0, viewportWidth, viewportHeight});
|
|
|
|
return mStretchFilter;
|
|
}
|
|
|
|
sk_sp<SkRuntimeEffect> StretchEffect::getStretchEffect() {
|
|
const static SkRuntimeEffect::Result instance = SkRuntimeEffect::Make(stretchShader);
|
|
return instance.effect;
|
|
}
|
|
|
|
} // namespace android::uirenderer
|