summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/androidfw/Android.mk35
-rw-r--r--libs/androidfw/ResourceTypes.cpp55
-rw-r--r--libs/androidfw/misc.cpp4
-rw-r--r--libs/androidfw/tests/Android.mk5
-rw-r--r--libs/androidfw/tests/ResTable_test.cpp12
-rw-r--r--libs/androidfw/tests/Split_test.cpp32
-rw-r--r--libs/androidfw/tests/TestHelpers.cpp48
-rw-r--r--libs/androidfw/tests/TestHelpers.h5
-rw-r--r--libs/androidfw/tests/data/app/R.h16
-rwxr-xr-xlibs/androidfw/tests/data/app/build15
-rw-r--r--libs/androidfw/tests/data/app/res/values/values.xml15
-rw-r--r--libs/androidfw/tests/data/basic/R.h23
-rw-r--r--libs/androidfw/tests/data/basic/basic_arsc.h88
-rwxr-xr-xlibs/androidfw/tests/data/basic/build32
-rw-r--r--libs/androidfw/tests/data/basic/res/layout-fr-sw600dp/main.xml15
-rw-r--r--libs/androidfw/tests/data/basic/res/layout/main.xml15
-rw-r--r--libs/androidfw/tests/data/basic/res/values-de/values.xml15
-rw-r--r--libs/androidfw/tests/data/basic/res/values-fr/values.xml15
-rw-r--r--libs/androidfw/tests/data/basic/res/values-hdpi/values.xml19
-rw-r--r--libs/androidfw/tests/data/basic/res/values-sv/values.xml15
-rw-r--r--libs/androidfw/tests/data/basic/res/values-xhdpi/values.xml19
-rw-r--r--libs/androidfw/tests/data/basic/res/values-xxhdpi/values.xml19
-rw-r--r--libs/androidfw/tests/data/basic/res/values/values.xml15
-rw-r--r--libs/androidfw/tests/data/basic/split_de_fr_arsc.h33
-rw-r--r--libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h68
-rw-r--r--libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h68
-rw-r--r--libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h68
-rwxr-xr-xlibs/androidfw/tests/data/feature/build15
-rw-r--r--libs/androidfw/tests/data/feature/feature_arsc.h80
-rw-r--r--libs/androidfw/tests/data/feature/res/values/values.xml15
-rw-r--r--libs/androidfw/tests/data/lib/R.h16
-rwxr-xr-xlibs/androidfw/tests/data/lib/build15
-rw-r--r--libs/androidfw/tests/data/lib/lib_arsc.h57
-rw-r--r--libs/androidfw/tests/data/lib/res/values/values.xml15
-rwxr-xr-xlibs/androidfw/tests/data/overlay/build15
-rw-r--r--libs/androidfw/tests/data/overlay/res/values/values.xml15
-rw-r--r--libs/androidfw/tests/data/system/R.h16
-rwxr-xr-xlibs/androidfw/tests/data/system/build15
-rw-r--r--libs/androidfw/tests/data/system/res/values/themes.xml15
-rw-r--r--libs/hwui/AmbientShadow.cpp6
-rw-r--r--libs/hwui/Caches.cpp42
-rw-r--r--libs/hwui/Caches.h1
-rw-r--r--libs/hwui/DeferredDisplayList.cpp11
-rw-r--r--libs/hwui/DeferredDisplayList.h5
-rw-r--r--libs/hwui/DeferredLayerUpdater.cpp21
-rw-r--r--libs/hwui/DeferredLayerUpdater.h2
-rw-r--r--libs/hwui/DisplayList.cpp22
-rw-r--r--libs/hwui/DisplayList.h7
-rw-r--r--libs/hwui/DisplayListOp.h5
-rw-r--r--libs/hwui/DisplayListRenderer.cpp35
-rw-r--r--libs/hwui/DisplayListRenderer.h20
-rw-r--r--libs/hwui/DrawProfiler.cpp50
-rw-r--r--libs/hwui/DrawProfiler.h4
-rw-r--r--libs/hwui/Layer.cpp19
-rw-r--r--libs/hwui/Layer.h8
-rw-r--r--libs/hwui/LayerCache.cpp2
-rw-r--r--libs/hwui/LayerRenderer.cpp14
-rw-r--r--libs/hwui/LayerRenderer.h1
-rwxr-xr-xlibs/hwui/OpenGLRenderer.cpp96
-rwxr-xr-xlibs/hwui/OpenGLRenderer.h16
-rw-r--r--libs/hwui/PathTessellator.cpp2
-rw-r--r--libs/hwui/Program.cpp3
-rw-r--r--libs/hwui/Program.h6
-rw-r--r--libs/hwui/ProgramCache.cpp13
-rw-r--r--libs/hwui/Properties.h9
-rw-r--r--libs/hwui/RenderNode.cpp31
-rw-r--r--libs/hwui/RenderNode.h2
-rw-r--r--libs/hwui/RenderState.cpp37
-rw-r--r--libs/hwui/RenderState.h19
-rw-r--r--libs/hwui/Renderer.h12
-rw-r--r--libs/hwui/ResourceCache.cpp29
-rw-r--r--libs/hwui/ResourceCache.h15
-rw-r--r--libs/hwui/SpotShadow.cpp1133
-rw-r--r--libs/hwui/SpotShadow.h41
-rw-r--r--libs/hwui/Vector.h4
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp20
-rw-r--r--libs/hwui/renderthread/CanvasContext.h13
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp6
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h1
-rw-r--r--libs/hwui/renderthread/EglManager.cpp59
-rw-r--r--libs/hwui/renderthread/EglManager.h14
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp54
-rw-r--r--libs/hwui/renderthread/RenderProxy.h8
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp2
-rw-r--r--libs/hwui/renderthread/TimeLord.cpp2
-rw-r--r--libs/hwui/tests/Android.mk55
-rw-r--r--libs/hwui/tests/TestContext.cpp45
-rw-r--r--libs/hwui/tests/TestContext.h33
-rw-r--r--libs/hwui/tests/main.cpp131
89 files changed, 1829 insertions, 1415 deletions
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk
index 9c0a747..461160a 100644
--- a/libs/androidfw/Android.mk
+++ b/libs/androidfw/Android.mk
@@ -14,7 +14,7 @@
LOCAL_PATH:= $(call my-dir)
-# libandroidfw is partially built for the host (used by obbtool and others)
+# libandroidfw is partially built for the host (used by obbtool, aapt, and others)
# These files are common to host and target builds.
commonSources := \
@@ -35,29 +35,19 @@ deviceSources := \
BackupHelpers.cpp \
CursorWindow.cpp
-hostSources := \
- $(commonSources)
+hostSources := $(commonSources)
# For the host
# =====================================================
-
include $(CLEAR_VARS)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-LOCAL_SRC_FILES:= $(hostSources)
-
LOCAL_MODULE:= libandroidfw
-
LOCAL_MODULE_TAGS := optional
-
LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
-
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-
-LOCAL_C_INCLUDES := \
- external/zlib
-
-LOCAL_STATIC_LIBRARIES := liblog libziparchive-host libutils
+LOCAL_SRC_FILES:= $(hostSources)
+LOCAL_C_INCLUDES := external/zlib
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -68,8 +58,13 @@ include $(BUILD_HOST_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+LOCAL_MODULE:= libandroidfw
+LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES:= $(deviceSources)
-
+LOCAL_C_INCLUDES := \
+ external/zlib \
+ system/core/include
+LOCAL_STATIC_LIBRARIES := libziparchive
LOCAL_SHARED_LIBRARIES := \
libbinder \
liblog \
@@ -77,16 +72,6 @@ LOCAL_SHARED_LIBRARIES := \
libutils \
libz
-LOCAL_STATIC_LIBRARIES := libziparchive
-
-LOCAL_C_INCLUDES := \
- external/zlib \
- system/core/include
-
-LOCAL_MODULE:= libandroidfw
-
-LOCAL_MODULE_TAGS := optional
-
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 679a75f..39f175f 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -95,11 +95,11 @@ inline static T max(T a, T b) {
// range checked; guaranteed to NUL-terminate within the stated number of available slots
// NOTE: if this truncates the dst string due to running out of space, no attempt is
// made to avoid splitting surrogate pairs.
-static void strcpy16_dtoh(char16_t* dst, const char16_t* src, size_t avail)
+static void strcpy16_dtoh(char16_t* dst, const uint16_t* src, size_t avail)
{
char16_t* last = dst + avail - 1;
while (*src && (dst < last)) {
- char16_t s = dtohs(*src);
+ char16_t s = dtohs(static_cast<char16_t>(*src));
*dst++ = s;
src++;
}
@@ -509,7 +509,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
if (mHeader->flags&ResStringPool_header::UTF8_FLAG) {
charSize = sizeof(uint8_t);
} else {
- charSize = sizeof(char16_t);
+ charSize = sizeof(uint16_t);
}
// There should be at least space for the smallest string
@@ -555,8 +555,8 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
e[i] = dtohl(mEntries[i]);
}
if (!(mHeader->flags&ResStringPool_header::UTF8_FLAG)) {
- const char16_t* strings = (const char16_t*)mStrings;
- char16_t* s = const_cast<char16_t*>(strings);
+ const uint16_t* strings = (const uint16_t*)mStrings;
+ uint16_t* s = const_cast<uint16_t*>(strings);
for (i=0; i<mStringPoolSize; i++) {
s[i] = dtohs(strings[i]);
}
@@ -566,7 +566,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData)
if ((mHeader->flags&ResStringPool_header::UTF8_FLAG &&
((uint8_t*)mStrings)[mStringPoolSize-1] != 0) ||
(!mHeader->flags&ResStringPool_header::UTF8_FLAG &&
- ((char16_t*)mStrings)[mStringPoolSize-1] != 0)) {
+ ((uint16_t*)mStrings)[mStringPoolSize-1] != 0)) {
ALOGW("Bad string block: last string is not 0-terminated\n");
return (mError=BAD_TYPE);
}
@@ -664,7 +664,7 @@ void ResStringPool::uninit()
* add it together with the next character.
*/
static inline size_t
-decodeLength(const char16_t** str)
+decodeLength(const uint16_t** str)
{
size_t len = **str;
if ((len & 0x8000) != 0) {
@@ -701,15 +701,15 @@ const char16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const
{
if (mError == NO_ERROR && idx < mHeader->stringCount) {
const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0;
- const uint32_t off = mEntries[idx]/(isUTF8?sizeof(char):sizeof(char16_t));
+ const uint32_t off = mEntries[idx]/(isUTF8?sizeof(uint8_t):sizeof(uint16_t));
if (off < (mStringPoolSize-1)) {
if (!isUTF8) {
- const char16_t* strings = (char16_t*)mStrings;
- const char16_t* str = strings+off;
+ const uint16_t* strings = (uint16_t*)mStrings;
+ const uint16_t* str = strings+off;
*u16len = decodeLength(&str);
if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) {
- return str;
+ return reinterpret_cast<const char16_t*>(str);
} else {
ALOGW("Bad string block: string #%d extends to %d, past end at %d\n",
(int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize);
@@ -3765,8 +3765,12 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag
Entry entry;
status_t err = getEntry(grp, t, e, &desiredConfig, &entry);
if (err != NO_ERROR) {
+ // Only log the failure when we're not running on the host as
+ // part of a tool. The caller will do its own logging.
+#ifndef STATIC_ANDROIDFW_FOR_TOOLS
ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) (error %d)\n",
resID, t, e, err);
+#endif
return err;
}
@@ -4794,8 +4798,15 @@ bool ResTable::stringToValue(Res_value* outValue, String16* outString,
// It's a reference!
if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') {
+ // Special case @null as undefined. This will be converted by
+ // AssetManager to TYPE_NULL with data DATA_NULL_UNDEFINED.
outValue->data = 0;
return true;
+ } else if (len == 6 && s[1]=='e' && s[2]=='m' && s[3]=='p' && s[4]=='t' && s[5]=='y') {
+ // Special case @empty as explicitly defined empty value.
+ outValue->dataType = Res_value::TYPE_NULL;
+ outValue->data = Res_value::DATA_NULL_EMPTY;
+ return true;
} else {
bool createIfNotFound = false;
const char16_t* resourceRefName;
@@ -5815,8 +5826,8 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
if (idx == 0) {
idx = mPackageGroups.size() + 1;
- char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)];
- strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t));
+ char16_t tmpName[sizeof(pkg->name)/sizeof(pkg->name[0])];
+ strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(pkg->name[0]));
group = new PackageGroup(this, String16(tmpName), id);
if (group == NULL) {
delete package;
@@ -6194,7 +6205,10 @@ status_t ResTable::createIdmap(const ResTable& overlay,
*outSize += 2 * sizeof(uint16_t);
// overlay packages are assumed to contain only one package group
- const String16 overlayPackage(overlay.mPackageGroups[0]->packages[0]->package->name);
+ const ResTable_package* overlayPackageStruct = overlay.mPackageGroups[0]->packages[0]->package;
+ char16_t tmpName[sizeof(overlayPackageStruct->name)/sizeof(overlayPackageStruct->name[0])];
+ strcpy16_dtoh(tmpName, overlayPackageStruct->name, sizeof(overlayPackageStruct->name)/sizeof(overlayPackageStruct->name[0]));
+ const String16 overlayPackage(tmpName);
for (size_t typeIndex = 0; typeIndex < pg->types.size(); ++typeIndex) {
const TypeList& typeList = pg->types[typeIndex];
@@ -6416,7 +6430,14 @@ String8 ResTable::normalizeForOutput( const char *input )
void ResTable::print_value(const Package* pkg, const Res_value& value) const
{
if (value.dataType == Res_value::TYPE_NULL) {
- printf("(null)\n");
+ if (value.data == Res_value::DATA_NULL_UNDEFINED) {
+ printf("(null)\n");
+ } else if (value.data == Res_value::DATA_NULL_EMPTY) {
+ printf("(null empty)\n");
+ } else {
+ // This should never happen.
+ printf("(null) 0x%08x\n", value.data);
+ }
} else if (value.dataType == Res_value::TYPE_REFERENCE) {
printf("(reference) 0x%08x\n", value.data);
} else if (value.dataType == Res_value::TYPE_DYNAMIC_REFERENCE) {
@@ -6496,8 +6517,10 @@ void ResTable::print(bool inclValues) const
// Use a package's real ID, since the ID may have been assigned
// if this package is a shared library.
packageId = pkg->package->id;
+ char16_t tmpName[sizeof(pkg->package->name)/sizeof(pkg->package->name[0])];
+ strcpy16_dtoh(tmpName, pkg->package->name, sizeof(pkg->package->name)/sizeof(pkg->package->name[0]));
printf(" Package %d id=0x%02x name=%s\n", (int)pkgIndex,
- pkg->package->id, String8(String16(pkg->package->name)).string());
+ pkg->package->id, String8(tmpName).string());
}
for (size_t typeIndex=0; typeIndex < pg->types.size(); typeIndex++) {
diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp
index 29686ef..ea54cc5 100644
--- a/libs/androidfw/misc.cpp
+++ b/libs/androidfw/misc.cpp
@@ -22,9 +22,9 @@
#include <androidfw/misc.h>
#include <sys/stat.h>
-#include <string.h>
+#include <cstring>
#include <errno.h>
-#include <stdio.h>
+#include <cstdio>
using namespace android;
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index 8aaa887..dd45200 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -26,6 +26,7 @@ testFiles := \
Idmap_test.cpp \
ResTable_test.cpp \
Split_test.cpp \
+ TestHelpers.cpp \
Theme_test.cpp \
TypeWrappers_test.cpp \
ZipUtils_test.cpp
@@ -54,6 +55,7 @@ include $(BUILD_HOST_NATIVE_TEST)
# ==========================================================
# Build the device tests: libandroidfw_tests
# ==========================================================
+ifneq ($(SDK_ONLY),true)
include $(CLEAR_VARS)
LOCAL_MODULE := libandroidfw_tests
@@ -65,7 +67,6 @@ LOCAL_CFLAGS += -Wno-unnamed-type-template-args
LOCAL_SRC_FILES := $(testFiles) \
BackupData_test.cpp \
ObbFile_test.cpp
-
LOCAL_SHARED_LIBRARIES := \
libandroidfw \
libcutils \
@@ -73,3 +74,5 @@ LOCAL_SHARED_LIBRARIES := \
libui \
include $(BUILD_NATIVE_TEST)
+endif # Not SDK_ONLY
+
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
index 89d271d0..6a9314e 100644
--- a/libs/androidfw/tests/ResTable_test.cpp
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -37,8 +37,6 @@ namespace {
#include "data/lib/lib_arsc.h"
-enum { MAY_NOT_BE_BAG = false };
-
TEST(ResTableTest, shouldLoadSuccessfully) {
ResTable table;
ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
@@ -48,15 +46,7 @@ TEST(ResTableTest, simpleTypeIsRetrievedCorrectly) {
ResTable table;
ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
- Res_value val;
- ssize_t block = table.getResource(base::R::string::test1, &val, MAY_NOT_BE_BAG);
-
- ASSERT_GE(block, 0);
- ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
-
- const ResStringPool* pool = table.getTableStringBlock(block);
- ASSERT_TRUE(NULL != pool);
- ASSERT_EQ(String8("test1"), pool->string8ObjectAt(val.data));
+ EXPECT_TRUE(IsStringEqual(table, base::R::string::test1, "test1"));
}
TEST(ResTableTest, resourceNameIsResolved) {
diff --git a/libs/androidfw/tests/Split_test.cpp b/libs/androidfw/tests/Split_test.cpp
index f63f566..b69d685 100644
--- a/libs/androidfw/tests/Split_test.cpp
+++ b/libs/androidfw/tests/Split_test.cpp
@@ -42,6 +42,9 @@ namespace {
* Package: com.android.test.basic
*/
#include "data/basic/split_de_fr_arsc.h"
+#include "data/basic/split_hdpi_v4_arsc.h"
+#include "data/basic/split_xhdpi_v4_arsc.h"
+#include "data/basic/split_xxhdpi_v4_arsc.h"
/**
* Include a binary resource table. This table
@@ -163,6 +166,33 @@ TEST(SplitTest, TypeEntrySpecFlagsAreUpdated) {
EXPECT_EQ(ResTable_config::CONFIG_LOCALE, frSpecFlags);
}
+TEST(SplitTest, SelectBestDensity) {
+ ResTable_config baseConfig;
+ memset(&baseConfig, 0, sizeof(baseConfig));
+ baseConfig.density = ResTable_config::DENSITY_XHIGH;
+ baseConfig.sdkVersion = 21;
+
+ ResTable table;
+ table.setParameters(&baseConfig);
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+ ASSERT_EQ(NO_ERROR, table.add(split_hdpi_v4_arsc, split_hdpi_v4_arsc_len));
+
+ EXPECT_TRUE(IsStringEqual(table, base::R::string::density, "hdpi"));
+
+ ASSERT_EQ(NO_ERROR, table.add(split_xhdpi_v4_arsc, split_xhdpi_v4_arsc_len));
+
+ EXPECT_TRUE(IsStringEqual(table, base::R::string::density, "xhdpi"));
+
+ ASSERT_EQ(NO_ERROR, table.add(split_xxhdpi_v4_arsc, split_xxhdpi_v4_arsc_len));
+
+ EXPECT_TRUE(IsStringEqual(table, base::R::string::density, "xhdpi"));
+
+ baseConfig.density = ResTable_config::DENSITY_XXHIGH;
+ table.setParameters(&baseConfig);
+
+ EXPECT_TRUE(IsStringEqual(table, base::R::string::density, "xxhdpi"));
+}
+
TEST(SplitFeatureTest, TestNewResourceIsAccessible) {
ResTable table;
ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
@@ -188,7 +218,7 @@ TEST(SplitFeatureTest, TestNewResourceNameHasCorrectName) {
ASSERT_EQ(NO_ERROR, table.add(feature_arsc, feature_arsc_len));
- EXPECT_TRUE(table.getResourceName(base::R::string::test3, false, &name));
+ ASSERT_TRUE(table.getResourceName(base::R::string::test3, false, &name));
EXPECT_EQ(String16("com.android.test.basic"),
String16(name.package, name.packageLen));
diff --git a/libs/androidfw/tests/TestHelpers.cpp b/libs/androidfw/tests/TestHelpers.cpp
new file mode 100644
index 0000000..41a19a7
--- /dev/null
+++ b/libs/androidfw/tests/TestHelpers.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 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 "TestHelpers.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <utils/String8.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resourceId, const char* expectedStr) {
+ Res_value val;
+ ssize_t block = table.getResource(resourceId, &val, MAY_NOT_BE_BAG);
+ if (block < 0) {
+ return ::testing::AssertionFailure() << "could not find resource";
+ }
+
+ if (val.dataType != Res_value::TYPE_STRING) {
+ return ::testing::AssertionFailure() << "resource is not a string";
+ }
+
+ const ResStringPool* pool = table.getTableStringBlock(block);
+ if (pool == NULL) {
+ return ::testing::AssertionFailure() << "table has no string pool for block " << block;
+ }
+
+ const String8 actual = pool->string8ObjectAt(val.data);
+ if (String8(expectedStr) != actual) {
+ return ::testing::AssertionFailure() << actual.string();
+ }
+ return ::testing::AssertionSuccess() << actual.string();
+}
+
+} // namespace android
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
index fe2e5ce..ac80d88 100644
--- a/libs/androidfw/tests/TestHelpers.h
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -6,6 +6,7 @@
#include <androidfw/ResourceTypes.h>
#include <utils/String8.h>
#include <utils/String16.h>
+#include <gtest/gtest.h>
static inline ::std::ostream& operator<<(::std::ostream& out, const android::String8& str) {
return out << str.string();
@@ -17,6 +18,8 @@ static inline ::std::ostream& operator<<(::std::ostream& out, const android::Str
namespace android {
+enum { MAY_NOT_BE_BAG = false };
+
static inline bool operator==(const android::ResTable_config& a, const android::ResTable_config& b) {
return memcmp(&a, &b, sizeof(a)) == 0;
}
@@ -25,6 +28,8 @@ static inline ::std::ostream& operator<<(::std::ostream& out, const android::Res
return out << c.toString().string();
}
+::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resourceId, const char* expectedStr);
+
} // namespace android
#endif // __TEST_HELPERS_H
diff --git a/libs/androidfw/tests/data/app/R.h b/libs/androidfw/tests/data/app/R.h
index 780a116..23e68e3 100644
--- a/libs/androidfw/tests/data/app/R.h
+++ b/libs/androidfw/tests/data/app/R.h
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
#ifndef __APP_R_H
#define __APP_R_H
diff --git a/libs/androidfw/tests/data/app/build b/libs/androidfw/tests/data/app/build
index 89c4641..62257bc 100755
--- a/libs/androidfw/tests/data/app/build
+++ b/libs/androidfw/tests/data/app/build
@@ -1,4 +1,19 @@
#!/bin/bash
+#
+# Copyright (C) 2014 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.
+#
aapt package -v -I ../system/bundle.apk -M AndroidManifest.xml -S res -F bundle.apk -f && \
unzip bundle.apk resources.arsc && \
diff --git a/libs/androidfw/tests/data/app/res/values/values.xml b/libs/androidfw/tests/data/app/res/values/values.xml
index b0ead38..c1cf64c 100644
--- a/libs/androidfw/tests/data/app/res/values/values.xml
+++ b/libs/androidfw/tests/data/app/res/values/values.xml
@@ -1,4 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
<resources>
<attr name="number" format="integer"/>
<style name="Theme.One" parent="@android:style/Theme.One">
diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h
index 363dcb9..aaac740 100644
--- a/libs/androidfw/tests/data/basic/R.h
+++ b/libs/androidfw/tests/data/basic/R.h
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
#ifndef __BASE_R_H
#define __BASE_R_H
@@ -21,9 +37,10 @@ namespace string {
enum {
test1 = 0x7f030000, // default
test2 = 0x7f030001, // default
+ density = 0x7f030002, // default
- test3 = 0x7f070000, // default (in feature)
- test4 = 0x7f070001, // default (in feature)
+ test3 = 0x7f080000, // default (in feature)
+ test4 = 0x7f080001, // default (in feature)
};
}
@@ -32,7 +49,7 @@ namespace integer {
number1 = 0x7f040000, // default, sv
number2 = 0x7f040001, // default
- test3 = 0x7f080000, // default (in feature)
+ test3 = 0x7f090000, // default (in feature)
};
}
diff --git a/libs/androidfw/tests/data/basic/basic_arsc.h b/libs/androidfw/tests/data/basic/basic_arsc.h
index 61cb94c..13ab4fa 100644
--- a/libs/androidfw/tests/data/basic/basic_arsc.h
+++ b/libs/androidfw/tests/data/basic/basic_arsc.h
@@ -1,5 +1,5 @@
unsigned char basic_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x60, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0c, 0x00, 0x68, 0x07, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x1c, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
@@ -16,7 +16,7 @@ unsigned char basic_arsc[] = {
0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
0x31, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,
0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01,
- 0x98, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
+ 0xa0, 0x06, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00,
0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00,
@@ -101,61 +101,61 @@ unsigned char basic_arsc[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
- 0x6c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x44, 0x00, 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
- 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
- 0xc8, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x7f, 0x01, 0x02, 0x44, 0x00,
- 0x5c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x73, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
- 0x08, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
- 0x90, 0x01, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x90, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0xc8, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01,
+ 0x00, 0x00, 0x06, 0x7f, 0x01, 0x02, 0x44, 0x00, 0x5c, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x76, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10, 0x90, 0x01, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x44, 0x00, 0x90, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
- 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10, 0x64, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x7f,
- 0x10, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x7f,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10,
- 0x2c, 0x01, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
- 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x44, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f,
+ 0x08, 0x00, 0x00, 0x10, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x7f,
+ 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x04, 0x7f, 0x10, 0x00, 0x01, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x7f, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10, 0x2c, 0x01, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+ 0x7c, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x10, 0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
- 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
- 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
- 0x03, 0x00, 0x00, 0x00
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x00
};
-unsigned int basic_arsc_len = 1888;
+unsigned int basic_arsc_len = 1896;
diff --git a/libs/androidfw/tests/data/basic/build b/libs/androidfw/tests/data/basic/build
index 036e468..fd289fa 100755
--- a/libs/androidfw/tests/data/basic/build
+++ b/libs/androidfw/tests/data/basic/build
@@ -1,11 +1,39 @@
#!/bin/bash
+#
+# Copyright (C) 2014 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.
+#
PATH_TO_FRAMEWORK_RES=$(gettop)/prebuilts/sdk/current/android.jar
-aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES --split fr,de -F bundle.apk -f && \
+aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES --split hdpi --split xhdpi --split xxhdpi --split fr,de -F bundle.apk -f && \
unzip bundle.apk resources.arsc && \
mv resources.arsc basic.arsc && \
xxd -i basic.arsc > basic_arsc.h && \
+\
unzip bundle_de_fr.apk resources.arsc && \
mv resources.arsc split_de_fr.arsc && \
-xxd -i split_de_fr.arsc > split_de_fr_arsc.h
+xxd -i split_de_fr.arsc > split_de_fr_arsc.h && \
+\
+unzip bundle_hdpi-v4.apk resources.arsc && \
+mv resources.arsc split_hdpi_v4.arsc && \
+xxd -i split_hdpi_v4.arsc > split_hdpi_v4_arsc.h && \
+\
+unzip bundle_xhdpi-v4.apk resources.arsc && \
+mv resources.arsc split_xhdpi_v4.arsc && \
+xxd -i split_xhdpi_v4.arsc > split_xhdpi_v4_arsc.h && \
+\
+unzip bundle_xxhdpi-v4.apk resources.arsc && \
+mv resources.arsc split_xxhdpi_v4.arsc && \
+xxd -i split_xxhdpi_v4.arsc > split_xxhdpi_v4_arsc.h \
diff --git a/libs/androidfw/tests/data/basic/res/layout-fr-sw600dp/main.xml b/libs/androidfw/tests/data/basic/res/layout-fr-sw600dp/main.xml
index 05ffd58..0dcf7e0 100644
--- a/libs/androidfw/tests/data/basic/res/layout-fr-sw600dp/main.xml
+++ b/libs/androidfw/tests/data/basic/res/layout-fr-sw600dp/main.xml
@@ -1,3 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
<merge>
</merge>
diff --git a/libs/androidfw/tests/data/basic/res/layout/main.xml b/libs/androidfw/tests/data/basic/res/layout/main.xml
index 05ffd58..0dcf7e0 100644
--- a/libs/androidfw/tests/data/basic/res/layout/main.xml
+++ b/libs/androidfw/tests/data/basic/res/layout/main.xml
@@ -1,3 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
<merge>
</merge>
diff --git a/libs/androidfw/tests/data/basic/res/values-de/values.xml b/libs/androidfw/tests/data/basic/res/values-de/values.xml
index 103c6a3..2683a7e 100644
--- a/libs/androidfw/tests/data/basic/res/values-de/values.xml
+++ b/libs/androidfw/tests/data/basic/res/values-de/values.xml
@@ -1,4 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
<resources>
<string name="test1">versuch 1</string>
<string name="test2">versuch 2</string>
diff --git a/libs/androidfw/tests/data/basic/res/values-fr/values.xml b/libs/androidfw/tests/data/basic/res/values-fr/values.xml
index 1806a2d..7d3bed3 100644
--- a/libs/androidfw/tests/data/basic/res/values-fr/values.xml
+++ b/libs/androidfw/tests/data/basic/res/values-fr/values.xml
@@ -1,4 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
<resources>
<string name="test1">essai 1</string>
<string name="test2">essai 2</string>
diff --git a/libs/androidfw/tests/data/basic/res/values-hdpi/values.xml b/libs/androidfw/tests/data/basic/res/values-hdpi/values.xml
new file mode 100644
index 0000000..04bf943
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/values-hdpi/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<resources>
+ <string name="density">hdpi</string>
+</resources>
diff --git a/libs/androidfw/tests/data/basic/res/values-sv/values.xml b/libs/androidfw/tests/data/basic/res/values-sv/values.xml
index 9d52307..7351b49 100644
--- a/libs/androidfw/tests/data/basic/res/values-sv/values.xml
+++ b/libs/androidfw/tests/data/basic/res/values-sv/values.xml
@@ -1,4 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
<resources>
<integer name="number1">400</integer>
</resources>
diff --git a/libs/androidfw/tests/data/basic/res/values-xhdpi/values.xml b/libs/androidfw/tests/data/basic/res/values-xhdpi/values.xml
new file mode 100644
index 0000000..845e9a0
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/values-xhdpi/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<resources>
+ <string name="density">xhdpi</string>
+</resources>
diff --git a/libs/androidfw/tests/data/basic/res/values-xxhdpi/values.xml b/libs/androidfw/tests/data/basic/res/values-xxhdpi/values.xml
new file mode 100644
index 0000000..964da02
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/values-xxhdpi/values.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<resources>
+ <string name="density">xxhdpi</string>
+</resources>
diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml
index 662eda6..a010cca 100644
--- a/libs/androidfw/tests/data/basic/res/values/values.xml
+++ b/libs/androidfw/tests/data/basic/res/values/values.xml
@@ -1,4 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
<resources>
<attr name="attr1" format="reference|integer" />
<attr name="attr2" format="reference|integer" />
diff --git a/libs/androidfw/tests/data/basic/split_de_fr_arsc.h b/libs/androidfw/tests/data/basic/split_de_fr_arsc.h
index a8eaf0b..b742d28 100644
--- a/libs/androidfw/tests/data/basic/split_de_fr_arsc.h
+++ b/libs/androidfw/tests/data/basic/split_de_fr_arsc.h
@@ -1,5 +1,5 @@
unsigned char split_de_fr_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0xd8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0c, 0x00, 0xe4, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x1c, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
@@ -10,7 +10,7 @@ unsigned char split_de_fr_arsc[] = {
0x32, 0x00, 0x00, 0x00, 0x07, 0x00, 0x65, 0x00, 0x73, 0x00, 0x73, 0x00,
0x61, 0x00, 0x69, 0x00, 0x20, 0x00, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00,
0x65, 0x00, 0x73, 0x00, 0x73, 0x00, 0x61, 0x00, 0x69, 0x00, 0x20, 0x00,
- 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x50, 0x03, 0x00, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x5c, 0x03, 0x00, 0x00,
0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
@@ -55,24 +55,25 @@ unsigned char split_de_fr_arsc[] = {
0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
- 0x04, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x65, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+ 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x64, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x01, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00,
- 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x72, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x44, 0x00, 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
@@ -82,4 +83,4 @@ unsigned char split_de_fr_arsc[] = {
0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
-unsigned int split_de_fr_arsc_len = 984;
+unsigned int split_de_fr_arsc_len = 996;
diff --git a/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h b/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h
new file mode 100644
index 0000000..e9fb7ea
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/split_hdpi_v4_arsc.h
@@ -0,0 +1,68 @@
+unsigned char split_hdpi_v4_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0x08, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x68, 0x00,
+ 0x64, 0x00, 0x70, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01,
+ 0xd0, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
+ 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
+ 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00,
+ 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00,
+ 0x69, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x20, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xb0, 0x01, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+ 0x90, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x2c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00,
+ 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00,
+ 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00,
+ 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00,
+ 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00,
+ 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+ 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6e, 0x00,
+ 0x73, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x44, 0x00, 0x60, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+unsigned int split_hdpi_v4_arsc_len = 776;
diff --git a/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h b/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h
new file mode 100644
index 0000000..7835f71
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/split_xhdpi_v4_arsc.h
@@ -0,0 +1,68 @@
+unsigned char split_xhdpi_v4_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x78, 0x00,
+ 0x68, 0x00, 0x64, 0x00, 0x70, 0x00, 0x69, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x20, 0x01, 0xd0, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
+ 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00,
+ 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00,
+ 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00,
+ 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0xb0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x90, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00,
+ 0x4c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
+ 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00,
+ 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00,
+ 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00,
+ 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x79, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00,
+ 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x64, 0x00,
+ 0x65, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x60, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+unsigned int split_xhdpi_v4_arsc_len = 780;
diff --git a/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h b/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h
new file mode 100644
index 0000000..f805db1
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/split_xxhdpi_v4_arsc.h
@@ -0,0 +1,68 @@
+unsigned char split_xxhdpi_v4_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x78, 0x00,
+ 0x78, 0x00, 0x68, 0x00, 0x64, 0x00, 0x70, 0x00, 0x69, 0x00, 0x00, 0x00,
+ 0x00, 0x02, 0x20, 0x01, 0xd0, 0x02, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
+ 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00,
+ 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00,
+ 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x2e, 0x00, 0x62, 0x00,
+ 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0xb0, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x90, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00,
+ 0x4c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
+ 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00,
+ 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00,
+ 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00,
+ 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x79, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00,
+ 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x34, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x64, 0x00,
+ 0x65, 0x00, 0x6e, 0x00, 0x73, 0x00, 0x69, 0x00, 0x74, 0x00, 0x79, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x60, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+unsigned int split_xxhdpi_v4_arsc_len = 780;
diff --git a/libs/androidfw/tests/data/feature/build b/libs/androidfw/tests/data/feature/build
index b547dc2..0f3307f 100755
--- a/libs/androidfw/tests/data/feature/build
+++ b/libs/androidfw/tests/data/feature/build
@@ -1,4 +1,19 @@
#!/bin/bash
+#
+# Copyright (C) 2014 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.
+#
aapt package -M AndroidManifest.xml -S res --feature-of ../basic/bundle.apk -F bundle.apk -f && \
unzip bundle.apk resources.arsc && \
diff --git a/libs/androidfw/tests/data/feature/feature_arsc.h b/libs/androidfw/tests/data/feature/feature_arsc.h
index cf7647d..cd29910 100644
--- a/libs/androidfw/tests/data/feature/feature_arsc.h
+++ b/libs/androidfw/tests/data/feature/feature_arsc.h
@@ -1,11 +1,11 @@
unsigned char feature_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0x40, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0c, 0x00, 0x44, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x33, 0x00,
0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
- 0x34, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xf4, 0x02, 0x00, 0x00,
+ 0x34, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xf8, 0x02, 0x00, 0x00,
0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
@@ -28,46 +28,46 @@ unsigned char feature_arsc[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x7c, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
- 0x2e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x3c, 0x00, 0x65, 0x00, 0x6d, 0x00,
- 0x70, 0x00, 0x74, 0x00, 0x79, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x04, 0x00,
- 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00,
- 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00,
- 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00,
- 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
- 0x58, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
- 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x33, 0x00,
- 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
- 0x34, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00,
- 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00,
- 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00,
- 0x07, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0xa0, 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x80, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00,
+ 0x1e, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x07, 0x00, 0x3c, 0x00,
+ 0x65, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x74, 0x00, 0x79, 0x00, 0x3e, 0x00,
+ 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00,
+ 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00,
+ 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00,
+ 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x58, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,
+ 0x74, 0x00, 0x33, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00,
+ 0x73, 0x00, 0x74, 0x00, 0x34, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00,
+ 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x33, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x18, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+ 0x6c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
- 0x01, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
- 0xc8, 0x00, 0x00, 0x00
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x10, 0xc8, 0x00, 0x00, 0x00
};
-unsigned int feature_arsc_len = 832;
+unsigned int feature_arsc_len = 836;
diff --git a/libs/androidfw/tests/data/feature/res/values/values.xml b/libs/androidfw/tests/data/feature/res/values/values.xml
index d03445a..343fd6c 100644
--- a/libs/androidfw/tests/data/feature/res/values/values.xml
+++ b/libs/androidfw/tests/data/feature/res/values/values.xml
@@ -1,4 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
<resources>
<string name="test3">test3</string>
<string name="test4">test4</string>
diff --git a/libs/androidfw/tests/data/lib/R.h b/libs/androidfw/tests/data/lib/R.h
index 13bf095..ff31120 100644
--- a/libs/androidfw/tests/data/lib/R.h
+++ b/libs/androidfw/tests/data/lib/R.h
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
#ifndef __LIB_R_H
#define __LIB_R_H
diff --git a/libs/androidfw/tests/data/lib/build b/libs/androidfw/tests/data/lib/build
index 8e6e70c..4102903 100755
--- a/libs/androidfw/tests/data/lib/build
+++ b/libs/androidfw/tests/data/lib/build
@@ -1,4 +1,19 @@
#!/bin/bash
+#
+# Copyright (C) 2014 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.
+#
aapt package -M AndroidManifest.xml -S res -F bundle.apk -f --shared-lib && \
unzip bundle.apk resources.arsc && \
diff --git a/libs/androidfw/tests/data/lib/lib_arsc.h b/libs/androidfw/tests/data/lib/lib_arsc.h
index d670c5b..dd3dad5 100644
--- a/libs/androidfw/tests/data/lib/lib_arsc.h
+++ b/libs/androidfw/tests/data/lib/lib_arsc.h
@@ -1,8 +1,8 @@
unsigned char lib_arsc[] = {
- 0x02, 0x00, 0x0c, 0x00, 0xc8, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x0c, 0x00, 0xb8, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xa0, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0x90, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
@@ -37,48 +37,25 @@ unsigned char lib_arsc[] = {
0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00,
0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x31, 0x00, 0x00, 0x00, 0x05, 0x00,
0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x00, 0x00,
- 0x03, 0x02, 0x0c, 0x00, 0x10, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
- 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
- 0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
- 0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x64, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+ 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
- 0x08, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
- 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x64, 0x00, 0x00, 0x00,
- 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
- 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x04, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+ 0x64, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
- 0x08, 0x00, 0x00, 0x10, 0xbc, 0x02, 0x00, 0x00
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x10, 0xbc, 0x02, 0x00, 0x00
};
-unsigned int lib_arsc_len = 968;
+unsigned int lib_arsc_len = 696;
diff --git a/libs/androidfw/tests/data/lib/res/values/values.xml b/libs/androidfw/tests/data/lib/res/values/values.xml
index a77f0c7..3ec79b1 100644
--- a/libs/androidfw/tests/data/lib/res/values/values.xml
+++ b/libs/androidfw/tests/data/lib/res/values/values.xml
@@ -1,4 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
<resources>
<attr name="attr1" format="integer" />
diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build
index 87cf6de..f737677 100755
--- a/libs/androidfw/tests/data/overlay/build
+++ b/libs/androidfw/tests/data/overlay/build
@@ -1,4 +1,19 @@
#!/bin/bash
+#
+# Copyright (C) 2014 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.
+#
aapt package -M AndroidManifest.xml -S res -F bundle.apk -f && \
unzip bundle.apk resources.arsc && \
diff --git a/libs/androidfw/tests/data/overlay/res/values/values.xml b/libs/androidfw/tests/data/overlay/res/values/values.xml
index 227e889..3e1af98 100644
--- a/libs/androidfw/tests/data/overlay/res/values/values.xml
+++ b/libs/androidfw/tests/data/overlay/res/values/values.xml
@@ -1,4 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
<resources>
<string name="test2">test2-overlay</string>
<integer-array name="integerArray1">
diff --git a/libs/androidfw/tests/data/system/R.h b/libs/androidfw/tests/data/system/R.h
index 7a9d3db..27f25fe 100644
--- a/libs/androidfw/tests/data/system/R.h
+++ b/libs/androidfw/tests/data/system/R.h
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
#ifndef __ANDROID_R_H
#define __ANDROID_R_H
diff --git a/libs/androidfw/tests/data/system/build b/libs/androidfw/tests/data/system/build
index 2a3ac0b..1a70e84 100755
--- a/libs/androidfw/tests/data/system/build
+++ b/libs/androidfw/tests/data/system/build
@@ -1,4 +1,19 @@
#!/bin/bash
+#
+# Copyright (C) 2014 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.
+#
aapt package -x -M AndroidManifest.xml -S res -F bundle.apk -f && \
unzip bundle.apk resources.arsc && \
diff --git a/libs/androidfw/tests/data/system/res/values/themes.xml b/libs/androidfw/tests/data/system/res/values/themes.xml
index b29848e..35d43c7 100644
--- a/libs/androidfw/tests/data/system/res/values/themes.xml
+++ b/libs/androidfw/tests/data/system/res/values/themes.xml
@@ -1,4 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
<resources>
<public name="background" type="attr" id="0x01010000"/>
<public name="foreground" type="attr" id="0x01010001"/>
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp
index cb3a002..21c869b 100644
--- a/libs/hwui/AmbientShadow.cpp
+++ b/libs/hwui/AmbientShadow.cpp
@@ -326,9 +326,9 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque,
shadowVertexBuffer.updateVertexCount(vertexBufferIndex);
shadowVertexBuffer.updateIndexCount(indexBufferIndex);
- ShadowTessellator::checkOverflow(vertexBufferIndex, totalVertexCount, "Vertex Buffer");
- ShadowTessellator::checkOverflow(indexBufferIndex, totalIndexCount, "Index Buffer");
- ShadowTessellator::checkOverflow(umbraIndex, totalUmbraCount, "Umbra Buffer");
+ ShadowTessellator::checkOverflow(vertexBufferIndex, totalVertexCount, "Ambient Vertex Buffer");
+ ShadowTessellator::checkOverflow(indexBufferIndex, totalIndexCount, "Ambient Index Buffer");
+ ShadowTessellator::checkOverflow(umbraIndex, totalUmbraCount, "Ambient Umbra Buffer");
#if DEBUG_SHADOW
for (int i = 0; i < vertexBufferIndex; i++) {
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index f0bf7b2..952f739 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -265,14 +265,27 @@ void Caches::dumpMemoryUsage() {
}
void Caches::dumpMemoryUsage(String8 &log) {
+ uint32_t total = 0;
log.appendFormat("Current memory usage / total memory usage (bytes):\n");
log.appendFormat(" TextureCache %8d / %8d\n",
textureCache.getSize(), textureCache.getMaxSize());
log.appendFormat(" LayerCache %8d / %8d (numLayers = %zu)\n",
layerCache.getSize(), layerCache.getMaxSize(), layerCache.getCount());
- log.appendFormat(" Garbage layers %8zu\n", mLayerGarbage.size());
- log.appendFormat(" Active layers %8zu\n",
- mRenderState ? mRenderState->mActiveLayers.size() : 0);
+ if (mRenderState) {
+ int memused = 0;
+ for (std::set<const Layer*>::iterator it = mRenderState->mActiveLayers.begin();
+ it != mRenderState->mActiveLayers.end(); it++) {
+ const Layer* layer = *it;
+ log.appendFormat(" Layer size %dx%d; isTextureLayer()=%d; texid=%u fbo=%u; refs=%d\n",
+ layer->getWidth(), layer->getHeight(),
+ layer->isTextureLayer(), layer->getTexture(),
+ layer->getFbo(), layer->getStrongCount());
+ memused += layer->getWidth() * layer->getHeight() * 4;
+ }
+ log.appendFormat(" Layers total %8d (numLayers = %zu)\n",
+ memused, mRenderState->mActiveLayers.size());
+ total += memused;
+ }
log.appendFormat(" RenderBufferCache %8d / %8d\n",
renderBufferCache.getSize(), renderBufferCache.getMaxSize());
log.appendFormat(" GradientCache %8d / %8d\n",
@@ -297,9 +310,7 @@ void Caches::dumpMemoryUsage(String8 &log) {
log.appendFormat(" FboCache %8d / %8d\n",
fboCache.getSize(), fboCache.getMaxSize());
- uint32_t total = 0;
total += textureCache.getSize();
- total += layerCache.getSize();
total += renderBufferCache.getSize();
total += gradientCache.getSize();
total += pathCache.getSize();
@@ -323,27 +334,6 @@ void Caches::clearGarbage() {
textureCache.clearGarbage();
pathCache.clearGarbage();
patchCache.clearGarbage();
-
- Vector<Layer*> layers;
-
- { // scope for the lock
- Mutex::Autolock _l(mGarbageLock);
- layers = mLayerGarbage;
- mLayerGarbage.clear();
- }
-
- size_t count = layers.size();
- for (size_t i = 0; i < count; i++) {
- Layer* layer = layers.itemAt(i);
- delete layer;
- }
- layers.clear();
-}
-
-void Caches::deleteLayerDeferred(Layer* layer) {
- Mutex::Autolock _l(mGarbageLock);
- layer->state = Layer::kState_InGarbageList;
- mLayerGarbage.push(layer);
}
void Caches::flush(FlushMode mode) {
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 7aa628c..e338686 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -340,7 +340,6 @@ public:
TessellationCache tessellationCache;
TextDropShadowCache dropShadowCache;
FboCache fboCache;
- ResourceCache resourceCache;
GammaFontRenderer* fontRenderer;
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index a998594..fab4a1a 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -525,7 +525,7 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
deferInfo.mergeable &= !recordingComplexClip();
deferInfo.opaqueOverBounds &= !recordingComplexClip() && mSaveStack.isEmpty();
- if (CC_LIKELY(mAvoidOverdraw) && mBatches.size() &&
+ if (mBatches.size() &&
state->mClipSideFlags != kClipSide_ConservativeFull &&
deferInfo.opaqueOverBounds && state->mBounds.contains(mBounds)) {
// avoid overdraw by resetting drawing state + discarding drawing ops
@@ -677,13 +677,12 @@ status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
DrawModifiers restoreDrawModifiers = renderer.getDrawModifiers();
renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
- if (CC_LIKELY(mAvoidOverdraw)) {
- for (unsigned int i = 1; i < mBatches.size(); i++) {
- if (mBatches[i] && mBatches[i]->coversBounds(mBounds)) {
- discardDrawingBatches(i - 1);
- }
+ for (unsigned int i = 1; i < mBatches.size(); i++) {
+ if (mBatches[i] && mBatches[i]->coversBounds(mBounds)) {
+ discardDrawingBatches(i - 1);
}
}
+
// NOTE: depth of the save stack at this point, before playback, should be reflected in
// FLUSH_SAVE_STACK_DEPTH, so that save/restores match up correctly
status |= replayBatchList(mBatches, renderer, dirty);
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 8a015b2..885b411 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -81,8 +81,8 @@ public:
class DeferredDisplayList {
friend class DeferStateStruct; // used to give access to allocator
public:
- DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) :
- mBounds(bounds), mAvoidOverdraw(avoidOverdraw) {
+ DeferredDisplayList(const Rect& bounds) :
+ mBounds(bounds) {
clear();
}
~DeferredDisplayList() { clear(); }
@@ -150,7 +150,6 @@ private:
// layer space bounds of rendering
Rect mBounds;
- const bool mAvoidOverdraw;
/**
* At defer time, stores the *defer time* savecount of save/saveLayer ops that were deferred, so
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index a6d7e78..d02455c 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -24,25 +24,6 @@
namespace android {
namespace uirenderer {
-class DeleteLayerTask : public renderthread::RenderTask {
-public:
- DeleteLayerTask(renderthread::EglManager& eglManager, Layer* layer)
- : mEglManager(eglManager)
- , mLayer(layer)
- {}
-
- virtual void run() {
- mEglManager.requireGlContext();
- LayerRenderer::destroyLayer(mLayer);
- mLayer = 0;
- delete this;
- }
-
-private:
- renderthread::EglManager& mEglManager;
- Layer* mLayer;
-};
-
DeferredLayerUpdater::DeferredLayerUpdater(renderthread::RenderThread& thread, Layer* layer)
: mSurfaceTexture(0)
, mTransform(0)
@@ -62,7 +43,7 @@ DeferredLayerUpdater::DeferredLayerUpdater(renderthread::RenderThread& thread, L
DeferredLayerUpdater::~DeferredLayerUpdater() {
SkSafeUnref(mColorFilter);
setTransform(0);
- mRenderThread.queue(new DeleteLayerTask(mRenderThread.eglManager(), mLayer));
+ mLayer->postDecStrong();
mLayer = 0;
}
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index dda3e89..84411ed 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -76,7 +76,7 @@ public:
ANDROID_API bool apply();
- ANDROID_API Layer* backingLayer() {
+ Layer* backingLayer() {
return mLayer;
}
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index d8932ce..8953166 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -39,33 +39,28 @@ DisplayListData::~DisplayListData() {
}
void DisplayListData::cleanupResources() {
- Caches& caches = Caches::getInstance();
- caches.unregisterFunctors(functors.size());
- caches.resourceCache.lock();
+ ResourceCache& resourceCache = ResourceCache::getInstance();
+ resourceCache.lock();
for (size_t i = 0; i < bitmapResources.size(); i++) {
- caches.resourceCache.decrementRefcountLocked(bitmapResources.itemAt(i));
+ resourceCache.decrementRefcountLocked(bitmapResources.itemAt(i));
}
for (size_t i = 0; i < ownedBitmapResources.size(); i++) {
const SkBitmap* bitmap = ownedBitmapResources.itemAt(i);
- caches.resourceCache.decrementRefcountLocked(bitmap);
- caches.resourceCache.destructorLocked(bitmap);
+ resourceCache.decrementRefcountLocked(bitmap);
+ resourceCache.destructorLocked(bitmap);
}
for (size_t i = 0; i < patchResources.size(); i++) {
- caches.resourceCache.decrementRefcountLocked(patchResources.itemAt(i));
+ resourceCache.decrementRefcountLocked(patchResources.itemAt(i));
}
for (size_t i = 0; i < sourcePaths.size(); i++) {
- caches.resourceCache.decrementRefcountLocked(sourcePaths.itemAt(i));
+ resourceCache.decrementRefcountLocked(sourcePaths.itemAt(i));
}
- for (size_t i = 0; i < layers.size(); i++) {
- caches.resourceCache.decrementRefcountLocked(layers.itemAt(i));
- }
-
- caches.resourceCache.unlock();
+ resourceCache.unlock();
for (size_t i = 0; i < paints.size(); i++) {
delete paints.itemAt(i);
@@ -86,7 +81,6 @@ void DisplayListData::cleanupResources() {
paints.clear();
regions.clear();
paths.clear();
- layers.clear();
}
size_t DisplayListData::addChild(DrawRenderNodeOp* op) {
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index dea109c..cb8a8d1 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -147,7 +147,6 @@ public:
Vector<const SkPath*> paths;
SortedVector<const SkPath*> sourcePaths;
Vector<const SkRegion*> regions;
- Vector<Layer*> layers;
Vector<Functor*> functors;
const Vector<Chunk>& getChunks() const {
@@ -157,11 +156,7 @@ public:
size_t addChild(DrawRenderNodeOp* childOp);
const Vector<DrawRenderNodeOp*>& children() { return mChildren; }
- void refProperty(CanvasPropertyPrimitive* prop) {
- mReferenceHolders.push(prop);
- }
-
- void refProperty(CanvasPropertyPaint* prop) {
+ void ref(VirtualLightRefBase* prop) {
mReferenceHolders.push(prop);
}
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index cb3ef9b..d78c1cb 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -212,10 +212,13 @@ protected:
// check state/paint for transparency
if (mPaint) {
+ if (mPaint->getAlpha() != 0xFF) {
+ return false;
+ }
if (mPaint->getShader() && !mPaint->getShader()->isOpaque()) {
return false;
}
- if (mPaint->getAlpha() != 0xFF) {
+ if (Renderer::isBlendedColorFilter(mPaint->getColorFilter())) {
return false;
}
}
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 1f70921..c2cb76e 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -21,8 +21,9 @@
#include <private/hwui/DrawGlInfo.h>
-#include "Caches.h"
+#include "ResourceCache.h"
#include "DeferredDisplayList.h"
+#include "DeferredLayerUpdater.h"
#include "DisplayListLogBuffer.h"
#include "DisplayListOp.h"
#include "DisplayListRenderer.h"
@@ -32,7 +33,7 @@ namespace android {
namespace uirenderer {
DisplayListRenderer::DisplayListRenderer()
- : mCaches(Caches::getInstance())
+ : mResourceCache(ResourceCache::getInstance())
, mDisplayListData(NULL)
, mTranslateX(0.0f)
, mTranslateY(0.0f)
@@ -188,9 +189,11 @@ status_t DisplayListRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty
return DrawGlInfo::kStatusDone;
}
-status_t DisplayListRenderer::drawLayer(Layer* layer, float x, float y) {
- layer = refLayer(layer);
- addDrawOp(new (alloc()) DrawLayerOp(layer, x, y));
+status_t DisplayListRenderer::drawLayer(DeferredLayerUpdater* layerHandle, float x, float y) {
+ // We ref the DeferredLayerUpdater due to its thread-safe ref-counting
+ // semantics.
+ mDisplayListData->ref(layerHandle);
+ addDrawOp(new (alloc()) DrawLayerOp(layerHandle->backingLayer(), x, y));
return DrawGlInfo::kStatusDone;
}
@@ -280,13 +283,13 @@ status_t DisplayListRenderer::drawRoundRect(
CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom,
CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry,
CanvasPropertyPaint* paint) {
- mDisplayListData->refProperty(left);
- mDisplayListData->refProperty(top);
- mDisplayListData->refProperty(right);
- mDisplayListData->refProperty(bottom);
- mDisplayListData->refProperty(rx);
- mDisplayListData->refProperty(ry);
- mDisplayListData->refProperty(paint);
+ mDisplayListData->ref(left);
+ mDisplayListData->ref(top);
+ mDisplayListData->ref(right);
+ mDisplayListData->ref(bottom);
+ mDisplayListData->ref(rx);
+ mDisplayListData->ref(ry);
+ mDisplayListData->ref(paint);
addDrawOp(new (alloc()) DrawRoundRectPropsOp(&left->value, &top->value,
&right->value, &bottom->value, &rx->value, &ry->value, &paint->value));
return DrawGlInfo::kStatusDone;
@@ -300,10 +303,10 @@ status_t DisplayListRenderer::drawCircle(float x, float y, float radius, const S
status_t DisplayListRenderer::drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y,
CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) {
- mDisplayListData->refProperty(x);
- mDisplayListData->refProperty(y);
- mDisplayListData->refProperty(radius);
- mDisplayListData->refProperty(paint);
+ mDisplayListData->ref(x);
+ mDisplayListData->ref(y);
+ mDisplayListData->ref(radius);
+ mDisplayListData->ref(paint);
addDrawOp(new (alloc()) DrawCirclePropsOp(&x->value, &y->value,
&radius->value, &paint->value));
return DrawGlInfo::kStatusDone;
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 3a3fc3a..2cc2be3 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -24,6 +24,7 @@
#include "DisplayListLogBuffer.h"
#include "RenderNode.h"
+#include "ResourceCache.h"
namespace android {
namespace uirenderer {
@@ -44,6 +45,7 @@ namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
class DeferredDisplayList;
+class DeferredLayerUpdater;
class DisplayListRenderer;
class DisplayListOp;
class DrawOp;
@@ -150,7 +152,7 @@ public:
// ----------------------------------------------------------------------------
// Canvas draw operations - special
// ----------------------------------------------------------------------------
- virtual status_t drawLayer(Layer* layer, float x, float y);
+ virtual status_t drawLayer(DeferredLayerUpdater* layerHandle, float x, float y);
virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags);
// TODO: rename for consistency
@@ -209,7 +211,7 @@ private:
mDisplayListData->paths.add(pathCopy);
}
if (mDisplayListData->sourcePaths.indexOf(path) < 0) {
- mCaches.resourceCache.incrementRefcount(path);
+ mResourceCache.incrementRefcount(path);
mDisplayListData->sourcePaths.add(path);
}
return pathCopy;
@@ -267,31 +269,25 @@ private:
return regionCopy;
}
- inline Layer* refLayer(Layer* layer) {
- mDisplayListData->layers.add(layer);
- mCaches.resourceCache.incrementRefcount(layer);
- return layer;
- }
-
inline const SkBitmap* refBitmap(const SkBitmap* bitmap) {
// Note that this assumes the bitmap is immutable. There are cases this won't handle
// correctly, such as creating the bitmap from scratch, drawing with it, changing its
// contents, and drawing again. The only fix would be to always copy it the first time,
// which doesn't seem worth the extra cycles for this unlikely case.
mDisplayListData->bitmapResources.add(bitmap);
- mCaches.resourceCache.incrementRefcount(bitmap);
+ mResourceCache.incrementRefcount(bitmap);
return bitmap;
}
inline const SkBitmap* refBitmapData(const SkBitmap* bitmap) {
mDisplayListData->ownedBitmapResources.add(bitmap);
- mCaches.resourceCache.incrementRefcount(bitmap);
+ mResourceCache.incrementRefcount(bitmap);
return bitmap;
}
inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) {
mDisplayListData->patchResources.add(patch);
- mCaches.resourceCache.incrementRefcount(patch);
+ mResourceCache.incrementRefcount(patch);
return patch;
}
@@ -299,7 +295,7 @@ private:
DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap;
DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap;
- Caches& mCaches;
+ ResourceCache& mResourceCache;
DisplayListData* mDisplayListData;
float mTranslateX;
diff --git a/libs/hwui/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp
index 2409554..e590642 100644
--- a/libs/hwui/DrawProfiler.cpp
+++ b/libs/hwui/DrawProfiler.cpp
@@ -22,7 +22,8 @@
#define DEFAULT_MAX_FRAMES 128
-#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == kNone)) return
+#define RETURN_IF_PROFILING_DISABLED() if (CC_LIKELY(mType == kNone)) return
+#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == kNone && !mShowDirtyRegions)) return
#define NANOS_TO_MILLIS_FLOAT(nanos) ((nanos) * 0.000001f)
@@ -64,7 +65,9 @@ DrawProfiler::DrawProfiler()
, mPreviousTime(0)
, mVerticalUnit(0)
, mHorizontalUnit(0)
- , mThresholdStroke(0) {
+ , mThresholdStroke(0)
+ , mShowDirtyRegions(false)
+ , mFlashToggle(false) {
setDensity(1);
}
@@ -82,27 +85,27 @@ void DrawProfiler::setDensity(float density) {
}
void DrawProfiler::startFrame(nsecs_t recordDurationNanos) {
- RETURN_IF_DISABLED();
+ RETURN_IF_PROFILING_DISABLED();
mData[mCurrentFrame].record = NANOS_TO_MILLIS_FLOAT(recordDurationNanos);
mPreviousTime = systemTime(CLOCK_MONOTONIC);
}
void DrawProfiler::markPlaybackStart() {
- RETURN_IF_DISABLED();
+ RETURN_IF_PROFILING_DISABLED();
nsecs_t now = systemTime(CLOCK_MONOTONIC);
mData[mCurrentFrame].prepare = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
mPreviousTime = now;
}
void DrawProfiler::markPlaybackEnd() {
- RETURN_IF_DISABLED();
+ RETURN_IF_PROFILING_DISABLED();
nsecs_t now = systemTime(CLOCK_MONOTONIC);
mData[mCurrentFrame].playback = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
mPreviousTime = now;
}
void DrawProfiler::finishFrame() {
- RETURN_IF_DISABLED();
+ RETURN_IF_PROFILING_DISABLED();
nsecs_t now = systemTime(CLOCK_MONOTONIC);
mData[mCurrentFrame].swapBuffers = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime);
mPreviousTime = now;
@@ -114,19 +117,30 @@ void DrawProfiler::unionDirty(SkRect* dirty) {
// Not worth worrying about minimizing the dirty region for debugging, so just
// dirty the entire viewport.
if (dirty) {
+ mDirtyRegion = *dirty;
dirty->setEmpty();
}
}
void DrawProfiler::draw(OpenGLRenderer* canvas) {
- if (CC_LIKELY(mType != kBars)) {
- return;
+ RETURN_IF_DISABLED();
+
+ if (mShowDirtyRegions) {
+ mFlashToggle = !mFlashToggle;
+ if (mFlashToggle) {
+ SkPaint paint;
+ paint.setColor(0x7fff0000);
+ canvas->drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop,
+ mDirtyRegion.fRight, mDirtyRegion.fBottom, &paint);
+ }
}
- prepareShapes(canvas->getViewportHeight());
- drawGraph(canvas);
- drawCurrentFrame(canvas);
- drawThreshold(canvas);
+ if (mType == kBars) {
+ prepareShapes(canvas->getViewportHeight());
+ drawGraph(canvas);
+ drawCurrentFrame(canvas);
+ drawThreshold(canvas);
+ }
}
void DrawProfiler::createData() {
@@ -217,6 +231,7 @@ DrawProfiler::ProfileType DrawProfiler::loadRequestedProfileType() {
}
bool DrawProfiler::loadSystemProperties() {
+ bool changed = false;
ProfileType newType = loadRequestedProfileType();
if (newType != mType) {
mType = newType;
@@ -225,13 +240,18 @@ bool DrawProfiler::loadSystemProperties() {
} else {
createData();
}
- return true;
+ changed = true;
}
- return false;
+ bool showDirty = property_get_bool(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false);
+ if (showDirty != mShowDirtyRegions) {
+ mShowDirtyRegions = showDirty;
+ changed = true;
+ }
+ return changed;
}
void DrawProfiler::dumpData(int fd) {
- RETURN_IF_DISABLED();
+ RETURN_IF_PROFILING_DISABLED();
// This method logs the last N frames (where N is <= mDataSize) since the
// last call to dumpData(). In other words if there's a dumpData(), draw frame,
diff --git a/libs/hwui/DrawProfiler.h b/libs/hwui/DrawProfiler.h
index 7c06e5d..de64088 100644
--- a/libs/hwui/DrawProfiler.h
+++ b/libs/hwui/DrawProfiler.h
@@ -88,6 +88,10 @@ private:
* information.
*/
float** mRects;
+
+ bool mShowDirtyRegions;
+ SkRect mDirtyRegion;
+ bool mFlashToggle;
};
} /* namespace uirenderer */
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index b5089aa..b95636b 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -35,6 +35,9 @@ Layer::Layer(Type layerType, RenderState& renderState, const uint32_t layerWidth
, renderState(renderState)
, texture(caches)
, type(layerType) {
+ // TODO: This is a violation of Android's typical ref counting, but it
+ // preserves the old inc/dec ref locations. This should be changed...
+ incStrong(0);
mesh = NULL;
meshElementCount = 0;
cacheable = true;
@@ -53,20 +56,14 @@ Layer::Layer(Type layerType, RenderState& renderState, const uint32_t layerWidth
forceFilter = false;
deferredList = NULL;
convexMask = NULL;
- caches.resourceCache.incrementRefcount(this);
rendererLightPosDirty = true;
wasBuildLayered = false;
- if (!isTextureLayer()) {
- // track only non-texture layer lifecycles in renderstate,
- // because texture layers are destroyed via finalizer
- renderState.registerLayer(this);
- }
+ renderState.registerLayer(this);
}
Layer::~Layer() {
- if (!isTextureLayer()) {
- renderState.unregisterLayer(this);
- }
+ renderState.requireGLContext();
+ renderState.unregisterLayer(this);
SkSafeUnref(colorFilter);
removeFbo();
deleteTexture();
@@ -292,5 +289,9 @@ void Layer::render(const OpenGLRenderer& rootRenderer) {
renderNode = NULL;
}
+void Layer::postDecStrong() {
+ renderState.postDecStrong(this);
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index b264595..2d6a727 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -52,7 +52,7 @@ struct DeferStateStruct;
/**
* A layer has dimensions and is backed by an OpenGL texture or FBO.
*/
-class Layer {
+class Layer : public VirtualLightRefBase {
public:
enum Type {
kType_Texture,
@@ -280,6 +280,12 @@ public:
void render(const OpenGLRenderer& rootRenderer);
/**
+ * Posts a decStrong call to the appropriate thread.
+ * Thread-safe.
+ */
+ void postDecStrong();
+
+ /**
* Bounds of the layer.
*/
Rect layer;
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 833f64b..3033dc6 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -84,7 +84,7 @@ void LayerCache::deleteLayer(Layer* layer) {
layer->getFbo());
mSize -= layer->getWidth() * layer->getHeight() * 4;
layer->state = Layer::kState_DeletedFromCache;
- Caches::getInstance().resourceCache.decrementRefcount(layer);
+ layer->decStrong(0);
}
}
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 103c843..394c647 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -212,7 +212,7 @@ Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width
// Creating a new layer always increment its refcount by 1, this allows
// us to destroy the layer object if one was created for us
- Caches::getInstance().resourceCache.decrementRefcount(layer);
+ layer->decStrong(0);
return NULL;
}
@@ -240,7 +240,7 @@ Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width
if (glGetError() != GL_NO_ERROR) {
ALOGE("Could not allocate texture for layer (fbo=%d %dx%d)", fbo, width, height);
renderState.bindFramebuffer(previousFbo);
- caches.resourceCache.decrementRefcount(layer);
+ layer->decStrong(0);
return NULL;
}
}
@@ -316,7 +316,7 @@ void LayerRenderer::destroyLayer(Layer* layer) {
if (!Caches::getInstance().layerCache.put(layer)) {
LAYER_RENDERER_LOGD(" Destroyed!");
- Caches::getInstance().resourceCache.decrementRefcount(layer);
+ layer->decStrong(0);
} else {
LAYER_RENDERER_LOGD(" Cached!");
#if DEBUG_LAYER_RENDERER
@@ -328,14 +328,6 @@ void LayerRenderer::destroyLayer(Layer* layer) {
}
}
-void LayerRenderer::destroyLayerDeferred(Layer* layer) {
- if (layer) {
- LAYER_RENDERER_LOGD("Deferring layer destruction, fbo = %d", layer->getFbo());
-
- Caches::getInstance().deleteLayerDeferred(layer);
- }
-}
-
void LayerRenderer::flushLayer(RenderState& renderState, Layer* layer) {
#ifdef GL_EXT_discard_framebuffer
if (!layer) return;
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index bf7828c..4d8620b 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -60,7 +60,6 @@ public:
static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
bool isOpaque, bool forceFilter, GLenum renderTarget, float* textureTransform);
static void destroyLayer(Layer* layer);
- ANDROID_API static void destroyLayerDeferred(Layer* layer);
static bool copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap);
static void flushLayer(RenderState& renderState, Layer* layer);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index ce1d09f..d570b0d 100755
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -51,6 +51,21 @@
#define EVENT_LOGD(...)
#endif
+static void atraceFormatBegin(const char* fmt, ...) {
+ const int BUFFER_SIZE = 256;
+ va_list ap;
+ char buf[BUFFER_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, BUFFER_SIZE, fmt, ap);
+ va_end(ap);
+
+ ATRACE_BEGIN(buf);
+}
+
+#define ATRACE_FORMAT_BEGIN(fmt, ...) \
+ if (CC_UNLIKELY(ATRACE_ENABLED())) atraceFormatBegin(fmt, ##__VA_ARGS__)
+
namespace android {
namespace uirenderer {
@@ -136,7 +151,6 @@ OpenGLRenderer::OpenGLRenderer(RenderState& renderState)
, mScissorOptimizationDisabled(false)
, mSuppressTiling(false)
, mFirstFrameAfterResize(true)
- , mCountOverdraw(false)
, mLightCenter((Vector3){FLT_MIN, FLT_MIN, FLT_MIN})
, mLightRadius(FLT_MIN)
, mAmbientShadowAlpha(0)
@@ -251,7 +265,7 @@ void OpenGLRenderer::discardFramebuffer(float left, float top, float right, floa
}
status_t OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
- if (!opaque || mCountOverdraw) {
+ if (!opaque) {
mCaches.enableScissor();
mCaches.setScissor(left, getViewportHeight() - bottom, right - left, bottom - top);
glClear(GL_COLOR_BUFFER_BIT);
@@ -332,10 +346,6 @@ void OpenGLRenderer::finish() {
#endif
}
- if (mCountOverdraw) {
- countOverdraw();
- }
-
mFrameStarted = false;
}
@@ -449,21 +459,6 @@ void OpenGLRenderer::renderOverdraw() {
}
}
-void OpenGLRenderer::countOverdraw() {
- size_t count = getWidth() * getHeight();
- uint32_t* buffer = new uint32_t[count];
- glReadPixels(0, 0, getWidth(), getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]);
-
- size_t total = 0;
- for (size_t i = 0; i < count; i++) {
- total += buffer[i] & 0xff;
- }
-
- mOverdraw = total / float(count);
-
- delete[] buffer;
-}
-
///////////////////////////////////////////////////////////////////////////////
// Layers
///////////////////////////////////////////////////////////////////////////////
@@ -514,11 +509,8 @@ void OpenGLRenderer::updateLayers() {
// Note: it is very important to update the layers in order
for (int i = 0; i < count; i++) {
- Layer* layer = mLayerUpdates.itemAt(i);
+ Layer* layer = mLayerUpdates.itemAt(i).get();
updateLayer(layer, false);
- if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
- mCaches.resourceCache.decrementRefcount(layer);
- }
}
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
@@ -537,16 +529,15 @@ void OpenGLRenderer::flushLayers() {
// Note: it is very important to update the layers in order
for (int i = 0; i < count; i++) {
+ Layer* layer = mLayerUpdates.itemAt(i).get();
+
sprintf(layerName, "Layer #%d", i);
startMark(layerName);
+ ATRACE_FORMAT_BEGIN("flushLayer %ux%u", layer->getWidth(), layer->getHeight());
- ATRACE_BEGIN("flushLayer");
- Layer* layer = mLayerUpdates.itemAt(i);
layer->flush();
- ATRACE_END();
-
- mCaches.resourceCache.decrementRefcount(layer);
+ ATRACE_END();
endMark();
}
@@ -569,7 +560,6 @@ void OpenGLRenderer::pushLayerUpdate(Layer* layer) {
}
}
mLayerUpdates.push_back(layer);
- mCaches.resourceCache.incrementRefcount(layer);
}
}
@@ -578,25 +568,12 @@ void OpenGLRenderer::cancelLayerUpdate(Layer* layer) {
for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
if (mLayerUpdates.itemAt(i) == layer) {
mLayerUpdates.removeAt(i);
- mCaches.resourceCache.decrementRefcount(layer);
break;
}
}
}
}
-void OpenGLRenderer::clearLayerUpdates() {
- size_t count = mLayerUpdates.size();
- if (count > 0) {
- mCaches.resourceCache.lock();
- for (size_t i = 0; i < count; i++) {
- mCaches.resourceCache.decrementRefcountLocked(mLayerUpdates.itemAt(i));
- }
- mCaches.resourceCache.unlock();
- mLayerUpdates.clear();
- }
-}
-
void OpenGLRenderer::flushLayerUpdates() {
ATRACE_CALL();
syncState();
@@ -631,6 +608,7 @@ void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot&
if (restoreLayer) {
endMark(); // Savelayer
+ ATRACE_END(); // SaveLayer
startMark("ComposeLayer");
composeLayer(removed, restored);
endMark();
@@ -814,6 +792,9 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto
mSnapshot->flags |= Snapshot::kFlagIsLayer;
mSnapshot->layer = layer;
+ ATRACE_FORMAT_BEGIN("%ssaveLayer %ux%u",
+ fboLayer ? "" : "unclipped ",
+ layer->getWidth(), layer->getHeight());
startMark("SaveLayer");
if (fboLayer) {
return createFboLayer(layer, bounds, clip);
@@ -956,7 +937,7 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto
layer->setConvexMask(NULL);
if (!mCaches.layerCache.put(layer)) {
LAYER_LOGD("Deleting layer");
- Caches::getInstance().resourceCache.decrementRefcount(layer);
+ layer->decStrong(0);
}
}
@@ -1627,8 +1608,6 @@ void OpenGLRenderer::setupDraw(bool clearLayer) {
mDescription.hasDebugHighlight = !mCaches.debugOverdraw &&
mCaches.debugStencilClip == Caches::kStencilShowHighlight &&
mCaches.stencil.isTestEnabled();
-
- mDescription.emulateStencil = mCountOverdraw;
}
void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) {
@@ -1714,13 +1693,6 @@ void OpenGLRenderer::accountForClear(SkXfermode::Mode mode) {
}
}
-static bool isBlendedColorFilter(const SkColorFilter* filter) {
- if (filter == NULL) {
- return false;
- }
- return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0;
-}
-
void OpenGLRenderer::setupDrawBlending(const Layer* layer, bool swapSrcDst) {
SkXfermode::Mode mode = layer->getMode();
// When the blending mode is kClear_Mode, we need to use a modulate color
@@ -1967,8 +1939,7 @@ status_t OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int
return status | replayStruct.mDrawGlStatus;
}
- bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs!
- DeferredDisplayList deferredList(*currentClipRect(), avoidOverdraw);
+ DeferredDisplayList deferredList(*currentClipRect());
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
renderNode->defer(deferStruct, 0);
@@ -3440,19 +3411,6 @@ void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
}
mSkipOutlineClip = true;
- if (mCountOverdraw) {
- if (!mCaches.blend) glEnable(GL_BLEND);
- if (mCaches.lastSrcMode != GL_ONE || mCaches.lastDstMode != GL_ONE) {
- glBlendFunc(GL_ONE, GL_ONE);
- }
-
- mCaches.blend = true;
- mCaches.lastSrcMode = GL_ONE;
- mCaches.lastDstMode = GL_ONE;
-
- return;
- }
-
blend = blend || mode != SkXfermode::kSrcOver_Mode;
if (blend) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 47ef1a9..e1c3d10 100755
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -136,19 +136,10 @@ public:
virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque);
virtual void finish();
- void setCountOverdrawEnabled(bool enabled) {
- mCountOverdraw = enabled;
- }
-
- float getOverdraw() {
- return mCountOverdraw ? mOverdraw : 0.0f;
- }
-
virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
void pushLayerUpdate(Layer* layer);
void cancelLayerUpdate(Layer* layer);
- void clearLayerUpdates();
void flushLayerUpdates();
void markLayersAsBuildLayers();
@@ -990,7 +981,7 @@ private:
// List of rectangles to clear after saveLayer() is invoked
Vector<Rect*> mLayers;
// List of layers to update at the beginning of a frame
- Vector<Layer*> mLayerUpdates;
+ Vector< sp<Layer> > mLayerUpdates;
// The following fields are used to setup drawing
// Used to describe the shaders to generate
@@ -1015,11 +1006,6 @@ private:
bool mSuppressTiling;
bool mFirstFrameAfterResize;
- // If true, this renderer will setup drawing to emulate
- // an increment stencil buffer in the color buffer
- bool mCountOverdraw;
- float mOverdraw;
-
bool mSkipOutlineClip;
// Lighting + shadows
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 281ca02..27ef06f 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -812,7 +812,7 @@ void PathTessellator::tessellatePoints(const float* points, int count, const SkP
// determine point shape
SkPath path;
float radius = paintInfo.halfStrokeWidth;
- if (radius == 0.0f) radius = 0.25f;
+ if (radius == 0.0f) radius = 0.5f;
if (paintInfo.cap == SkPaint::kRound_Cap) {
path.addCircle(0, 0, radius);
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index cc72ae0..0dad0dc 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -141,11 +141,12 @@ GLuint Program::buildShader(const char* source, GLenum type) {
GLint status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
+ ALOGE("Error while compiling this shader:\n===\n%s\n===", source);
// Some drivers return wrong values for GL_INFO_LOG_LENGTH
// use a fixed size instead
GLchar log[512];
glGetShaderInfoLog(shader, sizeof(log), 0, &log[0]);
- LOG_ALWAYS_FATAL("Error while compiling shader: %s", log);
+ LOG_ALWAYS_FATAL("Shader info log: %s", log);
return 0;
}
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index 56773f4..d05b331 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -84,8 +84,7 @@ namespace uirenderer {
#define PROGRAM_HAS_COLORS 42
#define PROGRAM_HAS_DEBUG_HIGHLIGHT 43
-#define PROGRAM_EMULATE_STENCIL 44
-#define PROGRAM_HAS_ROUND_RECT_CLIP 45
+#define PROGRAM_HAS_ROUND_RECT_CLIP 44
///////////////////////////////////////////////////////////////////////////////
// Types
@@ -161,7 +160,6 @@ struct ProgramDescription {
float gamma;
bool hasDebugHighlight;
- bool emulateStencil;
bool hasRoundRectClip;
/**
@@ -204,7 +202,6 @@ struct ProgramDescription {
gamma = 2.2f;
hasDebugHighlight = false;
- emulateStencil = false;
hasRoundRectClip = false;
}
@@ -272,7 +269,6 @@ struct ProgramDescription {
if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT;
if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS;
if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT;
- if (emulateStencil) key |= programid(0x1) << PROGRAM_EMULATE_STENCIL;
if (hasRoundRectClip) key |= programid(0x1) << PROGRAM_HAS_ROUND_RECT_CLIP;
return key;
}
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 06353c0..62835e0 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -347,12 +347,6 @@ const char* gFS_Main_FragColor_HasRoundRectClip =
const char* gFS_Main_DebugHighlight =
" gl_FragColor.rgb = vec3(0.0, gl_FragColor.a, 0.0);\n";
-const char* gFS_Main_EmulateStencil =
- " gl_FragColor.rgba = vec4(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0, 1.0);\n"
- " return;\n"
- " /*\n";
-const char* gFS_Footer_EmulateStencil =
- " */\n";
const char* gFS_Footer =
"}\n\n";
@@ -617,7 +611,6 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
&& !description.hasColors
&& description.colorOp == ProgramDescription::kColorNone
&& !description.hasDebugHighlight
- && !description.emulateStencil
&& !description.hasRoundRectClip) {
bool fast = false;
@@ -698,9 +691,6 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
// Begin the shader
shader.append(gFS_Main); {
- if (description.emulateStencil) {
- shader.append(gFS_Main_EmulateStencil);
- }
// Stores the result in fragColor directly
if (description.hasTexture || description.hasExternalTexture) {
if (description.hasAlpha8Texture) {
@@ -779,9 +769,6 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.append(gFS_Main_DebugHighlight);
}
}
- if (description.emulateStencil) {
- shader.append(gFS_Footer_EmulateStencil);
- }
// End the shader
shader.append(gFS_Footer);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 7eb9a32..befed16 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -133,6 +133,15 @@ enum DebugLevel {
#define PROPERTY_DEBUG_STENCIL_CLIP "debug.hwui.show_non_rect_clip"
/**
+ * Turn on to draw dirty regions every other frame.
+ *
+ * Possible values:
+ * "true", to enable dirty regions debugging
+ * "false", to disable dirty regions debugging
+ */
+#define PROPERTY_DEBUG_SHOW_DIRTY_REGIONS "debug.hwui.show_dirty_regions"
+
+/**
* Disables draw operation deferral if set to "true", forcing draw
* commands to be issued to OpenGL in order, and processed in sequence
* with state-manipulation canvas commands.
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 254492f..13c5499 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -50,10 +50,13 @@ void RenderNode::outputLogBuffer(int fd) {
fprintf(file, "\nRecent DisplayList operations\n");
logBuffer.outputCommands(file);
- String8 cachesLog;
- Caches::getInstance().dumpMemoryUsage(cachesLog);
- fprintf(file, "\nCaches:\n%s", cachesLog.string());
- fprintf(file, "\n");
+ if (Caches::hasInstance()) {
+ String8 cachesLog;
+ Caches::getInstance().dumpMemoryUsage(cachesLog);
+ fprintf(file, "\nCaches:\n%s\n", cachesLog.string());
+ } else {
+ fprintf(file, "\nNo caches instance.\n");
+ }
fflush(file);
}
@@ -84,16 +87,17 @@ RenderNode::RenderNode()
RenderNode::~RenderNode() {
deleteDisplayListData();
delete mStagingDisplayListData;
- LayerRenderer::destroyLayerDeferred(mLayer);
+ if (mLayer) {
+ ALOGW("Memory Warning: Layer %p missed its detachment, held on to for far too long!", mLayer);
+ mLayer->postDecStrong();
+ mLayer = 0;
+ }
}
void RenderNode::setStagingDisplayList(DisplayListData* data) {
mNeedsDisplayListDataSync = true;
delete mStagingDisplayListData;
mStagingDisplayListData = data;
- if (mStagingDisplayListData) {
- Caches::getInstance().registerFunctors(mStagingDisplayListData->functors.size());
- }
}
/**
@@ -198,6 +202,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
info.damageAccumulator->peekAtDirty(&dirty);
if (!mLayer) {
+ Caches::getInstance().dumpMemoryUsage();
if (info.errorHandler) {
std::string msg = "Unable to create layer for ";
msg += getName();
@@ -293,7 +298,14 @@ void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) {
mStagingDisplayListData->children()[i]->mRenderNode->incParentRefCount();
}
}
+ // Damage with the old display list first then the new one to catch any
+ // changes in isRenderable or, in the future, bounds
+ damageSelf(info);
deleteDisplayListData();
+ // TODO: Remove this caches stuff
+ if (mStagingDisplayListData && mStagingDisplayListData->functors.size()) {
+ Caches::getInstance().registerFunctors(mStagingDisplayListData->functors.size());
+ }
mDisplayListData = mStagingDisplayListData;
mStagingDisplayListData = NULL;
if (mDisplayListData) {
@@ -310,6 +322,9 @@ void RenderNode::deleteDisplayListData() {
for (size_t i = 0; i < mDisplayListData->children().size(); i++) {
mDisplayListData->children()[i]->mRenderNode->decParentRefCount();
}
+ if (mDisplayListData->functors.size()) {
+ Caches::getInstance().unregisterFunctors(mDisplayListData->functors.size());
+ }
}
delete mDisplayListData;
mDisplayListData = NULL;
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index f329283..2ce7cb7 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -101,7 +101,7 @@ public:
kReplayFlag_ClipChildren = 0x1
};
- ANDROID_API static void outputLogBuffer(int fd);
+ static void outputLogBuffer(int fd);
void debugDumpLayers(const char* prefix);
ANDROID_API void setStagingDisplayList(DisplayListData* newData);
diff --git a/libs/hwui/RenderState.cpp b/libs/hwui/RenderState.cpp
index 86bd7dd..a8cf26f 100644
--- a/libs/hwui/RenderState.cpp
+++ b/libs/hwui/RenderState.cpp
@@ -16,15 +16,18 @@
#include "RenderState.h"
#include "renderthread/CanvasContext.h"
+#include "renderthread/EglManager.h"
namespace android {
namespace uirenderer {
-RenderState::RenderState()
- : mCaches(NULL)
+RenderState::RenderState(renderthread::RenderThread& thread)
+ : mRenderThread(thread)
+ , mCaches(NULL)
, mViewportWidth(0)
, mViewportHeight(0)
, mFramebuffer(0) {
+ mThreadId = pthread_self();
}
RenderState::~RenderState() {
@@ -39,7 +42,6 @@ void RenderState::onGLContextCreated() {
void RenderState::onGLContextDestroyed() {
/*
- AutoMutex _lock(mLayerLock);
size_t size = mActiveLayers.size();
if (CC_UNLIKELY(size != 0)) {
ALOGE("Crashing, have %d contexts and %d layers at context destruction. isempty %d",
@@ -146,5 +148,34 @@ void RenderState::debugOverdraw(bool enable, bool clear) {
}
}
+void RenderState::requireGLContext() {
+ assertOnGLThread();
+ mRenderThread.eglManager().requireGlContext();
+}
+
+void RenderState::assertOnGLThread() {
+ pthread_t curr = pthread_self();
+ LOG_ALWAYS_FATAL_IF(!pthread_equal(mThreadId, curr), "Wrong thread!");
+}
+
+
+class DecStrongTask : public renderthread::RenderTask {
+public:
+ DecStrongTask(VirtualLightRefBase* object) : mObject(object) {}
+
+ virtual void run() {
+ mObject->decStrong(0);
+ mObject = 0;
+ delete this;
+ }
+
+private:
+ VirtualLightRefBase* mObject;
+};
+
+void RenderState::postDecStrong(VirtualLightRefBase* object) {
+ mRenderThread.queue(new DecStrongTask(object));
+}
+
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/RenderState.h b/libs/hwui/RenderState.h
index cbe7cfc..afeef95 100644
--- a/libs/hwui/RenderState.h
+++ b/libs/hwui/RenderState.h
@@ -53,16 +53,10 @@ public:
void debugOverdraw(bool enable, bool clear);
void registerLayer(const Layer* layer) {
- /*
- AutoMutex _lock(mLayerLock);
mActiveLayers.insert(layer);
- */
}
void unregisterLayer(const Layer* layer) {
- /*
- AutoMutex _lock(mLayerLock);
mActiveLayers.erase(layer);
- */
}
void registerCanvasContext(renderthread::CanvasContext* context) {
@@ -73,16 +67,24 @@ public:
mRegisteredContexts.erase(context);
}
+ void requireGLContext();
+
+ // TODO: This system is a little clunky feeling, this could use some
+ // more thinking...
+ void postDecStrong(VirtualLightRefBase* object);
+
private:
friend class renderthread::RenderThread;
friend class Caches;
void interruptForFunctorInvoke();
void resumeFromFunctorInvoke();
+ void assertOnGLThread();
- RenderState();
+ RenderState(renderthread::RenderThread& thread);
~RenderState();
+ renderthread::RenderThread& mRenderThread;
Caches* mCaches;
std::set<const Layer*> mActiveLayers;
std::set<renderthread::CanvasContext*> mRegisteredContexts;
@@ -90,7 +92,8 @@ private:
GLsizei mViewportWidth;
GLsizei mViewportHeight;
GLuint mFramebuffer;
- Mutex mLayerLock;
+
+ pthread_t mThreadId;
};
} /* namespace uirenderer */
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
index 9cedd5a..3159d1e 100644
--- a/libs/hwui/Renderer.h
+++ b/libs/hwui/Renderer.h
@@ -17,12 +17,13 @@
#ifndef ANDROID_HWUI_RENDERER_H
#define ANDROID_HWUI_RENDERER_H
+#include <SkColorFilter.h>
+#include <SkPaint.h>
#include <SkRegion.h>
#include <utils/String8.h>
#include "AssetAtlas.h"
-#include "SkPaint.h"
namespace android {
@@ -81,6 +82,14 @@ public:
&& !paint.getColorFilter()
&& getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode;
}
+
+ static bool isBlendedColorFilter(const SkColorFilter* filter) {
+ if (filter == NULL) {
+ return false;
+ }
+ return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0;
+ }
+
// ----------------------------------------------------------------------------
// Frame state operations
// ----------------------------------------------------------------------------
@@ -211,7 +220,6 @@ public:
// ----------------------------------------------------------------------------
// Canvas draw operations - special
// ----------------------------------------------------------------------------
- virtual status_t drawLayer(Layer* layer, float x, float y) = 0;
virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty,
int32_t replayFlags) = 0;
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 8b553d1..12d4928 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -21,6 +21,12 @@
#include "Caches.h"
namespace android {
+
+#ifdef USE_OPENGL_RENDERER
+using namespace uirenderer;
+ANDROID_SINGLETON_STATIC_INSTANCE(ResourceCache);
+#endif
+
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
@@ -75,10 +81,6 @@ void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) {
incrementRefcount((void*) patchResource, kNinePatch);
}
-void ResourceCache::incrementRefcount(Layer* layerResource) {
- incrementRefcount((void*) layerResource, kLayer);
-}
-
void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourceType) {
ssize_t index = mCache->indexOfKey(resource);
ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
@@ -103,10 +105,6 @@ void ResourceCache::incrementRefcountLocked(const Res_png_9patch* patchResource)
incrementRefcountLocked((void*) patchResource, kNinePatch);
}
-void ResourceCache::incrementRefcountLocked(Layer* layerResource) {
- incrementRefcountLocked((void*) layerResource, kLayer);
-}
-
void ResourceCache::decrementRefcount(void* resource) {
Mutex::Autolock _l(mLock);
decrementRefcountLocked(resource);
@@ -126,10 +124,6 @@ void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) {
decrementRefcount((void*) patchResource);
}
-void ResourceCache::decrementRefcount(Layer* layerResource) {
- decrementRefcount((void*) layerResource);
-}
-
void ResourceCache::decrementRefcountLocked(void* resource) {
ssize_t index = mCache->indexOfKey(resource);
ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
@@ -157,10 +151,6 @@ void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource)
decrementRefcountLocked((void*) patchResource);
}
-void ResourceCache::decrementRefcountLocked(Layer* layerResource) {
- decrementRefcountLocked((void*) layerResource);
-}
-
void ResourceCache::destructor(SkPath* resource) {
Mutex::Autolock _l(mLock);
destructorLocked(resource);
@@ -274,7 +264,7 @@ void ResourceCache::deleteResourceReferenceLocked(const void* resource, Resource
if (ref->recycled && ref->resourceType == kBitmap) {
((SkBitmap*) resource)->setPixels(NULL, NULL);
}
- if (ref->destroyed || ref->resourceType == kLayer) {
+ if (ref->destroyed) {
switch (ref->resourceType) {
case kBitmap: {
SkBitmap* bitmap = (SkBitmap*) resource;
@@ -305,11 +295,6 @@ void ResourceCache::deleteResourceReferenceLocked(const void* resource, Resource
}
}
break;
- case kLayer: {
- Layer* layer = (Layer*) resource;
- Caches::getInstance().deleteLayerDeferred(layer);
- }
- break;
}
}
mCache->removeItem(resource);
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index 3864d4b..a922d53 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -22,6 +22,7 @@
#include <SkBitmap.h>
#include <utils/KeyedVector.h>
+#include <utils/Singleton.h>
#include <androidfw/ResourceTypes.h>
@@ -36,8 +37,7 @@ namespace uirenderer {
enum ResourceType {
kBitmap,
kNinePatch,
- kPath,
- kLayer
+ kPath
};
class ResourceReference {
@@ -54,11 +54,14 @@ public:
ResourceType resourceType;
};
-class ANDROID_API ResourceCache {
-public:
+class ANDROID_API ResourceCache: public Singleton<ResourceCache> {
ResourceCache();
~ResourceCache();
+ friend class Singleton<ResourceCache>;
+
+public:
+
/**
* When using these two methods, make sure to only invoke the *Locked()
* variants of increment/decrementRefcount(), recyle() and destructor()
@@ -69,22 +72,18 @@ public:
void incrementRefcount(const SkPath* resource);
void incrementRefcount(const SkBitmap* resource);
void incrementRefcount(const Res_png_9patch* resource);
- void incrementRefcount(Layer* resource);
void incrementRefcountLocked(const SkPath* resource);
void incrementRefcountLocked(const SkBitmap* resource);
void incrementRefcountLocked(const Res_png_9patch* resource);
- void incrementRefcountLocked(Layer* resource);
void decrementRefcount(const SkBitmap* resource);
void decrementRefcount(const SkPath* resource);
void decrementRefcount(const Res_png_9patch* resource);
- void decrementRefcount(Layer* resource);
void decrementRefcountLocked(const SkBitmap* resource);
void decrementRefcountLocked(const SkPath* resource);
void decrementRefcountLocked(const Res_png_9patch* resource);
- void decrementRefcountLocked(Layer* resource);
void destructor(SkPath* resource);
void destructor(const SkBitmap* resource);
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index dbedf94..e8f1b9a 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -60,7 +60,7 @@
namespace android {
namespace uirenderer {
-static const double EPSILON = 1e-7;
+static const float EPSILON = 1e-7;
/**
* For each polygon's vertex, the light center will project it to the receiver
@@ -118,17 +118,17 @@ static float rayIntersectPoints(const Vector2& rayOrigin, float dx, float dy,
// intersection point should stay on both the ray and the edge of (p1, p2).
// solve([p1x+t*(p2x-p1x)=dx*t2+px,p1y+t*(p2y-p1y)=dy*t2+py],[t,t2]);
- double divisor = (dx * (p1.y - p2.y) + dy * p2.x - dy * p1.x);
+ float divisor = (dx * (p1.y - p2.y) + dy * p2.x - dy * p1.x);
if (divisor == 0) return -1.0f; // error, invalid divisor
#if DEBUG_SHADOW
- double interpVal = (dx * (p1.y - rayOrigin.y) + dy * rayOrigin.x - dy * p1.x) / divisor;
+ float interpVal = (dx * (p1.y - rayOrigin.y) + dy * rayOrigin.x - dy * p1.x) / divisor;
if (interpVal < 0 || interpVal > 1) {
ALOGW("rayIntersectPoints is hitting outside the segment %f", interpVal);
}
#endif
- double distance = (p1.x * (rayOrigin.y - p2.y) + p2.x * (p1.y - rayOrigin.y) +
+ float distance = (p1.x * (rayOrigin.y - p2.y) + p2.x * (p1.y - rayOrigin.y) +
rayOrigin.x * (p2.y - p1.y)) / divisor;
return distance; // may be negative in error cases
@@ -217,146 +217,12 @@ int SpotShadow::hull(Vector2* points, int pointsLength, Vector2* retPoly) {
*
* @return true if a right hand turn
*/
-bool SpotShadow::ccw(double ax, double ay, double bx, double by,
- double cx, double cy) {
+bool SpotShadow::ccw(float ax, float ay, float bx, float by,
+ float cx, float cy) {
return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax) > EPSILON;
}
/**
- * Calculates the intersection of poly1 with poly2 and put in poly2.
- * Note that both poly1 and poly2 must be in CW order already!
- *
- * @param poly1 The 1st polygon, as a Vector2 array.
- * @param poly1Length The number of vertices of 1st polygon.
- * @param poly2 The 2nd and output polygon, as a Vector2 array.
- * @param poly2Length The number of vertices of 2nd polygon.
- * @return number of vertices in output polygon as poly2.
- */
-int SpotShadow::intersection(const Vector2* poly1, int poly1Length,
- Vector2* poly2, int poly2Length) {
-#if DEBUG_SHADOW
- if (!ShadowTessellator::isClockwise(poly1, poly1Length)) {
- ALOGW("Poly1 is not clockwise! Intersection is wrong!");
- }
- if (!ShadowTessellator::isClockwise(poly2, poly2Length)) {
- ALOGW("Poly2 is not clockwise! Intersection is wrong!");
- }
-#endif
- Vector2 poly[poly1Length * poly2Length + 2];
- int count = 0;
- int pcount = 0;
-
- // If one vertex from one polygon sits inside another polygon, add it and
- // count them.
- for (int i = 0; i < poly1Length; i++) {
- if (testPointInsidePolygon(poly1[i], poly2, poly2Length)) {
- poly[count] = poly1[i];
- count++;
- pcount++;
-
- }
- }
-
- int insidePoly2 = pcount;
- for (int i = 0; i < poly2Length; i++) {
- if (testPointInsidePolygon(poly2[i], poly1, poly1Length)) {
- poly[count] = poly2[i];
- count++;
- }
- }
-
- int insidePoly1 = count - insidePoly2;
- // If all vertices from poly1 are inside poly2, then just return poly1.
- if (insidePoly2 == poly1Length) {
- memcpy(poly2, poly1, poly1Length * sizeof(Vector2));
- return poly1Length;
- }
-
- // If all vertices from poly2 are inside poly1, then just return poly2.
- if (insidePoly1 == poly2Length) {
- return poly2Length;
- }
-
- // Since neither polygon fully contain the other one, we need to add all the
- // intersection points.
- Vector2 intersection = {0, 0};
- for (int i = 0; i < poly2Length; i++) {
- for (int j = 0; j < poly1Length; j++) {
- int poly2LineStart = i;
- int poly2LineEnd = ((i + 1) % poly2Length);
- int poly1LineStart = j;
- int poly1LineEnd = ((j + 1) % poly1Length);
- bool found = lineIntersection(
- poly2[poly2LineStart].x, poly2[poly2LineStart].y,
- poly2[poly2LineEnd].x, poly2[poly2LineEnd].y,
- poly1[poly1LineStart].x, poly1[poly1LineStart].y,
- poly1[poly1LineEnd].x, poly1[poly1LineEnd].y,
- intersection);
- if (found) {
- poly[count].x = intersection.x;
- poly[count].y = intersection.y;
- count++;
- } else {
- Vector2 delta = poly2[i] - poly1[j];
- if (delta.lengthSquared() < EPSILON) {
- poly[count] = poly2[i];
- count++;
- }
- }
- }
- }
-
- if (count == 0) {
- return 0;
- }
-
- // Sort the result polygon around the center.
- Vector2 center = {0.0f, 0.0f};
- for (int i = 0; i < count; i++) {
- center += poly[i];
- }
- center /= count;
- sort(poly, count, center);
-
-#if DEBUG_SHADOW
- // Since poly2 is overwritten as the result, we need to save a copy to do
- // our verification.
- Vector2 oldPoly2[poly2Length];
- int oldPoly2Length = poly2Length;
- memcpy(oldPoly2, poly2, sizeof(Vector2) * poly2Length);
-#endif
-
- // Filter the result out from poly and put it into poly2.
- poly2[0] = poly[0];
- int lastOutputIndex = 0;
- for (int i = 1; i < count; i++) {
- Vector2 delta = poly[i] - poly2[lastOutputIndex];
- if (delta.lengthSquared() >= EPSILON) {
- poly2[++lastOutputIndex] = poly[i];
- } else {
- // If the vertices are too close, pick the inner one, because the
- // inner one is more likely to be an intersection point.
- Vector2 delta1 = poly[i] - center;
- Vector2 delta2 = poly2[lastOutputIndex] - center;
- if (delta1.lengthSquared() < delta2.lengthSquared()) {
- poly2[lastOutputIndex] = poly[i];
- }
- }
- }
- int resultLength = lastOutputIndex + 1;
-
-#if DEBUG_SHADOW
- testConvex(poly2, resultLength, "intersection");
- testConvex(poly1, poly1Length, "input poly1");
- testConvex(oldPoly2, oldPoly2Length, "input poly2");
-
- testIntersection(poly1, poly1Length, oldPoly2, oldPoly2Length, poly2, resultLength);
-#endif
-
- return resultLength;
-}
-
-/**
* Sort points about a center point
*
* @param poly The in and out polyogon as a Vector2 array.
@@ -441,13 +307,13 @@ void SpotShadow::quicksortX(Vector2* points, int low, int high) {
bool SpotShadow::testPointInsidePolygon(const Vector2 testPoint,
const Vector2* poly, int len) {
bool c = false;
- double testx = testPoint.x;
- double testy = testPoint.y;
+ float testx = testPoint.x;
+ float testy = testPoint.y;
for (int i = 0, j = len - 1; i < len; j = i++) {
- double startX = poly[j].x;
- double startY = poly[j].y;
- double endX = poly[i].x;
- double endY = poly[i].y;
+ float startX = poly[j].x;
+ float startY = poly[j].y;
+ float endX = poly[i].x;
+ float endY = poly[i].y;
if (((endY > testy) != (startY > testy))
&& (testx < (startX - endX) * (testy - endY)
@@ -490,46 +356,6 @@ void SpotShadow::reverse(Vector2* polygon, int len) {
}
/**
- * Intersects two lines in parametric form. This function is called in a tight
- * loop, and we need double precision to get things right.
- *
- * @param x1 the x coordinate point 1 of line 1
- * @param y1 the y coordinate point 1 of line 1
- * @param x2 the x coordinate point 2 of line 1
- * @param y2 the y coordinate point 2 of line 1
- * @param x3 the x coordinate point 1 of line 2
- * @param y3 the y coordinate point 1 of line 2
- * @param x4 the x coordinate point 2 of line 2
- * @param y4 the y coordinate point 2 of line 2
- * @param ret the x,y location of the intersection
- * @return true if it found an intersection
- */
-inline bool SpotShadow::lineIntersection(double x1, double y1, double x2, double y2,
- double x3, double y3, double x4, double y4, Vector2& ret) {
- double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
- if (d == 0.0) return false;
-
- double dx = (x1 * y2 - y1 * x2);
- double dy = (x3 * y4 - y3 * x4);
- double x = (dx * (x3 - x4) - (x1 - x2) * dy) / d;
- double y = (dx * (y3 - y4) - (y1 - y2) * dy) / d;
-
- // The intersection should be in the middle of the point 1 and point 2,
- // likewise point 3 and point 4.
- if (((x - x1) * (x - x2) > EPSILON)
- || ((x - x3) * (x - x4) > EPSILON)
- || ((y - y1) * (y - y2) > EPSILON)
- || ((y - y3) * (y - y4) > EPSILON)) {
- // Not interesected
- return false;
- }
- ret.x = x;
- ret.y = y;
- return true;
-
-}
-
-/**
* Compute a horizontal circular polygon about point (x , y , height) of radius
* (size)
*
@@ -542,7 +368,7 @@ void SpotShadow::computeLightPolygon(int points, const Vector3& lightCenter,
float size, Vector3* ret) {
// TODO: Caching all the sin / cos values and store them in a look up table.
for (int i = 0; i < points; i++) {
- double angle = 2 * i * M_PI / points;
+ float angle = 2 * i * M_PI / points;
ret[i].x = cosf(angle) * size + lightCenter.x;
ret[i].y = sinf(angle) * size + lightCenter.y;
ret[i].z = lightCenter.z;
@@ -792,83 +618,6 @@ void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3& lightCente
}
/**
- * Converts a polygon specified with CW vertices into an array of distance-from-centroid values.
- *
- * Returns false in error conditions
- *
- * @param poly Array of vertices. Note that these *must* be CW.
- * @param polyLength The number of vertices in the polygon.
- * @param polyCentroid The centroid of the polygon, from which rays will be cast
- * @param rayDist The output array for the calculated distances, must be SHADOW_RAY_COUNT in size
- */
-bool convertPolyToRayDist(const Vector2* poly, int polyLength, const Vector2& polyCentroid,
- float* rayDist) {
- const int rays = SHADOW_RAY_COUNT;
- const float step = M_PI * 2 / rays;
-
- const Vector2* lastVertex = &(poly[polyLength - 1]);
- float startAngle = angle(*lastVertex, polyCentroid);
-
- // Start with the ray that's closest to and less than startAngle
- int rayIndex = floor((startAngle - EPSILON) / step);
- rayIndex = (rayIndex + rays) % rays; // ensure positive
-
- for (int polyIndex = 0; polyIndex < polyLength; polyIndex++) {
- /*
- * For a given pair of vertices on the polygon, poly[i-1] and poly[i], the rays that
- * intersect these will be those that are between the two angles from the centroid that the
- * vertices define.
- *
- * Because the polygon vertices are stored clockwise, the closest ray with an angle
- * *smaller* than that defined by angle(poly[i], centroid) will be the first ray that does
- * not intersect with poly[i-1], poly[i].
- */
- float currentAngle = angle(poly[polyIndex], polyCentroid);
-
- // find first ray that will not intersect the line segment poly[i-1] & poly[i]
- int firstRayIndexOnNextSegment = floor((currentAngle - EPSILON) / step);
- firstRayIndexOnNextSegment = (firstRayIndexOnNextSegment + rays) % rays; // ensure positive
-
- // Iterate through all rays that intersect with poly[i-1], poly[i] line segment.
- // This may be 0 rays.
- while (rayIndex != firstRayIndexOnNextSegment) {
- float distanceToIntersect = rayIntersectPoints(polyCentroid,
- cos(rayIndex * step),
- sin(rayIndex * step),
- *lastVertex, poly[polyIndex]);
- if (distanceToIntersect < 0) {
-#if DEBUG_SHADOW
- ALOGW("ERROR: convertPolyToRayDist failed");
-#endif
- return false; // error case, abort
- }
-
- rayDist[rayIndex] = distanceToIntersect;
-
- rayIndex = (rayIndex - 1 + rays) % rays;
- }
- lastVertex = &poly[polyIndex];
- }
-
- return true;
-}
-
-int SpotShadow::calculateOccludedUmbra(const Vector2* umbra, int umbraLength,
- const Vector3* poly, int polyLength, Vector2* occludedUmbra) {
- // Occluded umbra area is computed as the intersection of the projected 2D
- // poly and umbra.
- for (int i = 0; i < polyLength; i++) {
- occludedUmbra[i].x = poly[i].x;
- occludedUmbra[i].y = poly[i].y;
- }
-
- // Both umbra and incoming polygon are guaranteed to be CW, so we can call
- // intersection() directly.
- return intersection(umbra, umbraLength,
- occludedUmbra, polyLength);
-}
-
-/**
* This is only for experimental purpose.
* After intersections are calculated, we could smooth the polygon if needed.
* So far, we don't think it is more appealing yet.
@@ -889,490 +638,223 @@ void SpotShadow::smoothPolygon(int level, int rays, float* rayDist) {
}
}
-/**
- * Generate a array of the angleData for either umbra or penumbra vertices.
- *
- * This array will be merged and used to guide where to shoot the rays, in clockwise order.
- *
- * @param angleDataList The result array of angle data.
- *
- * @return int The maximum angle's index in the array.
- */
-int SpotShadow::setupAngleList(VertexAngleData* angleDataList,
- int polyLength, const Vector2* polygon, const Vector2& centroid,
- bool isPenumbra, const char* name) {
- float maxAngle = FLT_MIN;
- int maxAngleIndex = 0;
- for (int i = 0; i < polyLength; i++) {
- float currentAngle = angle(polygon[i], centroid);
- if (currentAngle > maxAngle) {
- maxAngle = currentAngle;
- maxAngleIndex = i;
- }
- angleDataList[i].set(currentAngle, isPenumbra, i);
-#if DEBUG_SHADOW
- ALOGD("%s AngleList i %d %f", name, i, currentAngle);
-#endif
- }
- return maxAngleIndex;
-}
+// Index pair is meant for storing the tessellation information for the penumbra
+// area. One index must come from exterior tangent of the circles, the other one
+// must come from the interior tangent of the circles.
+struct IndexPair {
+ int outerIndex;
+ int innerIndex;
+};
-/**
- * Make sure the polygons are indeed in clockwise order.
- *
- * Possible reasons to return false: 1. The input polygon is not setup properly. 2. The hull
- * algorithm is not able to generate it properly.
- *
- * Anyway, since the algorithm depends on the clockwise, when these kind of unexpected error
- * situation is found, we need to detect it and early return without corrupting the memory.
- *
- * @return bool True if the angle list is actually from big to small.
- */
-bool SpotShadow::checkClockwise(int indexOfMaxAngle, int listLength, VertexAngleData* angleList,
- const char* name) {
- int currentIndex = indexOfMaxAngle;
-#if DEBUG_SHADOW
- ALOGD("max index %d", currentIndex);
-#endif
- for (int i = 0; i < listLength - 1; i++) {
- // TODO: Cache the last angle.
- float currentAngle = angleList[currentIndex].mAngle;
- float nextAngle = angleList[(currentIndex + 1) % listLength].mAngle;
- if (currentAngle < nextAngle) {
-#if DEBUG_SHADOW
- ALOGE("%s, is not CW, at index %d", name, currentIndex);
-#endif
- return false;
+// For one penumbra vertex, find the cloest umbra vertex and return its index.
+inline int getClosestUmbraIndex(const Vector2& pivot, const Vector2* polygon, int polygonLength) {
+ float minLengthSquared = FLT_MAX;
+ int resultIndex = -1;
+ bool hasDecreased = false;
+ // Starting with some negative offset, assuming both umbra and penumbra are starting
+ // at the same angle, this can help to find the result faster.
+ // Normally, loop 3 times, we can find the closest point.
+ int offset = polygonLength - 2;
+ for (int i = 0; i < polygonLength; i++) {
+ int currentIndex = (i + offset) % polygonLength;
+ float currentLengthSquared = (pivot - polygon[currentIndex]).lengthSquared();
+ if (currentLengthSquared < minLengthSquared) {
+ if (minLengthSquared != FLT_MAX) {
+ hasDecreased = true;
+ }
+ minLengthSquared = currentLengthSquared;
+ resultIndex = currentIndex;
+ } else if (currentLengthSquared > minLengthSquared && hasDecreased) {
+ // Early break b/c we have found the closet one and now the length
+ // is increasing again.
+ break;
}
- currentIndex = (currentIndex + 1) % listLength;
}
- return true;
+ if(resultIndex == -1) {
+ ALOGE("resultIndex is -1, the polygon must be invalid!");
+ resultIndex = 0;
+ }
+ return resultIndex;
}
-/**
- * Check the polygon is clockwise.
- *
- * @return bool True is the polygon is clockwise.
- */
-bool SpotShadow::checkPolyClockwise(int polyAngleLength, int maxPolyAngleIndex,
- const float* polyAngleList) {
- bool isPolyCW = true;
- // Starting from maxPolyAngleIndex , check around to make sure angle decrease.
- for (int i = 0; i < polyAngleLength - 1; i++) {
- float currentAngle = polyAngleList[(i + maxPolyAngleIndex) % polyAngleLength];
- float nextAngle = polyAngleList[(i + maxPolyAngleIndex + 1) % polyAngleLength];
- if (currentAngle < nextAngle) {
- isPolyCW = false;
- }
+inline bool sameDirections(bool isPositiveCross, float a, float b) {
+ if (isPositiveCross) {
+ return a >= 0 && b >= 0;
+ } else {
+ return a <= 0 && b <= 0;
}
- return isPolyCW;
}
-/**
- * Given the sorted array of all the vertices angle data, calculate for each
- * vertices, the offset value to array element which represent the start edge
- * of the polygon we need to shoot the ray at.
- *
- * TODO: Calculate this for umbra and penumbra in one loop using one single array.
- *
- * @param distances The result of the array distance counter.
- */
-void SpotShadow::calculateDistanceCounter(bool needsOffsetToUmbra, int angleLength,
- const VertexAngleData* allVerticesAngleData, int* distances) {
-
- bool firstVertexIsPenumbra = allVerticesAngleData[0].mIsPenumbra;
- // If we want distance to inner, then we just set to 0 when we see inner.
- bool needsSearch = needsOffsetToUmbra ? firstVertexIsPenumbra : !firstVertexIsPenumbra;
- int distanceCounter = 0;
- if (needsSearch) {
- int foundIndex = -1;
- for (int i = (angleLength - 1); i >= 0; i--) {
- bool currentIsOuter = allVerticesAngleData[i].mIsPenumbra;
- // If we need distance to inner, then we need to find a inner vertex.
- if (currentIsOuter != firstVertexIsPenumbra) {
- foundIndex = i;
- break;
- }
- }
- LOG_ALWAYS_FATAL_IF(foundIndex == -1, "Wrong index found, means either"
- " umbra or penumbra's length is 0");
- distanceCounter = angleLength - foundIndex;
- }
+// Find the right polygon edge to shoot the ray at.
+inline int findPolyIndex(bool isPositiveCross, int startPolyIndex, const Vector2& umbraDir,
+ const Vector2* polyToCentroid, int polyLength) {
+ // Make sure we loop with a bound.
+ for (int i = 0; i < polyLength; i++) {
+ int currentIndex = (i + startPolyIndex) % polyLength;
+ const Vector2& currentToCentroid = polyToCentroid[currentIndex];
+ const Vector2& nextToCentroid = polyToCentroid[(currentIndex + 1) % polyLength];
+
+ float currentCrossUmbra = currentToCentroid.cross(umbraDir);
+ float umbraCrossNext = umbraDir.cross(nextToCentroid);
+ if (sameDirections(isPositiveCross, currentCrossUmbra, umbraCrossNext)) {
#if DEBUG_SHADOW
- ALOGD("distances[0] is %d", distanceCounter);
+ ALOGD("findPolyIndex loop %d times , index %d", i, currentIndex );
#endif
-
- distances[0] = distanceCounter; // means never see a target poly
-
- for (int i = 1; i < angleLength; i++) {
- bool firstVertexIsPenumbra = allVerticesAngleData[i].mIsPenumbra;
- // When we needs for distance for each outer vertex to inner, then we
- // increase the distance when seeing outer vertices. Otherwise, we clear
- // to 0.
- bool needsIncrement = needsOffsetToUmbra ? firstVertexIsPenumbra : !firstVertexIsPenumbra;
- // If counter is not -1, that means we have seen an other polygon's vertex.
- if (needsIncrement && distanceCounter != -1) {
- distanceCounter++;
- } else {
- distanceCounter = 0;
+ return currentIndex;
}
- distances[i] = distanceCounter;
}
+ LOG_ALWAYS_FATAL("Can't find the right polygon's edge from startPolyIndex %d", startPolyIndex);
+ return -1;
}
-/**
- * Given umbra and penumbra angle data list, merge them by sorting the angle
- * from the biggest to smallest.
- *
- * @param allVerticesAngleData The result array of merged angle data.
- */
-void SpotShadow::mergeAngleList(int maxUmbraAngleIndex, int maxPenumbraAngleIndex,
- const VertexAngleData* umbraAngleList, int umbraLength,
- const VertexAngleData* penumbraAngleList, int penumbraLength,
- VertexAngleData* allVerticesAngleData) {
-
- int totalRayNumber = umbraLength + penumbraLength;
- int umbraIndex = maxUmbraAngleIndex;
- int penumbraIndex = maxPenumbraAngleIndex;
-
- float currentUmbraAngle = umbraAngleList[umbraIndex].mAngle;
- float currentPenumbraAngle = penumbraAngleList[penumbraIndex].mAngle;
-
- // TODO: Clean this up using a while loop with 2 iterators.
- for (int i = 0; i < totalRayNumber; i++) {
- if (currentUmbraAngle > currentPenumbraAngle) {
- allVerticesAngleData[i] = umbraAngleList[umbraIndex];
- umbraIndex = (umbraIndex + 1) % umbraLength;
-
- // If umbraIndex round back, that means we are running out of
- // umbra vertices to merge, so just copy all the penumbra leftover.
- // Otherwise, we update the currentUmbraAngle.
- if (umbraIndex != maxUmbraAngleIndex) {
- currentUmbraAngle = umbraAngleList[umbraIndex].mAngle;
- } else {
- for (int j = i + 1; j < totalRayNumber; j++) {
- allVerticesAngleData[j] = penumbraAngleList[penumbraIndex];
- penumbraIndex = (penumbraIndex + 1) % penumbraLength;
- }
+// Generate the index pair for penumbra / umbra vertices, and more penumbra vertices
+// if needed.
+inline void genNewPenumbraAndPairWithUmbra(const Vector2* penumbra, int penumbraLength,
+ const Vector2* umbra, int umbraLength, Vector2* newPenumbra, int& newPenumbraIndex,
+ IndexPair* verticesPair, int& verticesPairIndex) {
+ // In order to keep everything in just one loop, we need to pre-compute the
+ // closest umbra vertex for the last penumbra vertex.
+ int previousClosestUmbraIndex = getClosestUmbraIndex(penumbra[penumbraLength - 1],
+ umbra, umbraLength);
+ for (int i = 0; i < penumbraLength; i++) {
+ const Vector2& currentPenumbraVertex = penumbra[i];
+ // For current penumbra vertex, starting from previousClosestUmbraIndex,
+ // then check the next one until the distance increase.
+ // The last one before the increase is the umbra vertex we need to pair with.
+ int currentUmbraIndex = previousClosestUmbraIndex;
+ float currentLengthSquared = (currentPenumbraVertex - umbra[currentUmbraIndex]).lengthSquared();
+ int currentClosestUmbraIndex = -1;
+ int indexDelta = 0;
+ for (int j = 1; j < umbraLength; j++) {
+ int newUmbraIndex = (previousClosestUmbraIndex + j) % umbraLength;
+ float newLengthSquared = (currentPenumbraVertex - umbra[newUmbraIndex]).lengthSquared();
+ if (newLengthSquared > currentLengthSquared) {
+ currentClosestUmbraIndex = (previousClosestUmbraIndex + j - 1) % umbraLength;
break;
- }
- } else {
- allVerticesAngleData[i] = penumbraAngleList[penumbraIndex];
- penumbraIndex = (penumbraIndex + 1) % penumbraLength;
- // If penumbraIndex round back, that means we are running out of
- // penumbra vertices to merge, so just copy all the umbra leftover.
- // Otherwise, we update the currentPenumbraAngle.
- if (penumbraIndex != maxPenumbraAngleIndex) {
- currentPenumbraAngle = penumbraAngleList[penumbraIndex].mAngle;
} else {
- for (int j = i + 1; j < totalRayNumber; j++) {
- allVerticesAngleData[j] = umbraAngleList[umbraIndex];
- umbraIndex = (umbraIndex + 1) % umbraLength;
- }
- break;
+ currentLengthSquared = newLengthSquared;
+ indexDelta++;
}
}
- }
-}
-
-#if DEBUG_SHADOW
-/**
- * DEBUG ONLY: Verify all the offset compuation is correctly done by examining
- * each vertex and its neighbor.
- */
-static void verifyDistanceCounter(const VertexAngleData* allVerticesAngleData,
- const int* distances, int angleLength, const char* name) {
- int currentDistance = distances[0];
- for (int i = 1; i < angleLength; i++) {
- if (distances[i] != INT_MIN) {
- if (!((currentDistance + 1) == distances[i]
- || distances[i] == 0)) {
- ALOGE("Wrong distance found at i %d name %s", i, name);
- }
- currentDistance = distances[i];
- if (currentDistance != 0) {
- bool currentOuter = allVerticesAngleData[i].mIsPenumbra;
- for (int j = 1; j <= (currentDistance - 1); j++) {
- bool neigborOuter =
- allVerticesAngleData[(i + angleLength - j) % angleLength].mIsPenumbra;
- if (neigborOuter != currentOuter) {
- ALOGE("Wrong distance found at i %d name %s", i, name);
- }
- }
- bool oppositeOuter =
- allVerticesAngleData[(i + angleLength - currentDistance) % angleLength].mIsPenumbra;
- if (oppositeOuter == currentOuter) {
- ALOGE("Wrong distance found at i %d name %s", i, name);
- }
+ LOG_ALWAYS_FATAL_IF(currentClosestUmbraIndex == -1, "Can't find a closet umbra vertext at all");
+
+ if (indexDelta > 1) {
+ // For those umbra don't have penumbra, generate new penumbra vertices by interpolation.
+ //
+ // Assuming Pi for penumbra vertices, and Ui for umbra vertices.
+ // In the case like below P1 paired with U1 and P2 paired with U5.
+ // U2 to U4 are unpaired umbra vertices.
+ //
+ // P1 P2
+ // | |
+ // U1 U2 U3 U4 U5
+ //
+ // We will need to generate 3 more penumbra vertices P1.1, P1.2, P1.3
+ // to pair with U2 to U4.
+ //
+ // P1 P1.1 P1.2 P1.3 P2
+ // | | | | |
+ // U1 U2 U3 U4 U5
+ //
+ // That distance ratio b/t Ui to U1 and Ui to U5 decides its paired penumbra
+ // vertex's location.
+ int newPenumbraNumber = indexDelta - 1;
+
+ float accumulatedDeltaLength[newPenumbraNumber];
+ float totalDeltaLength = 0;
+
+ // To save time, cache the previous umbra vertex info outside the loop
+ // and update each loop.
+ Vector2 previousClosestUmbra = umbra[previousClosestUmbraIndex];
+ Vector2 skippedUmbra;
+ // Use umbra data to precompute the length b/t unpaired umbra vertices,
+ // and its ratio against the total length.
+ for (int k = 0; k < indexDelta; k++) {
+ int skippedUmbraIndex = (previousClosestUmbraIndex + k + 1) % umbraLength;
+ skippedUmbra = umbra[skippedUmbraIndex];
+ float currentDeltaLength = (skippedUmbra - previousClosestUmbra).length();
+
+ totalDeltaLength += currentDeltaLength;
+ accumulatedDeltaLength[k] = totalDeltaLength;
+
+ previousClosestUmbra = skippedUmbra;
}
- }
- }
-}
-/**
- * DEBUG ONLY: Verify all the angle data compuated are is correctly done
- */
-static void verifyAngleData(int totalRayNumber, const VertexAngleData* allVerticesAngleData,
- const int* distancesToInner, const int* distancesToOuter,
- const VertexAngleData* umbraAngleList, int maxUmbraAngleIndex, int umbraLength,
- const VertexAngleData* penumbraAngleList, int maxPenumbraAngleIndex,
- int penumbraLength) {
- for (int i = 0; i < totalRayNumber; i++) {
- ALOGD("currentAngleList i %d, angle %f, isInner %d, index %d distancesToInner"
- " %d distancesToOuter %d", i, allVerticesAngleData[i].mAngle,
- !allVerticesAngleData[i].mIsPenumbra,
- allVerticesAngleData[i].mVertexIndex, distancesToInner[i], distancesToOuter[i]);
- }
+ const Vector2& previousPenumbra = penumbra[(i + penumbraLength - 1) % penumbraLength];
+ // Then for each unpaired umbra vertex, create a new penumbra by the ratio,
+ // and pair them togehter.
+ for (int k = 0; k < newPenumbraNumber; k++) {
+ float weightForCurrentPenumbra = 1.0f;
+ if (totalDeltaLength != 0.0f) {
+ weightForCurrentPenumbra = accumulatedDeltaLength[k] / totalDeltaLength;
+ }
+ float weightForPreviousPenumbra = 1.0f - weightForCurrentPenumbra;
- verifyDistanceCounter(allVerticesAngleData, distancesToInner, totalRayNumber, "distancesToInner");
- verifyDistanceCounter(allVerticesAngleData, distancesToOuter, totalRayNumber, "distancesToOuter");
+ Vector2 interpolatedPenumbra = currentPenumbraVertex * weightForCurrentPenumbra +
+ previousPenumbra * weightForPreviousPenumbra;
- for (int i = 0; i < totalRayNumber; i++) {
- if ((distancesToInner[i] * distancesToOuter[i]) != 0) {
- ALOGE("distancesToInner wrong at index %d distancesToInner[i] %d,"
- " distancesToOuter[i] %d", i, distancesToInner[i], distancesToOuter[i]);
- }
- }
- int currentUmbraVertexIndex =
- umbraAngleList[maxUmbraAngleIndex].mVertexIndex;
- int currentPenumbraVertexIndex =
- penumbraAngleList[maxPenumbraAngleIndex].mVertexIndex;
- for (int i = 0; i < totalRayNumber; i++) {
- if (allVerticesAngleData[i].mIsPenumbra == true) {
- if (allVerticesAngleData[i].mVertexIndex != currentPenumbraVertexIndex) {
- ALOGW("wrong penumbra indexing i %d allVerticesAngleData[i].mVertexIndex %d "
- "currentpenumbraVertexIndex %d", i,
- allVerticesAngleData[i].mVertexIndex, currentPenumbraVertexIndex);
- }
- currentPenumbraVertexIndex = (currentPenumbraVertexIndex + 1) % penumbraLength;
- } else {
- if (allVerticesAngleData[i].mVertexIndex != currentUmbraVertexIndex) {
- ALOGW("wrong umbra indexing i %d allVerticesAngleData[i].mVertexIndex %d "
- "currentUmbraVertexIndex %d", i,
- allVerticesAngleData[i].mVertexIndex, currentUmbraVertexIndex);
+ int skippedUmbraIndex = (previousClosestUmbraIndex + k + 1) % umbraLength;
+ verticesPair[verticesPairIndex++] = {newPenumbraIndex, skippedUmbraIndex};
+ newPenumbra[newPenumbraIndex++] = interpolatedPenumbra;
}
- currentUmbraVertexIndex = (currentUmbraVertexIndex + 1) % umbraLength;
- }
- }
- for (int i = 0; i < totalRayNumber - 1; i++) {
- float currentAngle = allVerticesAngleData[i].mAngle;
- float nextAngle = allVerticesAngleData[(i + 1) % totalRayNumber].mAngle;
- if (currentAngle < nextAngle) {
- ALOGE("Unexpected angle values!, currentAngle nextAngle %f %f", currentAngle, nextAngle);
}
+ verticesPair[verticesPairIndex++] = {newPenumbraIndex, currentClosestUmbraIndex};
+ newPenumbra[newPenumbraIndex++] = currentPenumbraVertex;
+
+ previousClosestUmbraIndex = currentClosestUmbraIndex;
}
}
-#endif
-/**
- * In order to compute the occluded umbra, we need to setup the angle data list
- * for the polygon data. Since we only store one poly vertex per polygon vertex,
- * this array only needs to be a float array which are the angles for each vertex.
- *
- * @param polyAngleList The result list
- *
- * @return int The index for the maximum angle in this array.
- */
-int SpotShadow::setupPolyAngleList(float* polyAngleList, int polyAngleLength,
- const Vector2* poly2d, const Vector2& centroid) {
- int maxPolyAngleIndex = -1;
- float maxPolyAngle = -FLT_MAX;
- for (int i = 0; i < polyAngleLength; i++) {
- polyAngleList[i] = angle(poly2d[i], centroid);
- if (polyAngleList[i] > maxPolyAngle) {
- maxPolyAngle = polyAngleList[i];
- maxPolyAngleIndex = i;
+// Precompute all the polygon's vector, return true if the reference cross product is positive.
+inline bool genPolyToCentroid(const Vector2* poly2d, int polyLength,
+ const Vector2& centroid, Vector2* polyToCentroid) {
+ for (int j = 0; j < polyLength; j++) {
+ polyToCentroid[j] = poly2d[j] - centroid;
+ }
+ float refCrossProduct = 0;
+ for (int j = 0; j < polyLength; j++) {
+ refCrossProduct = polyToCentroid[j].cross(polyToCentroid[(j + 1) % polyLength]);
+ if (refCrossProduct != 0) {
+ break;
}
}
- return maxPolyAngleIndex;
-}
-/**
- * For umbra and penumbra, given the offset info and the current ray number,
- * find the right edge index (the (starting vertex) for the ray to shoot at.
- *
- * @return int The index of the starting vertex of the edge.
- */
-inline int SpotShadow::getEdgeStartIndex(const int* offsets, int rayIndex, int totalRayNumber,
- const VertexAngleData* allVerticesAngleData) {
- int tempOffset = offsets[rayIndex];
- int targetRayIndex = (rayIndex - tempOffset + totalRayNumber) % totalRayNumber;
- return allVerticesAngleData[targetRayIndex].mVertexIndex;
+ return refCrossProduct > 0;
}
-/**
- * For the occluded umbra, given the array of angles, find the index of the
- * starting vertex of the edge, for the ray to shoo at.
- *
- * TODO: Save the last result to shorten the search distance.
- *
- * @return int The index of the starting vertex of the edge.
- */
-inline int SpotShadow::getPolyEdgeStartIndex(int maxPolyAngleIndex, int polyLength,
- const float* polyAngleList, float rayAngle) {
- int minPolyAngleIndex = (maxPolyAngleIndex + polyLength - 1) % polyLength;
- int resultIndex = -1;
- if (rayAngle > polyAngleList[maxPolyAngleIndex]
- || rayAngle <= polyAngleList[minPolyAngleIndex]) {
- resultIndex = minPolyAngleIndex;
+// For one umbra vertex, shoot an ray from centroid to it.
+// If the ray hit the polygon first, then return the intersection point as the
+// closer vertex.
+inline Vector2 getCloserVertex(const Vector2& umbraVertex, const Vector2& centroid,
+ const Vector2* poly2d, int polyLength, const Vector2* polyToCentroid,
+ bool isPositiveCross, int& previousPolyIndex) {
+ Vector2 umbraToCentroid = umbraVertex - centroid;
+ float distanceToUmbra = umbraToCentroid.length();
+ umbraToCentroid = umbraToCentroid / distanceToUmbra;
+
+ // previousPolyIndex is updated for each item such that we can minimize the
+ // looping inside findPolyIndex();
+ previousPolyIndex = findPolyIndex(isPositiveCross, previousPolyIndex,
+ umbraToCentroid, polyToCentroid, polyLength);
+
+ float dx = umbraToCentroid.x;
+ float dy = umbraToCentroid.y;
+ float distanceToIntersectPoly = rayIntersectPoints(centroid, dx, dy,
+ poly2d[previousPolyIndex], poly2d[(previousPolyIndex + 1) % polyLength]);
+ if (distanceToIntersectPoly < 0) {
+ distanceToIntersectPoly = 0;
+ }
+
+ // Pick the closer one as the occluded area vertex.
+ Vector2 closerVertex;
+ if (distanceToIntersectPoly < distanceToUmbra) {
+ closerVertex.x = centroid.x + dx * distanceToIntersectPoly;
+ closerVertex.y = centroid.y + dy * distanceToIntersectPoly;
} else {
- for (int i = 0; i < polyLength - 1; i++) {
- int currentIndex = (maxPolyAngleIndex + i) % polyLength;
- int nextIndex = (maxPolyAngleIndex + i + 1) % polyLength;
- if (rayAngle <= polyAngleList[currentIndex]
- && rayAngle > polyAngleList[nextIndex]) {
- resultIndex = currentIndex;
- }
- }
- }
- if (CC_UNLIKELY(resultIndex == -1)) {
- // TODO: Add more error handling here.
- ALOGE("Wrong index found, means no edge can't be found for rayAngle %f", rayAngle);
- }
- return resultIndex;
-}
-
-/**
- * Convert the incoming polygons into arrays of vertices, for each ray.
- * Ray only shoots when there is one vertex either on penumbra on umbra.
- *
- * Finally, it will generate vertices per ray for umbra, penumbra and optionally
- * occludedUmbra.
- *
- * Return true (success) when all vertices are generated
- */
-int SpotShadow::convertPolysToVerticesPerRay(
- bool hasOccludedUmbraArea, const Vector2* poly2d, int polyLength,
- const Vector2* umbra, int umbraLength, const Vector2* penumbra,
- int penumbraLength, const Vector2& centroid,
- Vector2* umbraVerticesPerRay, Vector2* penumbraVerticesPerRay,
- Vector2* occludedUmbraVerticesPerRay) {
- int totalRayNumber = umbraLength + penumbraLength;
-
- // For incoming umbra / penumbra polygons, we will build an intermediate data
- // structure to help us sort all the vertices according to the vertices.
- // Using this data structure, we can tell where (the angle) to shoot the ray,
- // whether we shoot at penumbra edge or umbra edge, and which edge to shoot at.
- //
- // We first parse each vertices and generate a table of VertexAngleData.
- // Based on that, we create 2 arrays telling us which edge to shoot at.
- VertexAngleData allVerticesAngleData[totalRayNumber];
- VertexAngleData umbraAngleList[umbraLength];
- VertexAngleData penumbraAngleList[penumbraLength];
-
- int polyAngleLength = hasOccludedUmbraArea ? polyLength : 0;
- float polyAngleList[polyAngleLength];
-
- const int maxUmbraAngleIndex =
- setupAngleList(umbraAngleList, umbraLength, umbra, centroid, false, "umbra");
- const int maxPenumbraAngleIndex =
- setupAngleList(penumbraAngleList, penumbraLength, penumbra, centroid, true, "penumbra");
- const int maxPolyAngleIndex = setupPolyAngleList(polyAngleList, polyAngleLength, poly2d, centroid);
-
- // Check all the polygons here are CW.
- bool isPolyCW = checkPolyClockwise(polyAngleLength, maxPolyAngleIndex, polyAngleList);
- bool isUmbraCW = checkClockwise(maxUmbraAngleIndex, umbraLength,
- umbraAngleList, "umbra");
- bool isPenumbraCW = checkClockwise(maxPenumbraAngleIndex, penumbraLength,
- penumbraAngleList, "penumbra");
-
- if (!isUmbraCW || !isPenumbraCW || !isPolyCW) {
-#if DEBUG_SHADOW
- ALOGE("One polygon is not CW isUmbraCW %d isPenumbraCW %d isPolyCW %d",
- isUmbraCW, isPenumbraCW, isPolyCW);
-#endif
- return false;
- }
-
- mergeAngleList(maxUmbraAngleIndex, maxPenumbraAngleIndex,
- umbraAngleList, umbraLength, penumbraAngleList, penumbraLength,
- allVerticesAngleData);
-
- // Calculate the offset to the left most Inner vertex for each outerVertex.
- // Then the offset to the left most Outer vertex for each innerVertex.
- int offsetToInner[totalRayNumber];
- int offsetToOuter[totalRayNumber];
- calculateDistanceCounter(true, totalRayNumber, allVerticesAngleData, offsetToInner);
- calculateDistanceCounter(false, totalRayNumber, allVerticesAngleData, offsetToOuter);
-
- // Generate both umbraVerticesPerRay and penumbraVerticesPerRay
- for (int i = 0; i < totalRayNumber; i++) {
- float rayAngle = allVerticesAngleData[i].mAngle;
- bool isUmbraVertex = !allVerticesAngleData[i].mIsPenumbra;
-
- float dx = cosf(rayAngle);
- float dy = sinf(rayAngle);
- float distanceToIntersectUmbra = -1;
-
- if (isUmbraVertex) {
- // We can just copy umbra easily, and calculate the distance for the
- // occluded umbra computation.
- int startUmbraIndex = allVerticesAngleData[i].mVertexIndex;
- umbraVerticesPerRay[i] = umbra[startUmbraIndex];
- if (hasOccludedUmbraArea) {
- distanceToIntersectUmbra = (umbraVerticesPerRay[i] - centroid).length();
- }
-
- //shoot ray to penumbra only
- int startPenumbraIndex = getEdgeStartIndex(offsetToOuter, i, totalRayNumber,
- allVerticesAngleData);
- float distanceToIntersectPenumbra = rayIntersectPoints(centroid, dx, dy,
- penumbra[startPenumbraIndex],
- penumbra[(startPenumbraIndex + 1) % penumbraLength]);
- if (distanceToIntersectPenumbra < 0) {
-#if DEBUG_SHADOW
- ALOGW("convertPolyToRayDist for penumbra failed rayAngle %f dx %f dy %f",
- rayAngle, dx, dy);
-#endif
- distanceToIntersectPenumbra = 0;
- }
- penumbraVerticesPerRay[i].x = centroid.x + dx * distanceToIntersectPenumbra;
- penumbraVerticesPerRay[i].y = centroid.y + dy * distanceToIntersectPenumbra;
- } else {
- // We can just copy the penumbra
- int startPenumbraIndex = allVerticesAngleData[i].mVertexIndex;
- penumbraVerticesPerRay[i] = penumbra[startPenumbraIndex];
-
- // And shoot ray to umbra only
- int startUmbraIndex = getEdgeStartIndex(offsetToInner, i, totalRayNumber,
- allVerticesAngleData);
-
- distanceToIntersectUmbra = rayIntersectPoints(centroid, dx, dy,
- umbra[startUmbraIndex], umbra[(startUmbraIndex + 1) % umbraLength]);
- if (distanceToIntersectUmbra < 0) {
-#if DEBUG_SHADOW
- ALOGW("convertPolyToRayDist for umbra failed rayAngle %f dx %f dy %f",
- rayAngle, dx, dy);
-#endif
- distanceToIntersectUmbra = 0;
- }
- umbraVerticesPerRay[i].x = centroid.x + dx * distanceToIntersectUmbra;
- umbraVerticesPerRay[i].y = centroid.y + dy * distanceToIntersectUmbra;
- }
-
- if (hasOccludedUmbraArea) {
- // Shoot the same ray to the poly2d, and get the distance.
- int startPolyIndex = getPolyEdgeStartIndex(maxPolyAngleIndex, polyLength,
- polyAngleList, rayAngle);
-
- float distanceToIntersectPoly = rayIntersectPoints(centroid, dx, dy,
- poly2d[startPolyIndex], poly2d[(startPolyIndex + 1) % polyLength]);
- if (distanceToIntersectPoly < 0) {
- distanceToIntersectPoly = 0;
- }
- distanceToIntersectPoly = MathUtils::min(distanceToIntersectUmbra, distanceToIntersectPoly);
- occludedUmbraVerticesPerRay[i].x = centroid.x + dx * distanceToIntersectPoly;
- occludedUmbraVerticesPerRay[i].y = centroid.y + dy * distanceToIntersectPoly;
- }
+ closerVertex = umbraVertex;
}
-#if DEBUG_SHADOW
- verifyAngleData(totalRayNumber, allVerticesAngleData, offsetToInner,
- offsetToOuter, umbraAngleList, maxUmbraAngleIndex, umbraLength,
- penumbraAngleList, maxPenumbraAngleIndex, penumbraLength);
-#endif
- return true; // success
-
+ return closerVertex;
}
/**
@@ -1382,7 +864,6 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength
Vector2* penumbra, int penumbraLength, Vector2* umbra, int umbraLength,
const Vector3* poly, int polyLength, VertexBuffer& shadowTriangleStrip,
const Vector2& centroid) {
-
bool hasOccludedUmbraArea = false;
Vector2 poly2d[polyLength];
@@ -1398,128 +879,140 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength
}
}
- int totalRayNum = umbraLength + penumbraLength;
- Vector2 umbraVertices[totalRayNum];
- Vector2 penumbraVertices[totalRayNum];
- Vector2 occludedUmbraVertices[totalRayNum];
- bool convertSuccess = convertPolysToVerticesPerRay(hasOccludedUmbraArea, poly2d,
- polyLength, umbra, umbraLength, penumbra, penumbraLength,
- centroid, umbraVertices, penumbraVertices, occludedUmbraVertices);
- if (!convertSuccess) {
- return;
+ // For each penumbra vertex, find its corresponding closest umbra vertex index.
+ //
+ // Penumbra Vertices marked as Pi
+ // Umbra Vertices marked as Ui
+ // (P3)
+ // (P2) | ' (P4)
+ // (P1)' | | '
+ // ' | | '
+ // (P0) ------------------------------------------------(P5)
+ // | (U0) |(U1)
+ // | |
+ // | |(U2) (P5.1)
+ // | |
+ // | |
+ // | |
+ // | |
+ // | |
+ // | |
+ // (U4)-----------------------------------(U3) (P6)
+ //
+ // At least, like P0, P1, P2, they will find the matching umbra as U0.
+ // If we jump over some umbra vertex without matching penumbra vertex, then
+ // we will generate some new penumbra vertex by interpolation. Like P6 is
+ // matching U3, but U2 is not matched with any penumbra vertex.
+ // So interpolate P5.1 out and match U2.
+ // In this way, every umbra vertex will have a matching penumbra vertex.
+ //
+ // The total pair number can be as high as umbraLength + penumbraLength.
+ const int maxNewPenumbraLength = umbraLength + penumbraLength;
+ IndexPair verticesPair[maxNewPenumbraLength];
+ int verticesPairIndex = 0;
+
+ // Cache all the existing penumbra vertices and newly interpolated vertices into a
+ // a new array.
+ Vector2 newPenumbra[maxNewPenumbraLength];
+ int newPenumbraIndex = 0;
+
+ // For each penumbra vertex, find its closet umbra vertex by comparing the
+ // neighbor umbra vertices.
+ genNewPenumbraAndPairWithUmbra(penumbra, penumbraLength, umbra, umbraLength, newPenumbra,
+ newPenumbraIndex, verticesPair, verticesPairIndex);
+ ShadowTessellator::checkOverflow(verticesPairIndex, maxNewPenumbraLength, "Spot pair");
+ ShadowTessellator::checkOverflow(newPenumbraIndex, maxNewPenumbraLength, "Spot new penumbra");
+#if DEBUG_SHADOW
+ for (int i = 0; i < umbraLength; i++) {
+ ALOGD("umbra i %d, [%f, %f]", i, umbra[i].x, umbra[i].y);
+ }
+ for (int i = 0; i < newPenumbraIndex; i++) {
+ ALOGD("new penumbra i %d, [%f, %f]", i, newPenumbra[i].x, newPenumbra[i].y);
}
+ for (int i = 0; i < verticesPairIndex; i++) {
+ ALOGD("index i %d, [%d, %d]", i, verticesPair[i].outerIndex, verticesPair[i].innerIndex);
+ }
+#endif
- // Minimal value is 1, for each vertex show up once.
- // The bigger this value is , the smoother the look is, but more memory
- // is consumed.
- // When the ray number is high, that means the polygon has been fine
- // tessellated, we don't need this extra slice, just keep it as 1.
- int sliceNumberPerEdge = (totalRayNum > FINE_TESSELLATED_POLYGON_RAY_NUMBER) ? 1 : 2;
-
- // For each polygon, we at most add (totalRayNum * sliceNumberPerEdge) vertices.
- int slicedVertexCountPerPolygon = totalRayNum * sliceNumberPerEdge;
- int totalVertexCount = slicedVertexCountPerPolygon * 2 + totalRayNum;
- int totalIndexCount = 2 * (slicedVertexCountPerPolygon * 2 + 2);
+ // For the size of vertex buffer, we need 3 rings, one has newPenumbraSize,
+ // one has umbraLength, the last one has at most umbraLength.
+ //
+ // For the size of index buffer, the umbra area needs (2 * umbraLength + 2).
+ // The penumbra one can vary a bit, but it is bounded by (2 * verticesPairIndex + 2).
+ // And 2 more for jumping between penumbra to umbra.
+ const int newPenumbraLength = newPenumbraIndex;
+ const int totalVertexCount = newPenumbraLength + umbraLength * 2;
+ const int totalIndexCount = 2 * umbraLength + 2 * verticesPairIndex + 6;
AlphaVertex* shadowVertices =
shadowTriangleStrip.alloc<AlphaVertex>(totalVertexCount);
uint16_t* indexBuffer =
shadowTriangleStrip.allocIndices<uint16_t>(totalIndexCount);
-
- int indexBufferIndex = 0;
int vertexBufferIndex = 0;
+ int indexBufferIndex = 0;
- uint16_t slicedUmbraVertexIndex[totalRayNum * sliceNumberPerEdge];
- // Should be something like 0 0 0 1 1 1 2 3 3 3...
- int rayNumberPerSlicedUmbra[totalRayNum * sliceNumberPerEdge];
- int realUmbraVertexCount = 0;
- for (int i = 0; i < totalRayNum; i++) {
- Vector2 currentPenumbra = penumbraVertices[i];
- Vector2 currentUmbra = umbraVertices[i];
-
- Vector2 nextPenumbra = penumbraVertices[(i + 1) % totalRayNum];
- Vector2 nextUmbra = umbraVertices[(i + 1) % totalRayNum];
- // NextUmbra/Penumbra will be done in the next loop!!
- for (int weight = 0; weight < sliceNumberPerEdge; weight++) {
- const Vector2& slicedPenumbra = (currentPenumbra * (sliceNumberPerEdge - weight)
- + nextPenumbra * weight) / sliceNumberPerEdge;
-
- const Vector2& slicedUmbra = (currentUmbra * (sliceNumberPerEdge - weight)
- + nextUmbra * weight) / sliceNumberPerEdge;
-
- // In the vertex buffer, we fill the Penumbra first, then umbra.
- indexBuffer[indexBufferIndex++] = vertexBufferIndex;
- AlphaVertex::set(&shadowVertices[vertexBufferIndex++], slicedPenumbra.x,
- slicedPenumbra.y, 0.0f);
+ // Fill the IB and VB for the penumbra area.
+ for (int i = 0; i < newPenumbraLength; i++) {
+ AlphaVertex::set(&shadowVertices[vertexBufferIndex++], newPenumbra[i].x,
+ newPenumbra[i].y, 0.0f);
+ }
+ for (int i = 0; i < umbraLength; i++) {
+ AlphaVertex::set(&shadowVertices[vertexBufferIndex++], umbra[i].x, umbra[i].y,
+ M_PI);
+ }
- // When we add umbra vertex, we need to remember its current ray number.
- // And its own vertexBufferIndex. This is for occluded umbra usage.
- indexBuffer[indexBufferIndex++] = vertexBufferIndex;
- rayNumberPerSlicedUmbra[realUmbraVertexCount] = i;
- slicedUmbraVertexIndex[realUmbraVertexCount] = vertexBufferIndex;
- realUmbraVertexCount++;
- AlphaVertex::set(&shadowVertices[vertexBufferIndex++], slicedUmbra.x,
- slicedUmbra.y, M_PI);
- }
+ for (int i = 0; i < verticesPairIndex; i++) {
+ indexBuffer[indexBufferIndex++] = verticesPair[i].outerIndex;
+ // All umbra index need to be offseted by newPenumbraSize.
+ indexBuffer[indexBufferIndex++] = verticesPair[i].innerIndex + newPenumbraLength;
}
+ indexBuffer[indexBufferIndex++] = verticesPair[0].outerIndex;
+ indexBuffer[indexBufferIndex++] = verticesPair[0].innerIndex + newPenumbraLength;
- indexBuffer[indexBufferIndex++] = 0;
- //RealUmbraVertexIndex[0] must be 1, so we connect back well at the
- //beginning of occluded area.
- indexBuffer[indexBufferIndex++] = 1;
+ // Now fill the IB and VB for the umbra area.
+ // First duplicated the index from previous strip and the first one for the
+ // degenerated triangles.
+ indexBuffer[indexBufferIndex] = indexBuffer[indexBufferIndex - 1];
+ indexBufferIndex++;
+ indexBuffer[indexBufferIndex++] = newPenumbraLength + 0;
+ // Save the first VB index for umbra area in order to close the loop.
+ int savedStartIndex = vertexBufferIndex;
- float occludedUmbraAlpha = M_PI;
if (hasOccludedUmbraArea) {
- // Now the occludedUmbra area;
- int currentRayNumber = -1;
- int firstOccludedUmbraIndex = -1;
- for (int i = 0; i < realUmbraVertexCount; i++) {
- indexBuffer[indexBufferIndex++] = slicedUmbraVertexIndex[i];
-
- // If the occludedUmbra vertex has not been added yet, then add it.
- // Otherwise, just use the previously added occludedUmbra vertices.
- if (rayNumberPerSlicedUmbra[i] != currentRayNumber) {
- currentRayNumber++;
- indexBuffer[indexBufferIndex++] = vertexBufferIndex;
- // We need to remember the begining of the occludedUmbra vertices
- // to close this loop.
- if (currentRayNumber == 0) {
- firstOccludedUmbraIndex = vertexBufferIndex;
- }
- AlphaVertex::set(&shadowVertices[vertexBufferIndex++],
- occludedUmbraVertices[currentRayNumber].x,
- occludedUmbraVertices[currentRayNumber].y,
- occludedUmbraAlpha);
- } else {
- indexBuffer[indexBufferIndex++] = (vertexBufferIndex - 1);
- }
+ // Precompute all the polygon's vector, and the reference cross product,
+ // in order to find the right polygon edge for the ray to intersect.
+ Vector2 polyToCentroid[polyLength];
+ bool isPositiveCross = genPolyToCentroid(poly2d, polyLength, centroid, polyToCentroid);
+
+ // Because both the umbra and polygon are going in the same direction,
+ // we can save the previous polygon index to make sure we have less polygon
+ // vertex to compute for each ray.
+ int previousPolyIndex = 0;
+ for (int i = 0; i < umbraLength; i++) {
+ // Shoot a ray from centroid to each umbra vertices and pick the one with
+ // shorter distance to the centroid, b/t the umbra vertex or the intersection point.
+ Vector2 closerVertex = getCloserVertex(umbra[i], centroid, poly2d, polyLength,
+ polyToCentroid, isPositiveCross, previousPolyIndex);
+
+ // We already stored the umbra vertices, just need to add the occlued umbra's ones.
+ indexBuffer[indexBufferIndex++] = newPenumbraLength + i;
+ indexBuffer[indexBufferIndex++] = vertexBufferIndex;
+ AlphaVertex::set(&shadowVertices[vertexBufferIndex++],
+ closerVertex.x, closerVertex.y, M_PI);
}
- // Close the loop here!
- indexBuffer[indexBufferIndex++] = slicedUmbraVertexIndex[0];
- indexBuffer[indexBufferIndex++] = firstOccludedUmbraIndex;
} else {
+ // If there is no occluded umbra at all, then draw the triangle fan
+ // starting from the centroid to all umbra vertices.
int lastCentroidIndex = vertexBufferIndex;
AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid.x,
- centroid.y, occludedUmbraAlpha);
- for (int i = 0; i < realUmbraVertexCount; i++) {
- indexBuffer[indexBufferIndex++] = slicedUmbraVertexIndex[i];
+ centroid.y, M_PI);
+ for (int i = 0; i < umbraLength; i++) {
+ indexBuffer[indexBufferIndex++] = newPenumbraLength + i;
indexBuffer[indexBufferIndex++] = lastCentroidIndex;
}
- // Close the loop here!
- indexBuffer[indexBufferIndex++] = slicedUmbraVertexIndex[0];
- indexBuffer[indexBufferIndex++] = lastCentroidIndex;
- }
-
-#if DEBUG_SHADOW
- ALOGD("allocated IB %d allocated VB is %d", totalIndexCount, totalVertexCount);
- ALOGD("IB index %d VB index is %d", indexBufferIndex, vertexBufferIndex);
- for (int i = 0; i < vertexBufferIndex; i++) {
- ALOGD("vertexBuffer i %d, (%f, %f %f)", i, shadowVertices[i].x, shadowVertices[i].y,
- shadowVertices[i].alpha);
- }
- for (int i = 0; i < indexBufferIndex; i++) {
- ALOGD("indexBuffer i %d, indexBuffer[i] %d", i, indexBuffer[i]);
}
-#endif
+ // Closing the umbra area triangle's loop here.
+ indexBuffer[indexBufferIndex++] = newPenumbraLength;
+ indexBuffer[indexBufferIndex++] = savedStartIndex;
// At the end, update the real index and vertex buffer size.
shadowTriangleStrip.updateVertexCount(vertexBufferIndex);
@@ -1585,8 +1078,8 @@ bool SpotShadow::testConvex(const Vector2* polygon, int polygonLength,
Vector2 middle = polygon[(i + 1) % polygonLength];
Vector2 end = polygon[(i + 2) % polygonLength];
- double delta = (double(middle.x) - start.x) * (double(end.y) - start.y) -
- (double(middle.y) - start.y) * (double(end.x) - start.x);
+ float delta = (float(middle.x) - start.x) * (float(end.y) - start.y) -
+ (float(middle.y) - start.y) * (float(end.x) - start.x);
bool isCCWOrCoLinear = (delta >= EPSILON);
if (isCCWOrCoLinear) {
@@ -1621,8 +1114,8 @@ void SpotShadow::testIntersection(const Vector2* poly1, int poly1Length,
bool dumpPoly = false;
for (int k = 0; k < TEST_POINT_NUMBER; k++) {
// Generate a random point between minX, minY and maxX, maxY.
- double randomX = rand() / double(RAND_MAX);
- double randomY = rand() / double(RAND_MAX);
+ float randomX = rand() / float(RAND_MAX);
+ float randomY = rand() / float(RAND_MAX);
Vector2 testPoint;
testPoint.x = lowerBound.x + randomX * (upperBound.x - lowerBound.x);
diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h
index 23fdca9..e2d94f7 100644
--- a/libs/hwui/SpotShadow.h
+++ b/libs/hwui/SpotShadow.h
@@ -35,42 +35,6 @@ private:
static float projectCasterToOutline(Vector2& outline,
const Vector3& lightCenter, const Vector3& polyVertex);
- static int calculateOccludedUmbra(const Vector2* umbra, int umbraLength,
- const Vector3* poly, int polyLength, Vector2* occludedUmbra);
-
- static int setupAngleList(VertexAngleData* angleDataList,
- int polyLength, const Vector2* polygon, const Vector2& centroid,
- bool isPenumbra, const char* name);
-
- static int convertPolysToVerticesPerRay(
- bool hasOccludedUmbraArea, const Vector2* poly2d, int polyLength,
- const Vector2* umbra, int umbraLength, const Vector2* penumbra,
- int penumbraLength, const Vector2& centroid,
- Vector2* umbraVerticesPerRay, Vector2* penumbraVerticesPerRay,
- Vector2* occludedUmbraVerticesPerRay);
-
- static bool checkClockwise(int maxIndex, int listLength,
- VertexAngleData* angleList, const char* name);
-
- static void calculateDistanceCounter(bool needsOffsetToUmbra, int angleLength,
- const VertexAngleData* allVerticesAngleData, int* distances);
-
- static void mergeAngleList(int maxUmbraAngleIndex, int maxPenumbraAngleIndex,
- const VertexAngleData* umbraAngleList, int umbraLength,
- const VertexAngleData* penumbraAngleList, int penumbraLength,
- VertexAngleData* allVerticesAngleData);
-
- static int setupPolyAngleList(float* polyAngleList, int polyAngleLength,
- const Vector2* poly2d, const Vector2& centroid);
-
- static bool checkPolyClockwise(int polyAngleLength, int maxPolyAngleIndex,
- const float* polyAngleList);
-
- static int getEdgeStartIndex(const int* offsets, int rayIndex, int totalRayNumber,
- const VertexAngleData* allVerticesAngleData);
-
- static int getPolyEdgeStartIndex(int maxPolyAngleIndex, int polyLength,
- const float* polyAngleList, float rayAngle);
static void computeLightPolygon(int points, const Vector3& lightCenter,
float size, Vector3* ret);
@@ -81,8 +45,7 @@ private:
static void xsort(Vector2* points, int pointsLength);
static int hull(Vector2* points, int pointsLength, Vector2* retPoly);
- static bool ccw(double ax, double ay, double bx, double by, double cx, double cy);
- static int intersection(const Vector2* poly1, int poly1length, Vector2* poly2, int poly2length);
+ static bool ccw(float ax, float ay, float bx, float by, float cx, float cy);
static void sort(Vector2* poly, int polyLength, const Vector2& center);
static void swap(Vector2* points, int i, int j);
@@ -92,8 +55,6 @@ private:
static bool testPointInsidePolygon(const Vector2 testPoint, const Vector2* poly, int len);
static void makeClockwise(Vector2* polygon, int len);
static void reverse(Vector2* polygon, int len);
- static inline bool lineIntersection(double x1, double y1, double x2, double y2,
- double x3, double y3, double x4, double y4, Vector2& ret);
static void generateTriangleStrip(bool isCasterOpaque, float shadowStrengthScale,
Vector2* penumbra, int penumbraLength, Vector2* umbra, int umbraLength,
diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h
index d033ed9..aa6acc9 100644
--- a/libs/hwui/Vector.h
+++ b/libs/hwui/Vector.h
@@ -99,6 +99,10 @@ struct Vector2 {
return x * v.x + y * v.y;
}
+ float cross(const Vector2& v) const {
+ return x * v.y - y * v.x;
+ }
+
void dump() {
ALOGD("Vector2[%.2f, %.2f]", x, y);
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b50a433..6c3637d 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -42,7 +42,8 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
: mRenderThread(thread)
, mEglManager(thread.eglManager())
, mEglSurface(EGL_NO_SURFACE)
- , mDirtyRegionsEnabled(false)
+ , mBufferPreserved(false)
+ , mSwapBehavior(kSwap_default)
, mOpaque(!translucent)
, mCanvas(NULL)
, mHaveNewSurface(false)
@@ -82,7 +83,8 @@ void CanvasContext::setSurface(ANativeWindow* window) {
}
if (mEglSurface != EGL_NO_SURFACE) {
- mDirtyRegionsEnabled = mEglManager.enableDirtyRegions(mEglSurface);
+ const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer);
+ mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer);
mHaveNewSurface = true;
makeCurrent();
} else {
@@ -103,6 +105,10 @@ void CanvasContext::requireSurface() {
makeCurrent();
}
+void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) {
+ mSwapBehavior = swapBehavior;
+}
+
bool CanvasContext::initialize(ANativeWindow* window) {
setSurface(window);
if (mCanvas) return false;
@@ -160,6 +166,11 @@ void CanvasContext::prepareTree(TreeInfo& info) {
freePrefetechedLayers();
}
+ if (CC_UNLIKELY(!mNativeWindow.get())) {
+ info.out.canDrawThisFrame = false;
+ return;
+ }
+
int runningBehind = 0;
// TODO: This query is moderately expensive, investigate adding some sort
// of fast-path based off when we last called eglSwapBuffers() as well as
@@ -200,7 +211,7 @@ void CanvasContext::draw() {
if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
mCanvas->setViewport(width, height);
dirty.setEmpty();
- } else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
+ } else if (!mBufferPreserved || mHaveNewSurface) {
dirty.setEmpty();
} else {
if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) {
@@ -230,6 +241,8 @@ void CanvasContext::draw() {
if (status & DrawGlInfo::kStatusDrew) {
swapBuffers();
+ } else {
+ mEglManager.cancelFrame();
}
profiler().finishFrame();
@@ -330,6 +343,7 @@ void CanvasContext::trimMemory(RenderThread& thread, int level) {
// No context means nothing to free
if (!thread.eglManager().hasEglContext()) return;
+ ATRACE_CALL();
thread.eglManager().requireGlContext();
if (level >= TRIM_MEMORY_COMPLETE) {
Caches::getInstance().flush(Caches::kFlushMode_Full);
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index d4282fa..435244e 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -48,6 +48,11 @@ namespace renderthread {
class EglManager;
+enum SwapBehavior {
+ kSwap_default,
+ kSwap_discardBuffer,
+};
+
// This per-renderer class manages the bridge between the global EGL context
// and the render surface.
// TODO: Rename to Renderer or some other per-window, top-level manager
@@ -57,9 +62,14 @@ public:
IContextFactory* contextFactory);
virtual ~CanvasContext();
+ // Won't take effect until next EGLSurface creation
+ void setSwapBehavior(SwapBehavior swapBehavior);
+
bool initialize(ANativeWindow* window);
void updateSurface(ANativeWindow* window);
void pauseSurface(ANativeWindow* window);
+ bool hasSurface() { return mNativeWindow.get(); }
+
void setup(int width, int height, const Vector3& lightCenter, float lightRadius,
uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
void setOpaque(bool opaque);
@@ -111,7 +121,8 @@ private:
EglManager& mEglManager;
sp<ANativeWindow> mNativeWindow;
EGLSurface mEglSurface;
- bool mDirtyRegionsEnabled;
+ bool mBufferPreserved;
+ SwapBehavior mSwapBehavior;
bool mOpaque;
OpenGLRenderer* mCanvas;
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index dd34e09..97b31a9 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -132,6 +132,12 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) {
mLayers.clear();
mContext->prepareTree(info);
+ // This is after the prepareTree so that any pending operations
+ // (RenderNode tree state, prefetched layers, etc...) will be flushed.
+ if (CC_UNLIKELY(!mContext->hasSurface())) {
+ mSyncResult |= kSync_LostSurfaceRewardIfFound;
+ }
+
if (info.out.hasAnimations) {
if (info.out.requiresUiRedraw) {
mSyncResult |= kSync_UIRedrawRequired;
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 243cc5d..28f6cb2 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -42,6 +42,7 @@ class RenderThread;
enum SyncResult {
kSync_OK = 0,
kSync_UIRedrawRequired = 1 << 0,
+ kSync_LostSurfaceRewardIfFound = 1 << 1,
};
/*
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index a87834e..9bd6f41 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -70,12 +70,13 @@ EglManager::EglManager(RenderThread& thread)
, mEglConfig(0)
, mEglContext(EGL_NO_CONTEXT)
, mPBufferSurface(EGL_NO_SURFACE)
- , mRequestDirtyRegions(load_dirty_regions_property())
+ , mAllowPreserveBuffer(load_dirty_regions_property())
, mCurrentSurface(EGL_NO_SURFACE)
, mAtlasMap(NULL)
- , mAtlasMapSize(0) {
- mCanSetDirtyRegions = mRequestDirtyRegions;
- ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false");
+ , mAtlasMapSize(0)
+ , mInFrame(false) {
+ mCanSetPreserveBuffer = mAllowPreserveBuffer;
+ ALOGD("Use EGL_SWAP_BEHAVIOR_PRESERVED: %s", mAllowPreserveBuffer ? "true" : "false");
}
void EglManager::initialize() {
@@ -105,15 +106,16 @@ bool EglManager::hasEglContext() {
void EglManager::requireGlContext() {
LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, "No EGL context");
- // We don't care *WHAT* surface is active, just that one is active to give
- // us access to the GL context
- if (mCurrentSurface == EGL_NO_SURFACE) {
+ if (!mInFrame) {
+ // We can't be certain about the state of the current surface (whether
+ // or not it is destroyed, for example), so err on the side of using
+ // the pbuffer surface which we fully control
usePBufferSurface();
}
}
void EglManager::loadConfig() {
- EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+ EGLint swapBehavior = mCanSetPreserveBuffer ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
EGLint attribs[] = {
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_RED_SIZE, 8,
@@ -131,10 +133,10 @@ void EglManager::loadConfig() {
if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
|| num_configs != 1) {
// Failed to get a valid config
- if (mCanSetDirtyRegions) {
+ if (mCanSetPreserveBuffer) {
ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
// Try again without dirty regions enabled
- mCanSetDirtyRegions = false;
+ mCanSetPreserveBuffer = false;
loadConfig();
} else {
LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
@@ -252,9 +254,11 @@ void EglManager::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) {
eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
}
eglBeginFrame(mEglDisplay, surface);
+ mInFrame = true;
}
bool EglManager::swapBuffers(EGLSurface surface) {
+ mInFrame = false;
eglSwapBuffers(mEglDisplay, surface);
EGLint err = eglGetError();
if (CC_LIKELY(err == EGL_SUCCESS)) {
@@ -273,25 +277,34 @@ bool EglManager::swapBuffers(EGLSurface surface) {
return false;
}
-bool EglManager::enableDirtyRegions(EGLSurface surface) {
- if (!mRequestDirtyRegions) return false;
+void EglManager::cancelFrame() {
+ mInFrame = false;
+}
+
+bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) {
+ if (CC_UNLIKELY(!mAllowPreserveBuffer)) return false;
- if (mCanSetDirtyRegions) {
- if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) {
+ bool preserved = false;
+ if (mCanSetPreserveBuffer) {
+ preserved = eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR,
+ preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED);
+ if (CC_UNLIKELY(!preserved)) {
ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
(void*) surface, egl_error_str());
- return false;
}
- return true;
}
- // Perhaps it is already enabled?
- EGLint value;
- if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) {
- ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p",
- (void*) surface, egl_error_str());
- return false;
+ if (CC_UNLIKELY(!preserved)) {
+ // Maybe it's already set?
+ EGLint swapBehavior;
+ if (eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &swapBehavior)) {
+ preserved = (swapBehavior == EGL_BUFFER_PRESERVED);
+ } else {
+ ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p",
+ (void*) surface, egl_error_str());
+ }
}
- return value == EGL_BUFFER_PRESERVED;
+
+ return preserved;
}
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
index 71213fb..e12db3a 100644
--- a/libs/hwui/renderthread/EglManager.h
+++ b/libs/hwui/renderthread/EglManager.h
@@ -48,8 +48,10 @@ public:
bool makeCurrent(EGLSurface surface);
void beginFrame(EGLSurface surface, EGLint* width, EGLint* height);
bool swapBuffers(EGLSurface surface);
+ void cancelFrame();
- bool enableDirtyRegions(EGLSurface surface);
+ // Returns true iff the surface is now preserving buffers.
+ bool setPreserveBuffer(EGLSurface surface, bool preserve);
void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
@@ -71,14 +73,20 @@ private:
EGLContext mEglContext;
EGLSurface mPBufferSurface;
- const bool mRequestDirtyRegions;
- bool mCanSetDirtyRegions;
+ const bool mAllowPreserveBuffer;
+ bool mCanSetPreserveBuffer;
EGLSurface mCurrentSurface;
sp<GraphicBuffer> mAtlasBuffer;
int64_t* mAtlasMap;
size_t mAtlasMapSize;
+
+ // Whether or not we are in the middle of drawing a frame. This is used
+ // to avoid switching surfaces mid-frame if requireGlContext() is called
+ // TODO: Need to be better about surface/context management so that this isn't
+ // necessary
+ bool mInFrame;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 047819d..5d55ea6 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -103,6 +103,18 @@ void RenderProxy::setFrameInterval(nsecs_t frameIntervalNanos) {
post(task);
}
+CREATE_BRIDGE2(setSwapBehavior, CanvasContext* context, SwapBehavior swapBehavior) {
+ args->context->setSwapBehavior(args->swapBehavior);
+ return NULL;
+}
+
+void RenderProxy::setSwapBehavior(SwapBehavior swapBehavior) {
+ SETUP_TASK(setSwapBehavior);
+ args->context = mContext;
+ args->swapBehavior = swapBehavior;
+ post(task);
+}
+
CREATE_BRIDGE1(loadSystemProperties, CanvasContext* context) {
bool needsRedraw = false;
if (Caches::hasInstance()) {
@@ -223,12 +235,7 @@ void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) {
// waitForCompletion = true is expected to be fairly rare and only
// happen in destruction. Thus it should be fine to temporarily
// create a Mutex
- Mutex mutex;
- Condition condition;
- SignalingRenderTask syncTask(task, &mutex, &condition);
- AutoMutex _lock(mutex);
- thread.queue(&syncTask);
- condition.wait(mutex);
+ staticPostAndWait(task);
} else {
thread.queue(task);
}
@@ -246,17 +253,6 @@ void RenderProxy::runWithGlContext(RenderTask* gltask) {
postAndWait(task);
}
-CREATE_BRIDGE1(destroyLayer, Layer* layer) {
- LayerRenderer::destroyLayer(args->layer);
- return NULL;
-}
-
-void RenderProxy::enqueueDestroyLayer(Layer* layer) {
- SETUP_TASK(destroyLayer);
- args->layer = layer;
- RenderThread::getInstance().queue(task);
-}
-
CREATE_BRIDGE2(createTextureLayer, RenderThread* thread, CanvasContext* context) {
Layer* layer = args->context->createTextureLayer();
if (!layer) return 0;
@@ -388,6 +384,17 @@ void RenderProxy::dumpProfileInfo(int fd) {
postAndWait(task);
}
+CREATE_BRIDGE1(outputLogBuffer, int fd) {
+ RenderNode::outputLogBuffer(args->fd);
+ return NULL;
+}
+
+void RenderProxy::outputLogBuffer(int fd) {
+ SETUP_TASK(outputLogBuffer);
+ args->fd = fd;
+ staticPostAndWait(task);
+}
+
CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map, size_t size) {
CanvasContext::setTextureAtlas(*args->thread, args->buffer, args->map, args->size);
args->buffer->decStrong(0);
@@ -418,6 +425,19 @@ void* RenderProxy::postAndWait(MethodInvokeRenderTask* task) {
return retval;
}
+void* RenderProxy::staticPostAndWait(MethodInvokeRenderTask* task) {
+ RenderThread& thread = RenderThread::getInstance();
+ void* retval;
+ task->setReturnPtr(&retval);
+ Mutex mutex;
+ Condition condition;
+ SignalingRenderTask syncTask(task, &mutex, &condition);
+ AutoMutex _lock(mutex);
+ thread.queue(&syncTask);
+ condition.wait(mutex);
+ return retval;
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 678e7e2..4989b14 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -31,6 +31,7 @@
#include "../Caches.h"
#include "../IContextFactory.h"
+#include "CanvasContext.h"
#include "DrawFrameTask.h"
namespace android {
@@ -44,7 +45,6 @@ class Rect;
namespace renderthread {
-class CanvasContext;
class ErrorChannel;
class RenderThread;
class RenderProxyBridge;
@@ -63,6 +63,8 @@ public:
ANDROID_API virtual ~RenderProxy();
ANDROID_API void setFrameInterval(nsecs_t frameIntervalNanos);
+ // Won't take effect until next EGLSurface creation
+ ANDROID_API void setSwapBehavior(SwapBehavior swapBehavior);
ANDROID_API bool loadSystemProperties();
ANDROID_API bool initialize(const sp<ANativeWindow>& window);
@@ -79,7 +81,6 @@ public:
ANDROID_API void runWithGlContext(RenderTask* task);
- static void enqueueDestroyLayer(Layer* layer);
ANDROID_API DeferredLayerUpdater* createTextureLayer();
ANDROID_API void buildLayer(RenderNode* node);
ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
@@ -95,6 +96,7 @@ public:
ANDROID_API void notifyFramePending();
ANDROID_API void dumpProfileInfo(int fd);
+ ANDROID_API static void outputLogBuffer(int fd);
ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size);
@@ -112,6 +114,8 @@ private:
void post(RenderTask* task);
void* postAndWait(MethodInvokeRenderTask* task);
+ static void* staticPostAndWait(MethodInvokeRenderTask* task);
+
// Friend class to help with bridging
friend class RenderProxyBridge;
};
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 403e164..f887103 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -168,7 +168,7 @@ void RenderThread::initializeDisplayEventReceiver() {
void RenderThread::initThreadLocals() {
initializeDisplayEventReceiver();
mEglManager = new EglManager(*this);
- mRenderState = new RenderState();
+ mRenderState = new RenderState(*this);
}
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp
index cf3d039..9bd4eae 100644
--- a/libs/hwui/renderthread/TimeLord.cpp
+++ b/libs/hwui/renderthread/TimeLord.cpp
@@ -20,7 +20,7 @@ namespace uirenderer {
namespace renderthread {
TimeLord::TimeLord()
- : mFrameIntervalNanos(0)
+ : mFrameIntervalNanos(milliseconds_to_nanoseconds(16))
, mFrameTimeNanos(0) {
}
diff --git a/libs/hwui/tests/Android.mk b/libs/hwui/tests/Android.mk
new file mode 100644
index 0000000..9622073
--- /dev/null
+++ b/libs/hwui/tests/Android.mk
@@ -0,0 +1,55 @@
+#
+# Copyright (C) 2014 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.
+#
+
+local_target_dir := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
+LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\"
+
+LOCAL_SRC_FILES:= \
+ TestContext.cpp \
+ main.cpp
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/.. \
+ external/skia/src/core
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libcutils \
+ libutils \
+ libskia \
+ libgui \
+ libui \
+ libhwui
+
+ifeq ($(WITH_MALLOC_LEAK_CHECK),true)
+ LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
+endif
+
+LOCAL_MODULE_PATH := $(local_target_dir)
+LOCAL_MODULE:= hwuitest
+LOCAL_MODULE_TAGS := tests
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := hwuitest
+LOCAL_MODULE_STEM_64 := hwuitest64
+
+include external/stlport/libstlport.mk
+include $(BUILD_EXECUTABLE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libs/hwui/tests/TestContext.cpp b/libs/hwui/tests/TestContext.cpp
new file mode 100644
index 0000000..35e402d
--- /dev/null
+++ b/libs/hwui/tests/TestContext.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014 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 "TestContext.h"
+
+#include <gui/ISurfaceComposer.h>
+#include <gui/SurfaceComposerClient.h>
+
+using namespace android;
+
+DisplayInfo gDisplay;
+sp<SurfaceComposerClient> gSession;
+
+void createTestEnvironment() {
+ gSession = new SurfaceComposerClient();
+ sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
+ ISurfaceComposer::eDisplayIdMain));
+ status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &gDisplay);
+ LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n");
+}
+
+sp<SurfaceControl> createWindow(int width, int height) {
+ sp<SurfaceControl> control = gSession->createSurface(String8("HwuiTest"),
+ width, height, PIXEL_FORMAT_RGBX_8888);
+
+ SurfaceComposerClient::openGlobalTransaction();
+ control->setLayer(0x7FFFFFF);
+ control->show();
+ SurfaceComposerClient::closeGlobalTransaction();
+
+ return control;
+}
diff --git a/libs/hwui/tests/TestContext.h b/libs/hwui/tests/TestContext.h
new file mode 100644
index 0000000..8a5d530
--- /dev/null
+++ b/libs/hwui/tests/TestContext.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef TESTCONTEXT_H
+#define TESTCONTEXT_H
+
+#include <ui/DisplayInfo.h>
+#include <gui/SurfaceControl.h>
+
+extern android::DisplayInfo gDisplay;
+#define dp(x) ((x) * gDisplay.density)
+
+// Initializes all the static globals that are shared across all contexts
+// such as display info
+void createTestEnvironment();
+
+// Defaults to fullscreen
+android::sp<android::SurfaceControl> createWindow(int width = -1, int height = -1);
+
+#endif
diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp
new file mode 100644
index 0000000..2d99e9f
--- /dev/null
+++ b/libs/hwui/tests/main.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2014 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 <stdio.h>
+
+#include <cutils/log.h>
+#include <gui/Surface.h>
+#include <ui/PixelFormat.h>
+
+#include <AnimationContext.h>
+#include <DisplayListRenderer.h>
+#include <RenderNode.h>
+#include <renderthread/RenderProxy.h>
+
+#include "TestContext.h"
+
+using namespace android;
+using namespace android::uirenderer;
+using namespace android::uirenderer::renderthread;
+
+class ContextFactory : public IContextFactory {
+public:
+ virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) {
+ return new AnimationContext(clock);
+ }
+};
+
+static DisplayListRenderer* startRecording(RenderNode* node) {
+ DisplayListRenderer* renderer = new DisplayListRenderer();
+ renderer->setViewport(node->getWidth(), node->getHeight());
+ renderer->prepare(false);
+ return renderer;
+}
+
+static void endRecording(DisplayListRenderer* renderer, RenderNode* node) {
+ renderer->finish();
+ node->setStagingDisplayList(renderer->finishRecording());
+ delete renderer;
+}
+
+sp<RenderNode> createCard(int x, int y, int width, int height) {
+ sp<RenderNode> node = new RenderNode();
+ node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height);
+ node->mutateStagingProperties().setElevation(dp(16));
+ node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1);
+ node->mutateStagingProperties().mutableOutline().setShouldClip(true);
+ node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z);
+
+ DisplayListRenderer* renderer = startRecording(node.get());
+ renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode);
+ endRecording(renderer, node.get());
+
+ return node;
+}
+
+int main(int argc, char* argv[]) {
+ createTestEnvironment();
+
+ // create the native surface
+ const int width = gDisplay.w;
+ const int height = gDisplay.h;
+ sp<SurfaceControl> control = createWindow(width, height);
+ sp<Surface> surface = control->getSurface();
+
+ RenderNode* rootNode = new RenderNode();
+ rootNode->incStrong(0);
+ rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height);
+ rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ rootNode->mutateStagingProperties().setClipToBounds(false);
+ rootNode->setPropertyFieldsDirty(RenderNode::GENERIC);
+
+ ContextFactory factory;
+ RenderProxy* proxy = new RenderProxy(false, rootNode, &factory);
+ proxy->loadSystemProperties();
+ proxy->initialize(surface);
+ float lightX = width / 2.0;
+ proxy->setup(width, height, (Vector3){lightX, dp(-200.0f), dp(800.0f)},
+ dp(800.0f), 255 * 0.075, 255 * 0.15);
+
+ android::uirenderer::Rect DUMMY;
+
+ std::vector< sp<RenderNode> > cards;
+
+ DisplayListRenderer* renderer = startRecording(rootNode);
+ renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode);
+ renderer->insertReorderBarrier(true);
+
+ for (int x = dp(16); x < (width - dp(116)); x += dp(116)) {
+ for (int y = dp(16); y < (height - dp(116)); y += dp(116)) {
+ sp<RenderNode> card = createCard(x, y, dp(100), dp(100));
+ renderer->drawRenderNode(card.get(), DUMMY, 0);
+ cards.push_back(card);
+ }
+ }
+
+ renderer->insertReorderBarrier(false);
+ endRecording(renderer, rootNode);
+
+ for (int i = 0; i < 150; i++) {
+ ATRACE_NAME("UI-Draw Frame");
+ for (int ci = 0; ci < cards.size(); ci++) {
+ cards[ci]->mutateStagingProperties().setTranslationX(i);
+ cards[ci]->mutateStagingProperties().setTranslationY(i);
+ cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y);
+ }
+ nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC);
+ proxy->syncAndDrawFrame(frameTimeNs, 0, gDisplay.density);
+ usleep(12000);
+ }
+
+ sleep(5);
+
+ delete proxy;
+ rootNode->decStrong(0);
+
+ printf("Success!\n");
+ return 0;
+}