Merge "Wire SKSL based stretch shader to HWUI" into sc-dev
This commit is contained in:
@@ -22188,9 +22188,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
||||
* and hardware acceleration.
|
||||
*/
|
||||
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
|
||||
// Clear the overscroll effect:
|
||||
// TODO: Use internal API instead of overriding the existing RenderEffect
|
||||
setRenderEffect(null);
|
||||
|
||||
final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
|
||||
/* If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList.
|
||||
|
||||
@@ -29,7 +29,6 @@ import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RecordingCanvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RenderEffect;
|
||||
import android.graphics.RenderNode;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
@@ -83,6 +82,8 @@ public class EdgeEffect {
|
||||
public @interface EdgeEffectType {
|
||||
}
|
||||
|
||||
private static final float DEFAULT_MAX_STRETCH_INTENSITY = 1.5f;
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
private static final String TAG = "EdgeEffect";
|
||||
|
||||
@@ -128,6 +129,8 @@ public class EdgeEffect {
|
||||
|
||||
private long mStartTime;
|
||||
private float mDuration;
|
||||
private float mStretchIntensity = DEFAULT_MAX_STRETCH_INTENSITY;
|
||||
private float mStretchDistance = -1f;
|
||||
|
||||
private final Interpolator mInterpolator;
|
||||
|
||||
@@ -146,6 +149,8 @@ public class EdgeEffect {
|
||||
private float mPullDistance;
|
||||
|
||||
private final Rect mBounds = new Rect();
|
||||
private float mWidth;
|
||||
private float mHeight;
|
||||
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123769450)
|
||||
private final Paint mPaint = new Paint();
|
||||
private float mRadius;
|
||||
@@ -202,6 +207,19 @@ public class EdgeEffect {
|
||||
mBaseGlowScale = h > 0 ? Math.min(oh / h, 1.f) : 1.f;
|
||||
|
||||
mBounds.set(mBounds.left, mBounds.top, width, (int) Math.min(height, h));
|
||||
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the distance in pixels to stretch the content. This is only consumed as part
|
||||
* if {@link #setType(int)} is set to {@link #TYPE_STRETCH}
|
||||
* @param stretchDistance Stretch distance in pixels when the target View is overscrolled
|
||||
* @hide
|
||||
*/
|
||||
public void setStretchDistance(float stretchDistance) {
|
||||
mStretchDistance = stretchDistance;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -436,6 +454,13 @@ public class EdgeEffect {
|
||||
mEdgeEffectType = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void setMaxStretchIntensity(float stretchIntensity) {
|
||||
mStretchIntensity = stretchIntensity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or clear the blend mode. A blend mode defines how source pixels
|
||||
* (generated by a drawing command) are composited with the destination pixels
|
||||
@@ -520,23 +545,55 @@ public class EdgeEffect {
|
||||
RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
|
||||
if (mTmpMatrix == null) {
|
||||
mTmpMatrix = new Matrix();
|
||||
mTmpPoints = new float[4];
|
||||
mTmpPoints = new float[12];
|
||||
}
|
||||
//noinspection deprecation
|
||||
recordingCanvas.getMatrix(mTmpMatrix);
|
||||
mTmpPoints[0] = mBounds.width() * mDisplacement;
|
||||
mTmpPoints[1] = mDistance * mBounds.height();
|
||||
mTmpPoints[2] = mTmpPoints[0];
|
||||
mTmpPoints[3] = 0;
|
||||
|
||||
mTmpPoints[0] = 0;
|
||||
mTmpPoints[1] = 0; // top-left
|
||||
mTmpPoints[2] = mWidth;
|
||||
mTmpPoints[3] = 0; // top-right
|
||||
mTmpPoints[4] = mWidth;
|
||||
mTmpPoints[5] = mHeight; // bottom-right
|
||||
mTmpPoints[6] = 0;
|
||||
mTmpPoints[7] = mHeight; // bottom-left
|
||||
mTmpPoints[8] = mWidth * mDisplacement;
|
||||
mTmpPoints[9] = 0; // drag start point
|
||||
mTmpPoints[10] = mWidth * mDisplacement;
|
||||
mTmpPoints[11] = mHeight * mDistance; // drag point
|
||||
mTmpMatrix.mapPoints(mTmpPoints);
|
||||
float x = mTmpPoints[0] - mTmpPoints[2];
|
||||
float y = mTmpPoints[1] - mTmpPoints[3];
|
||||
|
||||
RenderNode renderNode = recordingCanvas.mNode;
|
||||
|
||||
// TODO: use stretchy RenderEffect and use internal API when it is ready
|
||||
// TODO: wrap existing RenderEffect
|
||||
renderNode.setRenderEffect(RenderEffect.createOffsetEffect(x, y));
|
||||
float left = renderNode.getLeft()
|
||||
+ min(mTmpPoints[0], mTmpPoints[2], mTmpPoints[4], mTmpPoints[6]);
|
||||
float top = renderNode.getTop()
|
||||
+ min(mTmpPoints[1], mTmpPoints[3], mTmpPoints[5], mTmpPoints[7]);
|
||||
float right = renderNode.getLeft()
|
||||
+ max(mTmpPoints[0], mTmpPoints[2], mTmpPoints[4], mTmpPoints[6]);
|
||||
float bottom = renderNode.getTop()
|
||||
+ max(mTmpPoints[1], mTmpPoints[3], mTmpPoints[5], mTmpPoints[7]);
|
||||
// assume rotations of increments of 90 degrees
|
||||
float x = mTmpPoints[10] - mTmpPoints[8];
|
||||
float width = right - left;
|
||||
float vecX = Math.max(-1f, Math.min(1f, x / width));
|
||||
float y = mTmpPoints[11] - mTmpPoints[9];
|
||||
float height = bottom - top;
|
||||
float vecY = Math.max(-1f, Math.min(1f, y / height));
|
||||
renderNode.stretch(
|
||||
left,
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
vecX * mStretchIntensity,
|
||||
vecY * mStretchIntensity,
|
||||
// TODO (njawad/mount) figure out proper stretch distance from UX
|
||||
// for now leverage placeholder logic if no stretch distance is provided to
|
||||
// consume the displacement ratio times the minimum of the width or height
|
||||
mStretchDistance > 0 ? mStretchDistance :
|
||||
(mDisplacement * Math.min(mWidth, mHeight))
|
||||
);
|
||||
}
|
||||
|
||||
boolean oneLastFrame = false;
|
||||
@@ -548,6 +605,18 @@ public class EdgeEffect {
|
||||
return mState != STATE_IDLE || oneLastFrame;
|
||||
}
|
||||
|
||||
private float min(float f1, float f2, float f3, float f4) {
|
||||
float min = Math.min(f1, f2);
|
||||
min = Math.min(min, f3);
|
||||
return Math.min(min, f4);
|
||||
}
|
||||
|
||||
private float max(float f1, float f2, float f3, float f4) {
|
||||
float max = Math.max(f1, f2);
|
||||
max = Math.max(max, f3);
|
||||
return Math.max(max, f4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maximum height that the edge effect will be drawn at given the original
|
||||
* {@link #setSize(int, int) input size}.
|
||||
|
||||
@@ -248,6 +248,26 @@ public class HorizontalScrollView extends FrameLayout {
|
||||
setRightEdgeEffectColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* API used for prototyping stretch effect parameters in framework sample apps
|
||||
* @hide
|
||||
*/
|
||||
public void setEdgeEffectIntensity(float intensity) {
|
||||
mEdgeGlowLeft.setMaxStretchIntensity(intensity);
|
||||
mEdgeGlowRight.setMaxStretchIntensity(intensity);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* API used for prototyping stretch effect parameters in the framework sample apps
|
||||
* @hide
|
||||
*/
|
||||
public void setStretchDistance(float distance) {
|
||||
mEdgeGlowLeft.setStretchDistance(distance);
|
||||
mEdgeGlowRight.setStretchDistance(distance);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the right edge effect color.
|
||||
*
|
||||
|
||||
@@ -280,6 +280,26 @@ public class ScrollView extends FrameLayout {
|
||||
setBottomEdgeEffectColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* API used for prototyping stretch effect parameters in framework sample apps
|
||||
* @hide
|
||||
*/
|
||||
public void setEdgeEffectIntensity(float intensity) {
|
||||
mEdgeGlowTop.setMaxStretchIntensity(intensity);
|
||||
mEdgeGlowBottom.setMaxStretchIntensity(intensity);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* API used for prototyping stretch effect parameters in the framework sample apps
|
||||
* @hide
|
||||
*/
|
||||
public void setStretchDistance(float distance) {
|
||||
mEdgeGlowTop.setStretchDistance(distance);
|
||||
mEdgeGlowBottom.setStretchDistance(distance);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bottom edge effect color.
|
||||
*
|
||||
|
||||
@@ -719,11 +719,11 @@ public final class RenderNode {
|
||||
/** @hide */
|
||||
public boolean stretch(float left, float top, float right, float bottom,
|
||||
float vecX, float vecY, float maxStretchAmount) {
|
||||
if (1.0 < vecX || vecX < -1.0) {
|
||||
throw new IllegalArgumentException("vecX must be in the range [-1, 1], was " + vecX);
|
||||
if (Float.isInfinite(vecX) || Float.isNaN(vecX)) {
|
||||
throw new IllegalArgumentException("vecX must be a finite, non-NaN value " + vecX);
|
||||
}
|
||||
if (1.0 < vecY || vecY < -1.0) {
|
||||
throw new IllegalArgumentException("vecY must be in the range [-1, 1], was " + vecY);
|
||||
if (Float.isInfinite(vecY) || Float.isNaN(vecY)) {
|
||||
throw new IllegalArgumentException("vecY must be a finite, non-NaN value " + vecY);
|
||||
}
|
||||
if (top >= bottom || left >= right) {
|
||||
throw new IllegalArgumentException(
|
||||
@@ -734,7 +734,16 @@ public final class RenderNode {
|
||||
throw new IllegalArgumentException(
|
||||
"The max stretch amount must be >0, got " + maxStretchAmount);
|
||||
}
|
||||
return nStretch(mNativeRenderNode, left, top, right, bottom, vecX, vecY, maxStretchAmount);
|
||||
return nStretch(
|
||||
mNativeRenderNode,
|
||||
left,
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
vecX,
|
||||
vecY,
|
||||
maxStretchAmount
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -552,8 +552,8 @@ public:
|
||||
|
||||
bool promotedToLayer() const {
|
||||
return mLayerProperties.mType == LayerType::None && fitsOnLayer() &&
|
||||
(mComputedFields.mNeedLayerForFunctors ||
|
||||
mLayerProperties.mImageFilter != nullptr ||
|
||||
(mComputedFields.mNeedLayerForFunctors || mLayerProperties.mImageFilter != nullptr ||
|
||||
!mLayerProperties.getStretchEffect().isEmpty() ||
|
||||
(!MathUtils::isZero(mPrimitiveFields.mAlpha) && mPrimitiveFields.mAlpha < 1 &&
|
||||
mPrimitiveFields.mHasOverlappingRendering));
|
||||
}
|
||||
|
||||
@@ -15,13 +15,195 @@
|
||||
*/
|
||||
|
||||
#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 {
|
||||
|
||||
sk_sp<SkImageFilter> StretchEffect::getImageFilter() const {
|
||||
// TODO: Implement & Cache
|
||||
// Probably need to use mutable to achieve caching
|
||||
return nullptr;
|
||||
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 uStretchAffectedDist;
|
||||
|
||||
// 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,
|
||||
uStretchAffectedDist,
|
||||
uDistanceStretchedX,
|
||||
uDistDiffX
|
||||
);
|
||||
computeOverscroll(
|
||||
outV,
|
||||
inV,
|
||||
uOverscrollY,
|
||||
uStretchAffectedDist,
|
||||
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 distanceNotStretchedX = maxStretchAmount / stretchArea.width();
|
||||
float distanceNotStretchedY = maxStretchAmount / stretchArea.height();
|
||||
float normOverScrollDistX = mStretchDirection.x();
|
||||
float normOverScrollDistY = mStretchDirection.y();
|
||||
float distanceStretchedX = maxStretchAmount / (1 + abs(normOverScrollDistX));
|
||||
float distanceStretchedY = maxStretchAmount / (1 + abs(normOverScrollDistY));
|
||||
float diffX = distanceStretchedX - distanceNotStretchedX;
|
||||
float diffY = distanceStretchedY - distanceNotStretchedY;
|
||||
float viewportWidth = stretchArea.width();
|
||||
float viewportHeight = stretchArea.height();
|
||||
|
||||
if (mBuilder == nullptr) {
|
||||
mBuilder = std::make_unique<SkRuntimeShaderBuilder>(getStretchEffect());
|
||||
}
|
||||
|
||||
mBuilder->child("uContentTexture") = snapshotImage->makeShader(
|
||||
SkTileMode::kClamp, SkTileMode::kClamp, SkSamplingOptions(SkFilterMode::kLinear));
|
||||
mBuilder->uniform("uStretchAffectedDist").set(&maxStretchAmount, 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
|
||||
@@ -18,9 +18,11 @@
|
||||
|
||||
#include "utils/MathUtils.h"
|
||||
|
||||
#include <SkImage.h>
|
||||
#include <SkImageFilter.h>
|
||||
#include <SkPoint.h>
|
||||
#include <SkRect.h>
|
||||
#include <SkImageFilter.h>
|
||||
#include <SkRuntimeEffect.h>
|
||||
|
||||
namespace android::uirenderer {
|
||||
|
||||
@@ -31,15 +33,27 @@ public:
|
||||
SmoothStep,
|
||||
};
|
||||
|
||||
StretchEffect(const SkRect& area, const SkVector& direction, float maxStretchAmount)
|
||||
: stretchArea(area), maxStretchAmount(maxStretchAmount), mStretchDirection(direction) {}
|
||||
|
||||
StretchEffect() {}
|
||||
|
||||
bool isEmpty() const {
|
||||
return MathUtils::isZero(stretchDirection.x())
|
||||
&& MathUtils::isZero(stretchDirection.y());
|
||||
return MathUtils::isZero(mStretchDirection.x()) && MathUtils::isZero(mStretchDirection.y());
|
||||
}
|
||||
|
||||
void setEmpty() {
|
||||
*this = StretchEffect{};
|
||||
}
|
||||
|
||||
StretchEffect& operator=(const StretchEffect& other) {
|
||||
this->stretchArea = other.stretchArea;
|
||||
this->mStretchDirection = other.mStretchDirection;
|
||||
this->mStretchFilter = nullptr;
|
||||
this->maxStretchAmount = other.maxStretchAmount;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void mergeWith(const StretchEffect& other) {
|
||||
if (other.isEmpty()) {
|
||||
return;
|
||||
@@ -48,7 +62,7 @@ public:
|
||||
*this = other;
|
||||
return;
|
||||
}
|
||||
stretchDirection += other.stretchDirection;
|
||||
setStretchDirection(mStretchDirection + other.mStretchDirection);
|
||||
if (isEmpty()) {
|
||||
return setEmpty();
|
||||
}
|
||||
@@ -56,11 +70,23 @@ public:
|
||||
maxStretchAmount = std::max(maxStretchAmount, other.maxStretchAmount);
|
||||
}
|
||||
|
||||
sk_sp<SkImageFilter> getImageFilter() const;
|
||||
sk_sp<SkImageFilter> getImageFilter(const sk_sp<SkImage>& snapshotImage) const;
|
||||
|
||||
SkRect stretchArea {0, 0, 0, 0};
|
||||
SkVector stretchDirection {0, 0};
|
||||
float maxStretchAmount = 0;
|
||||
|
||||
void setStretchDirection(const SkVector& direction) {
|
||||
mStretchFilter = nullptr;
|
||||
mStretchDirection = direction;
|
||||
}
|
||||
|
||||
const SkVector getStretchDirection() const { return mStretchDirection; }
|
||||
|
||||
private:
|
||||
static sk_sp<SkRuntimeEffect> getStretchEffect();
|
||||
mutable SkVector mStretchDirection{0, 0};
|
||||
mutable std::unique_ptr<SkRuntimeShaderBuilder> mBuilder;
|
||||
mutable sk_sp<SkImageFilter> mStretchFilter;
|
||||
};
|
||||
|
||||
} // namespace android::uirenderer
|
||||
|
||||
@@ -180,14 +180,13 @@ static jboolean android_view_RenderNode_clearStretch(CRITICAL_JNI_PARAMS_COMMA j
|
||||
}
|
||||
|
||||
static jboolean android_view_RenderNode_stretch(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr,
|
||||
jfloat left, jfloat top, jfloat right, jfloat bottom, jfloat vX, jfloat vY, jfloat max) {
|
||||
jfloat left, jfloat top, jfloat right,
|
||||
jfloat bottom, jfloat vX, jfloat vY, jfloat max) {
|
||||
StretchEffect effect =
|
||||
StretchEffect(SkRect::MakeLTRB(left, top, right, bottom), {.fX = vX, .fY = vY}, max);
|
||||
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
|
||||
renderNode->mutateStagingProperties().mutateLayerProperties().mutableStretchEffect().mergeWith(
|
||||
StretchEffect{
|
||||
.stretchArea = SkRect::MakeLTRB(left, top, right, bottom),
|
||||
.stretchDirection = {.fX = vX, .fY = vY},
|
||||
.maxStretchAmount = max
|
||||
});
|
||||
effect);
|
||||
renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
|
||||
return true;
|
||||
}
|
||||
@@ -659,10 +658,11 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
|
||||
return;
|
||||
}
|
||||
#ifdef __ANDROID__ // Layoutlib does not support CanvasContext
|
||||
SkVector stretchDirection = effect->getStretchDirection();
|
||||
env->CallVoidMethod(localref, gPositionListener_ApplyStretchMethod,
|
||||
info.canvasContext.getFrameNumber(), area.left, area.top,
|
||||
area.right, area.bottom, effect->stretchDirection.fX,
|
||||
effect->stretchDirection.fY, effect->maxStretchAmount);
|
||||
area.right, area.bottom, stretchDirection.fX, stretchDirection.fY,
|
||||
effect->maxStretchAmount);
|
||||
#endif
|
||||
env->DeleteLocalRef(localref);
|
||||
}
|
||||
@@ -702,106 +702,110 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject,
|
||||
const char* const kClassPathName = "android/graphics/RenderNode";
|
||||
|
||||
static const JNINativeMethod gMethods[] = {
|
||||
// ----------------------------------------------------------------------------
|
||||
// Regular JNI
|
||||
// ----------------------------------------------------------------------------
|
||||
{ "nCreate", "(Ljava/lang/String;)J", (void*) android_view_RenderNode_create },
|
||||
{ "nGetNativeFinalizer", "()J", (void*) android_view_RenderNode_getNativeFinalizer },
|
||||
{ "nOutput", "(J)V", (void*) android_view_RenderNode_output },
|
||||
{ "nGetUsageSize", "(J)I", (void*) android_view_RenderNode_getUsageSize },
|
||||
{ "nGetAllocatedSize", "(J)I", (void*) android_view_RenderNode_getAllocatedSize },
|
||||
{ "nAddAnimator", "(JJ)V", (void*) android_view_RenderNode_addAnimator },
|
||||
{ "nEndAllAnimators", "(J)V", (void*) android_view_RenderNode_endAllAnimators },
|
||||
{ "nRequestPositionUpdates", "(JLandroid/graphics/RenderNode$PositionUpdateListener;)V", (void*) android_view_RenderNode_requestPositionUpdates },
|
||||
// ----------------------------------------------------------------------------
|
||||
// Regular JNI
|
||||
// ----------------------------------------------------------------------------
|
||||
{"nCreate", "(Ljava/lang/String;)J", (void*)android_view_RenderNode_create},
|
||||
{"nGetNativeFinalizer", "()J", (void*)android_view_RenderNode_getNativeFinalizer},
|
||||
{"nOutput", "(J)V", (void*)android_view_RenderNode_output},
|
||||
{"nGetUsageSize", "(J)I", (void*)android_view_RenderNode_getUsageSize},
|
||||
{"nGetAllocatedSize", "(J)I", (void*)android_view_RenderNode_getAllocatedSize},
|
||||
{"nAddAnimator", "(JJ)V", (void*)android_view_RenderNode_addAnimator},
|
||||
{"nEndAllAnimators", "(J)V", (void*)android_view_RenderNode_endAllAnimators},
|
||||
{"nRequestPositionUpdates", "(JLandroid/graphics/RenderNode$PositionUpdateListener;)V",
|
||||
(void*)android_view_RenderNode_requestPositionUpdates},
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Critical JNI via @CriticalNative annotation in RenderNode.java
|
||||
// ----------------------------------------------------------------------------
|
||||
{ "nDiscardDisplayList", "(J)V", (void*) android_view_RenderNode_discardDisplayList },
|
||||
{ "nIsValid", "(J)Z", (void*) android_view_RenderNode_isValid },
|
||||
{ "nSetLayerType", "(JI)Z", (void*) android_view_RenderNode_setLayerType },
|
||||
{ "nGetLayerType", "(J)I", (void*) android_view_RenderNode_getLayerType },
|
||||
{ "nSetLayerPaint", "(JJ)Z", (void*) android_view_RenderNode_setLayerPaint },
|
||||
{ "nSetStaticMatrix", "(JJ)Z", (void*) android_view_RenderNode_setStaticMatrix },
|
||||
{ "nSetAnimationMatrix", "(JJ)Z", (void*) android_view_RenderNode_setAnimationMatrix },
|
||||
{ "nGetAnimationMatrix", "(JJ)Z", (void*) android_view_RenderNode_getAnimationMatrix },
|
||||
{ "nSetClipToBounds", "(JZ)Z", (void*) android_view_RenderNode_setClipToBounds },
|
||||
{ "nGetClipToBounds", "(J)Z", (void*) android_view_RenderNode_getClipToBounds },
|
||||
{ "nSetClipBounds", "(JIIII)Z", (void*) android_view_RenderNode_setClipBounds },
|
||||
{ "nSetClipBoundsEmpty", "(J)Z", (void*) android_view_RenderNode_setClipBoundsEmpty },
|
||||
{ "nSetProjectBackwards", "(JZ)Z", (void*) android_view_RenderNode_setProjectBackwards },
|
||||
{ "nSetProjectionReceiver","(JZ)Z", (void*) android_view_RenderNode_setProjectionReceiver },
|
||||
// ----------------------------------------------------------------------------
|
||||
// Critical JNI via @CriticalNative annotation in RenderNode.java
|
||||
// ----------------------------------------------------------------------------
|
||||
{"nDiscardDisplayList", "(J)V", (void*)android_view_RenderNode_discardDisplayList},
|
||||
{"nIsValid", "(J)Z", (void*)android_view_RenderNode_isValid},
|
||||
{"nSetLayerType", "(JI)Z", (void*)android_view_RenderNode_setLayerType},
|
||||
{"nGetLayerType", "(J)I", (void*)android_view_RenderNode_getLayerType},
|
||||
{"nSetLayerPaint", "(JJ)Z", (void*)android_view_RenderNode_setLayerPaint},
|
||||
{"nSetStaticMatrix", "(JJ)Z", (void*)android_view_RenderNode_setStaticMatrix},
|
||||
{"nSetAnimationMatrix", "(JJ)Z", (void*)android_view_RenderNode_setAnimationMatrix},
|
||||
{"nGetAnimationMatrix", "(JJ)Z", (void*)android_view_RenderNode_getAnimationMatrix},
|
||||
{"nSetClipToBounds", "(JZ)Z", (void*)android_view_RenderNode_setClipToBounds},
|
||||
{"nGetClipToBounds", "(J)Z", (void*)android_view_RenderNode_getClipToBounds},
|
||||
{"nSetClipBounds", "(JIIII)Z", (void*)android_view_RenderNode_setClipBounds},
|
||||
{"nSetClipBoundsEmpty", "(J)Z", (void*)android_view_RenderNode_setClipBoundsEmpty},
|
||||
{"nSetProjectBackwards", "(JZ)Z", (void*)android_view_RenderNode_setProjectBackwards},
|
||||
{"nSetProjectionReceiver", "(JZ)Z", (void*)android_view_RenderNode_setProjectionReceiver},
|
||||
|
||||
{ "nSetOutlineRoundRect", "(JIIIIFF)Z", (void*) android_view_RenderNode_setOutlineRoundRect },
|
||||
{ "nSetOutlinePath", "(JJF)Z", (void*) android_view_RenderNode_setOutlinePath },
|
||||
{ "nSetOutlineEmpty", "(J)Z", (void*) android_view_RenderNode_setOutlineEmpty },
|
||||
{ "nSetOutlineNone", "(J)Z", (void*) android_view_RenderNode_setOutlineNone },
|
||||
{ "nClearStretch", "(J)Z", (void*) android_view_RenderNode_clearStretch },
|
||||
{ "nStretch", "(JFFFFFFF)Z", (void*) android_view_RenderNode_stretch },
|
||||
{ "nHasShadow", "(J)Z", (void*) android_view_RenderNode_hasShadow },
|
||||
{ "nSetSpotShadowColor", "(JI)Z", (void*) android_view_RenderNode_setSpotShadowColor },
|
||||
{ "nGetSpotShadowColor", "(J)I", (void*) android_view_RenderNode_getSpotShadowColor },
|
||||
{ "nSetAmbientShadowColor","(JI)Z", (void*) android_view_RenderNode_setAmbientShadowColor },
|
||||
{ "nGetAmbientShadowColor","(J)I", (void*) android_view_RenderNode_getAmbientShadowColor },
|
||||
{ "nSetClipToOutline", "(JZ)Z", (void*) android_view_RenderNode_setClipToOutline },
|
||||
{ "nSetRevealClip", "(JZFFF)Z", (void*) android_view_RenderNode_setRevealClip },
|
||||
{"nSetOutlineRoundRect", "(JIIIIFF)Z", (void*)android_view_RenderNode_setOutlineRoundRect},
|
||||
{"nSetOutlinePath", "(JJF)Z", (void*)android_view_RenderNode_setOutlinePath},
|
||||
{"nSetOutlineEmpty", "(J)Z", (void*)android_view_RenderNode_setOutlineEmpty},
|
||||
{"nSetOutlineNone", "(J)Z", (void*)android_view_RenderNode_setOutlineNone},
|
||||
{"nClearStretch", "(J)Z", (void*)android_view_RenderNode_clearStretch},
|
||||
{"nStretch", "(JFFFFFFF)Z", (void*)android_view_RenderNode_stretch},
|
||||
{"nHasShadow", "(J)Z", (void*)android_view_RenderNode_hasShadow},
|
||||
{"nSetSpotShadowColor", "(JI)Z", (void*)android_view_RenderNode_setSpotShadowColor},
|
||||
{"nGetSpotShadowColor", "(J)I", (void*)android_view_RenderNode_getSpotShadowColor},
|
||||
{"nSetAmbientShadowColor", "(JI)Z", (void*)android_view_RenderNode_setAmbientShadowColor},
|
||||
{"nGetAmbientShadowColor", "(J)I", (void*)android_view_RenderNode_getAmbientShadowColor},
|
||||
{"nSetClipToOutline", "(JZ)Z", (void*)android_view_RenderNode_setClipToOutline},
|
||||
{"nSetRevealClip", "(JZFFF)Z", (void*)android_view_RenderNode_setRevealClip},
|
||||
|
||||
{ "nSetAlpha", "(JF)Z", (void*) android_view_RenderNode_setAlpha },
|
||||
{ "nSetRenderEffect", "(JJ)Z", (void*) android_view_RenderNode_setRenderEffect },
|
||||
{ "nSetHasOverlappingRendering", "(JZ)Z",
|
||||
(void*) android_view_RenderNode_setHasOverlappingRendering },
|
||||
{ "nSetUsageHint", "(JI)V", (void*) android_view_RenderNode_setUsageHint },
|
||||
{ "nSetElevation", "(JF)Z", (void*) android_view_RenderNode_setElevation },
|
||||
{ "nSetTranslationX", "(JF)Z", (void*) android_view_RenderNode_setTranslationX },
|
||||
{ "nSetTranslationY", "(JF)Z", (void*) android_view_RenderNode_setTranslationY },
|
||||
{ "nSetTranslationZ", "(JF)Z", (void*) android_view_RenderNode_setTranslationZ },
|
||||
{ "nSetRotation", "(JF)Z", (void*) android_view_RenderNode_setRotation },
|
||||
{ "nSetRotationX", "(JF)Z", (void*) android_view_RenderNode_setRotationX },
|
||||
{ "nSetRotationY", "(JF)Z", (void*) android_view_RenderNode_setRotationY },
|
||||
{ "nSetScaleX", "(JF)Z", (void*) android_view_RenderNode_setScaleX },
|
||||
{ "nSetScaleY", "(JF)Z", (void*) android_view_RenderNode_setScaleY },
|
||||
{ "nSetPivotX", "(JF)Z", (void*) android_view_RenderNode_setPivotX },
|
||||
{ "nSetPivotY", "(JF)Z", (void*) android_view_RenderNode_setPivotY },
|
||||
{ "nResetPivot", "(J)Z", (void*) android_view_RenderNode_resetPivot },
|
||||
{ "nSetCameraDistance", "(JF)Z", (void*) android_view_RenderNode_setCameraDistance },
|
||||
{ "nSetLeft", "(JI)Z", (void*) android_view_RenderNode_setLeft },
|
||||
{ "nSetTop", "(JI)Z", (void*) android_view_RenderNode_setTop },
|
||||
{ "nSetRight", "(JI)Z", (void*) android_view_RenderNode_setRight },
|
||||
{ "nSetBottom", "(JI)Z", (void*) android_view_RenderNode_setBottom },
|
||||
{ "nGetLeft", "(J)I", (void*) android_view_RenderNode_getLeft },
|
||||
{ "nGetTop", "(J)I", (void*) android_view_RenderNode_getTop },
|
||||
{ "nGetRight", "(J)I", (void*) android_view_RenderNode_getRight },
|
||||
{ "nGetBottom", "(J)I", (void*) android_view_RenderNode_getBottom },
|
||||
{ "nSetLeftTopRightBottom","(JIIII)Z", (void*) android_view_RenderNode_setLeftTopRightBottom },
|
||||
{ "nOffsetLeftAndRight", "(JI)Z", (void*) android_view_RenderNode_offsetLeftAndRight },
|
||||
{ "nOffsetTopAndBottom", "(JI)Z", (void*) android_view_RenderNode_offsetTopAndBottom },
|
||||
{"nSetAlpha", "(JF)Z", (void*)android_view_RenderNode_setAlpha},
|
||||
{"nSetRenderEffect", "(JJ)Z", (void*)android_view_RenderNode_setRenderEffect},
|
||||
{"nSetHasOverlappingRendering", "(JZ)Z",
|
||||
(void*)android_view_RenderNode_setHasOverlappingRendering},
|
||||
{"nSetUsageHint", "(JI)V", (void*)android_view_RenderNode_setUsageHint},
|
||||
{"nSetElevation", "(JF)Z", (void*)android_view_RenderNode_setElevation},
|
||||
{"nSetTranslationX", "(JF)Z", (void*)android_view_RenderNode_setTranslationX},
|
||||
{"nSetTranslationY", "(JF)Z", (void*)android_view_RenderNode_setTranslationY},
|
||||
{"nSetTranslationZ", "(JF)Z", (void*)android_view_RenderNode_setTranslationZ},
|
||||
{"nSetRotation", "(JF)Z", (void*)android_view_RenderNode_setRotation},
|
||||
{"nSetRotationX", "(JF)Z", (void*)android_view_RenderNode_setRotationX},
|
||||
{"nSetRotationY", "(JF)Z", (void*)android_view_RenderNode_setRotationY},
|
||||
{"nSetScaleX", "(JF)Z", (void*)android_view_RenderNode_setScaleX},
|
||||
{"nSetScaleY", "(JF)Z", (void*)android_view_RenderNode_setScaleY},
|
||||
{"nSetPivotX", "(JF)Z", (void*)android_view_RenderNode_setPivotX},
|
||||
{"nSetPivotY", "(JF)Z", (void*)android_view_RenderNode_setPivotY},
|
||||
{"nResetPivot", "(J)Z", (void*)android_view_RenderNode_resetPivot},
|
||||
{"nSetCameraDistance", "(JF)Z", (void*)android_view_RenderNode_setCameraDistance},
|
||||
{"nSetLeft", "(JI)Z", (void*)android_view_RenderNode_setLeft},
|
||||
{"nSetTop", "(JI)Z", (void*)android_view_RenderNode_setTop},
|
||||
{"nSetRight", "(JI)Z", (void*)android_view_RenderNode_setRight},
|
||||
{"nSetBottom", "(JI)Z", (void*)android_view_RenderNode_setBottom},
|
||||
{"nGetLeft", "(J)I", (void*)android_view_RenderNode_getLeft},
|
||||
{"nGetTop", "(J)I", (void*)android_view_RenderNode_getTop},
|
||||
{"nGetRight", "(J)I", (void*)android_view_RenderNode_getRight},
|
||||
{"nGetBottom", "(J)I", (void*)android_view_RenderNode_getBottom},
|
||||
{"nSetLeftTopRightBottom", "(JIIII)Z",
|
||||
(void*)android_view_RenderNode_setLeftTopRightBottom},
|
||||
{"nOffsetLeftAndRight", "(JI)Z", (void*)android_view_RenderNode_offsetLeftAndRight},
|
||||
{"nOffsetTopAndBottom", "(JI)Z", (void*)android_view_RenderNode_offsetTopAndBottom},
|
||||
|
||||
{ "nHasOverlappingRendering", "(J)Z", (void*) android_view_RenderNode_hasOverlappingRendering },
|
||||
{ "nGetClipToOutline", "(J)Z", (void*) android_view_RenderNode_getClipToOutline },
|
||||
{ "nGetAlpha", "(J)F", (void*) android_view_RenderNode_getAlpha },
|
||||
{ "nGetCameraDistance", "(J)F", (void*) android_view_RenderNode_getCameraDistance },
|
||||
{ "nGetScaleX", "(J)F", (void*) android_view_RenderNode_getScaleX },
|
||||
{ "nGetScaleY", "(J)F", (void*) android_view_RenderNode_getScaleY },
|
||||
{ "nGetElevation", "(J)F", (void*) android_view_RenderNode_getElevation },
|
||||
{ "nGetTranslationX", "(J)F", (void*) android_view_RenderNode_getTranslationX },
|
||||
{ "nGetTranslationY", "(J)F", (void*) android_view_RenderNode_getTranslationY },
|
||||
{ "nGetTranslationZ", "(J)F", (void*) android_view_RenderNode_getTranslationZ },
|
||||
{ "nGetRotation", "(J)F", (void*) android_view_RenderNode_getRotation },
|
||||
{ "nGetRotationX", "(J)F", (void*) android_view_RenderNode_getRotationX },
|
||||
{ "nGetRotationY", "(J)F", (void*) android_view_RenderNode_getRotationY },
|
||||
{ "nIsPivotExplicitlySet", "(J)Z", (void*) android_view_RenderNode_isPivotExplicitlySet },
|
||||
{ "nHasIdentityMatrix", "(J)Z", (void*) android_view_RenderNode_hasIdentityMatrix },
|
||||
{"nHasOverlappingRendering", "(J)Z",
|
||||
(void*)android_view_RenderNode_hasOverlappingRendering},
|
||||
{"nGetClipToOutline", "(J)Z", (void*)android_view_RenderNode_getClipToOutline},
|
||||
{"nGetAlpha", "(J)F", (void*)android_view_RenderNode_getAlpha},
|
||||
{"nGetCameraDistance", "(J)F", (void*)android_view_RenderNode_getCameraDistance},
|
||||
{"nGetScaleX", "(J)F", (void*)android_view_RenderNode_getScaleX},
|
||||
{"nGetScaleY", "(J)F", (void*)android_view_RenderNode_getScaleY},
|
||||
{"nGetElevation", "(J)F", (void*)android_view_RenderNode_getElevation},
|
||||
{"nGetTranslationX", "(J)F", (void*)android_view_RenderNode_getTranslationX},
|
||||
{"nGetTranslationY", "(J)F", (void*)android_view_RenderNode_getTranslationY},
|
||||
{"nGetTranslationZ", "(J)F", (void*)android_view_RenderNode_getTranslationZ},
|
||||
{"nGetRotation", "(J)F", (void*)android_view_RenderNode_getRotation},
|
||||
{"nGetRotationX", "(J)F", (void*)android_view_RenderNode_getRotationX},
|
||||
{"nGetRotationY", "(J)F", (void*)android_view_RenderNode_getRotationY},
|
||||
{"nIsPivotExplicitlySet", "(J)Z", (void*)android_view_RenderNode_isPivotExplicitlySet},
|
||||
{"nHasIdentityMatrix", "(J)Z", (void*)android_view_RenderNode_hasIdentityMatrix},
|
||||
|
||||
{ "nGetTransformMatrix", "(JJ)V", (void*) android_view_RenderNode_getTransformMatrix },
|
||||
{ "nGetInverseTransformMatrix","(JJ)V", (void*) android_view_RenderNode_getInverseTransformMatrix },
|
||||
{"nGetTransformMatrix", "(JJ)V", (void*)android_view_RenderNode_getTransformMatrix},
|
||||
{"nGetInverseTransformMatrix", "(JJ)V",
|
||||
(void*)android_view_RenderNode_getInverseTransformMatrix},
|
||||
|
||||
{ "nGetPivotX", "(J)F", (void*) android_view_RenderNode_getPivotX },
|
||||
{ "nGetPivotY", "(J)F", (void*) android_view_RenderNode_getPivotY },
|
||||
{ "nGetWidth", "(J)I", (void*) android_view_RenderNode_getWidth },
|
||||
{ "nGetHeight", "(J)I", (void*) android_view_RenderNode_getHeight },
|
||||
{ "nSetAllowForceDark", "(JZ)Z", (void*) android_view_RenderNode_setAllowForceDark },
|
||||
{ "nGetAllowForceDark", "(J)Z", (void*) android_view_RenderNode_getAllowForceDark },
|
||||
{ "nGetUniqueId", "(J)J", (void*) android_view_RenderNode_getUniqueId },
|
||||
{"nGetPivotX", "(J)F", (void*)android_view_RenderNode_getPivotX},
|
||||
{"nGetPivotY", "(J)F", (void*)android_view_RenderNode_getPivotY},
|
||||
{"nGetWidth", "(J)I", (void*)android_view_RenderNode_getWidth},
|
||||
{"nGetHeight", "(J)I", (void*)android_view_RenderNode_getHeight},
|
||||
{"nSetAllowForceDark", "(JZ)Z", (void*)android_view_RenderNode_setAllowForceDark},
|
||||
{"nGetAllowForceDark", "(J)Z", (void*)android_view_RenderNode_getAllowForceDark},
|
||||
{"nGetUniqueId", "(J)J", (void*)android_view_RenderNode_getUniqueId},
|
||||
};
|
||||
|
||||
int register_android_view_RenderNode(JNIEnv* env) {
|
||||
|
||||
@@ -169,8 +169,8 @@ void RenderNodeDrawable::forceDraw(SkCanvas* canvas) const {
|
||||
displayList->mProjectedOutline = nullptr;
|
||||
}
|
||||
|
||||
static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultiplier,
|
||||
SkPaint* paint) {
|
||||
static bool layerNeedsPaint(const sk_sp<SkImage>& snapshotImage, const LayerProperties& properties,
|
||||
float alphaMultiplier, SkPaint* paint) {
|
||||
if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
|
||||
properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr ||
|
||||
properties.getImageFilter() != nullptr || !properties.getStretchEffect().isEmpty()) {
|
||||
@@ -179,7 +179,8 @@ static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultip
|
||||
paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));
|
||||
|
||||
sk_sp<SkImageFilter> imageFilter = sk_ref_sp(properties.getImageFilter());
|
||||
sk_sp<SkImageFilter> stretchFilter = properties.getStretchEffect().getImageFilter();
|
||||
sk_sp<SkImageFilter> stretchFilter =
|
||||
properties.getStretchEffect().getImageFilter(snapshotImage);
|
||||
sk_sp<SkImageFilter> filter;
|
||||
if (imageFilter && stretchFilter) {
|
||||
filter = SkImageFilters::Compose(
|
||||
@@ -240,7 +241,8 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
|
||||
if (renderNode->getLayerSurface() && mComposeLayer) {
|
||||
SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
|
||||
SkPaint paint;
|
||||
layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
|
||||
sk_sp<SkImage> snapshotImage = renderNode->getLayerSurface()->makeImageSnapshot();
|
||||
layerNeedsPaint(snapshotImage, layerProperties, alphaMultiplier, &paint);
|
||||
SkSamplingOptions sampling(SkFilterMode::kLinear);
|
||||
|
||||
// surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so
|
||||
@@ -254,8 +256,8 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
|
||||
canvas->drawAnnotation(bounds, String8::format(
|
||||
"SurfaceID|%" PRId64, renderNode->uniqueId()).c_str(), nullptr);
|
||||
}
|
||||
canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot(), bounds,
|
||||
bounds, sampling, &paint, SkCanvas::kStrict_SrcRectConstraint);
|
||||
canvas->drawImageRect(snapshotImage, bounds, bounds, sampling, &paint,
|
||||
SkCanvas::kStrict_SrcRectConstraint);
|
||||
|
||||
if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
|
||||
renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
|
||||
|
||||
@@ -771,6 +771,24 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="StretchShaderActivity"
|
||||
android:label="RenderEffect/Stretch"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="com.android.test.hwui.TEST"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="EdgeEffectStretchActivity"
|
||||
android:label="RenderEffect/EdgeEffect stretch"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="com.android.test.hwui.TEST"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity android:name="TextActivity"
|
||||
android:label="Text/Simple Text"
|
||||
android:theme="@android:style/Theme.NoTitleBar"
|
||||
|
||||
88
tests/HwAccelerationTest/res/layout/stretch_layout.xml
Normal file
88
tests/HwAccelerationTest/res/layout/stretch_layout.xml
Normal file
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2010 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.
|
||||
-->
|
||||
<ScrollView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/scroll_view"
|
||||
android:edgeEffectType="stretch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<HorizontalScrollView
|
||||
android:id="@+id/horizontal_scroll_view"
|
||||
android:edgeEffectType="stretch"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:src="@drawable/sunset1"/>
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:src="@drawable/sunset1"/>
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:src="@drawable/sunset1"/>
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:src="@drawable/sunset1"/>
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:src="@drawable/sunset1"/>
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:src="@drawable/sunset1"/>
|
||||
</LinearLayout>
|
||||
</HorizontalScrollView>
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:src="@drawable/sunset1"/>
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:src="@drawable/sunset1"/>
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:src="@drawable/sunset1"/>
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:src="@drawable/sunset1"/>
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:src="@drawable/sunset1"/>
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="200dp"
|
||||
android:src="@drawable/sunset1"/>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.test.hwui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.widget.HorizontalScrollView;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
public class EdgeEffectStretchActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.stretch_layout);
|
||||
HorizontalScrollView hsv = findViewById(R.id.horizontal_scroll_view);
|
||||
hsv.setStretchDistance(50f);
|
||||
|
||||
ScrollView sv = findViewById(R.id.scroll_view);
|
||||
sv.setStretchDistance(50f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,540 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.test.hwui;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Activity;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.RecordingCanvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RenderEffect;
|
||||
import android.graphics.RuntimeShader;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class StretchShaderActivity extends Activity {
|
||||
|
||||
private static final float MAX_STRETCH_INTENSITY = 1.5f;
|
||||
private static final float STRETCH_AFFECTED_DISTANCE = 1.0f;
|
||||
|
||||
private float mScrollX = 0f;
|
||||
private float mScrollY = 0f;
|
||||
|
||||
private float mMaxStretchIntensity = MAX_STRETCH_INTENSITY;
|
||||
private float mStretchAffectedDistance = STRETCH_AFFECTED_DISTANCE;
|
||||
|
||||
private float mOverscrollX = 25f;
|
||||
private float mOverscrollY = 25f;
|
||||
|
||||
private RuntimeShader mRuntimeShader;
|
||||
private ImageView mImageView;
|
||||
private ImageView mTestImageView;
|
||||
|
||||
private Bitmap mBitmap;
|
||||
|
||||
private StretchDrawable mStretchDrawable = new StretchDrawable();
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
LinearLayout linearLayout = new LinearLayout(this);
|
||||
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
mBitmap = ((BitmapDrawable) getDrawable(R.drawable.sunset1)).getBitmap();
|
||||
mRuntimeShader = new RuntimeShader(SKSL, false);
|
||||
|
||||
BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP,
|
||||
Shader.TileMode.CLAMP);
|
||||
mRuntimeShader.setInputShader("uContentTexture", bitmapShader);
|
||||
|
||||
mImageView = new ImageView(this);
|
||||
|
||||
mImageView.setRenderEffect(RenderEffect.createShaderEffect(mRuntimeShader));
|
||||
mImageView.setImageDrawable(new ColorDrawable(Color.CYAN));
|
||||
|
||||
TextView overscrollXText = new TextView(this);
|
||||
overscrollXText.setText("Overscroll X");
|
||||
|
||||
SeekBar overscrollXBar = new SeekBar(this);
|
||||
overscrollXBar.setProgress(0);
|
||||
overscrollXBar.setMin(-50);
|
||||
overscrollXBar.setMax(50);
|
||||
overscrollXBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
mOverscrollX = progress;
|
||||
overscrollXText.setText("Overscroll X: " + mOverscrollX);
|
||||
updateShader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
TextView overscrollYText = new TextView(this);
|
||||
overscrollYText.setText("Overscroll Y");
|
||||
|
||||
SeekBar overscrollYBar = new SeekBar(this);
|
||||
overscrollYBar.setProgress(0);
|
||||
overscrollYBar.setMin(-50);
|
||||
overscrollYBar.setMax(50);
|
||||
overscrollYBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
mOverscrollY = progress;
|
||||
overscrollYText.setText("Overscroll Y: " + mOverscrollY);
|
||||
updateShader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
TextView scrollXText = new TextView(this);
|
||||
scrollXText.setText("Scroll X");
|
||||
SeekBar scrollXSeekBar = new SeekBar(this);
|
||||
scrollXSeekBar.setMin(0);
|
||||
scrollXSeekBar.setMax(100);
|
||||
scrollXSeekBar.setProgress(0);
|
||||
scrollXSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
mScrollX = (progress / 100f);
|
||||
scrollXText.setText("Scroll X: " + mScrollY);
|
||||
updateShader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
TextView scrollYText = new TextView(this);
|
||||
scrollYText.setText("Scroll Y");
|
||||
SeekBar scrollYSeekBar = new SeekBar(this);
|
||||
scrollYSeekBar.setMin(0);
|
||||
scrollYSeekBar.setMax(100);
|
||||
scrollYSeekBar.setProgress(0);
|
||||
scrollYSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
mScrollY = (progress / 100f);
|
||||
scrollYText.setText("Scroll Y: " + mScrollY);
|
||||
updateShader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
TextView stretchIntensityText = new TextView(this);
|
||||
int stretchProgress = (int) (mMaxStretchIntensity * 100);
|
||||
stretchIntensityText.setText("StretchIntensity: " + mMaxStretchIntensity);
|
||||
SeekBar stretchIntensitySeekbar = new SeekBar(this);
|
||||
stretchIntensitySeekbar.setProgress(stretchProgress);
|
||||
stretchIntensitySeekbar.setMin(1);
|
||||
stretchIntensitySeekbar.setMax((int) (MAX_STRETCH_INTENSITY * 100));
|
||||
stretchIntensitySeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
mMaxStretchIntensity = progress / 100f;
|
||||
stretchIntensityText.setText("StretchIntensity: " + mMaxStretchIntensity);
|
||||
updateShader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
TextView stretchDistanceText = new TextView(this);
|
||||
stretchDistanceText.setText("StretchDistance");
|
||||
SeekBar stretchDistanceSeekbar = new SeekBar(this);
|
||||
stretchDistanceSeekbar.setMin(0);
|
||||
stretchDistanceSeekbar.setProgress((int) (mStretchAffectedDistance * 100));
|
||||
stretchDistanceSeekbar.setMax(100);
|
||||
stretchDistanceSeekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
mStretchAffectedDistance = progress / 100f;
|
||||
stretchDistanceText.setText("StretchDistance: " + mStretchAffectedDistance);
|
||||
updateShader();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
linearLayout.addView(mImageView,
|
||||
new LinearLayout.LayoutParams(
|
||||
mBitmap.getWidth(),
|
||||
mBitmap.getHeight())
|
||||
);
|
||||
|
||||
linearLayout.addView(overscrollXText,
|
||||
new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
linearLayout.addView(overscrollXBar,
|
||||
new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
);
|
||||
|
||||
linearLayout.addView(overscrollYText,
|
||||
new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
linearLayout.addView(overscrollYBar,
|
||||
new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
);
|
||||
|
||||
linearLayout.addView(scrollXText,
|
||||
new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
|
||||
linearLayout.addView(scrollXSeekBar,
|
||||
new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
|
||||
linearLayout.addView(scrollYText,
|
||||
new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
|
||||
linearLayout.addView(scrollYSeekBar,
|
||||
new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
|
||||
linearLayout.addView(stretchIntensityText,
|
||||
new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
);
|
||||
|
||||
linearLayout.addView(stretchIntensitySeekbar,
|
||||
new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
);
|
||||
|
||||
linearLayout.addView(stretchDistanceText,
|
||||
new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
|
||||
linearLayout.addView(stretchDistanceSeekbar,
|
||||
new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
));
|
||||
|
||||
ImageView test = new ImageView(this);
|
||||
mStretchDrawable.setBitmap(mBitmap);
|
||||
test.setImageDrawable(mStretchDrawable);
|
||||
|
||||
mTestImageView = test;
|
||||
linearLayout.addView(test,
|
||||
new LinearLayout.LayoutParams(mBitmap.getWidth(), mBitmap.getHeight()));
|
||||
|
||||
setContentView(linearLayout);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
mImageView.getViewTreeObserver().addOnPreDrawListener(
|
||||
new ViewTreeObserver.OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
updateShader();
|
||||
mImageView.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateShader() {
|
||||
final float width = mImageView.getWidth();
|
||||
final float height = mImageView.getHeight();
|
||||
final float distanceNotStretched = mStretchAffectedDistance;
|
||||
final float normOverScrollDistX = mOverscrollX / width;
|
||||
final float normOverScrollDistY = mOverscrollY / height;
|
||||
final float distanceStretchedX =
|
||||
mStretchAffectedDistance
|
||||
/ (1 + Math.abs(normOverScrollDistX) * mMaxStretchIntensity);
|
||||
final float distanceStretchedY =
|
||||
mStretchAffectedDistance
|
||||
/ (1 + Math.abs(normOverScrollDistY) * mMaxStretchIntensity);
|
||||
final float diffX = distanceStretchedX - distanceNotStretched;
|
||||
final float diffY = distanceStretchedY - distanceNotStretched;
|
||||
float uScrollX = mScrollX;
|
||||
float uScrollY = mScrollY;
|
||||
|
||||
mRuntimeShader.setUniform("uMaxStretchIntensity", mMaxStretchIntensity);
|
||||
mRuntimeShader.setUniform("uStretchAffectedDist", mStretchAffectedDistance);
|
||||
mRuntimeShader.setUniform("uDistanceStretchedX", distanceStretchedX);
|
||||
mRuntimeShader.setUniform("uDistanceStretchedY", distanceStretchedY);
|
||||
mRuntimeShader.setUniform("uDistDiffX", diffX);
|
||||
mRuntimeShader.setUniform("uDistDiffY", diffY);
|
||||
mRuntimeShader.setUniform("uOverscrollX", normOverScrollDistX);
|
||||
mRuntimeShader.setUniform("uOverscrollY", normOverScrollDistY);
|
||||
mRuntimeShader.setUniform("uScrollX", uScrollX);
|
||||
mRuntimeShader.setUniform("uScrollY", uScrollY);
|
||||
mRuntimeShader.setUniform("viewportWidth", width);
|
||||
mRuntimeShader.setUniform("viewportHeight", height);
|
||||
|
||||
mImageView.setRenderEffect(RenderEffect.createShaderEffect(mRuntimeShader));
|
||||
|
||||
mStretchDrawable.setStretchDistance(mStretchAffectedDistance);
|
||||
mStretchDrawable.setOverscrollX(normOverScrollDistX);
|
||||
mStretchDrawable.setOverscrollY(normOverScrollDistY);
|
||||
}
|
||||
|
||||
private static class StretchDrawable extends Drawable {
|
||||
|
||||
private float mStretchDistance = 0;
|
||||
private float mOverScrollX = 0f;
|
||||
private float mOverScrollY = 0f;
|
||||
private Bitmap mBitmap = null;
|
||||
|
||||
public void setStretchDistance(float stretchDistance) {
|
||||
mStretchDistance = stretchDistance;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
public void setOverscrollX(float overscrollX) {
|
||||
mOverScrollX = overscrollX;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
public void setOverscrollY(float overscrollY) {
|
||||
mOverScrollY = overscrollY;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
public void setBitmap(Bitmap bitmap) {
|
||||
mBitmap = bitmap;
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
if (mStretchDistance > 0 && canvas instanceof RecordingCanvas) {
|
||||
Rect bounds = getBounds();
|
||||
((RecordingCanvas) canvas).mNode.stretch(
|
||||
0,
|
||||
0,
|
||||
bounds.width(),
|
||||
bounds.height(),
|
||||
mOverScrollX,
|
||||
mOverScrollY,
|
||||
mStretchDistance
|
||||
);
|
||||
}
|
||||
if (mBitmap != null) {
|
||||
canvas.drawBitmap(mBitmap, 0f, 0f, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter colorFilter) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String SKSL = "in shader uContentTexture;\n"
|
||||
+ "uniform float uMaxStretchIntensity; // multiplier to apply to scale effect\n"
|
||||
+ "uniform float uStretchAffectedDist; // Maximum percentage to stretch beyond bounds"
|
||||
+ " of target\n"
|
||||
+ "\n"
|
||||
+ "// Distance stretched as a function of the normalized overscroll times scale "
|
||||
+ "intensity\n"
|
||||
+ "uniform float uDistanceStretchedX;\n"
|
||||
+ "uniform float uDistanceStretchedY;\n"
|
||||
+ "uniform float uDistDiffX;\n"
|
||||
+ "uniform float uDistDiffY; // Difference between the peak stretch amount and "
|
||||
+ "overscroll amount normalized\n"
|
||||
+ "uniform float uScrollX; // Horizontal offset represented as a ratio of pixels "
|
||||
+ "divided by the target width\n"
|
||||
+ "uniform float uScrollY; // Vertical offset represented as a ratio of pixels "
|
||||
+ "divided by the target height\n"
|
||||
+ "uniform float uOverscrollX; // Normalized overscroll amount in the horizontal "
|
||||
+ "direction\n"
|
||||
+ "uniform float uOverscrollY; // Normalized overscroll amount in the vertical "
|
||||
+ "direction\n"
|
||||
+ "\n"
|
||||
+ "uniform float viewportWidth; // target height in pixels\n"
|
||||
+ "uniform float viewportHeight; // target width in pixels\n"
|
||||
+ "\n"
|
||||
+ "vec4 main(vec2 coord) {\n"
|
||||
+ "\n"
|
||||
+ " // Normalize SKSL pixel coordinate into a unit vector\n"
|
||||
+ " vec2 uv = vec2(coord.x / viewportWidth, coord.y / viewportHeight);\n"
|
||||
+ " float inU = uv.x;\n"
|
||||
+ " float inV = uv.y;\n"
|
||||
+ " float outU;\n"
|
||||
+ " float outV;\n"
|
||||
+ " float stretchIntensity;\n"
|
||||
+ "\n"
|
||||
+ " // Add the normalized scroll position within scrolling list\n"
|
||||
+ " inU += uScrollX;\n"
|
||||
+ " inV += uScrollY;\n"
|
||||
+ "\n"
|
||||
+ " outU = inU;\n"
|
||||
+ " outV = inV;\n"
|
||||
+ " if (uOverscrollX > 0) {\n"
|
||||
+ " if (inU <= uStretchAffectedDist) {\n"
|
||||
+ " inU = uStretchAffectedDist - inU;\n"
|
||||
+ " float posBasedVariation = smoothstep(0., uStretchAffectedDist, inU);\n"
|
||||
+ " stretchIntensity = uMaxStretchIntensity * uOverscrollX * "
|
||||
+ "posBasedVariation;\n"
|
||||
+ " outU = uDistanceStretchedX - (inU / (1. + stretchIntensity));\n"
|
||||
+ " } else {\n"
|
||||
+ " outU = uDistDiffX + inU;\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "\n"
|
||||
+ " if (uOverscrollX < 0) {\n"
|
||||
+ " float stretchAffectedDist = 1. - uStretchAffectedDist;\n"
|
||||
+ " if (inU >= stretchAffectedDist) {\n"
|
||||
+ " inU = inU - stretchAffectedDist;\n"
|
||||
+ " float posBasedVariation = (smoothstep(0., uStretchAffectedDist, "
|
||||
+ "inU));\n"
|
||||
+ " stretchIntensity = uMaxStretchIntensity * (-uOverscrollX) * "
|
||||
+ "posBasedVariation;\n"
|
||||
+ " outU = 1 - (uDistanceStretchedX - (inU / (1. + stretchIntensity)))"
|
||||
+ ";\n"
|
||||
+ " } else if (inU < stretchAffectedDist) {\n"
|
||||
+ " outU = -uDistDiffX + inU;\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "\n"
|
||||
+ " if (uOverscrollY > 0) {\n"
|
||||
+ " if (inV <= uStretchAffectedDist) {\n"
|
||||
+ " inV = uStretchAffectedDist - inV;\n"
|
||||
+ " float posBasedVariation = smoothstep(0., uStretchAffectedDist, inV);\n"
|
||||
+ " stretchIntensity = uMaxStretchIntensity * uOverscrollY * "
|
||||
+ "posBasedVariation;\n"
|
||||
+ " outV = uDistanceStretchedY - (inV / (1. + stretchIntensity));\n"
|
||||
+ " } else if (inV >= uStretchAffectedDist) {\n"
|
||||
+ " outV = uDistDiffY + inV;\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "\n"
|
||||
+ " if (uOverscrollY < 0) {\n"
|
||||
+ " float stretchAffectedDist = 1. - uStretchAffectedDist;\n"
|
||||
+ " if (inV >= stretchAffectedDist) {\n"
|
||||
+ " inV = inV - stretchAffectedDist;\n"
|
||||
+ " float posBasedVariation = (smoothstep(0., uStretchAffectedDist, inV));\n"
|
||||
+ " stretchIntensity = uMaxStretchIntensity * (-uOverscrollY) * "
|
||||
+ "posBasedVariation;\n"
|
||||
+ " outV = 1 - (uDistanceStretchedY - (inV / (1. + stretchIntensity)));\n"
|
||||
+ " } else if (inV < stretchAffectedDist) {\n"
|
||||
+ " outV = -uDistDiffY + inV;\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "\n"
|
||||
+ " uv.x = outU;\n"
|
||||
+ " uv.y = outV;\n"
|
||||
+ " coord.x = uv.x * viewportWidth;\n"
|
||||
+ " coord.y = uv.y * viewportHeight;\n"
|
||||
+ " return sample(uContentTexture, coord);\n"
|
||||
+ "}";
|
||||
}
|
||||
Reference in New Issue
Block a user