summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/androidfw/Android.mk1
-rw-r--r--libs/androidfw/AssetManager.cpp13
-rw-r--r--libs/androidfw/ResourceTypes.cpp2129
-rw-r--r--libs/androidfw/TypeWrappers.cpp70
-rw-r--r--libs/androidfw/tests/Android.mk77
-rw-r--r--libs/androidfw/tests/ByteBucketArray_test.cpp41
-rw-r--r--libs/androidfw/tests/Idmap_test.cpp124
-rw-r--r--libs/androidfw/tests/ResTable_test.cpp189
-rw-r--r--libs/androidfw/tests/ResourceTypes_test.cpp4
-rw-r--r--libs/androidfw/tests/Split_test.cpp173
-rw-r--r--libs/androidfw/tests/TestHelpers.h17
-rw-r--r--libs/androidfw/tests/TypeWrappers_test.cpp109
-rw-r--r--libs/androidfw/tests/data/.gitignore2
-rw-r--r--libs/androidfw/tests/data/basic/AndroidManifest.xml21
-rw-r--r--libs/androidfw/tests/data/basic/basic_arsc.h131
-rwxr-xr-xlibs/androidfw/tests/data/basic/build6
-rw-r--r--libs/androidfw/tests/data/basic/res/values-sv/values.xml4
-rw-r--r--libs/androidfw/tests/data/basic/res/values/values.xml26
-rw-r--r--libs/androidfw/tests/data/overlay/AndroidManifest.xml21
-rwxr-xr-xlibs/androidfw/tests/data/overlay/build6
-rw-r--r--libs/androidfw/tests/data/overlay/overlay_arsc.h69
-rw-r--r--libs/androidfw/tests/data/overlay/res/values/values.xml8
-rw-r--r--libs/androidfw/tests/data/split_base_arsc.h221
-rw-r--r--libs/androidfw/tests/data/split_de_fr_arsc.h118
-rw-r--r--libs/hwui/Android.mk16
-rw-r--r--libs/hwui/Animator.cpp4
-rw-r--r--libs/hwui/Animator.h6
-rw-r--r--libs/hwui/Caches.cpp15
-rw-r--r--libs/hwui/Caches.h5
-rw-r--r--libs/hwui/DamageAccumulator.cpp219
-rw-r--r--libs/hwui/DamageAccumulator.h103
-rw-r--r--libs/hwui/DeferredDisplayList.cpp2
-rw-r--r--libs/hwui/DeferredDisplayList.h1
-rw-r--r--libs/hwui/DeferredLayerUpdater.cpp26
-rw-r--r--libs/hwui/DeferredLayerUpdater.h9
-rw-r--r--libs/hwui/DisplayList.cpp9
-rw-r--r--libs/hwui/DisplayList.h9
-rw-r--r--libs/hwui/DisplayListOp.h90
-rw-r--r--libs/hwui/DisplayListRenderer.cpp23
-rw-r--r--libs/hwui/DisplayListRenderer.h38
-rw-r--r--libs/hwui/DrawProfiler.cpp2
-rw-r--r--libs/hwui/DrawProfiler.h2
-rw-r--r--libs/hwui/FontRenderer.cpp24
-rw-r--r--libs/hwui/FontRenderer.h4
-rw-r--r--libs/hwui/Interpolator.cpp4
-rw-r--r--libs/hwui/Layer.cpp38
-rw-r--r--libs/hwui/Layer.h10
-rw-r--r--libs/hwui/LayerCache.cpp4
-rw-r--r--libs/hwui/LayerCache.h4
-rw-r--r--libs/hwui/LayerRenderer.cpp59
-rw-r--r--libs/hwui/LayerRenderer.h18
-rw-r--r--libs/hwui/Matrix.cpp2
-rw-r--r--libs/hwui/Matrix.h1
-rw-r--r--libs/hwui/OpenGLRenderer.cpp114
-rw-r--r--libs/hwui/OpenGLRenderer.h31
-rw-r--r--libs/hwui/PathCache.cpp7
-rw-r--r--libs/hwui/PathCache.h5
-rw-r--r--libs/hwui/PathTessellator.cpp38
-rw-r--r--libs/hwui/RenderNode.cpp291
-rw-r--r--libs/hwui/RenderNode.h56
-rw-r--r--libs/hwui/RenderProperties.cpp51
-rw-r--r--libs/hwui/RenderProperties.h317
-rw-r--r--libs/hwui/RenderState.cpp112
-rw-r--r--libs/hwui/RenderState.h70
-rw-r--r--libs/hwui/Renderer.h23
-rw-r--r--libs/hwui/StatefulBaseRenderer.cpp12
-rw-r--r--libs/hwui/StatefulBaseRenderer.h4
-rw-r--r--libs/hwui/TessellationCache.cpp123
-rw-r--r--libs/hwui/TessellationCache.h40
-rw-r--r--libs/hwui/TextureCache.cpp59
-rw-r--r--libs/hwui/TextureCache.h2
-rw-r--r--libs/hwui/TreeInfo.h64
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp392
-rw-r--r--libs/hwui/renderthread/CanvasContext.h16
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp24
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.h4
-rw-r--r--libs/hwui/renderthread/EglManager.cpp288
-rw-r--r--libs/hwui/renderthread/EglManager.h88
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp51
-rw-r--r--libs/hwui/renderthread/RenderProxy.h7
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp15
-rw-r--r--libs/hwui/renderthread/RenderThread.h14
-rw-r--r--libs/hwui/thread/Task.h4
-rw-r--r--libs/hwui/utils/Blur.cpp12
-rw-r--r--libs/hwui/utils/Blur.h6
-rw-r--r--libs/input/SpriteController.cpp5
86 files changed, 4518 insertions, 2154 deletions
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk
index d21197e..957809d 100644
--- a/libs/androidfw/Android.mk
+++ b/libs/androidfw/Android.mk
@@ -25,6 +25,7 @@ commonSources := \
ObbFile.cpp \
ResourceTypes.cpp \
StreamingZipInflater.cpp \
+ TypeWrappers.cpp \
ZipFileRO.cpp \
ZipUtils.cpp
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 91dda75..0340928 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -256,7 +256,7 @@ bool AssetManager::addOverlayPath(const String8& packagePath, int32_t* cookie)
String8 targetPath;
String8 overlayPath;
if (!ResTable::getIdmapInfo(idmap->getBuffer(false), idmap->getLength(),
- NULL, NULL, &targetPath, &overlayPath)) {
+ NULL, NULL, NULL, &targetPath, &overlayPath)) {
ALOGW("failed to read idmap file %s\n", idmapPath.string());
delete idmap;
return false;
@@ -311,7 +311,7 @@ bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApk
ALOGW("failed to find resources.arsc in %s\n", ap.path.string());
return false;
}
- tables[i].add(ass, 1, false /* copyData */, NULL /* idMap */);
+ tables[i].add(ass);
}
return tables[0].createIdmap(tables[1], targetCrc, overlayCrc,
@@ -617,7 +617,7 @@ const ResTable* AssetManager::getResTable(bool required) const
// can quickly copy it out for others.
ALOGV("Creating shared resources for %s", ap.path.string());
sharedRes = new ResTable();
- sharedRes->add(ass, i + 1, false, idmap);
+ sharedRes->add(ass, idmap, i + 1, false);
#ifdef HAVE_ANDROID_OS
const char* data = getenv("ANDROID_DATA");
LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
@@ -646,7 +646,7 @@ const ResTable* AssetManager::getResTable(bool required) const
mResources->add(sharedRes);
} else {
ALOGV("Parsing resources for %s", ap.path.string());
- mResources->add(ass, i + 1, !shared, idmap);
+ mResources->add(ass, idmap, i + 1, !shared);
}
onlyEmptyResources = false;
@@ -654,7 +654,7 @@ const ResTable* AssetManager::getResTable(bool required) const
delete ass;
}
} else {
- ALOGW("Installing empty resources in to table %p\n", mResources);
+ ALOGV("Installing empty resources in to table %p\n", mResources);
mResources->addEmpty(i + 1);
}
@@ -736,7 +736,7 @@ void AssetManager::addSystemOverlays(const char* pathOverlaysList,
if (oass != NULL) {
Asset* oidmap = openIdmapLocked(oap);
offset++;
- sharedRes->add(oass, offset + 1, false, oidmap);
+ sharedRes->add(oass, oidmap, offset + 1, false);
const_cast<AssetManager*>(this)->mAssetPaths.add(oap);
const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap);
}
@@ -1768,6 +1768,7 @@ bool AssetManager::fncScanAndMergeDirLocked(
}
mergeInfoLocked(pMergedInfo, pContents);
+ delete pContents;
return true;
}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index a4b78a6..2e3abb5 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -17,7 +17,9 @@
#define LOG_TAG "ResourceType"
//#define LOG_NDEBUG 0
+#include <androidfw/ByteBucketArray.h>
#include <androidfw/ResourceTypes.h>
+#include <androidfw/TypeWrappers.h>
#include <utils/Atomic.h>
#include <utils/ByteOrder.h>
#include <utils/Debug.h>
@@ -30,6 +32,7 @@
#include <memory.h>
#include <ctype.h>
#include <stdint.h>
+#include <stddef.h>
#ifndef INT32_MAX
#define INT32_MAX ((int32_t)(2147483647))
@@ -42,7 +45,7 @@
#define TABLE_SUPER_NOISY(x) //x
#define LOAD_TABLE_NOISY(x) //x
#define TABLE_THEME(x) //x
-#define LIB_NOISY(x) x
+#define LIB_NOISY(x) //x
namespace android {
@@ -63,9 +66,8 @@ namespace android {
#endif
#endif
-#define IDMAP_MAGIC 0x706d6469
-// size measured in sizeof(uint32_t)
-#define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t))
+#define IDMAP_MAGIC 0x504D4449
+#define IDMAP_CURRENT_VERSION 0x00000001
#define APP_PACKAGE_ID 0x7f
#define SYS_PACKAGE_ID 0x01
@@ -77,6 +79,11 @@ inline int isspace16(char16_t c) {
return (c < 0x0080 && isspace(c));
}
+template<typename T>
+inline static T max(T a, T b) {
+ return a > b ? a : 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.
@@ -215,104 +222,179 @@ void Res_png_9patch::serialize(const Res_png_9patch& patch, const int32_t* xDivs
fill9patchOffsets(reinterpret_cast<Res_png_9patch*>(outData));
}
-static bool assertIdmapHeader(const uint32_t* map, size_t sizeBytes)
-{
- if (sizeBytes < ResTable::IDMAP_HEADER_SIZE_BYTES) {
- ALOGW("idmap assertion failed: size=%d bytes\n", (int)sizeBytes);
+static bool assertIdmapHeader(const void* idmap, size_t size) {
+ if (reinterpret_cast<uintptr_t>(idmap) & 0x03) {
+ ALOGE("idmap: header is not word aligned");
+ return false;
+ }
+
+ if (size < ResTable::IDMAP_HEADER_SIZE_BYTES) {
+ ALOGW("idmap: header too small (%d bytes)", (uint32_t) size);
return false;
}
- if (*map != htodl(IDMAP_MAGIC)) { // htodl: map data expected to be in correct endianess
- ALOGW("idmap assertion failed: invalid magic found (is 0x%08x, expected 0x%08x)\n",
- *map, htodl(IDMAP_MAGIC));
+
+ const uint32_t magic = htodl(*reinterpret_cast<const uint32_t*>(idmap));
+ if (magic != IDMAP_MAGIC) {
+ ALOGW("idmap: no magic found in header (is 0x%08x, expected 0x%08x)",
+ magic, IDMAP_MAGIC);
+ return false;
+ }
+
+ const uint32_t version = htodl(*(reinterpret_cast<const uint32_t*>(idmap) + 1));
+ if (version != IDMAP_CURRENT_VERSION) {
+ // We are strict about versions because files with this format are
+ // auto-generated and don't need backwards compatibility.
+ ALOGW("idmap: version mismatch in header (is 0x%08x, expected 0x%08x)",
+ version, IDMAP_CURRENT_VERSION);
return false;
}
return true;
}
-static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, uint32_t* outValue)
-{
- // see README for details on the format of map
- if (!assertIdmapHeader(map, sizeBytes)) {
- return UNKNOWN_ERROR;
- }
- map = map + IDMAP_HEADER_SIZE; // skip ahead to data segment
- // size of data block, in uint32_t
- const size_t size = (sizeBytes - ResTable::IDMAP_HEADER_SIZE_BYTES) / sizeof(uint32_t);
- const uint32_t type = Res_GETTYPE(key) + 1; // add one, idmap stores "public" type id
- const uint32_t entry = Res_GETENTRY(key);
- const uint32_t typeCount = *map;
+class IdmapEntries {
+public:
+ IdmapEntries() : mData(NULL) {}
- if (type > typeCount) {
- ALOGW("Resource ID map: type=%d exceeds number of types=%d\n", type, typeCount);
- return UNKNOWN_ERROR;
+ bool hasEntries() const {
+ if (mData == NULL) {
+ return false;
+ }
+
+ return (dtohs(*mData) > 0);
}
- if (typeCount > size) {
- ALOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, (int)size);
- return UNKNOWN_ERROR;
+
+ size_t byteSize() const {
+ if (mData == NULL) {
+ return 0;
+ }
+ uint16_t entryCount = dtohs(mData[2]);
+ return (sizeof(uint16_t) * 4) + (sizeof(uint32_t) * static_cast<size_t>(entryCount));
}
- const uint32_t typeOffset = map[type];
- if (typeOffset == 0) {
- *outValue = 0;
- return NO_ERROR;
+
+ uint8_t targetTypeId() const {
+ if (mData == NULL) {
+ return 0;
+ }
+ return dtohs(mData[0]);
}
- if (typeOffset + 1 > size) {
- ALOGW("Resource ID map: type offset=%d exceeds reasonable value, size of map=%d\n",
- typeOffset, (int)size);
- return UNKNOWN_ERROR;
+
+ uint8_t overlayTypeId() const {
+ if (mData == NULL) {
+ return 0;
+ }
+ return dtohs(mData[1]);
}
- const uint32_t entryCount = map[typeOffset];
- const uint32_t entryOffset = map[typeOffset + 1];
- if (entryCount == 0 || entry < entryOffset || entry - entryOffset > entryCount - 1) {
- *outValue = 0;
+
+ status_t setTo(const void* entryHeader, size_t size) {
+ if (reinterpret_cast<uintptr_t>(entryHeader) & 0x03) {
+ ALOGE("idmap: entry header is not word aligned");
+ return UNKNOWN_ERROR;
+ }
+
+ if (size < sizeof(uint16_t) * 4) {
+ ALOGE("idmap: entry header is too small (%u bytes)", (uint32_t) size);
+ return UNKNOWN_ERROR;
+ }
+
+ const uint16_t* header = reinterpret_cast<const uint16_t*>(entryHeader);
+ const uint16_t targetTypeId = dtohs(header[0]);
+ const uint16_t overlayTypeId = dtohs(header[1]);
+ if (targetTypeId == 0 || overlayTypeId == 0 || targetTypeId > 255 || overlayTypeId > 255) {
+ ALOGE("idmap: invalid type map (%u -> %u)", targetTypeId, overlayTypeId);
+ return UNKNOWN_ERROR;
+ }
+
+ uint16_t entryCount = dtohs(header[2]);
+ if (size < sizeof(uint32_t) * (entryCount + 2)) {
+ ALOGE("idmap: too small (%u bytes) for the number of entries (%u)",
+ (uint32_t) size, (uint32_t) entryCount);
+ return UNKNOWN_ERROR;
+ }
+ mData = header;
return NO_ERROR;
}
- const uint32_t index = typeOffset + 2 + entry - entryOffset;
- if (index > size) {
- ALOGW("Resource ID map: entry index=%u exceeds size of map=%d\n", index, (int)size);
- *outValue = 0;
+
+ status_t lookup(uint16_t entryId, uint16_t* outEntryId) const {
+ uint16_t entryCount = dtohs(mData[2]);
+ uint16_t offset = dtohs(mData[3]);
+
+ if (entryId < offset) {
+ // The entry is not present in this idmap
+ return BAD_INDEX;
+ }
+
+ entryId -= offset;
+
+ if (entryId >= entryCount) {
+ // The entry is not present in this idmap
+ return BAD_INDEX;
+ }
+
+ // It is safe to access the type here without checking the size because
+ // we have checked this when it was first loaded.
+ const uint32_t* entries = reinterpret_cast<const uint32_t*>(mData) + 2;
+ uint32_t mappedEntry = dtohl(entries[entryId]);
+ if (mappedEntry == 0xffffffff) {
+ // This entry is not present in this idmap
+ return BAD_INDEX;
+ }
+ *outEntryId = static_cast<uint16_t>(mappedEntry);
return NO_ERROR;
}
- *outValue = map[index];
- return NO_ERROR;
-}
+private:
+ const uint16_t* mData;
+};
-static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t *outId)
-{
- if (!assertIdmapHeader(map, mapSize)) {
+status_t parseIdmap(const void* idmap, size_t size, uint8_t* outPackageId, KeyedVector<uint8_t, IdmapEntries>* outMap) {
+ if (!assertIdmapHeader(idmap, size)) {
return UNKNOWN_ERROR;
}
- if (mapSize <= IDMAP_HEADER_SIZE + 1) {
- ALOGW("corrupt idmap: map size %d too short\n", (int)mapSize);
+
+ size -= ResTable::IDMAP_HEADER_SIZE_BYTES;
+ if (size < sizeof(uint16_t) * 2) {
+ ALOGE("idmap: too small to contain any mapping");
return UNKNOWN_ERROR;
}
- uint32_t typeCount = *(map + IDMAP_HEADER_SIZE);
- if (typeCount == 0) {
- ALOGW("corrupt idmap: no types\n");
+
+ const uint16_t* data = reinterpret_cast<const uint16_t*>(
+ reinterpret_cast<const uint8_t*>(idmap) + ResTable::IDMAP_HEADER_SIZE_BYTES);
+
+ uint16_t targetPackageId = dtohs(*(data++));
+ if (targetPackageId == 0 || targetPackageId > 255) {
+ ALOGE("idmap: target package ID is invalid (%02x)", targetPackageId);
return UNKNOWN_ERROR;
}
- if (IDMAP_HEADER_SIZE + 1 + typeCount > mapSize) {
- ALOGW("corrupt idmap: number of types %u extends past idmap size %d\n", typeCount, (int)mapSize);
+
+ uint16_t mapCount = dtohs(*(data++));
+ if (mapCount == 0) {
+ ALOGE("idmap: no mappings");
return UNKNOWN_ERROR;
}
- const uint32_t* p = map + IDMAP_HEADER_SIZE + 1;
- // find first defined type
- while (*p == 0) {
- ++p;
- if (--typeCount == 0) {
- ALOGW("corrupt idmap: types declared, none found\n");
- return UNKNOWN_ERROR;
- }
+
+ if (mapCount > 255) {
+ ALOGW("idmap: too many mappings. Only 255 are possible but %u are present", (uint32_t) mapCount);
}
- // determine package id from first entry of first type
- const uint32_t offset = *p + IDMAP_HEADER_SIZE + 2;
- if (offset > mapSize) {
- ALOGW("corrupt idmap: entry offset %u points outside map size %d\n", offset, (int)mapSize);
- return UNKNOWN_ERROR;
+ while (size > sizeof(uint16_t) * 4) {
+ IdmapEntries entries;
+ status_t err = entries.setTo(data, size);
+ if (err != NO_ERROR) {
+ return err;
+ }
+
+ ssize_t index = outMap->add(entries.overlayTypeId(), entries);
+ if (index < 0) {
+ return NO_MEMORY;
+ }
+
+ data += entries.byteSize() / sizeof(uint16_t);
+ size -= entries.byteSize();
}
- *outId = (map[offset] >> 24) & 0x000000ff;
+ if (outPackageId != NULL) {
+ *outPackageId = static_cast<uint8_t>(targetPackageId);
+ }
return NO_ERROR;
}
@@ -2726,7 +2808,7 @@ struct ResTable::Header
free(resourceIDMap);
}
- ResTable* const owner;
+ const ResTable* const owner;
void* ownedData;
const ResTable_header* header;
size_t size;
@@ -2739,6 +2821,17 @@ struct ResTable::Header
size_t resourceIDMapSize;
};
+struct ResTable::Entry {
+ ResTable_config config;
+ const ResTable_entry* entry;
+ const ResTable_type* type;
+ uint32_t specFlags;
+ const Package* package;
+
+ StringPoolRef typeStr;
+ StringPoolRef keyStr;
+};
+
struct ResTable::Type
{
Type(const Header* _header, const Package* _package, size_t count)
@@ -2749,33 +2842,29 @@ struct ResTable::Type
const size_t entryCount;
const ResTable_typeSpec* typeSpec;
const uint32_t* typeSpecFlags;
+ IdmapEntries idmapEntries;
Vector<const ResTable_type*> configs;
};
struct ResTable::Package
{
Package(ResTable* _owner, const Header* _header, const ResTable_package* _package)
- : owner(_owner), header(_header), package(_package) { }
- ~Package()
- {
- size_t i = types.size();
- while (i > 0) {
- i--;
- delete types[i];
+ : owner(_owner), header(_header), package(_package), typeIdOffset(0) {
+ if (dtohs(package->header.headerSize) == sizeof(package)) {
+ // The package structure is the same size as the definition.
+ // This means it contains the typeIdOffset field.
+ typeIdOffset = package->typeIdOffset;
}
}
- ResTable* const owner;
+ const ResTable* const owner;
const Header* const header;
const ResTable_package* const package;
- Vector<Type*> types;
ResStringPool typeStrings;
ResStringPool keyStrings;
- const Type* getType(size_t idx) const {
- return idx < types.size() ? types[idx] : NULL;
- }
+ size_t typeIdOffset;
};
// A group of objects describing a particular resource package.
@@ -2787,13 +2876,24 @@ struct ResTable::PackageGroup
: owner(_owner)
, name(_name)
, id(_id)
- , typeCount(0)
+ , largestTypeId(0)
, bags(NULL)
, dynamicRefTable(static_cast<uint8_t>(_id))
{ }
~PackageGroup() {
clearBagCache();
+ const size_t numTypes = types.size();
+ for (size_t i = 0; i < numTypes; i++) {
+ const TypeList& typeList = types[i];
+ const size_t numInnerTypes = typeList.size();
+ for (size_t j = 0; j < numInnerTypes; j++) {
+ if (typeList[j]->package->owner == owner) {
+ delete typeList[j];
+ }
+ }
+ }
+
const size_t N = packages.size();
for (size_t i=0; i<N; i++) {
Package* pkg = packages[i];
@@ -2806,17 +2906,15 @@ struct ResTable::PackageGroup
void clearBagCache() {
if (bags) {
TABLE_NOISY(printf("bags=%p\n", bags));
- Package* pkg = packages[0];
- TABLE_NOISY(printf("typeCount=%x\n", typeCount));
- for (size_t i=0; i<typeCount; i++) {
+ for (size_t i = 0; i < bags->size(); i++) {
TABLE_NOISY(printf("type=%d\n", i));
- const Type* type = pkg->getType(i);
- if (type != NULL) {
- bag_set** typeBags = bags[i];
+ const TypeList& typeList = types[i];
+ if (typeList.isEmpty()) {
+ bag_set** typeBags = bags->get(i);
TABLE_NOISY(printf("typeBags=%p\n", typeBags));
if (typeBags) {
- TABLE_NOISY(printf("type->entryCount=%x\n", type->entryCount));
- const size_t N = type->entryCount;
+ const size_t N = typeList[0]->entryCount;
+ TABLE_NOISY(printf("type->entryCount=%x\n", N));
for (size_t j=0; j<N; j++) {
if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF)
free(typeBags[j]);
@@ -2825,25 +2923,38 @@ struct ResTable::PackageGroup
}
}
}
- free(bags);
+ delete bags;
bags = NULL;
}
}
- ResTable* const owner;
+ ssize_t findType16(const char16_t* type, size_t len) const {
+ const size_t N = packages.size();
+ for (size_t i = 0; i < N; i++) {
+ ssize_t index = packages[i]->typeStrings.indexOfString(type, len);
+ if (index >= 0) {
+ return index + packages[i]->typeIdOffset;
+ }
+ }
+ return -1;
+ }
+
+ const ResTable* const owner;
String16 const name;
uint32_t const id;
+
+ // This is mainly used to keep track of the loaded packages
+ // and to clean them up properly. Accessing resources happens from
+ // the 'types' array.
Vector<Package*> packages;
- // This is for finding typeStrings and other common package stuff.
- Package* basePackage;
+ ByteBucketArray<TypeList> types;
- // For quick access.
- size_t typeCount;
+ uint8_t largestTypeId;
// Computed attribute bags, first indexed by the type and second
// by the entry in that type.
- bag_set*** bags;
+ ByteBucketArray<bag_set**>* bags;
// The table mapping dynamic references to resolved references for
// this package group.
@@ -2879,7 +2990,7 @@ ResTable::Theme::~Theme()
void ResTable::Theme::free_package(package_info* pi)
{
- for (size_t j=0; j<pi->numTypes; j++) {
+ for (size_t j = 0; j <= Res_MAXTYPE; j++) {
theme_entry* te = pi->types[j].entries;
if (te != NULL) {
free(te);
@@ -2890,10 +3001,8 @@ void ResTable::Theme::free_package(package_info* pi)
ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi)
{
- package_info* newpi = (package_info*)malloc(
- sizeof(package_info) + (pi->numTypes*sizeof(type_info)));
- newpi->numTypes = pi->numTypes;
- for (size_t j=0; j<newpi->numTypes; j++) {
+ package_info* newpi = (package_info*)malloc(sizeof(package_info));
+ for (size_t j = 0; j <= Res_MAXTYPE; j++) {
size_t cnt = pi->types[j].numEntries;
newpi->types[j].numEntries = cnt;
theme_entry* te = pi->types[j].entries;
@@ -2946,17 +3055,14 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force)
curPI = mPackages[pidx];
if (curPI == NULL) {
PackageGroup* const grp = mTable.mPackageGroups[pidx];
- int cnt = grp->typeCount;
- curPI = (package_info*)malloc(
- sizeof(package_info) + (cnt*sizeof(type_info)));
- curPI->numTypes = cnt;
- memset(curPI->types, 0, cnt*sizeof(type_info));
+ curPI = (package_info*)malloc(sizeof(package_info));
+ memset(curPI, 0, sizeof(*curPI));
mPackages[pidx] = curPI;
}
curType = 0xffffffff;
}
if (curType != t) {
- if (t >= curPI->numTypes) {
+ if (t > Res_MAXTYPE) {
ALOGE("Style contains key with bad type: 0x%08x\n", attrRes);
bag++;
continue;
@@ -2965,8 +3071,8 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force)
curEntries = curPI->types[t].entries;
if (curEntries == NULL) {
PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex];
- const Type* type = grp->packages[0]->getType(t);
- int cnt = type != NULL ? type->entryCount : 0;
+ const TypeList& typeList = grp->types[t];
+ int cnt = typeList.isEmpty() ? 0 : typeList[0]->entryCount;
curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry));
memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry));
curPI->types[t].numEntries = cnt;
@@ -2981,8 +3087,8 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force)
}
theme_entry* curEntry = curEntries + e;
TABLE_NOISY(ALOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x",
- attrRes, bag->map.value.dataType, bag->map.value.data,
- curEntry->value.dataType));
+ attrRes, bag->map.value.dataType, bag->map.value.data,
+ curEntry->value.dataType));
if (force || curEntry->value.dataType == Res_value::TYPE_NULL) {
curEntry->stringBlock = bag->stringBlock;
curEntry->typeSpecFlags |= bagTypeSpecFlags;
@@ -3057,8 +3163,8 @@ ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue,
const package_info* const pi = mPackages[p];
TABLE_THEME(ALOGI("Found package: %p", pi));
if (pi != NULL) {
- TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, pi->numTypes));
- if (t < pi->numTypes) {
+ TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, Res_MAXTYPE + 1));
+ if (t <= Res_MAXTYPE) {
const type_info& ti = pi->types[t];
TABLE_THEME(ALOGI("Desired entry index is %ld in avail %d", e, ti.numEntries));
if (e < ti.numEntries) {
@@ -3120,14 +3226,13 @@ void ResTable::Theme::dumpToLog() const
package_info* pi = mPackages[i];
if (pi == NULL) continue;
- ALOGI(" Package #0x%02x:\n", (int)(i+1));
- for (size_t j=0; j<pi->numTypes; j++) {
+ ALOGI(" Package #0x%02x:\n", (int)(i + 1));
+ for (size_t j = 0; j <= Res_MAXTYPE; j++) {
type_info& ti = pi->types[j];
if (ti.numEntries == 0) continue;
-
- ALOGI(" Type #0x%02x:\n", (int)(j+1));
- for (size_t k=0; k<ti.numEntries; k++) {
- theme_entry& te = ti.entries[k];
+ ALOGI(" Type #0x%02x:\n", (int)(j + 1));
+ for (size_t k = 0; k < ti.numEntries; k++) {
+ const theme_entry& te = ti.entries[k];
if (te.value.dataType == Res_value::TYPE_NULL) continue;
ALOGI(" 0x%08x: t=0x%x, d=0x%08x (block=%d)\n",
(int)Res_MAKEID(i, j, k),
@@ -3150,7 +3255,7 @@ ResTable::ResTable(const void* data, size_t size, const int32_t cookie, bool cop
{
memset(&mParams, 0, sizeof(mParams));
memset(mPackageMap, 0, sizeof(mPackageMap));
- addInternal(data, size, cookie, copyData, NULL /* idMap */);
+ addInternal(data, size, NULL, 0, cookie, copyData);
LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table");
//ALOGI("Creating ResTable %p\n", this);
}
@@ -3166,21 +3271,45 @@ inline ssize_t ResTable::getResourcePackageIndex(uint32_t resID) const
return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1;
}
-status_t ResTable::add(const void* data, size_t size) {
- return addInternal(data, size, 0 /* cookie */,
- false /* copyData */, NULL /* idMap */);
+status_t ResTable::add(const void* data, size_t size, const int32_t cookie, bool copyData) {
+ return addInternal(data, size, NULL, 0, cookie, copyData);
}
-status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData, const void* idmap)
-{
+status_t ResTable::add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
+ const int32_t cookie, bool copyData) {
+ return addInternal(data, size, idmapData, idmapDataSize, cookie, copyData);
+}
+
+status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData) {
const void* data = asset->getBuffer(true);
if (data == NULL) {
ALOGW("Unable to get buffer of resource asset file");
return UNKNOWN_ERROR;
}
- size_t size = (size_t)asset->getLength();
- return addInternal(data, size, cookie, copyData,
- reinterpret_cast<const Asset*>(idmap));
+
+ return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, 0, cookie, copyData);
+}
+
+status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData) {
+ const void* data = asset->getBuffer(true);
+ if (data == NULL) {
+ ALOGW("Unable to get buffer of resource asset file");
+ return UNKNOWN_ERROR;
+ }
+
+ size_t idmapSize = 0;
+ const void* idmapData = NULL;
+ if (idmapAsset != NULL) {
+ idmapData = idmapAsset->getBuffer(true);
+ if (idmapData == NULL) {
+ ALOGW("Unable to get buffer of idmap asset file");
+ return UNKNOWN_ERROR;
+ }
+ idmapSize = static_cast<size_t>(idmapAsset->getLength());
+ }
+
+ return addInternal(data, static_cast<size_t>(asset->getLength()),
+ idmapData, idmapSize, cookie, copyData);
}
status_t ResTable::add(ResTable* src)
@@ -3197,8 +3326,16 @@ status_t ResTable::add(ResTable* src)
for (size_t j=0; j<srcPg->packages.size(); j++) {
pg->packages.add(srcPg->packages[j]);
}
- pg->basePackage = srcPg->basePackage;
- pg->typeCount = srcPg->typeCount;
+
+ for (size_t j = 0; j < srcPg->types.size(); j++) {
+ if (srcPg->types[j].isEmpty()) {
+ continue;
+ }
+
+ TypeList& typeList = pg->types.editItemAt(j);
+ typeList.appendVector(srcPg->types[j]);
+ }
+ pg->largestTypeId = max(pg->largestTypeId, srcPg->largestTypeId);
mPackageGroups.add(pg);
}
@@ -3224,38 +3361,39 @@ status_t ResTable::addEmpty(const int32_t cookie) {
return (mError=NO_ERROR);
}
-status_t ResTable::addInternal(const void* data, size_t size, const int32_t cookie,
- bool copyData, const Asset* idmap)
+status_t ResTable::addInternal(const void* data, size_t dataSize, const void* idmapData, size_t idmapDataSize,
+ const int32_t cookie, bool copyData)
{
- if (!data) return NO_ERROR;
+ if (!data) {
+ return NO_ERROR;
+ }
+
Header* header = new Header(this);
header->index = mHeaders.size();
header->cookie = cookie;
- if (idmap != NULL) {
- const size_t idmap_size = idmap->getLength();
- const void* idmap_data = const_cast<Asset*>(idmap)->getBuffer(true);
- header->resourceIDMap = (uint32_t*)malloc(idmap_size);
+ if (idmapData != NULL) {
+ header->resourceIDMap = (uint32_t*) malloc(idmapDataSize);
if (header->resourceIDMap == NULL) {
delete header;
return (mError = NO_MEMORY);
}
- memcpy((void*)header->resourceIDMap, idmap_data, idmap_size);
- header->resourceIDMapSize = idmap_size;
+ memcpy(header->resourceIDMap, idmapData, idmapDataSize);
+ header->resourceIDMapSize = idmapDataSize;
}
mHeaders.add(header);
const bool notDeviceEndian = htods(0xf0) != 0xf0;
LOAD_TABLE_NOISY(
- ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, asset=%p, copy=%d "
- "idmap=%p\n", data, size, cookie, asset, copyData, idmap));
+ ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, copy=%d "
+ "idmap=%p\n", data, dataSize, cookie, copyData, idmap));
if (copyData || notDeviceEndian) {
- header->ownedData = malloc(size);
+ header->ownedData = malloc(dataSize);
if (header->ownedData == NULL) {
return (mError=NO_MEMORY);
}
- memcpy(header->ownedData, data, size);
+ memcpy(header->ownedData, data, dataSize);
data = header->ownedData;
}
@@ -3265,10 +3403,10 @@ status_t ResTable::addInternal(const void* data, size_t size, const int32_t cook
// dtohl(header->header->header.size), header->header->header.size);
LOAD_TABLE_NOISY(ALOGV("Loading ResTable @%p:\n", header->header));
if (dtohs(header->header->header.headerSize) > header->size
- || header->size > size) {
+ || header->size > dataSize) {
ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n",
(int)dtohs(header->header->header.headerSize),
- (int)header->size, (int)size);
+ (int)header->size, (int)dataSize);
return (mError=BAD_TYPE);
}
if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) {
@@ -3313,16 +3451,8 @@ status_t ResTable::addInternal(const void* data, size_t size, const int32_t cook
dtohl(header->header->packageCount));
return (mError=BAD_TYPE);
}
- uint32_t idmap_id = 0;
- if (idmap != NULL) {
- uint32_t tmp;
- if (getIdmapPackageId(header->resourceIDMap,
- header->resourceIDMapSize,
- &tmp) == NO_ERROR) {
- idmap_id = tmp;
- }
- }
- if (parsePackage((ResTable_package*)chunk, header, idmap_id) != NO_ERROR) {
+
+ if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) {
return mError;
}
curPackage++;
@@ -3405,46 +3535,38 @@ bool ResTable::getResourceName(uint32_t resID, bool allowUtf8, resource_name* ou
ALOGW("Bad identifier when getting name for resource number 0x%08x", resID);
return false;
}
- if (grp->packages.size() > 0) {
- const Package* const package = grp->packages[0];
- const ResTable_type* type;
- const ResTable_entry* entry;
- ssize_t offset = getEntry(package, t, e, NULL, &type, &entry, NULL);
- if (offset <= 0) {
- return false;
- }
+ Entry entry;
+ status_t err = getEntry(grp, t, e, NULL, &entry);
+ if (err != NO_ERROR) {
+ return false;
+ }
- outName->package = grp->name.string();
- outName->packageLen = grp->name.size();
- if (allowUtf8) {
- outName->type8 = grp->basePackage->typeStrings.string8At(t, &outName->typeLen);
- outName->name8 = grp->basePackage->keyStrings.string8At(
- dtohl(entry->key.index), &outName->nameLen);
- } else {
- outName->type8 = NULL;
- outName->name8 = NULL;
- }
- if (outName->type8 == NULL) {
- outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen);
- // If we have a bad index for some reason, we should abort.
- if (outName->type == NULL) {
- return false;
- }
+ outName->package = grp->name.string();
+ outName->packageLen = grp->name.size();
+ if (allowUtf8) {
+ outName->type8 = entry.typeStr.string8(&outName->typeLen);
+ outName->name8 = entry.keyStr.string8(&outName->nameLen);
+ } else {
+ outName->type8 = NULL;
+ outName->name8 = NULL;
+ }
+ if (outName->type8 == NULL) {
+ outName->type = entry.typeStr.string16(&outName->typeLen);
+ // If we have a bad index for some reason, we should abort.
+ if (outName->type == NULL) {
+ return false;
}
- if (outName->name8 == NULL) {
- outName->name = grp->basePackage->keyStrings.stringAt(
- dtohl(entry->key.index), &outName->nameLen);
- // If we have a bad index for some reason, we should abort.
- if (outName->name == NULL) {
- return false;
- }
+ }
+ if (outName->name8 == NULL) {
+ outName->name = entry.keyStr.string16(&outName->nameLen);
+ // If we have a bad index for some reason, we should abort.
+ if (outName->name == NULL) {
+ return false;
}
-
- return true;
}
- return false;
+ return true;
}
ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density,
@@ -3471,15 +3593,6 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag
return BAD_INDEX;
}
- const Res_value* bestValue = NULL;
- const Package* bestPackage = NULL;
- ResTable_config bestItem;
- memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up
-
- if (outSpecFlags != NULL) *outSpecFlags = 0;
-
- // Look through all resource packages, starting with the most
- // recently added.
const PackageGroup* const grp = mPackageGroups[p];
if (grp == NULL) {
ALOGW("Bad identifier when getting value for resource number 0x%08x", resID);
@@ -3487,142 +3600,62 @@ ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag
}
// Allow overriding density
- const ResTable_config* desiredConfig = &mParams;
- ResTable_config* overrideConfig = NULL;
+ ResTable_config desiredConfig = mParams;
if (density > 0) {
- overrideConfig = (ResTable_config*) malloc(sizeof(ResTable_config));
- if (overrideConfig == NULL) {
- ALOGE("Couldn't malloc ResTable_config for overrides: %s", strerror(errno));
- return BAD_INDEX;
- }
- memcpy(overrideConfig, &mParams, sizeof(ResTable_config));
- overrideConfig->density = density;
- desiredConfig = overrideConfig;
- }
-
- ssize_t rc = BAD_VALUE;
- size_t ip = grp->packages.size();
- while (ip > 0) {
- ip--;
- int T = t;
- int E = e;
-
- const Package* const package = grp->packages[ip];
- if (package->header->resourceIDMap) {
- uint32_t overlayResID = 0x0;
- status_t retval = idmapLookup(package->header->resourceIDMap,
- package->header->resourceIDMapSize,
- resID, &overlayResID);
- if (retval == NO_ERROR && overlayResID != 0x0) {
- // for this loop iteration, this is the type and entry we really want
- ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID);
- T = Res_GETTYPE(overlayResID);
- E = Res_GETENTRY(overlayResID);
- } else {
- // resource not present in overlay package, continue with the next package
- continue;
- }
- }
-
- const ResTable_type* type;
- const ResTable_entry* entry;
- const Type* typeClass;
- ssize_t offset = getEntry(package, T, E, desiredConfig, &type, &entry, &typeClass);
- if (offset <= 0) {
- // No {entry, appropriate config} pair found in package. If this
- // package is an overlay package (ip != 0), this simply means the
- // overlay package did not specify a default.
- // Non-overlay packages are still required to provide a default.
- if (offset < 0 && ip == 0) {
- ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n",
- resID, T, E, ip, (int)offset);
- rc = offset;
- goto out;
- }
- continue;
- }
+ desiredConfig.density = density;
+ }
- if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) {
- if (!mayBeBag) {
- ALOGW("Requesting resource 0x%x failed because it is complex\n",
- resID);
- }
- continue;
- }
+ Entry entry;
+ status_t err = getEntry(grp, t, e, &desiredConfig, &entry);
+ if (err != NO_ERROR) {
+ ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) (error %d)\n",
+ resID, t, e, err);
+ return err;
+ }
- if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) {
- ALOGW("ResTable_item at %d is beyond type chunk data %d",
- (int)offset, dtohl(type->header.size));
- rc = BAD_TYPE;
- goto out;
+ if ((dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) != 0) {
+ if (!mayBeBag) {
+ ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID);
}
+ return BAD_VALUE;
+ }
- const Res_value* item =
- (const Res_value*)(((const uint8_t*)type) + offset);
- ResTable_config thisConfig;
- thisConfig.copyFromDtoH(type->config);
+ const Res_value* value = reinterpret_cast<const Res_value*>(
+ reinterpret_cast<const uint8_t*>(entry.entry) + entry.entry->size);
- if (outSpecFlags != NULL) {
- if (typeClass->typeSpecFlags != NULL) {
- *outSpecFlags |= dtohl(typeClass->typeSpecFlags[E]);
- } else {
- *outSpecFlags = -1;
- }
- }
+ outValue->size = dtohs(value->size);
+ outValue->res0 = value->res0;
+ outValue->dataType = value->dataType;
+ outValue->data = dtohl(value->data);
- if (bestPackage != NULL &&
- (bestItem.isMoreSpecificThan(thisConfig) || bestItem.diff(thisConfig) == 0)) {
- // Discard thisConfig not only if bestItem is more specific, but also if the two configs
- // are identical (diff == 0), or overlay packages will not take effect.
- continue;
- }
-
- bestItem = thisConfig;
- bestValue = item;
- bestPackage = package;
+ // The reference may be pointing to a resource in a shared library. These
+ // references have build-time generated package IDs. These ids may not match
+ // the actual package IDs of the corresponding packages in this ResTable.
+ // We need to fix the package ID based on a mapping.
+ if (grp->dynamicRefTable.lookupResourceValue(outValue) != NO_ERROR) {
+ ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data);
+ return BAD_VALUE;
}
- TABLE_NOISY(printf("Found result: package %p\n", bestPackage));
-
- if (bestValue) {
- outValue->size = dtohs(bestValue->size);
- outValue->res0 = bestValue->res0;
- outValue->dataType = bestValue->dataType;
- outValue->data = dtohl(bestValue->data);
-
- // The reference may be pointing to a resource in a shared library. These
- // references have build-time generated package IDs. These ids may not match
- // the actual package IDs of the corresponding packages in this ResTable.
- // We need to fix the package ID based on a mapping.
- status_t err = grp->dynamicRefTable.lookupResourceValue(outValue);
- if (err != NO_ERROR) {
- ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data);
- rc = BAD_VALUE;
- goto out;
- }
+ TABLE_NOISY(size_t len;
+ printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n",
+ entry.package->header->index,
+ outValue->dataType,
+ outValue->dataType == Res_value::TYPE_STRING
+ ? String8(entry.package->header->values.stringAt(
+ outValue->data, &len)).string()
+ : "",
+ outValue->data));
- if (outConfig != NULL) {
- *outConfig = bestItem;
- }
- TABLE_NOISY(size_t len;
- printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n",
- bestPackage->header->index,
- outValue->dataType,
- outValue->dataType == bestValue->TYPE_STRING
- ? String8(bestPackage->header->values.stringAt(
- outValue->data, &len)).string()
- : "",
- outValue->data));
- rc = bestPackage->header->index;
- goto out;
+ if (outSpecFlags != NULL) {
+ *outSpecFlags = entry.specFlags;
}
-out:
- if (overrideConfig != NULL) {
- free(overrideConfig);
+ if (outConfig != NULL) {
+ *outConfig = entry.config;
}
- return rc;
+ return entry.package->header->index;
}
ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex,
@@ -3721,29 +3754,25 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
PackageGroup* const grp = mPackageGroups[p];
if (grp == NULL) {
ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID);
- return false;
+ return BAD_INDEX;
}
- if (t >= (int)grp->typeCount) {
- ALOGW("Type identifier 0x%x is larger than type count 0x%x",
- t+1, (int)grp->typeCount);
+ const TypeList& typeConfigs = grp->types[t];
+ if (typeConfigs.isEmpty()) {
+ ALOGW("Type identifier 0x%x does not exist.", t+1);
return BAD_INDEX;
}
- const Package* const basePackage = grp->packages[0];
-
- const Type* const typeConfigs = basePackage->getType(t);
-
- const size_t NENTRY = typeConfigs->entryCount;
+ const size_t NENTRY = typeConfigs[0]->entryCount;
if (e >= (int)NENTRY) {
ALOGW("Entry identifier 0x%x is larger than entry count 0x%x",
- e, (int)typeConfigs->entryCount);
+ e, (int)typeConfigs[0]->entryCount);
return BAD_INDEX;
}
// First see if we've already computed this bag...
if (grp->bags) {
- bag_set** typeSet = grp->bags[t];
+ bag_set** typeSet = grp->bags->get(t);
if (typeSet) {
bag_set* set = typeSet[e];
if (set) {
@@ -3764,229 +3793,174 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
// Bag not found, we need to compute it!
if (!grp->bags) {
- grp->bags = (bag_set***)calloc(grp->typeCount, sizeof(bag_set*));
+ grp->bags = new ByteBucketArray<bag_set**>();
if (!grp->bags) return NO_MEMORY;
}
- bag_set** typeSet = grp->bags[t];
+ bag_set** typeSet = grp->bags->get(t);
if (!typeSet) {
typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*));
if (!typeSet) return NO_MEMORY;
- grp->bags[t] = typeSet;
+ grp->bags->set(t, typeSet);
}
// Mark that we are currently working on this one.
typeSet[e] = (bag_set*)0xFFFFFFFF;
- // This is what we are building.
- bag_set* set = NULL;
-
TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID));
- ResTable_config bestConfig;
- memset(&bestConfig, 0, sizeof(bestConfig));
+ // Now collect all bag attributes
+ Entry entry;
+ status_t err = getEntry(grp, t, e, &mParams, &entry);
+ if (err != NO_ERROR) {
+ return err;
+ }
- // Now collect all bag attributes from all packages.
- size_t ip = grp->packages.size();
- while (ip > 0) {
- ip--;
- int T = t;
- int E = e;
-
- const Package* const package = grp->packages[ip];
- if (package->header->resourceIDMap) {
- uint32_t overlayResID = 0x0;
- status_t retval = idmapLookup(package->header->resourceIDMap,
- package->header->resourceIDMapSize,
- resID, &overlayResID);
- if (retval == NO_ERROR && overlayResID != 0x0) {
- // for this loop iteration, this is the type and entry we really want
- ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID);
- T = Res_GETTYPE(overlayResID);
- E = Res_GETENTRY(overlayResID);
- } else {
- // resource not present in overlay package, continue with the next package
- continue;
- }
- }
+ const uint16_t entrySize = dtohs(entry.entry->size);
+ const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
+ ? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0;
+ const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
+ ? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0;
- const ResTable_type* type;
- const ResTable_entry* entry;
- const Type* typeClass;
- ALOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E);
- ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass);
- ALOGV("Resulting offset=%d\n", (int)offset);
- if (offset <= 0) {
- // No {entry, appropriate config} pair found in package. If this
- // package is an overlay package (ip != 0), this simply means the
- // overlay package did not specify a default.
- // Non-overlay packages are still required to provide a default.
- if (offset < 0 && ip == 0) {
- if (set) free(set);
- return offset;
- }
- continue;
- }
+ size_t N = count;
- if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) {
- ALOGW("Skipping entry 0x%x in package table %zu because it is not complex!\n",
- resID, ip);
- continue;
+ TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n",
+ entrySize, parent, count));
+
+ // If this map inherits from another, we need to start
+ // with its parent's values. Otherwise start out empty.
+ TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n",
+ entrySize, parent));
+
+ // This is what we are building.
+ bag_set* set = NULL;
+
+ if (parent) {
+ uint32_t resolvedParent = parent;
+
+ // Bags encode a parent reference without using the standard
+ // Res_value structure. That means we must always try to
+ // resolve a parent reference in case it is actually a
+ // TYPE_DYNAMIC_REFERENCE.
+ status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent);
+ if (err != NO_ERROR) {
+ ALOGE("Failed resolving bag parent id 0x%08x", parent);
+ return UNKNOWN_ERROR;
}
- if (set != NULL && !type->config.isBetterThan(bestConfig, NULL)) {
- continue;
+ const bag_entry* parentBag;
+ uint32_t parentTypeSpecFlags = 0;
+ const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags);
+ const size_t NT = ((NP >= 0) ? NP : 0) + N;
+ set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT);
+ if (set == NULL) {
+ return NO_MEMORY;
}
- bestConfig = type->config;
- if (set) {
- free(set);
- set = NULL;
+ if (NP > 0) {
+ memcpy(set+1, parentBag, NP*sizeof(bag_entry));
+ set->numAttrs = NP;
+ TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP));
+ } else {
+ TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n"));
+ set->numAttrs = 0;
}
+ set->availAttrs = NT;
+ set->typeSpecFlags = parentTypeSpecFlags;
+ } else {
+ set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N);
+ if (set == NULL) {
+ return NO_MEMORY;
+ }
+ set->numAttrs = 0;
+ set->availAttrs = N;
+ set->typeSpecFlags = 0;
+ }
- const uint16_t entrySize = dtohs(entry->size);
- const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
- ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0;
- const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
- ? dtohl(((const ResTable_map_entry*)entry)->count) : 0;
-
- size_t N = count;
+ set->typeSpecFlags |= entry.specFlags;
- TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n",
- entrySize, parent, count));
+ // Now merge in the new attributes...
+ size_t curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type))
+ + dtohs(entry.entry->size);
+ const ResTable_map* map;
+ bag_entry* entries = (bag_entry*)(set+1);
+ size_t curEntry = 0;
+ uint32_t pos = 0;
+ TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n",
+ set, entries, set->availAttrs));
+ while (pos < count) {
+ TABLE_NOISY(printf("Now at %p\n", (void*)curOff));
- // If this map inherits from another, we need to start
- // with its parent's values. Otherwise start out empty.
- TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n",
- entrySize, parent));
- if (parent) {
- uint32_t resolvedParent = parent;
+ if (curOff > (dtohl(entry.type->header.size)-sizeof(ResTable_map))) {
+ ALOGW("ResTable_map at %d is beyond type chunk data %d",
+ (int)curOff, dtohl(entry.type->header.size));
+ return BAD_TYPE;
+ }
+ map = (const ResTable_map*)(((const uint8_t*)entry.type) + curOff);
+ N++;
- // Bags encode a parent reference without using the standard
- // Res_value structure. That means we must always try to
- // resolve a parent reference in case it is actually a
- // TYPE_DYNAMIC_REFERENCE.
- status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent);
- if (err != NO_ERROR) {
- ALOGE("Failed resolving bag parent id 0x%08x", parent);
- return UNKNOWN_ERROR;
- }
+ const uint32_t newName = htodl(map->name.ident);
+ bool isInside;
+ uint32_t oldName = 0;
+ while ((isInside=(curEntry < set->numAttrs))
+ && (oldName=entries[curEntry].map.name.ident) < newName) {
+ TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n",
+ curEntry, entries[curEntry].map.name.ident));
+ curEntry++;
+ }
- const bag_entry* parentBag;
- uint32_t parentTypeSpecFlags = 0;
- const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags);
- const size_t NT = ((NP >= 0) ? NP : 0) + N;
- set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT);
- if (set == NULL) {
- return NO_MEMORY;
- }
- if (NP > 0) {
- memcpy(set+1, parentBag, NP*sizeof(bag_entry));
- set->numAttrs = NP;
- TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP));
- } else {
- TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n"));
- set->numAttrs = 0;
- }
- set->availAttrs = NT;
- set->typeSpecFlags = parentTypeSpecFlags;
+ if ((!isInside) || oldName != newName) {
+ // This is a new attribute... figure out what to do with it.
+ if (set->numAttrs >= set->availAttrs) {
+ // Need to alloc more memory...
+ const size_t newAvail = set->availAttrs+N;
+ set = (bag_set*)realloc(set,
+ sizeof(bag_set)
+ + sizeof(bag_entry)*newAvail);
+ if (set == NULL) {
+ return NO_MEMORY;
+ }
+ set->availAttrs = newAvail;
+ entries = (bag_entry*)(set+1);
+ TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n",
+ set, entries, set->availAttrs));
+ }
+ if (isInside) {
+ // Going in the middle, need to make space.
+ memmove(entries+curEntry+1, entries+curEntry,
+ sizeof(bag_entry)*(set->numAttrs-curEntry));
+ set->numAttrs++;
+ }
+ TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n",
+ curEntry, newName));
} else {
- set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N);
- if (set == NULL) {
- return NO_MEMORY;
- }
- set->numAttrs = 0;
- set->availAttrs = N;
- set->typeSpecFlags = 0;
+ TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n",
+ curEntry, oldName));
}
- if (typeClass->typeSpecFlags != NULL) {
- set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[E]);
- } else {
- set->typeSpecFlags = -1;
- }
-
- // Now merge in the new attributes...
- ssize_t curOff = offset;
- const ResTable_map* map;
- bag_entry* entries = (bag_entry*)(set+1);
- size_t curEntry = 0;
- uint32_t pos = 0;
- TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n",
- set, entries, set->availAttrs));
- while (pos < count) {
- TABLE_NOISY(printf("Now at %p\n", (void*)curOff));
-
- if ((size_t)curOff > (dtohl(type->header.size)-sizeof(ResTable_map))) {
- ALOGW("ResTable_map at %d is beyond type chunk data %d",
- (int)curOff, dtohl(type->header.size));
- return BAD_TYPE;
- }
- map = (const ResTable_map*)(((const uint8_t*)type) + curOff);
- N++;
-
- const uint32_t newName = htodl(map->name.ident);
- bool isInside;
- uint32_t oldName = 0;
- while ((isInside=(curEntry < set->numAttrs))
- && (oldName=entries[curEntry].map.name.ident) < newName) {
- TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n",
- curEntry, entries[curEntry].map.name.ident));
- curEntry++;
- }
-
- if ((!isInside) || oldName != newName) {
- // This is a new attribute... figure out what to do with it.
- if (set->numAttrs >= set->availAttrs) {
- // Need to alloc more memory...
- const size_t newAvail = set->availAttrs+N;
- set = (bag_set*)realloc(set,
- sizeof(bag_set)
- + sizeof(bag_entry)*newAvail);
- if (set == NULL) {
- return NO_MEMORY;
- }
- set->availAttrs = newAvail;
- entries = (bag_entry*)(set+1);
- TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n",
- set, entries, set->availAttrs));
- }
- if (isInside) {
- // Going in the middle, need to make space.
- memmove(entries+curEntry+1, entries+curEntry,
- sizeof(bag_entry)*(set->numAttrs-curEntry));
- set->numAttrs++;
- }
- TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n",
- curEntry, newName));
- } else {
- TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n",
- curEntry, oldName));
- }
+ bag_entry* cur = entries+curEntry;
- bag_entry* cur = entries+curEntry;
+ cur->stringBlock = entry.package->header->index;
+ cur->map.name.ident = newName;
+ cur->map.value.copyFrom_dtoh(map->value);
+ status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value);
+ if (err != NO_ERROR) {
+ ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data);
+ return UNKNOWN_ERROR;
+ }
- cur->stringBlock = package->header->index;
- cur->map.name.ident = newName;
- cur->map.value.copyFrom_dtoh(map->value);
- status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value);
- if (err != NO_ERROR) {
- ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data);
- return UNKNOWN_ERROR;
- }
+ TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n",
+ curEntry, cur, cur->stringBlock, cur->map.name.ident,
+ cur->map.value.dataType, cur->map.value.data));
- TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n",
- curEntry, cur, cur->stringBlock, cur->map.name.ident,
- cur->map.value.dataType, cur->map.value.data));
+ // On to the next!
+ curEntry++;
+ pos++;
+ const size_t size = dtohs(map->value.size);
+ curOff += size + sizeof(*map)-sizeof(map->value);
+ };
- // On to the next!
- curEntry++;
- pos++;
- const size_t size = dtohs(map->value.size);
- curOff += size + sizeof(*map)-sizeof(map->value);
- };
- if (curEntry > set->numAttrs) {
- set->numAttrs = curEntry;
- }
+ if (curEntry > set->numAttrs) {
+ set->numAttrs = curEntry;
}
// And this is it...
@@ -4154,80 +4128,63 @@ nope:
continue;
}
- const ssize_t ti = group->basePackage->typeStrings.indexOfString(type, typeLen);
+ const ssize_t ti = group->findType16(type, typeLen);
if (ti < 0) {
TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string()));
continue;
}
- const ssize_t ei = group->basePackage->keyStrings.indexOfString(name, nameLen);
- if (ei < 0) {
- TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string()));
- continue;
- }
-
- TABLE_NOISY(printf("Search indices: type=%d, name=%d\n", ti, ei));
-
- const Type* const typeConfigs = group->packages[0]->getType(ti);
- if (typeConfigs == NULL || typeConfigs->configs.size() <= 0) {
- TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n",
+ const TypeList& typeList = group->types[ti];
+ if (typeList.isEmpty()) {
+ TABLE_NOISY(printf("Expected type structure not found in package %s for index %d\n",
String8(group->name).string(), ti));
+ continue;
}
- size_t NTC = typeConfigs->configs.size();
- for (size_t tci=0; tci<NTC; tci++) {
- const ResTable_type* const ty = typeConfigs->configs[tci];
- const uint32_t typeOffset = dtohl(ty->entriesStart);
-
- const uint8_t* const end = ((const uint8_t*)ty) + dtohl(ty->header.size);
- const uint32_t* const eindex = (const uint32_t*)
- (((const uint8_t*)ty) + dtohs(ty->header.headerSize));
-
- const size_t NE = dtohl(ty->entryCount);
- for (size_t i=0; i<NE; i++) {
- uint32_t offset = dtohl(eindex[i]);
- if (offset == ResTable_type::NO_ENTRY) {
- continue;
- }
-
- offset += typeOffset;
+ const size_t typeCount = typeList.size();
+ for (size_t i = 0; i < typeCount; i++) {
+ const Type* t = typeList[i];
+ const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen);
+ if (ei < 0) {
+ continue;
+ }
- if (offset > (dtohl(ty->header.size)-sizeof(ResTable_entry))) {
- ALOGW("ResTable_entry at %d is beyond type chunk data %d",
- offset, dtohl(ty->header.size));
- return 0;
- }
- if ((offset&0x3) != 0) {
- ALOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s",
- (int)offset, (int)group->id, (int)ti+1, (int)i,
- String8(package, packageLen).string(),
- String8(type, typeLen).string(),
- String8(name, nameLen).string());
- return 0;
- }
+ const size_t configCount = t->configs.size();
+ for (size_t j = 0; j < configCount; j++) {
+ const TypeVariant tv(t->configs[j]);
+ for (TypeVariant::iterator iter = tv.beginEntries();
+ iter != tv.endEntries();
+ iter++) {
+ const ResTable_entry* entry = *iter;
+ if (entry == NULL) {
+ continue;
+ }
- const ResTable_entry* const entry = (const ResTable_entry*)
- (((const uint8_t*)ty) + offset);
- if (dtohs(entry->size) < sizeof(*entry)) {
- ALOGW("ResTable_entry size %d is too small", dtohs(entry->size));
- return BAD_TYPE;
- }
+ if (dtohl(entry->key.index) == (size_t) ei) {
+ uint32_t resId = Res_MAKEID(group->id - 1, ti, iter.index());
+ if (outTypeSpecFlags) {
+ Entry result;
+ if (getEntry(group, ti, iter.index(), NULL, &result) != NO_ERROR) {
+ ALOGW("Failed to find spec flags for %s:%s/%s (0x%08x)",
+ String8(group->name).string(),
+ String8(String16(type, typeLen)).string(),
+ String8(String16(name, nameLen)).string(),
+ resId);
+ return 0;
+ }
+ *outTypeSpecFlags = result.specFlags;
- TABLE_SUPER_NOISY(printf("Looking at entry #%d: want str %d, have %d\n",
- i, ei, dtohl(entry->key.index)));
- if (dtohl(entry->key.index) == (size_t)ei) {
- if (outTypeSpecFlags) {
- *outTypeSpecFlags = typeConfigs->typeSpecFlags[i];
- if (fakePublic) {
- *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC;
+ if (fakePublic) {
+ *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC;
+ }
}
+ return resId;
}
- return Res_MAKEID(group->id-1, ti, i);
}
}
}
+ break;
}
-
return 0;
}
@@ -5260,6 +5217,18 @@ uint32_t ResTable::getBasePackageId(size_t idx) const
return mPackageGroups[idx]->id;
}
+uint32_t ResTable::getLastTypeIdForPackage(size_t idx) const
+{
+ if (mError != NO_ERROR) {
+ return 0;
+ }
+ LOG_FATAL_IF(idx >= mPackageGroups.size(),
+ "Requested package index %d past package count %d",
+ (int)idx, (int)mPackageGroups.size());
+ const PackageGroup* const group = mPackageGroups[idx];
+ return group->largestTypeId;
+}
+
size_t ResTable::getTableCount() const
{
return mHeaders.size();
@@ -5292,32 +5261,31 @@ const DynamicRefTable* ResTable::getDynamicRefTableForCookie(int32_t cookie) con
void ResTable::getConfigurations(Vector<ResTable_config>* configs) const
{
- const size_t I = mPackageGroups.size();
- for (size_t i=0; i<I; i++) {
+ const size_t packageCount = mPackageGroups.size();
+ for (size_t i = 0; i < packageCount; i++) {
const PackageGroup* packageGroup = mPackageGroups[i];
- const size_t J = packageGroup->packages.size();
- for (size_t j=0; j<J; j++) {
- const Package* package = packageGroup->packages[j];
- const size_t K = package->types.size();
- for (size_t k=0; k<K; k++) {
- const Type* type = package->types[k];
- if (type == NULL) continue;
- const size_t L = type->configs.size();
- for (size_t l=0; l<L; l++) {
- const ResTable_type* config = type->configs[l];
+ const size_t typeCount = packageGroup->types.size();
+ for (size_t j = 0; j < typeCount; j++) {
+ const TypeList& typeList = packageGroup->types[j];
+ const size_t numTypes = typeList.size();
+ for (size_t k = 0; k < numTypes; k++) {
+ const Type* type = typeList[k];
+ const size_t numConfigs = type->configs.size();
+ for (size_t m = 0; m < numConfigs; m++) {
+ const ResTable_type* config = type->configs[m];
ResTable_config cfg;
memset(&cfg, 0, sizeof(ResTable_config));
cfg.copyFromDtoH(config->config);
// only insert unique
- const size_t M = configs->size();
- size_t m;
- for (m=0; m<M; m++) {
- if (0 == (*configs)[m].compare(cfg)) {
+ const size_t N = configs->size();
+ size_t n;
+ for (n = 0; n < N; n++) {
+ if (0 == (*configs)[n].compare(cfg)) {
break;
}
}
// if we didn't find it
- if (m == M) {
+ if (n == N) {
configs->add(cfg);
}
}
@@ -5350,122 +5318,180 @@ void ResTable::getLocales(Vector<String8>* locales) const
}
}
-ssize_t ResTable::getEntry(
- const Package* package, int typeIndex, int entryIndex,
- const ResTable_config* config,
- const ResTable_type** outType, const ResTable_entry** outEntry,
- const Type** outTypeClass) const
-{
- ALOGV("Getting entry from package %p\n", package);
- const ResTable_package* const pkg = package->package;
+StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index)
+ : mPool(pool), mIndex(index) {}
- const Type* allTypes = package->getType(typeIndex);
- ALOGV("allTypes=%p\n", allTypes);
- if (allTypes == NULL) {
- ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex);
- return 0;
+StringPoolRef::StringPoolRef()
+ : mPool(NULL), mIndex(0) {}
+
+const char* StringPoolRef::string8(size_t* outLen) const {
+ if (mPool != NULL) {
+ return mPool->string8At(mIndex, outLen);
}
+ if (outLen != NULL) {
+ *outLen = 0;
+ }
+ return NULL;
+}
- if ((size_t)entryIndex >= allTypes->entryCount) {
- ALOGW("getEntry failing because entryIndex %d is beyond type entryCount %d",
- entryIndex, (int)allTypes->entryCount);
+const char16_t* StringPoolRef::string16(size_t* outLen) const {
+ if (mPool != NULL) {
+ return mPool->stringAt(mIndex, outLen);
+ }
+ if (outLen != NULL) {
+ *outLen = 0;
+ }
+ return NULL;
+}
+
+status_t ResTable::getEntry(
+ const PackageGroup* packageGroup, int typeIndex, int entryIndex,
+ const ResTable_config* config,
+ Entry* outEntry) const
+{
+ const TypeList& typeList = packageGroup->types[typeIndex];
+ if (typeList.isEmpty()) {
+ ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex);
return BAD_TYPE;
}
- const ResTable_type* type = NULL;
- uint32_t offset = ResTable_type::NO_ENTRY;
+ const ResTable_type* bestType = NULL;
+ uint32_t bestOffset = ResTable_type::NO_ENTRY;
+ const Package* bestPackage = NULL;
+ uint32_t specFlags = 0;
+ uint8_t actualTypeIndex = typeIndex;
ResTable_config bestConfig;
- memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up
-
- const size_t NT = allTypes->configs.size();
- for (size_t i=0; i<NT; i++) {
- const ResTable_type* const thisType = allTypes->configs[i];
- if (thisType == NULL) continue;
-
- ResTable_config thisConfig;
- thisConfig.copyFromDtoH(thisType->config);
+ memset(&bestConfig, 0, sizeof(bestConfig));
- TABLE_GETENTRY(ALOGI("Match entry 0x%x in type 0x%x (sz 0x%x): %s\n",
- entryIndex, typeIndex+1, dtohl(thisType->config.size),
- thisConfig.toString().string()));
+ // Iterate over the Types of each package.
+ const size_t typeCount = typeList.size();
+ for (size_t i = 0; i < typeCount; i++) {
+ const Type* const typeSpec = typeList[i];
+
+ int realEntryIndex = entryIndex;
+ int realTypeIndex = typeIndex;
+ bool currentTypeIsOverlay = false;
+
+ // Runtime overlay packages provide a mapping of app resource
+ // ID to package resource ID.
+ if (typeSpec->idmapEntries.hasEntries()) {
+ uint16_t overlayEntryIndex;
+ if (typeSpec->idmapEntries.lookup(entryIndex, &overlayEntryIndex) != NO_ERROR) {
+ // No such mapping exists
+ continue;
+ }
+ realEntryIndex = overlayEntryIndex;
+ realTypeIndex = typeSpec->idmapEntries.overlayTypeId() - 1;
+ currentTypeIsOverlay = true;
+ }
- // Check to make sure this one is valid for the current parameters.
- if (config && !thisConfig.match(*config)) {
- TABLE_GETENTRY(ALOGI("Does not match config!\n"));
+ if (static_cast<size_t>(realEntryIndex) >= typeSpec->entryCount) {
+ ALOGW("For resource 0x%08x, entry index(%d) is beyond type entryCount(%d)",
+ Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex),
+ entryIndex, static_cast<int>(typeSpec->entryCount));
+ // We should normally abort here, but some legacy apps declare
+ // resources in the 'android' package (old bug in AAPT).
continue;
}
- // Check if there is the desired entry in this type.
+ // Aggregate all the flags for each package that defines this entry.
+ if (typeSpec->typeSpecFlags != NULL) {
+ specFlags |= dtohl(typeSpec->typeSpecFlags[realEntryIndex]);
+ } else {
+ specFlags = -1;
+ }
- const uint8_t* const end = ((const uint8_t*)thisType)
- + dtohl(thisType->header.size);
- const uint32_t* const eindex = (const uint32_t*)
- (((const uint8_t*)thisType) + dtohs(thisType->header.headerSize));
+ const size_t numConfigs = typeSpec->configs.size();
+ for (size_t c = 0; c < numConfigs; c++) {
+ const ResTable_type* const thisType = typeSpec->configs[c];
+ if (thisType == NULL) {
+ continue;
+ }
- uint32_t thisOffset = dtohl(eindex[entryIndex]);
- if (thisOffset == ResTable_type::NO_ENTRY) {
- TABLE_GETENTRY(ALOGI("Skipping because it is not defined!\n"));
- continue;
- }
+ ResTable_config thisConfig;
+ thisConfig.copyFromDtoH(thisType->config);
- if (type != NULL) {
- // Check if this one is less specific than the last found. If so,
- // we will skip it. We check starting with things we most care
- // about to those we least care about.
- if (!thisConfig.isBetterThan(bestConfig, config)) {
- TABLE_GETENTRY(ALOGI("This config is worse than last!\n"));
+ // Check to make sure this one is valid for the current parameters.
+ if (config != NULL && !thisConfig.match(*config)) {
+ continue;
+ }
+
+ // Check if there is the desired entry in this type.
+ const uint8_t* const end = reinterpret_cast<const uint8_t*>(thisType)
+ + dtohl(thisType->header.size);
+ const uint32_t* const eindex = reinterpret_cast<const uint32_t*>(
+ reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize));
+
+ uint32_t thisOffset = dtohl(eindex[realEntryIndex]);
+ if (thisOffset == ResTable_type::NO_ENTRY) {
+ // There is no entry for this index and configuration.
continue;
}
- }
- type = thisType;
- offset = thisOffset;
- bestConfig = thisConfig;
- TABLE_GETENTRY(ALOGI("Best entry so far -- using it!\n"));
- if (!config) break;
+ if (bestType != NULL) {
+ // Check if this one is less specific than the last found. If so,
+ // we will skip it. We check starting with things we most care
+ // about to those we least care about.
+ if (!thisConfig.isBetterThan(bestConfig, config)) {
+ if (!currentTypeIsOverlay || thisConfig.compare(bestConfig) != 0) {
+ continue;
+ }
+ }
+ }
+
+ bestType = thisType;
+ bestOffset = thisOffset;
+ bestConfig = thisConfig;
+ bestPackage = typeSpec->package;
+ actualTypeIndex = realTypeIndex;
+
+ // If no config was specified, any type will do, so skip
+ if (config == NULL) {
+ break;
+ }
+ }
}
- if (type == NULL) {
- TABLE_GETENTRY(ALOGI("No value found for requested entry!\n"));
+ if (bestType == NULL) {
return BAD_INDEX;
}
- offset += dtohl(type->entriesStart);
- TABLE_NOISY(ALOGD("Looking in resource table %p, typeOff=%p, offset=%p",
- package->header->header, (void*)(((const char*)type)-((const char*)package->header->header)),
- (void*)offset));
+ bestOffset += dtohl(bestType->entriesStart);
- if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) {
+ if (bestOffset > (dtohl(bestType->header.size)-sizeof(ResTable_entry))) {
ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x",
- offset, dtohl(type->header.size));
+ bestOffset, dtohl(bestType->header.size));
return BAD_TYPE;
}
- if ((offset&0x3) != 0) {
- ALOGW("ResTable_entry at 0x%x is not on an integer boundary",
- offset);
+ if ((bestOffset & 0x3) != 0) {
+ ALOGW("ResTable_entry at 0x%x is not on an integer boundary", bestOffset);
return BAD_TYPE;
}
- const ResTable_entry* const entry = (const ResTable_entry*)
- (((const uint8_t*)type) + offset);
+ const ResTable_entry* const entry = reinterpret_cast<const ResTable_entry*>(
+ reinterpret_cast<const uint8_t*>(bestType) + bestOffset);
if (dtohs(entry->size) < sizeof(*entry)) {
ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size));
return BAD_TYPE;
}
- *outType = type;
- *outEntry = entry;
- if (outTypeClass != NULL) {
- *outTypeClass = allTypes;
+ if (outEntry != NULL) {
+ outEntry->entry = entry;
+ outEntry->config = bestConfig;
+ outEntry->type = bestType;
+ outEntry->specFlags = specFlags;
+ outEntry->package = bestPackage;
+ outEntry->typeStr = StringPoolRef(&bestPackage->typeStrings, actualTypeIndex - bestPackage->typeIdOffset);
+ outEntry->keyStr = StringPoolRef(&bestPackage->keyStrings, dtohl(entry->key.index));
}
- return offset + dtohs(entry->size);
+ return NO_ERROR;
}
status_t ResTable::parsePackage(const ResTable_package* const pkg,
- const Header* const header, uint32_t idmap_id)
+ const Header* const header)
{
const uint8_t* base = (const uint8_t*)pkg;
- status_t err = validate_chunk(&pkg->header, sizeof(*pkg),
+ status_t err = validate_chunk(&pkg->header, sizeof(*pkg) - sizeof(pkg->typeIdOffset),
header->dataEnd, "ResTable_package");
if (err != NO_ERROR) {
return (mError=err);
@@ -5494,89 +5520,88 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
return (mError=BAD_TYPE);
}
- Package* package = NULL;
- PackageGroup* group = NULL;
- uint32_t id = idmap_id != 0 ? idmap_id : dtohl(pkg->id);
- // If at this point id == 0, pkg is an overlay package without a
- // corresponding idmap. During regular usage, overlay packages are
- // always loaded alongside their idmaps, but during idmap creation
- // the package is temporarily loaded by itself.
- if (id < 256) {
-
- package = new Package(this, header, pkg);
- if (package == NULL) {
- return (mError=NO_MEMORY);
- }
-
- if (idmap_id == 0) {
- err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
- header->dataEnd-(base+dtohl(pkg->typeStrings)));
- if (err != NO_ERROR) {
- delete group;
- delete package;
- return (mError=err);
- }
+ uint32_t id = dtohl(pkg->id);
+ KeyedVector<uint8_t, IdmapEntries> idmapEntries;
- err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
- header->dataEnd-(base+dtohl(pkg->keyStrings)));
- if (err != NO_ERROR) {
- delete group;
- delete package;
- return (mError=err);
- }
+ if (header->resourceIDMap != NULL) {
+ uint8_t targetPackageId = 0;
+ status_t err = parseIdmap(header->resourceIDMap, header->resourceIDMapSize, &targetPackageId, &idmapEntries);
+ if (err != NO_ERROR) {
+ ALOGW("Overlay is broken");
+ return (mError=err);
}
+ id = targetPackageId;
+ }
- if (id == 0) {
- // This is a library so assign an ID
- id = mNextPackageId++;
- }
+ if (id >= 256) {
+ LOG_ALWAYS_FATAL("Package id out of range");
+ return NO_ERROR;
+ } else if (id == 0) {
+ // This is a library so assign an ID
+ id = mNextPackageId++;
+ }
- size_t idx = mPackageMap[id];
- if (idx == 0) {
- idx = mPackageGroups.size()+1;
+ PackageGroup* group = NULL;
+ Package* package = new Package(this, header, pkg);
+ if (package == NULL) {
+ return (mError=NO_MEMORY);
+ }
- char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)];
- strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t));
- group = new PackageGroup(this, String16(tmpName), id);
- if (group == NULL) {
- delete package;
- return (mError=NO_MEMORY);
- }
+ err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
+ header->dataEnd-(base+dtohl(pkg->typeStrings)));
+ if (err != NO_ERROR) {
+ delete group;
+ delete package;
+ return (mError=err);
+ }
- //printf("Adding new package id %d at index %d\n", id, idx);
- err = mPackageGroups.add(group);
- if (err < NO_ERROR) {
- return (mError=err);
- }
- group->basePackage = package;
+ err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
+ header->dataEnd-(base+dtohl(pkg->keyStrings)));
+ if (err != NO_ERROR) {
+ delete group;
+ delete package;
+ return (mError=err);
+ }
- mPackageMap[id] = (uint8_t)idx;
+ size_t idx = mPackageMap[id];
+ if (idx == 0) {
+ idx = mPackageGroups.size() + 1;
- // Find all packages that reference this package
- size_t N = mPackageGroups.size();
- for (size_t i = 0; i < N; i++) {
- mPackageGroups[i]->dynamicRefTable.addMapping(
- group->name, static_cast<uint8_t>(group->id));
- }
- } else {
- group = mPackageGroups.itemAt(idx-1);
- if (group == NULL) {
- return (mError=UNKNOWN_ERROR);
- }
+ char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)];
+ strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t));
+ group = new PackageGroup(this, String16(tmpName), id);
+ if (group == NULL) {
+ delete package;
+ return (mError=NO_MEMORY);
}
- err = group->packages.add(package);
+
+ //printf("Adding new package id %d at index %d\n", id, idx);
+ err = mPackageGroups.add(group);
if (err < NO_ERROR) {
return (mError=err);
}
+
+ mPackageMap[id] = static_cast<uint8_t>(idx);
+
+ // Find all packages that reference this package
+ size_t N = mPackageGroups.size();
+ for (size_t i = 0; i < N; i++) {
+ mPackageGroups[i]->dynamicRefTable.addMapping(
+ group->name, static_cast<uint8_t>(group->id));
+ }
} else {
- LOG_ALWAYS_FATAL("Package id out of range");
- return NO_ERROR;
+ group = mPackageGroups.itemAt(idx - 1);
+ if (group == NULL) {
+ return (mError=UNKNOWN_ERROR);
+ }
}
+ err = group->packages.add(package);
+ if (err < NO_ERROR) {
+ return (mError=err);
+ }
// Iterate through all chunks.
- size_t curPackage = 0;
-
const ResChunk_header* chunk =
(const ResChunk_header*)(((const uint8_t*)pkg)
+ dtohs(pkg->header.headerSize));
@@ -5597,6 +5622,7 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
}
const size_t typeSpecSize = dtohl(typeSpec->header.size);
+ const size_t newEntryCount = dtohl(typeSpec->entryCount);
LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n",
(void*)(base-(const uint8_t*)chunk),
@@ -5605,12 +5631,11 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
(void*)typeSpecSize));
// look for block overrun or int overflow when multiplying by 4
if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t))
- || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount))
+ || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*newEntryCount)
> typeSpecSize)) {
ALOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.",
- (void*)(dtohs(typeSpec->header.headerSize)
- +(sizeof(uint32_t)*dtohl(typeSpec->entryCount))),
- (void*)typeSpecSize);
+ (void*)(dtohs(typeSpec->header.headerSize) + (sizeof(uint32_t)*newEntryCount)),
+ (void*)typeSpecSize);
return (mError=BAD_TYPE);
}
@@ -5619,21 +5644,36 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
return (mError=BAD_TYPE);
}
- while (package->types.size() < typeSpec->id) {
- package->types.add(NULL);
- }
- Type* t = package->types[typeSpec->id-1];
- if (t == NULL) {
- t = new Type(header, package, dtohl(typeSpec->entryCount));
- package->types.editItemAt(typeSpec->id-1) = t;
- } else if (dtohl(typeSpec->entryCount) != t->entryCount) {
- ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d",
- (int)dtohl(typeSpec->entryCount), (int)t->entryCount);
- return (mError=BAD_TYPE);
+ if (newEntryCount > 0) {
+ uint8_t typeIndex = typeSpec->id - 1;
+ ssize_t idmapIndex = idmapEntries.indexOfKey(typeSpec->id);
+ if (idmapIndex >= 0) {
+ typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1;
+ }
+
+ TypeList& typeList = group->types.editItemAt(typeIndex);
+ if (!typeList.isEmpty()) {
+ const Type* existingType = typeList[0];
+ if (existingType->entryCount != newEntryCount && idmapIndex < 0) {
+ ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d",
+ (int) newEntryCount, (int) existingType->entryCount);
+ // We should normally abort here, but some legacy apps declare
+ // resources in the 'android' package (old bug in AAPT).
+ }
+ }
+
+ Type* t = new Type(header, package, newEntryCount);
+ t->typeSpec = typeSpec;
+ t->typeSpecFlags = (const uint32_t*)(
+ ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize));
+ if (idmapIndex >= 0) {
+ t->idmapEntries = idmapEntries[idmapIndex];
+ }
+ typeList.add(t);
+ group->largestTypeId = max(group->largestTypeId, typeSpec->id);
+ } else {
+ ALOGV("Skipping empty ResTable_typeSpec for type %d", typeSpec->id);
}
- t->typeSpecFlags = (const uint32_t*)(
- ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize));
- t->typeSpec = typeSpec;
} else if (ctype == RES_TABLE_TYPE_TYPE) {
const ResTable_type* type = (const ResTable_type*)(chunk);
@@ -5644,50 +5684,69 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
}
const uint32_t typeSize = dtohl(type->header.size);
+ const size_t newEntryCount = dtohl(type->entryCount);
LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n",
(void*)(base-(const uint8_t*)chunk),
dtohs(type->header.type),
dtohs(type->header.headerSize),
(void*)typeSize));
- if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount))
- > typeSize) {
+ if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*newEntryCount)
+ > typeSize) {
ALOGW("ResTable_type entry index to %p extends beyond chunk end 0x%x.",
- (void*)(dtohs(type->header.headerSize)
- +(sizeof(uint32_t)*dtohl(type->entryCount))),
- typeSize);
+ (void*)(dtohs(type->header.headerSize) + (sizeof(uint32_t)*newEntryCount)),
+ typeSize);
return (mError=BAD_TYPE);
}
- if (dtohl(type->entryCount) != 0
+
+ if (newEntryCount != 0
&& dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) {
ALOGW("ResTable_type entriesStart at 0x%x extends beyond chunk end 0x%x.",
dtohl(type->entriesStart), typeSize);
return (mError=BAD_TYPE);
}
+
if (type->id == 0) {
ALOGW("ResTable_type has an id of 0.");
return (mError=BAD_TYPE);
}
- while (package->types.size() < type->id) {
- package->types.add(NULL);
- }
- Type* t = package->types[type->id-1];
- if (t == NULL) {
- t = new Type(header, package, dtohl(type->entryCount));
- package->types.editItemAt(type->id-1) = t;
- } else if (dtohl(type->entryCount) != t->entryCount) {
- ALOGW("ResTable_type entry count inconsistent: given %d, previously %d",
- (int)dtohl(type->entryCount), (int)t->entryCount);
- return (mError=BAD_TYPE);
+ if (newEntryCount > 0) {
+ uint8_t typeIndex = type->id - 1;
+ ssize_t idmapIndex = idmapEntries.indexOfKey(type->id);
+ if (idmapIndex >= 0) {
+ typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1;
+ }
+
+ TypeList& typeList = group->types.editItemAt(typeIndex);
+ if (typeList.isEmpty()) {
+ ALOGE("No TypeSpec for type %d", type->id);
+ return (mError=BAD_TYPE);
+ }
+
+ Type* t = typeList.editItemAt(typeList.size() - 1);
+ if (newEntryCount != t->entryCount) {
+ ALOGE("ResTable_type entry count inconsistent: given %d, previously %d",
+ (int)newEntryCount, (int)t->entryCount);
+ return (mError=BAD_TYPE);
+ }
+
+ if (t->package != package) {
+ ALOGE("No TypeSpec for type %d", type->id);
+ return (mError=BAD_TYPE);
+ }
+
+ t->configs.add(type);
+
+ TABLE_GETENTRY(
+ ResTable_config thisConfig;
+ thisConfig.copyFromDtoH(type->config);
+ ALOGI("Adding config to type %d: %s\n",
+ type->id, thisConfig.toString().string()));
+ } else {
+ ALOGV("Skipping empty ResTable_type for type %d", type->id);
}
- TABLE_GETENTRY(
- ResTable_config thisConfig;
- thisConfig.copyFromDtoH(type->config);
- ALOGI("Adding config to type %d: %s\n",
- type->id, thisConfig.toString().string()));
- t->configs.add(type);
} else if (ctype == RES_TABLE_LIBRARY_TYPE) {
if (group->dynamicRefTable.entries().size() == 0) {
status_t err = group->dynamicRefTable.load((const ResTable_lib_header*) chunk);
@@ -5714,10 +5773,6 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg,
(((const uint8_t*)chunk) + csize);
}
- if (group->typeCount == 0) {
- group->typeCount = package->types.size();
- }
-
return NO_ERROR;
}
@@ -5818,6 +5873,12 @@ status_t DynamicRefTable::lookupResourceValue(Res_value* value) const {
return NO_ERROR;
}
+struct IdmapTypeMap {
+ ssize_t overlayTypeId;
+ size_t entryOffset;
+ Vector<uint32_t> entryMap;
+};
+
status_t ResTable::createIdmap(const ResTable& overlay,
uint32_t targetCrc, uint32_t overlayCrc,
const char* targetPath, const char* overlayPath,
@@ -5828,41 +5889,46 @@ status_t ResTable::createIdmap(const ResTable& overlay,
ALOGW("idmap: target package has no package groups, cannot create idmap\n");
return UNKNOWN_ERROR;
}
+
if (mPackageGroups[0]->packages.size() == 0) {
ALOGW("idmap: target package has no packages in its first package group, "
"cannot create idmap\n");
return UNKNOWN_ERROR;
}
- Vector<Vector<uint32_t> > map;
+ KeyedVector<uint8_t, IdmapTypeMap> map;
+
// overlaid packages are assumed to contain only one package group
const PackageGroup* pg = mPackageGroups[0];
- const Package* pkg = pg->packages[0];
- size_t typeCount = pkg->types.size();
- // starting size is header + first item (number of types in map)
- *outSize = (IDMAP_HEADER_SIZE + 1) * sizeof(uint32_t);
+
+ // starting size is header
+ *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES;
+
+ // target package id and number of types in map
+ *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 uint32_t pkg_id = pkg->package->id << 24;
-
- for (size_t typeIndex = 0; typeIndex < typeCount; ++typeIndex) {
- ssize_t first = -1;
- ssize_t last = -1;
- const Type* typeConfigs = pkg->getType(typeIndex);
- ssize_t mapIndex = map.add();
- if (mapIndex < 0) {
- return NO_MEMORY;
+
+ for (size_t typeIndex = 0; typeIndex < pg->types.size(); ++typeIndex) {
+ const TypeList& typeList = pg->types[typeIndex];
+ if (typeList.isEmpty()) {
+ continue;
}
- Vector<uint32_t>& vector = map.editItemAt(mapIndex);
+
+ const Type* typeConfigs = typeList[0];
+
+ IdmapTypeMap typeMap;
+ typeMap.overlayTypeId = -1;
+ typeMap.entryOffset = 0;
+
for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) {
- uint32_t resID = pkg_id
- | (0x00ff0000 & ((typeIndex+1)<<16))
- | (0x0000ffff & (entryIndex));
+ uint32_t resID = Res_MAKEID(pg->id - 1, typeIndex, entryIndex);
resource_name resName;
if (!this->getResourceName(resID, false, &resName)) {
- ALOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID);
- // add dummy value, or trimming leading/trailing zeroes later will fail
- vector.push(0);
+ if (typeMap.entryMap.isEmpty()) {
+ typeMap.entryOffset++;
+ }
continue;
}
@@ -5874,49 +5940,55 @@ status_t ResTable::createIdmap(const ResTable& overlay,
overlayType.size(),
overlayPackage.string(),
overlayPackage.size());
- if (overlayResID != 0) {
- overlayResID = pkg_id | (0x00ffffff & overlayResID);
- last = Res_GETENTRY(resID);
- if (first == -1) {
- first = Res_GETENTRY(resID);
+ if (overlayResID == 0) {
+ if (typeMap.entryMap.isEmpty()) {
+ typeMap.entryOffset++;
}
+ continue;
}
- vector.push(overlayResID);
-#if 0
- if (overlayResID != 0) {
- ALOGD("%s/%s 0x%08x -> 0x%08x\n",
- String8(String16(resName.type)).string(),
- String8(String16(resName.name)).string(),
- resID, overlayResID);
+
+ if (typeMap.overlayTypeId == -1) {
+ typeMap.overlayTypeId = Res_GETTYPE(overlayResID) + 1;
+ }
+
+ if (Res_GETTYPE(overlayResID) + 1 != static_cast<size_t>(typeMap.overlayTypeId)) {
+ ALOGE("idmap: can't mix type ids in entry map. Resource 0x%08x maps to 0x%08x"
+ " but entries should map to resources of type %02x",
+ resID, overlayResID, typeMap.overlayTypeId);
+ return BAD_TYPE;
+ }
+
+ if (typeMap.entryOffset + typeMap.entryMap.size() < entryIndex) {
+ // Resize to accomodate this entry and the 0's in between.
+ if (typeMap.entryMap.resize((entryIndex - typeMap.entryOffset) + 1) < 0) {
+ return NO_MEMORY;
+ }
+ typeMap.entryMap.editTop() = Res_GETENTRY(overlayResID);
+ } else {
+ typeMap.entryMap.add(Res_GETENTRY(overlayResID));
}
-#endif
}
- if (first != -1) {
- // shave off trailing entries which lack overlay values
- const size_t last_past_one = last + 1;
- if (last_past_one < vector.size()) {
- vector.removeItemsAt(last_past_one, vector.size() - last_past_one);
+ if (!typeMap.entryMap.isEmpty()) {
+ if (map.add(static_cast<uint8_t>(typeIndex), typeMap) < 0) {
+ return NO_MEMORY;
}
- // shave off leading entries which lack overlay values
- vector.removeItemsAt(0, first);
- // store offset to first overlaid resource ID of this type
- vector.insertAt((uint32_t)first, 0, 1);
- // reserve space for number and offset of entries, and the actual entries
- *outSize += (2 + vector.size()) * sizeof(uint32_t);
- } else {
- // no entries of current type defined in overlay package
- vector.clear();
- // reserve space for type offset
- *outSize += 1 * sizeof(uint32_t);
+ *outSize += (4 * sizeof(uint16_t)) + (typeMap.entryMap.size() * sizeof(uint32_t));
}
}
+ if (map.isEmpty()) {
+ ALOGW("idmap: no resources in overlay package present in base package");
+ return UNKNOWN_ERROR;
+ }
+
if ((*outData = malloc(*outSize)) == NULL) {
return NO_MEMORY;
}
+
uint32_t* data = (uint32_t*)*outData;
*data++ = htodl(IDMAP_MAGIC);
+ *data++ = htodl(IDMAP_CURRENT_VERSION);
*data++ = htodl(targetCrc);
*data++ = htodl(overlayCrc);
const char* paths[] = { targetPath, overlayPath };
@@ -5934,44 +6006,30 @@ status_t ResTable::createIdmap(const ResTable& overlay,
data += 256 / sizeof(uint32_t);
}
const size_t mapSize = map.size();
- *data++ = htodl(mapSize);
- size_t offset = mapSize;
+ uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
+ *typeData++ = htods(pg->id);
+ *typeData++ = htods(mapSize);
for (size_t i = 0; i < mapSize; ++i) {
- const Vector<uint32_t>& vector = map.itemAt(i);
- const size_t N = vector.size();
- if (N == 0) {
- *data++ = htodl(0);
- } else {
- offset++;
- *data++ = htodl(offset);
- offset += N;
- }
- }
- if (offset == mapSize) {
- ALOGW("idmap: no resources in overlay package present in base package\n");
- return UNKNOWN_ERROR;
- }
- for (size_t i = 0; i < mapSize; ++i) {
- const Vector<uint32_t>& vector = map.itemAt(i);
- const size_t N = vector.size();
- if (N == 0) {
- continue;
- }
- if (N == 1) { // vector expected to hold (offset) + (N > 0 entries)
- ALOGW("idmap: type %u supposedly has entries, but no entries found\n", (uint32_t)i);
- return UNKNOWN_ERROR;
- }
- *data++ = htodl(N - 1); // do not count the offset (which is vector's first element)
- for (size_t j = 0; j < N; ++j) {
- const uint32_t& overlayResID = vector.itemAt(j);
- *data++ = htodl(overlayResID);
+ uint8_t targetTypeId = map.keyAt(i);
+ const IdmapTypeMap& typeMap = map[i];
+ *typeData++ = htods(targetTypeId + 1);
+ *typeData++ = htods(typeMap.overlayTypeId);
+ *typeData++ = htods(typeMap.entryMap.size());
+ *typeData++ = htods(typeMap.entryOffset);
+
+ const size_t entryCount = typeMap.entryMap.size();
+ uint32_t* entries = reinterpret_cast<uint32_t*>(typeData);
+ for (size_t j = 0; j < entryCount; j++) {
+ entries[j] = htodl(typeMap.entryMap[j]);
}
+ typeData += entryCount * 2;
}
return NO_ERROR;
}
bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes,
+ uint32_t* pVersion,
uint32_t* pTargetCrc, uint32_t* pOverlayCrc,
String8* pTargetPath, String8* pOverlayPath)
{
@@ -5979,17 +6037,20 @@ bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes,
if (!assertIdmapHeader(map, sizeBytes)) {
return false;
}
+ if (pVersion) {
+ *pVersion = dtohl(map[1]);
+ }
if (pTargetCrc) {
- *pTargetCrc = map[1];
+ *pTargetCrc = dtohl(map[2]);
}
if (pOverlayCrc) {
- *pOverlayCrc = map[2];
+ *pOverlayCrc = dtohl(map[3]);
}
if (pTargetPath) {
- pTargetPath->setTo(reinterpret_cast<const char*>(map + 3));
+ pTargetPath->setTo(reinterpret_cast<const char*>(map + 4));
}
if (pOverlayPath) {
- pOverlayPath->setTo(reinterpret_cast<const char*>(map + 3 + 256 / sizeof(uint32_t)));
+ pOverlayPath->setTo(reinterpret_cast<const char*>(map + 4 + 256 / sizeof(uint32_t)));
}
return true;
}
@@ -6138,184 +6199,184 @@ void ResTable::print(bool inclValues) const
size_t pkgCount = pg->packages.size();
for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) {
const Package* pkg = pg->packages[pkgIndex];
- size_t typeCount = pkg->types.size();
- printf(" Package %d id=%d name=%s typeCount=%d\n", (int)pkgIndex,
- pkg->package->id, String8(String16(pkg->package->name)).string(),
- (int)typeCount);
- for (size_t typeIndex=0; typeIndex<typeCount; typeIndex++) {
- const Type* typeConfigs = pkg->getType(typeIndex);
- if (typeConfigs == NULL) {
- printf(" type %d NULL\n", (int)typeIndex);
+ printf(" Package %d id=%d name=%s\n", (int)pkgIndex,
+ pkg->package->id, String8(String16(pkg->package->name)).string());
+ }
+
+ for (size_t typeIndex=0; typeIndex < pg->types.size(); typeIndex++) {
+ const TypeList& typeList = pg->types[typeIndex];
+ if (typeList.isEmpty()) {
+ //printf(" type %d NULL\n", (int)typeIndex);
+ continue;
+ }
+ const Type* typeConfigs = typeList[0];
+ const size_t NTC = typeConfigs->configs.size();
+ printf(" type %d configCount=%d entryCount=%d\n",
+ (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount);
+ if (typeConfigs->typeSpecFlags != NULL) {
+ for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) {
+ uint32_t resID = (0xff000000 & ((pg->id)<<24))
+ | (0x00ff0000 & ((typeIndex+1)<<16))
+ | (0x0000ffff & (entryIndex));
+ // Since we are creating resID without actually
+ // iterating over them, we have no idea which is a
+ // dynamic reference. We must check.
+ pg->dynamicRefTable.lookupResourceId(&resID);
+
+ resource_name resName;
+ if (this->getResourceName(resID, true, &resName)) {
+ String8 type8;
+ String8 name8;
+ if (resName.type8 != NULL) {
+ type8 = String8(resName.type8, resName.typeLen);
+ } else {
+ type8 = String8(resName.type, resName.typeLen);
+ }
+ if (resName.name8 != NULL) {
+ name8 = String8(resName.name8, resName.nameLen);
+ } else {
+ name8 = String8(resName.name, resName.nameLen);
+ }
+ printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
+ resID,
+ CHAR16_TO_CSTR(resName.package, resName.packageLen),
+ type8.string(), name8.string(),
+ dtohl(typeConfigs->typeSpecFlags[entryIndex]));
+ } else {
+ printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
+ }
+ }
+ }
+ for (size_t configIndex=0; configIndex<NTC; configIndex++) {
+ const ResTable_type* type = typeConfigs->configs[configIndex];
+ if ((((uint64_t)type)&0x3) != 0) {
+ printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type);
continue;
}
- const size_t NTC = typeConfigs->configs.size();
- printf(" type %d configCount=%d entryCount=%d\n",
- (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount);
- if (typeConfigs->typeSpecFlags != NULL) {
- for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) {
- uint32_t resID = (0xff000000 & ((pkg->package->id)<<24))
- | (0x00ff0000 & ((typeIndex+1)<<16))
- | (0x0000ffff & (entryIndex));
- // Since we are creating resID without actually
- // iterating over them, we have no idea which is a
- // dynamic reference. We must check.
- pg->dynamicRefTable.lookupResourceId(&resID);
-
- resource_name resName;
- if (this->getResourceName(resID, true, &resName)) {
- String8 type8;
- String8 name8;
- if (resName.type8 != NULL) {
- type8 = String8(resName.type8, resName.typeLen);
- } else {
- type8 = String8(resName.type, resName.typeLen);
- }
- if (resName.name8 != NULL) {
- name8 = String8(resName.name8, resName.nameLen);
- } else {
- name8 = String8(resName.name, resName.nameLen);
- }
- printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
- resID,
- CHAR16_TO_CSTR(resName.package, resName.packageLen),
- type8.string(), name8.string(),
- dtohl(typeConfigs->typeSpecFlags[entryIndex]));
+ String8 configStr = type->config.toString();
+ printf(" config %s:\n", configStr.size() > 0
+ ? configStr.string() : "(default)");
+ size_t entryCount = dtohl(type->entryCount);
+ uint32_t entriesStart = dtohl(type->entriesStart);
+ if ((entriesStart&0x3) != 0) {
+ printf(" NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n", entriesStart);
+ continue;
+ }
+ uint32_t typeSize = dtohl(type->header.size);
+ if ((typeSize&0x3) != 0) {
+ printf(" NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize);
+ continue;
+ }
+ for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
+
+ const uint8_t* const end = ((const uint8_t*)type)
+ + dtohl(type->header.size);
+ const uint32_t* const eindex = (const uint32_t*)
+ (((const uint8_t*)type) + dtohs(type->header.headerSize));
+
+ uint32_t thisOffset = dtohl(eindex[entryIndex]);
+ if (thisOffset == ResTable_type::NO_ENTRY) {
+ continue;
+ }
+
+ uint32_t resID = (0xff000000 & ((pg->id)<<24))
+ | (0x00ff0000 & ((typeIndex+1)<<16))
+ | (0x0000ffff & (entryIndex));
+ pg->dynamicRefTable.lookupResourceId(&resID);
+ resource_name resName;
+ if (this->getResourceName(resID, true, &resName)) {
+ String8 type8;
+ String8 name8;
+ if (resName.type8 != NULL) {
+ type8 = String8(resName.type8, resName.typeLen);
+ } else {
+ type8 = String8(resName.type, resName.typeLen);
+ }
+ if (resName.name8 != NULL) {
+ name8 = String8(resName.name8, resName.nameLen);
} else {
- printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
+ name8 = String8(resName.name, resName.nameLen);
}
+ printf(" resource 0x%08x %s:%s/%s: ", resID,
+ CHAR16_TO_CSTR(resName.package, resName.packageLen),
+ type8.string(), name8.string());
+ } else {
+ printf(" INVALID RESOURCE 0x%08x: ", resID);
}
- }
- for (size_t configIndex=0; configIndex<NTC; configIndex++) {
- const ResTable_type* type = typeConfigs->configs[configIndex];
- if ((((uint64_t)type)&0x3) != 0) {
- printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type);
+ if ((thisOffset&0x3) != 0) {
+ printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset);
continue;
}
- String8 configStr = type->config.toString();
- printf(" config %s:\n", configStr.size() > 0
- ? configStr.string() : "(default)");
- size_t entryCount = dtohl(type->entryCount);
- uint32_t entriesStart = dtohl(type->entriesStart);
- if ((entriesStart&0x3) != 0) {
- printf(" NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n", entriesStart);
+ if ((thisOffset+sizeof(ResTable_entry)) > typeSize) {
+ printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n",
+ entriesStart, thisOffset, typeSize);
continue;
}
- uint32_t typeSize = dtohl(type->header.size);
- if ((typeSize&0x3) != 0) {
- printf(" NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize);
+
+ const ResTable_entry* ent = (const ResTable_entry*)
+ (((const uint8_t*)type) + entriesStart + thisOffset);
+ if (((entriesStart + thisOffset)&0x3) != 0) {
+ printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n",
+ (entriesStart + thisOffset));
continue;
}
- for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
- const uint8_t* const end = ((const uint8_t*)type)
- + dtohl(type->header.size);
- const uint32_t* const eindex = (const uint32_t*)
- (((const uint8_t*)type) + dtohs(type->header.headerSize));
+ uintptr_t esize = dtohs(ent->size);
+ if ((esize&0x3) != 0) {
+ printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize);
+ continue;
+ }
+ if ((thisOffset+esize) > typeSize) {
+ printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n",
+ entriesStart, thisOffset, (void *)esize, typeSize);
+ continue;
+ }
- uint32_t thisOffset = dtohl(eindex[entryIndex]);
- if (thisOffset == ResTable_type::NO_ENTRY) {
- continue;
- }
+ const Res_value* valuePtr = NULL;
+ const ResTable_map_entry* bagPtr = NULL;
+ Res_value value;
+ if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
+ printf("<bag>");
+ bagPtr = (const ResTable_map_entry*)ent;
+ } else {
+ valuePtr = (const Res_value*)
+ (((const uint8_t*)ent) + esize);
+ value.copyFrom_dtoh(*valuePtr);
+ printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
+ (int)value.dataType, (int)value.data,
+ (int)value.size, (int)value.res0);
+ }
- uint32_t resID = (0xff000000 & ((pkg->package->id)<<24))
- | (0x00ff0000 & ((typeIndex+1)<<16))
- | (0x0000ffff & (entryIndex));
- pg->dynamicRefTable.lookupResourceId(&resID);
- resource_name resName;
- if (this->getResourceName(resID, true, &resName)) {
- String8 type8;
- String8 name8;
- if (resName.type8 != NULL) {
- type8 = String8(resName.type8, resName.typeLen);
- } else {
- type8 = String8(resName.type, resName.typeLen);
- }
- if (resName.name8 != NULL) {
- name8 = String8(resName.name8, resName.nameLen);
- } else {
- name8 = String8(resName.name, resName.nameLen);
+ if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
+ printf(" (PUBLIC)");
+ }
+ printf("\n");
+
+ if (inclValues) {
+ if (valuePtr != NULL) {
+ printf(" ");
+ print_value(typeConfigs->package, value);
+ } else if (bagPtr != NULL) {
+ const int N = dtohl(bagPtr->count);
+ const uint8_t* baseMapPtr = (const uint8_t*)ent;
+ size_t mapOffset = esize;
+ const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
+ const uint32_t parent = dtohl(bagPtr->parent.ident);
+ uint32_t resolvedParent = parent;
+ status_t err = pg->dynamicRefTable.lookupResourceId(&resolvedParent);
+ if (err != NO_ERROR) {
+ resolvedParent = 0;
}
- printf(" resource 0x%08x %s:%s/%s: ", resID,
- CHAR16_TO_CSTR(resName.package, resName.packageLen),
- type8.string(), name8.string());
- } else {
- printf(" INVALID RESOURCE 0x%08x: ", resID);
- }
- if ((thisOffset&0x3) != 0) {
- printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset);
- continue;
- }
- if ((thisOffset+sizeof(ResTable_entry)) > typeSize) {
- printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n",
- entriesStart, thisOffset, typeSize);
- continue;
- }
-
- const ResTable_entry* ent = (const ResTable_entry*)
- (((const uint8_t*)type) + entriesStart + thisOffset);
- if (((entriesStart + thisOffset)&0x3) != 0) {
- printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n",
- (entriesStart + thisOffset));
- continue;
- }
-
- uintptr_t esize = dtohs(ent->size);
- if ((esize&0x3) != 0) {
- printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize);
- continue;
- }
- if ((thisOffset+esize) > typeSize) {
- printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n",
- entriesStart, thisOffset, (void *)esize, typeSize);
- continue;
- }
-
- const Res_value* valuePtr = NULL;
- const ResTable_map_entry* bagPtr = NULL;
- Res_value value;
- if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
- printf("<bag>");
- bagPtr = (const ResTable_map_entry*)ent;
- } else {
- valuePtr = (const Res_value*)
- (((const uint8_t*)ent) + esize);
- value.copyFrom_dtoh(*valuePtr);
- printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
- (int)value.dataType, (int)value.data,
- (int)value.size, (int)value.res0);
- }
-
- if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
- printf(" (PUBLIC)");
- }
- printf("\n");
-
- if (inclValues) {
- if (valuePtr != NULL) {
- printf(" ");
- print_value(pkg, value);
- } else if (bagPtr != NULL) {
- const int N = dtohl(bagPtr->count);
- const uint8_t* baseMapPtr = (const uint8_t*)ent;
- size_t mapOffset = esize;
- const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
- const uint32_t parent = dtohl(bagPtr->parent.ident);
- uint32_t resolvedParent = parent;
- status_t err = pg->dynamicRefTable.lookupResourceId(&resolvedParent);
- if (err != NO_ERROR) {
- resolvedParent = 0;
- }
- printf(" Parent=0x%08x(Resolved=0x%08x), Count=%d\n",
- parent, resolvedParent, N);
- for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) {
- printf(" #%i (Key=0x%08x): ",
- i, dtohl(mapPtr->name.ident));
- value.copyFrom_dtoh(mapPtr->value);
- print_value(pkg, value);
- const size_t size = dtohs(mapPtr->value.size);
- mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value);
- mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
- }
+ printf(" Parent=0x%08x(Resolved=0x%08x), Count=%d\n",
+ parent, resolvedParent, N);
+ for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) {
+ printf(" #%i (Key=0x%08x): ",
+ i, dtohl(mapPtr->name.ident));
+ value.copyFrom_dtoh(mapPtr->value);
+ print_value(typeConfigs->package, value);
+ const size_t size = dtohs(mapPtr->value.size);
+ mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value);
+ mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
}
}
}
diff --git a/libs/androidfw/TypeWrappers.cpp b/libs/androidfw/TypeWrappers.cpp
new file mode 100644
index 0000000..06b4040
--- /dev/null
+++ b/libs/androidfw/TypeWrappers.cpp
@@ -0,0 +1,70 @@
+/*
+ * 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 <androidfw/TypeWrappers.h>
+
+namespace android {
+
+TypeVariant::iterator& TypeVariant::iterator::operator++() {
+ mIndex++;
+ if (mIndex > dtohl(mTypeVariant->data->entryCount)) {
+ mIndex = dtohl(mTypeVariant->data->entryCount);
+ }
+ return *this;
+}
+
+const ResTable_entry* TypeVariant::iterator::operator*() const {
+ const ResTable_type* type = mTypeVariant->data;
+ const uint32_t entryCount = dtohl(type->entryCount);
+ if (mIndex >= entryCount) {
+ return NULL;
+ }
+
+ const uintptr_t containerEnd = reinterpret_cast<uintptr_t>(type)
+ + dtohl(type->header.size);
+ const uint32_t* const entryIndices = reinterpret_cast<const uint32_t*>(
+ reinterpret_cast<uintptr_t>(type) + dtohs(type->header.headerSize));
+ if (reinterpret_cast<uintptr_t>(entryIndices) + (sizeof(uint32_t) * entryCount) > containerEnd) {
+ ALOGE("Type's entry indices extend beyond its boundaries");
+ return NULL;
+ }
+
+ const uint32_t entryOffset = dtohl(entryIndices[mIndex]);
+ if (entryOffset == ResTable_type::NO_ENTRY) {
+ return NULL;
+ }
+
+ if ((entryOffset & 0x3) != 0) {
+ ALOGE("Index %u points to entry with unaligned offset 0x%08x", mIndex, entryOffset);
+ return NULL;
+ }
+
+ const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
+ reinterpret_cast<uintptr_t>(type) + dtohl(type->entriesStart) + entryOffset);
+ if (reinterpret_cast<uintptr_t>(entry) > containerEnd - sizeof(*entry)) {
+ ALOGE("Entry offset at index %u points outside the Type's boundaries", mIndex);
+ return NULL;
+ } else if (reinterpret_cast<uintptr_t>(entry) + dtohs(entry->size) > containerEnd) {
+ ALOGE("Entry at index %u extends beyond Type's boundaries", mIndex);
+ return NULL;
+ } else if (dtohs(entry->size) < sizeof(*entry)) {
+ ALOGE("Entry at index %u is too small (%u)", mIndex, dtohs(entry->size));
+ return NULL;
+ }
+ return entry;
+}
+
+} // namespace android
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index 9e9649c..4ff6eec 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -1,33 +1,66 @@
-# Build the unit tests.
+#
+# 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.
+#
+
+# ==========================================================
+# Setup some common variables for the different build
+# targets here.
+# ==========================================================
LOCAL_PATH:= $(call my-dir)
+testFiles := \
+ ByteBucketArray_test.cpp \
+ Idmap_test.cpp \
+ ResourceTypes_test.cpp \
+ ResTable_test.cpp \
+ Split_test.cpp \
+ TypeWrappers_test.cpp \
+ ZipUtils_test.cpp
+
+# ==========================================================
+# Build the host tests: libandroidfw_tests
+# ==========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libandroidfw_tests
+
+LOCAL_SRC_FILES := $(testFiles)
+LOCAL_STATIC_LIBRARIES := \
+ libandroidfw \
+ libutils \
+ libcutils \
+ liblog
+
+include $(BUILD_HOST_NATIVE_TEST)
+
+
+# ==========================================================
+# Build the device tests: libandroidfw_tests
+# ==========================================================
include $(CLEAR_VARS)
-# Build the unit tests.
-test_src_files := \
+LOCAL_MODULE := libandroidfw_tests
+
+LOCAL_SRC_FILES := $(testFiles) \
BackupData_test.cpp \
- ObbFile_test.cpp \
- ZipUtils_test.cpp \
- ResourceTypes_test.cpp
+ ObbFile_test.cpp
-shared_libraries := \
+LOCAL_SHARED_LIBRARIES := \
libandroidfw \
libcutils \
libutils \
libui \
libstlport
-static_libraries := \
- libgtest \
- libgtest_main
-
-$(foreach file,$(test_src_files), \
- $(eval include $(CLEAR_VARS)) \
- $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
- $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
- $(eval LOCAL_SRC_FILES := $(file)) \
- $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
- $(eval include $(BUILD_NATIVE_TEST)) \
-)
-
-# Build the manual test programs.
-include $(call all-makefiles-under, $(LOCAL_PATH))
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/androidfw/tests/ByteBucketArray_test.cpp b/libs/androidfw/tests/ByteBucketArray_test.cpp
new file mode 100644
index 0000000..376e79c
--- /dev/null
+++ b/libs/androidfw/tests/ByteBucketArray_test.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 <androidfw/ByteBucketArray.h>
+
+#include <gtest/gtest.h>
+
+using android::ByteBucketArray;
+
+TEST(ByteBucketArrayTest, TestSparseInsertion) {
+ ByteBucketArray<int> bba;
+ ASSERT_TRUE(bba.set(0, 1));
+ ASSERT_TRUE(bba.set(10, 2));
+ ASSERT_TRUE(bba.set(26, 3));
+ ASSERT_TRUE(bba.set(129, 4));
+ ASSERT_TRUE(bba.set(234, 5));
+
+ for (size_t i = 0; i < bba.size(); i++) {
+ switch (i) {
+ case 0: EXPECT_EQ(1, bba[i]); break;
+ case 10: EXPECT_EQ(2, bba[i]); break;
+ case 26: EXPECT_EQ(3, bba[i]); break;
+ case 129: EXPECT_EQ(4, bba[i]); break;
+ case 234: EXPECT_EQ(5, bba[i]); break;
+ default: EXPECT_EQ(0, bba[i]); break;
+ }
+ }
+}
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
new file mode 100644
index 0000000..d829b76
--- /dev/null
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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 <androidfw/ResourceTypes.h>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include "TestHelpers.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace {
+
+/**
+ * Include a binary resource table.
+ *
+ * Package: com.android.test.basic
+ */
+#include "data/basic/basic_arsc.h"
+
+/**
+ * Include a binary resource table.
+ * This table is an overlay.
+ *
+ * Package: com.android.test.basic
+ */
+#include "data/overlay/overlay_arsc.h"
+
+enum { MAY_NOT_BE_BAG = false };
+
+static const uint32_t attr_attr1 = 0x7f010000;
+static const uint32_t attr_attr2 = 0x7f010001;
+static const uint32_t string_test1 = 0x7f020000;
+static const uint32_t string_test2 = 0x7f020001;
+static const uint32_t integer_number1 = 0x7f030000;
+static const uint32_t integer_number2 = 0x7f030001;
+static const uint32_t style_Theme1 = 0x7f040000;
+static const uint32_t style_Theme2 = 0x7f040001;
+static const uint32_t array_integerArray1 = 0x7f050000;
+
+class IdmapTest : public ::testing::Test {
+protected:
+ virtual void SetUp() {
+ ASSERT_EQ(NO_ERROR, mTargetTable.add(basic_arsc, basic_arsc_len));
+ ASSERT_EQ(NO_ERROR, mOverlayTable.add(overlay_arsc, overlay_arsc_len));
+ char targetName[256] = "com.android.test.basic";
+ ASSERT_EQ(NO_ERROR, mTargetTable.createIdmap(mOverlayTable, 0, 0,
+ targetName, targetName, &mData, &mDataSize));
+ }
+
+ virtual void TearDown() {
+ free(mData);
+ }
+
+ ResTable mTargetTable;
+ ResTable mOverlayTable;
+ void* mData;
+ size_t mDataSize;
+};
+
+TEST_F(IdmapTest, canLoadIdmap) {
+ ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));
+}
+
+TEST_F(IdmapTest, overlayOverridesResourceValue) {
+ Res_value val;
+ ssize_t block = mTargetTable.getResource(string_test2, &val, false);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
+ const ResStringPool* pool = mTargetTable.getTableStringBlock(block);
+ ASSERT_TRUE(pool != NULL);
+ ASSERT_LT(val.data, pool->size());
+
+ size_t strLen;
+ const char16_t* targetStr16 = pool->stringAt(val.data, &strLen);
+ ASSERT_TRUE(targetStr16 != NULL);
+ ASSERT_EQ(String16("test2"), String16(targetStr16, strLen));
+
+ ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));
+
+ ssize_t newBlock = mTargetTable.getResource(string_test2, &val, false);
+ ASSERT_GE(newBlock, 0);
+ ASSERT_NE(block, newBlock);
+ ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
+ pool = mTargetTable.getTableStringBlock(newBlock);
+ ASSERT_TRUE(pool != NULL);
+ ASSERT_LT(val.data, pool->size());
+
+ targetStr16 = pool->stringAt(val.data, &strLen);
+ ASSERT_TRUE(targetStr16 != NULL);
+ ASSERT_EQ(String16("test2-overlay"), String16(targetStr16, strLen));
+}
+
+TEST_F(IdmapTest, overlaidResourceHasSameName) {
+ ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));
+
+ ResTable::resource_name resName;
+ ASSERT_TRUE(mTargetTable.getResourceName(array_integerArray1, false, &resName));
+
+ ASSERT_TRUE(resName.package != NULL);
+ ASSERT_TRUE(resName.type != NULL);
+ ASSERT_TRUE(resName.name != NULL);
+
+ EXPECT_EQ(String16("com.android.test.basic"), String16(resName.package, resName.packageLen));
+ EXPECT_EQ(String16("array"), String16(resName.type, resName.typeLen));
+ EXPECT_EQ(String16("integerArray1"), String16(resName.name, resName.nameLen));
+}
+
+} // namespace
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
new file mode 100644
index 0000000..54d42c3
--- /dev/null
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -0,0 +1,189 @@
+/*
+ * 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 <androidfw/ResourceTypes.h>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include "TestHelpers.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace {
+
+/**
+ * Include a binary resource table.
+ *
+ * Package: com.android.test.basic
+ */
+#include "data/basic/basic_arsc.h"
+
+enum { MAY_NOT_BE_BAG = false };
+
+static const uint32_t attr_attr1 = 0x7f010000;
+static const uint32_t attr_attr2 = 0x7f010001;
+static const uint32_t string_test1 = 0x7f020000;
+static const uint32_t string_test2 = 0x7f020001;
+static const uint32_t integer_number1 = 0x7f030000;
+static const uint32_t integer_number2 = 0x7f030001;
+static const uint32_t style_Theme1 = 0x7f040000;
+static const uint32_t style_Theme2 = 0x7f040001;
+static const uint32_t array_integerArray1 = 0x7f050000;
+
+TEST(ResTableTest, shouldLoadSuccessfully) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+}
+
+TEST(ResTableTest, simpleTypeIsRetrievedCorrectly) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ Res_value val;
+ ssize_t block = table.getResource(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));
+}
+
+TEST(ResTableTest, resourceNameIsResolved) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ String16 defPackage("com.android.test.basic");
+ String16 testName("@string/test1");
+ uint32_t resID = table.identifierForName(testName.string(), testName.size(),
+ 0, 0,
+ defPackage.string(), defPackage.size());
+ ASSERT_NE(uint32_t(0x00000000), resID);
+ ASSERT_EQ(string_test1, resID);
+}
+
+TEST(ResTableTest, noParentThemeIsAppliedCorrectly) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ ResTable::Theme theme(table);
+ ASSERT_EQ(NO_ERROR, theme.applyStyle(style_Theme1));
+
+ Res_value val;
+ uint32_t specFlags = 0;
+ ssize_t index = theme.getAttribute(attr_attr1, &val, &specFlags);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(100), val.data);
+
+ index = theme.getAttribute(attr_attr2, &val, &specFlags);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(integer_number1, val.data);
+}
+
+TEST(ResTableTest, parentThemeIsAppliedCorrectly) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ ResTable::Theme theme(table);
+ ASSERT_EQ(NO_ERROR, theme.applyStyle(style_Theme2));
+
+ Res_value val;
+ uint32_t specFlags = 0;
+ ssize_t index = theme.getAttribute(attr_attr1, &val, &specFlags);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(300), val.data);
+
+ index = theme.getAttribute(attr_attr2, &val, &specFlags);
+ ASSERT_GE(index, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(integer_number1, val.data);
+}
+
+TEST(ResTableTest, referenceToBagIsNotResolved) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ Res_value val;
+ ssize_t block = table.getResource(integer_number2, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(array_integerArray1, val.data);
+
+ ssize_t newBlock = table.resolveReference(&val, block);
+ EXPECT_EQ(block, newBlock);
+ EXPECT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ EXPECT_EQ(array_integerArray1, val.data);
+}
+
+TEST(ResTableTest, resourcesStillAccessibleAfterParameterChange) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ Res_value val;
+ ssize_t block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+
+ const ResTable::bag_entry* entry;
+ ssize_t count = table.lockBag(array_integerArray1, &entry);
+ ASSERT_GE(count, 0);
+ table.unlockBag(entry);
+
+ ResTable_config param;
+ memset(&param, 0, sizeof(param));
+ param.density = 320;
+ table.setParameters(&param);
+
+ block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+
+ count = table.lockBag(array_integerArray1, &entry);
+ ASSERT_GE(count, 0);
+ table.unlockBag(entry);
+}
+
+TEST(ResTableTest, resourceIsOverridenWithBetterConfig) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ Res_value val;
+ ssize_t block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(200), val.data);
+
+ ResTable_config param;
+ memset(&param, 0, sizeof(param));
+ param.language[0] = 's';
+ param.language[1] = 'v';
+ param.country[0] = 'S';
+ param.country[1] = 'E';
+ table.setParameters(&param);
+
+ block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+ ASSERT_EQ(uint32_t(400), val.data);
+}
+
+}
diff --git a/libs/androidfw/tests/ResourceTypes_test.cpp b/libs/androidfw/tests/ResourceTypes_test.cpp
index 4888b4a..6041e08 100644
--- a/libs/androidfw/tests/ResourceTypes_test.cpp
+++ b/libs/androidfw/tests/ResourceTypes_test.cpp
@@ -64,8 +64,8 @@ TEST(ResourceTypesTest, ResourceConfig_packAndUnpack3LetterLanguage) {
config.packLanguage("eng");
// 1-00110-01 101-00100
- EXPECT_EQ(0x99, config.language[0]);
- EXPECT_EQ(0xa4, config.language[1]);
+ EXPECT_EQ('\x99', config.language[0]);
+ EXPECT_EQ('\xA4', config.language[1]);
char out[4] = { 1, 1, 1, 1};
config.unpackLanguage(out);
diff --git a/libs/androidfw/tests/Split_test.cpp b/libs/androidfw/tests/Split_test.cpp
new file mode 100644
index 0000000..dbfdeae
--- /dev/null
+++ b/libs/androidfw/tests/Split_test.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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 <androidfw/ResourceTypes.h>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include "TestHelpers.h"
+
+#include <gtest/gtest.h>
+
+/**
+ * Include a binary resource table. This table
+ * is a base table for an APK split.
+ *
+ * Package: com.android.example.split
+ *
+ * layout/main 0x7f020000 {default, fr-sw600dp-v13}
+ *
+ * string/app_title 0x7f030000 {default}
+ * string/test 0x7f030001 {default}
+ * string/boom 0x7f030002 {default}
+ * string/blah 0x7f030003 {default}
+ *
+ * array/lotsofstrings 0x7f040000 {default}
+ * array/numList 0x7f040001 {default}
+ * array/ary 0x7f040002 {default}
+ *
+ */
+#include "data/split_base_arsc.h"
+
+/**
+ * Include a binary resource table. This table
+ * is a configuration split table for an APK split.
+ *
+ * Package: com.android.example.split
+ *
+ * string/app_title 0x7f030000 {fr}
+ * string/test 0x7f030001 {de,fr}
+ * string/blah 0x7f030003 {fr}
+ *
+ * array/lotsofstrings 0x7f040000 {fr}
+ *
+ */
+#include "data/split_de_fr_arsc.h"
+
+
+using namespace android;
+
+enum { MAY_NOT_BE_BAG = false };
+
+void makeConfigFrench(ResTable_config* config) {
+ memset(config, 0, sizeof(*config));
+ config->language[0] = 'f';
+ config->language[1] = 'r';
+}
+
+TEST(SplitTest, TestLoadBase) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
+}
+
+TEST(SplitTest, TestGetResourceFromBase) {
+ ResTable_config frenchConfig;
+ makeConfigFrench(&frenchConfig);
+
+ ResTable table;
+ table.setParameters(&frenchConfig);
+
+ ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
+
+ ResTable_config expectedConfig;
+ memset(&expectedConfig, 0, sizeof(expectedConfig));
+
+ Res_value val;
+ ResTable_config config;
+ ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, NULL, &config);
+
+ // The returned block should tell us which string pool to get the value, if it is a string.
+ EXPECT_GE(block, 0);
+
+ // We expect the default resource to be selected since it is the only resource configuration.
+ EXPECT_EQ(0, expectedConfig.compare(config));
+
+ EXPECT_EQ(Res_value::TYPE_STRING, val.dataType);
+}
+
+TEST(SplitTest, TestGetResourceFromSplit) {
+ ResTable_config expectedConfig;
+ makeConfigFrench(&expectedConfig);
+
+ ResTable table;
+ table.setParameters(&expectedConfig);
+
+ ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
+ ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));
+
+ Res_value val;
+ ResTable_config config;
+ ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, NULL, &config);
+
+ EXPECT_GE(block, 0);
+
+ EXPECT_EQ(0, expectedConfig.compare(config));
+
+ EXPECT_EQ(Res_value::TYPE_STRING, val.dataType);
+}
+
+TEST(SplitTest, ResourcesFromBaseAndSplitHaveSameNames) {
+ ResTable_config expectedConfig;
+ makeConfigFrench(&expectedConfig);
+
+ ResTable table;
+ table.setParameters(&expectedConfig);
+
+ ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
+
+ ResTable::resource_name baseName;
+ EXPECT_TRUE(table.getResourceName(0x7f030003, false, &baseName));
+
+ ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));
+
+ ResTable::resource_name frName;
+ EXPECT_TRUE(table.getResourceName(0x7f030003, false, &frName));
+
+ EXPECT_EQ(
+ String16(baseName.package, baseName.packageLen),
+ String16(frName.package, frName.packageLen));
+
+ EXPECT_EQ(
+ String16(baseName.type, baseName.typeLen),
+ String16(frName.type, frName.typeLen));
+
+ EXPECT_EQ(
+ String16(baseName.name, baseName.nameLen),
+ String16(frName.name, frName.nameLen));
+}
+
+TEST(SplitTest, TypeEntrySpecFlagsAreUpdated) {
+ ResTable_config defaultConfig;
+ memset(&defaultConfig, 0, sizeof(defaultConfig));
+
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
+
+ Res_value val;
+ uint32_t specFlags = 0;
+ ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, &specFlags, NULL);
+ EXPECT_GE(block, 0);
+
+ EXPECT_EQ(static_cast<uint32_t>(0), specFlags);
+
+ ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));
+
+ uint32_t frSpecFlags = 0;
+ block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, &frSpecFlags, NULL);
+ EXPECT_GE(block, 0);
+
+ EXPECT_EQ(ResTable_config::CONFIG_LOCALE, frSpecFlags);
+}
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
new file mode 100644
index 0000000..75a233a
--- /dev/null
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -0,0 +1,17 @@
+#ifndef __TEST_HELPERS_H
+#define __TEST_HELPERS_H
+
+#include <ostream>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+static inline ::std::ostream& operator<<(::std::ostream& out, const android::String8& str) {
+ return out << str.string();
+}
+
+static inline ::std::ostream& operator<<(::std::ostream& out, const android::String16& str) {
+ return out << android::String8(str).string();
+}
+
+#endif // __TEST_HELPERS_H
diff --git a/libs/androidfw/tests/TypeWrappers_test.cpp b/libs/androidfw/tests/TypeWrappers_test.cpp
new file mode 100644
index 0000000..d69abe5
--- /dev/null
+++ b/libs/androidfw/tests/TypeWrappers_test.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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 <androidfw/ResourceTypes.h>
+#include <androidfw/TypeWrappers.h>
+#include <utils/String8.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+void* createTypeData() {
+ ResTable_type t;
+ memset(&t, 0, sizeof(t));
+ t.header.type = RES_TABLE_TYPE_TYPE;
+ t.header.headerSize = sizeof(t);
+ t.id = 1;
+ t.entryCount = 3;
+
+ uint32_t offsets[3];
+ t.entriesStart = t.header.headerSize + sizeof(offsets);
+ t.header.size = t.entriesStart;
+
+ offsets[0] = 0;
+ ResTable_entry e1;
+ memset(&e1, 0, sizeof(e1));
+ e1.size = sizeof(e1);
+ e1.key.index = 0;
+ t.header.size += sizeof(e1);
+
+ Res_value v1;
+ memset(&v1, 0, sizeof(v1));
+ t.header.size += sizeof(v1);
+
+ offsets[1] = ResTable_type::NO_ENTRY;
+
+ offsets[2] = sizeof(e1) + sizeof(v1);
+ ResTable_entry e2;
+ memset(&e2, 0, sizeof(e2));
+ e2.size = sizeof(e2);
+ e2.key.index = 1;
+ t.header.size += sizeof(e2);
+
+ Res_value v2;
+ memset(&v2, 0, sizeof(v2));
+ t.header.size += sizeof(v2);
+
+ uint8_t* data = (uint8_t*)malloc(t.header.size);
+ uint8_t* p = data;
+ memcpy(p, &t, sizeof(t));
+ p += sizeof(t);
+ memcpy(p, offsets, sizeof(offsets));
+ p += sizeof(offsets);
+ memcpy(p, &e1, sizeof(e1));
+ p += sizeof(e1);
+ memcpy(p, &v1, sizeof(v1));
+ p += sizeof(v1);
+ memcpy(p, &e2, sizeof(e2));
+ p += sizeof(e2);
+ memcpy(p, &v2, sizeof(v2));
+ p += sizeof(v2);
+ return data;
+}
+
+TEST(TypeVariantIteratorTest, shouldIterateOverTypeWithoutErrors) {
+ ResTable_type* data = (ResTable_type*) createTypeData();
+
+ TypeVariant v(data);
+
+ TypeVariant::iterator iter = v.beginEntries();
+ ASSERT_EQ(uint32_t(0), iter.index());
+ ASSERT_TRUE(NULL != *iter);
+ ASSERT_EQ(uint32_t(0), iter->key.index);
+ ASSERT_NE(v.endEntries(), iter);
+
+ iter++;
+
+ ASSERT_EQ(uint32_t(1), iter.index());
+ ASSERT_TRUE(NULL == *iter);
+ ASSERT_NE(v.endEntries(), iter);
+
+ iter++;
+
+ ASSERT_EQ(uint32_t(2), iter.index());
+ ASSERT_TRUE(NULL != *iter);
+ ASSERT_EQ(uint32_t(1), iter->key.index);
+ ASSERT_NE(v.endEntries(), iter);
+
+ iter++;
+
+ ASSERT_EQ(v.endEntries(), iter);
+
+ free(data);
+}
+
+} // namespace android
diff --git a/libs/androidfw/tests/data/.gitignore b/libs/androidfw/tests/data/.gitignore
new file mode 100644
index 0000000..c05cfb0
--- /dev/null
+++ b/libs/androidfw/tests/data/.gitignore
@@ -0,0 +1,2 @@
+*.apk
+*.arsc
diff --git a/libs/androidfw/tests/data/basic/AndroidManifest.xml b/libs/androidfw/tests/data/basic/AndroidManifest.xml
new file mode 100644
index 0000000..a56ac18
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.basic">
+ <application>
+ </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/basic/basic_arsc.h b/libs/androidfw/tests/data/basic/basic_arsc.h
new file mode 100644
index 0000000..6532076
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/basic_arsc.h
@@ -0,0 +1,131 @@
+unsigned char basic_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0xfc, 0x05, 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, 0x31, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xb0, 0x05, 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,
+ 0x05, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x7c, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00,
+ 0x3c, 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, 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,
+ 0xdc, 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, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+ 0x2a, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00,
+ 0x5c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x31, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00,
+ 0x32, 0x00, 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, 0x07, 0x00, 0x6e, 0x00,
+ 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x31, 0x00,
+ 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00,
+ 0x65, 0x00, 0x72, 0x00, 0x32, 0x00, 0x00, 0x00, 0x06, 0x00, 0x54, 0x00,
+ 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x31, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
+ 0x32, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00,
+ 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x41, 0x00, 0x72, 0x00,
+ 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 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,
+ 0x01, 0x02, 0x44, 0x00, 0x84, 0x00, 0x00, 0x00, 0x01, 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,
+ 0x1c, 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, 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x02, 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, 0x02, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x03, 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, 0x04, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 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, 0x04, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x10, 0xc8, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x7f,
+ 0x01, 0x02, 0x44, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x03, 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, 0x04, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x10, 0x90, 0x01, 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, 0x01, 0x02, 0x44, 0x00,
+ 0x90, 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,
+ 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, 0x06, 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, 0x03, 0x7f, 0x10, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x04, 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, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x7c, 0x00, 0x00, 0x00,
+ 0x05, 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, 0x01, 0x00, 0x08, 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 = 1532;
diff --git a/libs/androidfw/tests/data/basic/build b/libs/androidfw/tests/data/basic/build
new file mode 100755
index 0000000..237342c
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/build
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+aapt package -M AndroidManifest.xml -S res -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc basic.arsc && \
+xxd -i basic.arsc > basic_arsc.h
diff --git a/libs/androidfw/tests/data/basic/res/values-sv/values.xml b/libs/androidfw/tests/data/basic/res/values-sv/values.xml
new file mode 100644
index 0000000..9d52307
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/values-sv/values.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <integer name="number1">400</integer>
+</resources>
diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml
new file mode 100644
index 0000000..662eda6
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/values/values.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <attr name="attr1" format="reference|integer" />
+ <attr name="attr2" format="reference|integer" />
+
+ <string name="test1">test1</string>
+ <string name="test2">test2</string>
+
+ <integer name="number1">200</integer>
+ <integer name="number2">@array/integerArray1</integer>
+
+ <style name="Theme1">
+ <item name="com.android.test.basic:attr1">100</item>
+ <item name="com.android.test.basic:attr2">@integer/number1</item>
+ </style>
+
+ <style name="Theme2" parent="@com.android.test.basic:style/Theme1">
+ <item name="com.android.test.basic:attr1">300</item>
+ </style>
+
+ <integer-array name="integerArray1">
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ </integer-array>
+</resources>
diff --git a/libs/androidfw/tests/data/overlay/AndroidManifest.xml b/libs/androidfw/tests/data/overlay/AndroidManifest.xml
new file mode 100644
index 0000000..a56ac18
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.test.basic">
+ <application>
+ </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build
new file mode 100755
index 0000000..87cf6de
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/build
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+aapt package -M AndroidManifest.xml -S res -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc overlay.arsc && \
+xxd -i overlay.arsc > overlay_arsc.h
diff --git a/libs/androidfw/tests/data/overlay/overlay_arsc.h b/libs/androidfw/tests/data/overlay/overlay_arsc.h
new file mode 100644
index 0000000..5bd98b2
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/overlay_arsc.h
@@ -0,0 +1,69 @@
+unsigned char overlay_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0x10, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x40, 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, 0x0d, 0x00, 0x74, 0x00,
+ 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x6f, 0x00,
+ 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xc4, 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,
+ 0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 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,
+ 0x0c, 0x00, 0x00, 0x00, 0x1c, 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,
+ 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x50, 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, 0x32, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x69, 0x00, 0x6e, 0x00,
+ 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x41, 0x00,
+ 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 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,
+ 0x01, 0x02, 0x44, 0x00, 0x58, 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, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x44, 0x00, 0x70, 0x00, 0x00, 0x00, 0x03, 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, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+ 0x0b, 0x00, 0x00, 0x00
+};
+unsigned int overlay_arsc_len = 784;
diff --git a/libs/androidfw/tests/data/overlay/res/values/values.xml b/libs/androidfw/tests/data/overlay/res/values/values.xml
new file mode 100644
index 0000000..227e889
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/res/values/values.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="test2">test2-overlay</string>
+ <integer-array name="integerArray1">
+ <item>10</item>
+ <item>11</item>
+ </integer-array>
+</resources>
diff --git a/libs/androidfw/tests/data/split_base_arsc.h b/libs/androidfw/tests/data/split_base_arsc.h
new file mode 100644
index 0000000..e0321e9
--- /dev/null
+++ b/libs/androidfw/tests/data/split_base_arsc.h
@@ -0,0 +1,221 @@
+unsigned char split_base_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0x30, 0x0a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x94, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
+ 0x72, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00,
+ 0xb4, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00,
+ 0xf4, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x2a, 0x01, 0x00, 0x00,
+ 0x44, 0x01, 0x00, 0x00, 0x13, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00,
+ 0x2f, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00,
+ 0x74, 0x00, 0x2f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00,
+ 0x2e, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x22, 0x00,
+ 0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2f, 0x00, 0x6c, 0x00, 0x61, 0x00,
+ 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x2d, 0x00, 0x66, 0x00,
+ 0x72, 0x00, 0x2d, 0x00, 0x73, 0x00, 0x77, 0x00, 0x36, 0x00, 0x30, 0x00,
+ 0x30, 0x00, 0x64, 0x00, 0x70, 0x00, 0x2d, 0x00, 0x76, 0x00, 0x31, 0x00,
+ 0x33, 0x00, 0x2f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00,
+ 0x2e, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x53, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x20, 0x00,
+ 0x41, 0x00, 0x50, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x48, 0x00,
+ 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x2c, 0x00, 0x20, 0x00,
+ 0x57, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6c, 0x00, 0x64, 0x00, 0x21, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x42, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6d, 0x00,
+ 0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x42, 0x00, 0x6c, 0x00, 0x61, 0x00,
+ 0x68, 0x00, 0x2e, 0x00, 0x2e, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x0b, 0x00,
+ 0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x20, 0x00,
+ 0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x20, 0x00,
+ 0x62, 0x00, 0x79, 0x00, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x49, 0x00,
+ 0x20, 0x00, 0x68, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x20, 0x00, 0x41, 0x00,
+ 0x4c, 0x00, 0x4c, 0x00, 0x21, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x49, 0x00,
+ 0x20, 0x00, 0x68, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x20, 0x00, 0x31, 0x00,
+ 0x21, 0x00, 0x31, 0x00, 0x21, 0x00, 0x20, 0x00, 0x3a, 0x00, 0x29, 0x00,
+ 0x00, 0x00, 0x0b, 0x00, 0x49, 0x00, 0x20, 0x00, 0x6e, 0x00, 0x6f, 0x00,
+ 0x20, 0x00, 0x68, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x20, 0x00, 0x3a, 0x00,
+ 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x01,
+ 0x90, 0x08, 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, 0x65, 0x00, 0x78, 0x00,
+ 0x61, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x2e, 0x00,
+ 0x73, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x1c, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00,
+ 0x0e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0xd4, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
+ 0x3a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
+ 0x66, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x7c, 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, 0x05, 0x00, 0x61, 0x00,
+ 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x07, 0x00,
+ 0x70, 0x00, 0x6c, 0x00, 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6c, 0x00,
+ 0x73, 0x00, 0x00, 0x00, 0x04, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00,
+ 0x6c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6c, 0x00,
+ 0x6f, 0x00, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x64, 0x00, 0x69, 0x00,
+ 0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x69, 0x00,
+ 0x64, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00,
+ 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0x30, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x22, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00,
+ 0x46, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00,
+ 0x80, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00,
+ 0xa4, 0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0xca, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x74, 0x00,
+ 0x69, 0x00, 0x74, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x04, 0x00,
+ 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00,
+ 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x00,
+ 0x62, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x68, 0x00, 0x00, 0x00, 0x0d, 0x00,
+ 0x6c, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00,
+ 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00,
+ 0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00,
+ 0x4c, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x03, 0x00,
+ 0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x00, 0x00, 0x04, 0x00, 0x70, 0x00,
+ 0x6c, 0x00, 0x75, 0x00, 0x72, 0x00, 0x00, 0x00, 0x03, 0x00, 0x71, 0x00,
+ 0x75, 0x00, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x67, 0x00, 0x72, 0x00,
+ 0x65, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x77, 0x00,
+ 0x69, 0x00, 0x64, 0x00, 0x74, 0x00, 0x68, 0x00, 0x00, 0x00, 0x0a, 0x00,
+ 0x69, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00,
+ 0x66, 0x00, 0x69, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00,
+ 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x04, 0x24, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x58, 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,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x48, 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, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+ 0x94, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x54, 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,
+ 0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0xc8, 0x00, 0x00, 0x00,
+ 0x04, 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,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x10, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03,
+ 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03,
+ 0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+ 0x08, 0x00, 0x00, 0x10, 0xd2, 0x04, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x01, 0x00, 0x03, 0x7f,
+ 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x7f,
+ 0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x05, 0x01, 0x19, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+ 0x7c, 0x00, 0x00, 0x00, 0x05, 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, 0x01, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x03, 0x0a, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x03, 0x08, 0x00, 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,
+ 0x58, 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,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x12, 0xff, 0xff, 0xff, 0xff,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+ 0x58, 0x00, 0x00, 0x00, 0x07, 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,
+ 0x0a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x1d, 0x00, 0xff, 0x00, 0xff,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x24, 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,
+ 0x0b, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x05, 0x01, 0x17, 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, 0x0d, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x05,
+ 0x01, 0xe6, 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,
+ 0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x0b, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x0a, 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, 0x0d, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
+ 0x7b, 0x00, 0x00, 0x00
+};
+unsigned int split_base_arsc_len = 2608;
diff --git a/libs/androidfw/tests/data/split_de_fr_arsc.h b/libs/androidfw/tests/data/split_de_fr_arsc.h
new file mode 100644
index 0000000..6f6a416
--- /dev/null
+++ b/libs/androidfw/tests/data/split_de_fr_arsc.h
@@ -0,0 +1,118 @@
+unsigned char split_de_fr_arsc[] = {
+ 0x02, 0x00, 0x0c, 0x00, 0x64, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0xb8, 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, 0x14, 0x00, 0x00, 0x00,
+ 0x2c, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00,
+ 0x6c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x41, 0x00, 0x63, 0x00, 0x68, 0x00,
+ 0x74, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x21, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x41, 0x00, 0x50, 0x00, 0x4b, 0x00, 0x20, 0x00, 0x44, 0x00,
+ 0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x73, 0x00, 0xe9, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x42, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x6a, 0x00, 0x6f, 0x00,
+ 0x75, 0x00, 0x72, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x4d, 0x00, 0x6f, 0x00,
+ 0x6e, 0x00, 0x64, 0x00, 0x65, 0x00, 0x21, 0x00, 0x00, 0x00, 0x06, 0x00,
+ 0x42, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x68, 0x00, 0x2e, 0x00, 0x2e, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x48, 0x00, 0xe9, 0x00, 0x20, 0x00, 0x6c, 0x00,
+ 0xe0, 0x00, 0x00, 0x00, 0x09, 0x00, 0x41, 0x00, 0x75, 0x00, 0x20, 0x00,
+ 0x72, 0x00, 0x65, 0x00, 0x76, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x72, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x01, 0xa0, 0x04, 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, 0x65, 0x00, 0x78, 0x00, 0x61, 0x00, 0x6d, 0x00,
+ 0x70, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x2e, 0x00, 0x73, 0x00, 0x70, 0x00,
+ 0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x1c, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00,
+ 0x4c, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00,
+ 0x74, 0x00, 0x00, 0x00, 0x7c, 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, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00,
+ 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x07, 0x00, 0x70, 0x00, 0x6c, 0x00,
+ 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x73, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x00, 0x00,
+ 0x05, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x72, 0x00,
+ 0x00, 0x00, 0x05, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x65, 0x00,
+ 0x6e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x69, 0x00, 0x64, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00,
+ 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+ 0x78, 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, 0x22, 0x00, 0x00, 0x00,
+ 0x2e, 0x00, 0x00, 0x00, 0x09, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00,
+ 0x5f, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x6c, 0x00, 0x65, 0x00,
+ 0x00, 0x00, 0x04, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+ 0x00, 0x00, 0x04, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x68, 0x00,
+ 0x00, 0x00, 0x0d, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x73, 0x00,
+ 0x6f, 0x00, 0x66, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00,
+ 0x6e, 0x00, 0x67, 0x00, 0x73, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x10, 0x00, 0x00, 0x00, 0x01, 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,
+ 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x64, 0x00, 0x00, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x54, 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,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+ 0x84, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x54, 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, 0x10, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+ 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x08, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x02, 0x44, 0x00, 0x78, 0x00, 0x00, 0x00, 0x04, 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,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x01, 0x00,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+ 0x01, 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, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+ 0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+ 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+unsigned int split_de_fr_arsc_len = 1380;
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 900a72e..a704e19 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -14,6 +14,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
AmbientShadow.cpp \
Animator.cpp \
AssetAtlas.cpp \
+ DamageAccumulator.cpp \
FontRenderer.cpp \
GammaFontRenderer.cpp \
Caches.cpp \
@@ -44,6 +45,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
RenderBufferCache.cpp \
RenderNode.cpp \
RenderProperties.cpp \
+ RenderState.cpp \
ResourceCache.cpp \
ShadowTessellator.cpp \
SkiaShader.cpp \
@@ -60,6 +62,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SRC_FILES += \
renderthread/CanvasContext.cpp \
renderthread/DrawFrameTask.cpp \
+ renderthread/EglManager.cpp \
renderthread/RenderProxy.cpp \
renderthread/RenderTask.cpp \
renderthread/RenderThread.cpp \
@@ -68,12 +71,8 @@ ifeq ($(USE_OPENGL_RENDERER),true)
intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,)
LOCAL_C_INCLUDES += \
- $(JNI_H_INCLUDE) \
- $(LOCAL_PATH)/../../include/utils \
external/skia/src/core
- include external/stlport/libstlport.mk
-
LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
@@ -81,16 +80,15 @@ ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_MODULE := libhwui
LOCAL_MODULE_TAGS := optional
+ include external/stlport/libstlport.mk
+
ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT
- LOCAL_SHARED_LIBRARIES += libRS libRScpp libstlport
+ LOCAL_SHARED_LIBRARIES += libRS libRScpp
LOCAL_C_INCLUDES += \
$(intermediates) \
frameworks/rs/cpp \
- frameworks/rs \
- external/stlport/stlport \
- bionic/ \
- bionic/libstdc++/include
+ frameworks/rs
endif
ifndef HWUI_COMPILE_SYMBOLS
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index eff3011..dc6d852 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -160,6 +160,10 @@ void RenderPropertyAnimator::onAttached(RenderNode* target) {
(target->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
}
+uint32_t RenderPropertyAnimator::dirtyMask() {
+ return mPropertyAccess->dirtyMask;
+}
+
float RenderPropertyAnimator::getValue(RenderNode* target) const {
return (target->properties().*mPropertyAccess->getter)();
}
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index a0c7c55..6cb72c4c 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -61,6 +61,8 @@ public:
bool isFinished() { return mPlayState == FINISHED; }
float finalValue() { return mFinalValue; }
+ ANDROID_API virtual uint32_t dirtyMask() { return 0; }
+
protected:
BaseRenderNodeAnimator(float finalValue);
virtual ~BaseRenderNodeAnimator();
@@ -112,12 +114,14 @@ public:
ANDROID_API virtual void onAttached(RenderNode* target);
+ ANDROID_API virtual uint32_t dirtyMask();
+
protected:
virtual float getValue(RenderNode* target) const;
virtual void setValue(RenderNode* target, float value);
private:
- typedef void (RenderProperties::*SetFloatProperty)(float value);
+ typedef bool (RenderProperties::*SetFloatProperty)(float value);
typedef float (RenderProperties::*GetFloatProperty)() const;
struct PropertyAccessors;
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 6fd9999..402f28b 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -565,11 +565,8 @@ void Caches::deleteTexture(GLuint texture) {
// call, any texture operation will be performed on the default
// texture (name=0)
- for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
- if (mBoundTextures[i] == texture) {
- mBoundTextures[i] = 0;
- }
- }
+ unbindTexture(texture);
+
glDeleteTextures(1, &texture);
}
@@ -577,6 +574,14 @@ void Caches::resetBoundTextures() {
memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint));
}
+void Caches::unbindTexture(GLuint texture) {
+ for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
+ if (mBoundTextures[i] == texture) {
+ mBoundTextures[i] = 0;
+ }
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
// Scissor
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index b4b5927..83a5d9a 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -261,6 +261,11 @@ public:
void resetBoundTextures();
/**
+ * Clear the cache of bound textures.
+ */
+ void unbindTexture(GLuint texture);
+
+ /**
* Sets the scissor for the current surface.
*/
bool setScissor(GLint x, GLint y, GLint width, GLint height);
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
new file mode 100644
index 0000000..8b32c40
--- /dev/null
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -0,0 +1,219 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "DamageAccumulator"
+
+#include "DamageAccumulator.h"
+
+#include <cutils/log.h>
+
+#include "RenderNode.h"
+#include "utils/MathUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+NullDamageAccumulator NullDamageAccumulator::sInstance;
+
+NullDamageAccumulator* NullDamageAccumulator::instance() {
+ return &sInstance;
+}
+
+enum TransformType {
+ TransformInvalid = 0,
+ TransformRenderNode,
+ TransformMatrix4,
+ TransformNone,
+};
+
+struct DirtyStack {
+ TransformType type;
+ union {
+ const RenderNode* renderNode;
+ const Matrix4* matrix4;
+ };
+ // When this frame is pop'd, this rect is mapped through the above transform
+ // and applied to the previous (aka parent) frame
+ SkRect pendingDirty;
+ DirtyStack* prev;
+ DirtyStack* next;
+};
+
+DamageAccumulator::DamageAccumulator() {
+ mHead = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
+ memset(mHead, 0, sizeof(DirtyStack));
+ // Create a root that we will not pop off
+ mHead->prev = mHead;
+ mHead->type = TransformNone;
+}
+
+void DamageAccumulator::pushCommon() {
+ if (!mHead->next) {
+ DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack));
+ nextFrame->next = 0;
+ nextFrame->prev = mHead;
+ mHead->next = nextFrame;
+ }
+ mHead = mHead->next;
+ mHead->pendingDirty.setEmpty();
+}
+
+void DamageAccumulator::pushTransform(const RenderNode* transform) {
+ pushCommon();
+ mHead->type = TransformRenderNode;
+ mHead->renderNode = transform;
+}
+
+void DamageAccumulator::pushTransform(const Matrix4* transform) {
+ pushCommon();
+ mHead->type = TransformMatrix4;
+ mHead->matrix4 = transform;
+}
+
+void DamageAccumulator::pushNullTransform() {
+ pushCommon();
+ mHead->type = TransformNone;
+}
+
+void DamageAccumulator::popTransform() {
+ LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!");
+ DirtyStack* dirtyFrame = mHead;
+ mHead = mHead->prev;
+ switch (dirtyFrame->type) {
+ case TransformRenderNode:
+ applyRenderNodeTransform(dirtyFrame);
+ break;
+ case TransformMatrix4:
+ applyMatrix4Transform(dirtyFrame);
+ break;
+ case TransformNone:
+ mHead->pendingDirty.join(dirtyFrame->pendingDirty);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type);
+ }
+}
+
+static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) {
+ if (in.isEmpty()) return;
+ Rect temp(in);
+ matrix->mapRect(temp);
+ out->join(RECT_ARGS(temp));
+}
+
+void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) {
+ mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty);
+}
+
+static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) {
+ if (in.isEmpty()) return;
+ const SkMatrix* transform = props.getTransformMatrix();
+ SkRect temp(in);
+ if (transform && !transform->isIdentity()) {
+ transform->mapRect(&temp);
+ }
+ temp.offset(props.getLeft(), props.getTop());
+ out->join(temp);
+}
+
+static DirtyStack* findParentRenderNode(DirtyStack* frame) {
+ while (frame->prev != frame) {
+ frame = frame->prev;
+ if (frame->type == TransformRenderNode) {
+ return frame;
+ }
+ }
+ return NULL;
+}
+
+static DirtyStack* findProjectionReceiver(DirtyStack* frame) {
+ if (frame) {
+ while (frame->prev != frame) {
+ frame = frame->prev;
+ if (frame->type == TransformRenderNode
+ && frame->renderNode->hasProjectionReceiver()) {
+ return frame;
+ }
+ }
+ }
+ return NULL;
+}
+
+static void applyTransforms(DirtyStack* frame, DirtyStack* end) {
+ SkRect* rect = &frame->pendingDirty;
+ while (frame != end) {
+ if (frame->type == TransformRenderNode) {
+ mapRect(frame->renderNode->properties(), *rect, rect);
+ } else {
+ mapRect(frame->matrix4, *rect, rect);
+ }
+ frame = frame->prev;
+ }
+}
+
+void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) {
+ if (frame->pendingDirty.isEmpty()) {
+ return;
+ }
+
+ const RenderProperties& props = frame->renderNode->properties();
+ if (props.getAlpha() <= 0) {
+ return;
+ }
+
+ // Perform clipping
+ if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
+ if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) {
+ frame->pendingDirty.setEmpty();
+ }
+ }
+
+ // apply all transforms
+ mapRect(props, frame->pendingDirty, &mHead->pendingDirty);
+
+ // project backwards if necessary
+ if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) {
+ // First, find our parent RenderNode:
+ DirtyStack* parentNode = findParentRenderNode(frame);
+ // Find our parent's projection receiver, which is what we project onto
+ DirtyStack* projectionReceiver = findProjectionReceiver(parentNode);
+ if (projectionReceiver) {
+ applyTransforms(frame, projectionReceiver);
+ projectionReceiver->pendingDirty.join(frame->pendingDirty);
+ }
+
+ frame->pendingDirty.setEmpty();
+ }
+}
+
+void DamageAccumulator::dirty(float left, float top, float right, float bottom) {
+ mHead->pendingDirty.join(left, top, right, bottom);
+}
+
+void DamageAccumulator::peekAtDirty(SkRect* dest) {
+ *dest = mHead->pendingDirty;
+}
+
+void DamageAccumulator::finish(SkRect* totalDirty) {
+ LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", mHead->prev, mHead);
+ // Root node never has a transform, so this is the fully mapped dirty rect
+ *totalDirty = mHead->pendingDirty;
+ totalDirty->roundOut();
+ mHead->pendingDirty.setEmpty();
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
new file mode 100644
index 0000000..fc9b41b
--- /dev/null
+++ b/libs/hwui/DamageAccumulator.h
@@ -0,0 +1,103 @@
+/*
+ * 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 DAMAGEACCUMULATOR_H
+#define DAMAGEACCUMULATOR_H
+
+#include <cutils/compiler.h>
+#include <utils/LinearAllocator.h>
+
+#include <SkMatrix.h>
+#include <SkRect.h>
+
+#include "utils/Macros.h"
+
+namespace android {
+namespace uirenderer {
+
+struct DirtyStack;
+class RenderNode;
+class Matrix4;
+
+class IDamageAccumulator {
+public:
+ virtual void pushTransform(const RenderNode* transform) = 0;
+ virtual void pushTransform(const Matrix4* transform) = 0;
+ virtual void pushNullTransform() = 0;
+ virtual void popTransform() = 0;
+ virtual void dirty(float left, float top, float right, float bottom) = 0;
+ virtual void peekAtDirty(SkRect* dest) = 0;
+protected:
+ virtual ~IDamageAccumulator() {}
+};
+
+class DamageAccumulator : public IDamageAccumulator {
+ PREVENT_COPY_AND_ASSIGN(DamageAccumulator);
+public:
+ DamageAccumulator();
+ // mAllocator will clean everything up for us, no need for a dtor
+
+ // Push a transform node onto the stack. This should be called prior
+ // to any dirty() calls. Subsequent calls to dirty()
+ // will be affected by the transform when popTransform() is called.
+ virtual void pushTransform(const RenderNode* transform);
+ virtual void pushTransform(const Matrix4* transform);
+ // This is used in combination with peekAtDirty to inspect the damage
+ // area of a subtree
+ virtual void pushNullTransform();
+
+ // Pops a transform node from the stack, propagating the dirty rect
+ // up to the parent node. Returns the IDamageTransform that was just applied
+ virtual void popTransform();
+
+ virtual void dirty(float left, float top, float right, float bottom);
+
+ // Returns the current dirty area, *NOT* transformed by pushed transforms
+ virtual void peekAtDirty(SkRect* dest);
+
+ void finish(SkRect* totalDirty);
+
+private:
+ void pushCommon();
+ void applyMatrix4Transform(DirtyStack* frame);
+ void applyRenderNodeTransform(DirtyStack* frame);
+
+ LinearAllocator mAllocator;
+ DirtyStack* mHead;
+};
+
+class NullDamageAccumulator : public IDamageAccumulator {
+ PREVENT_COPY_AND_ASSIGN(NullDamageAccumulator);
+public:
+ virtual void pushTransform(const RenderNode* transform) { }
+ virtual void pushTransform(const Matrix4* transform) { }
+ virtual void pushNullTransform() { }
+ virtual void popTransform() { }
+ virtual void dirty(float left, float top, float right, float bottom) { }
+ virtual void peekAtDirty(SkRect* dest) { dest->setEmpty(); }
+
+ ANDROID_API static NullDamageAccumulator* instance();
+
+private:
+ NullDamageAccumulator() {}
+ ~NullDamageAccumulator() {}
+
+ static NullDamageAccumulator sInstance;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* DAMAGEACCUMULATOR_H */
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 937bf8d..a998594 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -500,7 +500,7 @@ void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, StateOp* o
void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
/* 1: op calculates local bounds */
DeferredDisplayState* const state = createState();
- if (op->getLocalBounds(renderer.getDrawModifiers(), state->mBounds)) {
+ if (op->getLocalBounds(state->mBounds)) {
if (state->mBounds.isEmpty()) {
// valid empty bounds, don't bother deferring
tryRecycleState(state);
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 48489c2..8a015b2 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -38,7 +38,6 @@ class SaveLayerOp;
class StateOp;
class DeferredDisplayState;
-class OpenGLRenderer;
class Batch;
class DrawBatch;
diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp
index 97e9bf6..8e99b9a 100644
--- a/libs/hwui/DeferredLayerUpdater.cpp
+++ b/libs/hwui/DeferredLayerUpdater.cpp
@@ -27,8 +27,7 @@ static void defaultLayerDestroyer(Layer* layer) {
}
DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, LayerDestroyer destroyer)
- : mDisplayList(0)
- , mSurfaceTexture(0)
+ : mSurfaceTexture(0)
, mTransform(0)
, mNeedsGLContextAttach(false)
, mUpdateTexImage(false)
@@ -41,7 +40,6 @@ DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, LayerDestroyer destroye
mColorFilter = SkSafeRef(mLayer->getColorFilter());
mAlpha = mLayer->getAlpha();
mMode = mLayer->getMode();
- mDirtyRect.setEmpty();
if (!mDestroyer) {
mDestroyer = defaultLayerDestroyer;
@@ -60,33 +58,13 @@ void DeferredLayerUpdater::setPaint(const SkPaint* paint) {
SkRefCnt_SafeAssign(mColorFilter, colorFilter);
}
-void DeferredLayerUpdater::setDisplayList(RenderNode* displayList,
- int left, int top, int right, int bottom) {
- mDisplayList = displayList;
- if (mDirtyRect.isEmpty()) {
- mDirtyRect.set(left, top, right, bottom);
- } else {
- mDirtyRect.unionWith(Rect(left, top, right, bottom));
- }
-}
-
bool DeferredLayerUpdater::apply(TreeInfo& info) {
bool success = true;
// These properties are applied the same to both layer types
mLayer->setColorFilter(mColorFilter);
mLayer->setAlpha(mAlpha, mMode);
- if (mDisplayList.get()) {
- if (mWidth != mLayer->layer.getWidth() || mHeight != mLayer->layer.getHeight()) {
- success = LayerRenderer::resizeLayer(mLayer, mWidth, mHeight);
- }
- mLayer->setBlend(mBlend);
- mDisplayList->prepareTree(info);
- mLayer->updateDeferred(mDisplayList.get(),
- mDirtyRect.left, mDirtyRect.top, mDirtyRect.right, mDirtyRect.bottom);
- mDirtyRect.setEmpty();
- mDisplayList = 0;
- } else if (mSurfaceTexture.get()) {
+ if (mSurfaceTexture.get()) {
if (mNeedsGLContextAttach) {
mNeedsGLContextAttach = false;
mSurfaceTexture->attachToContext(mLayer->getTexture());
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index b7cfe80..c76bd5e 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -23,7 +23,6 @@
#include <utils/StrongPointer.h>
#include "Layer.h"
-#include "OpenGLRenderer.h"
#include "Rect.h"
#include "RenderNode.h"
@@ -74,9 +73,6 @@ public:
mTransform = matrix ? new SkMatrix(*matrix) : 0;
}
- ANDROID_API void setDisplayList(RenderNode* displayList,
- int left, int top, int right, int bottom);
-
ANDROID_API void setPaint(const SkPaint* paint);
ANDROID_API bool apply(TreeInfo& info);
@@ -94,11 +90,6 @@ private:
int mAlpha;
SkXfermode::Mode mMode;
- // Layer type specific properties
- // displayList and surfaceTexture are mutually exclusive, only 1 may be set
- // dirtyRect is only valid if displayList is set
- sp<RenderNode> mDisplayList;
- Rect mDirtyRect;
sp<GLConsumer> mSurfaceTexture;
SkMatrix* mTransform;
bool mNeedsGLContextAttach;
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 96c6292..e38b532 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -80,10 +80,6 @@ void DisplayListData::cleanupResources() {
delete paths.itemAt(i);
}
- for (size_t i = 0; i < matrices.size(); i++) {
- delete matrices.itemAt(i);
- }
-
bitmapResources.clear();
ownedBitmapResources.clear();
patchResources.clear();
@@ -91,12 +87,11 @@ void DisplayListData::cleanupResources() {
paints.clear();
regions.clear();
paths.clear();
- matrices.clear();
layers.clear();
}
-void DisplayListData::addChild(DrawDisplayListOp* op) {
- LOG_ALWAYS_FATAL_IF(!op->renderNode(), "DrawDisplayListOp with no render node!");
+void DisplayListData::addChild(DrawRenderNodeOp* op) {
+ LOG_ALWAYS_FATAL_IF(!op->renderNode(), "DrawRenderNodeOp with no render node!");
mChildren.push(op);
mReferenceHolders.push(op->renderNode());
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 11e78b0..bfffbb4 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -61,7 +61,7 @@ class ClipRectOp;
class SaveLayerOp;
class SaveOp;
class RestoreToCountOp;
-class DrawDisplayListOp;
+class DrawRenderNodeOp;
/**
* Holds data used in the playback a tree of DisplayLists.
@@ -125,7 +125,6 @@ public:
Vector<const SkPath*> paths;
SortedVector<const SkPath*> sourcePaths;
Vector<const SkRegion*> regions;
- Vector<const SkMatrix*> matrices;
Vector<Layer*> layers;
uint32_t functorCount;
bool hasDrawOps;
@@ -134,8 +133,8 @@ public:
return !displayListOps.size();
}
- void addChild(DrawDisplayListOp* childOp);
- const Vector<DrawDisplayListOp*>& children() { return mChildren; }
+ void addChild(DrawRenderNodeOp* childOp);
+ const Vector<DrawRenderNodeOp*>& children() { return mChildren; }
void refProperty(CanvasPropertyPrimitive* prop) {
mReferenceHolders.push(prop);
@@ -149,7 +148,7 @@ private:
Vector< sp<VirtualLightRefBase> > mReferenceHolders;
// list of children display lists for quick, non-drawing traversal
- Vector<DrawDisplayListOp*> mChildren;
+ Vector<DrawRenderNodeOp*> mChildren;
void cleanupResources();
};
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 3281116..5ed04a0 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -171,7 +171,7 @@ public:
*
* returns true if bounds exist
*/
- virtual bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) {
+ virtual bool getLocalBounds(Rect& localBounds) {
return false;
}
@@ -253,7 +253,7 @@ public:
// default empty constructor for bounds, to be overridden in child constructor body
DrawBoundedOp(const SkPaint* paint): DrawOp(paint) { }
- bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) {
+ bool getLocalBounds(Rect& localBounds) {
localBounds.set(mLocalBounds);
OpenGLRenderer::TextShadow textShadow;
if (OpenGLRenderer::getTextShadow(mPaint, &textShadow)) {
@@ -472,7 +472,7 @@ private:
class SetMatrixOp : public StateOp {
public:
- SetMatrixOp(const SkMatrix* matrix)
+ SetMatrixOp(const SkMatrix& matrix)
: mMatrix(matrix) {}
virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
@@ -480,22 +480,22 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- if (mMatrix) {
- OP_LOG("SetMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(mMatrix));
- } else {
+ if (mMatrix.isIdentity()) {
OP_LOGS("SetMatrix (reset)");
+ } else {
+ OP_LOG("SetMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix));
}
}
virtual const char* name() { return "SetMatrix"; }
private:
- const SkMatrix* mMatrix;
+ const SkMatrix mMatrix;
};
class ConcatMatrixOp : public StateOp {
public:
- ConcatMatrixOp(const SkMatrix* matrix)
+ ConcatMatrixOp(const SkMatrix& matrix)
: mMatrix(matrix) {}
virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
@@ -503,13 +503,13 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(mMatrix));
+ OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix));
}
virtual const char* name() { return "ConcatMatrix"; }
private:
- const SkMatrix* mMatrix;
+ const SkMatrix mMatrix;
};
class ClipOp : public StateOp {
@@ -746,10 +746,10 @@ protected:
class DrawBitmapMatrixOp : public DrawBoundedOp {
public:
- DrawBitmapMatrixOp(const SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint)
+ DrawBitmapMatrixOp(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint)
: DrawBoundedOp(paint), mBitmap(bitmap), mMatrix(matrix) {
mLocalBounds.set(0, 0, bitmap->width(), bitmap->height());
- const mat4 transform(*matrix);
+ const mat4 transform(matrix);
transform.mapRect(mLocalBounds);
}
@@ -758,7 +758,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw bitmap %p matrix " SK_MATRIX_STRING, mBitmap, SK_MATRIX_ARGS(mMatrix));
+ OP_LOG("Draw bitmap %p matrix " SK_MATRIX_STRING, mBitmap, SK_MATRIX_ARGS(&mMatrix));
}
virtual const char* name() { return "DrawBitmapMatrix"; }
@@ -770,7 +770,7 @@ public:
private:
const SkBitmap* mBitmap;
- const SkMatrix* mMatrix;
+ const SkMatrix mMatrix;
};
class DrawBitmapRectOp : public DrawBoundedOp {
@@ -788,7 +788,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw bitmap %p src="RECT_STRING", dst="RECT_STRING,
+ OP_LOG("Draw bitmap %p src=" RECT_STRING ", dst=" RECT_STRING,
mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds));
}
@@ -978,7 +978,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw patch "RECT_STRING, RECT_ARGS(mLocalBounds));
+ OP_LOG("Draw patch " RECT_STRING, RECT_ARGS(mLocalBounds));
}
virtual const char* name() { return "DrawPatch"; }
@@ -1029,7 +1029,7 @@ public:
DrawStrokableOp(float left, float top, float right, float bottom, const SkPaint* paint)
: DrawBoundedOp(left, top, right, bottom, paint) {};
- bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) {
+ bool getLocalBounds(Rect& localBounds) {
localBounds.set(mLocalBounds);
if (mPaint && mPaint->getStyle() != SkPaint::kFill_Style) {
localBounds.outset(strokeWidthOutset());
@@ -1060,7 +1060,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw Rect "RECT_STRING, RECT_ARGS(mLocalBounds));
+ OP_LOG("Draw Rect " RECT_STRING, RECT_ARGS(mLocalBounds));
}
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
@@ -1111,15 +1111,15 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw RoundRect "RECT_STRING", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
+ OP_LOG("Draw RoundRect " RECT_STRING ", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
}
virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
const DeferredDisplayState& state) {
DrawStrokableOp::onDefer(renderer, deferInfo, state);
if (!mPaint->getPathEffect()) {
- renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix,
- mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy, mPaint);
+ renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix, *mPaint,
+ mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy);
}
}
@@ -1184,7 +1184,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw Oval "RECT_STRING, RECT_ARGS(mLocalBounds));
+ OP_LOG("Draw Oval " RECT_STRING, RECT_ARGS(mLocalBounds));
}
virtual const char* name() { return "DrawOval"; }
@@ -1204,7 +1204,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw Arc "RECT_STRING", start %f, sweep %f, useCenter %d",
+ OP_LOG("Draw Arc " RECT_STRING ", start %f, sweep %f, useCenter %d",
RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter);
}
@@ -1241,7 +1241,7 @@ public:
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
+ OP_LOG("Draw Path %p in " RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
}
virtual const char* name() { return "DrawPath"; }
@@ -1399,7 +1399,7 @@ public:
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
Rect bounds;
- getLocalBounds(renderer.getDrawModifiers(), bounds);
+ getLocalBounds(bounds);
return renderer.drawText(mText, mBytesCount, mCount, mX, mY,
mPositions, getPaint(renderer), mTotalAdvance, bounds);
}
@@ -1465,48 +1465,48 @@ private:
Functor* mFunctor;
};
-class DrawDisplayListOp : public DrawBoundedOp {
- friend class RenderNode; // grant DisplayList access to info of child
+class DrawRenderNodeOp : public DrawBoundedOp {
+ friend class RenderNode; // grant RenderNode access to info of child
public:
- DrawDisplayListOp(RenderNode* displayList, int flags, const mat4& transformFromParent)
- : DrawBoundedOp(0, 0, displayList->getWidth(), displayList->getHeight(), 0),
- mDisplayList(displayList), mFlags(flags), mTransformFromParent(transformFromParent) {}
+ DrawRenderNodeOp(RenderNode* renderNode, int flags, const mat4& transformFromParent)
+ : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), 0),
+ mRenderNode(renderNode), mFlags(flags), mTransformFromParent(transformFromParent) {}
virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
bool useQuickReject) {
- if (mDisplayList && mDisplayList->isRenderable() && !mSkipInOrderDraw) {
- mDisplayList->deferNodeInParent(deferStruct, level + 1);
+ if (mRenderNode && mRenderNode->isRenderable() && !mSkipInOrderDraw) {
+ mRenderNode->defer(deferStruct, level + 1);
}
}
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
bool useQuickReject) {
- if (mDisplayList && mDisplayList->isRenderable() && !mSkipInOrderDraw) {
- mDisplayList->replayNodeInParent(replayStruct, level + 1);
+ if (mRenderNode && mRenderNode->isRenderable() && !mSkipInOrderDraw) {
+ mRenderNode->replay(replayStruct, level + 1);
}
}
- // NOT USED since replay() is overridden
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- return DrawGlInfo::kStatusDone;
+ LOG_ALWAYS_FATAL("should not be called, because replay() is overridden");
+ return 0;
}
virtual void output(int level, uint32_t logFlags) const {
- OP_LOG("Draw Display List %p, flags %#x", mDisplayList, mFlags);
- if (mDisplayList && (logFlags & kOpLogFlag_Recurse)) {
- mDisplayList->output(level + 1);
+ OP_LOG("Draw Display List %p, flags %#x", mRenderNode, mFlags);
+ if (mRenderNode && (logFlags & kOpLogFlag_Recurse)) {
+ mRenderNode->output(level + 1);
}
}
- virtual const char* name() { return "DrawDisplayList"; }
+ virtual const char* name() { return "DrawRenderNode"; }
- RenderNode* renderNode() { return mDisplayList; }
+ RenderNode* renderNode() { return mRenderNode; }
private:
- RenderNode* mDisplayList;
+ RenderNode* mRenderNode;
const int mFlags;
///////////////////////////
- // Properties below are used by DisplayList::computeOrderingImpl() and iterate()
+ // Properties below are used by RenderNode::computeOrderingImpl() and issueOperations()
///////////////////////////
/**
* Records transform vs parent, used for computing total transform without rerunning DL contents
@@ -1514,12 +1514,12 @@ private:
const mat4 mTransformFromParent;
/**
- * Holds the transformation between the projection surface ViewGroup and this DisplayList
+ * Holds the transformation between the projection surface ViewGroup and this RenderNode
* drawing instance. Represents any translations / transformations done within the drawing of
* the compositing ancestor ViewGroup's draw, before the draw of the View represented by this
* DisplayList draw instance.
*
- * Note: doesn't include any transformation recorded within the DisplayList and its properties.
+ * Note: doesn't include transformation within the RenderNode, or its properties.
*/
mat4 mTransformFromCompositingAncestor;
bool mSkipInOrderDraw;
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 229afdf..7789358 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -141,14 +141,12 @@ void DisplayListRenderer::skew(float sx, float sy) {
StatefulBaseRenderer::skew(sx, sy);
}
-void DisplayListRenderer::setMatrix(const SkMatrix* matrix) {
- matrix = refMatrix(matrix);
+void DisplayListRenderer::setMatrix(const SkMatrix& matrix) {
addStateOp(new (alloc()) SetMatrixOp(matrix));
StatefulBaseRenderer::setMatrix(matrix);
}
-void DisplayListRenderer::concatMatrix(const SkMatrix* matrix) {
- matrix = refMatrix(matrix);
+void DisplayListRenderer::concatMatrix(const SkMatrix& matrix) {
addStateOp(new (alloc()) ConcatMatrixOp(matrix));
StatefulBaseRenderer::concatMatrix(matrix);
}
@@ -171,18 +169,16 @@ bool DisplayListRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) {
return StatefulBaseRenderer::clipRegion(region, op);
}
-status_t DisplayListRenderer::drawDisplayList(RenderNode* displayList,
- Rect& dirty, int32_t flags) {
+status_t DisplayListRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t flags) {
// dirty is an out parameter and should not be recorded,
// it matters only when replaying the display list
- if (displayList->stagingProperties().isProjectionReceiver()) {
+ if (renderNode->stagingProperties().isProjectionReceiver()) {
// use staging property, since recording on UI thread
mDisplayListData->projectionReceiveIndex = mDisplayListData->displayListOps.size();
}
- DrawDisplayListOp* op = new (alloc()) DrawDisplayListOp(displayList,
- flags, *currentTransform());
+ DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(renderNode, flags, *currentTransform());
addDrawOp(op);
mDisplayListData->addChild(op);
return DrawGlInfo::kStatusDone;
@@ -203,10 +199,9 @@ status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, float left, flo
return DrawGlInfo::kStatusDone;
}
-status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix,
+status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix,
const SkPaint* paint) {
bitmap = refBitmap(bitmap);
- matrix = refMatrix(matrix);
paint = refPaint(paint);
addDrawOp(new (alloc()) DrawBitmapMatrixOp(bitmap, matrix, paint));
@@ -311,6 +306,10 @@ status_t DisplayListRenderer::drawOval(float left, float top, float right, float
status_t DisplayListRenderer::drawArc(float left, float top, float right, float bottom,
float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) {
+ if (fabs(sweepAngle) > 360.0f) {
+ return drawOval(left, top, right, bottom, paint);
+ }
+
paint = refPaint(paint);
addDrawOp(new (alloc()) DrawArcOp(left, top, right, bottom,
startAngle, sweepAngle, useCenter, paint));
@@ -426,7 +425,7 @@ void DisplayListRenderer::addStateOp(StateOp* op) {
void DisplayListRenderer::addDrawOp(DrawOp* op) {
Rect localBounds;
- if (op->getLocalBounds(mDrawModifiers, localBounds)) {
+ if (op->getLocalBounds(localBounds)) {
bool rejected = quickRejectConservative(localBounds.left, localBounds.top,
localBounds.right, localBounds.bottom);
op->setQuickRejected(rejected);
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index dff4f6c..8ca9af1 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -23,7 +23,6 @@
#include <cutils/compiler.h>
#include "DisplayListLogBuffer.h"
-#include "OpenGLRenderer.h"
#include "RenderNode.h"
namespace android {
@@ -53,12 +52,12 @@ class StateOp;
/**
* Records drawing commands in a display list for later playback into an OpenGLRenderer.
*/
-class DisplayListRenderer: public OpenGLRenderer {
+class ANDROID_API DisplayListRenderer: public StatefulBaseRenderer {
public:
- ANDROID_API DisplayListRenderer();
+ DisplayListRenderer();
virtual ~DisplayListRenderer();
- ANDROID_API DisplayListData* finishRecording();
+ DisplayListData* finishRecording();
virtual bool isRecording() const { return true; }
@@ -81,13 +80,13 @@ public:
const SkPaint* paint, int flags);
// Matrix
- virtual void translate(float dx, float dy, float dz);
+ virtual void translate(float dx, float dy, float dz = 0.0f);
virtual void rotate(float degrees);
virtual void scale(float sx, float sy);
virtual void skew(float sx, float sy);
- virtual void setMatrix(const SkMatrix* matrix);
- virtual void concatMatrix(const SkMatrix* matrix);
+ virtual void setMatrix(const SkMatrix& matrix);
+ virtual void concatMatrix(const SkMatrix& matrix);
// Clip
virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
@@ -98,6 +97,10 @@ public:
virtual void resetPaintFilter();
virtual void setupPaintFilter(int clearBits, int setBits);
+ bool isCurrentTransformSimple() {
+ return currentTransform()->isSimple();
+ }
+
// ----------------------------------------------------------------------------
// Canvas draw operations
// ----------------------------------------------------------------------------
@@ -106,7 +109,7 @@ public:
// Bitmap-based
virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top,
const SkPaint* paint);
- virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix,
+ virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix,
const SkPaint* paint);
virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
@@ -148,16 +151,10 @@ public:
// Canvas draw operations - special
// ----------------------------------------------------------------------------
virtual status_t drawLayer(Layer* layer, float x, float y);
- virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty,
- int32_t replayFlags);
+ virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags);
// TODO: rename for consistency
virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
-protected:
- // NOTE: must override these to avoid calling into super class, which calls GL. These may be
- // removed once DisplayListRenderer no longer inherits from OpenGLRenderer
- virtual void onViewportInitialized() {};
- virtual void onSnapshotRestored() {};
private:
void insertRestoreToCount();
@@ -238,17 +235,6 @@ private:
return regionCopy;
}
- inline const SkMatrix* refMatrix(const SkMatrix* matrix) {
- if (matrix) {
- // Copying the matrix is cheap and prevents against the user changing
- // the original matrix before the operation that uses it
- const SkMatrix* copy = new SkMatrix(*matrix);
- mDisplayListData->matrices.add(copy);
- return copy;
- }
- return matrix;
- }
-
inline Layer* refLayer(Layer* layer) {
mDisplayListData->layers.add(layer);
mCaches.resourceCache.incrementRefcount(layer);
diff --git a/libs/hwui/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp
index 971a66e..2409554 100644
--- a/libs/hwui/DrawProfiler.cpp
+++ b/libs/hwui/DrawProfiler.cpp
@@ -109,7 +109,7 @@ void DrawProfiler::finishFrame() {
mCurrentFrame = (mCurrentFrame + 1) % mDataSize;
}
-void DrawProfiler::unionDirty(Rect* dirty) {
+void DrawProfiler::unionDirty(SkRect* dirty) {
RETURN_IF_DISABLED();
// Not worth worrying about minimizing the dirty region for debugging, so just
// dirty the entire viewport.
diff --git a/libs/hwui/DrawProfiler.h b/libs/hwui/DrawProfiler.h
index c1aa1c6..7c06e5d 100644
--- a/libs/hwui/DrawProfiler.h
+++ b/libs/hwui/DrawProfiler.h
@@ -37,7 +37,7 @@ public:
void markPlaybackEnd();
void finishFrame();
- void unionDirty(Rect* dirty);
+ void unionDirty(SkRect* dirty);
void draw(OpenGLRenderer* canvas);
void dumpData(int fd);
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 4407ab0..bf0ab5c 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -592,7 +592,7 @@ void FontRenderer::setFont(const SkPaint* paint, const mat4& matrix) {
}
FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text,
- uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
+ uint32_t startIndex, uint32_t len, int numGlyphs, float radius, const float* positions) {
checkInit();
DropShadow image;
@@ -613,8 +613,9 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co
Rect bounds;
mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
- uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
- uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
+ uint32_t intRadius = Blur::convertRadiusToInt(radius);
+ uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius;
+ uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius;
uint32_t maxSize = Caches::getInstance().maxTextureSize;
if (paddedWidth > maxSize || paddedHeight > maxSize) {
@@ -635,8 +636,8 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co
memset(dataBuffer, 0, size);
- int penX = radius - bounds.left;
- int penY = radius - bounds.bottom;
+ int penX = intRadius - bounds.left;
+ int penY = intRadius - bounds.bottom;
if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) {
// text has non-whitespace, so draw and blur to create the shadow
@@ -727,9 +728,10 @@ void FontRenderer::removeFont(const Font* font) {
}
}
-void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
+void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) {
+ uint32_t intRadius = Blur::convertRadiusToInt(radius);
#ifdef ANDROID_ENABLE_RENDERSCRIPT
- if (width * height * radius >= RS_MIN_INPUT_CUTOFF) {
+ if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF) {
uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
if (mRs == 0) {
@@ -768,12 +770,12 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int
}
#endif
- float *gaussian = new float[2 * radius + 1];
- Blur::generateGaussianWeights(gaussian, radius);
+ float *gaussian = new float[2 * intRadius + 1];
+ Blur::generateGaussianWeights(gaussian, intRadius);
uint8_t* scratch = new uint8_t[width * height];
- Blur::horizontal(gaussian, radius, *image, scratch, width, height);
- Blur::vertical(gaussian, radius, scratch, *image, width, height);
+ Blur::horizontal(gaussian, intRadius, *image, scratch, width, height);
+ Blur::vertical(gaussian, intRadius, scratch, *image, width, height);
delete[] gaussian;
delete[] scratch;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 9259028..8ce22b0 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -129,7 +129,7 @@ public:
// After renderDropShadow returns, the called owns the memory in DropShadow.image
// and is responsible for releasing it when it's done with it
DropShadow renderDropShadow(const SkPaint* paint, const char *text, uint32_t startIndex,
- uint32_t len, int numGlyphs, uint32_t radius, const float* positions);
+ uint32_t len, int numGlyphs, float radius, const float* positions);
void setTextureFiltering(bool linearFiltering) {
mLinearFiltering = linearFiltering;
@@ -218,7 +218,7 @@ private:
int32_t width, int32_t height);
// the input image handle may have its pointer replaced (to avoid copies)
- void blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius);
+ void blurImage(uint8_t** image, int32_t width, int32_t height, float radius);
};
}; // namespace uirenderer
diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp
index 1f84b86..fc0e8a0 100644
--- a/libs/hwui/Interpolator.cpp
+++ b/libs/hwui/Interpolator.cpp
@@ -112,6 +112,10 @@ float LUTInterpolator::interpolate(float input) {
int i1 = (int) ipart;
int i2 = MathUtils::min(i1 + 1, mSize - 1);
+ LOG_ALWAYS_FATAL_IF(i1 < 0 || i2 < 0, "negatives in interpolation!"
+ " i1=%d, i2=%d, input=%f, lutpos=%f, size=%zu, values=%p, ipart=%f, weight=%f",
+ i1, i2, input, lutpos, mSize, mValues, ipart, weight);
+
float v1 = mValues[i1];
float v2 = mValues[i2];
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 6a2ef2a..73a4da5 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -20,6 +20,7 @@
#include "Caches.h"
#include "DeferredDisplayList.h"
+#include "RenderState.h"
#include "Layer.h"
#include "LayerRenderer.h"
#include "OpenGLRenderer.h"
@@ -28,8 +29,10 @@
namespace android {
namespace uirenderer {
-Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight):
- caches(Caches::getInstance()), texture(caches) {
+Layer::Layer(RenderState& renderState, const uint32_t layerWidth, const uint32_t layerHeight)
+ : caches(Caches::getInstance())
+ , renderState(renderState)
+ , texture(caches) {
mesh = NULL;
meshElementCount = 0;
cacheable = true;
@@ -41,7 +44,7 @@ Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight):
colorFilter = NULL;
deferredUpdateScheduled = false;
renderer = NULL;
- displayList = NULL;
+ renderNode = NULL;
fbo = 0;
stencil = NULL;
debugDrawUpdate = false;
@@ -72,7 +75,7 @@ uint32_t Layer::computeIdealHeight(uint32_t layerHeight) {
void Layer::requireRenderer() {
if (!renderer) {
- renderer = new LayerRenderer(this);
+ renderer = new LayerRenderer(renderState, this);
renderer->initProperties();
}
}
@@ -123,28 +126,26 @@ bool Layer::resize(const uint32_t width, const uint32_t height) {
void Layer::removeFbo(bool flush) {
if (stencil) {
- GLuint previousFbo;
- glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
- if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ GLuint previousFbo = renderState.getFramebuffer();
+ renderState.bindFramebuffer(fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
- if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+ renderState.bindFramebuffer(previousFbo);
caches.renderBufferCache.put(stencil);
stencil = NULL;
}
if (fbo) {
- if (flush) LayerRenderer::flushLayer(this);
+ if (flush) LayerRenderer::flushLayer(renderState, this);
// If put fails the cache will delete the FBO
caches.fboCache.put(fbo);
fbo = 0;
}
}
-void Layer::updateDeferred(RenderNode* displayList,
- int left, int top, int right, int bottom) {
+void Layer::updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom) {
requireRenderer();
- this->displayList = displayList;
+ this->renderNode = renderNode;
const Rect r(left, top, right, bottom);
dirtyRect.unionWith(r);
deferredUpdateScheduled = true;
@@ -185,6 +186,7 @@ void Layer::deleteTexture() {
}
void Layer::clearTexture() {
+ caches.unbindTexture(texture.id);
texture.id = 0;
}
@@ -218,14 +220,14 @@ void Layer::defer() {
renderer->setupFrameState(dirtyRect.left, dirtyRect.top,
dirtyRect.right, dirtyRect.bottom, !isBlend());
- displayList->computeOrdering();
- displayList->deferNodeTree(deferredState);
+ renderNode->computeOrdering();
+ renderNode->defer(deferredState, 0);
deferredUpdateScheduled = false;
}
void Layer::cancelDefer() {
- displayList = NULL;
+ renderNode = NULL;
deferredUpdateScheduled = false;
if (deferredList) {
delete deferredList;
@@ -245,7 +247,7 @@ void Layer::flush() {
renderer->finish();
dirtyRect.setEmpty();
- displayList = NULL;
+ renderNode = NULL;
}
}
@@ -254,14 +256,14 @@ void Layer::render() {
renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
!isBlend());
- renderer->drawDisplayList(displayList.get(), dirtyRect, RenderNode::kReplayFlag_ClipChildren);
+ renderer->drawRenderNode(renderNode.get(), dirtyRect, RenderNode::kReplayFlag_ClipChildren);
renderer->finish();
dirtyRect.setEmpty();
deferredUpdateScheduled = false;
- displayList = NULL;
+ renderNode = NULL;
}
}; // namespace uirenderer
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 49610d5..0bf05d0 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -43,6 +43,7 @@ namespace uirenderer {
// Forward declarations
class Caches;
+class RenderState;
class OpenGLRenderer;
class RenderNode;
class DeferredDisplayList;
@@ -53,7 +54,7 @@ class DeferStateStruct;
*/
class Layer {
public:
- Layer(const uint32_t layerWidth, const uint32_t layerHeight);
+ Layer(RenderState& renderState, const uint32_t layerWidth, const uint32_t layerHeight);
~Layer();
static uint32_t computeIdealWidth(uint32_t layerWidth);
@@ -85,8 +86,7 @@ public:
regionRect.translate(layer.left, layer.top);
}
- void updateDeferred(RenderNode* displayList,
- int left, int top, int right, int bottom);
+ void updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom);
inline uint32_t getWidth() const {
return texture.width;
@@ -297,7 +297,7 @@ public:
*/
bool deferredUpdateScheduled;
OpenGLRenderer* renderer;
- sp<RenderNode> displayList;
+ sp<RenderNode> renderNode;
Rect dirtyRect;
bool debugDrawUpdate;
bool hasDrawnSinceUpdate;
@@ -307,6 +307,8 @@ private:
Caches& caches;
+ RenderState& renderState;
+
/**
* Name of the FBO used to render the layer. If the name is 0
* this layer is not backed by an FBO, but a simple texture.
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 6be0146..13869aa 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -91,7 +91,7 @@ void LayerCache::clear() {
mCache.clear();
}
-Layer* LayerCache::get(const uint32_t width, const uint32_t height) {
+Layer* LayerCache::get(RenderState& renderState, const uint32_t width, const uint32_t height) {
Layer* layer = NULL;
LayerEntry entry(width, height);
@@ -108,7 +108,7 @@ Layer* LayerCache::get(const uint32_t width, const uint32_t height) {
} else {
LAYER_LOGD("Creating new layer %dx%d", entry.mWidth, entry.mHeight);
- layer = new Layer(entry.mWidth, entry.mHeight);
+ layer = new Layer(renderState, entry.mWidth, entry.mHeight);
layer->setBlend(true);
layer->setEmpty(true);
layer->setFbo(0);
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
index 221bfe0..1b0fc2c 100644
--- a/libs/hwui/LayerCache.h
+++ b/libs/hwui/LayerCache.h
@@ -24,6 +24,8 @@
namespace android {
namespace uirenderer {
+class RenderState;
+
///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////
@@ -55,7 +57,7 @@ public:
* @param width The desired width of the layer
* @param height The desired height of the layer
*/
- Layer* get(const uint32_t width, const uint32_t height);
+ Layer* get(RenderState& renderState, const uint32_t width, const uint32_t height);
/**
* Adds the layer to the cache. The layer will not be added if there is
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index df9aee5..873baf5 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -20,6 +20,7 @@
#include <private/hwui/DrawGlInfo.h>
+#include "RenderState.h"
#include "LayerCache.h"
#include "LayerRenderer.h"
#include "Matrix.h"
@@ -33,7 +34,9 @@ namespace uirenderer {
// Rendering
///////////////////////////////////////////////////////////////////////////////
-LayerRenderer::LayerRenderer(Layer* layer): mLayer(layer) {
+LayerRenderer::LayerRenderer(RenderState& renderState, Layer* layer)
+ : OpenGLRenderer(renderState)
+ , mLayer(layer) {
}
LayerRenderer::~LayerRenderer() {
@@ -43,7 +46,7 @@ status_t LayerRenderer::prepareDirty(float left, float top, float right, float b
bool opaque) {
LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo());
- glBindFramebuffer(GL_FRAMEBUFFER, mLayer->getFbo());
+ renderState().bindFramebuffer(mLayer->getFbo());
const float width = mLayer->layer.getWidth();
const float height = mLayer->layer.getHeight();
@@ -180,7 +183,7 @@ void LayerRenderer::generateMesh() {
// Layers management
///////////////////////////////////////////////////////////////////////////////
-Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) {
+Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height) {
LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height);
Caches& caches = Caches::getInstance();
@@ -191,7 +194,7 @@ Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) {
}
caches.activeTexture(0);
- Layer* layer = caches.layerCache.get(width, height);
+ Layer* layer = caches.layerCache.get(renderState, width, height);
if (!layer) {
ALOGW("Could not obtain a layer");
return NULL;
@@ -221,10 +224,9 @@ Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) {
layer->setDirty(true);
layer->region.clear();
- GLuint previousFbo;
- glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
+ GLuint previousFbo = renderState.getFramebuffer();
- glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo());
+ renderState.bindFramebuffer(layer->getFbo());
layer->bindTexture();
// Initialize the texture if needed
@@ -235,7 +237,7 @@ Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) {
// This should only happen if we run out of memory
if (glGetError() != GL_NO_ERROR) {
ALOGE("Could not allocate texture for layer (fbo=%d %dx%d)", fbo, width, height);
- glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+ renderState.bindFramebuffer(previousFbo);
caches.resourceCache.decrementRefcount(layer);
return NULL;
}
@@ -244,7 +246,7 @@ Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
layer->getTexture(), 0);
- glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+ renderState.bindFramebuffer(previousFbo);
return layer;
}
@@ -265,10 +267,10 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) {
return true;
}
-Layer* LayerRenderer::createTextureLayer() {
+Layer* LayerRenderer::createTextureLayer(RenderState& renderState) {
LAYER_RENDERER_LOGD("Creating new texture layer");
- Layer* layer = new Layer(0, 0);
+ Layer* layer = new Layer(renderState, 0, 0);
layer->setCacheable(false);
layer->setTextureLayer(true);
layer->setEmpty(true);
@@ -332,27 +334,32 @@ void LayerRenderer::destroyLayerDeferred(Layer* layer) {
}
}
-void LayerRenderer::flushLayer(Layer* layer) {
+void LayerRenderer::flushLayer(RenderState& renderState, Layer* layer) {
#ifdef GL_EXT_discard_framebuffer
+ if (!layer) return;
+
GLuint fbo = layer->getFbo();
- if (layer && fbo) {
+ if (fbo) {
// If possible, discard any enqueud operations on deferred
// rendering architectures
if (Extensions::getInstance().hasDiscardFramebuffer()) {
- GLuint previousFbo;
- glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
- if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ GLuint previousFbo = renderState.getFramebuffer();
+ if (fbo != previousFbo) {
+ renderState.bindFramebuffer(fbo);
+ }
const GLenum attachments[] = { GL_COLOR_ATTACHMENT0 };
glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments);
- if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+ if (fbo != previousFbo) {
+ renderState.bindFramebuffer(previousFbo);
+ }
}
}
#endif
}
-bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
+bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap) {
Caches& caches = Caches::getInstance();
if (layer && bitmap->width() <= caches.maxTextureSize &&
bitmap->height() <= caches.maxTextureSize) {
@@ -367,7 +374,8 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
GLuint texture;
GLuint previousFbo;
- GLuint previousViewport[4];
+ GLsizei previousViewportWidth;
+ GLsizei previousViewportHeight;
GLenum format;
GLenum type;
@@ -402,9 +410,9 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
layer->setAlpha(255, SkXfermode::kSrc_Mode);
layer->setFbo(fbo);
- glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
- glGetIntegerv(GL_VIEWPORT, (GLint*) &previousViewport);
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+ previousFbo = renderState.getFramebuffer();
+ renderState.getViewport(&previousViewportWidth, &previousViewportHeight);
+ renderState.bindFramebuffer(fbo);
glGenTextures(1, &texture);
if ((error = glGetError()) != GL_NO_ERROR) goto error;
@@ -429,7 +437,7 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
if ((error = glGetError()) != GL_NO_ERROR) goto error;
{
- LayerRenderer renderer(layer);
+ LayerRenderer renderer(renderState, layer);
renderer.setViewport(bitmap->width(), bitmap->height());
renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
bitmap->width(), bitmap->height(), !layer->isBlend());
@@ -469,13 +477,12 @@ error:
}
#endif
- glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+ renderState.bindFramebuffer(previousFbo);
layer->setAlpha(alpha, mode);
layer->setFbo(previousLayerFbo);
caches.deleteTexture(texture);
caches.fboCache.put(fbo);
- glViewport(previousViewport[0], previousViewport[1],
- previousViewport[2], previousViewport[3]);
+ renderState.setViewport(previousViewportWidth, previousViewportHeight);
return status;
}
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index e79e7b8..9202e49 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -27,6 +27,8 @@
namespace android {
namespace uirenderer {
+class RenderState;
+
///////////////////////////////////////////////////////////////////////////////
// Defines
///////////////////////////////////////////////////////////////////////////////
@@ -44,7 +46,7 @@ namespace uirenderer {
class LayerRenderer: public OpenGLRenderer {
public:
- ANDROID_API LayerRenderer(Layer* layer);
+ LayerRenderer(RenderState& renderState, Layer* layer);
virtual ~LayerRenderer();
virtual void onViewportInitialized(int width, int height) { /* do nothing */ }
@@ -52,16 +54,16 @@ public:
virtual status_t clear(float left, float top, float right, float bottom, bool opaque);
virtual void finish();
- ANDROID_API static Layer* createTextureLayer();
- ANDROID_API static Layer* createRenderLayer(uint32_t width, uint32_t height);
- ANDROID_API static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height);
- ANDROID_API static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
+ static Layer* createTextureLayer(RenderState& renderState);
+ static Layer* createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height);
+ static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height);
+ static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
bool isOpaque, bool forceFilter, GLenum renderTarget, float* textureTransform);
- ANDROID_API static void destroyLayer(Layer* layer);
+ static void destroyLayer(Layer* layer);
ANDROID_API static void destroyLayerDeferred(Layer* layer);
- ANDROID_API static bool copyLayer(Layer* layer, SkBitmap* bitmap);
+ static bool copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap);
- static void flushLayer(Layer* layer);
+ static void flushLayer(RenderState& renderState, Layer* layer);
protected:
virtual void ensureStencilBuffer();
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 2268386..9f2014f 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -417,6 +417,8 @@ void Matrix4::mapPoint(float& x, float& y) const {
}
void Matrix4::mapRect(Rect& r) const {
+ if (isIdentity()) return;
+
if (isSimple()) {
MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]);
MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]);
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index e33a001..1c5c578 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -147,6 +147,7 @@ public:
data[kTranslateX] += x;
data[kTranslateY] += y;
data[kTranslateZ] += z;
+ mType |= kTypeUnknown;
} else {
// Doing a translation will only affect the translate bit of the type
// Save the type
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 31f399a..ede89d7 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -35,6 +35,7 @@
#include "DeferredDisplayList.h"
#include "DisplayListRenderer.h"
#include "Fence.h"
+#include "RenderState.h"
#include "PathTessellator.h"
#include "Properties.h"
#include "ShadowTessellator.h"
@@ -129,8 +130,10 @@ static inline T min(T a, T b) {
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-OpenGLRenderer::OpenGLRenderer():
- mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) {
+OpenGLRenderer::OpenGLRenderer(RenderState& renderState)
+ : mCaches(Caches::getInstance())
+ , mExtensions(Extensions::getInstance())
+ , mRenderState(renderState) {
// *set* draw modifiers to be 0
memset(&mDrawModifiers, 0, sizeof(mDrawModifiers));
mDrawModifiers.mOverrideLayerAlpha = 1.0f;
@@ -187,7 +190,7 @@ status_t OpenGLRenderer::startFrame() {
discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom);
- glViewport(0, 0, getWidth(), getHeight());
+ mRenderState.setViewport(getWidth(), getHeight());
// Functors break the tiling extension in pretty spectacular ways
// This ensures we don't use tiling when a functor is going to be
@@ -311,46 +314,9 @@ void OpenGLRenderer::finish() {
mFrameStarted = false;
}
-void OpenGLRenderer::interrupt() {
- if (mCaches.currentProgram) {
- if (mCaches.currentProgram->isInUse()) {
- mCaches.currentProgram->remove();
- mCaches.currentProgram = NULL;
- }
- }
- mCaches.resetActiveTexture();
- mCaches.unbindMeshBuffer();
- mCaches.unbindIndicesBuffer();
- mCaches.resetVertexPointers();
- mCaches.disableTexCoordsVertexArray();
- debugOverdraw(false, false);
-}
-
-void OpenGLRenderer::resume() {
- const Snapshot* snapshot = currentSnapshot();
- glViewport(0, 0, getViewportWidth(), getViewportHeight());
- glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo);
- debugOverdraw(true, false);
-
- glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-
- mCaches.scissorEnabled = glIsEnabled(GL_SCISSOR_TEST);
- mCaches.enableScissor();
- mCaches.resetScissor();
- dirtyClip();
-
- mCaches.activeTexture(0);
- mCaches.resetBoundTextures();
-
- mCaches.blend = true;
- glEnable(GL_BLEND);
- glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode);
- glBlendEquation(GL_FUNC_ADD);
-}
-
void OpenGLRenderer::resumeAfterLayer() {
- glViewport(0, 0, getViewportWidth(), getViewportHeight());
- glBindFramebuffer(GL_FRAMEBUFFER, currentSnapshot()->fbo);
+ mRenderState.setViewport(getViewportWidth(), getViewportHeight());
+ mRenderState.bindFramebuffer(currentSnapshot()->fbo);
debugOverdraw(true, false);
mCaches.resetScissor();
@@ -379,20 +345,19 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
info.height = getViewportHeight();
currentTransform()->copyTo(&info.transform[0]);
- bool dirtyClip = mDirtyClip;
+ bool prevDirtyClip = mDirtyClip;
// setup GL state for functor
if (mDirtyClip) {
setStencilFromClip(); // can issue draws, so must precede enableScissor()/interrupt()
}
- if (mCaches.enableScissor() || dirtyClip) {
+ if (mCaches.enableScissor() || prevDirtyClip) {
setScissorFromClip();
}
- interrupt();
- // call functor immediately after GL state setup
- (*functor)(DrawGlInfo::kModeDraw, &info);
+ mRenderState.invokeFunctor(functor, DrawGlInfo::kModeDraw, &info);
+ // Scissor may have been modified, reset dirty clip
+ dirtyClip();
- resume();
return DrawGlInfo::kStatusDrew;
}
@@ -413,17 +378,7 @@ void OpenGLRenderer::endMark() const {
}
void OpenGLRenderer::debugOverdraw(bool enable, bool clear) {
- if (mCaches.debugOverdraw && getTargetFbo() == 0) {
- if (clear) {
- mCaches.disableScissor();
- mCaches.stencil.clear();
- }
- if (enable) {
- mCaches.stencil.enableDebugWrite();
- } else {
- mCaches.stencil.disable();
- }
- }
+ mRenderState.debugOverdraw(enable, clear);
}
void OpenGLRenderer::renderOverdraw() {
@@ -474,8 +429,8 @@ void OpenGLRenderer::countOverdraw() {
///////////////////////////////////////////////////////////////////////////////
bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
- if (layer->deferredUpdateScheduled && layer->renderer &&
- layer->displayList.get() && layer->displayList->isRenderable()) {
+ if (layer->deferredUpdateScheduled && layer->renderer
+ && layer->renderNode.get() && layer->renderNode->isRenderable()) {
ATRACE_CALL();
Rect& dirty = layer->dirtyRect;
@@ -528,7 +483,7 @@ void OpenGLRenderer::updateLayers() {
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
mLayerUpdates.clear();
- glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
+ mRenderState.bindFramebuffer(getTargetFbo());
}
endMark();
}
@@ -556,7 +511,7 @@ void OpenGLRenderer::flushLayers() {
}
mLayerUpdates.clear();
- glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
+ mRenderState.bindFramebuffer(getTargetFbo());
endMark();
}
@@ -620,7 +575,7 @@ void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot&
bool restoreLayer = removed.flags & Snapshot::kFlagIsLayer;
if (restoreViewport) {
- glViewport(0, 0, getViewportWidth(), getViewportHeight());
+ mRenderState.setViewport(getViewportWidth(), getViewportHeight());
}
if (restoreClip) {
@@ -791,7 +746,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto
}
mCaches.activeTexture(0);
- Layer* layer = mCaches.layerCache.get(bounds.getWidth(), bounds.getHeight());
+ Layer* layer = mCaches.layerCache.get(mRenderState, bounds.getWidth(), bounds.getHeight());
if (!layer) {
return false;
}
@@ -853,7 +808,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
endTiling();
debugOverdraw(false, false);
// Bind texture to FBO
- glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo());
+ mRenderState.bindFramebuffer(layer->getFbo());
layer->bindTexture();
// Initialize the texture if needed
@@ -876,7 +831,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) {
dirtyClip();
// Change the ortho projection
- glViewport(0, 0, bounds.getWidth(), bounds.getHeight());
+ mRenderState.setViewport(bounds.getWidth(), bounds.getHeight());
return true;
}
@@ -907,7 +862,7 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto
layer->removeFbo(false);
// Unbind current FBO and restore previous one
- glBindFramebuffer(GL_FRAMEBUFFER, restored.fbo);
+ mRenderState.bindFramebuffer(restored.fbo);
debugOverdraw(true, false);
startTilingCurrentClip();
@@ -1931,25 +1886,24 @@ void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) {
// Drawing
///////////////////////////////////////////////////////////////////////////////
-status_t OpenGLRenderer::drawDisplayList(RenderNode* displayList, Rect& dirty,
- int32_t replayFlags) {
+status_t OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) {
status_t status;
// All the usual checks and setup operations (quickReject, setupDraw, etc.)
// will be performed by the display list itself
- if (displayList && displayList->isRenderable()) {
+ if (renderNode && renderNode->isRenderable()) {
// compute 3d ordering
- displayList->computeOrdering();
+ renderNode->computeOrdering();
if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
status = startFrame();
ReplayStateStruct replayStruct(*this, dirty, replayFlags);
- displayList->replayNodeTree(replayStruct);
+ renderNode->replay(replayStruct, 0);
return status | replayStruct.mDrawGlStatus;
}
bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs!
DeferredDisplayList deferredList(*currentClipRect(), avoidOverdraw);
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
- displayList->deferNodeTree(deferStruct);
+ renderNode->defer(deferStruct, 0);
flushLayers();
status = startFrame();
@@ -2043,10 +1997,10 @@ status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, float left, float to
return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix,
+status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix,
const SkPaint* paint) {
Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height());
- const mat4 transform(*matrix);
+ const mat4 transform(matrix);
transform.mapRect(r);
if (quickRejectSetupScissor(r.left, r.top, r.right, r.bottom)) {
@@ -2556,8 +2510,8 @@ status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float
return drawShape(left, top, texture, p);
}
- const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(*currentTransform(),
- right - left, bottom - top, rx, ry, p);
+ const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(
+ *currentTransform(), *p, right - left, bottom - top, rx, ry);
return drawVertexBuffer(left, top, *vertexBuffer, p);
}
@@ -2611,10 +2565,6 @@ status_t OpenGLRenderer::drawArc(float left, float top, float right, float botto
return DrawGlInfo::kStatusDone;
}
- if (fabs(sweepAngle) >= 360.0f) {
- return drawOval(left, top, right, bottom, p);
- }
-
// TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) {
mCaches.activeTexture(0);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index b9b369f..4ff5780 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -57,6 +57,7 @@ namespace android {
namespace uirenderer {
class DeferredDisplayState;
+class RenderState;
class RenderNode;
class TextSetupFunctor;
class VertexBuffer;
@@ -119,33 +120,31 @@ enum ModelViewMode {
*/
class OpenGLRenderer : public StatefulBaseRenderer {
public:
- ANDROID_API OpenGLRenderer();
+ OpenGLRenderer(RenderState& renderState);
virtual ~OpenGLRenderer();
- ANDROID_API void initProperties();
+ void initProperties();
virtual void onViewportInitialized();
virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque);
virtual void finish();
- virtual void interrupt();
- virtual void resume();
- ANDROID_API void setCountOverdrawEnabled(bool enabled) {
+ void setCountOverdrawEnabled(bool enabled) {
mCountOverdraw = enabled;
}
- ANDROID_API float getOverdraw() {
+ float getOverdraw() {
return mCountOverdraw ? mOverdraw : 0.0f;
}
virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
- ANDROID_API void pushLayerUpdate(Layer* layer);
- ANDROID_API void cancelLayerUpdate(Layer* layer);
- ANDROID_API void clearLayerUpdates();
- ANDROID_API void flushLayerUpdates();
+ void pushLayerUpdate(Layer* layer);
+ void cancelLayerUpdate(Layer* layer);
+ void clearLayerUpdates();
+ void flushLayerUpdates();
- ANDROID_API virtual int saveLayer(float left, float top, float right, float bottom,
+ virtual int saveLayer(float left, float top, float right, float bottom,
const SkPaint* paint, int flags) {
return saveLayer(left, top, right, bottom, paint, flags, NULL);
}
@@ -158,13 +157,13 @@ public:
int saveLayerDeferred(float left, float top, float right, float bottom,
const SkPaint* paint, int flags);
- virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1);
+ virtual status_t drawRenderNode(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1);
virtual status_t drawLayer(Layer* layer, float x, float y);
virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top,
const SkPaint* paint);
status_t drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
TextureVertex* vertices, bool pureTranslate, const Rect& bounds, const SkPaint* paint);
- virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix,
+ virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix,
const SkPaint* paint);
virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
@@ -231,7 +230,7 @@ public:
const DrawModifiers& getDrawModifiers() { return mDrawModifiers; }
void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; }
- ANDROID_API bool isCurrentTransformSimple() {
+ bool isCurrentTransformSimple() {
return currentTransform()->isSimple();
}
@@ -474,6 +473,8 @@ protected:
return false;
}
+ inline RenderState& renderState() { return mRenderState; }
+
private:
/**
* Discards the content of the framebuffer if supported by the driver.
@@ -977,6 +978,7 @@ private:
// Various caches
Caches& mCaches;
Extensions& mExtensions;
+ RenderState& mRenderState;
// List of rectangles to clear after saveLayer() is invoked
Vector<Rect*> mLayers;
@@ -1012,7 +1014,6 @@ private:
bool mSkipOutlineClip;
- friend class DisplayListRenderer;
friend class Layer;
friend class TextSetupFunctor;
friend class DrawBitmapOp;
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index ab6b742..97eb583 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -104,8 +104,7 @@ void PathCache::computeBounds(const SkRect& bounds, const SkPaint* paint,
}
static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
- bitmap.setConfig(SkBitmap::kA8_Config, width, height);
- bitmap.allocPixels();
+ bitmap.allocPixels(SkImageInfo::MakeA8(width, height));
bitmap.eraseColor(0);
}
@@ -339,7 +338,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
float left, top, offset;
uint32_t width, height;
- PathCache::computePathBounds(t->path, t->paint, left, top, offset, width, height);
+ PathCache::computePathBounds(t->path, &t->paint, left, top, offset, width, height);
PathTexture* texture = t->texture;
texture->left = left;
@@ -350,7 +349,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
if (width <= mMaxTextureSize && height <= mMaxTextureSize) {
SkBitmap* bitmap = new SkBitmap();
- drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height);
+ drawPath(t->path, &t->paint, *bitmap, left, top, offset, width, height);
t->setResult(bitmap);
} else {
texture->width = 0;
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 6177ff1..eee138b 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -272,7 +272,7 @@ private:
class PathTask: public Task<SkBitmap*> {
public:
PathTask(const SkPath* path, const SkPaint* paint, PathTexture* texture):
- path(path), paint(paint), texture(texture) {
+ path(path), paint(*paint), texture(texture) {
}
~PathTask() {
@@ -280,7 +280,8 @@ private:
}
const SkPath* path;
- const SkPaint* paint;
+ //copied, since input paint may not be immutable
+ const SkPaint paint;
PathTexture* texture;
};
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index a58e3e7..310b107 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -57,17 +57,26 @@ namespace uirenderer {
#define ROUND_CAP_THRESH 0.25f
#define PI 3.1415926535897932f
+// temporary error thresholds
+#define ERROR_DEPTH 20
+#define ERROR_SCALE 1e10
+#define ERROR_SQR_INV_THRESH 1e-20
+
void PathTessellator::extractTessellationScales(const Matrix4& transform,
float* scaleX, float* scaleY) {
- *scaleX = 1.0f;
- *scaleY = 1.0f;
- if (CC_UNLIKELY(!transform.isPureTranslate())) {
+ if (CC_LIKELY(transform.isPureTranslate())) {
+ *scaleX = 1.0f;
+ *scaleY = 1.0f;
+ } else {
float m00 = transform.data[Matrix4::kScaleX];
float m01 = transform.data[Matrix4::kSkewY];
float m10 = transform.data[Matrix4::kSkewX];
float m11 = transform.data[Matrix4::kScaleY];
*scaleX = sqrt(m00 * m00 + m01 * m01);
*scaleY = sqrt(m10 * m10 + m11 * m11);
+
+ LOG_ALWAYS_FATAL_IF(*scaleX > ERROR_SCALE || *scaleY > ERROR_SCALE,
+ "scales %e x %e too large for tessellation", *scaleX, *scaleY);
}
}
@@ -92,10 +101,12 @@ struct PaintInfo {
public:
PaintInfo(const SkPaint* paint, const mat4& transform) :
style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()),
- inverseScaleX(1.0f), inverseScaleY(1.0f),
halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) {
// compute inverse scales
- if (CC_UNLIKELY(!transform.isPureTranslate())) {
+ if (CC_LIKELY(transform.isPureTranslate())) {
+ inverseScaleX = 1.0f;
+ inverseScaleY = 1.0f;
+ } else {
float scaleX, scaleY;
PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f;
@@ -922,6 +933,9 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo
Vector<Vertex>& outputVertices) {
ATRACE_CALL();
+ LOG_ALWAYS_FATAL_IF(sqrInvScaleX < ERROR_SQR_INV_THRESH || sqrInvScaleY < ERROR_SQR_INV_THRESH,
+ "Invalid scale factors used for approx %e, %e", sqrInvScaleX, sqrInvScaleY);
+
// TODO: to support joins other than sharp miter, join vertices should be labelled in the
// perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case.
SkPath::Iter iter(path, forceClose);
@@ -975,14 +989,14 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo
// Bezier approximation
///////////////////////////////////////////////////////////////////////////////
-// Depth at which recursion is aborted
-#define ABORT_DEPTH 10
-
void PathTessellator::recursiveCubicBezierVertices(
float p1x, float p1y, float c1x, float c1y,
float p2x, float p2y, float c2x, float c2y,
float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Vector<Vertex>& outputVertices, int depth) {
+ LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR DEPTH exceeded: cubic approx, invscale %e x %e, vertcount %d",
+ sqrInvScaleX, sqrInvScaleY, outputVertices.size());
+
float dx = p2x - p1x;
float dy = p2y - p1y;
float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx);
@@ -990,8 +1004,7 @@ void PathTessellator::recursiveCubicBezierVertices(
float d = d1 + d2;
// multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors
-
- if (depth >= ABORT_DEPTH || d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+ if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
// below thresh, draw line by adding endpoint
pushToVector(outputVertices, p2x, p2y);
} else {
@@ -1029,11 +1042,14 @@ void PathTessellator::recursiveQuadraticBezierVertices(
float cx, float cy,
float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared,
Vector<Vertex>& outputVertices, int depth) {
+ LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR_DEPTH exceeded: quadratic approx, invscale %e x %e, vertcount %d",
+ sqrInvScaleX, sqrInvScaleY, outputVertices.size());
+
float dx = bx - ax;
float dy = by - ay;
float d = (cx - bx) * dy - (cy - by) * dx;
- if (depth >= ABORT_DEPTH || d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
+ if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) {
// below thresh, draw line by adding endpoint
pushToVector(outputVertices, bx, by);
} else {
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d964efc..131384a 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -15,19 +15,24 @@
*/
#define ATRACE_TAG ATRACE_TAG_VIEW
+#define LOG_TAG "OpenGLRenderer"
#include "RenderNode.h"
#include <algorithm>
+#include <string>
#include <SkCanvas.h>
#include <algorithm>
#include <utils/Trace.h>
+#include "DamageAccumulator.h"
#include "Debug.h"
#include "DisplayListOp.h"
#include "DisplayListLogBuffer.h"
+#include "LayerRenderer.h"
+#include "OpenGLRenderer.h"
#include "utils/MathUtils.h"
namespace android {
@@ -57,12 +62,14 @@ RenderNode::RenderNode()
, mNeedsDisplayListDataSync(false)
, mDisplayListData(0)
, mStagingDisplayListData(0)
- , mNeedsAnimatorsSync(false) {
+ , mNeedsAnimatorsSync(false)
+ , mLayer(0) {
}
RenderNode::~RenderNode() {
delete mDisplayListData;
delete mStagingDisplayListData;
+ LayerRenderer::destroyLayerDeferred(mLayer);
}
void RenderNode::setStagingDisplayList(DisplayListData* data) {
@@ -110,14 +117,97 @@ void RenderNode::prepareTree(TreeInfo& info) {
prepareTreeImpl(info);
}
-void RenderNode::prepareTreeImpl(TreeInfo& info) {
- if (info.performStagingPush) {
- pushStagingChanges(info);
+void RenderNode::damageSelf(TreeInfo& info) {
+ if (isRenderable()) {
+ if (properties().getClipDamageToBounds()) {
+ info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight());
+ } else {
+ // Hope this is big enough?
+ // TODO: Get this from the display list ops or something
+ info.damageAccumulator->dirty(INT_MIN, INT_MIN, INT_MAX, INT_MAX);
+ }
+ }
+}
+
+void RenderNode::prepareLayer(TreeInfo& info) {
+ LayerType layerType = properties().layerProperties().type();
+ if (CC_UNLIKELY(layerType == kLayerTypeRenderLayer)) {
+ // We push a null transform here as we don't care what the existing dirty
+ // area is, only what our display list dirty is as well as our children's
+ // dirty area
+ info.damageAccumulator->pushNullTransform();
+ }
+}
+
+void RenderNode::pushLayerUpdate(TreeInfo& info) {
+ LayerType layerType = properties().layerProperties().type();
+ // If we are not a layer OR we cannot be rendered (eg, view was detached)
+ // we need to destroy any Layers we may have had previously
+ if (CC_LIKELY(layerType != kLayerTypeRenderLayer) || CC_UNLIKELY(!isRenderable())) {
+ if (layerType == kLayerTypeRenderLayer) {
+ info.damageAccumulator->popTransform();
+ }
+ if (CC_UNLIKELY(mLayer)) {
+ LayerRenderer::destroyLayer(mLayer);
+ mLayer = NULL;
+ }
+ return;
+ }
+
+ if (!mLayer) {
+ mLayer = LayerRenderer::createRenderLayer(info.renderState, getWidth(), getHeight());
+ applyLayerPropertiesToLayer(info);
+ damageSelf(info);
+ } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
+ if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
+ LayerRenderer::destroyLayer(mLayer);
+ mLayer = 0;
+ }
+ damageSelf(info);
+ }
+
+ SkRect dirty;
+ info.damageAccumulator->peekAtDirty(&dirty);
+ info.damageAccumulator->popTransform();
+
+ if (!mLayer) {
+ if (info.errorHandler) {
+ std::string msg = "Unable to create layer for ";
+ msg += getName();
+ info.errorHandler->onError(msg);
+ }
+ return;
}
- if (info.evaluateAnimations) {
+
+ if (!dirty.isEmpty()) {
+ mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
+ }
+ // This is not inside the above if because we may have called
+ // updateDeferred on a previous prepare pass that didn't have a renderer
+ if (info.renderer && mLayer->deferredUpdateScheduled) {
+ info.renderer->pushLayerUpdate(mLayer);
+ }
+}
+
+void RenderNode::prepareTreeImpl(TreeInfo& info) {
+ info.damageAccumulator->pushTransform(this);
+ if (info.mode == TreeInfo::MODE_FULL) {
+ pushStagingPropertiesChanges(info);
evaluateAnimations(info);
+ } else if (info.mode == TreeInfo::MODE_MAYBE_DETACHING) {
+ pushStagingPropertiesChanges(info);
+ } else if (info.mode == TreeInfo::MODE_RT_ONLY) {
+ evaluateAnimations(info);
+ }
+
+ prepareLayer(info);
+ if (info.mode == TreeInfo::MODE_FULL) {
+ pushStagingDisplayListChanges(info);
}
prepareSubTree(info, mDisplayListData);
+ pushLayerUpdate(info);
+
+ info.damageAccumulator->popTransform();
}
class PushAnimatorsFunctor {
@@ -134,7 +224,7 @@ private:
TreeInfo& mInfo;
};
-void RenderNode::pushStagingChanges(TreeInfo& info) {
+void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) {
// Push the animators first so that setupStartValueIfNecessary() is called
// before properties() is trampled by stagingProperties(), as they are
// required by some animators.
@@ -149,18 +239,41 @@ void RenderNode::pushStagingChanges(TreeInfo& info) {
}
if (mDirtyPropertyFields) {
mDirtyPropertyFields = 0;
+ damageSelf(info);
+ info.damageAccumulator->popTransform();
mProperties = mStagingProperties;
+ applyLayerPropertiesToLayer(info);
+ // We could try to be clever and only re-damage if the matrix changed.
+ // However, we don't need to worry about that. The cost of over-damaging
+ // here is only going to be a single additional map rect of this node
+ // plus a rect join(). The parent's transform (and up) will only be
+ // performed once.
+ info.damageAccumulator->pushTransform(this);
+ damageSelf(info);
}
+}
+
+void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) {
+ if (CC_LIKELY(!mLayer)) return;
+
+ const LayerProperties& props = properties().layerProperties();
+ mLayer->setAlpha(props.alpha(), props.xferMode());
+ mLayer->setColorFilter(props.colorFilter());
+ mLayer->setBlend(props.needsBlending());
+}
+
+void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) {
if (mNeedsDisplayListDataSync) {
mNeedsDisplayListDataSync = false;
// Do a push pass on the old tree to handle freeing DisplayListData
// that are no longer used
- TreeInfo oldTreeInfo;
+ TreeInfo oldTreeInfo(TreeInfo::MODE_MAYBE_DETACHING, info.renderState);
+ oldTreeInfo.damageAccumulator = info.damageAccumulator;
prepareSubTree(oldTreeInfo, mDisplayListData);
- // TODO: The damage for the old tree should be accounted for
delete mDisplayListData;
mDisplayListData = mStagingDisplayListData;
mStagingDisplayListData = 0;
+ damageSelf(info);
}
}
@@ -180,12 +293,21 @@ private:
void RenderNode::evaluateAnimations(TreeInfo& info) {
if (!mAnimators.size()) return;
+ // TODO: Can we target this better? For now treat it like any other staging
+ // property push and just damage self before and after animators are run
+
+ damageSelf(info);
+ info.damageAccumulator->popTransform();
+
AnimateFunctor functor(this, info);
std::vector< sp<BaseRenderNodeAnimator> >::iterator newEnd;
newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
mAnimators.erase(newEnd, mAnimators.end());
mProperties.updateMatrix();
info.out.hasAnimations |= mAnimators.size();
+
+ info.damageAccumulator->pushTransform(this);
+ damageSelf(info);
}
void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
@@ -201,8 +323,11 @@ void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]);
}
for (size_t i = 0; i < subtree->children().size(); i++) {
- RenderNode* childNode = subtree->children()[i]->mDisplayList;
+ DrawRenderNodeOp* op = subtree->children()[i];
+ RenderNode* childNode = op->mRenderNode;
+ info.damageAccumulator->pushTransform(&op->mTransformFromParent);
childNode->prepareTreeImpl(info);
+ info.damageAccumulator->popTransform();
}
}
}
@@ -223,9 +348,9 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) {
renderer.translate(properties().getLeft(), properties().getTop());
}
if (properties().getStaticMatrix()) {
- renderer.concatMatrix(properties().getStaticMatrix());
+ renderer.concatMatrix(*properties().getStaticMatrix());
} else if (properties().getAnimationMatrix()) {
- renderer.concatMatrix(properties().getAnimationMatrix());
+ renderer.concatMatrix(*properties().getAnimationMatrix());
}
if (properties().hasTransformMatrix()) {
if (properties().isTransformTranslateOnly()) {
@@ -234,9 +359,10 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) {
renderer.concatMatrix(*properties().getTransformMatrix());
}
}
- bool clipToBoundsNeeded = properties().getCaching() ? false : properties().getClipToBounds();
+ const bool isLayer = properties().layerProperties().type() != kLayerTypeNone;
+ bool clipToBoundsNeeded = isLayer ? false : properties().getClipToBounds();
if (properties().getAlpha() < 1) {
- if (properties().getCaching()) {
+ if (isLayer) {
renderer.setOverrideLayerAlpha(properties().getAlpha());
} else if (!properties().getHasOverlappingRendering()) {
renderer.scaleAlpha(properties().getAlpha());
@@ -329,16 +455,16 @@ void RenderNode::computeOrdering() {
// transform properties are applied correctly to top level children
if (mDisplayListData == NULL) return;
for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
- DrawDisplayListOp* childOp = mDisplayListData->children()[i];
- childOp->mDisplayList->computeOrderingImpl(childOp,
+ DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
+ childOp->mRenderNode->computeOrderingImpl(childOp,
properties().getOutline().getPath(), &mProjectedNodes, &mat4::identity());
}
}
void RenderNode::computeOrderingImpl(
- DrawDisplayListOp* opState,
+ DrawRenderNodeOp* opState,
const SkPath* outlineOfProjectionSurface,
- Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
+ Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
const mat4* transformFromProjectionSurface) {
mProjectedNodes.clear();
if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
@@ -362,11 +488,11 @@ void RenderNode::computeOrderingImpl(
const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
bool haveAppliedPropertiesToProjection = false;
for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
- DrawDisplayListOp* childOp = mDisplayListData->children()[i];
- RenderNode* child = childOp->mDisplayList;
+ DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
+ RenderNode* child = childOp->mRenderNode;
const SkPath* projectionOutline = NULL;
- Vector<DrawDisplayListOp*>* projectionChildren = NULL;
+ Vector<DrawRenderNodeOp*>* projectionChildren = NULL;
const mat4* projectionTransform = NULL;
if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
// if receiving projections, collect projecting descendent
@@ -410,15 +536,7 @@ private:
const int mLevel;
};
-void RenderNode::deferNodeTree(DeferStateStruct& deferStruct) {
- DeferOperationHandler handler(deferStruct, 0);
- if (MathUtils::isPositive(properties().getZ())) {
- issueDrawShadowOperation(Matrix4::identity(), handler);
- }
- issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
-}
-
-void RenderNode::deferNodeInParent(DeferStateStruct& deferStruct, const int level) {
+void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
DeferOperationHandler handler(deferStruct, level);
issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
}
@@ -448,29 +566,21 @@ private:
const int mLevel;
};
-void RenderNode::replayNodeTree(ReplayStateStruct& replayStruct) {
- ReplayOperationHandler handler(replayStruct, 0);
- if (MathUtils::isPositive(properties().getZ())) {
- issueDrawShadowOperation(Matrix4::identity(), handler);
- }
- issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
-}
-
-void RenderNode::replayNodeInParent(ReplayStateStruct& replayStruct, const int level) {
+void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
ReplayOperationHandler handler(replayStruct, level);
issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
}
-void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
+void RenderNode::buildZSortedChildList(Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) {
if (mDisplayListData == NULL || mDisplayListData->children().size() == 0) return;
for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
- DrawDisplayListOp* childOp = mDisplayListData->children()[i];
- RenderNode* child = childOp->mDisplayList;
+ DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
+ RenderNode* child = childOp->mRenderNode;
float childZ = child->properties().getZ();
if (!MathUtils::isZero(childZ)) {
- zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
+ zTranslatedNodes.add(ZDrawRenderNodeOpPair(childZ, childOp));
childOp->mSkipInOrderDraw = true;
} else if (!child->properties().getProjectBackwards()) {
// regular, in order drawing DisplayList
@@ -515,10 +625,40 @@ void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T&
handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
}
+template <class T>
+int RenderNode::issueOperationsOfNegZChildren(
+ const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+ OpenGLRenderer& renderer, T& handler) {
+ if (zTranslatedNodes.isEmpty()) return -1;
+
+ // create a save around the body of the ViewGroup's draw method, so that
+ // matrix/clip methods don't affect composited children
+ int shadowSaveCount = renderer.getSaveCount();
+ handler(new (handler.allocator()) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
+ PROPERTY_SAVECOUNT, properties().getClipToBounds());
+
+ issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
+ return shadowSaveCount;
+}
+
+template <class T>
+void RenderNode::issueOperationsOfPosZChildren(int shadowRestoreTo,
+ const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+ OpenGLRenderer& renderer, T& handler) {
+ if (zTranslatedNodes.isEmpty()) return;
+
+ LOG_ALWAYS_FATAL_IF(shadowRestoreTo < 0, "invalid save to restore to");
+ handler(new (handler.allocator()) RestoreToCountOp(shadowRestoreTo),
+ PROPERTY_SAVECOUNT, properties().getClipToBounds());
+ renderer.setOverrideLayerAlpha(1.0f);
+
+ issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
+}
+
#define SHADOW_DELTA 0.1f
template <class T>
-void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
const int size = zTranslatedNodes.size();
if (size == 0
@@ -553,8 +693,8 @@ void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair
float lastCasterZ = 0.0f;
while (shadowIndex < endIndex || drawIndex < endIndex) {
if (shadowIndex < endIndex) {
- DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
- RenderNode* caster = casterOp->mDisplayList;
+ DrawRenderNodeOp* casterOp = zTranslatedNodes[shadowIndex].value;
+ RenderNode* caster = casterOp->mRenderNode;
const float casterZ = zTranslatedNodes[shadowIndex].key;
// attempt to render the shadow if the caster about to be drawn is its caster,
// OR if its caster's Z value is similar to the previous potential caster
@@ -571,8 +711,8 @@ void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair
// since it modifies the renderer's matrix
int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
- DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
- RenderNode* child = childOp->mDisplayList;
+ DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
+ RenderNode* child = childOp->mRenderNode;
renderer.concatMatrix(childOp->mTransformFromParent);
childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
@@ -588,8 +728,6 @@ template <class T>
void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) {
DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size());
const SkPath* projectionReceiverOutline = properties().getOutline().getPath();
- bool maskProjecteesWithPath = projectionReceiverOutline != NULL
- && !projectionReceiverOutline->isRect(NULL);
int restoreTo = renderer.getSaveCount();
// If the projection reciever has an outline, we mask each of the projected rendernodes to it
@@ -610,7 +748,7 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T&
SaveLayerOp* op = new (alloc) SaveLayerOp(
outlineBounds.left(), outlineBounds.top(),
outlineBounds.right(), outlineBounds.bottom(),
- 255, SkCanvas::kARGB_ClipLayer_SaveFlag);
+ 255, SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag | SkCanvas::kARGB_ClipLayer_SaveFlag);
op->setMask(projectionReceiverOutline);
handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
@@ -623,7 +761,7 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T&
// draw projected nodes
for (size_t i = 0; i < mProjectedNodes.size(); i++) {
- DrawDisplayListOp* childOp = mProjectedNodes[i];
+ DrawRenderNodeOp* childOp = mProjectedNodes[i];
// matrix save, concat, and restore can be done safely without allocating operations
int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
@@ -651,8 +789,14 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T&
*/
template <class T>
void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
+ const bool drawLayer = (mLayer && (&renderer != mLayer->renderer));
+ // If we are updating the contents of mLayer, we don't want to apply any of
+ // the RenderNode's properties to this issueOperations pass. Those will all
+ // be applied when the layer is drawn, aka when this is true.
+ const bool useViewProperties = (!mLayer || drawLayer);
+
const int level = handler.level();
- if (mDisplayListData->isEmpty() || properties().getAlpha() <= 0) {
+ if (mDisplayListData->isEmpty() || (useViewProperties && properties().getAlpha() <= 0)) {
DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName());
return;
}
@@ -674,7 +818,9 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
- setViewProperties<T>(renderer, handler);
+ if (useViewProperties) {
+ setViewProperties<T>(renderer, handler);
+ }
bool quickRejected = properties().getClipToBounds()
&& renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight());
@@ -683,31 +829,36 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) {
renderer.setClippingOutline(alloc, &(mProperties.getOutline()));
}
- Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
- buildZSortedChildList(zTranslatedNodes);
+ if (drawLayer) {
+ handler(new (alloc) DrawLayerOp(mLayer, 0, 0),
+ renderer.getSaveCount() - 1, properties().getClipToBounds());
+ } else {
+ Vector<ZDrawRenderNodeOpPair> zTranslatedNodes;
+ buildZSortedChildList(zTranslatedNodes);
- // for 3d root, draw children with negative z values
- issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
+ // for 3d root, draw children with negative z values
+ int shadowRestoreTo = issueOperationsOfNegZChildren(zTranslatedNodes, renderer, handler);
- DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
- const int saveCountOffset = renderer.getSaveCount() - 1;
- const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
- for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
- DisplayListOp *op = mDisplayListData->displayListOps[i];
+ DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+ const int saveCountOffset = renderer.getSaveCount() - 1;
+ const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
+ for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
+ DisplayListOp *op = mDisplayListData->displayListOps[i];
#if DEBUG_DISPLAY_LIST
- op->output(level + 1);
+ op->output(level + 1);
#endif
- logBuffer.writeCommand(level, op->name());
- handler(op, saveCountOffset, properties().getClipToBounds());
+ logBuffer.writeCommand(level, op->name());
+ handler(op, saveCountOffset, properties().getClipToBounds());
- if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
- issueOperationsOfProjectedChildren(renderer, handler);
+ if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
+ issueOperationsOfProjectedChildren(renderer, handler);
+ }
}
- }
- // for 3d root, draw children with positive z values
- issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
+ // for 3d root, draw children with positive z values
+ issueOperationsOfPosZChildren(shadowRestoreTo, zTranslatedNodes, renderer, handler);
+ }
}
DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 1a5377b..3980dad 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -39,6 +39,7 @@
#include <androidfw/ResourceTypes.h>
+#include "DamageAccumulator.h"
#include "Debug.h"
#include "Matrix.h"
#include "DeferredDisplayList.h"
@@ -54,7 +55,6 @@ class SkRegion;
namespace android {
namespace uirenderer {
-class DeferredDisplayList;
class DisplayListOp;
class DisplayListRenderer;
class OpenGLRenderer;
@@ -66,7 +66,7 @@ class ClipRectOp;
class SaveLayerOp;
class SaveOp;
class RestoreToCountOp;
-class DrawDisplayListOp;
+class DrawRenderNodeOp;
/**
* Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
@@ -112,11 +112,8 @@ public:
void computeOrdering();
- void deferNodeTree(DeferStateStruct& deferStruct);
- void deferNodeInParent(DeferStateStruct& deferStruct, const int level);
-
- void replayNodeTree(ReplayStateStruct& replayStruct);
- void replayNodeInParent(ReplayStateStruct& replayStruct, const int level);
+ void defer(DeferStateStruct& deferStruct, const int level);
+ void replay(ReplayStateStruct& replayStruct, const int level);
ANDROID_API void output(uint32_t level = 1);
ANDROID_API int getDebugSize();
@@ -125,6 +122,10 @@ public:
return mDisplayListData && mDisplayListData->hasDrawOps;
}
+ bool hasProjectionReceiver() const {
+ return mDisplayListData && mDisplayListData->projectionReceiveIndex >= 0;
+ }
+
const char* getName() const {
return mName.string();
}
@@ -148,7 +149,7 @@ public:
mDirtyPropertyFields |= fields;
}
- const RenderProperties& properties() {
+ const RenderProperties& properties() const {
return mProperties;
}
@@ -184,13 +185,18 @@ public:
// UI thread only!
ANDROID_API void removeAnimator(const sp<BaseRenderNodeAnimator>& animator) {
mStagingAnimators.erase(animator);
+ // Force a sync of the staging property value
+ mDirtyPropertyFields |= animator->dirtyMask();
mNeedsAnimatorsSync = true;
}
+protected:
+ virtual void damageSelf(TreeInfo& info);
+
private:
- typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair;
+ typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair;
- static size_t findNonNegativeIndex(const Vector<ZDrawDisplayListOpPair>& nodes) {
+ static size_t findNonNegativeIndex(const Vector<ZDrawRenderNodeOpPair>& nodes) {
for (size_t i = 0; i < nodes.size(); i++) {
if (nodes[i].key >= 0.0f) return i;
}
@@ -204,21 +210,29 @@ private:
void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false);
- void computeOrderingImpl(DrawDisplayListOp* opState,
+ void computeOrderingImpl(DrawRenderNodeOp* opState,
const SkPath* outlineOfProjectionSurface,
- Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
+ Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
const mat4* transformFromProjectionSurface);
template <class T>
inline void setViewProperties(OpenGLRenderer& renderer, T& handler);
- void buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes);
+ void buildZSortedChildList(Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes);
template<class T>
inline void issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler);
template <class T>
- inline void issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+ inline int issueOperationsOfNegZChildren(
+ const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+ OpenGLRenderer& renderer, T& handler);
+ template <class T>
+ inline void issueOperationsOfPosZChildren(int shadowRestoreTo,
+ const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+ OpenGLRenderer& renderer, T& handler);
+ template <class T>
+ inline void issueOperationsOf3dChildren(const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler);
template <class T>
@@ -226,7 +240,7 @@ private:
/**
* Issue the RenderNode's operations into a handler, recursing for subtrees through
- * DrawDisplayListOp's defer() or replay() methods
+ * DrawRenderNodeOp's defer() or replay() methods
*/
template <class T>
inline void issueOperations(OpenGLRenderer& renderer, T& handler);
@@ -246,9 +260,13 @@ private:
};
void prepareTreeImpl(TreeInfo& info);
- void pushStagingChanges(TreeInfo& info);
+ void pushStagingPropertiesChanges(TreeInfo& info);
+ void pushStagingDisplayListChanges(TreeInfo& info);
void evaluateAnimations(TreeInfo& info);
void prepareSubTree(TreeInfo& info, DisplayListData* subtree);
+ void applyLayerPropertiesToLayer(TreeInfo& info);
+ void prepareLayer(TreeInfo& info);
+ void pushLayerUpdate(TreeInfo& info);
String8 mName;
@@ -264,12 +282,16 @@ private:
std::set< sp<BaseRenderNodeAnimator> > mStagingAnimators;
std::vector< sp<BaseRenderNodeAnimator> > mAnimators;
+ // Owned by RT. Lifecycle is managed by prepareTree(), with the exception
+ // being in ~RenderNode() which may happen on any thread.
+ Layer* mLayer;
+
/**
* Draw time state - these properties are only set and used during rendering
*/
// for projection surfaces, contains a list of all children items
- Vector<DrawDisplayListOp*> mProjectedNodes;
+ Vector<DrawRenderNodeOp*> mProjectedNodes;
}; // class RenderNode
} /* namespace uirenderer */
diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp
index 5f7d4e3..8848b2f 100644
--- a/libs/hwui/RenderProperties.cpp
+++ b/libs/hwui/RenderProperties.cpp
@@ -21,16 +21,59 @@
#include <utils/Trace.h>
#include <SkCanvas.h>
+#include <SkColorFilter.h>
#include <SkMatrix.h>
#include <SkPath.h>
#include <SkPathOps.h>
#include "Matrix.h"
+#include "OpenGLRenderer.h"
#include "utils/MathUtils.h"
namespace android {
namespace uirenderer {
+LayerProperties::LayerProperties()
+ : mType(kLayerTypeNone)
+ , mColorFilter(NULL) {
+ reset();
+}
+
+LayerProperties::~LayerProperties() {
+ setType(kLayerTypeNone);
+}
+
+void LayerProperties::reset() {
+ mOpaque = false;
+ setFromPaint(NULL);
+}
+
+bool LayerProperties::setColorFilter(SkColorFilter* filter) {
+ if (mColorFilter == filter) return false;
+ SkRefCnt_SafeAssign(mColorFilter, filter);
+ return true;
+}
+
+bool LayerProperties::setFromPaint(const SkPaint* paint) {
+ bool changed = false;
+ SkXfermode::Mode mode;
+ int alpha;
+ OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
+ changed |= setAlpha(static_cast<uint8_t>(alpha));
+ changed |= setXferMode(mode);
+ changed |= setColorFilter(paint ? paint->getColorFilter() : NULL);
+ return changed;
+}
+
+LayerProperties& LayerProperties::operator=(const LayerProperties& other) {
+ setType(other.type());
+ setOpaque(other.opaque());
+ setAlpha(other.alpha());
+ setXferMode(other.xferMode());
+ setColorFilter(other.colorFilter());
+ return *this;
+}
+
RenderProperties::PrimitiveFields::PrimitiveFields()
: mClipToBounds(true)
, mProjectBackwards(false)
@@ -45,8 +88,7 @@ RenderProperties::PrimitiveFields::PrimitiveFields()
, mLeft(0), mTop(0), mRight(0), mBottom(0)
, mWidth(0), mHeight(0)
, mPivotExplicitlySet(false)
- , mMatrixOrPivotDirty(false)
- , mCaching(false) {
+ , mMatrixOrPivotDirty(false) {
}
RenderProperties::ComputedFields::ComputedFields()
@@ -73,6 +115,7 @@ RenderProperties& RenderProperties::operator=(const RenderProperties& other) {
setStaticMatrix(other.getStaticMatrix());
setAnimationMatrix(other.getAnimationMatrix());
setCameraDistance(other.getCameraDistance());
+ mLayerProperties = other.layerProperties();
// Force recalculation of the matrix, since other's dirty bit may be clear
mPrimitiveFields.mMatrixOrPivotDirty = true;
@@ -103,9 +146,9 @@ void RenderProperties::debugOutputProperties(const int level) const {
}
}
- bool clipToBoundsNeeded = mPrimitiveFields.mCaching ? false : mPrimitiveFields.mClipToBounds;
+ bool clipToBoundsNeeded = layerProperties().type() != kLayerTypeNone ? false : mPrimitiveFields.mClipToBounds;
if (mPrimitiveFields.mAlpha < 1) {
- if (mPrimitiveFields.mCaching) {
+ if (layerProperties().type() != kLayerTypeNone) {
ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha);
} else if (!mPrimitiveFields.mHasOverlappingRendering) {
ALOGD("%*sScaleAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha);
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index c0e3ce7..227d56e 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -21,6 +21,7 @@
#include <vector>
#include <cutils/compiler.h>
#include <androidfw/ResourceTypes.h>
+#include <utils/Log.h>
#include <SkCamera.h>
#include <SkMatrix.h>
@@ -30,8 +31,10 @@
#include "Rect.h"
#include "RevealClip.h"
#include "Outline.h"
+#include "utils/MathUtils.h"
class SkBitmap;
+class SkColorFilter;
class SkPaint;
namespace android {
@@ -39,40 +42,125 @@ namespace uirenderer {
class Matrix4;
class RenderNode;
+class RenderProperties;
+
+// The __VA_ARGS__ will be executed if a & b are not equal
+#define RP_SET(a, b, ...) (a != b ? (a = b, ##__VA_ARGS__, true) : false)
+#define RP_SET_AND_DIRTY(a, b) RP_SET(a, b, mPrimitiveFields.mMatrixOrPivotDirty = true)
+
+// Keep in sync with View.java:LAYER_TYPE_*
+enum LayerType {
+ kLayerTypeNone = 0,
+ // Although we cannot build the software layer directly (must be done at
+ // record time), this information is used when applying alpha.
+ kLayerTypeSoftware = 1,
+ kLayerTypeRenderLayer = 2,
+ // TODO: LayerTypeSurfaceTexture? Maybe?
+};
+
+class ANDROID_API LayerProperties {
+public:
+ bool setType(LayerType type) {
+ if (RP_SET(mType, type)) {
+ reset();
+ return true;
+ }
+ return false;
+ }
+
+ LayerType type() const {
+ return mType;
+ }
+
+ bool setOpaque(bool opaque) {
+ return RP_SET(mOpaque, opaque);
+ }
+
+ bool opaque() const {
+ return mOpaque;
+ }
+
+ bool setAlpha(uint8_t alpha) {
+ return RP_SET(mAlpha, alpha);
+ }
+
+ uint8_t alpha() const {
+ return mAlpha;
+ }
+
+ bool setXferMode(SkXfermode::Mode mode) {
+ return RP_SET(mMode, mode);
+ }
+
+ SkXfermode::Mode xferMode() const {
+ return mMode;
+ }
+
+ bool setColorFilter(SkColorFilter* filter);
+
+ SkColorFilter* colorFilter() const {
+ return mColorFilter;
+ }
+
+ // Sets alpha, xfermode, and colorfilter from an SkPaint
+ // paint may be NULL, in which case defaults will be set
+ bool setFromPaint(const SkPaint* paint);
+
+ bool needsBlending() const {
+ return !opaque() || alpha() < 255;
+ }
+
+ LayerProperties& operator=(const LayerProperties& other);
+
+private:
+ LayerProperties();
+ ~LayerProperties();
+ void reset();
+
+ friend class RenderProperties;
+
+ LayerType mType;
+ // Whether or not that Layer's content is opaque, doesn't include alpha
+ bool mOpaque;
+ uint8_t mAlpha;
+ SkXfermode::Mode mMode;
+ SkColorFilter* mColorFilter;
+};
/*
* Data structure that holds the properties for a RenderNode
*/
-class RenderProperties {
+class ANDROID_API RenderProperties {
public:
RenderProperties();
virtual ~RenderProperties();
RenderProperties& operator=(const RenderProperties& other);
- void setClipToBounds(bool clipToBounds) {
- mPrimitiveFields.mClipToBounds = clipToBounds;
+ bool setClipToBounds(bool clipToBounds) {
+ return RP_SET(mPrimitiveFields.mClipToBounds, clipToBounds);
}
- void setProjectBackwards(bool shouldProject) {
- mPrimitiveFields.mProjectBackwards = shouldProject;
+ bool setProjectBackwards(bool shouldProject) {
+ return RP_SET(mPrimitiveFields.mProjectBackwards, shouldProject);
}
- void setProjectionReceiver(bool shouldRecieve) {
- mPrimitiveFields.mProjectionReceiver = shouldRecieve;
+ bool setProjectionReceiver(bool shouldRecieve) {
+ return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldRecieve);
}
bool isProjectionReceiver() const {
return mPrimitiveFields.mProjectionReceiver;
}
- void setStaticMatrix(const SkMatrix* matrix) {
+ bool setStaticMatrix(const SkMatrix* matrix) {
delete mStaticMatrix;
if (matrix) {
mStaticMatrix = new SkMatrix(*matrix);
} else {
mStaticMatrix = NULL;
}
+ return true;
}
// Can return NULL
@@ -80,72 +168,61 @@ public:
return mStaticMatrix;
}
- void setAnimationMatrix(const SkMatrix* matrix) {
+ bool setAnimationMatrix(const SkMatrix* matrix) {
delete mAnimationMatrix;
if (matrix) {
mAnimationMatrix = new SkMatrix(*matrix);
} else {
mAnimationMatrix = NULL;
}
+ return true;
}
- void setAlpha(float alpha) {
+ bool setAlpha(float alpha) {
alpha = fminf(1.0f, fmaxf(0.0f, alpha));
- if (alpha != mPrimitiveFields.mAlpha) {
- mPrimitiveFields.mAlpha = alpha;
- }
+ return RP_SET(mPrimitiveFields.mAlpha, alpha);
}
float getAlpha() const {
return mPrimitiveFields.mAlpha;
}
- void setHasOverlappingRendering(bool hasOverlappingRendering) {
- mPrimitiveFields.mHasOverlappingRendering = hasOverlappingRendering;
+ bool setHasOverlappingRendering(bool hasOverlappingRendering) {
+ return RP_SET(mPrimitiveFields.mHasOverlappingRendering, hasOverlappingRendering);
}
bool hasOverlappingRendering() const {
return mPrimitiveFields.mHasOverlappingRendering;
}
- void setElevation(float elevation) {
- if (elevation != mPrimitiveFields.mElevation) {
- mPrimitiveFields.mElevation = elevation;
- // mMatrixOrPivotDirty not set, since matrix doesn't respect Z
- }
+ bool setElevation(float elevation) {
+ return RP_SET(mPrimitiveFields.mElevation, elevation);
+ // Don't dirty matrix/pivot, since they don't respect Z
}
float getElevation() const {
return mPrimitiveFields.mElevation;
}
- void setTranslationX(float translationX) {
- if (translationX != mPrimitiveFields.mTranslationX) {
- mPrimitiveFields.mTranslationX = translationX;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ bool setTranslationX(float translationX) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationX, translationX);
}
float getTranslationX() const {
return mPrimitiveFields.mTranslationX;
}
- void setTranslationY(float translationY) {
- if (translationY != mPrimitiveFields.mTranslationY) {
- mPrimitiveFields.mTranslationY = translationY;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ bool setTranslationY(float translationY) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationY, translationY);
}
float getTranslationY() const {
return mPrimitiveFields.mTranslationY;
}
- void setTranslationZ(float translationZ) {
- if (translationZ != mPrimitiveFields.mTranslationZ) {
- mPrimitiveFields.mTranslationZ = translationZ;
- // mMatrixOrPivotDirty not set, since matrix doesn't respect Z
- }
+ bool setTranslationZ(float translationZ) {
+ return RP_SET(mPrimitiveFields.mTranslationZ, translationZ);
+ // mMatrixOrPivotDirty not set, since matrix doesn't respect Z
}
float getTranslationZ() const {
@@ -153,8 +230,8 @@ public:
}
// Animation helper
- void setX(float value) {
- setTranslationX(value - getLeft());
+ bool setX(float value) {
+ return setTranslationX(value - getLeft());
}
// Animation helper
@@ -163,8 +240,8 @@ public:
}
// Animation helper
- void setY(float value) {
- setTranslationY(value - getTop());
+ bool setY(float value) {
+ return setTranslationY(value - getTop());
}
// Animation helper
@@ -173,87 +250,82 @@ public:
}
// Animation helper
- void setZ(float value) {
- setTranslationZ(value - getElevation());
+ bool setZ(float value) {
+ return setTranslationZ(value - getElevation());
}
float getZ() const {
return getElevation() + getTranslationZ();
}
- void setRotation(float rotation) {
- if (rotation != mPrimitiveFields.mRotation) {
- mPrimitiveFields.mRotation = rotation;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ bool setRotation(float rotation) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mRotation, rotation);
}
float getRotation() const {
return mPrimitiveFields.mRotation;
}
- void setRotationX(float rotationX) {
- if (rotationX != mPrimitiveFields.mRotationX) {
- mPrimitiveFields.mRotationX = rotationX;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ bool setRotationX(float rotationX) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationX, rotationX);
}
float getRotationX() const {
return mPrimitiveFields.mRotationX;
}
- void setRotationY(float rotationY) {
- if (rotationY != mPrimitiveFields.mRotationY) {
- mPrimitiveFields.mRotationY = rotationY;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ bool setRotationY(float rotationY) {
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationY, rotationY);
}
float getRotationY() const {
return mPrimitiveFields.mRotationY;
}
- void setScaleX(float scaleX) {
- if (scaleX != mPrimitiveFields.mScaleX) {
- mPrimitiveFields.mScaleX = scaleX;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ bool setScaleX(float scaleX) {
+ LOG_ALWAYS_FATAL_IF(scaleX > 1000000, "invalid scaleX %e", scaleX);
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleX, scaleX);
}
float getScaleX() const {
return mPrimitiveFields.mScaleX;
}
- void setScaleY(float scaleY) {
- if (scaleY != mPrimitiveFields.mScaleY) {
- mPrimitiveFields.mScaleY = scaleY;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ bool setScaleY(float scaleY) {
+ LOG_ALWAYS_FATAL_IF(scaleY > 1000000, "invalid scaleY %e", scaleY);
+ return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleY, scaleY);
}
float getScaleY() const {
return mPrimitiveFields.mScaleY;
}
- void setPivotX(float pivotX) {
- mPrimitiveFields.mPivotX = pivotX;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- mPrimitiveFields.mPivotExplicitlySet = true;
+ bool setPivotX(float pivotX) {
+ if (RP_SET(mPrimitiveFields.mPivotX, pivotX)
+ || !mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ mPrimitiveFields.mPivotExplicitlySet = true;
+ return true;
+ }
+ return false;
}
/* Note that getPivotX and getPivotY are adjusted by updateMatrix(),
- * so the value returned mPrimitiveFields.may be stale if the RenderProperties has been
- * mPrimitiveFields.modified since the last call to updateMatrix()
+ * so the value returned may be stale if the RenderProperties has been
+ * modified since the last call to updateMatrix()
*/
float getPivotX() const {
return mPrimitiveFields.mPivotX;
}
- void setPivotY(float pivotY) {
- mPrimitiveFields.mPivotY = pivotY;
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- mPrimitiveFields.mPivotExplicitlySet = true;
+ bool setPivotY(float pivotY) {
+ if (RP_SET(mPrimitiveFields.mPivotY, pivotY)
+ || !mPrimitiveFields.mPivotExplicitlySet) {
+ mPrimitiveFields.mMatrixOrPivotDirty = true;
+ mPrimitiveFields.mPivotExplicitlySet = true;
+ return true;
+ }
+ return false;
}
float getPivotY() const {
@@ -264,11 +336,13 @@ public:
return mPrimitiveFields.mPivotExplicitlySet;
}
- void setCameraDistance(float distance) {
+ bool setCameraDistance(float distance) {
if (distance != getCameraDistance()) {
mPrimitiveFields.mMatrixOrPivotDirty = true;
mComputedFields.mTransformCamera.setCameraLocation(0, 0, distance);
+ return true;
}
+ return false;
}
float getCameraDistance() const {
@@ -276,75 +350,73 @@ public:
return const_cast<Sk3DView*>(&mComputedFields.mTransformCamera)->getCameraLocationZ();
}
- void setLeft(int left) {
- if (left != mPrimitiveFields.mLeft) {
- mPrimitiveFields.mLeft = left;
+ bool setLeft(int left) {
+ if (RP_SET(mPrimitiveFields.mLeft, left)) {
mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
if (!mPrimitiveFields.mPivotExplicitlySet) {
mPrimitiveFields.mMatrixOrPivotDirty = true;
}
+ return true;
}
+ return false;
}
float getLeft() const {
return mPrimitiveFields.mLeft;
}
- void setTop(int top) {
- if (top != mPrimitiveFields.mTop) {
- mPrimitiveFields.mTop = top;
+ bool setTop(int top) {
+ if (RP_SET(mPrimitiveFields.mTop, top)) {
mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
if (!mPrimitiveFields.mPivotExplicitlySet) {
mPrimitiveFields.mMatrixOrPivotDirty = true;
}
+ return true;
}
+ return false;
}
float getTop() const {
return mPrimitiveFields.mTop;
}
- void setRight(int right) {
- if (right != mPrimitiveFields.mRight) {
- mPrimitiveFields.mRight = right;
+ bool setRight(int right) {
+ if (RP_SET(mPrimitiveFields.mRight, right)) {
mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
if (!mPrimitiveFields.mPivotExplicitlySet) {
mPrimitiveFields.mMatrixOrPivotDirty = true;
}
+ return true;
}
+ return false;
}
float getRight() const {
return mPrimitiveFields.mRight;
}
- void setBottom(int bottom) {
- if (bottom != mPrimitiveFields.mBottom) {
- mPrimitiveFields.mBottom = bottom;
+ bool setBottom(int bottom) {
+ if (RP_SET(mPrimitiveFields.mBottom, bottom)) {
mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
if (!mPrimitiveFields.mPivotExplicitlySet) {
mPrimitiveFields.mMatrixOrPivotDirty = true;
}
+ return true;
}
+ return false;
}
float getBottom() const {
return mPrimitiveFields.mBottom;
}
- void setLeftTop(int left, int top) {
- if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop) {
- mPrimitiveFields.mLeft = left;
- mPrimitiveFields.mTop = top;
- mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft;
- mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop;
- if (!mPrimitiveFields.mPivotExplicitlySet) {
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
- }
+ bool setLeftTop(int left, int top) {
+ bool leftResult = setLeft(left);
+ bool topResult = setTop(top);
+ return leftResult || topResult;
}
- void setLeftTopRightBottom(int left, int top, int right, int bottom) {
+ bool setLeftTopRightBottom(int left, int top, int right, int bottom) {
if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop
|| right != mPrimitiveFields.mRight || bottom != mPrimitiveFields.mBottom) {
mPrimitiveFields.mLeft = left;
@@ -356,31 +428,27 @@ public:
if (!mPrimitiveFields.mPivotExplicitlySet) {
mPrimitiveFields.mMatrixOrPivotDirty = true;
}
+ return true;
}
+ return false;
}
- void offsetLeftRight(float offset) {
+ bool offsetLeftRight(float offset) {
if (offset != 0) {
mPrimitiveFields.mLeft += offset;
mPrimitiveFields.mRight += offset;
- if (!mPrimitiveFields.mPivotExplicitlySet) {
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ return true;
}
+ return false;
}
- void offsetTopBottom(float offset) {
+ bool offsetTopBottom(float offset) {
if (offset != 0) {
mPrimitiveFields.mTop += offset;
mPrimitiveFields.mBottom += offset;
- if (!mPrimitiveFields.mPivotExplicitlySet) {
- mPrimitiveFields.mMatrixOrPivotDirty = true;
- }
+ return true;
}
- }
-
- void setCaching(bool caching) {
- mPrimitiveFields.mCaching = caching;
+ return false;
}
int getWidth() const {
@@ -409,10 +477,6 @@ public:
return mComputedFields.mTransformMatrix;
}
- bool getCaching() const {
- return mPrimitiveFields.mCaching;
- }
-
bool getClipToBounds() const {
return mPrimitiveFields.mClipToBounds;
}
@@ -435,7 +499,7 @@ public:
void debugOutputProperties(const int level) const;
- ANDROID_API void updateMatrix();
+ void updateMatrix();
bool hasClippingPath() const {
return mPrimitiveFields.mRevealClip.willClip();
@@ -458,6 +522,23 @@ public:
return mPrimitiveFields.mRevealClip;
}
+ const LayerProperties& layerProperties() const {
+ return mLayerProperties;
+ }
+
+ LayerProperties& mutateLayerProperties() {
+ return mLayerProperties;
+ }
+
+ // Returns true if damage calculations should be clipped to bounds
+ // TODO: Figure out something better for getZ(), as children should still be
+ // clipped to this RP's bounds. But as we will damage -INT_MAX to INT_MAX
+ // for this RP's getZ() anyway, this can be optimized when we have a
+ // Z damage estimate instead of INT_MAX
+ bool getClipDamageToBounds() const {
+ return getClipToBounds() && (getZ() <= 0 || getOutline().isEmpty());
+ }
+
private:
// Rendering properties
@@ -480,11 +561,11 @@ private:
int mWidth, mHeight;
bool mPivotExplicitlySet;
bool mMatrixOrPivotDirty;
- bool mCaching;
} mPrimitiveFields;
SkMatrix* mStaticMatrix;
SkMatrix* mAnimationMatrix;
+ LayerProperties mLayerProperties;
/**
* These fields are all generated from other properties and are not set directly.
diff --git a/libs/hwui/RenderState.cpp b/libs/hwui/RenderState.cpp
new file mode 100644
index 0000000..97f379d
--- /dev/null
+++ b/libs/hwui/RenderState.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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 "RenderState.h"
+
+namespace android {
+namespace uirenderer {
+
+RenderState::RenderState()
+ : mCaches(NULL)
+ , mViewportWidth(0)
+ , mViewportHeight(0)
+ , mFramebuffer(0) {
+}
+
+RenderState::~RenderState() {
+}
+
+void RenderState::onGLContextCreated() {
+ // This is delayed because the first access of Caches makes GL calls
+ mCaches = &Caches::getInstance();
+ mCaches->init();
+}
+
+void RenderState::setViewport(GLsizei width, GLsizei height) {
+ mViewportWidth = width;
+ mViewportHeight = height;
+ glViewport(0, 0, mViewportWidth, mViewportHeight);
+}
+
+
+void RenderState::getViewport(GLsizei* outWidth, GLsizei* outHeight) {
+ *outWidth = mViewportWidth;
+ *outHeight = mViewportHeight;
+}
+
+void RenderState::bindFramebuffer(GLuint fbo) {
+ if (mFramebuffer != fbo) {
+ mFramebuffer = fbo;
+ glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
+ }
+}
+
+void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) {
+ interruptForFunctorInvoke();
+ (*functor)(mode, info);
+ resumeFromFunctorInvoke();
+}
+
+void RenderState::interruptForFunctorInvoke() {
+ if (mCaches->currentProgram) {
+ if (mCaches->currentProgram->isInUse()) {
+ mCaches->currentProgram->remove();
+ mCaches->currentProgram = NULL;
+ }
+ }
+ mCaches->resetActiveTexture();
+ mCaches->unbindMeshBuffer();
+ mCaches->unbindIndicesBuffer();
+ mCaches->resetVertexPointers();
+ mCaches->disableTexCoordsVertexArray();
+ debugOverdraw(false, false);
+}
+
+void RenderState::resumeFromFunctorInvoke() {
+ glViewport(0, 0, mViewportWidth, mViewportHeight);
+ glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
+ debugOverdraw(false, false);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+ mCaches->scissorEnabled = glIsEnabled(GL_SCISSOR_TEST);
+ mCaches->enableScissor();
+ mCaches->resetScissor();
+
+ mCaches->activeTexture(0);
+ mCaches->resetBoundTextures();
+
+ mCaches->blend = true;
+ glEnable(GL_BLEND);
+ glBlendFunc(mCaches->lastSrcMode, mCaches->lastDstMode);
+ glBlendEquation(GL_FUNC_ADD);
+}
+
+void RenderState::debugOverdraw(bool enable, bool clear) {
+ if (mCaches->debugOverdraw && mFramebuffer == 0) {
+ if (clear) {
+ mCaches->disableScissor();
+ mCaches->stencil.clear();
+ }
+ if (enable) {
+ mCaches->stencil.enableDebugWrite();
+ } else {
+ mCaches->stencil.disable();
+ }
+ }
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/RenderState.h b/libs/hwui/RenderState.h
new file mode 100644
index 0000000..f7116e2
--- /dev/null
+++ b/libs/hwui/RenderState.h
@@ -0,0 +1,70 @@
+/*
+ * 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 RENDERSTATE_H
+#define RENDERSTATE_H
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <private/hwui/DrawGlInfo.h>
+
+#include "Caches.h"
+#include "utils/Macros.h"
+
+namespace android {
+namespace uirenderer {
+
+namespace renderthread {
+class RenderThread;
+}
+
+// TODO: Replace Cache's GL state tracking with this. For now it's more a thin
+// wrapper of Caches for users to migrate to.
+class RenderState {
+ PREVENT_COPY_AND_ASSIGN(RenderState);
+public:
+ void onGLContextCreated();
+
+ void setViewport(GLsizei width, GLsizei height);
+ void getViewport(GLsizei* outWidth, GLsizei* outHeight);
+
+ void bindFramebuffer(GLuint fbo);
+ GLint getFramebuffer() { return mFramebuffer; }
+
+ void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info);
+
+ void debugOverdraw(bool enable, bool clear);
+
+private:
+ friend class renderthread::RenderThread;
+
+ void interruptForFunctorInvoke();
+ void resumeFromFunctorInvoke();
+
+ RenderState();
+ ~RenderState();
+
+ Caches* mCaches;
+
+ GLsizei mViewportWidth;
+ GLsizei mViewportHeight;
+ GLuint mFramebuffer;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* RENDERSTATE_H */
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
index 23cab0e..2ec99c9 100644
--- a/libs/hwui/Renderer.h
+++ b/libs/hwui/Renderer.h
@@ -129,21 +129,6 @@ public:
*/
virtual void finish() = 0;
- /**
- * This method must be invoked before handing control over to a draw functor.
- * See callDrawGLFunction() for instance.
- *
- * This command must not be recorded inside display lists.
- */
- virtual void interrupt() = 0;
-
- /**
- * This method must be invoked after getting control back from a draw functor.
- *
- * This command must not be recorded inside display lists.
- */
- virtual void resume() = 0;
-
// ----------------------------------------------------------------------------
// Canvas state operations
// ----------------------------------------------------------------------------
@@ -170,8 +155,8 @@ public:
virtual void scale(float sx, float sy) = 0;
virtual void skew(float sx, float sy) = 0;
- virtual void setMatrix(const SkMatrix* matrix) = 0;
- virtual void concatMatrix(const SkMatrix* matrix) = 0;
+ virtual void setMatrix(const SkMatrix& matrix) = 0;
+ virtual void concatMatrix(const SkMatrix& matrix) = 0;
// clip
virtual const Rect& getLocalClipBounds() const = 0;
@@ -193,7 +178,7 @@ public:
// Bitmap-based
virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top,
const SkPaint* paint) = 0;
- virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix,
+ virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix,
const SkPaint* paint) = 0;
virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
@@ -233,7 +218,7 @@ public:
// Canvas draw operations - special
// ----------------------------------------------------------------------------
virtual status_t drawLayer(Layer* layer, float x, float y) = 0;
- virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty,
+ virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty,
int32_t replayFlags) = 0;
// TODO: rename for consistency
diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp
index f2e28e1..95c0ee5 100644
--- a/libs/hwui/StatefulBaseRenderer.cpp
+++ b/libs/hwui/StatefulBaseRenderer.cpp
@@ -125,20 +125,16 @@ void StatefulBaseRenderer::skew(float sx, float sy) {
mSnapshot->transform->skew(sx, sy);
}
-void StatefulBaseRenderer::setMatrix(const SkMatrix* matrix) {
- if (matrix) {
- mSnapshot->transform->load(*matrix);
- } else {
- mSnapshot->transform->loadIdentity();
- }
+void StatefulBaseRenderer::setMatrix(const SkMatrix& matrix) {
+ mSnapshot->transform->load(matrix);
}
void StatefulBaseRenderer::setMatrix(const Matrix4& matrix) {
mSnapshot->transform->load(matrix);
}
-void StatefulBaseRenderer::concatMatrix(const SkMatrix* matrix) {
- mat4 transform(*matrix);
+void StatefulBaseRenderer::concatMatrix(const SkMatrix& matrix) {
+ mat4 transform(matrix);
mSnapshot->transform->multiply(transform);
}
diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h
index dbb1d85..e8e024f 100644
--- a/libs/hwui/StatefulBaseRenderer.h
+++ b/libs/hwui/StatefulBaseRenderer.h
@@ -76,9 +76,9 @@ public:
virtual void scale(float sx, float sy);
virtual void skew(float sx, float sy);
- virtual void setMatrix(const SkMatrix* matrix);
+ virtual void setMatrix(const SkMatrix& matrix);
void setMatrix(const Matrix4& matrix); // internal only convenience method
- virtual void concatMatrix(const SkMatrix* matrix);
+ virtual void concatMatrix(const SkMatrix& matrix);
void concatMatrix(const Matrix4& matrix); // internal only convenience method
// Clip
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index 37d5347..08b54ff 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -39,6 +39,8 @@ namespace uirenderer {
TessellationCache::Description::Description()
: type(kNone)
+ , scaleX(1.0f)
+ , scaleY(1.0f)
, aa(false)
, cap(SkPaint::kDefault_Cap)
, style(SkPaint::kFill_Style)
@@ -46,21 +48,13 @@ TessellationCache::Description::Description()
memset(&shape, 0, sizeof(Shape));
}
-TessellationCache::Description::Description(Type type)
+TessellationCache::Description::Description(Type type, const Matrix4& transform, const SkPaint& paint)
: type(type)
- , aa(false)
- , cap(SkPaint::kDefault_Cap)
- , style(SkPaint::kFill_Style)
- , strokeWidth(1.0f) {
- memset(&shape, 0, sizeof(Shape));
-}
-
-TessellationCache::Description::Description(Type type, const SkPaint* paint)
- : type(type)
- , aa(paint->isAntiAlias())
- , cap(paint->getStrokeCap())
- , style(paint->getStyle())
- , strokeWidth(paint->getStrokeWidth()) {
+ , aa(paint.isAntiAlias())
+ , cap(paint.getStrokeCap())
+ , style(paint.getStyle())
+ , strokeWidth(paint.getStrokeWidth()) {
+ PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
memset(&shape, 0, sizeof(Shape));
}
@@ -70,10 +64,20 @@ hash_t TessellationCache::Description::hash() const {
hash = JenkinsHashMix(hash, cap);
hash = JenkinsHashMix(hash, style);
hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
+ hash = JenkinsHashMix(hash, android::hash_type(scaleX));
+ hash = JenkinsHashMix(hash, android::hash_type(scaleY));
hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape));
return JenkinsHashWhiten(hash);
}
+void TessellationCache::Description::setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const {
+ matrix->loadScale(scaleX, scaleY, 1.0f);
+ paint->setAntiAlias(aa);
+ paint->setStrokeCap(cap);
+ paint->setStyle(style);
+ paint->setStrokeWidth(strokeWidth);
+}
+
TessellationCache::ShadowDescription::ShadowDescription()
: nodeKey(NULL) {
memset(&matrixData, 0, 16 * sizeof(float));
@@ -96,20 +100,15 @@ hash_t TessellationCache::ShadowDescription::hash() const {
class TessellationCache::TessellationTask : public Task<VertexBuffer*> {
public:
- TessellationTask(Tessellator tessellator, const Description& description,
- const SkPaint* paint)
+ TessellationTask(Tessellator tessellator, const Description& description)
: tessellator(tessellator)
- , description(description)
- , paint(*paint) {
+ , description(description) {
}
~TessellationTask() {}
Tessellator tessellator;
Description description;
-
- //copied, since input paint may not be immutable
- const SkPaint paint;
};
class TessellationCache::TessellationProcessor : public TaskProcessor<VertexBuffer*> {
@@ -121,7 +120,7 @@ public:
virtual void onProcess(const sp<Task<VertexBuffer*> >& task) {
TessellationTask* t = static_cast<TessellationTask*>(task.get());
ATRACE_NAME("shape tessellation");
- VertexBuffer* buffer = t->tessellator(t->description, t->paint);
+ VertexBuffer* buffer = t->tessellator(t->description);
t->setResult(buffer);
}
};
@@ -354,12 +353,11 @@ void TessellationCache::setMaxSize(uint32_t maxSize) {
void TessellationCache::trim() {
- // uint32_t size = getSize();
- // while (size > mMaxSize) {
- // size -= mCache.peekOldestValue()->getSize();
- // mCache.removeOldest();
- // }
- mCache.clear(); // Workaround caching tessellation corruption
+ uint32_t size = getSize();
+ while (size > mMaxSize) {
+ size -= mCache.peekOldestValue()->getSize();
+ mCache.removeOldest();
+ }
mShadowCache.clear();
}
@@ -417,21 +415,12 @@ void TessellationCache::getShadowBuffers(const Matrix4* drawTransform, const Rec
// Tessellation precaching
///////////////////////////////////////////////////////////////////////////////
-static VertexBuffer* tessellatePath(const SkPath& path, const SkPaint* paint,
- float scaleX, float scaleY) {
- VertexBuffer* buffer = new VertexBuffer();
- Matrix4 matrix;
- matrix.loadScale(scaleX, scaleY, 1);
- PathTessellator::tessellatePath(path, paint, matrix, *buffer);
- return buffer;
-}
-
TessellationCache::Buffer* TessellationCache::getOrCreateBuffer(
- const Description& entry, Tessellator tessellator, const SkPaint* paint) {
+ const Description& entry, Tessellator tessellator) {
Buffer* buffer = mCache.get(entry);
if (!buffer) {
// not cached, enqueue a task to fill the buffer
- sp<TessellationTask> task = new TessellationTask(tessellator, entry, paint);
+ sp<TessellationTask> task = new TessellationTask(tessellator, entry);
buffer = new Buffer(task);
if (mProcessor == NULL) {
@@ -443,43 +432,49 @@ TessellationCache::Buffer* TessellationCache::getOrCreateBuffer(
return buffer;
}
+static VertexBuffer* tessellatePath(const TessellationCache::Description& description,
+ const SkPath& path) {
+ Matrix4 matrix;
+ SkPaint paint;
+ description.setupMatrixAndPaint(&matrix, &paint);
+ VertexBuffer* buffer = new VertexBuffer();
+ PathTessellator::tessellatePath(path, &paint, matrix, *buffer);
+ return buffer;
+}
+
///////////////////////////////////////////////////////////////////////////////
-// Rounded rects
+// RoundRect
///////////////////////////////////////////////////////////////////////////////
-static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description,
- const SkPaint& paint) {
- SkRect rect = SkRect::MakeWH(description.shape.roundRect.mWidth,
- description.shape.roundRect.mHeight);
- float rx = description.shape.roundRect.mRx;
- float ry = description.shape.roundRect.mRy;
- if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) {
- float outset = paint.getStrokeWidth() / 2;
+static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description) {
+ SkRect rect = SkRect::MakeWH(description.shape.roundRect.width,
+ description.shape.roundRect.height);
+ float rx = description.shape.roundRect.rx;
+ float ry = description.shape.roundRect.ry;
+ if (description.style == SkPaint::kStrokeAndFill_Style) {
+ float outset = description.strokeWidth / 2;
rect.outset(outset, outset);
rx += outset;
ry += outset;
}
SkPath path;
path.addRoundRect(rect, rx, ry);
- return tessellatePath(path, &paint,
- description.shape.roundRect.mScaleX, description.shape.roundRect.mScaleY);
+ return tessellatePath(description, path);
}
-TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(const Matrix4& transform,
- float width, float height, float rx, float ry, const SkPaint* paint) {
- Description entry(Description::kRoundRect, paint);
- entry.shape.roundRect.mWidth = width;
- entry.shape.roundRect.mHeight = height;
- entry.shape.roundRect.mRx = rx;
- entry.shape.roundRect.mRy = ry;
- PathTessellator::extractTessellationScales(transform,
- &entry.shape.roundRect.mScaleX, &entry.shape.roundRect.mScaleY);
-
- return getOrCreateBuffer(entry, &tessellateRoundRect, paint);
+TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(
+ const Matrix4& transform, const SkPaint& paint,
+ float width, float height, float rx, float ry) {
+ Description entry(Description::kRoundRect, transform, paint);
+ entry.shape.roundRect.width = width;
+ entry.shape.roundRect.height = height;
+ entry.shape.roundRect.rx = rx;
+ entry.shape.roundRect.ry = ry;
+ return getOrCreateBuffer(entry, &tessellateRoundRect);
}
-const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform,
- float width, float height, float rx, float ry, const SkPaint* paint) {
- return getRoundRectBuffer(transform, width, height, rx, ry, paint)->getVertexBuffer();
+const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, const SkPaint& paint,
+ float width, float height, float rx, float ry) {
+ return getRoundRectBuffer(transform, paint, width, height, rx, ry)->getVertexBuffer();
}
}; // namespace uirenderer
diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h
index d4ff943..688a699 100644
--- a/libs/hwui/TessellationCache.h
+++ b/libs/hwui/TessellationCache.h
@@ -50,30 +50,28 @@ public:
enum Type {
kNone,
kRoundRect,
- kAmbientShadow,
- kSpotShadow
};
Type type;
+ float scaleX;
+ float scaleY;
bool aa;
SkPaint::Cap cap;
SkPaint::Style style;
float strokeWidth;
union Shape {
struct RoundRect {
- float mScaleX;
- float mScaleY;
- float mWidth;
- float mHeight;
- float mRx;
- float mRy;
+ float width;
+ float height;
+ float rx;
+ float ry;
} roundRect;
} shape;
Description();
- Description(Type type);
- Description(Type type, const SkPaint* paint);
+ Description(Type type, const Matrix4& transform, const SkPaint& paint);
hash_t hash() const;
+ void setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const;
};
struct ShadowDescription {
@@ -123,12 +121,12 @@ public:
// TODO: precache/get for Oval, Lines, Points, etc.
- void precacheRoundRect(const Matrix4& transform,
- float width, float height, float rx, float ry, const SkPaint* paint) {
- getRoundRectBuffer(transform, width, height, rx, ry, paint);
+ void precacheRoundRect(const Matrix4& transform, const SkPaint& paint,
+ float width, float height, float rx, float ry) {
+ getRoundRectBuffer(transform, paint, width, height, rx, ry);
}
- const VertexBuffer* getRoundRect(const Matrix4& transform,
- float width, float height, float rx, float ry, const SkPaint* paint);
+ const VertexBuffer* getRoundRect(const Matrix4& transform, const SkPaint& paint,
+ float width, float height, float rx, float ry);
void precacheShadows(const Matrix4* drawTransform, const Rect& localClip,
bool opaque, const SkPath* casterPerimeter,
@@ -146,14 +144,14 @@ private:
class TessellationTask;
class TessellationProcessor;
+ typedef VertexBuffer* (*Tessellator)(const Description&);
- typedef VertexBuffer* (*Tessellator)(const Description&, const SkPaint&);
+ Buffer* getRectBuffer(const Matrix4& transform, const SkPaint& paint,
+ float width, float height);
+ Buffer* getRoundRectBuffer(const Matrix4& transform, const SkPaint& paint,
+ float width, float height, float rx, float ry);
- Buffer* getRoundRectBuffer(const Matrix4& transform,
- float width, float height, float rx, float ry, const SkPaint* paint);
-
- Buffer* getOrCreateBuffer(const Description& entry,
- Tessellator tessellator, const SkPaint* paint);
+ Buffer* getOrCreateBuffer(const Description& entry, Tessellator tessellator);
uint32_t mSize;
uint32_t mMaxSize;
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 60b4b96..9212d0a 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -288,19 +288,19 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo
switch (bitmap->config()) {
case SkBitmap::kA8_Config:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(),
+ uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
texture->blend = true;
break;
case SkBitmap::kRGB_565_Config:
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
- uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(),
+ uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
texture->blend = false;
break;
case SkBitmap::kARGB_8888_Config:
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
- uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(),
+ uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(),
texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
// Do this after calling getPixels() to make sure Skia's deferred
// decoding happened
@@ -333,34 +333,55 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo
void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap,
uint32_t width, uint32_t height) {
SkBitmap rgbaBitmap;
- rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0, bitmap->alphaType());
- rgbaBitmap.allocPixels();
+ rgbaBitmap.allocPixels(SkImageInfo::MakeN32(width, height, bitmap->alphaType()));
rgbaBitmap.eraseColor(0);
SkCanvas canvas(rgbaBitmap);
canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL);
- uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), width, height,
- GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
+ uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), rgbaBitmap.bytesPerPixel(),
+ width, height, GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
}
-void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride,
+void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp,
GLsizei width, GLsizei height, GLenum type, const GLvoid * data) {
- // TODO: With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
- // if the stride doesn't match the width
const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength();
- if (useStride) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
- }
+ if ((stride == width) || useStride) {
+ if (useStride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
+ }
- if (resize) {
- glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
+ if (resize) {
+ glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
+ } else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
+ }
+
+ if (useStride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ }
} else {
- glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
- }
+ // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
+ // if the stride doesn't match the width
+
+ GLvoid * temp = (GLvoid *) malloc(width * height * bpp);
+ if (!temp) return;
+
+ uint8_t * pDst = (uint8_t *)temp;
+ uint8_t * pSrc = (uint8_t *)data;
+ for (GLsizei i = 0; i < height; i++) {
+ memcpy(pDst, pSrc, width * bpp);
+ pDst += width * bpp;
+ pSrc += stride * bpp;
+ }
+
+ if (resize) {
+ glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp);
+ } else {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp);
+ }
- if (useStride) {
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ free(temp);
}
}
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index e5b5c1a..61db5b0 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -142,7 +142,7 @@ private:
void generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate = false);
void uploadLoFiTexture(bool resize, const SkBitmap* bitmap, uint32_t width, uint32_t height);
- void uploadToTexture(bool resize, GLenum format, GLsizei stride,
+ void uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp,
GLsizei width, GLsizei height, GLenum type, const GLvoid * data);
void init();
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 8355f83..249e525 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -16,13 +16,20 @@
#ifndef TREEINFO_H
#define TREEINFO_H
+#include <string>
+
#include <utils/Timers.h>
+#include "DamageAccumulator.h"
+#include "utils/Macros.h"
+
namespace android {
namespace uirenderer {
class BaseRenderNodeAnimator;
class AnimationListener;
+class OpenGLRenderer;
+class RenderState;
class AnimationHook {
public:
@@ -31,21 +38,60 @@ protected:
~AnimationHook() {}
};
-struct TreeInfo {
- // The defaults here should be safe for everyone but DrawFrameTask to use as-is.
- TreeInfo()
- : frameTimeMs(0)
+class ErrorHandler {
+public:
+ virtual void onError(const std::string& message) = 0;
+protected:
+ ~ErrorHandler() {}
+};
+
+// This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN
+class TreeInfo {
+ PREVENT_COPY_AND_ASSIGN(TreeInfo);
+public:
+ enum TraversalMode {
+ // The full monty - sync, push, run animators, etc... Used by DrawFrameTask
+ // May only be used if both the UI thread and RT thread are blocked on the
+ // prepare
+ MODE_FULL,
+ // Run only what can be done safely on RT thread. Currently this only means
+ // animators, but potentially things like SurfaceTexture updates
+ // could be handled by this as well if there are no listeners
+ MODE_RT_ONLY,
+ // The subtree is being detached. Maybe. If the RenderNode is present
+ // in both the old and new display list's children then it will get a
+ // MODE_MAYBE_DETACHING followed shortly by a MODE_FULL.
+ // Push any pending display list changes in case it is detached,
+ // but don't evaluate animators and such as if it isn't detached as a
+ // MODE_FULL will follow shortly.
+ MODE_MAYBE_DETACHING,
+ // TODO: TRIM_MEMORY?
+ };
+
+ explicit TreeInfo(TraversalMode mode, RenderState& renderState)
+ : mode(mode)
+ , frameTimeMs(0)
, animationHook(NULL)
- , prepareTextures(false)
- , performStagingPush(true)
- , evaluateAnimations(false)
+ , prepareTextures(mode == MODE_FULL)
+ , damageAccumulator(NullDamageAccumulator::instance())
+ , renderState(renderState)
+ , renderer(NULL)
+ , errorHandler(NULL)
{}
+ const TraversalMode mode;
nsecs_t frameTimeMs;
AnimationHook* animationHook;
+ // TODO: Remove this? Currently this is used to signal to stop preparing
+ // textures if we run out of cache space.
bool prepareTextures;
- bool performStagingPush;
- bool evaluateAnimations;
+ // Must not be null
+ IDamageAccumulator* damageAccumulator;
+ RenderState& renderState;
+ // The renderer that will be drawing the next frame. Use this to push any
+ // layer updates or similar. May be NULL.
+ OpenGLRenderer* renderer;
+ ErrorHandler* errorHandler;
struct Out {
Out()
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 9ebee1d..281a8e1 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -18,332 +18,31 @@
#include "CanvasContext.h"
-#include <cutils/properties.h>
#include <private/hwui/DrawGlInfo.h>
#include <strings.h>
+#include "EglManager.h"
#include "RenderThread.h"
#include "../Caches.h"
#include "../DeferredLayerUpdater.h"
+#include "../RenderState.h"
#include "../LayerRenderer.h"
#include "../OpenGLRenderer.h"
#include "../Stencil.h"
-#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
-#define GLES_VERSION 2
-
-// Android-specific addition that is used to show when frames began in systrace
-EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
-
namespace android {
namespace uirenderer {
namespace renderthread {
-#define ERROR_CASE(x) case x: return #x;
-static const char* egl_error_str(EGLint error) {
- switch (error) {
- ERROR_CASE(EGL_SUCCESS)
- ERROR_CASE(EGL_NOT_INITIALIZED)
- ERROR_CASE(EGL_BAD_ACCESS)
- ERROR_CASE(EGL_BAD_ALLOC)
- ERROR_CASE(EGL_BAD_ATTRIBUTE)
- ERROR_CASE(EGL_BAD_CONFIG)
- ERROR_CASE(EGL_BAD_CONTEXT)
- ERROR_CASE(EGL_BAD_CURRENT_SURFACE)
- ERROR_CASE(EGL_BAD_DISPLAY)
- ERROR_CASE(EGL_BAD_MATCH)
- ERROR_CASE(EGL_BAD_NATIVE_PIXMAP)
- ERROR_CASE(EGL_BAD_NATIVE_WINDOW)
- ERROR_CASE(EGL_BAD_PARAMETER)
- ERROR_CASE(EGL_BAD_SURFACE)
- ERROR_CASE(EGL_CONTEXT_LOST)
- default:
- return "Unknown error";
- }
-}
-static const char* egl_error_str() {
- return egl_error_str(eglGetError());
-}
-
-static bool load_dirty_regions_property() {
- char buf[PROPERTY_VALUE_MAX];
- int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true");
- return !strncasecmp("true", buf, len);
-}
-
-// This class contains the shared global EGL objects, such as EGLDisplay
-// and EGLConfig, which are re-used by CanvasContext
-class GlobalContext {
-public:
- static GlobalContext* get();
-
- // Returns true on success, false on failure
- void initialize();
-
- bool hasContext();
-
- void usePBufferSurface();
- EGLSurface createSurface(EGLNativeWindowType window);
- void destroySurface(EGLSurface surface);
-
- void destroy();
-
- bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; }
- // Returns true if the current surface changed, false if it was already current
- bool makeCurrent(EGLSurface surface);
- void beginFrame(EGLSurface surface, EGLint* width, EGLint* height);
- void swapBuffers(EGLSurface surface);
-
- bool enableDirtyRegions(EGLSurface surface);
-
- void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
-
-private:
- GlobalContext();
- // GlobalContext is never destroyed, method is purposely not implemented
- ~GlobalContext();
-
- void loadConfig();
- void createContext();
- void initAtlas();
-
- static GlobalContext* sContext;
-
- EGLDisplay mEglDisplay;
- EGLConfig mEglConfig;
- EGLContext mEglContext;
- EGLSurface mPBufferSurface;
-
- const bool mRequestDirtyRegions;
- bool mCanSetDirtyRegions;
-
- EGLSurface mCurrentSurface;
-
- sp<GraphicBuffer> mAtlasBuffer;
- int64_t* mAtlasMap;
- size_t mAtlasMapSize;
-};
-
-GlobalContext* GlobalContext::sContext = 0;
-
-GlobalContext* GlobalContext::get() {
- if (!sContext) {
- sContext = new GlobalContext();
- }
- return sContext;
-}
-
-GlobalContext::GlobalContext()
- : mEglDisplay(EGL_NO_DISPLAY)
- , mEglConfig(0)
- , mEglContext(EGL_NO_CONTEXT)
- , mPBufferSurface(EGL_NO_SURFACE)
- , mRequestDirtyRegions(load_dirty_regions_property())
- , mCurrentSurface(EGL_NO_SURFACE)
- , mAtlasMap(NULL)
- , mAtlasMapSize(0) {
- mCanSetDirtyRegions = mRequestDirtyRegions;
- ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false");
-}
-
-void GlobalContext::initialize() {
- if (hasContext()) return;
-
- mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
- LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
- "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
-
- EGLint major, minor;
- LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
- "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
-
- ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
-
- loadConfig();
- createContext();
- usePBufferSurface();
- Caches::getInstance().init();
- initAtlas();
-}
-
-bool GlobalContext::hasContext() {
- return mEglDisplay != EGL_NO_DISPLAY;
-}
-
-void GlobalContext::loadConfig() {
- EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
- EGLint attribs[] = {
- EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
- EGL_RED_SIZE, 8,
- EGL_GREEN_SIZE, 8,
- EGL_BLUE_SIZE, 8,
- EGL_ALPHA_SIZE, 8,
- EGL_DEPTH_SIZE, 0,
- EGL_CONFIG_CAVEAT, EGL_NONE,
- EGL_STENCIL_SIZE, Stencil::getStencilSize(),
- EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
- EGL_NONE
- };
-
- EGLint num_configs = 1;
- if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
- || num_configs != 1) {
- // Failed to get a valid config
- if (mCanSetDirtyRegions) {
- ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
- // Try again without dirty regions enabled
- mCanSetDirtyRegions = false;
- loadConfig();
- } else {
- LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
- }
- }
-}
-
-void GlobalContext::createContext() {
- EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE };
- mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
- LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
- "Failed to create context, error = %s", egl_error_str());
-}
-
-void GlobalContext::setTextureAtlas(const sp<GraphicBuffer>& buffer,
- int64_t* map, size_t mapSize) {
-
- // Already initialized
- if (mAtlasBuffer.get()) {
- ALOGW("Multiple calls to setTextureAtlas!");
- delete map;
- return;
- }
-
- mAtlasBuffer = buffer;
- mAtlasMap = map;
- mAtlasMapSize = mapSize;
-
- if (hasContext()) {
- usePBufferSurface();
- initAtlas();
- }
-}
-
-void GlobalContext::initAtlas() {
- if (mAtlasBuffer.get()) {
- Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize);
- }
-}
-
-void GlobalContext::usePBufferSurface() {
- LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
- "usePBufferSurface() called on uninitialized GlobalContext!");
-
- if (mPBufferSurface == EGL_NO_SURFACE) {
- EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
- mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
- }
- makeCurrent(mPBufferSurface);
-}
-
-EGLSurface GlobalContext::createSurface(EGLNativeWindowType window) {
- initialize();
- return eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL);
-}
-
-void GlobalContext::destroySurface(EGLSurface surface) {
- if (isCurrent(surface)) {
- makeCurrent(EGL_NO_SURFACE);
- }
- if (!eglDestroySurface(mEglDisplay, surface)) {
- ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str());
- }
-}
-
-void GlobalContext::destroy() {
- if (mEglDisplay == EGL_NO_DISPLAY) return;
-
- usePBufferSurface();
- if (Caches::hasInstance()) {
- Caches::getInstance().terminate();
- }
-
- eglDestroyContext(mEglDisplay, mEglContext);
- eglDestroySurface(mEglDisplay, mPBufferSurface);
- eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- eglTerminate(mEglDisplay);
- eglReleaseThread();
-
- mEglDisplay = EGL_NO_DISPLAY;
- mEglContext = EGL_NO_CONTEXT;
- mPBufferSurface = EGL_NO_SURFACE;
- mCurrentSurface = EGL_NO_SURFACE;
-}
-
-bool GlobalContext::makeCurrent(EGLSurface surface) {
- if (isCurrent(surface)) return false;
-
- if (surface == EGL_NO_SURFACE) {
- // If we are setting EGL_NO_SURFACE we don't care about any of the potential
- // return errors, which would only happen if mEglDisplay had already been
- // destroyed in which case the current context is already NO_CONTEXT
- eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
- } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
- LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
- (void*)surface, egl_error_str());
- }
- mCurrentSurface = surface;
- return true;
-}
-
-void GlobalContext::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) {
- LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
- "Tried to beginFrame on EGL_NO_SURFACE!");
- makeCurrent(surface);
- if (width) {
- eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
- }
- if (height) {
- eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
- }
- eglBeginFrame(mEglDisplay, surface);
-}
-
-void GlobalContext::swapBuffers(EGLSurface surface) {
- eglSwapBuffers(mEglDisplay, surface);
- EGLint err = eglGetError();
- LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS,
- "Encountered EGL error %d %s during rendering", err, egl_error_str(err));
-}
-
-bool GlobalContext::enableDirtyRegions(EGLSurface surface) {
- if (!mRequestDirtyRegions) return false;
-
- if (mCanSetDirtyRegions) {
- if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_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;
- }
- return value == EGL_BUFFER_PRESERVED;
-}
-
-CanvasContext::CanvasContext(bool translucent, RenderNode* rootRenderNode)
- : mRenderThread(RenderThread::getInstance())
+CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode)
+ : mRenderThread(thread)
+ , mEglManager(thread.eglManager())
, mEglSurface(EGL_NO_SURFACE)
, mDirtyRegionsEnabled(false)
, mOpaque(!translucent)
- , mCanvas(0)
+ , mCanvas(NULL)
, mHaveNewSurface(false)
, mRootRenderNode(rootRenderNode) {
- mGlobalContext = GlobalContext::get();
}
CanvasContext::~CanvasContext() {
@@ -363,19 +62,16 @@ void CanvasContext::setSurface(ANativeWindow* window) {
mNativeWindow = window;
if (mEglSurface != EGL_NO_SURFACE) {
- mGlobalContext->destroySurface(mEglSurface);
+ mEglManager.destroySurface(mEglSurface);
mEglSurface = EGL_NO_SURFACE;
}
if (window) {
- mEglSurface = mGlobalContext->createSurface(window);
- LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
- "Failed to create EGLSurface for window %p, eglErr = %s",
- (void*) window, egl_error_str());
+ mEglSurface = mEglManager.createSurface(window);
}
if (mEglSurface != EGL_NO_SURFACE) {
- mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface);
+ mDirtyRegionsEnabled = mEglManager.enableDirtyRegions(mEglSurface);
mHaveNewSurface = true;
makeCurrent();
} else {
@@ -384,7 +80,7 @@ void CanvasContext::setSurface(ANativeWindow* window) {
}
void CanvasContext::swapBuffers() {
- mGlobalContext->swapBuffers(mEglSurface);
+ mEglManager.swapBuffers(mEglSurface);
mHaveNewSurface = false;
}
@@ -397,7 +93,7 @@ void CanvasContext::requireSurface() {
bool CanvasContext::initialize(ANativeWindow* window) {
if (mCanvas) return false;
setSurface(window);
- mCanvas = new OpenGLRenderer();
+ mCanvas = new OpenGLRenderer(mRenderThread.renderState());
mCanvas->initProperties();
return true;
}
@@ -424,7 +120,7 @@ void CanvasContext::setOpaque(bool opaque) {
void CanvasContext::makeCurrent() {
// TODO: Figure out why this workaround is needed, see b/13913604
// In the meantime this matches the behavior of GLRenderer, so it is not a regression
- mHaveNewSurface |= mGlobalContext->makeCurrent(mEglSurface);
+ mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface);
}
void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info) {
@@ -439,6 +135,8 @@ void CanvasContext::prepareTree(TreeInfo& info) {
mRenderThread.removeFrameCallback(this);
info.frameTimeMs = mRenderThread.timeLord().frameTimeMs();
+ info.damageAccumulator = &mDamageAccumulator;
+ info.renderer = mCanvas;
mRootRenderNode->prepareTree(info);
int runningBehind = 0;
@@ -450,9 +148,7 @@ void CanvasContext::prepareTree(TreeInfo& info) {
info.out.canDrawThisFrame = !runningBehind;
if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
- if (info.out.hasFunctors) {
- info.out.requiresUiRedraw = true;
- } else if (!info.out.requiresUiRedraw) {
+ if (!info.out.requiresUiRedraw) {
// If animationsNeedsRedraw is set don't bother posting for an RT anim
// as we will just end up fighting the UI thread.
mRenderThread.postFrameCallback(this);
@@ -465,33 +161,36 @@ void CanvasContext::notifyFramePending() {
mRenderThread.pushBackFrameCallback(this);
}
-void CanvasContext::draw(Rect* dirty) {
+void CanvasContext::draw() {
LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
- "drawDisplayList called on a context with no canvas or surface!");
+ "drawRenderNode called on a context with no canvas or surface!");
profiler().markPlaybackStart();
+ SkRect dirty;
+ mDamageAccumulator.finish(&dirty);
+
EGLint width, height;
- mGlobalContext->beginFrame(mEglSurface, &width, &height);
+ mEglManager.beginFrame(mEglSurface, &width, &height);
if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
mCanvas->setViewport(width, height);
- dirty = NULL;
+ dirty.setEmpty();
} else if (!mDirtyRegionsEnabled || mHaveNewSurface) {
- dirty = NULL;
+ dirty.setEmpty();
} else {
- profiler().unionDirty(dirty);
+ profiler().unionDirty(&dirty);
}
status_t status;
- if (dirty && !dirty->isEmpty()) {
- status = mCanvas->prepareDirty(dirty->left, dirty->top,
- dirty->right, dirty->bottom, mOpaque);
+ if (!dirty.isEmpty()) {
+ status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop,
+ dirty.fRight, dirty.fBottom, mOpaque);
} else {
status = mCanvas->prepare(mOpaque);
}
Rect outBounds;
- status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds);
+ status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
profiler().draw(mCanvas);
@@ -516,40 +215,35 @@ void CanvasContext::doFrame() {
profiler().startFrame();
- TreeInfo info;
- info.evaluateAnimations = true;
- info.performStagingPush = false;
+ TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
info.prepareTextures = false;
prepareTree(info);
if (info.out.canDrawThisFrame) {
- draw(NULL);
+ draw();
}
}
-void CanvasContext::invokeFunctor(Functor* functor) {
+void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) {
ATRACE_CALL();
DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
- if (mGlobalContext->hasContext()) {
- requireGlContext();
+ if (thread.eglManager().hasEglContext()) {
+ thread.eglManager().requireGlContext();
mode = DrawGlInfo::kModeProcess;
}
- (*functor)(mode, NULL);
- if (mCanvas) {
- mCanvas->resume();
- }
+ thread.renderState().invokeFunctor(functor, mode, NULL);
}
bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
requireGlContext();
- TreeInfo info;
+ TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState());
layer->apply(info);
- return LayerRenderer::copyLayer(layer->backingLayer(), bitmap);
+ return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap);
}
void CanvasContext::flushCaches(Caches::FlushMode flushMode) {
- if (mGlobalContext->hasContext()) {
+ if (mEglManager.hasEglContext()) {
requireGlContext();
Caches::getInstance().flush(flushMode);
}
@@ -562,25 +256,25 @@ void CanvasContext::runWithGlContext(RenderTask* task) {
Layer* CanvasContext::createRenderLayer(int width, int height) {
requireSurface();
- return LayerRenderer::createRenderLayer(width, height);
+ return LayerRenderer::createRenderLayer(mRenderThread.renderState(), width, height);
}
Layer* CanvasContext::createTextureLayer() {
requireSurface();
- return LayerRenderer::createTextureLayer();
+ return LayerRenderer::createTextureLayer(mRenderThread.renderState());
}
void CanvasContext::requireGlContext() {
if (mEglSurface != EGL_NO_SURFACE) {
makeCurrent();
} else {
- mGlobalContext->usePBufferSurface();
+ mEglManager.usePBufferSurface();
}
}
-void CanvasContext::setTextureAtlas(const sp<GraphicBuffer>& buffer,
- int64_t* map, size_t mapSize) {
- GlobalContext::get()->setTextureAtlas(buffer, map, mapSize);
+void CanvasContext::setTextureAtlas(RenderThread& thread,
+ const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) {
+ thread.eglManager().setTextureAtlas(buffer, map, mapSize);
}
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 00c5bf0..d2ce1a6 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -23,6 +23,7 @@
#include <utils/Functor.h>
#include <utils/Vector.h>
+#include "../DamageAccumulator.h"
#include "../DrawProfiler.h"
#include "../RenderNode.h"
#include "RenderTask.h"
@@ -40,13 +41,13 @@ class Layer;
namespace renderthread {
-class GlobalContext;
+class EglManager;
// This per-renderer class manages the bridge between the global EGL context
// and the render surface.
class CanvasContext : public IFrameCallback {
public:
- CanvasContext(bool translucent, RenderNode* rootRenderNode);
+ CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode);
virtual ~CanvasContext();
bool initialize(ANativeWindow* window);
@@ -57,7 +58,7 @@ public:
void makeCurrent();
void processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info);
void prepareTree(TreeInfo& info);
- void draw(Rect* dirty);
+ void draw();
void destroyCanvasAndSurface();
// IFrameCallback, Chroreographer-driven frame callback entry point
@@ -67,15 +68,15 @@ public:
void flushCaches(Caches::FlushMode flushMode);
- void invokeFunctor(Functor* functor);
+ static void invokeFunctor(RenderThread& thread, Functor* functor);
void runWithGlContext(RenderTask* task);
Layer* createRenderLayer(int width, int height);
Layer* createTextureLayer();
- ANDROID_API static void setTextureAtlas(const sp<GraphicBuffer>& buffer,
- int64_t* map, size_t mapSize);
+ ANDROID_API static void setTextureAtlas(RenderThread& thread,
+ const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
void notifyFramePending();
@@ -90,8 +91,8 @@ private:
void requireGlContext();
- GlobalContext* mGlobalContext;
RenderThread& mRenderThread;
+ EglManager& mEglManager;
sp<ANativeWindow> mNativeWindow;
EGLSurface mEglSurface;
bool mDirtyRegionsEnabled;
@@ -99,6 +100,7 @@ private:
bool mOpaque;
OpenGLRenderer* mCanvas;
bool mHaveNewSurface;
+ DamageAccumulator mDamageAccumulator;
const sp<RenderNode> mRootRenderNode;
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 61d67ca..fddffd5 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -68,10 +68,6 @@ void DrawFrameTask::removeLayerUpdate(DeferredLayerUpdater* layer) {
}
}
-void DrawFrameTask::setDirty(int left, int top, int right, int bottom) {
- mDirty.set(left, top, right, bottom);
-}
-
int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) {
LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
@@ -83,7 +79,6 @@ int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos
// Reset the single-frame data
mFrameTimeNanos = 0;
mRecordDurationNanos = 0;
- mDirty.setEmpty();
return mSyncResult;
}
@@ -103,13 +98,12 @@ void DrawFrameTask::run() {
bool canUnblockUiThread;
bool canDrawThisFrame;
{
- TreeInfo info;
+ TreeInfo info(TreeInfo::MODE_FULL, mRenderThread->renderState());
canUnblockUiThread = syncFrameState(info);
canDrawThisFrame = info.out.canDrawThisFrame;
}
// Grab a copy of everything we need
- Rect dirty(mDirty);
CanvasContext* context = mContext;
// From this point on anything in "this" is *UNSAFE TO ACCESS*
@@ -118,7 +112,7 @@ void DrawFrameTask::run() {
}
if (CC_LIKELY(canDrawThisFrame)) {
- context->draw(&dirty);
+ context->draw();
}
if (!canUnblockUiThread) {
@@ -126,37 +120,25 @@ void DrawFrameTask::run() {
}
}
-static void initTreeInfo(TreeInfo& info) {
- info.prepareTextures = true;
- info.performStagingPush = true;
- info.evaluateAnimations = true;
-}
-
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
ATRACE_CALL();
mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos);
mContext->makeCurrent();
Caches::getInstance().textureCache.resetMarkInUse();
- initTreeInfo(info);
for (size_t i = 0; i < mLayers.size(); i++) {
mContext->processLayerUpdate(mLayers[i].get(), info);
}
mLayers.clear();
- if (info.out.hasAnimations) {
- // TODO: Uh... crap?
- }
mContext->prepareTree(info);
if (info.out.hasAnimations) {
- // TODO: dirty calculations, for now just do a full-screen inval
- mDirty.setEmpty();
if (info.out.requiresUiRedraw) {
mSyncResult |= kSync_UIRedrawRequired;
}
}
// If prepareTextures is false, we ran out of texture cache space
- return !info.out.hasFunctors && info.prepareTextures;
+ return info.prepareTextures;
}
void DrawFrameTask::unblockUiThread() {
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index d4129b6..243cc5d 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -41,7 +41,7 @@ class RenderThread;
enum SyncResult {
kSync_OK = 0,
- kSync_UIRedrawRequired = 1 << 1,
+ kSync_UIRedrawRequired = 1 << 0,
};
/*
@@ -60,7 +60,6 @@ public:
void pushLayerUpdate(DeferredLayerUpdater* layer);
void removeLayerUpdate(DeferredLayerUpdater* layer);
- void setDirty(int left, int top, int right, int bottom);
void setDensity(float density) { mDensity = density; }
int drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos);
@@ -80,7 +79,6 @@ private:
/*********************************************
* Single frame data
*********************************************/
- Rect mDirty;
nsecs_t mFrameTimeNanos;
nsecs_t mRecordDurationNanos;
float mDensity;
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
new file mode 100644
index 0000000..05ca34d
--- /dev/null
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -0,0 +1,288 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "EglContext"
+
+#include "EglManager.h"
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include "../RenderState.h"
+#include "RenderThread.h"
+
+#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
+#define GLES_VERSION 2
+
+// Android-specific addition that is used to show when frames began in systrace
+EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+#define ERROR_CASE(x) case x: return #x;
+static const char* egl_error_str(EGLint error) {
+ switch (error) {
+ ERROR_CASE(EGL_SUCCESS)
+ ERROR_CASE(EGL_NOT_INITIALIZED)
+ ERROR_CASE(EGL_BAD_ACCESS)
+ ERROR_CASE(EGL_BAD_ALLOC)
+ ERROR_CASE(EGL_BAD_ATTRIBUTE)
+ ERROR_CASE(EGL_BAD_CONFIG)
+ ERROR_CASE(EGL_BAD_CONTEXT)
+ ERROR_CASE(EGL_BAD_CURRENT_SURFACE)
+ ERROR_CASE(EGL_BAD_DISPLAY)
+ ERROR_CASE(EGL_BAD_MATCH)
+ ERROR_CASE(EGL_BAD_NATIVE_PIXMAP)
+ ERROR_CASE(EGL_BAD_NATIVE_WINDOW)
+ ERROR_CASE(EGL_BAD_PARAMETER)
+ ERROR_CASE(EGL_BAD_SURFACE)
+ ERROR_CASE(EGL_CONTEXT_LOST)
+ default:
+ return "Unknown error";
+ }
+}
+static const char* egl_error_str() {
+ return egl_error_str(eglGetError());
+}
+
+static bool load_dirty_regions_property() {
+ char buf[PROPERTY_VALUE_MAX];
+ int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true");
+ return !strncasecmp("true", buf, len);
+}
+
+EglManager::EglManager(RenderThread& thread)
+ : mRenderThread(thread)
+ , mEglDisplay(EGL_NO_DISPLAY)
+ , mEglConfig(0)
+ , mEglContext(EGL_NO_CONTEXT)
+ , mPBufferSurface(EGL_NO_SURFACE)
+ , mRequestDirtyRegions(load_dirty_regions_property())
+ , mCurrentSurface(EGL_NO_SURFACE)
+ , mAtlasMap(NULL)
+ , mAtlasMapSize(0) {
+ mCanSetDirtyRegions = mRequestDirtyRegions;
+ ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false");
+}
+
+void EglManager::initialize() {
+ if (hasEglContext()) return;
+
+ mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
+ "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
+
+ EGLint major, minor;
+ LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
+ "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
+
+ ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
+
+ loadConfig();
+ createContext();
+ usePBufferSurface();
+ mRenderThread.renderState().onGLContextCreated();
+ initAtlas();
+}
+
+bool EglManager::hasEglContext() {
+ return mEglDisplay != EGL_NO_DISPLAY;
+}
+
+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) {
+ usePBufferSurface();
+ }
+}
+
+void EglManager::loadConfig() {
+ EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+ EGLint attribs[] = {
+ EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+ EGL_RED_SIZE, 8,
+ EGL_GREEN_SIZE, 8,
+ EGL_BLUE_SIZE, 8,
+ EGL_ALPHA_SIZE, 8,
+ EGL_DEPTH_SIZE, 0,
+ EGL_CONFIG_CAVEAT, EGL_NONE,
+ EGL_STENCIL_SIZE, Stencil::getStencilSize(),
+ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
+ EGL_NONE
+ };
+
+ EGLint num_configs = 1;
+ if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
+ || num_configs != 1) {
+ // Failed to get a valid config
+ if (mCanSetDirtyRegions) {
+ ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
+ // Try again without dirty regions enabled
+ mCanSetDirtyRegions = false;
+ loadConfig();
+ } else {
+ LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
+ }
+ }
+}
+
+void EglManager::createContext() {
+ EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE };
+ mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
+ LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
+ "Failed to create context, error = %s", egl_error_str());
+}
+
+void EglManager::setTextureAtlas(const sp<GraphicBuffer>& buffer,
+ int64_t* map, size_t mapSize) {
+
+ // Already initialized
+ if (mAtlasBuffer.get()) {
+ ALOGW("Multiple calls to setTextureAtlas!");
+ delete map;
+ return;
+ }
+
+ mAtlasBuffer = buffer;
+ mAtlasMap = map;
+ mAtlasMapSize = mapSize;
+
+ if (hasEglContext()) {
+ usePBufferSurface();
+ initAtlas();
+ }
+}
+
+void EglManager::initAtlas() {
+ if (mAtlasBuffer.get()) {
+ Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize);
+ }
+}
+
+void EglManager::usePBufferSurface() {
+ LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
+ "usePBufferSurface() called on uninitialized GlobalContext!");
+
+ if (mPBufferSurface == EGL_NO_SURFACE) {
+ EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
+ mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
+ }
+ makeCurrent(mPBufferSurface);
+}
+
+EGLSurface EglManager::createSurface(EGLNativeWindowType window) {
+ initialize();
+ EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL);
+ LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
+ "Failed to create EGLSurface for window %p, eglErr = %s",
+ (void*) window, egl_error_str());
+ return surface;
+}
+
+void EglManager::destroySurface(EGLSurface surface) {
+ if (isCurrent(surface)) {
+ makeCurrent(EGL_NO_SURFACE);
+ }
+ if (!eglDestroySurface(mEglDisplay, surface)) {
+ ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str());
+ }
+}
+
+void EglManager::destroy() {
+ if (mEglDisplay == EGL_NO_DISPLAY) return;
+
+ usePBufferSurface();
+ if (Caches::hasInstance()) {
+ Caches::getInstance().terminate();
+ }
+
+ eglDestroyContext(mEglDisplay, mEglContext);
+ eglDestroySurface(mEglDisplay, mPBufferSurface);
+ eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ eglTerminate(mEglDisplay);
+ eglReleaseThread();
+
+ mEglDisplay = EGL_NO_DISPLAY;
+ mEglContext = EGL_NO_CONTEXT;
+ mPBufferSurface = EGL_NO_SURFACE;
+ mCurrentSurface = EGL_NO_SURFACE;
+}
+
+bool EglManager::makeCurrent(EGLSurface surface) {
+ if (isCurrent(surface)) return false;
+
+ if (surface == EGL_NO_SURFACE) {
+ // If we are setting EGL_NO_SURFACE we don't care about any of the potential
+ // return errors, which would only happen if mEglDisplay had already been
+ // destroyed in which case the current context is already NO_CONTEXT
+ eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
+ LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
+ (void*)surface, egl_error_str());
+ }
+ mCurrentSurface = surface;
+ return true;
+}
+
+void EglManager::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) {
+ LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
+ "Tried to beginFrame on EGL_NO_SURFACE!");
+ makeCurrent(surface);
+ if (width) {
+ eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
+ }
+ if (height) {
+ eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
+ }
+ eglBeginFrame(mEglDisplay, surface);
+}
+
+void EglManager::swapBuffers(EGLSurface surface) {
+ eglSwapBuffers(mEglDisplay, surface);
+ EGLint err = eglGetError();
+ LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS,
+ "Encountered EGL error %d %s during rendering", err, egl_error_str(err));
+}
+
+bool EglManager::enableDirtyRegions(EGLSurface surface) {
+ if (!mRequestDirtyRegions) return false;
+
+ if (mCanSetDirtyRegions) {
+ if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_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;
+ }
+ return value == EGL_BUFFER_PRESERVED;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
new file mode 100644
index 0000000..a844cfc
--- /dev/null
+++ b/libs/hwui/renderthread/EglManager.h
@@ -0,0 +1,88 @@
+/*
+ * 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 EGLMANAGER_H
+#define EGLMANAGER_H
+
+#include <cutils/compiler.h>
+#include <EGL/egl.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+class RenderThread;
+
+// This class contains the shared global EGL objects, such as EGLDisplay
+// and EGLConfig, which are re-used by CanvasContext
+class EglManager {
+public:
+ // Returns true on success, false on failure
+ void initialize();
+
+ bool hasEglContext();
+ void requireGlContext();
+
+ void usePBufferSurface();
+ EGLSurface createSurface(EGLNativeWindowType window);
+ void destroySurface(EGLSurface surface);
+
+ void destroy();
+
+ bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; }
+ // Returns true if the current surface changed, false if it was already current
+ bool makeCurrent(EGLSurface surface);
+ void beginFrame(EGLSurface surface, EGLint* width, EGLint* height);
+ void swapBuffers(EGLSurface surface);
+
+ bool enableDirtyRegions(EGLSurface surface);
+
+ void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
+
+private:
+ friend class RenderThread;
+
+ EglManager(RenderThread& thread);
+ // EglContext is never destroyed, method is purposely not implemented
+ ~EglManager();
+
+ void loadConfig();
+ void createContext();
+ void initAtlas();
+
+ RenderThread& mRenderThread;
+
+ EGLDisplay mEglDisplay;
+ EGLConfig mEglConfig;
+ EGLContext mEglContext;
+ EGLSurface mPBufferSurface;
+
+ const bool mRequestDirtyRegions;
+ bool mCanSetDirtyRegions;
+
+ EGLSurface mCurrentSurface;
+
+ sp<GraphicBuffer> mAtlasBuffer;
+ int64_t* mAtlasMap;
+ size_t mAtlasMapSize;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* EGLMANAGER_H */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 0901963..f90a26a 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -47,13 +47,13 @@ namespace renderthread {
#define SETUP_TASK(method) \
LOG_ALWAYS_FATAL_IF( METHOD_INVOKE_PAYLOAD_SIZE < sizeof(ARGS(method)), \
- "METHOD_INVOKE_PAYLOAD_SIZE %d is smaller than sizeof(" #method "Args) %d", \
+ "METHOD_INVOKE_PAYLOAD_SIZE %zu is smaller than sizeof(" #method "Args) %zu", \
METHOD_INVOKE_PAYLOAD_SIZE, sizeof(ARGS(method))); \
MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
ARGS(method) *args = (ARGS(method) *) task->payload()
-CREATE_BRIDGE2(createContext, bool translucent, RenderNode* rootRenderNode) {
- return new CanvasContext(args->translucent, args->rootRenderNode);
+CREATE_BRIDGE3(createContext, RenderThread* thread, bool translucent, RenderNode* rootRenderNode) {
+ return new CanvasContext(*args->thread, args->translucent, args->rootRenderNode);
}
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode)
@@ -62,6 +62,7 @@ RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode)
SETUP_TASK(createContext);
args->translucent = translucent;
args->rootRenderNode = rootRenderNode;
+ args->thread = &mRenderThread;
mContext = (CanvasContext*) postAndWait(task);
mDrawFrameTask.setContext(&mRenderThread, mContext);
}
@@ -180,8 +181,7 @@ void RenderProxy::setOpaque(bool opaque) {
}
int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
- float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
- mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
+ float density) {
mDrawFrameTask.setDensity(density);
return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos);
}
@@ -200,20 +200,29 @@ void RenderProxy::destroyCanvasAndSurface() {
postAndWait(task);
}
-CREATE_BRIDGE2(invokeFunctor, CanvasContext* context, Functor* functor) {
- args->context->invokeFunctor(args->functor);
+CREATE_BRIDGE2(invokeFunctor, RenderThread* thread, Functor* functor) {
+ CanvasContext::invokeFunctor(*args->thread, args->functor);
return NULL;
}
void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) {
ATRACE_CALL();
+ RenderThread& thread = RenderThread::getInstance();
SETUP_TASK(invokeFunctor);
- args->context = mContext;
+ args->thread = &thread;
args->functor = functor;
if (waitForCompletion) {
- postAndWait(task);
+ // 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);
} else {
- post(task);
+ thread.queue(task);
}
}
@@ -234,7 +243,7 @@ CREATE_BRIDGE1(destroyLayer, Layer* layer) {
return NULL;
}
-static void enqueueDestroyLayer(Layer* layer) {
+void RenderProxy::enqueueDestroyLayer(Layer* layer) {
SETUP_TASK(destroyLayer);
args->layer = layer;
RenderThread::getInstance().queue(task);
@@ -243,7 +252,7 @@ static void enqueueDestroyLayer(Layer* layer) {
CREATE_BRIDGE3(createDisplayListLayer, CanvasContext* context, int width, int height) {
Layer* layer = args->context->createRenderLayer(args->width, args->height);
if (!layer) return 0;
- return new DeferredLayerUpdater(layer, enqueueDestroyLayer);
+ return new DeferredLayerUpdater(layer, RenderProxy::enqueueDestroyLayer);
}
DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height) {
@@ -259,7 +268,7 @@ DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height)
CREATE_BRIDGE1(createTextureLayer, CanvasContext* context) {
Layer* layer = args->context->createTextureLayer();
if (!layer) return 0;
- return new DeferredLayerUpdater(layer, enqueueDestroyLayer);
+ return new DeferredLayerUpdater(layer, RenderProxy::enqueueDestroyLayer);
}
DeferredLayerUpdater* RenderProxy::createTextureLayer() {
@@ -337,6 +346,22 @@ void RenderProxy::dumpProfileInfo(int fd) {
postAndWait(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);
+ return NULL;
+}
+
+void RenderProxy::setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size) {
+ SETUP_TASK(setTextureAtlas);
+ args->thread = &mRenderThread;
+ args->buffer = buffer.get();
+ args->buffer->incStrong(0);
+ args->map = map;
+ args->size = size;
+ post(task);
+}
+
void RenderProxy::post(RenderTask* task) {
mRenderThread.queue(task);
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 944ff9c..df0aff0 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -70,13 +70,14 @@ public:
ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius);
ANDROID_API void setOpaque(bool opaque);
ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos,
- float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
+ float density);
ANDROID_API void destroyCanvasAndSurface();
- ANDROID_API void invokeFunctor(Functor* functor, bool waitForCompletion);
+ ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion);
ANDROID_API void runWithGlContext(RenderTask* task);
+ static void enqueueDestroyLayer(Layer* layer);
ANDROID_API DeferredLayerUpdater* createDisplayListLayer(int width, int height);
ANDROID_API DeferredLayerUpdater* createTextureLayer();
ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
@@ -90,6 +91,8 @@ public:
ANDROID_API void dumpProfileInfo(int fd);
+ ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size);
+
private:
RenderThread& mRenderThread;
CanvasContext* mContext;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 4a4e254..03e98d5 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -21,7 +21,9 @@
#include <gui/DisplayEventReceiver.h>
#include <utils/Log.h>
+#include "../RenderState.h"
#include "CanvasContext.h"
+#include "EglManager.h"
#include "RenderProxy.h"
namespace android {
@@ -138,13 +140,16 @@ RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
, mDisplayEventReceiver(0)
, mVsyncRequested(false)
, mFrameCallbackTaskPending(false)
- , mFrameCallbackTask(0) {
+ , mFrameCallbackTask(0)
+ , mRenderState(NULL)
+ , mEglManager(NULL) {
mFrameCallbackTask = new DispatchFrameCallbacks(this);
mLooper = new Looper(false);
run("RenderThread");
}
RenderThread::~RenderThread() {
+ LOG_ALWAYS_FATAL("Can't destroy the render thread");
}
void RenderThread::initializeDisplayEventReceiver() {
@@ -159,6 +164,12 @@ void RenderThread::initializeDisplayEventReceiver() {
Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this);
}
+void RenderThread::initThreadLocals() {
+ initializeDisplayEventReceiver();
+ mEglManager = new EglManager(*this);
+ mRenderState = new RenderState();
+}
+
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
ALOGE("Display event receiver pipe was closed or an error occurred. "
@@ -233,7 +244,7 @@ void RenderThread::requestVsync() {
}
bool RenderThread::threadLoop() {
- initializeDisplayEventReceiver();
+ initThreadLocals();
int timeoutMillis = -1;
for (;;) {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 4412584..0b91e9d 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -31,12 +31,18 @@
#include "TimeLord.h"
namespace android {
+
class DisplayEventReceiver;
namespace uirenderer {
+
+class RenderState;
+
namespace renderthread {
class DispatchFrameCallbacks;
+class EglManager;
+class RenderProxy;
class TaskQueue {
public:
@@ -62,7 +68,7 @@ protected:
~IFrameCallback() {}
};
-class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> {
+class ANDROID_API RenderThread : public Thread, protected Singleton<RenderThread> {
public:
// RenderThread takes complete ownership of tasks that are queued
// and will delete them after they are run
@@ -79,6 +85,8 @@ public:
void pushBackFrameCallback(IFrameCallback* callback);
TimeLord& timeLord() { return mTimeLord; }
+ RenderState& renderState() { return *mRenderState; }
+ EglManager& eglManager() { return *mEglManager; }
protected:
virtual bool threadLoop();
@@ -86,10 +94,12 @@ protected:
private:
friend class Singleton<RenderThread>;
friend class DispatchFrameCallbacks;
+ friend class RenderProxy;
RenderThread();
virtual ~RenderThread();
+ void initThreadLocals();
void initializeDisplayEventReceiver();
static int displayEventReceiverCallback(int fd, int events, void* data);
void drainDisplayEventQueue(bool skipCallbacks = false);
@@ -119,6 +129,8 @@ private:
DispatchFrameCallbacks* mFrameCallbackTask;
TimeLord mTimeLord;
+ RenderState* mRenderState;
+ EglManager* mEglManager;
};
} /* namespace renderthread */
diff --git a/libs/hwui/thread/Task.h b/libs/hwui/thread/Task.h
index 9a211a2..30b6ff2 100644
--- a/libs/hwui/thread/Task.h
+++ b/libs/hwui/thread/Task.h
@@ -17,8 +17,6 @@
#ifndef ANDROID_HWUI_TASK_H
#define ANDROID_HWUI_TASK_H
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
#include <utils/RefBase.h>
#include <utils/Trace.h>
@@ -40,7 +38,7 @@ public:
virtual ~Task() { }
T getResult() const {
- ATRACE_NAME("waitForTask");
+ ScopedTrace tracer(ATRACE_TAG_VIEW, "waitForTask");
return mFuture->get();
}
diff --git a/libs/hwui/utils/Blur.cpp b/libs/hwui/utils/Blur.cpp
index c020b40..877a422 100644
--- a/libs/hwui/utils/Blur.cpp
+++ b/libs/hwui/utils/Blur.cpp
@@ -19,6 +19,7 @@
#include <math.h>
#include "Blur.h"
+#include "MathUtils.h"
namespace android {
namespace uirenderer {
@@ -35,6 +36,17 @@ float Blur::convertSigmaToRadius(float sigma) {
return sigma > 0.5f ? (sigma - 0.5f) / BLUR_SIGMA_SCALE : 0.0f;
}
+// if the original radius was on an integer boundary and the resulting radius
+// is within the conversion error tolerance then we attempt to snap to the
+// original integer boundary.
+uint32_t Blur::convertRadiusToInt(float radius) {
+ const float radiusCeil = ceilf(radius);
+ if (MathUtils::areEqual(radiusCeil, radius)) {
+ return radiusCeil;
+ }
+ return radius;
+}
+
/**
* HWUI has used a slightly different equation than Skia to generate the value
* for sigma and to preserve compatibility we have kept that logic.
diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h
index 79aff65..b145333 100644
--- a/libs/hwui/utils/Blur.h
+++ b/libs/hwui/utils/Blur.h
@@ -27,8 +27,12 @@ class Blur {
public:
// If radius > 0, return the corresponding sigma, else return 0
ANDROID_API static float convertRadiusToSigma(float radius);
- // If sigma > 0.6, return the corresponding radius, else return 0
+ // If sigma > 0.5, return the corresponding radius, else return 0
ANDROID_API static float convertSigmaToRadius(float sigma);
+ // If the original radius was on an integer boundary then after the sigma to
+ // radius conversion a small rounding error may be introduced. This function
+ // accounts for that error and snaps to the appropriate integer boundary.
+ static uint32_t convertRadiusToInt(float radius);
static void generateGaussianWeights(float* weights, int32_t radius);
static void horizontal(float* weights, int32_t radius, const uint8_t* source,
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 3f6ccc9..063383b 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -206,9 +206,8 @@ void SpriteController::doUpdateSprites() {
} else {
SkBitmap surfaceBitmap;
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
- surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config,
- outBuffer.width, outBuffer.height, bpr);
- surfaceBitmap.setPixels(outBuffer.bits);
+ surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height),
+ outBuffer.bits, bpr);
SkCanvas surfaceCanvas(surfaceBitmap);