Add stretch plumbing
Bug: 179047472
Test: builds & boots
Change-Id: I30b5d6683160af598f98555f61af1cf5a7639bbe
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 615bf4d..ce1d96c 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -435,6 +435,7 @@
"canvas/CanvasFrontend.cpp",
"canvas/CanvasOpBuffer.cpp",
"canvas/CanvasOpRasterizer.cpp",
+ "effects/StretchEffect.cpp",
"pipeline/skia/SkiaDisplayList.cpp",
"pipeline/skia/SkiaRecordingCanvas.cpp",
"pipeline/skia/RenderNodeDrawable.cpp",
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index aeb60e6..609706e 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -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 @@
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 @@
SkBlendMode mMode;
sk_sp<SkColorFilter> mColorFilter;
sk_sp<SkImageFilter> mImageFilter;
+ StretchEffect mStretchEffect;
};
/*
diff --git a/libs/hwui/effects/StretchEffect.cpp b/libs/hwui/effects/StretchEffect.cpp
new file mode 100644
index 0000000..51cbc75
--- /dev/null
+++ b/libs/hwui/effects/StretchEffect.cpp
@@ -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
\ No newline at end of file
diff --git a/libs/hwui/effects/StretchEffect.h b/libs/hwui/effects/StretchEffect.h
new file mode 100644
index 0000000..7dfd639
--- /dev/null
+++ b/libs/hwui/effects/StretchEffect.h
@@ -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
diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp
index 8b35d96..8023968 100644
--- a/libs/hwui/jni/android_graphics_RenderNode.cpp
+++ b/libs/hwui/jni/android_graphics_RenderNode.cpp
@@ -166,6 +166,31 @@
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 @@
{ "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 },
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 070a765..26c1674 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -20,6 +20,8 @@
#include "SkiaDisplayList.h"
#include "utils/TraceUtils.h"
+#include <include/effects/SkImageFilters.h>
+
#include <optional>
namespace android {
@@ -172,11 +174,25 @@
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;