Add stretch plumbing
Bug: 179047472 Test: builds & boots Change-Id: I30b5d6683160af598f98555f61af1cf5a7639bbe
This commit is contained in:
@@ -21367,6 +21367,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
||||
int height = mBottom - mTop;
|
||||
int layerType = getLayerType();
|
||||
|
||||
// Hacky hack: Reset any stretch effects as those are applied during the draw pass
|
||||
// instead of being "stateful" like other RenderNode properties
|
||||
renderNode.clearStretch();
|
||||
|
||||
final RecordingCanvas canvas = renderNode.beginRecording(width, height);
|
||||
|
||||
try {
|
||||
@@ -22793,6 +22797,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
|
||||
final Rect bounds = drawable.getBounds();
|
||||
final int width = bounds.width();
|
||||
final int height = bounds.height();
|
||||
|
||||
// Hacky hack: Reset any stretch effects as those are applied during the draw pass
|
||||
// instead of being "stateful" like other RenderNode properties
|
||||
renderNode.clearStretch();
|
||||
|
||||
final RecordingCanvas canvas = renderNode.beginRecording(width, height);
|
||||
|
||||
// Reverse left/top translation done by drawable canvas, which will
|
||||
|
||||
@@ -693,6 +693,32 @@ public final class RenderNode {
|
||||
throw new IllegalArgumentException("Unrecognized outline?");
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public boolean clearStretch() {
|
||||
return nClearStretch(mNativeRenderNode);
|
||||
}
|
||||
|
||||
/** @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 (1.0 < vecY || vecY < -1.0) {
|
||||
throw new IllegalArgumentException("vecY must be in the range [-1, 1], was " + vecY);
|
||||
}
|
||||
if (top <= bottom || right <= left) {
|
||||
throw new IllegalArgumentException(
|
||||
"Stretch region must not be empty, got "
|
||||
+ new RectF(left, top, right, bottom).toString());
|
||||
}
|
||||
if (maxStretchAmount <= 0.0f) {
|
||||
throw new IllegalArgumentException(
|
||||
"The max stretch amount must be >0, got " + maxStretchAmount);
|
||||
}
|
||||
return nStretch(mNativeRenderNode, left, top, right, bottom, vecX, vecY, maxStretchAmount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the RenderNode has a shadow. That is, if the combination of {@link #getElevation()}
|
||||
* and {@link #getTranslationZ()} is greater than zero, there is an {@link Outline} set with
|
||||
@@ -1637,6 +1663,13 @@ public final class RenderNode {
|
||||
@CriticalNative
|
||||
private static native boolean nSetOutlineNone(long renderNode);
|
||||
|
||||
@CriticalNative
|
||||
private static native boolean nClearStretch(long renderNode);
|
||||
|
||||
@CriticalNative
|
||||
private static native boolean nStretch(long renderNode, float left, float top, float right,
|
||||
float bottom, float vecX, float vecY, float maxStretch);
|
||||
|
||||
@CriticalNative
|
||||
private static native boolean nHasShadow(long renderNode);
|
||||
|
||||
|
||||
@@ -435,6 +435,7 @@ cc_defaults {
|
||||
"canvas/CanvasFrontend.cpp",
|
||||
"canvas/CanvasOpBuffer.cpp",
|
||||
"canvas/CanvasOpRasterizer.cpp",
|
||||
"effects/StretchEffect.cpp",
|
||||
"pipeline/skia/SkiaDisplayList.cpp",
|
||||
"pipeline/skia/SkiaRecordingCanvas.cpp",
|
||||
"pipeline/skia/RenderNodeDrawable.cpp",
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "Outline.h"
|
||||
#include "Rect.h"
|
||||
#include "RevealClip.h"
|
||||
#include "effects/StretchEffect.h"
|
||||
#include "utils/MathUtils.h"
|
||||
#include "utils/PaintUtils.h"
|
||||
|
||||
@@ -98,6 +99,10 @@ public:
|
||||
|
||||
SkImageFilter* getImageFilter() const { return mImageFilter.get(); }
|
||||
|
||||
const StretchEffect& getStretchEffect() const { return mStretchEffect; }
|
||||
|
||||
StretchEffect& mutableStretchEffect() { return mStretchEffect; }
|
||||
|
||||
// Sets alpha, xfermode, and colorfilter from an SkPaint
|
||||
// paint may be NULL, in which case defaults will be set
|
||||
bool setFromPaint(const SkPaint* paint);
|
||||
@@ -124,6 +129,7 @@ private:
|
||||
SkBlendMode mMode;
|
||||
sk_sp<SkColorFilter> mColorFilter;
|
||||
sk_sp<SkImageFilter> mImageFilter;
|
||||
StretchEffect mStretchEffect;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
27
libs/hwui/effects/StretchEffect.cpp
Normal file
27
libs/hwui/effects/StretchEffect.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
namespace android::uirenderer {
|
||||
|
||||
sk_sp<SkImageFilter> StretchEffect::getImageFilter() const {
|
||||
// TODO: Implement & Cache
|
||||
// Probably need to use mutable to achieve caching
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace android::uirenderer
|
||||
66
libs/hwui/effects/StretchEffect.h
Normal file
66
libs/hwui/effects/StretchEffect.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "utils/MathUtils.h"
|
||||
|
||||
#include <SkPoint.h>
|
||||
#include <SkRect.h>
|
||||
#include <SkImageFilter.h>
|
||||
|
||||
namespace android::uirenderer {
|
||||
|
||||
// TODO: Inherit from base RenderEffect type?
|
||||
class StretchEffect {
|
||||
public:
|
||||
enum class StretchInterpolator {
|
||||
SmoothStep,
|
||||
};
|
||||
|
||||
bool isEmpty() const {
|
||||
return MathUtils::isZero(stretchDirection.x())
|
||||
&& MathUtils::isZero(stretchDirection.y());
|
||||
}
|
||||
|
||||
void setEmpty() {
|
||||
*this = StretchEffect{};
|
||||
}
|
||||
|
||||
void mergeWith(const StretchEffect& other) {
|
||||
if (other.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (isEmpty()) {
|
||||
*this = other;
|
||||
return;
|
||||
}
|
||||
stretchDirection += other.stretchDirection;
|
||||
if (isEmpty()) {
|
||||
return setEmpty();
|
||||
}
|
||||
stretchArea.join(other.stretchArea);
|
||||
maxStretchAmount = std::max(maxStretchAmount, other.maxStretchAmount);
|
||||
}
|
||||
|
||||
sk_sp<SkImageFilter> getImageFilter() const;
|
||||
|
||||
SkRect stretchArea {0, 0, 0, 0};
|
||||
SkVector stretchDirection {0, 0};
|
||||
float maxStretchAmount = 0;
|
||||
};
|
||||
|
||||
} // namespace android::uirenderer
|
||||
@@ -166,6 +166,31 @@ static jboolean android_view_RenderNode_setOutlineNone(CRITICAL_JNI_PARAMS_COMMA
|
||||
return true;
|
||||
}
|
||||
|
||||
static jboolean android_view_RenderNode_clearStretch(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
|
||||
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
|
||||
auto& stretch = renderNode->mutateStagingProperties()
|
||||
.mutateLayerProperties().mutableStretchEffect();
|
||||
if (stretch.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
stretch.setEmpty();
|
||||
renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
|
||||
return true;
|
||||
}
|
||||
|
||||
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) {
|
||||
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
|
||||
});
|
||||
renderNode->setPropertyFieldsDirty(RenderNode::GENERIC);
|
||||
return true;
|
||||
}
|
||||
|
||||
static jboolean android_view_RenderNode_hasShadow(CRITICAL_JNI_PARAMS_COMMA jlong renderNodePtr) {
|
||||
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
|
||||
return renderNode->stagingProperties().hasShadow();
|
||||
@@ -678,6 +703,8 @@ static const JNINativeMethod gMethods[] = {
|
||||
{ "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 },
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
#include "SkiaDisplayList.h"
|
||||
#include "utils/TraceUtils.h"
|
||||
|
||||
#include <include/effects/SkImageFilters.h>
|
||||
|
||||
#include <optional>
|
||||
|
||||
namespace android {
|
||||
@@ -172,11 +174,25 @@ static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultip
|
||||
paint->setFilterQuality(kLow_SkFilterQuality);
|
||||
if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
|
||||
properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr ||
|
||||
properties.getImageFilter() != nullptr) {
|
||||
properties.getImageFilter() != nullptr || !properties.getStretchEffect().isEmpty()) {
|
||||
paint->setAlpha(properties.alpha() * alphaMultiplier);
|
||||
paint->setBlendMode(properties.xferMode());
|
||||
paint->setColorFilter(sk_ref_sp(properties.getColorFilter()));
|
||||
paint->setImageFilter(sk_ref_sp(properties.getImageFilter()));
|
||||
|
||||
sk_sp<SkImageFilter> imageFilter = sk_ref_sp(properties.getImageFilter());
|
||||
sk_sp<SkImageFilter> stretchFilter = properties.getStretchEffect().getImageFilter();
|
||||
sk_sp<SkImageFilter> filter;
|
||||
if (imageFilter && stretchFilter) {
|
||||
filter = SkImageFilters::Compose(
|
||||
std::move(stretchFilter),
|
||||
std::move(imageFilter)
|
||||
);
|
||||
} else if (stretchFilter) {
|
||||
filter = std::move(stretchFilter);
|
||||
} else {
|
||||
filter = std::move(imageFilter);
|
||||
}
|
||||
paint->setImageFilter(std::move(filter));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user