2021-02-01 13:47:31 -05:00
|
|
|
/*
|
|
|
|
* 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"
|
2021-02-23 18:14:22 -08:00
|
|
|
#include <SkImageFilter.h>
|
|
|
|
#include <SkRefCnt.h>
|
|
|
|
#include <SkRuntimeEffect.h>
|
|
|
|
#include <SkString.h>
|
|
|
|
#include <SkSurface.h>
|
|
|
|
#include <include/effects/SkImageFilters.h>
|
|
|
|
|
|
|
|
#include <memory>
|
2021-02-01 13:47:31 -05:00
|
|
|
|
|
|
|
namespace android::uirenderer {
|
|
|
|
|
2021-02-23 18:14:22 -08:00
|
|
|
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
|
2021-03-17 15:12:58 -07:00
|
|
|
uniform float uStretchAffectedDistX;
|
|
|
|
uniform float uStretchAffectedDistY;
|
2021-02-23 18:14:22 -08:00
|
|
|
|
|
|
|
// Distance stretched as a function of the normalized overscroll times
|
|
|
|
// scale intensity
|
|
|
|
uniform float uDistanceStretchedX;
|
|
|
|
uniform float uDistanceStretchedY;
|
2021-04-01 15:54:09 -07:00
|
|
|
uniform float uInverseDistanceStretchedX;
|
|
|
|
uniform float uInverseDistanceStretchedY;
|
2021-02-23 18:14:22 -08:00
|
|
|
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
|
|
|
|
|
2021-04-13 12:30:51 -07:00
|
|
|
// uInterpolationStrength is the intensity of the interpolation.
|
|
|
|
// if uInterpolationStrength is 0, then the stretch is constant for all the
|
|
|
|
// uStretchAffectedDist. if uInterpolationStrength is 1, then stretch intensity
|
|
|
|
// is interpolated based on the pixel position in the uStretchAffectedDist area;
|
|
|
|
// The closer we are from the scroll anchor point, the more it stretches,
|
|
|
|
// and the other way around.
|
|
|
|
uniform float uInterpolationStrength;
|
|
|
|
|
2021-05-06 10:48:18 -07:00
|
|
|
float easeIn(float t, float d) {
|
|
|
|
return t * d;
|
2021-04-01 15:54:09 -07:00
|
|
|
}
|
|
|
|
|
2021-03-31 13:11:57 -07:00
|
|
|
float computeOverscrollStart(
|
2021-02-23 18:14:22 -08:00
|
|
|
float inPos,
|
|
|
|
float overscroll,
|
|
|
|
float uStretchAffectedDist,
|
2021-04-01 15:54:09 -07:00
|
|
|
float uInverseStretchAffectedDist,
|
2021-04-13 12:30:51 -07:00
|
|
|
float distanceStretched,
|
|
|
|
float interpolationStrength
|
2021-02-23 18:14:22 -08:00
|
|
|
) {
|
|
|
|
float offsetPos = uStretchAffectedDist - inPos;
|
2021-04-13 12:30:51 -07:00
|
|
|
float posBasedVariation = mix(
|
2021-05-06 10:48:18 -07:00
|
|
|
1. ,easeIn(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
|
2021-02-23 18:14:22 -08:00
|
|
|
float stretchIntensity = overscroll * posBasedVariation;
|
2021-03-31 13:11:57 -07:00
|
|
|
return distanceStretched - (offsetPos / (1. + stretchIntensity));
|
2021-02-23 18:14:22 -08:00
|
|
|
}
|
|
|
|
|
2021-03-31 13:11:57 -07:00
|
|
|
float computeOverscrollEnd(
|
2021-02-23 18:14:22 -08:00
|
|
|
float inPos,
|
|
|
|
float overscroll,
|
|
|
|
float reverseStretchDist,
|
|
|
|
float uStretchAffectedDist,
|
2021-04-01 15:54:09 -07:00
|
|
|
float uInverseStretchAffectedDist,
|
2021-04-13 12:30:51 -07:00
|
|
|
float distanceStretched,
|
2021-05-13 20:43:18 -07:00
|
|
|
float interpolationStrength,
|
|
|
|
float viewportDimension
|
2021-02-23 18:14:22 -08:00
|
|
|
) {
|
|
|
|
float offsetPos = inPos - reverseStretchDist;
|
2021-04-13 12:30:51 -07:00
|
|
|
float posBasedVariation = mix(
|
2021-05-06 10:48:18 -07:00
|
|
|
1. ,easeIn(offsetPos, uInverseStretchAffectedDist), interpolationStrength);
|
2021-02-23 18:14:22 -08:00
|
|
|
float stretchIntensity = (-overscroll) * posBasedVariation;
|
2021-05-13 20:43:18 -07:00
|
|
|
return viewportDimension - (distanceStretched - (offsetPos / (1. + stretchIntensity)));
|
2021-02-23 18:14:22 -08:00
|
|
|
}
|
|
|
|
|
2021-03-31 13:11:57 -07:00
|
|
|
// Prefer usage of return values over out parameters as it enables
|
|
|
|
// SKSL to properly inline method calls and works around potential GPU
|
|
|
|
// driver issues on Wembly. See b/182566543 for details
|
|
|
|
float computeOverscroll(
|
2021-02-23 18:14:22 -08:00
|
|
|
float inPos,
|
|
|
|
float overscroll,
|
|
|
|
float uStretchAffectedDist,
|
2021-04-01 15:54:09 -07:00
|
|
|
float uInverseStretchAffectedDist,
|
2021-02-23 18:14:22 -08:00
|
|
|
float distanceStretched,
|
2021-04-13 12:30:51 -07:00
|
|
|
float distanceDiff,
|
2021-05-13 20:43:18 -07:00
|
|
|
float interpolationStrength,
|
|
|
|
float viewportDimension
|
2021-02-23 18:14:22 -08:00
|
|
|
) {
|
2021-03-31 13:11:57 -07:00
|
|
|
if (overscroll > 0) {
|
2021-05-13 19:08:04 -07:00
|
|
|
if (inPos <= uStretchAffectedDist) {
|
|
|
|
return computeOverscrollStart(
|
|
|
|
inPos,
|
|
|
|
overscroll,
|
|
|
|
uStretchAffectedDist,
|
|
|
|
uInverseStretchAffectedDist,
|
|
|
|
distanceStretched,
|
|
|
|
interpolationStrength
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
return distanceDiff + inPos;
|
2021-02-23 18:14:22 -08:00
|
|
|
}
|
2021-05-13 19:08:04 -07:00
|
|
|
} else if (overscroll < 0) {
|
2021-05-13 20:43:18 -07:00
|
|
|
float stretchAffectedDist = viewportDimension - uStretchAffectedDist;
|
2021-05-13 19:08:04 -07:00
|
|
|
if (inPos >= stretchAffectedDist) {
|
|
|
|
return computeOverscrollEnd(
|
|
|
|
inPos,
|
|
|
|
overscroll,
|
|
|
|
stretchAffectedDist,
|
|
|
|
uStretchAffectedDist,
|
|
|
|
uInverseStretchAffectedDist,
|
|
|
|
distanceStretched,
|
2021-05-13 20:43:18 -07:00
|
|
|
interpolationStrength,
|
|
|
|
viewportDimension
|
2021-05-13 19:08:04 -07:00
|
|
|
);
|
|
|
|
} else {
|
|
|
|
return -distanceDiff + inPos;
|
2021-02-23 18:14:22 -08:00
|
|
|
}
|
2021-05-13 19:08:04 -07:00
|
|
|
} else {
|
|
|
|
return inPos;
|
|
|
|
}
|
2021-02-23 18:14:22 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
vec4 main(vec2 coord) {
|
2021-05-13 20:43:18 -07:00
|
|
|
float inU = coord.x;
|
|
|
|
float inV = coord.y;
|
2021-02-23 18:14:22 -08:00
|
|
|
float outU;
|
|
|
|
float outV;
|
2021-05-13 20:43:18 -07:00
|
|
|
|
2021-02-23 18:14:22 -08:00
|
|
|
inU += uScrollX;
|
|
|
|
inV += uScrollY;
|
2021-03-31 13:11:57 -07:00
|
|
|
outU = computeOverscroll(
|
2021-02-23 18:14:22 -08:00
|
|
|
inU,
|
|
|
|
uOverscrollX,
|
2021-03-17 15:12:58 -07:00
|
|
|
uStretchAffectedDistX,
|
2021-04-01 15:54:09 -07:00
|
|
|
uInverseDistanceStretchedX,
|
2021-02-23 18:14:22 -08:00
|
|
|
uDistanceStretchedX,
|
2021-04-13 12:30:51 -07:00
|
|
|
uDistDiffX,
|
2021-05-13 20:43:18 -07:00
|
|
|
uInterpolationStrength,
|
|
|
|
viewportWidth
|
2021-02-23 18:14:22 -08:00
|
|
|
);
|
2021-03-31 13:11:57 -07:00
|
|
|
outV = computeOverscroll(
|
2021-02-23 18:14:22 -08:00
|
|
|
inV,
|
|
|
|
uOverscrollY,
|
2021-03-17 15:12:58 -07:00
|
|
|
uStretchAffectedDistY,
|
2021-04-01 15:54:09 -07:00
|
|
|
uInverseDistanceStretchedY,
|
2021-02-23 18:14:22 -08:00
|
|
|
uDistanceStretchedY,
|
2021-04-13 12:30:51 -07:00
|
|
|
uDistDiffY,
|
2021-05-13 20:43:18 -07:00
|
|
|
uInterpolationStrength,
|
|
|
|
viewportHeight
|
2021-02-23 18:14:22 -08:00
|
|
|
);
|
2021-05-13 20:43:18 -07:00
|
|
|
coord.x = outU;
|
|
|
|
coord.y = outV;
|
2021-02-23 18:14:22 -08:00
|
|
|
return sample(uContentTexture, coord);
|
|
|
|
})");
|
|
|
|
|
|
|
|
static const float ZERO = 0.f;
|
2021-04-13 12:30:51 -07:00
|
|
|
static const float INTERPOLATION_STRENGTH_VALUE = 0.7f;
|
2021-06-23 11:48:58 -07:00
|
|
|
static const char CONTENT_TEXTURE[] = "uContentTexture";
|
2021-02-23 18:14:22 -08:00
|
|
|
|
2021-04-19 19:45:13 -07:00
|
|
|
sk_sp<SkShader> StretchEffect::getShader(float width, float height,
|
2021-06-09 10:14:43 -07:00
|
|
|
const sk_sp<SkImage>& snapshotImage,
|
|
|
|
const SkMatrix* matrix) const {
|
2021-02-23 18:14:22 -08:00
|
|
|
if (isEmpty()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2021-03-17 15:12:58 -07:00
|
|
|
float normOverScrollDistX = mStretchDirection.x();
|
|
|
|
float normOverScrollDistY = mStretchDirection.y();
|
2021-05-13 20:43:18 -07:00
|
|
|
float distanceStretchedX = width / (1 + abs(normOverScrollDistX));
|
|
|
|
float distanceStretchedY = height / (1 + abs(normOverScrollDistY));
|
|
|
|
float inverseDistanceStretchedX = 1.f / width;
|
|
|
|
float inverseDistanceStretchedY = 1.f / height;
|
|
|
|
float diffX = distanceStretchedX - width;
|
|
|
|
float diffY = distanceStretchedY - height;
|
2021-02-23 18:14:22 -08:00
|
|
|
|
|
|
|
if (mBuilder == nullptr) {
|
|
|
|
mBuilder = std::make_unique<SkRuntimeShaderBuilder>(getStretchEffect());
|
|
|
|
}
|
|
|
|
|
2021-06-23 11:48:58 -07:00
|
|
|
mBuilder->child(CONTENT_TEXTURE) =
|
2021-06-09 10:14:43 -07:00
|
|
|
snapshotImage->makeShader(SkTileMode::kClamp, SkTileMode::kClamp,
|
|
|
|
SkSamplingOptions(SkFilterMode::kLinear), matrix);
|
2021-04-13 12:30:51 -07:00
|
|
|
mBuilder->uniform("uInterpolationStrength").set(&INTERPOLATION_STRENGTH_VALUE, 1);
|
2021-05-13 20:43:18 -07:00
|
|
|
mBuilder->uniform("uStretchAffectedDistX").set(&width, 1);
|
|
|
|
mBuilder->uniform("uStretchAffectedDistY").set(&height, 1);
|
2021-02-23 18:14:22 -08:00
|
|
|
mBuilder->uniform("uDistanceStretchedX").set(&distanceStretchedX, 1);
|
|
|
|
mBuilder->uniform("uDistanceStretchedY").set(&distanceStretchedY, 1);
|
2021-04-01 15:54:09 -07:00
|
|
|
mBuilder->uniform("uInverseDistanceStretchedX").set(&inverseDistanceStretchedX, 1);
|
|
|
|
mBuilder->uniform("uInverseDistanceStretchedY").set(&inverseDistanceStretchedY, 1);
|
2021-02-23 18:14:22 -08:00
|
|
|
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);
|
2021-04-19 19:45:13 -07:00
|
|
|
mBuilder->uniform("viewportWidth").set(&width, 1);
|
|
|
|
mBuilder->uniform("viewportHeight").set(&height, 1);
|
2021-02-23 18:14:22 -08:00
|
|
|
|
2021-06-23 11:48:58 -07:00
|
|
|
auto result = mBuilder->makeShader(nullptr, false);
|
|
|
|
mBuilder->child(CONTENT_TEXTURE) = nullptr;
|
|
|
|
return result;
|
2021-02-23 18:14:22 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
sk_sp<SkRuntimeEffect> StretchEffect::getStretchEffect() {
|
2021-04-20 19:46:39 +00:00
|
|
|
const static SkRuntimeEffect::Result instance = SkRuntimeEffect::MakeForShader(stretchShader);
|
2021-02-23 18:14:22 -08:00
|
|
|
return instance.effect;
|
2021-02-01 13:47:31 -05:00
|
|
|
}
|
|
|
|
|
2021-05-06 10:48:18 -07:00
|
|
|
/**
|
|
|
|
* Helper method that maps the input texture position to the stretch position
|
|
|
|
* based on the given overscroll value that represents an overscroll from
|
|
|
|
* either the top or left
|
|
|
|
* @param overscroll current overscroll value
|
|
|
|
* @param input normalized input position (can be x or y) on the input texture
|
|
|
|
* @return stretched position of the input normalized from 0 to 1
|
|
|
|
*/
|
|
|
|
float reverseMapStart(float overscroll, float input) {
|
|
|
|
float numerator = (-input * overscroll * overscroll) -
|
|
|
|
(2 * input * overscroll) - input;
|
|
|
|
float denominator = 1.f + (.3f * overscroll) +
|
|
|
|
(.7f * input * overscroll * overscroll) + (.7f * input * overscroll);
|
|
|
|
return -(numerator / denominator);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper method that maps the input texture position to the stretch position
|
|
|
|
* based on the given overscroll value that represents an overscroll from
|
|
|
|
* either the bottom or right
|
|
|
|
* @param overscroll current overscroll value
|
|
|
|
* @param input normalized input position (can be x or y) on the input texture
|
|
|
|
* @return stretched position of the input normalized from 0 to 1
|
|
|
|
*/
|
|
|
|
float reverseMapEnd(float overscroll, float input) {
|
|
|
|
float numerator = (.3f * overscroll * overscroll) -
|
|
|
|
(.3f * input * overscroll * overscroll) +
|
|
|
|
(1.3f * input * overscroll) - overscroll - input;
|
|
|
|
float denominator = (.7f * input * overscroll * overscroll) -
|
|
|
|
(.7f * input * overscroll) - (.7f * overscroll * overscroll) +
|
|
|
|
overscroll - 1.f;
|
|
|
|
return numerator / denominator;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculates the normalized stretch position given the normalized input
|
|
|
|
* position. This handles calculating the overscroll from either the
|
|
|
|
* top or left vs bottom or right depending on the sign of the given overscroll
|
|
|
|
* value
|
|
|
|
*
|
|
|
|
* @param overscroll unit vector of overscroll from -1 to 1 indicating overscroll
|
|
|
|
* from the bottom or right vs top or left respectively
|
|
|
|
* @param normalizedInput the
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
float computeReverseOverscroll(float overscroll, float normalizedInput) {
|
|
|
|
float distanceStretched = 1.f / (1.f + abs(overscroll));
|
|
|
|
float distanceDiff = distanceStretched - 1.f;
|
|
|
|
if (overscroll > 0) {
|
|
|
|
float output = reverseMapStart(overscroll, normalizedInput);
|
|
|
|
if (output <= 1.0f) {
|
|
|
|
return output;
|
|
|
|
} else if (output >= distanceStretched){
|
|
|
|
return output - distanceDiff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (overscroll < 0) {
|
|
|
|
float output = reverseMapEnd(overscroll, normalizedInput);
|
|
|
|
if (output >= 0.f) {
|
|
|
|
return output;
|
|
|
|
} else if (output < 0.f){
|
|
|
|
return output + distanceDiff;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return normalizedInput;
|
|
|
|
}
|
|
|
|
|
|
|
|
float StretchEffect::computeStretchedPositionX(float normalizedX) const {
|
|
|
|
return computeReverseOverscroll(mStretchDirection.x(), normalizedX);
|
|
|
|
}
|
|
|
|
|
|
|
|
float StretchEffect::computeStretchedPositionY(float normalizedY) const {
|
|
|
|
return computeReverseOverscroll(mStretchDirection.y(), normalizedY);
|
|
|
|
}
|
|
|
|
|
2021-02-01 13:47:31 -05:00
|
|
|
} // namespace android::uirenderer
|