summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/androidfw/Asset.cpp92
-rw-r--r--libs/androidfw/AssetManager.cpp2
-rw-r--r--libs/androidfw/LocaleData.cpp31
-rw-r--r--libs/androidfw/include/androidfw/Asset.h15
-rw-r--r--libs/androidfw/include/androidfw/AssetManager.h6
-rw-r--r--libs/androidfw/tests/Android.mk1
-rw-r--r--libs/androidfw/tests/Asset_test.cpp37
-rw-r--r--libs/androidfw/tests/ConfigLocale_test.cpp67
-rw-r--r--libs/hwui/FrameBuilder.cpp2
-rw-r--r--libs/hwui/Interpolator.cpp3
-rw-r--r--libs/hwui/VectorDrawable.cpp2
-rw-r--r--libs/hwui/VectorDrawable.h13
-rw-r--r--libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp68
-rw-r--r--libs/hwui/tests/unit/FrameBuilderTests.cpp29
14 files changed, 247 insertions, 121 deletions
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index 2cfa6666e9ab..8e8c6a2e25a2 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -52,6 +52,47 @@ static int32_t gCount = 0;
static Asset* gHead = NULL;
static Asset* gTail = NULL;
+void Asset::registerAsset(Asset* asset)
+{
+ AutoMutex _l(gAssetLock);
+ gCount++;
+ asset->mNext = asset->mPrev = NULL;
+ if (gTail == NULL) {
+ gHead = gTail = asset;
+ } else {
+ asset->mPrev = gTail;
+ gTail->mNext = asset;
+ gTail = asset;
+ }
+
+ if (kIsDebug) {
+ ALOGI("Creating Asset %p #%d\n", asset, gCount);
+ }
+}
+
+void Asset::unregisterAsset(Asset* asset)
+{
+ AutoMutex _l(gAssetLock);
+ gCount--;
+ if (gHead == asset) {
+ gHead = asset->mNext;
+ }
+ if (gTail == asset) {
+ gTail = asset->mPrev;
+ }
+ if (asset->mNext != NULL) {
+ asset->mNext->mPrev = asset->mPrev;
+ }
+ if (asset->mPrev != NULL) {
+ asset->mPrev->mNext = asset->mNext;
+ }
+ asset->mNext = asset->mPrev = NULL;
+
+ if (kIsDebug) {
+ ALOGI("Destroying Asset in %p #%d\n", asset, gCount);
+ }
+}
+
int32_t Asset::getGlobalCount()
{
AutoMutex _l(gAssetLock);
@@ -79,43 +120,8 @@ String8 Asset::getAssetAllocations()
}
Asset::Asset(void)
- : mAccessMode(ACCESS_UNKNOWN)
+ : mAccessMode(ACCESS_UNKNOWN), mNext(NULL), mPrev(NULL)
{
- AutoMutex _l(gAssetLock);
- gCount++;
- mNext = mPrev = NULL;
- if (gTail == NULL) {
- gHead = gTail = this;
- } else {
- mPrev = gTail;
- gTail->mNext = this;
- gTail = this;
- }
- if (kIsDebug) {
- ALOGI("Creating Asset %p #%d\n", this, gCount);
- }
-}
-
-Asset::~Asset(void)
-{
- AutoMutex _l(gAssetLock);
- gCount--;
- if (gHead == this) {
- gHead = mNext;
- }
- if (gTail == this) {
- gTail = mPrev;
- }
- if (mNext != NULL) {
- mNext->mPrev = mPrev;
- }
- if (mPrev != NULL) {
- mPrev->mNext = mNext;
- }
- mNext = mPrev = NULL;
- if (kIsDebug) {
- ALOGI("Destroying Asset in %p #%d\n", this, gCount);
- }
}
/*
@@ -361,6 +367,9 @@ off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t m
_FileAsset::_FileAsset(void)
: mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL)
{
+ // Register the Asset with the global list here after it is fully constructed and its
+ // vtable pointer points to this concrete type. b/31113965
+ registerAsset(this);
}
/*
@@ -369,6 +378,10 @@ _FileAsset::_FileAsset(void)
_FileAsset::~_FileAsset(void)
{
close();
+
+ // Unregister the Asset from the global list here before it is destructed and while its vtable
+ // pointer still points to this concrete type. b/31113965
+ unregisterAsset(this);
}
/*
@@ -685,6 +698,9 @@ _CompressedAsset::_CompressedAsset(void)
: mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0),
mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL)
{
+ // Register the Asset with the global list here after it is fully constructed and its
+ // vtable pointer points to this concrete type. b/31113965
+ registerAsset(this);
}
/*
@@ -693,6 +709,10 @@ _CompressedAsset::_CompressedAsset(void)
_CompressedAsset::~_CompressedAsset(void)
{
close();
+
+ // Unregister the Asset from the global list here before it is destructed and while its vtable
+ // pointer still points to this concrete type. b/31113965
+ unregisterAsset(this);
}
/*
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 81e91a80783b..80968e51d48c 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -79,7 +79,7 @@ static volatile int32_t gCount = 0;
const char* AssetManager::RESOURCES_FILENAME = "resources.arsc";
const char* AssetManager::IDMAP_BIN = "/system/bin/idmap";
const char* AssetManager::OVERLAY_DIR = "/vendor/overlay";
-const char* AssetManager::OVERLAY_SKU_DIR_PROPERTY = "ro.boot.vendor.overlay.sku";
+const char* AssetManager::OVERLAY_THEME_DIR_PROPERTY = "ro.boot.vendor.overlay.theme";
const char* AssetManager::TARGET_PACKAGE_NAME = "android";
const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk";
const char* AssetManager::IDMAP_DIR = "/data/resource-cache";
diff --git a/libs/androidfw/LocaleData.cpp b/libs/androidfw/LocaleData.cpp
index 038ef5839fe2..889d166d853b 100644
--- a/libs/androidfw/LocaleData.cpp
+++ b/libs/androidfw/LocaleData.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <array>
#include <cstdint>
#include <cstdlib>
#include <cstring>
@@ -121,6 +122,16 @@ inline bool isRepresentative(uint32_t language_and_region, const char* script) {
return (REPRESENTATIVE_LOCALES.count(packed_locale) != 0);
}
+const uint32_t US_SPANISH = 0x65735553lu; // es-US
+const uint32_t MEXICAN_SPANISH = 0x65734D58lu; // es-MX
+const uint32_t LATIN_AMERICAN_SPANISH = 0x6573A424lu; // es-419
+
+// The two locales es-US and es-MX are treated as special fallbacks for es-419.
+// If there is no es-419, they are considered its equivalent.
+inline bool isSpecialSpanish(uint32_t language_and_region) {
+ return (language_and_region == US_SPANISH || language_and_region == MEXICAN_SPANISH);
+}
+
int localeDataCompareRegions(
const char* left_region, const char* right_region,
const char* requested_language, const char* requested_script,
@@ -129,18 +140,30 @@ int localeDataCompareRegions(
if (left_region[0] == right_region[0] && left_region[1] == right_region[1]) {
return 0;
}
- const uint32_t left = packLocale(requested_language, left_region);
- const uint32_t right = packLocale(requested_language, right_region);
+ uint32_t left = packLocale(requested_language, left_region);
+ uint32_t right = packLocale(requested_language, right_region);
const uint32_t request = packLocale(requested_language, requested_region);
+ // If one and only one of the two locales is a special Spanish locale, we
+ // replace it with es-419. We don't do the replacement if the other locale
+ // is already es-419, or both locales are special Spanish locales (when
+ // es-US is being compared to es-MX).
+ const bool leftIsSpecialSpanish = isSpecialSpanish(left);
+ const bool rightIsSpecialSpanish = isSpecialSpanish(right);
+ if (leftIsSpecialSpanish && !rightIsSpecialSpanish && right != LATIN_AMERICAN_SPANISH) {
+ left = LATIN_AMERICAN_SPANISH;
+ } else if (rightIsSpecialSpanish && !leftIsSpecialSpanish && left != LATIN_AMERICAN_SPANISH) {
+ right = LATIN_AMERICAN_SPANISH;
+ }
+
uint32_t request_ancestors[MAX_PARENT_DEPTH+1];
ssize_t left_right_index;
// Find the parents of the request, but stop as soon as we saw left or right
- const uint32_t left_and_right[] = {left, right};
+ const std::array<uint32_t, 2> left_and_right = {{left, right}};
const size_t ancestor_count = findAncestors(
request_ancestors, &left_right_index,
request, requested_script,
- left_and_right, sizeof(left_and_right)/sizeof(left_and_right[0]));
+ left_and_right.data(), left_and_right.size());
if (left_right_index == 0) { // We saw left earlier
return 1;
}
diff --git a/libs/androidfw/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
index 36efbe5a48e9..2cd8c0bf3c78 100644
--- a/libs/androidfw/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -44,7 +44,7 @@ namespace android {
*/
class Asset {
public:
- virtual ~Asset(void);
+ virtual ~Asset(void) = default;
static int32_t getGlobalCount();
static String8 getAssetAllocations();
@@ -119,6 +119,19 @@ public:
const char* getAssetSource(void) const { return mAssetSource.string(); }
protected:
+ /*
+ * Adds this Asset to the global Asset list for debugging and
+ * accounting.
+ * Concrete subclasses must call this in their constructor.
+ */
+ static void registerAsset(Asset* asset);
+
+ /*
+ * Removes this Asset from the global Asset list.
+ * Concrete subclasses must call this in their destructor.
+ */
+ static void unregisterAsset(Asset* asset);
+
Asset(void); // constructor; only invoked indirectly
/* handle common seek() housekeeping */
diff --git a/libs/androidfw/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
index b39dccf44a8b..594dba5049dd 100644
--- a/libs/androidfw/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -64,11 +64,11 @@ public:
static const char* IDMAP_BIN;
static const char* OVERLAY_DIR;
/*
- * If OVERLAY_SKU_DIR_PROPERTY is set, search for runtime resource overlay
- * APKs in OVERLAY_DIR/<value of OVERLAY_SKU_DIR_PROPERTY> rather than in
+ * If OVERLAY_THEME_DIR_PROPERTY is set, search for runtime resource overlay
+ * APKs in OVERLAY_DIR/<value of OVERLAY_THEME_DIR_PROPERTY> in addition to
* OVERLAY_DIR.
*/
- static const char* OVERLAY_SKU_DIR_PROPERTY;
+ static const char* OVERLAY_THEME_DIR_PROPERTY;
static const char* TARGET_PACKAGE_NAME;
static const char* TARGET_APK_PATH;
static const char* IDMAP_DIR;
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index 2bc026b79ca2..1fe1773578fa 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -22,6 +22,7 @@ LOCAL_PATH:= $(call my-dir)
testFiles := \
AppAsLib_test.cpp \
+ Asset_test.cpp \
AttributeFinder_test.cpp \
ByteBucketArray_test.cpp \
Config_test.cpp \
diff --git a/libs/androidfw/tests/Asset_test.cpp b/libs/androidfw/tests/Asset_test.cpp
new file mode 100644
index 000000000000..45c8cef92918
--- /dev/null
+++ b/libs/androidfw/tests/Asset_test.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 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 <androidfw/Asset.h>
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+TEST(AssetTest, FileAssetRegistersItself) {
+ const int32_t count = Asset::getGlobalCount();
+ Asset* asset = new _FileAsset();
+ EXPECT_EQ(count + 1, Asset::getGlobalCount());
+ delete asset;
+ EXPECT_EQ(count, Asset::getGlobalCount());
+}
+
+TEST(AssetTest, CompressedAssetRegistersItself) {
+ const int32_t count = Asset::getGlobalCount();
+ Asset* asset = new _CompressedAsset();
+ EXPECT_EQ(count + 1, Asset::getGlobalCount());
+ delete asset;
+ EXPECT_EQ(count, Asset::getGlobalCount());
+}
diff --git a/libs/androidfw/tests/ConfigLocale_test.cpp b/libs/androidfw/tests/ConfigLocale_test.cpp
index 2bf9b12b6ce5..10f4d46058ec 100644
--- a/libs/androidfw/tests/ConfigLocale_test.cpp
+++ b/libs/androidfw/tests/ConfigLocale_test.cpp
@@ -470,15 +470,80 @@ TEST(ConfigLocaleTest, isLocaleBetterThan_regionComparison) {
EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
fillIn("es", "AR", NULL, NULL, &request);
+ fillIn("es", "US", NULL, NULL, &config1);
+ fillIn("es", NULL, NULL, NULL, &config2);
+ // Special case for Latin American Spanish: es-MX and es-US are
+ // pseudo-parents of all Latin Ameircan Spanish locales.
+ EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+ EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+ fillIn("es", "MX", NULL, NULL, &request);
+ fillIn("es", "US", NULL, NULL, &config1);
+ fillIn("es", NULL, NULL, NULL, &config2);
+ // Special case for Latin American Spanish: es-MX and es-US are
+ // pseudo-parents of all Latin Ameircan Spanish locales.
+ EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+ EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+ fillIn("es", "AR", NULL, NULL, &request);
+ fillIn("es", "MX", NULL, NULL, &config1);
+ fillIn("es", NULL, NULL, NULL, &config2);
+ // Special case for Latin American Spanish: es-MX and es-US are
+ // pseudo-parents of all Latin Ameircan Spanish locales.
+ EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+ EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+ fillIn("es", "US", NULL, NULL, &request);
+ fillIn("es", "MX", NULL, NULL, &config1);
+ fillIn("es", NULL, NULL, NULL, &config2);
+ // Special case for Latin American Spanish: es-MX and es-US are
+ // pseudo-parents of all Latin Ameircan Spanish locales.
+ EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+ EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+ fillIn("es", "AR", NULL, NULL, &request);
+ fillIn("es", "419", NULL, NULL, &config1);
+ fillIn("es", "MX", NULL, NULL, &config2);
+ // Even though es-MX and es-US are pseudo-parents of all Latin Ameircan
+ // Spanish locales, es-419 is a closer parent.
+ EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+ EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+ fillIn("es", "US", NULL, NULL, &request);
+ fillIn("es", "419", NULL, NULL, &config1);
+ fillIn("es", "MX", NULL, NULL, &config2);
+ // Even though es-MX and es-US are pseudo-parents of all Latin Ameircan
+ // Spanish locales, es-419 is a closer parent.
+ EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+ EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+ fillIn("es", "MX", NULL, NULL, &request);
+ fillIn("es", "419", NULL, NULL, &config1);
+ fillIn("es", "US", NULL, NULL, &config2);
+ // Even though es-MX and es-US are pseudo-parents of all Latin Ameircan
+ // Spanish locales, es-419 is a closer parent.
+ EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+ EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+ fillIn("es", "AR", NULL, NULL, &request);
fillIn("es", "MX", NULL, NULL, &config1);
fillIn("es", "BO", NULL, NULL, &config2);
- // A representative locale is better if they are equidistant.
+ // Special case for Latin American Spanish: es-MX and es-US are
+ // pseudo-parents of all Latin Ameircan Spanish locales.
EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
fillIn("es", "AR", NULL, NULL, &request);
fillIn("es", "US", NULL, NULL, &config1);
fillIn("es", "BO", NULL, NULL, &config2);
+ // Special case for Latin American Spanish: es-MX and es-US are
+ // pseudo-parents of all Latin Ameircan Spanish locales.
+ EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
+ EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
+
+ fillIn("es", "IC", NULL, NULL, &request);
+ fillIn("es", "ES", NULL, NULL, &config1);
+ fillIn("es", "GQ", NULL, NULL, &config2);
// A representative locale is better if they are equidistant.
EXPECT_TRUE(config1.isLocaleBetterThan(config2, &request));
EXPECT_FALSE(config2.isLocaleBetterThan(config1, &request));
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index 37d9d0e749e6..7524ba0dcea6 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -591,7 +591,7 @@ void FrameBuilder::deferArcOp(const ArcOp& op) {
}
static bool hasMergeableClip(const BakedOpState& state) {
- return state.computedState.clipState
+ return !state.computedState.clipState
|| state.computedState.clipState->mode == ClipMode::Rectangle;
}
diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp
index 9af8eeba14a0..b5a33afccdc7 100644
--- a/libs/hwui/Interpolator.cpp
+++ b/libs/hwui/Interpolator.cpp
@@ -98,7 +98,8 @@ LUTInterpolator::~LUTInterpolator() {
}
float LUTInterpolator::interpolate(float input) {
- float lutpos = input * mSize;
+ // lut position should only be at the end of the table when input is 1f.
+ float lutpos = input * (mSize - 1);
if (lutpos >= (mSize - 1)) {
return mValues[mSize - 1];
}
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index aeee66106fb3..223605fa34ed 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -576,7 +576,7 @@ bool Tree::allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height) {
}
bool Tree::canReuseBitmap(const SkBitmap& bitmap, int width, int height) {
- return width == bitmap.width() && height == bitmap.height();
+ return width <= bitmap.width() && height <= bitmap.height();
}
void Tree::onPropertyChanged(TreeProperties* prop) {
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index 6c1815fe1301..b867dbc59714 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -622,10 +622,15 @@ public:
}
void setScaledSize(int width, int height) {
- if (mNonAnimatableProperties.scaledWidth != width
- || mNonAnimatableProperties.scaledHeight != height) {
- mNonAnimatableProperties.scaledWidth = width;
- mNonAnimatableProperties.scaledHeight = height;
+ // If the requested size is bigger than what the bitmap was, then
+ // we increase the bitmap size to match. The width and height
+ // are bound by MAX_CACHED_BITMAP_SIZE.
+ if (mNonAnimatableProperties.scaledWidth < width
+ || mNonAnimatableProperties.scaledHeight < height) {
+ mNonAnimatableProperties.scaledWidth = std::max(width,
+ mNonAnimatableProperties.scaledWidth);
+ mNonAnimatableProperties.scaledHeight = std::max(height,
+ mNonAnimatableProperties.scaledHeight);
mNonAnimatablePropertiesDirty = true;
mTree->onPropertyChanged(this);
}
diff --git a/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp
deleted file mode 100644
index a438afd5c889..000000000000
--- a/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2017 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 "TestSceneBase.h"
-
-class ShadowShaderAnimation;
-
-static TestScene::Registrar _ShadowShader(TestScene::Info{
- "shadowshader",
- "A set of overlapping shadowed areas with simple tessellation useful for"
- " benchmarking shadow shader performance.",
- TestScene::simpleCreateScene<ShadowShaderAnimation>
-});
-
-class ShadowShaderAnimation : public TestScene {
-public:
- std::vector< sp<RenderNode> > cards;
- void createContent(int width, int height, TestCanvas& canvas) override {
- canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
- canvas.insertReorderBarrier(true);
-
- int outset = 50;
- for (int i = 0; i < 10; i++) {
- sp<RenderNode> card = createCard(outset, outset,
- width - (outset * 2), height - (outset * 2));
- canvas.drawRenderNode(card.get());
- cards.push_back(card);
- }
-
- canvas.insertReorderBarrier(false);
- }
- void doFrame(int frameNr) override {
- int curFrame = frameNr % 10;
- for (size_t ci = 0; ci < cards.size(); ci++) {
- cards[ci]->mutateStagingProperties().setTranslationX(curFrame);
- cards[ci]->mutateStagingProperties().setTranslationY(curFrame);
- cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
- }
- }
-private:
- sp<RenderNode> createCard(int x, int y, int width, int height) {
- return TestUtils::createNode(x, y, x + width, y + height,
- [width, height](RenderProperties& props, TestCanvas& canvas) {
- props.setElevation(1000);
-
- // Set 0 radius, no clipping, so shadow is easy to compute. Slightly transparent outline
- // to signal contents aren't opaque (not necessary though, as elevation is so high, no
- // inner content to cut out)
- props.mutableOutline().setRoundRect(0, 0, width, height, 0, 0.99f);
- props.mutableOutline().setShouldClip(false);
-
- // don't draw anything to card's canvas - we just want the shadow
- });
- }
-};
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index 53dbede2f8e1..e2dc3a0a2c66 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -477,6 +477,35 @@ RENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
EXPECT_EQ(4, renderer.getIndex());
}
+RENDERTHREAD_TEST(FrameBuilder, regionClipStopsMerge) {
+ class RegionClipStopsMergeTestRenderer : public TestRendererBase {
+ public:
+ void onTextOp(const TextOp& op, const BakedOpState& state) override { mIndex++; }
+ };
+ auto node = TestUtils::createNode(0, 0, 400, 400,
+ [](RenderProperties& props, TestCanvas& canvas) {
+ SkPath path;
+ path.addCircle(200, 200, 200, SkPath::kCW_Direction);
+ canvas.save(SaveFlags::MatrixClip);
+ canvas.clipPath(&path, SkRegion::kIntersect_Op);
+ SkPaint paint;
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ paint.setAntiAlias(true);
+ paint.setTextSize(50);
+ TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100);
+ TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 200);
+ canvas.restore();
+ });
+
+ FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400,
+ sLightGeometry, Caches::getInstance());
+ frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node));
+
+ RegionClipStopsMergeTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(2, renderer.getIndex());
+}
+
RENDERTHREAD_TEST(FrameBuilder, textMerging) {
class TextMergingTestRenderer : public TestRendererBase {
public: