diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/androidfw/.clang-format | 5 | ||||
| -rw-r--r-- | libs/androidfw/ResourceTypes.cpp | 85 | ||||
| -rw-r--r-- | libs/androidfw/tests/Idmap_test.cpp | 100 | ||||
| -rw-r--r-- | libs/androidfw/tests/data/overlay/overlay.apk | bin | 1254 -> 2442 bytes | |||
| -rw-r--r-- | libs/androidfw/tests/data/overlay/res/values/values.xml | 2 | ||||
| -rw-r--r-- | libs/hwui/SkiaCanvas.cpp | 39 | ||||
| -rw-r--r-- | libs/hwui/SkiaCanvasProxy.cpp | 17 | ||||
| -rw-r--r-- | libs/hwui/SkiaCanvasProxy.h | 5 | ||||
| -rw-r--r-- | libs/hwui/VectorDrawable.cpp | 163 | ||||
| -rw-r--r-- | libs/hwui/VectorDrawable.h | 25 | ||||
| -rw-r--r-- | libs/hwui/tests/unit/FatalTestCanvas.h | 4 | ||||
| -rw-r--r-- | libs/hwui/tests/unit/VectorDrawableTests.cpp | 47 |
12 files changed, 204 insertions, 288 deletions
diff --git a/libs/androidfw/.clang-format b/libs/androidfw/.clang-format index ee1bee2bc644..c91502a257f3 100644 --- a/libs/androidfw/.clang-format +++ b/libs/androidfw/.clang-format @@ -1,2 +1,7 @@ BasedOnStyle: Google ColumnLimit: 100 +AllowShortBlocksOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +CommentPragmas: NOLINT:.* +DerivePointerAlignment: false +PointerAlignment: Left diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 359cface8b7f..244c52577a1a 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -6431,32 +6431,42 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, } if (newEntryCount > 0) { + bool addToType = true; uint8_t typeIndex = typeSpec->id - 1; ssize_t idmapIndex = idmapEntries.indexOfKey(typeSpec->id); if (idmapIndex >= 0) { typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1; + } else if (header->resourceIDMap != NULL) { + // This is an overlay, but the types in this overlay are not + // overlaying anything according to the idmap. We can skip these + // as they will otherwise conflict with the other resources in the package + // without a mapping. + addToType = false; } - TypeList& typeList = group->types.editItemAt(typeIndex); - if (!typeList.isEmpty()) { - const Type* existingType = typeList[0]; - if (existingType->entryCount != newEntryCount && idmapIndex < 0) { - ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", - (int) newEntryCount, (int) existingType->entryCount); - // We should normally abort here, but some legacy apps declare - // resources in the 'android' package (old bug in AAPT). + if (addToType) { + TypeList& typeList = group->types.editItemAt(typeIndex); + if (!typeList.isEmpty()) { + const Type* existingType = typeList[0]; + if (existingType->entryCount != newEntryCount && idmapIndex < 0) { + ALOGW("ResTable_typeSpec entry count inconsistent: " + "given %d, previously %d", + (int) newEntryCount, (int) existingType->entryCount); + // We should normally abort here, but some legacy apps declare + // resources in the 'android' package (old bug in AAPT). + } } - } - Type* t = new Type(header, package, newEntryCount); - t->typeSpec = typeSpec; - t->typeSpecFlags = (const uint32_t*)( - ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize)); - if (idmapIndex >= 0) { - t->idmapEntries = idmapEntries[idmapIndex]; + Type* t = new Type(header, package, newEntryCount); + t->typeSpec = typeSpec; + t->typeSpecFlags = (const uint32_t*)( + ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize)); + if (idmapIndex >= 0) { + t->idmapEntries = idmapEntries[idmapIndex]; + } + typeList.add(t); + group->largestTypeId = max(group->largestTypeId, typeSpec->id); } - typeList.add(t); - group->largestTypeId = max(group->largestTypeId, typeSpec->id); } else { ALOGV("Skipping empty ResTable_typeSpec for type %d", typeSpec->id); } @@ -6499,31 +6509,40 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, } if (newEntryCount > 0) { + bool addToType = true; uint8_t typeIndex = type->id - 1; ssize_t idmapIndex = idmapEntries.indexOfKey(type->id); if (idmapIndex >= 0) { typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1; + } else if (header->resourceIDMap != NULL) { + // This is an overlay, but the types in this overlay are not + // overlaying anything according to the idmap. We can skip these + // as they will otherwise conflict with the other resources in the package + // without a mapping. + addToType = false; } - TypeList& typeList = group->types.editItemAt(typeIndex); - if (typeList.isEmpty()) { - ALOGE("No TypeSpec for type %d", type->id); - return (mError=BAD_TYPE); - } + if (addToType) { + TypeList& typeList = group->types.editItemAt(typeIndex); + if (typeList.isEmpty()) { + ALOGE("No TypeSpec for type %d", type->id); + return (mError=BAD_TYPE); + } - Type* t = typeList.editItemAt(typeList.size() - 1); - if (t->package != package) { - ALOGE("No TypeSpec for type %d", type->id); - return (mError=BAD_TYPE); - } + Type* t = typeList.editItemAt(typeList.size() - 1); + if (t->package != package) { + ALOGE("No TypeSpec for type %d", type->id); + return (mError=BAD_TYPE); + } - t->configs.add(type); + t->configs.add(type); - if (kDebugTableGetEntry) { - ResTable_config thisConfig; - thisConfig.copyFromDtoH(type->config); - ALOGI("Adding config to type %d: %s\n", type->id, - thisConfig.toString().string()); + if (kDebugTableGetEntry) { + ResTable_config thisConfig; + thisConfig.copyFromDtoH(type->config); + ALOGI("Adding config to type %d: %s\n", type->id, + thisConfig.toString().string()); + } } } else { ALOGV("Skipping empty ResTable_type for type %d", type->id); diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp index 0928b1b976c3..d12be184745c 100644 --- a/libs/androidfw/tests/Idmap_test.cpp +++ b/libs/androidfw/tests/Idmap_test.cpp @@ -30,25 +30,23 @@ class IdmapTest : public ::testing::Test { protected: void SetUp() override { std::string contents; - ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", - "resources.arsc", &contents)); - ASSERT_EQ(NO_ERROR, - target_table_.add(contents.data(), contents.size(), 0, true)); - - ASSERT_TRUE( - ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", - "resources.arsc", &overlay_data_)); + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/basic/basic.apk", "resources.arsc", + &contents)); + ASSERT_EQ(NO_ERROR, target_table_.add(contents.data(), contents.size(), 0, true)); + + ASSERT_TRUE(ReadFileFromZipToString(GetTestDataPath() + "/overlay/overlay.apk", + "resources.arsc", &overlay_data_)); ResTable overlay_table; - ASSERT_EQ(NO_ERROR, - overlay_table.add(overlay_data_.data(), overlay_data_.size())); + ASSERT_EQ(NO_ERROR, overlay_table.add(overlay_data_.data(), overlay_data_.size())); char target_name[256] = "com.android.basic"; - ASSERT_EQ(NO_ERROR, - target_table_.createIdmap(overlay_table, 0, 0, target_name, - target_name, &data_, &data_size_)); + ASSERT_EQ(NO_ERROR, target_table_.createIdmap(overlay_table, 0, 0, target_name, target_name, + &data_, &data_size_)); } - void TearDown() override { ::free(data_); } + void TearDown() override { + ::free(data_); + } ResTable target_table_; std::string overlay_data_; @@ -56,13 +54,12 @@ class IdmapTest : public ::testing::Test { size_t data_size_ = 0; }; -TEST_F(IdmapTest, canLoadIdmap) { +TEST_F(IdmapTest, CanLoadIdmap) { ASSERT_EQ(NO_ERROR, - target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, - data_size_)); + target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_)); } -TEST_F(IdmapTest, overlayOverridesResourceValue) { +TEST_F(IdmapTest, OverlayOverridesResourceValue) { Res_value val; ssize_t block = target_table_.getResource(R::string::test2, &val, false); ASSERT_GE(block, 0); @@ -71,45 +68,60 @@ TEST_F(IdmapTest, overlayOverridesResourceValue) { ASSERT_TRUE(pool != NULL); ASSERT_LT(val.data, pool->size()); - size_t strLen; - const char16_t* targetStr16 = pool->stringAt(val.data, &strLen); - ASSERT_TRUE(targetStr16 != NULL); - ASSERT_EQ(String16("test2"), String16(targetStr16, strLen)); + size_t str_len; + const char16_t* target_str16 = pool->stringAt(val.data, &str_len); + ASSERT_TRUE(target_str16 != NULL); + ASSERT_EQ(String16("test2"), String16(target_str16, str_len)); ASSERT_EQ(NO_ERROR, - target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, - data_size_)); + target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_)); - ssize_t newBlock = target_table_.getResource(R::string::test2, &val, false); - ASSERT_GE(newBlock, 0); - ASSERT_NE(block, newBlock); + ssize_t new_block = target_table_.getResource(R::string::test2, &val, false); + ASSERT_GE(new_block, 0); + ASSERT_NE(block, new_block); ASSERT_EQ(Res_value::TYPE_STRING, val.dataType); - pool = target_table_.getTableStringBlock(newBlock); + pool = target_table_.getTableStringBlock(new_block); ASSERT_TRUE(pool != NULL); ASSERT_LT(val.data, pool->size()); - targetStr16 = pool->stringAt(val.data, &strLen); - ASSERT_TRUE(targetStr16 != NULL); - ASSERT_EQ(String16("test2-overlay"), String16(targetStr16, strLen)); + target_str16 = pool->stringAt(val.data, &str_len); + ASSERT_TRUE(target_str16 != NULL); + ASSERT_EQ(String16("test2-overlay"), String16(target_str16, str_len)); } -TEST_F(IdmapTest, overlaidResourceHasSameName) { +TEST_F(IdmapTest, OverlaidResourceHasSameName) { ASSERT_EQ(NO_ERROR, - target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, - data_size_)); + target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_)); + + ResTable::resource_name res_name; + ASSERT_TRUE(target_table_.getResourceName(R::array::integerArray1, false, &res_name)); + + ASSERT_TRUE(res_name.package != NULL); + ASSERT_TRUE(res_name.type != NULL); + ASSERT_TRUE(res_name.name != NULL); + + EXPECT_EQ(String16("com.android.basic"), String16(res_name.package, res_name.packageLen)); + EXPECT_EQ(String16("array"), String16(res_name.type, res_name.typeLen)); + EXPECT_EQ(String16("integerArray1"), String16(res_name.name, res_name.nameLen)); +} - ResTable::resource_name resName; - ASSERT_TRUE( - target_table_.getResourceName(R::array::integerArray1, false, &resName)); +constexpr const uint32_t kNonOverlaidResourceId = 0x7fff0000u; - ASSERT_TRUE(resName.package != NULL); - ASSERT_TRUE(resName.type != NULL); - ASSERT_TRUE(resName.name != NULL); +TEST_F(IdmapTest, OverlayDoesNotIncludeNonOverlaidResources) { + // First check that the resource we're trying to not include when overlaid is present when + // the overlay is loaded as a standalone APK. + ResTable table; + ASSERT_EQ(NO_ERROR, table.add(overlay_data_.data(), overlay_data_.size(), 0, true)); - EXPECT_EQ(String16("com.android.basic"), - String16(resName.package, resName.packageLen)); - EXPECT_EQ(String16("array"), String16(resName.type, resName.typeLen)); - EXPECT_EQ(String16("integerArray1"), String16(resName.name, resName.nameLen)); + Res_value val; + ssize_t block = table.getResource(kNonOverlaidResourceId, &val, false /*mayBeBag*/); + ASSERT_GE(block, 0); + + // Now add the overlay and verify that the unoverlaid resource is gone. + ASSERT_EQ(NO_ERROR, + target_table_.add(overlay_data_.data(), overlay_data_.size(), data_, data_size_)); + block = target_table_.getResource(kNonOverlaidResourceId, &val, false /*mayBeBag*/); + ASSERT_LT(block, 0); } } // namespace diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk Binary files differindex e0e054343601..40bf17c5951a 100644 --- a/libs/androidfw/tests/data/overlay/overlay.apk +++ b/libs/androidfw/tests/data/overlay/overlay.apk diff --git a/libs/androidfw/tests/data/overlay/res/values/values.xml b/libs/androidfw/tests/data/overlay/res/values/values.xml index 3e1af9878429..8e4417e457d1 100644 --- a/libs/androidfw/tests/data/overlay/res/values/values.xml +++ b/libs/androidfw/tests/data/overlay/res/values/values.xml @@ -20,4 +20,6 @@ <item>10</item> <item>11</item> </integer-array> + <public type="animator" name="unoverlaid" id="0x7fff0000" /> + <item type="animator" name="unoverlaid">@null</item> </resources> diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 812e4d885b39..daf14af87288 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -522,8 +522,10 @@ void SkiaCanvas::drawVertices(SkCanvas::VertexMode vertexMode, int vertexCount, SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid"); #endif const int ptCount = vertexCount >> 1; - mCanvas->drawVertices(vertexMode, ptCount, (SkPoint*)verts, (SkPoint*)texs, - (SkColor*)colors, indices, indexCount, paint); + mCanvas->drawVertices(SkVertices::MakeCopy(vertexMode, ptCount, (SkPoint*)verts, + (SkPoint*)texs, (SkColor*)colors, + indexCount, indices), + SkBlendMode::kModulate, paint); } // ---------------------------------------------------------------------------- @@ -560,23 +562,17 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& hwuiBitmap, int meshWidth, int meshHeigh hwuiBitmap.getSkBitmap(&bitmap); const int ptCount = (meshWidth + 1) * (meshHeight + 1); const int indexCount = meshWidth * meshHeight * 6; - - /* Our temp storage holds 2 or 3 arrays. - texture points [ptCount * sizeof(SkPoint)] - optionally vertex points [ptCount * sizeof(SkPoint)] if we need a - copy to convert from float to fixed - indices [ptCount * sizeof(uint16_t)] - */ - ssize_t storageSize = ptCount * sizeof(SkPoint); // texs[] - storageSize += indexCount * sizeof(uint16_t); // indices[] - - -#ifndef SK_SCALAR_IS_FLOAT - SkDEBUGFAIL("SkScalar must be a float for these conversions to be valid"); -#endif - std::unique_ptr<char[]> storage(new char[storageSize]); - SkPoint* texs = (SkPoint*)storage.get(); - uint16_t* indices = (uint16_t*)(texs + ptCount); + uint32_t flags = SkVertices::kHasTexCoords_BuilderFlag; + if (colors) { + flags |= SkVertices::kHasColors_BuilderFlag; + } + SkVertices::Builder builder(SkCanvas::kTriangles_VertexMode, ptCount, indexCount, flags); + memcpy(builder.positions(), vertices, ptCount * sizeof(SkPoint)); + if (colors) { + memcpy(builder.colors(), colors, ptCount * sizeof(SkColor)); + } + SkPoint* texs = builder.texCoords(); + uint16_t* indices = builder.indices(); // cons up texture coordinates and indices { @@ -625,7 +621,6 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& hwuiBitmap, int meshWidth, int meshHeigh index += 1; } SkASSERT(indexPtr - indices == indexCount); - SkASSERT((char*)indexPtr - (char*)storage.get() == storageSize); } // double-check that we have legal indices @@ -646,9 +641,7 @@ void SkiaCanvas::drawBitmapMesh(Bitmap& hwuiBitmap, int meshWidth, int meshHeigh sk_sp<SkImage> image = SkMakeImageFromRasterBitmap(bitmap, kNever_SkCopyPixelsMode); tmpPaint.setShader(image->makeShader(SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)); - mCanvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, (SkPoint*)vertices, - texs, (const SkColor*)colors, indices, - indexCount, tmpPaint); + mCanvas->drawVertices(builder.detach(), SkBlendMode::kModulate, tmpPaint); } void SkiaCanvas::drawNinePatch(Bitmap& hwuiBitmap, const Res_png_9patch& chunk, diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp index 20ca80b95465..f6e92dca2bb9 100644 --- a/libs/hwui/SkiaCanvasProxy.cpp +++ b/libs/hwui/SkiaCanvasProxy.cpp @@ -31,6 +31,7 @@ #include <SkRSXform.h> #include <SkSurface.h> #include <SkTextBlobRunIterator.h> +#include <SkVertices.h> namespace android { namespace uirenderer { @@ -180,20 +181,20 @@ void SkiaCanvasProxy::onDrawImageLattice(const SkImage* image, const Lattice& la } } -void SkiaCanvasProxy::onDrawVertices(VertexMode mode, int vertexCount, const SkPoint vertices[], - const SkPoint texs[], const SkColor colors[], SkBlendMode, const uint16_t indices[], - int indexCount, const SkPaint& paint) { +void SkiaCanvasProxy::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode, + const SkPaint& paint) { // TODO: should we pass through blendmode if (mFilterHwuiCalls) { return; } // convert the SkPoints into floats static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats"); - const int floatCount = vertexCount << 1; - const float* vArray = &vertices[0].fX; - const float* tArray = (texs) ? &texs[0].fX : NULL; - const int* cArray = (colors) ? (int*)colors : NULL; - mCanvas->drawVertices(mode, floatCount, vArray, tArray, cArray, indices, indexCount, paint); + const int floatCount = vertices->vertexCount() << 1; + const float* vArray = (const float*)vertices->positions(); + const float* tArray = (const float*)vertices->texCoords(); + const int* cArray = (const int*)vertices->colors(); + mCanvas->drawVertices(vertices->mode(), floatCount, vArray, tArray, cArray, + vertices->indices(), vertices->indexCount(), paint); } sk_sp<SkSurface> SkiaCanvasProxy::onNewSurface(const SkImageInfo&, const SkSurfaceProps&) { diff --git a/libs/hwui/SkiaCanvasProxy.h b/libs/hwui/SkiaCanvasProxy.h index 3b1dd7383f36..d11a779b3600 100644 --- a/libs/hwui/SkiaCanvasProxy.h +++ b/libs/hwui/SkiaCanvasProxy.h @@ -75,10 +75,7 @@ protected: const SkPaint*); virtual void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst, const SkPaint*); - virtual void onDrawVertices(VertexMode, int vertexCount, const SkPoint vertices[], - const SkPoint texs[], const SkColor colors[], SkBlendMode, - const uint16_t indices[], int indexCount, - const SkPaint&) override; + virtual void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override; virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override; diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 68d3dd5efb79..8823a9212958 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -33,65 +33,10 @@ namespace VectorDrawable { const int Tree::MAX_CACHED_BITMAP_SIZE = 2048; -void Path::draw(SkCanvas* outCanvas, const SkMatrix& groupStackedMatrix, float scaleX, float scaleY, - bool useStagingData) { - float matrixScale = getMatrixScale(groupStackedMatrix); - if (matrixScale == 0) { - // When either x or y is scaled to 0, we don't need to draw anything. - return; - } - - SkMatrix pathMatrix(groupStackedMatrix); - pathMatrix.postScale(scaleX, scaleY); - - //TODO: try apply the path matrix to the canvas instead of creating a new path. - SkPath renderPath; - renderPath.reset(); - - if (useStagingData) { - SkPath tmpPath; - getStagingPath(&tmpPath); - renderPath.addPath(tmpPath, pathMatrix); - } else { - renderPath.addPath(getUpdatedPath(), pathMatrix); - } - - float minScale = fmin(scaleX, scaleY); - float strokeScale = minScale * matrixScale; - drawPath(outCanvas, renderPath, strokeScale, pathMatrix, useStagingData); -} - void Path::dump() { ALOGD("Path: %s has %zu points", mName.c_str(), mProperties.getData().points.size()); } -float Path::getMatrixScale(const SkMatrix& groupStackedMatrix) { - // Given unit vectors A = (0, 1) and B = (1, 0). - // After matrix mapping, we got A' and B'. Let theta = the angel b/t A' and B'. - // Therefore, the final scale we want is min(|A'| * sin(theta), |B'| * sin(theta)), - // which is (|A'| * |B'| * sin(theta)) / max (|A'|, |B'|); - // If max (|A'|, |B'|) = 0, that means either x or y has a scale of 0. - // - // For non-skew case, which is most of the cases, matrix scale is computing exactly the - // scale on x and y axis, and take the minimal of these two. - // For skew case, an unit square will mapped to a parallelogram. And this function will - // return the minimal height of the 2 bases. - SkVector skVectors[2]; - skVectors[0].set(0, 1); - skVectors[1].set(1, 0); - groupStackedMatrix.mapVectors(skVectors, 2); - float scaleX = hypotf(skVectors[0].fX, skVectors[0].fY); - float scaleY = hypotf(skVectors[1].fX, skVectors[1].fY); - float crossProduct = skVectors[0].cross(skVectors[1]); - float maxScale = fmax(scaleX, scaleY); - - float matrixScale = 0; - if (maxScale > 0) { - matrixScale = fabs(crossProduct) / maxScale; - } - return matrixScale; -} - // Called from UI thread during the initial setup/theme change. Path::Path(const char* pathStr, size_t strLength) { PathParser::ParseResult result; @@ -104,18 +49,19 @@ Path::Path(const Path& path) : Node(path) { mStagingProperties.syncProperties(path.mStagingProperties); } -const SkPath& Path::getUpdatedPath() { - if (mSkPathDirty) { - mSkPath.reset(); - VectorDrawableUtils::verbsToPath(&mSkPath, mProperties.getData()); - mSkPathDirty = false; +const SkPath& Path::getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) { + if (useStagingData) { + tempStagingPath->reset(); + VectorDrawableUtils::verbsToPath(tempStagingPath, mStagingProperties.getData()); + return *tempStagingPath; + } else { + if (mSkPathDirty) { + mSkPath.reset(); + VectorDrawableUtils::verbsToPath(&mSkPath, mProperties.getData()); + mSkPathDirty = false; + } + return mSkPath; } - return mSkPath; -} - -void Path::getStagingPath(SkPath* outPath) { - outPath->reset(); - VectorDrawableUtils::verbsToPath(outPath, mStagingProperties.getData()); } void Path::syncProperties() { @@ -157,26 +103,35 @@ static void applyTrim(SkPath* outPath, const SkPath& inPath, float trimPathStart } } -const SkPath& FullPath::getUpdatedPath() { - if (!mSkPathDirty && !mProperties.mTrimDirty) { +const SkPath& FullPath::getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) { + if (!useStagingData && !mSkPathDirty && !mProperties.mTrimDirty) { return mTrimmedSkPath; } - Path::getUpdatedPath(); - if (mProperties.getTrimPathStart() != 0.0f || mProperties.getTrimPathEnd() != 1.0f) { - mProperties.mTrimDirty = false; - applyTrim(&mTrimmedSkPath, mSkPath, mProperties.getTrimPathStart(), - mProperties.getTrimPathEnd(), mProperties.getTrimPathOffset()); - return mTrimmedSkPath; + Path::getUpdatedPath(useStagingData, tempStagingPath); + SkPath *outPath; + if (useStagingData) { + SkPath inPath = *tempStagingPath; + applyTrim(tempStagingPath, inPath, mStagingProperties.getTrimPathStart(), + mStagingProperties.getTrimPathEnd(), mStagingProperties.getTrimPathOffset()); + outPath = tempStagingPath; } else { - return mSkPath; + if (mProperties.getTrimPathStart() != 0.0f || mProperties.getTrimPathEnd() != 1.0f) { + mProperties.mTrimDirty = false; + applyTrim(&mTrimmedSkPath, mSkPath, mProperties.getTrimPathStart(), + mProperties.getTrimPathEnd(), mProperties.getTrimPathOffset()); + outPath = &mTrimmedSkPath; + } else { + outPath = &mSkPath; + } } -} - -void FullPath::getStagingPath(SkPath* outPath) { - Path::getStagingPath(outPath); - SkPath inPath = *outPath; - applyTrim(outPath, inPath, mStagingProperties.getTrimPathStart(), - mStagingProperties.getTrimPathEnd(), mStagingProperties.getTrimPathOffset()); + const FullPathProperties& properties = useStagingData ? mStagingProperties : mProperties; + bool setFillPath = properties.getFillGradient() != nullptr + || properties.getFillColor() != SK_ColorTRANSPARENT; + if (setFillPath) { + SkPath::FillType ft = static_cast<SkPath::FillType>(properties.getFillType()); + outPath->setFillType(ft); + } + return *outPath; } void FullPath::dump() { @@ -192,16 +147,17 @@ inline SkColor applyAlpha(SkColor color, float alpha) { return SkColorSetA(color, alphaBytes * alpha); } -void FullPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, float strokeScale, - const SkMatrix& matrix, bool useStagingData){ +void FullPath::draw(SkCanvas* outCanvas, bool useStagingData) { const FullPathProperties& properties = useStagingData ? mStagingProperties : mProperties; + SkPath tempStagingPath; + const SkPath& renderPath = getUpdatedPath(useStagingData, &tempStagingPath); // Draw path's fill, if fill color or gradient is valid bool needsFill = false; SkPaint paint; if (properties.getFillGradient() != nullptr) { paint.setColor(applyAlpha(SK_ColorBLACK, properties.getFillAlpha())); - paint.setShader(properties.getFillGradient()->makeWithLocalMatrix(matrix)); + paint.setShader(sk_sp<SkShader>(SkSafeRef(properties.getFillGradient()))); needsFill = true; } else if (properties.getFillColor() != SK_ColorTRANSPARENT) { paint.setColor(applyAlpha(properties.getFillColor(), properties.getFillAlpha())); @@ -211,8 +167,6 @@ void FullPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, float strokeSca if (needsFill) { paint.setStyle(SkPaint::Style::kFill_Style); paint.setAntiAlias(true); - SkPath::FillType ft = static_cast<SkPath::FillType>(properties.getFillType()); - renderPath.setFillType(ft); outCanvas->drawPath(renderPath, paint); } @@ -220,7 +174,7 @@ void FullPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, float strokeSca bool needsStroke = false; if (properties.getStrokeGradient() != nullptr) { paint.setColor(applyAlpha(SK_ColorBLACK, properties.getStrokeAlpha())); - paint.setShader(properties.getStrokeGradient()->makeWithLocalMatrix(matrix)); + paint.setShader(sk_sp<SkShader>(SkSafeRef(properties.getStrokeGradient()))); needsStroke = true; } else if (properties.getStrokeColor() != SK_ColorTRANSPARENT) { paint.setColor(applyAlpha(properties.getStrokeColor(), properties.getStrokeAlpha())); @@ -232,7 +186,7 @@ void FullPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, float strokeSca paint.setStrokeJoin(SkPaint::Join(properties.getStrokeLineJoin())); paint.setStrokeCap(SkPaint::Cap(properties.getStrokeLineCap())); paint.setStrokeMiter(properties.getStrokeMiterLimit()); - paint.setStrokeWidth(properties.getStrokeWidth() * strokeScale); + paint.setStrokeWidth(properties.getStrokeWidth()); outCanvas->drawPath(renderPath, paint); } } @@ -306,36 +260,28 @@ void FullPath::FullPathProperties::setPropertyValue(int propertyId, float value) } } -void ClipPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, - float strokeScale, const SkMatrix& matrix, bool useStagingData){ - outCanvas->clipPath(renderPath); +void ClipPath::draw(SkCanvas* outCanvas, bool useStagingData) { + SkPath tempStagingPath; + outCanvas->clipPath(getUpdatedPath(useStagingData, &tempStagingPath)); } Group::Group(const Group& group) : Node(group) { mStagingProperties.syncProperties(group.mStagingProperties); } -void Group::draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix, float scaleX, - float scaleY, bool useStagingData) { - // TODO: Try apply the matrix to the canvas instead of passing it down the tree - - // Calculate current group's matrix by preConcat the parent's and - // and the current one on the top of the stack. - // Basically the Mfinal = Mviewport * M0 * M1 * M2; - // Mi the local matrix at level i of the group tree. +void Group::draw(SkCanvas* outCanvas, bool useStagingData) { + // Save the current clip and matrix information, which is local to this group. + SkAutoCanvasRestore saver(outCanvas, true); + // apply the current group's matrix to the canvas SkMatrix stackedMatrix; const GroupProperties& prop = useStagingData ? mStagingProperties : mProperties; getLocalMatrix(&stackedMatrix, prop); - stackedMatrix.postConcat(currentMatrix); - - // Save the current clip information, which is local to this group. - outCanvas->save(); + outCanvas->concat(stackedMatrix); // Draw the group tree in the same order as the XML file. for (auto& child : mChildren) { - child->draw(outCanvas, stackedMatrix, scaleX, scaleY, useStagingData); + child->draw(outCanvas, useStagingData); } - // Restore the previous clip information. - outCanvas->restore(); + // Restore the previous clip and matrix information. } void Group::dump() { @@ -556,7 +502,8 @@ void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) { mStagingProperties.getViewportHeight() : mProperties.getViewportHeight(); float scaleX = outCache.width() / viewportWidth; float scaleY = outCache.height() / viewportHeight; - mRootNode->draw(&outCanvas, SkMatrix::I(), scaleX, scaleY, useStagingData); + outCanvas.scale(scaleX, scaleY); + mRootNode->draw(&outCanvas, useStagingData); } bool Tree::allocateBitmapIfNeeded(Cache& cache, int width, int height) { diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h index 8244a3911183..729a4dd4ba76 100644 --- a/libs/hwui/VectorDrawable.h +++ b/libs/hwui/VectorDrawable.h @@ -109,8 +109,7 @@ public: mName = node.mName; } Node() {} - virtual void draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix, - float scaleX, float scaleY, bool useStagingData) = 0; + virtual void draw(SkCanvas* outCanvas, bool useStagingData) = 0; virtual void dump() = 0; void setName(const char* name) { mName = name; @@ -169,9 +168,6 @@ public: Path() {} void dump() override; - void draw(SkCanvas* outCanvas, const SkMatrix& groupStackedMatrix, - float scaleX, float scaleY, bool useStagingData) override; - static float getMatrixScale(const SkMatrix& groupStackedMatrix); virtual void syncProperties() override; virtual void onPropertyChanged(Properties* prop) override { if (prop == &mStagingProperties) { @@ -193,10 +189,7 @@ public: PathProperties* mutateProperties() { return &mProperties; } protected: - virtual const SkPath& getUpdatedPath(); - virtual void getStagingPath(SkPath* outPath); - virtual void drawPath(SkCanvas *outCanvas, SkPath& renderPath, - float strokeScale, const SkMatrix& matrix, bool useStagingData) = 0; + virtual const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath); // Internal data, render thread only. bool mSkPathDirty = true; @@ -364,6 +357,7 @@ public: FullPath(const FullPath& path); // for cloning FullPath(const char* path, size_t strLength) : Path(path, strLength) {} FullPath() : Path() {} + void draw(SkCanvas* outCanvas, bool useStagingData) override; void dump() override; FullPathProperties* mutateStagingProperties() { return &mStagingProperties; } const FullPathProperties* stagingProperties() { return &mStagingProperties; } @@ -387,10 +381,7 @@ public: } protected: - const SkPath& getUpdatedPath() override; - void getStagingPath(SkPath* outPath) override; - void drawPath(SkCanvas* outCanvas, SkPath& renderPath, - float strokeScale, const SkMatrix& matrix, bool useStagingData) override; + const SkPath& getUpdatedPath(bool useStagingData, SkPath* tempStagingPath) override; private: FullPathProperties mProperties = FullPathProperties(this); @@ -407,10 +398,7 @@ public: ClipPath(const ClipPath& path) : Path(path) {} ClipPath(const char* path, size_t strLength) : Path(path, strLength) {} ClipPath() : Path() {} - -protected: - void drawPath(SkCanvas* outCanvas, SkPath& renderPath, - float strokeScale, const SkMatrix& matrix, bool useStagingData) override; + void draw(SkCanvas* outCanvas, bool useStagingData) override; }; class ANDROID_API Group: public Node { @@ -519,8 +507,7 @@ public: GroupProperties* mutateProperties() { return &mProperties; } // Methods below could be called from either UI thread or Render Thread. - virtual void draw(SkCanvas* outCanvas, const SkMatrix& currentMatrix, - float scaleX, float scaleY, bool useStagingData) override; + virtual void draw(SkCanvas* outCanvas, bool useStagingData) override; void getLocalMatrix(SkMatrix* outMatrix, const GroupProperties& properties); void dump() override; static bool isValidProperty(int propertyId); diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h index 4831722b93e6..03d94964ac76 100644 --- a/libs/hwui/tests/unit/FatalTestCanvas.h +++ b/libs/hwui/tests/unit/FatalTestCanvas.h @@ -80,9 +80,7 @@ public: void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) { ADD_FAILURE() << "onDrawPoints not expected in this test"; } - void onDrawVertices(VertexMode, int vertexCount, const SkPoint vertices[], const SkPoint texs[], - const SkColor colors[], SkBlendMode, const uint16_t indices[], int indexCount, - const SkPaint&) { + void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) { ADD_FAILURE() << "onDrawVertices not expected in this test"; } void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int count, diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp index 8e0d3ee572a6..6f264e1ebf62 100644 --- a/libs/hwui/tests/unit/VectorDrawableTests.cpp +++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp @@ -347,51 +347,6 @@ TEST(VectorDrawableUtils, interpolatePathData) { } } -TEST(VectorDrawable, matrixScale) { - struct MatrixAndScale { - float buffer[9]; - float matrixScale; - }; - - const MatrixAndScale sMatrixAndScales[] { - { - {1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - 1.0 - }, - { - {1.0f, 0.0f, 240.0f, 0.0f, 1.0f, 240.0f, 0.0f, 0.0f, 1.0f}, - 1.0f, - }, - { - {1.5f, 0.0f, 24.0f, 0.0f, 1.5f, 24.0f, 0.0f, 0.0f, 1.0f}, - 1.5f, - }, - { - {0.99999994f, 0.0f, 300.0f, 0.0f, 0.99999994f, 158.57864f, 0.0f, 0.0f, 1.0f}, - 0.99999994f, - }, - { - {0.7071067f, 0.7071067f, 402.5305f, -0.7071067f, 0.7071067f, 169.18524f, 0.0f, 0.0f, 1.0f}, - 0.99999994f, - }, - { - {0.0f, 0.9999999f, 482.5305f, -0.9999999f, 0.0f, 104.18525f, 0.0f, 0.0f, 1.0f}, - 0.9999999f, - }, - { - {-0.35810637f, -0.93368083f, 76.55821f, 0.93368083f, -0.35810637f, 89.538506f, 0.0f, 0.0f, 1.0f}, - 1.0000001f, - }, - }; - - for (MatrixAndScale matrixAndScale : sMatrixAndScales) { - SkMatrix matrix; - matrix.set9(matrixAndScale.buffer); - float actualMatrixScale = VectorDrawable::Path::getMatrixScale(matrix); - EXPECT_EQ(matrixAndScale.matrixScale, actualMatrixScale); - } -} - TEST(VectorDrawable, groupProperties) { //TODO: Also need to test property sync and dirty flag when properties change. VectorDrawable::Group group; @@ -458,7 +413,7 @@ TEST(VectorDrawable, drawPathWithoutIncrementingShaderRefCount) { // Setting the fill gradient increments the ref count of the shader by 1 path.mutateStagingProperties()->setFillGradient(shader); - path.draw(&canvas, SkMatrix::I(), 1.0f, 1.0f, true); + path.draw(&canvas, true); // Resetting the fill gradient decrements the ref count of the shader by 1 path.mutateStagingProperties()->setFillGradient(nullptr); |