summaryrefslogtreecommitdiffstats
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/androidfw/Android.mk13
-rw-r--r--libs/androidfw/Asset.cpp4
-rw-r--r--libs/androidfw/AssetManager.cpp3
-rw-r--r--libs/androidfw/Keyboard.cpp1
-rw-r--r--libs/androidfw/ResourceTypes.cpp5
-rw-r--r--libs/androidfw/ZipFileCRO.cpp54
-rw-r--r--libs/androidfw/ZipFileRO.cpp931
-rw-r--r--libs/androidfw/ZipUtils.cpp345
-rw-r--r--libs/androidfw/misc.cpp83
-rw-r--r--libs/androidfw/tests/Android.mk3
-rw-r--r--libs/androidfw/tests/ZipFileRO_test.cpp64
-rw-r--r--libs/hwui/Android.mk21
-rw-r--r--libs/hwui/AssetAtlas.cpp137
-rw-r--r--libs/hwui/AssetAtlas.h173
-rw-r--r--libs/hwui/Caches.cpp128
-rw-r--r--libs/hwui/Caches.h52
-rw-r--r--libs/hwui/Debug.h2
-rw-r--r--libs/hwui/DeferredDisplayList.cpp13
-rw-r--r--libs/hwui/DisplayList.cpp4
-rw-r--r--libs/hwui/DisplayList.h7
-rw-r--r--libs/hwui/DisplayListOp.h118
-rw-r--r--libs/hwui/DisplayListRenderer.cpp24
-rw-r--r--libs/hwui/DisplayListRenderer.h6
-rw-r--r--libs/hwui/Dither.cpp13
-rw-r--r--libs/hwui/Dither.h7
-rw-r--r--libs/hwui/Extensions.cpp80
-rw-r--r--libs/hwui/Extensions.h26
-rw-r--r--libs/hwui/FontRenderer.cpp72
-rw-r--r--libs/hwui/FontRenderer.h5
-rw-r--r--libs/hwui/GradientCache.cpp6
-rw-r--r--libs/hwui/Image.cpp63
-rw-r--r--libs/hwui/Image.h67
-rw-r--r--libs/hwui/Layer.cpp59
-rw-r--r--libs/hwui/Layer.h46
-rw-r--r--libs/hwui/LayerRenderer.cpp6
-rw-r--r--libs/hwui/Matrix.h25
-rw-r--r--libs/hwui/OpenGLRenderer.cpp338
-rw-r--r--libs/hwui/OpenGLRenderer.h66
-rw-r--r--libs/hwui/Patch.cpp181
-rw-r--r--libs/hwui/Patch.h60
-rw-r--r--libs/hwui/PatchCache.cpp164
-rw-r--r--libs/hwui/PatchCache.h78
-rw-r--r--libs/hwui/PathCache.cpp6
-rw-r--r--libs/hwui/PathCache.h2
-rw-r--r--libs/hwui/PathTessellator.cpp145
-rw-r--r--libs/hwui/PathTessellator.h35
-rw-r--r--libs/hwui/PixelBuffer.cpp10
-rw-r--r--libs/hwui/Program.cpp35
-rw-r--r--libs/hwui/Program.h31
-rw-r--r--libs/hwui/ProgramCache.cpp53
-rw-r--r--libs/hwui/Properties.h13
-rw-r--r--libs/hwui/Rect.h4
-rw-r--r--libs/hwui/SkiaShader.cpp22
-rw-r--r--libs/hwui/SkiaShader.h19
-rw-r--r--libs/hwui/TextDropShadowCache.cpp9
-rw-r--r--libs/hwui/TextDropShadowCache.h4
-rw-r--r--libs/hwui/Texture.cpp84
-rw-r--r--libs/hwui/Texture.h93
-rw-r--r--libs/hwui/TextureCache.cpp8
-rw-r--r--libs/hwui/UvMapper.h133
-rw-r--r--libs/hwui/Vertex.h17
-rw-r--r--libs/hwui/font/CacheTexture.cpp10
-rw-r--r--libs/hwui/font/CacheTexture.h4
-rw-r--r--libs/hwui/thread/TaskManager.h2
64 files changed, 3378 insertions, 914 deletions
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk
index 3ed75a2..c06b144 100644
--- a/libs/androidfw/Android.mk
+++ b/libs/androidfw/Android.mk
@@ -22,9 +22,13 @@ commonUtilsSources:= \
Asset.cpp \
AssetDir.cpp \
AssetManager.cpp \
+ misc.cpp \
ObbFile.cpp \
ResourceTypes.cpp \
- StreamingZipInflater.cpp
+ StreamingZipInflater.cpp \
+ ZipFileCRO.cpp \
+ ZipFileRO.cpp \
+ ZipUtils.cpp
# formerly in libui
commonUiSources:= \
@@ -34,7 +38,6 @@ commonUiSources:= \
KeyCharacterMap.cpp \
KeyLayoutMap.cpp \
VelocityControl.cpp \
- VelocityTracker.cpp \
VirtualKeyMap.cpp
commonSources:= \
@@ -52,6 +55,8 @@ LOCAL_MODULE:= libandroidfw
LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
+
LOCAL_C_INCLUDES := \
external/zlib
@@ -68,7 +73,8 @@ LOCAL_SRC_FILES:= \
BackupData.cpp \
BackupHelpers.cpp \
CursorWindow.cpp \
- InputTransport.cpp
+ InputTransport.cpp \
+ VelocityTracker.cpp
LOCAL_SHARED_LIBRARIES := \
liblog \
@@ -92,6 +98,7 @@ include $(BUILD_SHARED_LIBRARY)
ifeq ($(TARGET_OS),linux)
include $(CLEAR_VARS)
+LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS
LOCAL_C_INCLUDES += \
external/skia/include/core \
external/zlib \
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index fd5b3e4..cb7628d 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -23,11 +23,11 @@
#include <androidfw/Asset.h>
#include <androidfw/StreamingZipInflater.h>
+#include <androidfw/ZipFileRO.h>
+#include <androidfw/ZipUtils.h>
#include <utils/Atomic.h>
#include <utils/FileMap.h>
#include <utils/Log.h>
-#include <utils/ZipFileRO.h>
-#include <utils/ZipUtils.h>
#include <utils/threads.h>
#include <assert.h>
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index b08c36b..6667daf 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -25,14 +25,15 @@
#include <androidfw/Asset.h>
#include <androidfw/AssetDir.h>
#include <androidfw/AssetManager.h>
+#include <androidfw/misc.h>
#include <androidfw/ResourceTypes.h>
+#include <androidfw/ZipFileRO.h>
#include <utils/Atomic.h>
#include <utils/Log.h>
#include <utils/String8.h>
#include <utils/String8.h>
#include <utils/threads.h>
#include <utils/Timers.h>
-#include <utils/ZipFileRO.h>
#ifdef HAVE_ANDROID_OS
#include <cutils/trace.h>
#endif
diff --git a/libs/androidfw/Keyboard.cpp b/libs/androidfw/Keyboard.cpp
index 4ddbeab..26a4e6a 100644
--- a/libs/androidfw/Keyboard.cpp
+++ b/libs/androidfw/Keyboard.cpp
@@ -27,7 +27,6 @@
#include <androidfw/InputDevice.h>
#include <utils/Errors.h>
#include <utils/Log.h>
-#include <cutils/properties.h>
namespace android {
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index a730065..e31246f 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -24,7 +24,6 @@
#include <utils/Log.h>
#include <utils/String16.h>
#include <utils/String8.h>
-#include <utils/TextOutput.h>
#include <stdlib.h>
#include <string.h>
@@ -5324,7 +5323,7 @@ bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes,
}
-#ifndef HAVE_ANDROID_OS
+#ifdef STATIC_ANDROIDFW_FOR_TOOLS
#define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string())
#define CHAR16_ARRAY_EQ(constant, var, len) \
@@ -5621,6 +5620,6 @@ void ResTable::print(bool inclValues) const
}
}
-#endif // HAVE_ANDROID_OS
+#endif // STATIC_ANDROIDFW_FOR_TOOLS
} // namespace android
diff --git a/libs/androidfw/ZipFileCRO.cpp b/libs/androidfw/ZipFileCRO.cpp
new file mode 100644
index 0000000..c8df845
--- /dev/null
+++ b/libs/androidfw/ZipFileCRO.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 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/ZipFileCRO.h>
+#include <androidfw/ZipFileRO.h>
+
+using namespace android;
+
+ZipFileCRO ZipFileXRO_open(const char* path) {
+ ZipFileRO* zip = new ZipFileRO();
+ if (zip->open(path) == NO_ERROR) {
+ return (ZipFileCRO)zip;
+ }
+ return NULL;
+}
+
+void ZipFileCRO_destroy(ZipFileCRO zipToken) {
+ ZipFileRO* zip = (ZipFileRO*)zipToken;
+ delete zip;
+}
+
+ZipEntryCRO ZipFileCRO_findEntryByName(ZipFileCRO zipToken,
+ const char* fileName) {
+ ZipFileRO* zip = (ZipFileRO*)zipToken;
+ return (ZipEntryCRO)zip->findEntryByName(fileName);
+}
+
+bool ZipFileCRO_getEntryInfo(ZipFileCRO zipToken, ZipEntryRO entryToken,
+ int* pMethod, size_t* pUncompLen,
+ size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) {
+ ZipFileRO* zip = (ZipFileRO*)zipToken;
+ ZipEntryRO entry = (ZipEntryRO)entryToken;
+ return zip->getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset,
+ pModWhen, pCrc32);
+}
+
+bool ZipFileCRO_uncompressEntry(ZipFileCRO zipToken, ZipEntryRO entryToken, int fd) {
+ ZipFileRO* zip = (ZipFileRO*)zipToken;
+ ZipEntryRO entry = (ZipEntryRO)entryToken;
+ return zip->uncompressEntry(entry, fd);
+}
diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp
new file mode 100644
index 0000000..b84f3b0
--- /dev/null
+++ b/libs/androidfw/ZipFileRO.cpp
@@ -0,0 +1,931 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+//
+// Read-only access to Zip archives, with minimal heap allocation.
+//
+#define LOG_TAG "zipro"
+//#define LOG_NDEBUG 0
+#include <androidfw/ZipFileRO.h>
+#include <utils/Log.h>
+#include <utils/Compat.h>
+#include <utils/misc.h>
+#include <utils/threads.h>
+
+#include <zlib.h>
+
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+
+/*
+ * We must open binary files using open(path, ... | O_BINARY) under Windows.
+ * Otherwise strange read errors will happen.
+ */
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+using namespace android;
+
+/*
+ * Zip file constants.
+ */
+#define kEOCDSignature 0x06054b50
+#define kEOCDLen 22
+#define kEOCDNumEntries 8 // offset to #of entries in file
+#define kEOCDSize 12 // size of the central directory
+#define kEOCDFileOffset 16 // offset to central directory
+
+#define kMaxCommentLen 65535 // longest possible in ushort
+#define kMaxEOCDSearch (kMaxCommentLen + kEOCDLen)
+
+#define kLFHSignature 0x04034b50
+#define kLFHLen 30 // excluding variable-len fields
+#define kLFHNameLen 26 // offset to filename length
+#define kLFHExtraLen 28 // offset to extra length
+
+#define kCDESignature 0x02014b50
+#define kCDELen 46 // excluding variable-len fields
+#define kCDEMethod 10 // offset to compression method
+#define kCDEModWhen 12 // offset to modification timestamp
+#define kCDECRC 16 // offset to entry CRC
+#define kCDECompLen 20 // offset to compressed length
+#define kCDEUncompLen 24 // offset to uncompressed length
+#define kCDENameLen 28 // offset to filename length
+#define kCDEExtraLen 30 // offset to extra length
+#define kCDECommentLen 32 // offset to comment length
+#define kCDELocalOffset 42 // offset to local hdr
+
+/*
+ * The values we return for ZipEntryRO use 0 as an invalid value, so we
+ * want to adjust the hash table index by a fixed amount. Using a large
+ * value helps insure that people don't mix & match arguments, e.g. to
+ * findEntryByIndex().
+ */
+#define kZipEntryAdj 10000
+
+ZipFileRO::~ZipFileRO() {
+ free(mHashTable);
+ if (mDirectoryMap)
+ mDirectoryMap->release();
+ if (mFd >= 0)
+ TEMP_FAILURE_RETRY(close(mFd));
+ if (mFileName)
+ free(mFileName);
+}
+
+/*
+ * Convert a ZipEntryRO to a hash table index, verifying that it's in a
+ * valid range.
+ */
+int ZipFileRO::entryToIndex(const ZipEntryRO entry) const
+{
+ long ent = ((intptr_t) entry) - kZipEntryAdj;
+ if (ent < 0 || ent >= mHashTableSize || mHashTable[ent].name == NULL) {
+ ALOGW("Invalid ZipEntryRO %p (%ld)\n", entry, ent);
+ return -1;
+ }
+ return ent;
+}
+
+
+/*
+ * Open the specified file read-only. We memory-map the entire thing and
+ * close the file before returning.
+ */
+status_t ZipFileRO::open(const char* zipFileName)
+{
+ int fd = -1;
+
+ assert(mDirectoryMap == NULL);
+
+ /*
+ * Open and map the specified file.
+ */
+ fd = TEMP_FAILURE_RETRY(::open(zipFileName, O_RDONLY | O_BINARY));
+ if (fd < 0) {
+ ALOGW("Unable to open zip '%s': %s\n", zipFileName, strerror(errno));
+ return NAME_NOT_FOUND;
+ }
+
+ mFileLength = lseek64(fd, 0, SEEK_END);
+ if (mFileLength < kEOCDLen) {
+ TEMP_FAILURE_RETRY(close(fd));
+ return UNKNOWN_ERROR;
+ }
+
+ if (mFileName != NULL) {
+ free(mFileName);
+ }
+ mFileName = strdup(zipFileName);
+
+ mFd = fd;
+
+ /*
+ * Find the Central Directory and store its size and number of entries.
+ */
+ if (!mapCentralDirectory()) {
+ goto bail;
+ }
+
+ /*
+ * Verify Central Directory and create data structures for fast access.
+ */
+ if (!parseZipArchive()) {
+ goto bail;
+ }
+
+ return OK;
+
+bail:
+ free(mFileName);
+ mFileName = NULL;
+ TEMP_FAILURE_RETRY(close(fd));
+ return UNKNOWN_ERROR;
+}
+
+/*
+ * Parse the Zip archive, verifying its contents and initializing internal
+ * data structures.
+ */
+bool ZipFileRO::mapCentralDirectory(void)
+{
+ ssize_t readAmount = kMaxEOCDSearch;
+ if (readAmount > (ssize_t) mFileLength)
+ readAmount = mFileLength;
+
+ unsigned char* scanBuf = (unsigned char*) malloc(readAmount);
+ if (scanBuf == NULL) {
+ ALOGW("couldn't allocate scanBuf: %s", strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+
+ /*
+ * Make sure this is a Zip archive.
+ */
+ if (lseek64(mFd, 0, SEEK_SET) != 0) {
+ ALOGW("seek to start failed: %s", strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+
+ ssize_t actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, sizeof(int32_t)));
+ if (actual != (ssize_t) sizeof(int32_t)) {
+ ALOGI("couldn't read first signature from zip archive: %s", strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+
+ {
+ unsigned int header = get4LE(scanBuf);
+ if (header == kEOCDSignature) {
+ ALOGI("Found Zip archive, but it looks empty\n");
+ free(scanBuf);
+ return false;
+ } else if (header != kLFHSignature) {
+ ALOGV("Not a Zip archive (found 0x%08x)\n", header);
+ free(scanBuf);
+ return false;
+ }
+ }
+
+ /*
+ * Perform the traditional EOCD snipe hunt.
+ *
+ * We're searching for the End of Central Directory magic number,
+ * which appears at the start of the EOCD block. It's followed by
+ * 18 bytes of EOCD stuff and up to 64KB of archive comment. We
+ * need to read the last part of the file into a buffer, dig through
+ * it to find the magic number, parse some values out, and use those
+ * to determine the extent of the CD.
+ *
+ * We start by pulling in the last part of the file.
+ */
+ off64_t searchStart = mFileLength - readAmount;
+
+ if (lseek64(mFd, searchStart, SEEK_SET) != searchStart) {
+ ALOGW("seek %ld failed: %s\n", (long) searchStart, strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+ actual = TEMP_FAILURE_RETRY(read(mFd, scanBuf, readAmount));
+ if (actual != (ssize_t) readAmount) {
+ ALOGW("Zip: read " ZD ", expected " ZD ". Failed: %s\n",
+ (ZD_TYPE) actual, (ZD_TYPE) readAmount, strerror(errno));
+ free(scanBuf);
+ return false;
+ }
+
+ /*
+ * Scan backward for the EOCD magic. In an archive without a trailing
+ * comment, we'll find it on the first try. (We may want to consider
+ * doing an initial minimal read; if we don't find it, retry with a
+ * second read as above.)
+ */
+ int i;
+ for (i = readAmount - kEOCDLen; i >= 0; i--) {
+ if (scanBuf[i] == 0x50 && get4LE(&scanBuf[i]) == kEOCDSignature) {
+ ALOGV("+++ Found EOCD at buf+%d\n", i);
+ break;
+ }
+ }
+ if (i < 0) {
+ ALOGD("Zip: EOCD not found, %s is not zip\n", mFileName);
+ free(scanBuf);
+ return false;
+ }
+
+ off64_t eocdOffset = searchStart + i;
+ const unsigned char* eocdPtr = scanBuf + i;
+
+ assert(eocdOffset < mFileLength);
+
+ /*
+ * Grab the CD offset and size, and the number of entries in the
+ * archive. After that, we can release our EOCD hunt buffer.
+ */
+ unsigned int numEntries = get2LE(eocdPtr + kEOCDNumEntries);
+ unsigned int dirSize = get4LE(eocdPtr + kEOCDSize);
+ unsigned int dirOffset = get4LE(eocdPtr + kEOCDFileOffset);
+ free(scanBuf);
+
+ // Verify that they look reasonable.
+ if ((long long) dirOffset + (long long) dirSize > (long long) eocdOffset) {
+ ALOGW("bad offsets (dir %ld, size %u, eocd %ld)\n",
+ (long) dirOffset, dirSize, (long) eocdOffset);
+ return false;
+ }
+ if (numEntries == 0) {
+ ALOGW("empty archive?\n");
+ return false;
+ }
+
+ ALOGV("+++ numEntries=%d dirSize=%d dirOffset=%d\n",
+ numEntries, dirSize, dirOffset);
+
+ mDirectoryMap = new FileMap();
+ if (mDirectoryMap == NULL) {
+ ALOGW("Unable to create directory map: %s", strerror(errno));
+ return false;
+ }
+
+ if (!mDirectoryMap->create(mFileName, mFd, dirOffset, dirSize, true)) {
+ ALOGW("Unable to map '%s' (" ZD " to " ZD "): %s\n", mFileName,
+ (ZD_TYPE) dirOffset, (ZD_TYPE) (dirOffset + dirSize), strerror(errno));
+ return false;
+ }
+
+ mNumEntries = numEntries;
+ mDirectoryOffset = dirOffset;
+
+ return true;
+}
+
+
+/*
+ * Round up to the next highest power of 2.
+ *
+ * Found on http://graphics.stanford.edu/~seander/bithacks.html.
+ */
+static unsigned int roundUpPower2(unsigned int val)
+{
+ val--;
+ val |= val >> 1;
+ val |= val >> 2;
+ val |= val >> 4;
+ val |= val >> 8;
+ val |= val >> 16;
+ val++;
+
+ return val;
+}
+
+bool ZipFileRO::parseZipArchive(void)
+{
+ bool result = false;
+ const unsigned char* cdPtr = (const unsigned char*) mDirectoryMap->getDataPtr();
+ size_t cdLength = mDirectoryMap->getDataLength();
+ int numEntries = mNumEntries;
+
+ /*
+ * Create hash table. We have a minimum 75% load factor, possibly as
+ * low as 50% after we round off to a power of 2.
+ */
+ mHashTableSize = roundUpPower2(1 + (numEntries * 4) / 3);
+ mHashTable = (HashEntry*) calloc(mHashTableSize, sizeof(HashEntry));
+
+ /*
+ * Walk through the central directory, adding entries to the hash
+ * table.
+ */
+ const unsigned char* ptr = cdPtr;
+ for (int i = 0; i < numEntries; i++) {
+ if (get4LE(ptr) != kCDESignature) {
+ ALOGW("Missed a central dir sig (at %d)\n", i);
+ goto bail;
+ }
+ if (ptr + kCDELen > cdPtr + cdLength) {
+ ALOGW("Ran off the end (at %d)\n", i);
+ goto bail;
+ }
+
+ long localHdrOffset = (long) get4LE(ptr + kCDELocalOffset);
+ if (localHdrOffset >= mDirectoryOffset) {
+ ALOGW("bad LFH offset %ld at entry %d\n", localHdrOffset, i);
+ goto bail;
+ }
+
+ unsigned int fileNameLen, extraLen, commentLen, hash;
+
+ fileNameLen = get2LE(ptr + kCDENameLen);
+ extraLen = get2LE(ptr + kCDEExtraLen);
+ commentLen = get2LE(ptr + kCDECommentLen);
+
+ /* add the CDE filename to the hash table */
+ hash = computeHash((const char*)ptr + kCDELen, fileNameLen);
+ addToHash((const char*)ptr + kCDELen, fileNameLen, hash);
+
+ ptr += kCDELen + fileNameLen + extraLen + commentLen;
+ if ((size_t)(ptr - cdPtr) > cdLength) {
+ ALOGW("bad CD advance (%d vs " ZD ") at entry %d\n",
+ (int) (ptr - cdPtr), (ZD_TYPE) cdLength, i);
+ goto bail;
+ }
+ }
+ ALOGV("+++ zip good scan %d entries\n", numEntries);
+ result = true;
+
+bail:
+ return result;
+}
+
+/*
+ * Simple string hash function for non-null-terminated strings.
+ */
+/*static*/ unsigned int ZipFileRO::computeHash(const char* str, int len)
+{
+ unsigned int hash = 0;
+
+ while (len--)
+ hash = hash * 31 + *str++;
+
+ return hash;
+}
+
+/*
+ * Add a new entry to the hash table.
+ */
+void ZipFileRO::addToHash(const char* str, int strLen, unsigned int hash)
+{
+ int ent = hash & (mHashTableSize-1);
+
+ /*
+ * We over-allocate the table, so we're guaranteed to find an empty slot.
+ */
+ while (mHashTable[ent].name != NULL)
+ ent = (ent + 1) & (mHashTableSize-1);
+
+ mHashTable[ent].name = str;
+ mHashTable[ent].nameLen = strLen;
+}
+
+/*
+ * Find a matching entry.
+ *
+ * Returns NULL if not found.
+ */
+ZipEntryRO ZipFileRO::findEntryByName(const char* fileName) const
+{
+ /*
+ * If the ZipFileRO instance is not initialized, the entry number will
+ * end up being garbage since mHashTableSize is -1.
+ */
+ if (mHashTableSize <= 0) {
+ return NULL;
+ }
+
+ int nameLen = strlen(fileName);
+ unsigned int hash = computeHash(fileName, nameLen);
+ int ent = hash & (mHashTableSize-1);
+
+ while (mHashTable[ent].name != NULL) {
+ if (mHashTable[ent].nameLen == nameLen &&
+ memcmp(mHashTable[ent].name, fileName, nameLen) == 0)
+ {
+ /* match */
+ return (ZipEntryRO)(long)(ent + kZipEntryAdj);
+ }
+
+ ent = (ent + 1) & (mHashTableSize-1);
+ }
+
+ return NULL;
+}
+
+/*
+ * Find the Nth entry.
+ *
+ * This currently involves walking through the sparse hash table, counting
+ * non-empty entries. If we need to speed this up we can either allocate
+ * a parallel lookup table or (perhaps better) provide an iterator interface.
+ */
+ZipEntryRO ZipFileRO::findEntryByIndex(int idx) const
+{
+ if (idx < 0 || idx >= mNumEntries) {
+ ALOGW("Invalid index %d\n", idx);
+ return NULL;
+ }
+
+ for (int ent = 0; ent < mHashTableSize; ent++) {
+ if (mHashTable[ent].name != NULL) {
+ if (idx-- == 0)
+ return (ZipEntryRO) (intptr_t)(ent + kZipEntryAdj);
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Get the useful fields from the zip entry.
+ *
+ * Returns "false" if the offsets to the fields or the contents of the fields
+ * appear to be bogus.
+ */
+bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen,
+ size_t* pCompLen, off64_t* pOffset, long* pModWhen, long* pCrc32) const
+{
+ bool ret = false;
+
+ const int ent = entryToIndex(entry);
+ if (ent < 0)
+ return false;
+
+ HashEntry hashEntry = mHashTable[ent];
+
+ /*
+ * Recover the start of the central directory entry from the filename
+ * pointer. The filename is the first entry past the fixed-size data,
+ * so we can just subtract back from that.
+ */
+ const unsigned char* ptr = (const unsigned char*) hashEntry.name;
+ off64_t cdOffset = mDirectoryOffset;
+
+ ptr -= kCDELen;
+
+ int method = get2LE(ptr + kCDEMethod);
+ if (pMethod != NULL)
+ *pMethod = method;
+
+ if (pModWhen != NULL)
+ *pModWhen = get4LE(ptr + kCDEModWhen);
+ if (pCrc32 != NULL)
+ *pCrc32 = get4LE(ptr + kCDECRC);
+
+ size_t compLen = get4LE(ptr + kCDECompLen);
+ if (pCompLen != NULL)
+ *pCompLen = compLen;
+ size_t uncompLen = get4LE(ptr + kCDEUncompLen);
+ if (pUncompLen != NULL)
+ *pUncompLen = uncompLen;
+
+ /*
+ * If requested, determine the offset of the start of the data. All we
+ * have is the offset to the Local File Header, which is variable size,
+ * so we have to read the contents of the struct to figure out where
+ * the actual data starts.
+ *
+ * We also need to make sure that the lengths are not so large that
+ * somebody trying to map the compressed or uncompressed data runs
+ * off the end of the mapped region.
+ *
+ * Note we don't verify compLen/uncompLen if they don't request the
+ * dataOffset, because dataOffset is expensive to determine. However,
+ * if they don't have the file offset, they're not likely to be doing
+ * anything with the contents.
+ */
+ if (pOffset != NULL) {
+ long localHdrOffset = get4LE(ptr + kCDELocalOffset);
+ if (localHdrOffset + kLFHLen >= cdOffset) {
+ ALOGE("ERROR: bad local hdr offset in zip\n");
+ return false;
+ }
+
+ unsigned char lfhBuf[kLFHLen];
+
+#ifdef HAVE_PREAD
+ /*
+ * This file descriptor might be from zygote's preloaded assets,
+ * so we need to do an pread64() instead of a lseek64() + read() to
+ * guarantee atomicity across the processes with the shared file
+ * descriptors.
+ */
+ ssize_t actual =
+ TEMP_FAILURE_RETRY(pread64(mFd, lfhBuf, sizeof(lfhBuf), localHdrOffset));
+
+ if (actual != sizeof(lfhBuf)) {
+ ALOGW("failed reading lfh from offset %ld\n", localHdrOffset);
+ return false;
+ }
+
+ if (get4LE(lfhBuf) != kLFHSignature) {
+ ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; "
+ "got: data=0x%08lx\n",
+ localHdrOffset, kLFHSignature, get4LE(lfhBuf));
+ return false;
+ }
+#else /* HAVE_PREAD */
+ /*
+ * For hosts don't have pread64() we cannot guarantee atomic reads from
+ * an offset in a file. Android should never run on those platforms.
+ * File descriptors inherited from a fork() share file offsets and
+ * there would be nothing to protect from two different processes
+ * calling lseek64() concurrently.
+ */
+
+ {
+ AutoMutex _l(mFdLock);
+
+ if (lseek64(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) {
+ ALOGW("failed seeking to lfh at offset %ld\n", localHdrOffset);
+ return false;
+ }
+
+ ssize_t actual =
+ TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf)));
+ if (actual != sizeof(lfhBuf)) {
+ ALOGW("failed reading lfh from offset %ld\n", localHdrOffset);
+ return false;
+ }
+
+ if (get4LE(lfhBuf) != kLFHSignature) {
+ off64_t actualOffset = lseek64(mFd, 0, SEEK_CUR);
+ ALOGW("didn't find signature at start of lfh; wanted: offset=%ld data=0x%08x; "
+ "got: offset=" ZD " data=0x%08lx\n",
+ localHdrOffset, kLFHSignature, (ZD_TYPE) actualOffset, get4LE(lfhBuf));
+ return false;
+ }
+ }
+#endif /* HAVE_PREAD */
+
+ off64_t dataOffset = localHdrOffset + kLFHLen
+ + get2LE(lfhBuf + kLFHNameLen) + get2LE(lfhBuf + kLFHExtraLen);
+ if (dataOffset >= cdOffset) {
+ ALOGW("bad data offset %ld in zip\n", (long) dataOffset);
+ return false;
+ }
+
+ /* check lengths */
+ if ((off64_t)(dataOffset + compLen) > cdOffset) {
+ ALOGW("bad compressed length in zip (%ld + " ZD " > %ld)\n",
+ (long) dataOffset, (ZD_TYPE) compLen, (long) cdOffset);
+ return false;
+ }
+
+ if (method == kCompressStored &&
+ (off64_t)(dataOffset + uncompLen) > cdOffset)
+ {
+ ALOGE("ERROR: bad uncompressed length in zip (%ld + " ZD " > %ld)\n",
+ (long) dataOffset, (ZD_TYPE) uncompLen, (long) cdOffset);
+ return false;
+ }
+
+ *pOffset = dataOffset;
+ }
+
+ return true;
+}
+
+/*
+ * Copy the entry's filename to the buffer.
+ */
+int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen)
+ const
+{
+ int ent = entryToIndex(entry);
+ if (ent < 0)
+ return -1;
+
+ int nameLen = mHashTable[ent].nameLen;
+ if (bufLen < nameLen+1)
+ return nameLen+1;
+
+ memcpy(buffer, mHashTable[ent].name, nameLen);
+ buffer[nameLen] = '\0';
+ return 0;
+}
+
+/*
+ * Create a new FileMap object that spans the data in "entry".
+ */
+FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const
+{
+ /*
+ * TODO: the efficient way to do this is to modify FileMap to allow
+ * sub-regions of a file to be mapped. A reference-counting scheme
+ * can manage the base memory mapping. For now, we just create a brand
+ * new mapping off of the Zip archive file descriptor.
+ */
+
+ FileMap* newMap;
+ size_t compLen;
+ off64_t offset;
+
+ if (!getEntryInfo(entry, NULL, NULL, &compLen, &offset, NULL, NULL))
+ return NULL;
+
+ newMap = new FileMap();
+ if (!newMap->create(mFileName, mFd, offset, compLen, true)) {
+ newMap->release();
+ return NULL;
+ }
+
+ return newMap;
+}
+
+/*
+ * Uncompress an entry, in its entirety, into the provided output buffer.
+ *
+ * This doesn't verify the data's CRC, which might be useful for
+ * uncompressed data. The caller should be able to manage it.
+ */
+bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const
+{
+ const size_t kSequentialMin = 32768;
+ bool result = false;
+ int ent = entryToIndex(entry);
+ if (ent < 0)
+ return -1;
+
+ int method;
+ size_t uncompLen, compLen;
+ off64_t offset;
+ const unsigned char* ptr;
+
+ getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL);
+
+ FileMap* file = createEntryFileMap(entry);
+ if (file == NULL) {
+ goto bail;
+ }
+
+ ptr = (const unsigned char*) file->getDataPtr();
+
+ /*
+ * Experiment with madvise hint. When we want to uncompress a file,
+ * we pull some stuff out of the central dir entry and then hit a
+ * bunch of compressed or uncompressed data sequentially. The CDE
+ * visit will cause a limited amount of read-ahead because it's at
+ * the end of the file. We could end up doing lots of extra disk
+ * access if the file we're prying open is small. Bottom line is we
+ * probably don't want to turn MADV_SEQUENTIAL on and leave it on.
+ *
+ * So, if the compressed size of the file is above a certain minimum
+ * size, temporarily boost the read-ahead in the hope that the extra
+ * pair of system calls are negated by a reduction in page faults.
+ */
+ if (compLen > kSequentialMin)
+ file->advise(FileMap::SEQUENTIAL);
+
+ if (method == kCompressStored) {
+ memcpy(buffer, ptr, uncompLen);
+ } else {
+ if (!inflateBuffer(buffer, ptr, uncompLen, compLen))
+ goto unmap;
+ }
+
+ if (compLen > kSequentialMin)
+ file->advise(FileMap::NORMAL);
+
+ result = true;
+
+unmap:
+ file->release();
+bail:
+ return result;
+}
+
+/*
+ * Uncompress an entry, in its entirety, to an open file descriptor.
+ *
+ * This doesn't verify the data's CRC, but probably should.
+ */
+bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const
+{
+ bool result = false;
+ int ent = entryToIndex(entry);
+ if (ent < 0)
+ return -1;
+
+ int method;
+ size_t uncompLen, compLen;
+ off64_t offset;
+ const unsigned char* ptr;
+
+ getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL);
+
+ FileMap* file = createEntryFileMap(entry);
+ if (file == NULL) {
+ goto bail;
+ }
+
+ ptr = (const unsigned char*) file->getDataPtr();
+
+ if (method == kCompressStored) {
+ ssize_t actual = TEMP_FAILURE_RETRY(write(fd, ptr, uncompLen));
+ if (actual < 0) {
+ ALOGE("Write failed: %s\n", strerror(errno));
+ goto unmap;
+ } else if ((size_t) actual != uncompLen) {
+ ALOGE("Partial write during uncompress (" ZD " of " ZD ")\n",
+ (ZD_TYPE) actual, (ZD_TYPE) uncompLen);
+ goto unmap;
+ } else {
+ ALOGI("+++ successful write\n");
+ }
+ } else {
+ if (!inflateBuffer(fd, ptr, uncompLen, compLen))
+ goto unmap;
+ }
+
+ result = true;
+
+unmap:
+ file->release();
+bail:
+ return result;
+}
+
+/*
+ * Uncompress "deflate" data from one buffer to another.
+ */
+/*static*/ bool ZipFileRO::inflateBuffer(void* outBuf, const void* inBuf,
+ size_t uncompLen, size_t compLen)
+{
+ bool result = false;
+ z_stream zstream;
+ int zerr;
+
+ /*
+ * Initialize the zlib stream struct.
+ */
+ memset(&zstream, 0, sizeof(zstream));
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.opaque = Z_NULL;
+ zstream.next_in = (Bytef*)inBuf;
+ zstream.avail_in = compLen;
+ zstream.next_out = (Bytef*) outBuf;
+ zstream.avail_out = uncompLen;
+ zstream.data_type = Z_UNKNOWN;
+
+ /*
+ * Use the undocumented "negative window bits" feature to tell zlib
+ * that there's no zlib header waiting for it.
+ */
+ zerr = inflateInit2(&zstream, -MAX_WBITS);
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ ALOGE("Installed zlib is not compatible with linked version (%s)\n",
+ ZLIB_VERSION);
+ } else {
+ ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
+ }
+ goto bail;
+ }
+
+ /*
+ * Expand data.
+ */
+ zerr = inflate(&zstream, Z_FINISH);
+ if (zerr != Z_STREAM_END) {
+ ALOGW("Zip inflate failed, zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n",
+ zerr, zstream.next_in, zstream.avail_in,
+ zstream.next_out, zstream.avail_out);
+ goto z_bail;
+ }
+
+ /* paranoia */
+ if (zstream.total_out != uncompLen) {
+ ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n",
+ zstream.total_out, (ZD_TYPE) uncompLen);
+ goto z_bail;
+ }
+
+ result = true;
+
+z_bail:
+ inflateEnd(&zstream); /* free up any allocated structures */
+
+bail:
+ return result;
+}
+
+/*
+ * Uncompress "deflate" data from one buffer to an open file descriptor.
+ */
+/*static*/ bool ZipFileRO::inflateBuffer(int fd, const void* inBuf,
+ size_t uncompLen, size_t compLen)
+{
+ bool result = false;
+ const size_t kWriteBufSize = 32768;
+ unsigned char writeBuf[kWriteBufSize];
+ z_stream zstream;
+ int zerr;
+
+ /*
+ * Initialize the zlib stream struct.
+ */
+ memset(&zstream, 0, sizeof(zstream));
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.opaque = Z_NULL;
+ zstream.next_in = (Bytef*)inBuf;
+ zstream.avail_in = compLen;
+ zstream.next_out = (Bytef*) writeBuf;
+ zstream.avail_out = sizeof(writeBuf);
+ zstream.data_type = Z_UNKNOWN;
+
+ /*
+ * Use the undocumented "negative window bits" feature to tell zlib
+ * that there's no zlib header waiting for it.
+ */
+ zerr = inflateInit2(&zstream, -MAX_WBITS);
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ ALOGE("Installed zlib is not compatible with linked version (%s)\n",
+ ZLIB_VERSION);
+ } else {
+ ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
+ }
+ goto bail;
+ }
+
+ /*
+ * Loop while we have more to do.
+ */
+ do {
+ /*
+ * Expand data.
+ */
+ zerr = inflate(&zstream, Z_NO_FLUSH);
+ if (zerr != Z_OK && zerr != Z_STREAM_END) {
+ ALOGW("zlib inflate: zerr=%d (nIn=%p aIn=%u nOut=%p aOut=%u)\n",
+ zerr, zstream.next_in, zstream.avail_in,
+ zstream.next_out, zstream.avail_out);
+ goto z_bail;
+ }
+
+ /* write when we're full or when we're done */
+ if (zstream.avail_out == 0 ||
+ (zerr == Z_STREAM_END && zstream.avail_out != sizeof(writeBuf)))
+ {
+ long writeSize = zstream.next_out - writeBuf;
+ int cc = TEMP_FAILURE_RETRY(write(fd, writeBuf, writeSize));
+ if (cc < 0) {
+ ALOGW("write failed in inflate: %s", strerror(errno));
+ goto z_bail;
+ } else if (cc != (int) writeSize) {
+ ALOGW("write failed in inflate (%d vs %ld)", cc, writeSize);
+ goto z_bail;
+ }
+
+ zstream.next_out = writeBuf;
+ zstream.avail_out = sizeof(writeBuf);
+ }
+ } while (zerr == Z_OK);
+
+ assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+
+ /* paranoia */
+ if (zstream.total_out != uncompLen) {
+ ALOGW("Size mismatch on inflated file (%ld vs " ZD ")\n",
+ zstream.total_out, (ZD_TYPE) uncompLen);
+ goto z_bail;
+ }
+
+ result = true;
+
+z_bail:
+ inflateEnd(&zstream); /* free up any allocated structures */
+
+bail:
+ return result;
+}
diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
new file mode 100644
index 0000000..997eb7d
--- /dev/null
+++ b/libs/androidfw/ZipUtils.cpp
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2007 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.
+ */
+
+//
+// Misc zip/gzip utility functions.
+//
+
+#define LOG_TAG "ziputil"
+
+#include <androidfw/ZipUtils.h>
+#include <androidfw/ZipFileRO.h>
+#include <utils/Log.h>
+#include <utils/Compat.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include <zlib.h>
+
+using namespace android;
+
+/*
+ * Utility function that expands zip/gzip "deflate" compressed data
+ * into a buffer.
+ *
+ * "fd" is an open file positioned at the start of the "deflate" data
+ * "buf" must hold at least "uncompressedLen" bytes.
+ */
+/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf,
+ long uncompressedLen, long compressedLen)
+{
+ bool result = false;
+ const unsigned long kReadBufSize = 32768;
+ unsigned char* readBuf = NULL;
+ z_stream zstream;
+ int zerr;
+ unsigned long compRemaining;
+
+ assert(uncompressedLen >= 0);
+ assert(compressedLen >= 0);
+
+ readBuf = new unsigned char[kReadBufSize];
+ if (readBuf == NULL)
+ goto bail;
+ compRemaining = compressedLen;
+
+ /*
+ * Initialize the zlib stream.
+ */
+ memset(&zstream, 0, sizeof(zstream));
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.opaque = Z_NULL;
+ zstream.next_in = NULL;
+ zstream.avail_in = 0;
+ zstream.next_out = (Bytef*) buf;
+ zstream.avail_out = uncompressedLen;
+ zstream.data_type = Z_UNKNOWN;
+
+ /*
+ * Use the undocumented "negative window bits" feature to tell zlib
+ * that there's no zlib header waiting for it.
+ */
+ zerr = inflateInit2(&zstream, -MAX_WBITS);
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ ALOGE("Installed zlib is not compatible with linked version (%s)\n",
+ ZLIB_VERSION);
+ } else {
+ ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
+ }
+ goto bail;
+ }
+
+ /*
+ * Loop while we have data.
+ */
+ do {
+ unsigned long getSize;
+
+ /* read as much as we can */
+ if (zstream.avail_in == 0) {
+ getSize = (compRemaining > kReadBufSize) ?
+ kReadBufSize : compRemaining;
+ ALOGV("+++ reading %ld bytes (%ld left)\n",
+ getSize, compRemaining);
+
+ int cc = TEMP_FAILURE_RETRY(read(fd, readBuf, getSize));
+ if (cc < 0) {
+ ALOGW("inflate read failed: %s", strerror(errno));
+ } else if (cc != (int) getSize) {
+ ALOGW("inflate read failed (%d vs %ld)", cc, getSize);
+ goto z_bail;
+ }
+
+ compRemaining -= getSize;
+
+ zstream.next_in = readBuf;
+ zstream.avail_in = getSize;
+ }
+
+ /* uncompress the data */
+ zerr = inflate(&zstream, Z_NO_FLUSH);
+ if (zerr != Z_OK && zerr != Z_STREAM_END) {
+ ALOGD("zlib inflate call failed (zerr=%d)\n", zerr);
+ goto z_bail;
+ }
+
+ /* output buffer holds all, so no need to write the output */
+ } while (zerr == Z_OK);
+
+ assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+
+ if ((long) zstream.total_out != uncompressedLen) {
+ ALOGW("Size mismatch on inflated file (%ld vs %ld)\n",
+ zstream.total_out, uncompressedLen);
+ goto z_bail;
+ }
+
+ // success!
+ result = true;
+
+z_bail:
+ inflateEnd(&zstream); /* free up any allocated structures */
+
+bail:
+ delete[] readBuf;
+ return result;
+}
+
+/*
+ * Utility function that expands zip/gzip "deflate" compressed data
+ * into a buffer.
+ *
+ * (This is a clone of the previous function, but it takes a FILE* instead
+ * of an fd. We could pass fileno(fd) to the above, but we can run into
+ * trouble when "fp" has a different notion of what fd's file position is.)
+ *
+ * "fp" is an open file positioned at the start of the "deflate" data
+ * "buf" must hold at least "uncompressedLen" bytes.
+ */
+/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf,
+ long uncompressedLen, long compressedLen)
+{
+ bool result = false;
+ const unsigned long kReadBufSize = 32768;
+ unsigned char* readBuf = NULL;
+ z_stream zstream;
+ int zerr;
+ unsigned long compRemaining;
+
+ assert(uncompressedLen >= 0);
+ assert(compressedLen >= 0);
+
+ readBuf = new unsigned char[kReadBufSize];
+ if (readBuf == NULL)
+ goto bail;
+ compRemaining = compressedLen;
+
+ /*
+ * Initialize the zlib stream.
+ */
+ memset(&zstream, 0, sizeof(zstream));
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.opaque = Z_NULL;
+ zstream.next_in = NULL;
+ zstream.avail_in = 0;
+ zstream.next_out = (Bytef*) buf;
+ zstream.avail_out = uncompressedLen;
+ zstream.data_type = Z_UNKNOWN;
+
+ /*
+ * Use the undocumented "negative window bits" feature to tell zlib
+ * that there's no zlib header waiting for it.
+ */
+ zerr = inflateInit2(&zstream, -MAX_WBITS);
+ if (zerr != Z_OK) {
+ if (zerr == Z_VERSION_ERROR) {
+ ALOGE("Installed zlib is not compatible with linked version (%s)\n",
+ ZLIB_VERSION);
+ } else {
+ ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
+ }
+ goto bail;
+ }
+
+ /*
+ * Loop while we have data.
+ */
+ do {
+ unsigned long getSize;
+
+ /* read as much as we can */
+ if (zstream.avail_in == 0) {
+ getSize = (compRemaining > kReadBufSize) ?
+ kReadBufSize : compRemaining;
+ ALOGV("+++ reading %ld bytes (%ld left)\n",
+ getSize, compRemaining);
+
+ int cc = fread(readBuf, 1, getSize, fp);
+ if (cc != (int) getSize) {
+ ALOGD("inflate read failed (%d vs %ld)\n",
+ cc, getSize);
+ goto z_bail;
+ }
+
+ compRemaining -= getSize;
+
+ zstream.next_in = readBuf;
+ zstream.avail_in = getSize;
+ }
+
+ /* uncompress the data */
+ zerr = inflate(&zstream, Z_NO_FLUSH);
+ if (zerr != Z_OK && zerr != Z_STREAM_END) {
+ ALOGD("zlib inflate call failed (zerr=%d)\n", zerr);
+ goto z_bail;
+ }
+
+ /* output buffer holds all, so no need to write the output */
+ } while (zerr == Z_OK);
+
+ assert(zerr == Z_STREAM_END); /* other errors should've been caught */
+
+ if ((long) zstream.total_out != uncompressedLen) {
+ ALOGW("Size mismatch on inflated file (%ld vs %ld)\n",
+ zstream.total_out, uncompressedLen);
+ goto z_bail;
+ }
+
+ // success!
+ result = true;
+
+z_bail:
+ inflateEnd(&zstream); /* free up any allocated structures */
+
+bail:
+ delete[] readBuf;
+ return result;
+}
+
+/*
+ * Look at the contents of a gzip archive. We want to know where the
+ * data starts, and how long it will be after it is uncompressed.
+ *
+ * We expect to find the CRC and length as the last 8 bytes on the file.
+ * This is a pretty reasonable thing to expect for locally-compressed
+ * files, but there's a small chance that some extra padding got thrown
+ * on (the man page talks about compressed data written to tape). We
+ * don't currently deal with that here. If "gzip -l" whines, we're going
+ * to fail too.
+ *
+ * On exit, "fp" is pointing at the start of the compressed data.
+ */
+/*static*/ bool ZipUtils::examineGzip(FILE* fp, int* pCompressionMethod,
+ long* pUncompressedLen, long* pCompressedLen, unsigned long* pCRC32)
+{
+ enum { // flags
+ FTEXT = 0x01,
+ FHCRC = 0x02,
+ FEXTRA = 0x04,
+ FNAME = 0x08,
+ FCOMMENT = 0x10,
+ };
+ int ic;
+ int method, flags;
+ int i;
+
+ ic = getc(fp);
+ if (ic != 0x1f || getc(fp) != 0x8b)
+ return false; // not gzip
+ method = getc(fp);
+ flags = getc(fp);
+
+ /* quick sanity checks */
+ if (method == EOF || flags == EOF)
+ return false;
+ if (method != ZipFileRO::kCompressDeflated)
+ return false;
+
+ /* skip over 4 bytes of mod time, 1 byte XFL, 1 byte OS */
+ for (i = 0; i < 6; i++)
+ (void) getc(fp);
+ /* consume "extra" field, if present */
+ if ((flags & FEXTRA) != 0) {
+ int len;
+
+ len = getc(fp);
+ len |= getc(fp) << 8;
+ while (len-- && getc(fp) != EOF)
+ ;
+ }
+ /* consume filename, if present */
+ if ((flags & FNAME) != 0) {
+ do {
+ ic = getc(fp);
+ } while (ic != 0 && ic != EOF);
+ }
+ /* consume comment, if present */
+ if ((flags & FCOMMENT) != 0) {
+ do {
+ ic = getc(fp);
+ } while (ic != 0 && ic != EOF);
+ }
+ /* consume 16-bit header CRC, if present */
+ if ((flags & FHCRC) != 0) {
+ (void) getc(fp);
+ (void) getc(fp);
+ }
+
+ if (feof(fp) || ferror(fp))
+ return false;
+
+ /* seek to the end; CRC and length are in the last 8 bytes */
+ long curPosn = ftell(fp);
+ unsigned char buf[8];
+ fseek(fp, -8, SEEK_END);
+ *pCompressedLen = ftell(fp) - curPosn;
+
+ if (fread(buf, 1, 8, fp) != 8)
+ return false;
+ /* seek back to start of compressed data */
+ fseek(fp, curPosn, SEEK_SET);
+
+ *pCompressionMethod = method;
+ *pCRC32 = ZipFileRO::get4LE(&buf[0]);
+ *pUncompressedLen = ZipFileRO::get4LE(&buf[4]);
+
+ return true;
+}
diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp
new file mode 100644
index 0000000..29686ef
--- /dev/null
+++ b/libs/androidfw/misc.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2005 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 "misc"
+
+//
+// Miscellaneous utility functions.
+//
+#include <androidfw/misc.h>
+
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+
+using namespace android;
+
+namespace android {
+
+/*
+ * Get a file's type.
+ */
+FileType getFileType(const char* fileName)
+{
+ struct stat sb;
+
+ if (stat(fileName, &sb) < 0) {
+ if (errno == ENOENT || errno == ENOTDIR)
+ return kFileTypeNonexistent;
+ else {
+ fprintf(stderr, "getFileType got errno=%d on '%s'\n",
+ errno, fileName);
+ return kFileTypeUnknown;
+ }
+ } else {
+ if (S_ISREG(sb.st_mode))
+ return kFileTypeRegular;
+ else if (S_ISDIR(sb.st_mode))
+ return kFileTypeDirectory;
+ else if (S_ISCHR(sb.st_mode))
+ return kFileTypeCharDev;
+ else if (S_ISBLK(sb.st_mode))
+ return kFileTypeBlockDev;
+ else if (S_ISFIFO(sb.st_mode))
+ return kFileTypeFifo;
+#ifdef HAVE_SYMLINKS
+ else if (S_ISLNK(sb.st_mode))
+ return kFileTypeSymlink;
+ else if (S_ISSOCK(sb.st_mode))
+ return kFileTypeSocket;
+#endif
+ else
+ return kFileTypeUnknown;
+ }
+}
+
+/*
+ * Get a file's modification date.
+ */
+time_t getFileModDate(const char* fileName)
+{
+ struct stat sb;
+
+ if (stat(fileName, &sb) < 0)
+ return (time_t) -1;
+
+ return sb.st_mtime;
+}
+
+}; // namespace android
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index 4ae23ec..6be7c5b 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -7,7 +7,8 @@ test_src_files := \
InputChannel_test.cpp \
InputEvent_test.cpp \
InputPublisherAndConsumer_test.cpp \
- ObbFile_test.cpp
+ ObbFile_test.cpp \
+ ZipFileRO_test.cpp
shared_libraries := \
libandroidfw \
diff --git a/libs/androidfw/tests/ZipFileRO_test.cpp b/libs/androidfw/tests/ZipFileRO_test.cpp
new file mode 100644
index 0000000..cb9c721
--- /dev/null
+++ b/libs/androidfw/tests/ZipFileRO_test.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 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 "ZipFileRO_test"
+#include <utils/Log.h>
+#include <androidfw/ZipFileRO.h>
+
+#include <gtest/gtest.h>
+
+#include <fcntl.h>
+#include <string.h>
+
+namespace android {
+
+class ZipFileROTest : public testing::Test {
+protected:
+ virtual void SetUp() {
+ }
+
+ virtual void TearDown() {
+ }
+};
+
+TEST_F(ZipFileROTest, ZipTimeConvertSuccess) {
+ struct tm t;
+
+ // 2011-06-29 14:40:40
+ long when = 0x3EDD7514;
+
+ ZipFileRO::zipTimeToTimespec(when, &t);
+
+ EXPECT_EQ(2011, t.tm_year + 1900)
+ << "Year was improperly converted.";
+
+ EXPECT_EQ(6, t.tm_mon)
+ << "Month was improperly converted.";
+
+ EXPECT_EQ(29, t.tm_mday)
+ << "Day was improperly converted.";
+
+ EXPECT_EQ(14, t.tm_hour)
+ << "Hour was improperly converted.";
+
+ EXPECT_EQ(40, t.tm_min)
+ << "Minute was improperly converted.";
+
+ EXPECT_EQ(40, t.tm_sec)
+ << "Second was improperly converted.";
+}
+
+}
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index a630ea1..5a30472 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -10,6 +10,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
thread/TaskManager.cpp \
font/CacheTexture.cpp \
font/Font.cpp \
+ AssetAtlas.cpp \
FontRenderer.cpp \
GammaFontRenderer.cpp \
Caches.cpp \
@@ -21,6 +22,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
Extensions.cpp \
FboCache.cpp \
GradientCache.cpp \
+ Image.cpp \
Layer.cpp \
LayerCache.cpp \
LayerRenderer.cpp \
@@ -39,6 +41,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
SkiaShader.cpp \
Snapshot.cpp \
Stencil.cpp \
+ Texture.cpp \
TextureCache.cpp \
TextDropShadowCache.cpp
@@ -52,17 +55,23 @@ ifeq ($(USE_OPENGL_RENDERER),true)
external/skia/include/images \
external/skia/src/core \
external/skia/src/ports \
- external/skia/include/utils \
- $(intermediates) \
- frameworks/rs/cpp \
- frameworks/rs
+ external/skia/include/utils
- LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DGL_GLEXT_PROTOTYPES
+ LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
- LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libGLESv2 libskia libui libRS libRScpp
+ LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui
LOCAL_MODULE := libhwui
LOCAL_MODULE_TAGS := optional
+ ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
+ LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT
+ LOCAL_SHARED_LIBRARIES += libRS libRScpp
+ LOCAL_C_INCLUDES += \
+ $(intermediates) \
+ frameworks/rs/cpp \
+ frameworks/rs
+ endif
+
ifndef HWUI_COMPILE_SYMBOLS
LOCAL_CFLAGS += -fvisibility=hidden
endif
diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp
new file mode 100644
index 0000000..782c052
--- /dev/null
+++ b/libs/hwui/AssetAtlas.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2013 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 "AssetAtlas.h"
+#include "Caches.h"
+
+#include <GLES2/gl2ext.h>
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Lifecycle
+///////////////////////////////////////////////////////////////////////////////
+
+void AssetAtlas::init(sp<GraphicBuffer> buffer, int* map, int count) {
+ if (mImage) {
+ return;
+ }
+
+ mImage = new Image(buffer);
+
+ if (mImage->getTexture()) {
+ Caches& caches = Caches::getInstance();
+
+ mTexture = new Texture(caches);
+ mTexture->id = mImage->getTexture();
+ mTexture->width = buffer->getWidth();
+ mTexture->height = buffer->getHeight();
+
+ createEntries(caches, map, count);
+ } else {
+ ALOGW("Could not create atlas image");
+
+ delete mImage;
+ mImage = NULL;
+ mTexture = NULL;
+ }
+}
+
+void AssetAtlas::terminate() {
+ if (mImage) {
+ delete mImage;
+ mImage = NULL;
+
+ delete mTexture;
+ mTexture = NULL;
+
+ for (size_t i = 0; i < mEntries.size(); i++) {
+ delete mEntries.valueAt(i);
+ }
+ mEntries.clear();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Entries
+///////////////////////////////////////////////////////////////////////////////
+
+AssetAtlas::Entry* AssetAtlas::getEntry(SkBitmap* const bitmap) const {
+ ssize_t index = mEntries.indexOfKey(bitmap);
+ return index >= 0 ? mEntries.valueAt(index) : NULL;
+}
+
+Texture* AssetAtlas::getEntryTexture(SkBitmap* const bitmap) const {
+ ssize_t index = mEntries.indexOfKey(bitmap);
+ return index >= 0 ? mEntries.valueAt(index)->texture : NULL;
+}
+
+/**
+ * Delegates changes to wrapping and filtering to the base atlas texture
+ * instead of applying the changes to the virtual textures.
+ */
+struct DelegateTexture: public Texture {
+ DelegateTexture(Caches& caches, Texture* delegate): Texture(caches), mDelegate(delegate) { }
+
+ virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false,
+ bool force = false, GLenum renderTarget = GL_TEXTURE_2D) {
+ mDelegate->setWrapST(wrapS, wrapT, bindTexture, force, renderTarget);
+ }
+
+ virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false,
+ bool force = false, GLenum renderTarget = GL_TEXTURE_2D) {
+ mDelegate->setFilterMinMag(min, mag, bindTexture, force, renderTarget);
+ }
+private:
+ Texture* const mDelegate;
+}; // struct DelegateTexture
+
+/**
+ * TODO: This method does not take the rotation flag into account
+ */
+void AssetAtlas::createEntries(Caches& caches, int* map, int count) {
+ const float width = float(mTexture->width);
+ const float height = float(mTexture->height);
+
+ for (int i = 0; i < count; ) {
+ SkBitmap* bitmap = (SkBitmap*) map[i++];
+ int x = map[i++];
+ int y = map[i++];
+ bool rotated = map[i++] > 0;
+
+ // Bitmaps should never be null, we're just extra paranoid
+ if (!bitmap) continue;
+
+ const UvMapper mapper(
+ x / width, (x + bitmap->width()) / width,
+ y / height, (y + bitmap->height()) / height);
+
+ Texture* texture = new DelegateTexture(caches, mTexture);
+ Entry* entry = new Entry(bitmap, x, y, rotated, texture, mapper, *this);
+
+ texture->id = mTexture->id;
+ texture->blend = !bitmap->isOpaque();
+ texture->width = bitmap->width();
+ texture->height = bitmap->height();
+ texture->uvMapper = &entry->uvMapper;
+
+ mEntries.add(entry->bitmap, entry);
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h
new file mode 100644
index 0000000..2624907
--- /dev/null
+++ b/libs/hwui/AssetAtlas.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_ASSET_ATLAS_H
+#define ANDROID_HWUI_ASSET_ATLAS_H
+
+#include <GLES2/gl2.h>
+
+#include <ui/GraphicBuffer.h>
+
+#include <utils/KeyedVector.h>
+
+#include <cutils/compiler.h>
+
+#include <SkBitmap.h>
+
+#include "Image.h"
+#include "Texture.h"
+#include "UvMapper.h"
+
+namespace android {
+namespace uirenderer {
+
+class Caches;
+
+/**
+ * An asset atlas holds a collection of framework bitmaps in a single OpenGL
+ * texture. Each bitmap is associated with a location, defined in pixels,
+ * inside the atlas. The atlas is generated by the framework and bound as
+ * an external texture using the EGLImageKHR extension.
+ */
+class AssetAtlas {
+public:
+ /**
+ * Entry representing the position and rotation of a
+ * bitmap inside the atlas.
+ */
+ struct Entry {
+ /**
+ * The bitmap that generated this atlas entry.
+ */
+ SkBitmap* bitmap;
+
+ /**
+ * Location of the bitmap inside the atlas, in pixels.
+ */
+ int x;
+ int y;
+
+ /**
+ * If set, the bitmap is rotated 90 degrees (clockwise)
+ * inside the atlas.
+ */
+ bool rotated;
+
+ /*
+ * A "virtual texture" object that represents the texture
+ * this entry belongs to. This texture should never be
+ * modified.
+ */
+ Texture* texture;
+
+ /**
+ * Maps texture coordinates in the [0..1] range into the
+ * correct range to sample this entry from the atlas.
+ */
+ const UvMapper uvMapper;
+
+ /**
+ * Atlas this entry belongs to.
+ */
+ const AssetAtlas& atlas;
+
+ private:
+ Entry(SkBitmap* bitmap, int x, int y, bool rotated,
+ Texture* texture, const UvMapper& mapper, const AssetAtlas& atlas):
+ bitmap(bitmap), x(x), y(y), rotated(rotated),
+ texture(texture), uvMapper(mapper), atlas(atlas) { }
+
+ ~Entry() {
+ delete texture;
+ }
+
+ friend class AssetAtlas;
+ };
+
+ AssetAtlas(): mTexture(NULL), mImage(NULL) { }
+ ~AssetAtlas() { terminate(); }
+
+ /**
+ * Initializes the atlas with the specified buffer and
+ * map. The buffer is a gralloc'd texture that will be
+ * used as an EGLImage. The map is a list of SkBitmap*
+ * and their (x, y) positions as well as their rotation
+ * flags.
+ *
+ * This method returns immediately if the atlas is already
+ * initialized. To re-initialize the atlas, you must
+ * first call terminate().
+ */
+ ANDROID_API void init(sp<GraphicBuffer> buffer, int* map, int count);
+
+ /**
+ * Destroys the atlas texture. This object can be
+ * re-initialized after calling this method.
+ *
+ * After calling this method, the width, height
+ * and texture are set to 0.
+ */
+ ANDROID_API void terminate();
+
+ /**
+ * Returns the width of this atlas in pixels.
+ * Can return 0 if the atlas is not initialized.
+ */
+ uint32_t getWidth() const {
+ return mTexture ? mTexture->width : 0;
+ }
+
+ /**
+ * Returns the height of this atlas in pixels.
+ * Can return 0 if the atlas is not initialized.
+ */
+ uint32_t getHeight() const {
+ return mTexture ? mTexture->height : 0;
+ }
+
+ /**
+ * Returns the OpenGL name of the texture backing this atlas.
+ * Can return 0 if the atlas is not initialized.
+ */
+ GLuint getTexture() const {
+ return mTexture ? mTexture->id : 0;
+ }
+
+ /**
+ * Returns the entry in the atlas associated with the specified
+ * bitmap. If the bitmap is not in the atlas, return NULL.
+ */
+ Entry* getEntry(SkBitmap* const bitmap) const;
+
+ /**
+ * Returns the texture for the atlas entry associated with the
+ * specified bitmap. If the bitmap is not in the atlas, return NULL.
+ */
+ Texture* getEntryTexture(SkBitmap* const bitmap) const;
+
+private:
+ void createEntries(Caches& caches, int* map, int count);
+
+ Texture* mTexture;
+ Image* mImage;
+
+ KeyedVector<SkBitmap*, Entry*> mEntries;
+}; // class AssetAtlas
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_ASSET_ATLAS_H
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index a381a68..74aeddb 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -47,19 +47,21 @@ namespace uirenderer {
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-Caches::Caches(): Singleton<Caches>(), mExtensions(Extensions::getInstance()), mInitialized(false) {
+Caches::Caches(): Singleton<Caches>(),
+ mExtensions(Extensions::getInstance()), mInitialized(false) {
init();
initFont();
initConstraints();
initProperties();
+ initStaticProperties();
initExtensions();
mDebugLevel = readDebugLevel();
ALOGD("Enabling debug mode %d", mDebugLevel);
}
-void Caches::init() {
- if (mInitialized) return;
+bool Caches::init() {
+ if (mInitialized) return false;
glGenBuffers(1, &meshBuffer);
glBindBuffer(GL_ARRAY_BUFFER, meshBuffer);
@@ -82,6 +84,7 @@ void Caches::init() {
mTextureUnit = 0;
mRegionMesh = NULL;
+ mMeshIndices = 0;
blend = false;
lastSrcMode = GL_ZERO;
@@ -94,7 +97,13 @@ void Caches::init() {
debugOverdraw = false;
debugStencilClip = kStencilHide;
+ patchCache.init(*this);
+
mInitialized = true;
+
+ resetBoundTextures();
+
+ return true;
}
void Caches::initFont() {
@@ -132,6 +141,18 @@ void Caches::initConstraints() {
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
}
+void Caches::initStaticProperties() {
+ gpuPixelBuffersEnabled = false;
+
+ // OpenGL ES 3.0+ specific features
+ if (mExtensions.getMajorGlVersion() >= 3) {
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, property, "true") > 0) {
+ gpuPixelBuffersEnabled = !strcmp(property, "true");
+ }
+ }
+}
+
bool Caches::initProperties() {
bool prevDebugLayersUpdates = debugLayersUpdates;
bool prevDebugOverdraw = debugOverdraw;
@@ -147,7 +168,7 @@ bool Caches::initProperties() {
if (property_get(PROPERTY_DEBUG_OVERDRAW, property, NULL) > 0) {
INIT_LOGD(" Overdraw debug enabled: %s", property);
- debugOverdraw = !strcmp(property, "true");
+ debugOverdraw = !strcmp(property, "show");
} else {
debugOverdraw = false;
}
@@ -191,8 +212,9 @@ void Caches::terminate() {
glDeleteBuffers(1, &meshBuffer);
mCurrentBuffer = 0;
- glDeleteBuffers(1, &mRegionMeshIndices);
+ glDeleteBuffers(1, &mMeshIndices);
delete[] mRegionMesh;
+ mMeshIndices = 0;
mRegionMesh = NULL;
fboCache.clear();
@@ -200,6 +222,10 @@ void Caches::terminate() {
programCache.clear();
currentProgram = NULL;
+ assetAtlas.terminate();
+
+ patchCache.clear();
+
mInitialized = false;
}
@@ -227,6 +253,8 @@ void Caches::dumpMemoryUsage(String8 &log) {
pathCache.getSize(), pathCache.getMaxSize());
log.appendFormat(" TextDropShadowCache %8d / %8d\n", dropShadowCache.getSize(),
dropShadowCache.getMaxSize());
+ log.appendFormat(" PatchCache %8d / %8d\n",
+ patchCache.getSize(), patchCache.getMaxSize());
for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
const uint32_t size = fontRenderer->getFontRendererSize(i);
log.appendFormat(" FontRenderer %d %8d / %8d\n", i, size, size);
@@ -234,8 +262,6 @@ void Caches::dumpMemoryUsage(String8 &log) {
log.appendFormat("Other:\n");
log.appendFormat(" FboCache %8d / %8d\n",
fboCache.getSize(), fboCache.getMaxSize());
- log.appendFormat(" PatchCache %8d / %8d\n",
- patchCache.getSize(), patchCache.getMaxSize());
uint32_t total = 0;
total += textureCache.getSize();
@@ -244,6 +270,7 @@ void Caches::dumpMemoryUsage(String8 &log) {
total += gradientCache.getSize();
total += pathCache.getSize();
total += dropShadowCache.getSize();
+ total += patchCache.getSize();
for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) {
total += fontRenderer->getFontRendererSize(i);
}
@@ -357,6 +384,32 @@ bool Caches::bindIndicesBuffer(const GLuint buffer) {
return false;
}
+bool Caches::bindIndicesBuffer() {
+ if (!mMeshIndices) {
+ uint16_t* regionIndices = new uint16_t[REGION_MESH_QUAD_COUNT * 6];
+ for (int i = 0; i < REGION_MESH_QUAD_COUNT; i++) {
+ uint16_t quad = i * 4;
+ int index = i * 6;
+ regionIndices[index ] = quad; // top-left
+ regionIndices[index + 1] = quad + 1; // top-right
+ regionIndices[index + 2] = quad + 2; // bottom-left
+ regionIndices[index + 3] = quad + 2; // bottom-left
+ regionIndices[index + 4] = quad + 1; // top-right
+ regionIndices[index + 5] = quad + 3; // bottom-right
+ }
+
+ glGenBuffers(1, &mMeshIndices);
+ bool force = bindIndicesBuffer(mMeshIndices);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, REGION_MESH_QUAD_COUNT * 6 * sizeof(uint16_t),
+ regionIndices, GL_STATIC_DRAW);
+
+ delete[] regionIndices;
+ return force;
+ }
+
+ return bindIndicesBuffer(mMeshIndices);
+}
+
bool Caches::unbindIndicesBuffer() {
if (mCurrentIndicesBuffer) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
@@ -441,6 +494,46 @@ void Caches::activeTexture(GLuint textureUnit) {
}
}
+void Caches::bindTexture(GLuint texture) {
+ if (mBoundTextures[mTextureUnit] != texture) {
+ glBindTexture(GL_TEXTURE_2D, texture);
+ mBoundTextures[mTextureUnit] = texture;
+ }
+}
+
+void Caches::bindTexture(GLenum target, GLuint texture) {
+ if (mBoundTextures[mTextureUnit] != texture) {
+ glBindTexture(target, texture);
+ mBoundTextures[mTextureUnit] = texture;
+ }
+}
+
+void Caches::deleteTexture(GLuint texture) {
+ // When glDeleteTextures() is called on a currently bound texture,
+ // OpenGL ES specifies that the texture is then considered unbound
+ // Consider the following series of calls:
+ //
+ // glGenTextures -> creates texture name 2
+ // glBindTexture(2)
+ // glDeleteTextures(2) -> 2 is now unbound
+ // glGenTextures -> can return 2 again
+ //
+ // If we don't call glBindTexture(2) after the second glGenTextures
+ // 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;
+ }
+ }
+ glDeleteTextures(1, &texture);
+}
+
+void Caches::resetBoundTextures() {
+ memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint));
+}
+
///////////////////////////////////////////////////////////////////////////////
// Scissor
///////////////////////////////////////////////////////////////////////////////
@@ -546,27 +639,6 @@ TextureVertex* Caches::getRegionMesh() {
// Create the mesh, 2 triangles and 4 vertices per rectangle in the region
if (!mRegionMesh) {
mRegionMesh = new TextureVertex[REGION_MESH_QUAD_COUNT * 4];
-
- uint16_t* regionIndices = new uint16_t[REGION_MESH_QUAD_COUNT * 6];
- for (int i = 0; i < REGION_MESH_QUAD_COUNT; i++) {
- uint16_t quad = i * 4;
- int index = i * 6;
- regionIndices[index ] = quad; // top-left
- regionIndices[index + 1] = quad + 1; // top-right
- regionIndices[index + 2] = quad + 2; // bottom-left
- regionIndices[index + 3] = quad + 2; // bottom-left
- regionIndices[index + 4] = quad + 1; // top-right
- regionIndices[index + 5] = quad + 3; // bottom-right
- }
-
- glGenBuffers(1, &mRegionMeshIndices);
- bindIndicesBuffer(mRegionMeshIndices);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, REGION_MESH_QUAD_COUNT * 6 * sizeof(uint16_t),
- regionIndices, GL_STATIC_DRAW);
-
- delete[] regionIndices;
- } else {
- bindIndicesBuffer(mRegionMeshIndices);
}
return mRegionMesh;
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 91b938b..bdde8fb 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -21,13 +21,18 @@
#define LOG_TAG "OpenGLRenderer"
#endif
+#include <GLES3/gl3.h>
+
+#include <utils/KeyedVector.h>
#include <utils/Singleton.h>
+#include <utils/Vector.h>
#include <cutils/compiler.h>
#include "thread/TaskProcessor.h"
#include "thread/TaskManager.h"
+#include "AssetAtlas.h"
#include "FontRenderer.h"
#include "GammaFontRenderer.h"
#include "TextureCache.h"
@@ -50,6 +55,7 @@ namespace uirenderer {
// Globals
///////////////////////////////////////////////////////////////////////////////
+// GL ES 2.0 defines that at least 16 texture units must be supported
#define REQUIRED_TEXTURE_UNITS_COUNT 3
#define REGION_MESH_QUAD_COUNT 512
@@ -74,6 +80,7 @@ static const GLsizei gVertexAAWidthOffset = 2 * sizeof(float);
static const GLsizei gVertexAALengthOffset = 3 * sizeof(float);
static const GLsizei gMeshCount = 4;
+// Must define as many texture units as specified by REQUIRED_TEXTURE_UNITS_COUNT
static const GLenum gTextureUnits[] = {
GL_TEXTURE0,
GL_TEXTURE1,
@@ -113,7 +120,7 @@ public:
/**
* Initialize caches.
*/
- void init();
+ bool init();
/**
* Initialize global system properties.
@@ -172,6 +179,11 @@ public:
*/
bool unbindMeshBuffer();
+ /**
+ * Binds a global indices buffer that can draw up to
+ * REGION_MESH_QUAD_COUNT quads.
+ */
+ bool bindIndicesBuffer();
bool bindIndicesBuffer(const GLuint buffer);
bool unbindIndicesBuffer();
@@ -213,6 +225,33 @@ public:
void activeTexture(GLuint textureUnit);
/**
+ * Binds the specified texture as a GL_TEXTURE_2D texture.
+ * All texture bindings must be performed with this method or
+ * bindTexture(GLenum, GLuint).
+ */
+ void bindTexture(GLuint texture);
+
+ /**
+ * Binds the specified texture with the specified render target.
+ * All texture bindings must be performed with this method or
+ * bindTexture(GLuint).
+ */
+ void bindTexture(GLenum target, GLuint texture);
+
+ /**
+ * Deletes the specified texture and clears it from the cache
+ * of bound textures.
+ * All textures must be deleted using this method.
+ */
+ void deleteTexture(GLuint texture);
+
+ /**
+ * Signals that the cache of bound textures should be cleared.
+ * Other users of the context may have altered which textures are bound.
+ */
+ void resetBoundTextures();
+
+ /**
* Sets the scissor for the current surface.
*/
bool setScissor(GLint x, GLint y, GLint width, GLint height);
@@ -290,6 +329,10 @@ public:
Dither dither;
Stencil stencil;
+ AssetAtlas assetAtlas;
+
+ bool gpuPixelBuffersEnabled;
+
// Debug methods
PFNGLINSERTEVENTMARKEREXTPROC eventMark;
PFNGLPUSHGROUPMARKEREXTPROC startMark;
@@ -302,6 +345,7 @@ private:
void initFont();
void initExtensions();
void initConstraints();
+ void initStaticProperties();
static void eventMarkNull(GLsizei length, const GLchar* marker) { }
static void startMarkNull(GLsizei length, const GLchar* marker) { }
@@ -336,7 +380,9 @@ private:
// Used to render layers
TextureVertex* mRegionMesh;
- GLuint mRegionMeshIndices;
+
+ // Global index buffer
+ GLuint mMeshIndices;
mutable Mutex mGarbageLock;
Vector<Layer*> mLayerGarbage;
@@ -346,6 +392,8 @@ private:
bool mInitialized;
uint32_t mFunctorsCount;
+
+ GLuint mBoundTextures[REQUIRED_TEXTURE_UNITS_COUNT];
}; // class Caches
}; // namespace uirenderer
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 790c4f4..786f12a 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -53,8 +53,6 @@
// Turn on to display debug info about 9patch objects
#define DEBUG_PATCHES 0
-// Turn on to "explode" 9patch objects
-#define DEBUG_EXPLODE_PATCHES 0
// Turn on to display vertex and tex coords data about 9patch objects
// This flag requires DEBUG_PATCHES to be turned on
#define DEBUG_PATCHES_VERTICES 0
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 9323a3a..512d3b1 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -85,8 +85,8 @@ public:
}
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
- DEFER_LOGD("%d replaying DrawingBatch %p, with %d ops (batch id %x, merge id %p)",
- index, this, mOps.size(), mOps[0]->getBatchId(), mOps[0]->getMergeId());
+ DEFER_LOGD("%d replaying DrawBatch %p, with %d ops (batch id %x, merge id %p)",
+ index, this, mOps.size(), getBatchId(), getMergeId());
status_t status = DrawGlInfo::kStatusDone;
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
@@ -145,7 +145,10 @@ public:
* dropped, so we make simplifying qualifications on the ops that can merge, per op type.
*/
bool canMergeWith(DrawOp* op) {
- if (!op->state.mMatrix.isPureTranslate()) return false;
+ if (getBatchId() == DeferredDisplayList::kOpBatch_Bitmap) {
+ // Bitmap batches can handle translate and scaling
+ if (!op->state.mMatrix.isSimple()) return false;
+ } else if (!op->state.mMatrix.isPureTranslate()) return false;
bool isTextBatch = getBatchId() == DeferredDisplayList::kOpBatch_Text ||
getBatchId() == DeferredDisplayList::kOpBatch_ColorText;
@@ -193,10 +196,10 @@ public:
}
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
- DEFER_LOGD("%d replaying DrawingBatch %p, with %d ops (batch id %x, merge id %p)",
+ DEFER_LOGD("%d replaying MergingDrawBatch %p, with %d ops (batch id %x, merge id %p)",
index, this, mOps.size(), getBatchId(), getMergeId());
if (mOps.size() == 1) {
- return DrawBatch::replay(renderer, dirty, false);
+ return DrawBatch::replay(renderer, dirty, 0);
}
DrawOp* op = mOps[0];
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 1cbd531..b2d9915 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -514,6 +514,10 @@ void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level)
for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
DisplayListOp *op = mDisplayListData->displayListOps[i];
+#if DEBUG_DISPLAY_LIST
+ op->output(level + 1);
+#endif
+
logBuffer.writeCommand(level, op->name());
handler(op, saveCount, mClipToBounds);
}
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 5f84329..97c34dc 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -129,7 +129,12 @@ public:
void setName(const char* name) {
if (name) {
- mName.setTo(name);
+ char* lastPeriod = strrchr(name, '.');
+ if (lastPeriod) {
+ mName.setTo(lastPeriod + 1);
+ } else {
+ mName.setTo(name);
+ }
}
}
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index a0290e3..028decd 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -26,8 +26,10 @@
#include <private/hwui/DrawGlInfo.h>
#include "OpenGLRenderer.h"
+#include "AssetAtlas.h"
#include "DeferredDisplayList.h"
#include "DisplayListRenderer.h"
+#include "UvMapper.h"
#include "utils/LinearAllocator.h"
#define CRASH() do { \
@@ -218,6 +220,9 @@ public:
DrawBoundedOp(float left, float top, float right, float bottom, SkPaint* paint)
: DrawOp(paint), mLocalBounds(left, top, right, bottom) {}
+ DrawBoundedOp(const Rect& localBounds, SkPaint* paint)
+ : DrawOp(paint), mLocalBounds(localBounds) {}
+
// Calculates bounds as smallest rect encompassing all points
// NOTE: requires at least 1 vertex, and doesn't account for stroke size (should be handled in
// subclass' constructor)
@@ -721,7 +726,6 @@ private:
int mSetBits;
};
-
///////////////////////////////////////////////////////////////////////////////
// DRAW OPERATIONS - these are operations that can draw to the canvas's device
///////////////////////////////////////////////////////////////////////////////
@@ -729,9 +733,16 @@ private:
class DrawBitmapOp : public DrawBoundedOp {
public:
DrawBitmapOp(SkBitmap* bitmap, float left, float top, SkPaint* paint)
- : DrawBoundedOp(left, top, left + bitmap->width(), top + bitmap->height(),
- paint),
- mBitmap(bitmap) {}
+ : DrawBoundedOp(left, top, left + bitmap->width(), top + bitmap->height(), paint),
+ mBitmap(bitmap), mAtlasEntry(NULL) {
+ }
+
+ DrawBitmapOp(SkBitmap* bitmap, float left, float top, SkPaint* paint,
+ const AssetAtlas::Entry* entry)
+ : DrawBoundedOp(left, top, left + bitmap->width(), top + bitmap->height(), paint),
+ mBitmap(bitmap), mAtlasEntry(entry) {
+ if (entry) mUvMapper = entry->uvMapper;
+ }
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmap(mBitmap, mLocalBounds.left, mLocalBounds.top,
@@ -749,14 +760,20 @@ public:
TextureVertex vertices[6 * ops.size()];
TextureVertex* vertex = &vertices[0];
- // TODO: manually handle rect clip for bitmaps by adjusting texCoords per op, and allowing
- // them to be merged in getBatchId()
- const Rect texCoords(0, 0, 1, 1);
+ bool transformed = false;
- const float width = mBitmap->width();
- const float height = mBitmap->height();
+ // TODO: manually handle rect clip for bitmaps by adjusting texCoords per op,
+ // and allowing them to be merged in getBatchId()
for (unsigned int i = 0; i < ops.size(); i++) {
const Rect& opBounds = ops[i]->state.mBounds;
+ // When we reach multiDraw(), the matrix can be either
+ // pureTranslate or simple (translate and/or scale).
+ // If the matrix is not pureTranslate, then we have a scale
+ if (!ops[i]->state.mMatrix.isPureTranslate()) transformed = true;
+
+ Rect texCoords(0, 0, 1, 1);
+ ((DrawBitmapOp*) ops[i])->mUvMapper.map(texCoords);
+
SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, top);
SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top);
SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom);
@@ -766,7 +783,8 @@ public:
SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, bottom);
}
- return renderer.drawBitmaps(mBitmap, ops.size(), &vertices[0], bounds, mPaint);
+ return renderer.drawBitmaps(mBitmap, ops.size(), &vertices[0],
+ transformed, bounds, mPaint);
}
virtual void output(int level, uint32_t logFlags) {
@@ -775,18 +793,25 @@ public:
virtual const char* name() { return "DrawBitmap"; }
+ bool bitmapMergeAllowed() {
+ return state.mMatrix.isSimple() && !state.mClipped &&
+ OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+ }
+
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
*batchId = DeferredDisplayList::kOpBatch_Bitmap;
- *mergeId = (mergeid_t)mBitmap;
+ *mergeId = mAtlasEntry ? (mergeid_t) &mAtlasEntry->atlas : (mergeid_t) mBitmap;
// don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in
// MergingDrawBatch::canMergeWith
- return mergeAllowed() && (mBitmap->getConfig() != SkBitmap::kA8_Config);
+ return bitmapMergeAllowed() && (mBitmap->getConfig() != SkBitmap::kA8_Config);
}
const SkBitmap* bitmap() { return mBitmap; }
protected:
SkBitmap* mBitmap;
+ const AssetAtlas::Entry* mAtlasEntry;
+ UvMapper mUvMapper;
};
class DrawBitmapMatrixOp : public DrawBoundedOp {
@@ -904,20 +929,25 @@ private:
class DrawPatchOp : public DrawBoundedOp {
public:
- DrawPatchOp(SkBitmap* bitmap, const int32_t* xDivs,
- const int32_t* yDivs, const uint32_t* colors, uint32_t width, uint32_t height,
- int8_t numColors, float left, float top, float right, float bottom,
- int alpha, SkXfermode::Mode mode)
+ DrawPatchOp(SkBitmap* bitmap, Res_png_9patch* patch,
+ float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode)
: DrawBoundedOp(left, top, right, bottom, 0),
- mBitmap(bitmap), mxDivs(xDivs), myDivs(yDivs),
- mColors(colors), mxDivsCount(width), myDivsCount(height),
- mNumColors(numColors), mAlpha(alpha), mMode(mode) {};
+ mBitmap(bitmap), mPatch(patch), mAlpha(alpha), mMode(mode),
+ mGenerationId(0), mMesh(NULL) {
+ mEntry = Caches::getInstance().assetAtlas.getEntry(bitmap);
+ };
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
- // NOTE: not calling the virtual method, which takes a paint
- return renderer.drawPatch(mBitmap, mxDivs, myDivs, mColors,
- mxDivsCount, myDivsCount, mNumColors,
- mLocalBounds.left, mLocalBounds.top,
+ if (!mMesh || renderer.getCaches().patchCache.getGenerationId() != mGenerationId) {
+ PatchCache& cache = renderer.getCaches().patchCache;
+ mMesh = cache.get(mEntry, mBitmap->width(), mBitmap->height(),
+ mLocalBounds.right - mLocalBounds.left, mLocalBounds.bottom - mLocalBounds.top,
+ mPatch);
+ mGenerationId = cache.getGenerationId();
+ }
+ // We're not calling the public variant of drawPatch() here
+ // This method won't perform the quickReject() since we've already done it at this point
+ return renderer.drawPatch(mBitmap, mMesh, mEntry, mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom, mAlpha, mMode);
}
@@ -929,20 +959,20 @@ public:
virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
*batchId = DeferredDisplayList::kOpBatch_Patch;
- *mergeId = (mergeid_t)mBitmap;
+ *mergeId = (mergeid_t) mBitmap;
return true;
}
private:
SkBitmap* mBitmap;
- const int32_t* mxDivs;
- const int32_t* myDivs;
- const uint32_t* mColors;
- uint32_t mxDivsCount;
- uint32_t myDivsCount;
- int8_t mNumColors;
+ Res_png_9patch* mPatch;
+
int mAlpha;
SkXfermode::Mode mMode;
+
+ uint32_t mGenerationId;
+ const Patch* mMesh;
+ AssetAtlas::Entry* mEntry;
};
class DrawColorOp : public DrawOp {
@@ -1270,23 +1300,10 @@ private:
class DrawTextOp : public DrawBoundedOp {
public:
DrawTextOp(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float length)
- : DrawBoundedOp(paint), mText(text), mBytesCount(bytesCount), mCount(count),
- mX(x), mY(y), mPositions(positions), mLength(length) {
- // duplicates bounds calculation from OpenGLRenderer::drawText, but doesn't alter mX
- SkPaint::FontMetrics metrics;
- paint->getFontMetrics(&metrics, 0.0f);
- switch (paint->getTextAlign()) {
- case SkPaint::kCenter_Align:
- x -= length / 2.0f;
- break;
- case SkPaint::kRight_Align:
- x -= length;
- break;
- default:
- break;
- }
- mLocalBounds.set(x, mY + metrics.fTop, x + length, mY + metrics.fBottom);
+ const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds)
+ : DrawBoundedOp(bounds, paint), mText(text), mBytesCount(bytesCount), mCount(count),
+ mX(x), mY(y), mPositions(positions), mTotalAdvance(totalAdvance) {
+ mLocalBounds.translate(x,y);
memset(&mPrecacheTransform.data[0], 0xff, 16 * sizeof(float));
}
@@ -1312,7 +1329,7 @@ public:
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawText(mText, mBytesCount, mCount, mX, mY,
- mPositions, getPaint(renderer), mLength);
+ mPositions, getPaint(renderer), mTotalAdvance, mLocalBounds);
}
virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
@@ -1325,7 +1342,8 @@ public:
DrawTextOp& op = *((DrawTextOp*)ops[i]);
status |= renderer.drawText(op.mText, op.mBytesCount, op.mCount, op.mX, op.mY,
- op.mPositions, op.getPaint(renderer), op.mLength, drawOpMode);
+ op.mPositions, op.getPaint(renderer), op.mTotalAdvance, op.mLocalBounds,
+ drawOpMode);
}
return status;
}
@@ -1343,7 +1361,7 @@ private:
float mX;
float mY;
const float* mPositions;
- float mLength;
+ float mTotalAdvance;
mat4 mPrecacheTransform;
};
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 876c38a..6d85a16 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -257,7 +257,8 @@ status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top
bitmap = refBitmap(bitmap);
paint = refPaint(paint);
- addDrawOp(new (alloc()) DrawBitmapOp(bitmap, left, top, paint));
+ const AssetAtlas::Entry* entry = mCaches.assetAtlas.getEntry(bitmap);
+ addDrawOp(new (alloc()) DrawBitmapOp(bitmap, left, top, paint, entry));
return DrawGlInfo::kStatusDone;
}
@@ -281,7 +282,8 @@ status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float
(srcBottom - srcTop == dstBottom - dstTop) &&
(srcRight - srcLeft == dstRight - dstLeft)) {
// transform simple rect to rect drawing case into position bitmap ops, since they merge
- addDrawOp(new (alloc()) DrawBitmapOp(bitmap, dstLeft, dstTop, paint));
+ const AssetAtlas::Entry* entry = mCaches.assetAtlas.getEntry(bitmap);
+ addDrawOp(new (alloc()) DrawBitmapOp(bitmap, dstLeft, dstTop, paint, entry));
return DrawGlInfo::kStatusDone;
}
@@ -313,20 +315,15 @@ status_t DisplayListRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, in
return DrawGlInfo::kStatusDone;
}
-status_t DisplayListRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs,
- const int32_t* yDivs, const uint32_t* colors, uint32_t width, uint32_t height,
- int8_t numColors, float left, float top, float right, float bottom, SkPaint* paint) {
+status_t DisplayListRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
+ float left, float top, float right, float bottom, SkPaint* paint) {
int alpha;
SkXfermode::Mode mode;
OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
bitmap = refBitmap(bitmap);
- xDivs = refBuffer<int>(xDivs, width);
- yDivs = refBuffer<int>(yDivs, height);
- colors = refBuffer<uint32_t>(colors, numColors);
- addDrawOp(new (alloc()) DrawPatchOp(bitmap, xDivs, yDivs, colors, width, height, numColors,
- left, top, right, bottom, alpha, mode));
+ addDrawOp(new (alloc()) DrawPatchOp(bitmap, patch, left, top, right, bottom, alpha, mode));
return DrawGlInfo::kStatusDone;
}
@@ -423,17 +420,16 @@ status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int
status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
float x, float y, const float* positions, SkPaint* paint,
- float length, DrawOpMode drawOpMode) {
+ float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode) {
if (!text || count <= 0) return DrawGlInfo::kStatusDone;
- if (length < 0.0f) length = paint->measureText(text, bytesCount);
-
text = refText(text, bytesCount);
positions = refBuffer<float>(positions, count * 2);
paint = refPaint(paint);
- DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count, x, y, positions, paint, length);
+ DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count,
+ x, y, positions, paint, totalAdvance, bounds);
addDrawOp(op);
return DrawGlInfo::kStatusDone;
}
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 75abad6..85d6107 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -103,8 +103,7 @@ public:
virtual status_t drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint);
virtual status_t drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
float* vertices, int* colors, SkPaint* paint);
- virtual status_t drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+ virtual status_t drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
float left, float top, float right, float bottom, SkPaint* paint);
virtual status_t drawColor(int color, SkXfermode::Mode mode);
virtual status_t drawRect(float left, float top, float right, float bottom, SkPaint* paint);
@@ -122,7 +121,8 @@ public:
virtual status_t drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint);
virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float length, DrawOpMode drawOpMode);
+ const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
+ DrawOpMode drawOpMode);
virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
index 19b3849..649a7bc 100644
--- a/libs/hwui/Dither.cpp
+++ b/libs/hwui/Dither.cpp
@@ -24,12 +24,15 @@ namespace uirenderer {
// Lifecycle
///////////////////////////////////////////////////////////////////////////////
+Dither::Dither(): mCaches(NULL), mInitialized(false), mDitherTexture(0) {
+}
+
void Dither::bindDitherTexture() {
if (!mInitialized) {
bool useFloatTexture = Extensions::getInstance().getMajorGlVersion() >= 3;
glGenTextures(1, &mDitherTexture);
- glBindTexture(GL_TEXTURE_2D, mDitherTexture);
+ mCaches->bindTexture(mDitherTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -68,13 +71,13 @@ void Dither::bindDitherTexture() {
mInitialized = true;
} else {
- glBindTexture(GL_TEXTURE_2D, mDitherTexture);
+ mCaches->bindTexture(mDitherTexture);
}
}
void Dither::clear() {
if (mInitialized) {
- glDeleteTextures(1, &mDitherTexture);
+ mCaches->deleteTexture(mDitherTexture);
mInitialized = false;
}
}
@@ -84,8 +87,10 @@ void Dither::clear() {
///////////////////////////////////////////////////////////////////////////////
void Dither::setupProgram(Program* program, GLuint* textureUnit) {
+ if (!mCaches) mCaches = &Caches::getInstance();
+
GLuint textureSlot = (*textureUnit)++;
- Caches::getInstance().activeTexture(textureSlot);
+ mCaches->activeTexture(textureSlot);
bindDitherTexture();
diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h
index 4d1f921..546236b 100644
--- a/libs/hwui/Dither.h
+++ b/libs/hwui/Dither.h
@@ -24,9 +24,7 @@
namespace android {
namespace uirenderer {
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
+class Caches;
// Must be a power of two
#define DITHER_KERNEL_SIZE 4
@@ -39,7 +37,7 @@ namespace uirenderer {
*/
class Dither {
public:
- Dither(): mInitialized(false), mDitherTexture(0) { }
+ Dither();
void clear();
void setupProgram(Program* program, GLuint* textureUnit);
@@ -47,6 +45,7 @@ public:
private:
void bindDitherTexture();
+ Caches* mCaches;
bool mInitialized;
GLuint mDitherTexture;
};
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index 51aec8d..eefdb84 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -14,6 +14,16 @@
* limitations under the License.
*/
+#define LOG_TAG "OpenGLRenderer"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <utils/Log.h>
+
#include "Debug.h"
#include "Extensions.h"
@@ -40,33 +50,22 @@ namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
Extensions::Extensions(): Singleton<Extensions>() {
- const char* buffer = (const char*) glGetString(GL_EXTENSIONS);
- const char* current = buffer;
- const char* head = current;
- EXT_LOGD("Available GL extensions:");
- do {
- head = strchr(current, ' ');
- String8 s(current, head ? head - current : strlen(current));
- if (s.length()) {
- mExtensionList.add(s);
- EXT_LOGD(" %s", s.string());
- }
- current = head + 1;
- } while (head);
-
- mHasNPot = hasExtension("GL_OES_texture_npot");
- mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch");
- mHasDiscardFramebuffer = hasExtension("GL_EXT_discard_framebuffer");
- mHasDebugMarker = hasExtension("GL_EXT_debug_marker");
- mHasDebugLabel = hasExtension("GL_EXT_debug_label");
- mHasTiledRendering = hasExtension("GL_QCOM_tiled_rendering");
- mHas1BitStencil = hasExtension("GL_OES_stencil1");
- mHas4BitStencil = hasExtension("GL_OES_stencil4");
-
- mExtensions = strdup(buffer);
+ // Query GL extensions
+ findExtensions((const char*) glGetString(GL_EXTENSIONS), mGlExtensionList);
+ mHasNPot = hasGlExtension("GL_OES_texture_npot");
+ mHasFramebufferFetch = hasGlExtension("GL_NV_shader_framebuffer_fetch");
+ mHasDiscardFramebuffer = hasGlExtension("GL_EXT_discard_framebuffer");
+ mHasDebugMarker = hasGlExtension("GL_EXT_debug_marker");
+ mHasDebugLabel = hasGlExtension("GL_EXT_debug_label");
+ mHasTiledRendering = hasGlExtension("GL_QCOM_tiled_rendering");
+ mHas1BitStencil = hasGlExtension("GL_OES_stencil1");
+ mHas4BitStencil = hasGlExtension("GL_OES_stencil4");
+
+ // Query EGL extensions
+ findExtensions(eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS), mEglExtensionList);
+ mHasNvSystemTime = hasEglExtension("EGL_NV_system_time");
const char* version = (const char*) glGetString(GL_VERSION);
- mVersion = strdup(version);
// Section 6.1.5 of the OpenGL ES specification indicates the GL version
// string strictly follows this format:
@@ -88,22 +87,41 @@ Extensions::Extensions(): Singleton<Extensions>() {
}
Extensions::~Extensions() {
- free(mExtensions);
- free(mVersion);
}
///////////////////////////////////////////////////////////////////////////////
// Methods
///////////////////////////////////////////////////////////////////////////////
-bool Extensions::hasExtension(const char* extension) const {
+bool Extensions::hasGlExtension(const char* extension) const {
const String8 s(extension);
- return mExtensionList.indexOf(s) >= 0;
+ return mGlExtensionList.indexOf(s) >= 0;
+}
+
+bool Extensions::hasEglExtension(const char* extension) const {
+ const String8 s(extension);
+ return mEglExtensionList.indexOf(s) >= 0;
+}
+
+void Extensions::findExtensions(const char* extensions, SortedVector<String8>& list) const {
+ const char* current = extensions;
+ const char* head = current;
+ EXT_LOGD("Available extensions:");
+ do {
+ head = strchr(current, ' ');
+ String8 s(current, head ? head - current : strlen(current));
+ if (s.length()) {
+ list.add(s);
+ EXT_LOGD(" %s", s.string());
+ }
+ current = head + 1;
+ } while (head);
}
void Extensions::dump() const {
- ALOGD("%s", mVersion);
- ALOGD("Supported extensions:\n%s", mExtensions);
+ ALOGD("%s", (const char*) glGetString(GL_VERSION));
+ ALOGD("Supported GL extensions:\n%s", (const char*) glGetString(GL_EXTENSIONS));
+ ALOGD("Supported EGL extensions:\n%s", eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS));
}
}; // namespace uirenderer
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 54a3987..3112244 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -17,13 +17,12 @@
#ifndef ANDROID_HWUI_EXTENSIONS_H
#define ANDROID_HWUI_EXTENSIONS_H
+#include <cutils/compiler.h>
+
#include <utils/Singleton.h>
#include <utils/SortedVector.h>
#include <utils/String8.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
namespace android {
namespace uirenderer {
@@ -31,11 +30,8 @@ namespace uirenderer {
// Classes
///////////////////////////////////////////////////////////////////////////////
-class Extensions: public Singleton<Extensions> {
+class ANDROID_API Extensions: public Singleton<Extensions> {
public:
- Extensions();
- ~Extensions();
-
inline bool hasNPot() const { return mHasNPot; }
inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
inline bool hasDiscardFramebuffer() const { return mHasDiscardFramebuffer; }
@@ -44,21 +40,26 @@ public:
inline bool hasTiledRendering() const { return mHasTiledRendering; }
inline bool has1BitStencil() const { return mHas1BitStencil; }
inline bool has4BitStencil() const { return mHas4BitStencil; }
+ inline bool hasNvSystemTime() const { return mHasNvSystemTime; }
inline int getMajorGlVersion() const { return mVersionMajor; }
inline int getMinorGlVersion() const { return mVersionMinor; }
- bool hasExtension(const char* extension) const;
+ bool hasGlExtension(const char* extension) const;
+ bool hasEglExtension(const char* extension) const;
void dump() const;
private:
- friend class Singleton<Extensions>;
+ Extensions();
+ ~Extensions();
- SortedVector<String8> mExtensionList;
+ void findExtensions(const char* extensions, SortedVector<String8>& list) const;
+
+ friend class Singleton<Extensions>;
- char* mExtensions;
- char* mVersion;
+ SortedVector<String8> mGlExtensionList;
+ SortedVector<String8> mEglExtensionList;
bool mHasNPot;
bool mHasFramebufferFetch;
@@ -68,6 +69,7 @@ private:
bool mHasTiledRendering;
bool mHas1BitStencil;
bool mHas4BitStencil;
+ bool mHasNvSystemTime;
int mVersionMajor;
int mVersionMinor;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 543cfa2..3e3d882 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -24,7 +24,9 @@
#include <utils/Functor.h>
#include <utils/Log.h>
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
#include <RenderScript.h>
+#endif
#include "utils/Blur.h"
#include "utils/Timing.h"
@@ -374,7 +376,7 @@ void FontRenderer::checkTextureUpdate() {
if (cacheTexture->getTextureId() != lastTextureId) {
lastTextureId = cacheTexture->getTextureId();
caches.activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, lastTextureId);
+ caches.bindTexture(lastTextureId);
}
if (cacheTexture->upload()) {
@@ -427,7 +429,7 @@ void FontRenderer::issueDrawCommand() {
first = false;
}
- glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
+ caches.bindTexture(texture->getTextureId());
texture->setLinearFiltering(mLinearFiltering, false);
TextureVertex* mesh = texture->mesh();
@@ -532,13 +534,18 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
return image;
}
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
// Align buffers for renderscript usage
if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) {
paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT;
}
-
int size = paddedWidth * paddedHeight;
uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size);
+#else
+ int size = paddedWidth * paddedHeight;
+ uint8_t* dataBuffer = (uint8_t*) malloc(size);
+#endif
+
memset(dataBuffer, 0, size);
int penX = radius - bounds.left;
@@ -633,43 +640,46 @@ void FontRenderer::removeFont(const Font* font) {
}
void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
- if (width * height * radius < RS_MIN_INPUT_CUTOFF) {
- float *gaussian = new float[2 * radius + 1];
- Blur::generateGaussianWeights(gaussian, radius);
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
+ if (width * height * radius >= RS_MIN_INPUT_CUTOFF) {
+ uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
+
+ if (mRs.get() == 0) {
+ mRs = new RSC::RS();
+ if (!mRs->init(true, true)) {
+ ALOGE("blur RS failed to init");
+ }
- uint8_t* scratch = new uint8_t[width * height];
- Blur::horizontal(gaussian, radius, *image, scratch, width, height);
- Blur::vertical(gaussian, radius, scratch, *image, width, height);
+ mRsElement = RSC::Element::A_8(mRs);
+ mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
+ }
- delete[] gaussian;
- delete[] scratch;
- return;
- }
+ sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
+ sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
+ RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
+ sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
+ RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
- uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
+ mRsScript->setRadius(radius);
+ mRsScript->blur(ain, aout);
- if (mRs.get() == 0) {
- mRs = new RSC::RS();
- if (!mRs->init(true, true)) {
- ALOGE("blur RS failed to init");
- }
+ // replace the original image's pointer, avoiding a copy back to the original buffer
+ free(*image);
+ *image = outImage;
- mRsElement = RSC::Element::A_8(mRs);
- mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement);
+ return;
}
+#endif
- sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
- sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
- RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image);
- sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE,
- RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage);
+ float *gaussian = new float[2 * radius + 1];
+ Blur::generateGaussianWeights(gaussian, radius);
- mRsScript->setRadius(radius);
- mRsScript->blur(ain, aout);
+ uint8_t* scratch = new uint8_t[width * height];
+ Blur::horizontal(gaussian, radius, *image, scratch, width, height);
+ Blur::vertical(gaussian, radius, scratch, *image, width, height);
- // replace the original image's pointer, avoiding a copy back to the original buffer
- free(*image);
- *image = outImage;
+ delete[] gaussian;
+ delete[] scratch;
}
uint32_t FontRenderer::getCacheSize() const {
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 307a1d9..cbbd871 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -19,6 +19,7 @@
#include <utils/LruCache.h>
#include <utils/Vector.h>
+#include <utils/StrongPointer.h>
#include <SkPaint.h>
@@ -32,11 +33,13 @@
#include "Matrix.h"
#include "Properties.h"
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
namespace RSC {
class Element;
class RS;
class ScriptIntrinsicBlur;
}
+#endif
class Functor;
@@ -168,10 +171,12 @@ private:
bool mLinearFiltering;
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
// RS constructs
sp<RSC::RS> mRs;
sp<const RSC::Element> mRsElement;
sp<RSC::ScriptIntrinsicBlur> mRsScript;
+#endif
static void computeGaussianWeights(float* weights, int32_t radius);
static void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest,
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 507ed95..1815bff 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -120,7 +120,7 @@ void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
const uint32_t size = texture->width * texture->height * bytesPerPixel();
mSize -= size;
- glDeleteTextures(1, &texture->id);
+ texture->deleteTexture();
delete texture;
}
}
@@ -173,7 +173,7 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
GradientInfo info;
getGradientInfo(colors, count, info);
- Texture* texture = new Texture;
+ Texture* texture = new Texture();
texture->width = info.width;
texture->height = 2;
texture->blend = info.hasAlpha;
@@ -286,7 +286,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions,
memcpy(pixels + rowBytes, pixels, rowBytes);
glGenTextures(1, &texture->id);
- glBindTexture(GL_TEXTURE_2D, texture->id);
+ Caches::getInstance().bindTexture(texture->id);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
if (mUseFloatTexture) {
diff --git a/libs/hwui/Image.cpp b/libs/hwui/Image.cpp
new file mode 100644
index 0000000..edf3930
--- /dev/null
+++ b/libs/hwui/Image.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 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 "OpenGLRenderer"
+
+#include <utils/Log.h>
+
+#include "Caches.h"
+#include "Image.h"
+
+namespace android {
+namespace uirenderer {
+
+Image::Image(sp<GraphicBuffer> buffer) {
+ // Create the EGLImage object that maps the GraphicBuffer
+ EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ EGLClientBuffer clientBuffer = (EGLClientBuffer) buffer->getNativeBuffer();
+ EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE };
+
+ mImage = eglCreateImageKHR(display, EGL_NO_CONTEXT,
+ EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs);
+
+ if (mImage == EGL_NO_IMAGE_KHR) {
+ ALOGW("Error creating image (%#x)", eglGetError());
+ mTexture = 0;
+ } else {
+ // Create a 2D texture to sample from the EGLImage
+ glGenTextures(1, &mTexture);
+ Caches::getInstance().bindTexture(mTexture);
+ glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, mImage);
+
+ GLenum status = GL_NO_ERROR;
+ while ((status = glGetError()) != GL_NO_ERROR) {
+ ALOGW("Error creating image (%#x)", status);
+ }
+ }
+}
+
+Image::~Image() {
+ if (mImage != EGL_NO_IMAGE_KHR) {
+ eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), mImage);
+ mImage = EGL_NO_IMAGE_KHR;
+
+ Caches::getInstance().deleteTexture(mTexture);
+ mTexture = 0;
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Image.h b/libs/hwui/Image.h
new file mode 100644
index 0000000..2514535
--- /dev/null
+++ b/libs/hwui/Image.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_IMAGE_H
+#define ANDROID_HWUI_IMAGE_H
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <ui/GraphicBuffer.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A simple wrapper that creates an EGLImage and a texture for a GraphicBuffer.
+ */
+class Image {
+public:
+ /**
+ * Creates a new image from the specified graphic buffer. If the image
+ * cannot be created, getTexture() will return 0 and getImage() will
+ * return EGL_NO_IMAGE_KHR.
+ */
+ Image(sp<GraphicBuffer> buffer);
+ ~Image();
+
+ /**
+ * Returns the name of the GL texture that can be used to sample
+ * from this image.
+ */
+ GLuint getTexture() const {
+ return mTexture;
+ }
+
+ /**
+ * Returns the name of the EGL image represented by this object.
+ */
+ EGLImageKHR getImage() const {
+ return mImage;
+ }
+
+private:
+ GLuint mTexture;
+ EGLImageKHR mImage;
+}; // class Image
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_IMAGE_H
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 4adad05..134f452 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -28,7 +28,8 @@
namespace android {
namespace uirenderer {
-Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight) {
+Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight):
+ caches(Caches::getInstance()), texture(caches) {
mesh = NULL;
meshIndices = NULL;
meshElementCount = 0;
@@ -47,11 +48,11 @@ Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight) {
debugDrawUpdate = false;
hasDrawnSinceUpdate = false;
deferredList = NULL;
- Caches::getInstance().resourceCache.incrementRefcount(this);
+ caches.resourceCache.incrementRefcount(this);
}
Layer::~Layer() {
- if (colorFilter) Caches::getInstance().resourceCache.decrementRefcount(colorFilter);
+ if (colorFilter) caches.resourceCache.decrementRefcount(colorFilter);
removeFbo();
deleteTexture();
@@ -76,7 +77,7 @@ bool Layer::resize(const uint32_t width, const uint32_t height) {
return true;
}
- const uint32_t maxTextureSize = Caches::getInstance().maxTextureSize;
+ const uint32_t maxTextureSize = caches.maxTextureSize;
if (desiredWidth > maxTextureSize || desiredHeight > maxTextureSize) {
ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)",
desiredWidth, desiredHeight, maxTextureSize, maxTextureSize);
@@ -89,7 +90,7 @@ bool Layer::resize(const uint32_t width, const uint32_t height) {
setSize(desiredWidth, desiredHeight);
if (fbo) {
- Caches::getInstance().activeTexture(0);
+ caches.activeTexture(0);
bindTexture();
allocateTexture();
@@ -120,14 +121,14 @@ void Layer::removeFbo(bool flush) {
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
- Caches::getInstance().renderBufferCache.put(stencil);
+ caches.renderBufferCache.put(stencil);
stencil = NULL;
}
if (fbo) {
if (flush) LayerRenderer::flushLayer(this);
// If put fails the cache will delete the FBO
- Caches::getInstance().fboCache.put(fbo);
+ caches.fboCache.put(fbo);
fbo = 0;
}
}
@@ -138,11 +139,51 @@ void Layer::setPaint(SkPaint* paint) {
void Layer::setColorFilter(SkiaColorFilter* filter) {
if (colorFilter) {
- Caches::getInstance().resourceCache.decrementRefcount(colorFilter);
+ caches.resourceCache.decrementRefcount(colorFilter);
}
colorFilter = filter;
if (colorFilter) {
- Caches::getInstance().resourceCache.incrementRefcount(colorFilter);
+ caches.resourceCache.incrementRefcount(colorFilter);
+ }
+}
+
+void Layer::bindTexture() const {
+ if (texture.id) {
+ caches.bindTexture(renderTarget, texture.id);
+ }
+}
+
+void Layer::bindStencilRenderBuffer() const {
+ if (stencil) {
+ stencil->bind();
+ }
+}
+
+void Layer::generateTexture() {
+ if (!texture.id) {
+ glGenTextures(1, &texture.id);
+ }
+}
+
+void Layer::deleteTexture() {
+ if (texture.id) {
+ texture.deleteTexture();
+ texture.id = 0;
+ }
+}
+
+void Layer::clearTexture() {
+ texture.id = 0;
+}
+
+void Layer::allocateTexture() {
+#if DEBUG_LAYERS
+ ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight());
+#endif
+ if (texture.id) {
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glTexImage2D(renderTarget, 0, GL_RGBA, getWidth(), getHeight(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
}
}
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 7186603..326b25a 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -40,6 +40,7 @@ namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// Forward declarations
+class Caches;
class OpenGLRenderer;
class DisplayList;
class DeferredDisplayList;
@@ -221,50 +222,19 @@ struct Layer {
ANDROID_API void setColorFilter(SkiaColorFilter* filter);
- inline void bindTexture() const {
- if (texture.id) {
- glBindTexture(renderTarget, texture.id);
- }
- }
-
- inline void bindStencilRenderBuffer() const {
- if (stencil) {
- stencil->bind();
- }
- }
+ void bindStencilRenderBuffer() const;
- inline void generateTexture() {
- if (!texture.id) {
- glGenTextures(1, &texture.id);
- }
- }
-
- inline void deleteTexture() {
- if (texture.id) {
- glDeleteTextures(1, &texture.id);
- texture.id = 0;
- }
- }
+ void bindTexture() const;
+ void generateTexture();
+ void allocateTexture();
+ void deleteTexture();
/**
* When the caller frees the texture itself, the caller
* must call this method to tell this layer that it lost
* the texture.
*/
- void clearTexture() {
- texture.id = 0;
- }
-
- inline void allocateTexture() {
-#if DEBUG_LAYERS
- ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight());
-#endif
- if (texture.id) {
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glTexImage2D(renderTarget, 0, GL_RGBA, getWidth(), getHeight(), 0,
- GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- }
- }
+ ANDROID_API void clearTexture();
inline mat4& getTexTransform() {
return texTransform;
@@ -320,6 +290,8 @@ struct Layer {
bool hasDrawnSinceUpdate;
private:
+ Caches& caches;
+
/**
* 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/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 3e55fff..cfb1e97 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -436,7 +436,7 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
if ((error = glGetError()) != GL_NO_ERROR) goto error;
caches.activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, texture);
+ caches.bindTexture(texture);
glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
@@ -467,7 +467,7 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
mat4 texTransform(layer->getTexTransform());
mat4 invert;
- invert.translate(0.0f, 1.0f, 0.0f);
+ invert.translate(0.0f, 1.0f);
invert.scale(1.0f, -1.0f, 1.0f);
layer->getTexTransform().multiply(invert);
@@ -498,7 +498,7 @@ error:
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
layer->setAlpha(alpha, mode);
layer->setFbo(previousLayerFbo);
- glDeleteTextures(1, &texture);
+ caches.deleteTexture(texture);
caches.fboCache.put(fbo);
glViewport(previousViewport[0], previousViewport[1],
previousViewport[2], previousViewport[3]);
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 75e280c..df744be 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -128,10 +128,27 @@ public:
void multiply(float v);
- void translate(float x, float y, float z) {
- Matrix4 u;
- u.loadTranslate(x, y, z);
- multiply(u);
+ void translate(float x, float y) {
+ if ((getType() & sGeometryMask) == kTypeTranslate) {
+ data[kTranslateX] += x;
+ data[kTranslateY] += y;
+ } else {
+ // Doing a translation will only affect the translate bit of the type
+ // Save the type
+ uint32_t type = mType;
+
+ Matrix4 u;
+ u.loadTranslate(x, y, 0.0f);
+ multiply(u);
+
+ // Restore the type and fix the translate bit
+ mType = type;
+ if (data[kTranslateX] != 0.0f || data[kTranslateY] != 0.0f) {
+ mType |= kTypeTranslate;
+ } else {
+ mType &= ~kTypeTranslate;
+ }
+ }
}
void scale(float sx, float sy, float sz) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index ddb190e..d95a62c 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -21,7 +21,6 @@
#include <sys/types.h>
#include <SkCanvas.h>
-#include <SkPathMeasure.h>
#include <SkTypeface.h>
#include <utils/Log.h>
@@ -120,6 +119,7 @@ OpenGLRenderer::OpenGLRenderer():
mFirstSnapshot = new Snapshot;
mFrameStarted = false;
+ mCountOverdraw = false;
mScissorOptimizationDisabled = false;
}
@@ -222,6 +222,7 @@ status_t OpenGLRenderer::prepare(bool opaque) {
status_t OpenGLRenderer::prepareDirty(float left, float top,
float right, float bottom, bool opaque) {
+
setupFrameState(left, top, right, bottom, opaque);
// Layer renderers will start the frame immediately
@@ -253,7 +254,7 @@ void OpenGLRenderer::discardFramebuffer(float left, float top, float right, floa
}
status_t OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) {
- if (!opaque) {
+ if (!opaque || mCountOverdraw) {
mCaches.enableScissor();
mCaches.setScissor(left, mSnapshot->height - bottom, right - left, bottom - top);
glClear(GL_COLOR_BUFFER_BIT);
@@ -335,6 +336,10 @@ void OpenGLRenderer::finish() {
#endif
}
+ if (mCountOverdraw) {
+ countOverdraw();
+ }
+
mFrameStarted = false;
}
@@ -366,6 +371,7 @@ void OpenGLRenderer::resume() {
dirtyClip();
mCaches.activeTexture(0);
+ mCaches.resetBoundTextures();
mCaches.blend = true;
glEnable(GL_BLEND);
@@ -459,7 +465,7 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
info.height = getSnapshot()->height;
getSnapshot()->transform->copyTo(&info.transform[0]);
- status_t result = (*functor)(DrawGlInfo::kModeDraw, &info) | DrawGlInfo::kStatusDrew;
+ status_t result = (*functor)(DrawGlInfo::kModeDraw, &info);
if (result != DrawGlInfo::kStatusDone) {
Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
@@ -471,7 +477,7 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
}
resume();
- return result;
+ return result | DrawGlInfo::kStatusDrew;
}
///////////////////////////////////////////////////////////////////////////////
@@ -524,6 +530,21 @@ void OpenGLRenderer::renderOverdraw() {
}
}
+void OpenGLRenderer::countOverdraw() {
+ size_t count = mWidth * mHeight;
+ uint32_t* buffer = new uint32_t[count];
+ glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]);
+
+ size_t total = 0;
+ for (size_t i = 0; i < count; i++) {
+ total += buffer[i] & 0xff;
+ }
+
+ mOverdraw = total / float(count);
+
+ delete[] buffer;
+}
+
///////////////////////////////////////////////////////////////////////////////
// Layers
///////////////////////////////////////////////////////////////////////////////
@@ -1339,11 +1360,15 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
// state has bounds initialized in local coordinates
if (!state.mBounds.isEmpty()) {
currentMatrix.mapRect(state.mBounds);
+ state.mClipped = !currentClip.contains(state.mBounds);
if (!state.mBounds.intersect(currentClip)) {
// quick rejected
return true;
}
} else {
+ // If we don't have bounds, let's assume we're clipped
+ // to prevent merging
+ state.mClipped = true;
state.mBounds.set(currentClip);
}
}
@@ -1382,7 +1407,7 @@ void OpenGLRenderer::setFullScreenClip() {
///////////////////////////////////////////////////////////////////////////////
void OpenGLRenderer::translate(float dx, float dy) {
- currentTransform().translate(dx, dy, 0.0f);
+ currentTransform().translate(dx, dy);
}
void OpenGLRenderer::rotate(float degrees) {
@@ -1656,6 +1681,8 @@ void OpenGLRenderer::setupDraw(bool clear) {
mDescription.hasDebugHighlight = !mCaches.debugOverdraw &&
mCaches.debugStencilClip == Caches::kStencilShowHighlight &&
mCaches.stencil.isTestEnabled();
+
+ mDescription.emulateStencil = mCountOverdraw;
}
void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) {
@@ -1681,11 +1708,6 @@ void OpenGLRenderer::setupDrawAA() {
mDescription.isAA = true;
}
-void OpenGLRenderer::setupDrawPoint(float pointSize) {
- mDescription.isPoint = true;
- mDescription.pointSize = pointSize;
-}
-
void OpenGLRenderer::setupDrawColor(int color, int alpha) {
mColorA = alpha / 255.0f;
mColorR = mColorA * ((color >> 16) & 0xFF) / 255.0f;
@@ -1800,11 +1822,6 @@ void OpenGLRenderer::setupDrawModelView(float left, float top, float right, floa
}
}
-void OpenGLRenderer::setupDrawPointUniforms() {
- int slot = mCaches.currentProgram->getUniform("pointSize");
- glUniform1f(slot, mDescription.pointSize);
-}
-
void OpenGLRenderer::setupDrawColorUniforms() {
if ((mColorSet && !mDrawModifiers.mShader) || (mDrawModifiers.mShader && mSetShaderColor)) {
mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
@@ -1873,7 +1890,7 @@ void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) {
void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
bool force = false;
- if (!vertices) {
+ if (!vertices || vbo) {
force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
} else {
force = mCaches.unbindMeshBuffer();
@@ -1904,8 +1921,18 @@ void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLvoid*
mCaches.unbindIndicesBuffer();
}
-void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords) {
- bool force = mCaches.unbindMeshBuffer();
+void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) {
+ bool force = false;
+ // If vbo is != 0 we want to treat the vertices parameter as an offset inside
+ // a VBO. However, if vertices is set to NULL and vbo == 0 then we want to
+ // use the default VBO found in Caches
+ if (!vertices || vbo) {
+ force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo);
+ } else {
+ force = mCaches.unbindMeshBuffer();
+ }
+ mCaches.bindIndicesBuffer();
+
mCaches.bindPositionVertexPointer(force, vertices);
if (mCaches.currentProgram->texCoords >= 0) {
mCaches.bindTexCoordsVertexPointer(force, texCoords);
@@ -1980,20 +2007,23 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, Sk
texture->setFilter(FILTER(paint), true);
}
+ // No need to check for a UV mapper on the texture object, only ARGB_8888
+ // bitmaps get packed in the atlas
drawAlpha8TextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
- paint != NULL, color, alpha, mode, (GLvoid*) NULL,
- (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
+ paint != NULL, color, alpha, mode, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset,
+ GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform);
}
status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices,
- const Rect& bounds, SkPaint* paint) {
+ bool transformed, const Rect& bounds, SkPaint* paint) {
// merged draw operations don't need scissor, but clip should still be valid
mCaches.setScissorEnabled(mScissorOptimizationDisabled);
mCaches.activeTexture(0);
- Texture* texture = mCaches.textureCache.get(bitmap);
+ Texture* texture = getTexture(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
+
const AutoTexture autoCleanup(texture);
int alpha;
@@ -2001,7 +2031,7 @@ status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureV
getAlphaAndMode(paint, &alpha, &mode);
texture->setWrap(GL_CLAMP_TO_EDGE, true);
- texture->setFilter(GL_NEAREST, true); // merged ops are always pure-translation for now
+ texture->setFilter(transformed ? FILTER(paint) : GL_NEAREST, true);
const float x = (int) floorf(bounds.left + 0.5f);
const float y = (int) floorf(bounds.top + 0.5f);
@@ -2030,7 +2060,7 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkP
}
mCaches.activeTexture(0);
- Texture* texture = mCaches.textureCache.get(bitmap);
+ Texture* texture = getTexture(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
@@ -2053,7 +2083,7 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint*
}
mCaches.activeTexture(0);
- Texture* texture = mCaches.textureCache.get(bitmap);
+ Texture* texture = getTexture(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
@@ -2116,6 +2146,10 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes
cleanupColors = true;
}
+ mCaches.activeTexture(0);
+ Texture* texture = mCaches.assetAtlas.getEntryTexture(bitmap);
+ const UvMapper& mapper(getMapper(texture));
+
for (int32_t y = 0; y < meshHeight; y++) {
for (int32_t x = 0; x < meshWidth; x++) {
uint32_t i = (y * (meshWidth + 1) + x) * 2;
@@ -2125,6 +2159,8 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes
float v1 = float(y) / meshHeight;
float v2 = float(y + 1) / meshHeight;
+ mapper.map(u1, v1, u2, v2);
+
int ax = i + (meshWidth + 1) * 2;
int ay = ax + 1;
int bx = i;
@@ -2154,11 +2190,12 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes
return DrawGlInfo::kStatusDone;
}
- mCaches.activeTexture(0);
- Texture* texture = mCaches.textureCache.get(bitmap);
if (!texture) {
- if (cleanupColors) delete[] colors;
- return DrawGlInfo::kStatusDone;
+ texture = mCaches.textureCache.get(bitmap);
+ if (!texture) {
+ if (cleanupColors) delete[] colors;
+ return DrawGlInfo::kStatusDone;
+ }
}
const AutoTexture autoCleanup(texture);
@@ -2211,17 +2248,19 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
}
mCaches.activeTexture(0);
- Texture* texture = mCaches.textureCache.get(bitmap);
+ Texture* texture = getTexture(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
const float width = texture->width;
const float height = texture->height;
- const float u1 = fmax(0.0f, srcLeft / width);
- const float v1 = fmax(0.0f, srcTop / height);
- const float u2 = fmin(1.0f, srcRight / width);
- const float v2 = fmin(1.0f, srcBottom / height);
+ float u1 = fmax(0.0f, srcLeft / width);
+ float v1 = fmax(0.0f, srcTop / height);
+ float u2 = fmin(1.0f, srcRight / width);
+ float v2 = fmin(1.0f, srcBottom / height);
+
+ getMapper(texture).map(u1, v1, u2, v2);
mCaches.unbindMeshBuffer();
resetDrawTextureTexCoords(u1, v1, u2, v2);
@@ -2292,34 +2331,37 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
return DrawGlInfo::kStatusDrew;
}
-status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
float left, float top, float right, float bottom, SkPaint* paint) {
int alpha;
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
- return drawPatch(bitmap, xDivs, yDivs, colors, width, height, numColors,
- left, top, right, bottom, alpha, mode);
-}
-
-status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
- float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode) {
if (quickReject(left, top, right, bottom)) {
return DrawGlInfo::kStatusDone;
}
- alpha *= mSnapshot->alpha;
+ AssetAtlas::Entry* entry = mCaches.assetAtlas.getEntry(bitmap);
+ const Patch* mesh = mCaches.patchCache.get(entry, bitmap->width(), bitmap->height(),
+ right - left, bottom - top, patch);
- const Patch* mesh = mCaches.patchCache.get(bitmap->width(), bitmap->height(),
- right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors);
+ return drawPatch(bitmap, mesh, entry, left, top, right, bottom, alpha, mode);
+}
+
+status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh,
+ AssetAtlas::Entry* entry, float left, float top, float right, float bottom,
+ int alpha, SkXfermode::Mode mode) {
+
+ if (quickReject(left, top, right, bottom)) {
+ return DrawGlInfo::kStatusDone;
+ }
if (CC_LIKELY(mesh && mesh->verticesCount > 0)) {
mCaches.activeTexture(0);
- Texture* texture = mCaches.textureCache.get(bitmap);
+ Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
+
texture->setWrap(GL_CLAMP_TO_EDGE, true);
texture->setFilter(GL_LINEAR, true);
@@ -2342,19 +2384,23 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const
}
}
+ alpha *= mSnapshot->alpha;
+
if (CC_LIKELY(pureTranslate)) {
const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
- drawTextureMesh(x, y, x + right - left, y + bottom - top, texture->id, alpha / 255.0f,
- mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset,
- GL_TRIANGLES, mesh->verticesCount, false, true, mesh->meshBuffer,
- true, !mesh->hasEmptyQuads);
+ right = x + right - left;
+ bottom = y + bottom - top;
+ drawIndexedTextureMesh(x, y, right, bottom, texture->id, alpha / 255.0f,
+ mode, texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset,
+ GL_TRIANGLES, mesh->indexCount, false, true,
+ mCaches.patchCache.getMeshBuffer(), true, !mesh->hasEmptyQuads);
} else {
- drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f,
- mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset,
- GL_TRIANGLES, mesh->verticesCount, false, false, mesh->meshBuffer,
- true, !mesh->hasEmptyQuads);
+ drawIndexedTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f,
+ mode, texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset,
+ GL_TRIANGLES, mesh->indexCount, false, false,
+ mCaches.patchCache.getMeshBuffer(), true, !mesh->hasEmptyQuads);
}
}
@@ -2363,7 +2409,7 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const
status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint,
bool useOffset) {
- if (!vertexBuffer.getSize()) {
+ if (!vertexBuffer.getVertexCount()) {
// no vertices to draw
return DrawGlInfo::kStatusDone;
}
@@ -2401,7 +2447,7 @@ status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPa
glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
}
- glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getSize());
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount());
if (isAA) {
glDisableVertexAttribArray(alphaSlot);
@@ -2464,65 +2510,22 @@ status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
}
status_t OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) {
- if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
-
- // TODO: The paint's cap style defines whether the points are square or circular
- // TODO: Handle AA for round points
+ if (mSnapshot->isIgnored() || count < 2) return DrawGlInfo::kStatusDone;
- // A stroke width of 0 has a special meaning in Skia:
- // it draws an unscaled 1px point
- float strokeWidth = paint->getStrokeWidth();
- const bool isHairLine = paint->getStrokeWidth() == 0.0f;
- if (isHairLine) {
- // Now that we know it's hairline, we can set the effective width, to be used later
- strokeWidth = 1.0f;
- }
- const float halfWidth = strokeWidth / 2;
+ count &= ~0x1; // round down to nearest two
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
- int verticesCount = count >> 1;
- int generatedVerticesCount = 0;
-
- TextureVertex pointsData[verticesCount];
- TextureVertex* vertex = &pointsData[0];
-
- // TODO: We should optimize this method to not generate vertices for points
- // that lie outside of the clip.
- mCaches.enableScissor();
-
- setupDraw();
- setupDrawNoTexture();
- setupDrawPoint(strokeWidth);
- setupDrawColor(paint->getColor(), alpha);
- setupDrawColorFilter();
- setupDrawShader();
- setupDrawBlending(mode);
- setupDrawProgram();
- setupDrawModelViewIdentity(true);
- setupDrawColorUniforms();
- setupDrawColorFilterUniforms();
- setupDrawPointUniforms();
- setupDrawShaderIdentityUniforms();
- setupDrawMesh(vertex);
-
- for (int i = 0; i < count; i += 2) {
- TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f);
- generatedVerticesCount++;
-
- float left = points[i] - halfWidth;
- float right = points[i] + halfWidth;
- float top = points[i + 1] - halfWidth;
- float bottom = points [i + 1] + halfWidth;
+ VertexBuffer buffer;
+ SkRect bounds;
+ PathTessellator::tessellatePoints(points, count, paint, mSnapshot->transform, bounds, buffer);
- dirtyLayer(left, top, right, bottom, currentTransform());
+ if (quickReject(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) {
+ return DrawGlInfo::kStatusDone;
}
- glDrawArrays(GL_POINTS, 0, generatedVerticesCount);
+ dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
- return DrawGlInfo::kStatusDrew;
+ bool useOffset = !paint->isAntiAlias();
+ return drawVertexBuffer(buffer, paint, useOffset);
}
status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
@@ -2853,8 +2856,8 @@ mat4 OpenGLRenderer::findBestFontTransform(const mat4& transform) const {
return fontTransform;
}
-status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
- float x, float y, const float* positions, SkPaint* paint, float length,
+status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
+ const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode) {
if (drawOpMode == kDrawOpMode_Immediate &&
@@ -2862,24 +2865,8 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
return DrawGlInfo::kStatusDone;
}
- if (length < 0.0f) length = paint->measureText(text, bytesCount);
- switch (paint->getTextAlign()) {
- case SkPaint::kCenter_Align:
- x -= length / 2.0f;
- break;
- case SkPaint::kRight_Align:
- x -= length;
- break;
- default:
- break;
- }
-
- SkPaint::FontMetrics metrics;
- paint->getFontMetrics(&metrics, 0.0f);
if (drawOpMode == kDrawOpMode_Immediate) {
- if (quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom)) {
- return DrawGlInfo::kStatusDone;
- }
+ if (quickReject(bounds)) return DrawGlInfo::kStatusDone;
} else {
// merged draw operations don't need scissor, but clip should still be valid
mCaches.setScissorEnabled(mScissorOptimizationDisabled);
@@ -2930,7 +2917,7 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
// TODO: Implement better clipping for scaled/rotated text
const Rect* clip = !pureTranslate ? NULL : mSnapshot->clipRect;
- Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
+ Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
bool status;
TextSetupFunctor functor(*this, x, y, pureTranslate, alpha, mode, paint);
@@ -2941,20 +2928,20 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
SkPaint paintCopy(*paint);
paintCopy.setTextAlign(SkPaint::kLeft_Align);
status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y,
- positions, hasActiveLayer ? &bounds : NULL, &functor, forceFinish);
+ positions, hasActiveLayer ? &layerBounds : NULL, &functor, forceFinish);
} else {
status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
- positions, hasActiveLayer ? &bounds : NULL, &functor, forceFinish);
+ positions, hasActiveLayer ? &layerBounds : NULL, &functor, forceFinish);
}
if ((status || drawOpMode != kDrawOpMode_Immediate) && hasActiveLayer) {
if (!pureTranslate) {
- transform.mapRect(bounds);
+ transform.mapRect(layerBounds);
}
- dirtyLayerUnchecked(bounds, getRegion());
+ dirtyLayerUnchecked(layerBounds, getRegion());
}
- drawTextDecorations(text, bytesCount, length, oldX, oldY, paint);
+ drawTextDecorations(text, bytesCount, totalAdvance, oldX, oldY, paint);
return DrawGlInfo::kStatusDrew;
}
@@ -3128,7 +3115,7 @@ void OpenGLRenderer::resetShader() {
void OpenGLRenderer::setupShader(SkiaShader* shader) {
mDrawModifiers.mShader = shader;
if (mDrawModifiers.mShader) {
- mDrawModifiers.mShader->set(&mCaches.textureCache, &mCaches.gradientCache);
+ mDrawModifiers.mShader->setCaches(mCaches);
}
}
@@ -3196,6 +3183,14 @@ SkPaint* OpenGLRenderer::filterPaint(SkPaint* paint) {
// Drawing implementation
///////////////////////////////////////////////////////////////////////////////
+Texture* OpenGLRenderer::getTexture(SkBitmap* bitmap) {
+ Texture* texture = mCaches.assetAtlas.getEntryTexture(bitmap);
+ if (!texture) {
+ return mCaches.textureCache.get(bitmap);
+ }
+ return texture;
+}
+
void OpenGLRenderer::drawPathTexture(const PathTexture* texture,
float x, float y, SkPaint* paint) {
if (quickReject(x, y, x + texture->width, y + texture->height)) {
@@ -3230,17 +3225,12 @@ void OpenGLRenderer::drawPathTexture(const PathTexture* texture,
#define kStdUnderline_Offset (1.0f / 9.0f)
#define kStdUnderline_Thickness (1.0f / 18.0f)
-void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float length,
+void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float underlineWidth,
float x, float y, SkPaint* paint) {
// Handle underline and strike-through
uint32_t flags = paint->getFlags();
if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
SkPaint paintCopy(*paint);
- float underlineWidth = length;
- // If length is > 0.0f, we already measured the text for the text alignment
- if (length <= 0.0f) {
- underlineWidth = paintCopy.measureText(text, bytesCount);
- }
if (CC_LIKELY(underlineWidth > 0.0f)) {
const float textSize = paintCopy.getTextSize();
@@ -3389,19 +3379,35 @@ void OpenGLRenderer::drawTextureRect(float left, float top, float right, float b
texture->setWrap(GL_CLAMP_TO_EDGE, true);
+ GLvoid* vertices = (GLvoid*) NULL;
+ GLvoid* texCoords = (GLvoid*) gMeshTextureOffset;
+
+ if (texture->uvMapper) {
+ vertices = &mMeshVertices[0].position[0];
+ texCoords = &mMeshVertices[0].texture[0];
+
+ Rect uvs(0.0f, 0.0f, 1.0f, 1.0f);
+ texture->uvMapper->map(uvs);
+
+ resetDrawTextureTexCoords(uvs.left, uvs.top, uvs.right, uvs.bottom);
+ }
+
if (CC_LIKELY(currentTransform().isPureTranslate())) {
const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f);
const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f);
texture->setFilter(GL_NEAREST, true);
drawTextureMesh(x, y, x + texture->width, y + texture->height, texture->id,
- alpha / 255.0f, mode, texture->blend, (GLvoid*) NULL,
- (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount, false, true);
+ alpha / 255.0f, mode, texture->blend, vertices, texCoords,
+ GL_TRIANGLE_STRIP, gMeshCount, false, true);
} else {
texture->setFilter(FILTER(paint), true);
drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode,
- texture->blend, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset,
- GL_TRIANGLE_STRIP, gMeshCount);
+ texture->blend, vertices, texCoords, GL_TRIANGLE_STRIP, gMeshCount);
+ }
+
+ if (texture->uvMapper) {
+ resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
}
}
@@ -3438,6 +3444,33 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b
finishDrawTexture();
}
+void OpenGLRenderer::drawIndexedTextureMesh(float left, float top, float right, float bottom,
+ GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
+ GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
+ bool swapSrcDst, bool ignoreTransform, GLuint vbo, bool ignoreScale, bool dirty) {
+
+ setupDraw();
+ setupDrawWithTexture();
+ setupDrawColor(alpha, alpha, alpha, alpha);
+ setupDrawColorFilter();
+ setupDrawBlending(blend, mode, swapSrcDst);
+ setupDrawProgram();
+ if (!dirty) setupDrawDirtyRegionsDisabled();
+ if (!ignoreScale) {
+ setupDrawModelView(left, top, right, bottom, ignoreTransform);
+ } else {
+ setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform);
+ }
+ setupDrawTexture(texture);
+ setupDrawPureColorUniforms();
+ setupDrawColorFilterUniforms();
+ setupDrawMeshIndices(vertices, texCoords, vbo);
+
+ glDrawElements(drawMode, elementsCount, GL_UNSIGNED_SHORT, NULL);
+
+ finishDrawTexture();
+}
+
void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, float bottom,
GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode,
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
@@ -3471,6 +3504,19 @@ void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, f
void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
ProgramDescription& description, bool swapSrcDst) {
+ if (mCountOverdraw) {
+ if (!mCaches.blend) glEnable(GL_BLEND);
+ if (mCaches.lastSrcMode != GL_ONE || mCaches.lastDstMode != GL_ONE) {
+ glBlendFunc(GL_ONE, GL_ONE);
+ }
+
+ mCaches.blend = true;
+ mCaches.lastSrcMode = GL_ONE;
+ mCaches.lastDstMode = GL_ONE;
+
+ return;
+ }
+
blend = blend || mode != SkXfermode::kSrcOver_Mode;
if (blend) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index a0ad888..ce4ce42 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -34,6 +34,8 @@
#include <cutils/compiler.h>
+#include <androidfw/ResourceTypes.h>
+
#include "Debug.h"
#include "Extensions.h"
#include "Matrix.h"
@@ -43,6 +45,7 @@
#include "Vertex.h"
#include "SkiaShader.h"
#include "SkiaColorFilter.h"
+#include "UvMapper.h"
#include "Caches.h"
namespace android {
@@ -78,11 +81,13 @@ enum DrawOpMode {
};
struct DeferredDisplayState {
- Rect mBounds; // global op bounds, mapped by mMatrix to be in screen space coordinates, clipped.
+ // global op bounds, mapped by mMatrix to be in screen space coordinates, clipped
+ Rect mBounds;
// the below are set and used by the OpenGLRenderer at record and deferred playback
bool mClipValid;
Rect mClip;
+ bool mClipped;
mat4 mMatrix;
DrawModifiers mDrawModifiers;
float mAlpha;
@@ -188,6 +193,14 @@ public:
*/
virtual void resume();
+ ANDROID_API void setCountOverdrawEnabled(bool enabled) {
+ mCountOverdraw = enabled;
+ }
+
+ ANDROID_API float getOverdraw() {
+ return mCountOverdraw ? mOverdraw : 0.0f;
+ }
+
ANDROID_API status_t invokeFunctors(Rect& dirty);
ANDROID_API void detachFunctor(Functor* functor);
ANDROID_API void attachFunctor(Functor* functor);
@@ -229,6 +242,9 @@ public:
ANDROID_API const Rect& getClipBounds();
ANDROID_API bool quickReject(float left, float top, float right, float bottom);
+ bool quickReject(const Rect& bounds) {
+ return quickReject(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ }
bool quickRejectNoScissor(float left, float top, float right, float bottom);
virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
virtual bool clipPath(SkPath* path, SkRegion::Op op);
@@ -240,7 +256,7 @@ public:
virtual status_t drawLayer(Layer* layer, float x, float y);
virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
status_t drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices,
- const Rect& bounds, SkPaint* paint);
+ bool transformed, const Rect& bounds, SkPaint* paint);
virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
virtual status_t drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
@@ -248,11 +264,9 @@ public:
virtual status_t drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint);
virtual status_t drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight,
float* vertices, int* colors, SkPaint* paint);
- virtual status_t drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+ virtual status_t drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
float left, float top, float right, float bottom, SkPaint* paint);
- status_t drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
+ status_t drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry,
float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode);
virtual status_t drawColor(int color, SkXfermode::Mode mode);
virtual status_t drawRect(float left, float top, float right, float bottom, SkPaint* paint);
@@ -270,7 +284,7 @@ public:
virtual status_t drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint);
virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float length = -1.0f,
+ const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode = kDrawOpMode_Immediate);
virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
@@ -798,6 +812,12 @@ private:
bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0,
bool ignoreScale = false, bool dirty = true);
+ void drawIndexedTextureMesh(float left, float top, float right, float bottom, GLuint texture,
+ float alpha, SkXfermode::Mode mode, bool blend,
+ GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
+ bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0,
+ bool ignoreScale = false, bool dirty = true);
+
void drawAlpha8TextureMesh(float left, float top, float right, float bottom,
GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode,
GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount,
@@ -808,12 +828,12 @@ private:
*
* @param text The text to decor
* @param bytesCount The number of bytes in the text
- * @param length The length in pixels of the text, can be <= 0.0f to force a measurement
+ * @param totalAdvance The total advance in pixels, defines underline/strikethrough length
* @param x The x coordinate where the text will be drawn
* @param y The y coordinate where the text will be drawn
* @param paint The paint to draw the text with
*/
- void drawTextDecorations(const char* text, int bytesCount, float length,
+ void drawTextDecorations(const char* text, int bytesCount, float totalAdvance,
float x, float y, SkPaint* paint);
/**
@@ -868,7 +888,7 @@ private:
* prior to calling this method.
*/
inline void bindTexture(GLuint texture) {
- glBindTexture(GL_TEXTURE_2D, texture);
+ mCaches.bindTexture(texture);
}
/**
@@ -876,7 +896,7 @@ private:
* prior to calling this method.
*/
inline void bindExternalTexture(GLuint texture) {
- glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
+ mCaches.bindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
}
/**
@@ -911,7 +931,6 @@ private:
void setupDrawWithExternalTexture();
void setupDrawNoTexture();
void setupDrawAA();
- void setupDrawPoint(float pointSize);
void setupDrawColor(int color, int alpha);
void setupDrawColor(float r, float g, float b, float a);
void setupDrawAlpha8Color(int color, int alpha);
@@ -929,7 +948,6 @@ private:
bool ignoreTransform = false, bool ignoreModelView = false);
void setupDrawModelViewTranslate(float left, float top, float right, float bottom,
bool ignoreTransform = false);
- void setupDrawPointUniforms();
void setupDrawColorUniforms();
void setupDrawPureColorUniforms();
void setupDrawShaderIdentityUniforms();
@@ -943,7 +961,7 @@ private:
void setupDrawTextGammaUniforms();
void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0);
void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLvoid* colors);
- void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords);
+ void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, GLuint vbo = 0);
void setupDrawVertices(GLvoid* vertices);
void finishDrawTexture();
void accountForClear(SkXfermode::Mode mode);
@@ -973,6 +991,7 @@ private:
void debugOverdraw(bool enable, bool clear);
void renderOverdraw();
+ void countOverdraw();
/**
* Should be invoked every time the glScissor is modified.
@@ -985,6 +1004,17 @@ private:
return *mSnapshot->transform;
}
+ inline const UvMapper& getMapper(const Texture* texture) {
+ return texture && texture->uvMapper ? *texture->uvMapper : mUvMapper;
+ }
+
+ /**
+ * Returns a texture object for the specified bitmap. The texture can
+ * come from the texture cache or an atlas. If this method returns
+ * NULL, the texture could not be found and/or allocated.
+ */
+ Texture* getTexture(SkBitmap* bitmap);
+
// Dimensions of the drawing surface
int mWidth, mHeight;
@@ -1010,6 +1040,9 @@ private:
// Used to draw textured quads
TextureVertex mMeshVertices[4];
+ // Default UV mapper
+ const UvMapper mUvMapper;
+
// shader, filters, and shadow
DrawModifiers mDrawModifiers;
SkPaint mFilteredPaint;
@@ -1050,6 +1083,11 @@ private:
// No-ops start/endTiling when set
bool mSuppressTiling;
+ // If true, this renderer will setup drawing to emulate
+ // an increment stencil buffer in the color buffer
+ bool mCountOverdraw;
+ float mOverdraw;
+
// Optional name of the renderer
String8 mName;
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 45c619e..6b0734a 100644
--- a/libs/hwui/Patch.cpp
+++ b/libs/hwui/Patch.cpp
@@ -20,9 +20,10 @@
#include <utils/Log.h>
-#include "Patch.h"
#include "Caches.h"
+#include "Patch.h"
#include "Properties.h"
+#include "UvMapper.h"
namespace android {
namespace uirenderer {
@@ -31,90 +32,61 @@ namespace uirenderer {
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-Patch::Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads):
- mXCount(xCount), mYCount(yCount), mEmptyQuads(emptyQuads) {
- // Initialized with the maximum number of vertices we will need
- // 2 triangles per patch, 3 vertices per triangle
- uint32_t maxVertices = ((xCount + 1) * (yCount + 1) - emptyQuads) * 2 * 3;
- mVertices = new TextureVertex[maxVertices];
- mAllocatedVerticesCount = 0;
-
- verticesCount = 0;
- hasEmptyQuads = emptyQuads > 0;
-
- mColorKey = 0;
- mXDivs = new int32_t[mXCount];
- mYDivs = new int32_t[mYCount];
-
- PATCH_LOGD(" patch: xCount = %d, yCount = %d, emptyQuads = %d, max vertices = %d",
- xCount, yCount, emptyQuads, maxVertices);
-
- glGenBuffers(1, &meshBuffer);
+Patch::Patch(): verticesCount(0), indexCount(0), hasEmptyQuads(false) {
}
Patch::~Patch() {
- delete[] mVertices;
- delete[] mXDivs;
- delete[] mYDivs;
- glDeleteBuffers(1, &meshBuffer);
}
///////////////////////////////////////////////////////////////////////////////
-// Patch management
+// Vertices management
///////////////////////////////////////////////////////////////////////////////
-void Patch::copy(const int32_t* xDivs, const int32_t* yDivs) {
- memcpy(mXDivs, xDivs, mXCount * sizeof(int32_t));
- memcpy(mYDivs, yDivs, mYCount * sizeof(int32_t));
+uint32_t Patch::getSize() const {
+ return verticesCount * sizeof(TextureVertex);
}
-void Patch::updateColorKey(const uint32_t colorKey) {
- mColorKey = colorKey;
+TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeight,
+ float left, float top, float right, float bottom, const Res_png_9patch* patch) {
+ UvMapper mapper;
+ return createMesh(bitmapWidth, bitmapHeight, left, top, right, bottom, mapper, patch);
}
-bool Patch::matches(const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t colorKey, const int8_t emptyQuads) {
+TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeight,
+ float left, float top, float right, float bottom,
+ const UvMapper& mapper, const Res_png_9patch* patch) {
- bool matches = true;
+ const uint32_t* colors = &patch->colors[0];
+ const int8_t numColors = patch->numColors;
- if (mEmptyQuads != emptyQuads) {
- mEmptyQuads = emptyQuads;
- hasEmptyQuads = emptyQuads > 0;
- matches = false;
- }
-
- if (mColorKey != colorKey) {
- updateColorKey(colorKey);
- matches = false;
- }
-
- if (memcmp(mXDivs, xDivs, mXCount * sizeof(int32_t))) {
- memcpy(mXDivs, xDivs, mXCount * sizeof(int32_t));
- matches = false;
+ mColorKey = 0;
+ int8_t emptyQuads = 0;
+
+ if (uint8_t(numColors) < sizeof(uint32_t) * 4) {
+ for (int8_t i = 0; i < numColors; i++) {
+ if (colors[i] == 0x0) {
+ emptyQuads++;
+ mColorKey |= 0x1 << i;
+ }
+ }
}
- if (memcmp(mYDivs, yDivs, mYCount * sizeof(int32_t))) {
- memcpy(mYDivs, yDivs, mYCount * sizeof(int32_t));
- matches = false;
- }
+ hasEmptyQuads = emptyQuads > 0;
- return matches;
-}
+ uint32_t xCount = patch->numXDivs;
+ uint32_t yCount = patch->numYDivs;
-///////////////////////////////////////////////////////////////////////////////
-// Vertices management
-///////////////////////////////////////////////////////////////////////////////
+ uint32_t maxVertices = ((xCount + 1) * (yCount + 1) - emptyQuads) * 4;
+ if (maxVertices == 0) return NULL;
-void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
- float left, float top, float right, float bottom) {
- if (hasEmptyQuads) quads.clear();
+ TextureVertex* vertices = new TextureVertex[maxVertices];
+ TextureVertex* vertex = vertices;
- // Reset the vertices count here, we will count exactly how many
- // vertices we actually need when generating the quads
- verticesCount = 0;
+ const int32_t* xDivs = patch->xDivs;
+ const int32_t* yDivs = patch->yDivs;
- const uint32_t xStretchCount = (mXCount + 1) >> 1;
- const uint32_t yStretchCount = (mYCount + 1) >> 1;
+ const uint32_t xStretchCount = (xCount + 1) >> 1;
+ const uint32_t yStretchCount = (yCount + 1) >> 1;
float stretchX = 0.0f;
float stretchY = 0.0f;
@@ -124,8 +96,8 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
if (xStretchCount > 0) {
uint32_t stretchSize = 0;
- for (uint32_t i = 1; i < mXCount; i += 2) {
- stretchSize += mXDivs[i] - mXDivs[i - 1];
+ for (uint32_t i = 1; i < xCount; i += 2) {
+ stretchSize += xDivs[i] - xDivs[i - 1];
}
const float xStretchTex = stretchSize;
const float fixed = bitmapWidth - stretchSize;
@@ -136,8 +108,8 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
if (yStretchCount > 0) {
uint32_t stretchSize = 0;
- for (uint32_t i = 1; i < mYCount; i += 2) {
- stretchSize += mYDivs[i] - mYDivs[i - 1];
+ for (uint32_t i = 1; i < yCount; i += 2) {
+ stretchSize += yDivs[i] - yDivs[i - 1];
}
const float yStretchTex = stretchSize;
const float fixed = bitmapHeight - stretchSize;
@@ -146,7 +118,6 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
rescaleY = fixed == 0.0f ? 0.0f : fminf(fmaxf(bottom - top, 0.0f) / fixed, 1.0f);
}
- TextureVertex* vertex = mVertices;
uint32_t quadCount = 0;
float previousStepY = 0.0f;
@@ -155,8 +126,10 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
float y2 = 0.0f;
float v1 = 0.0f;
- for (uint32_t i = 0; i < mYCount; i++) {
- float stepY = mYDivs[i];
+ mUvMapper = mapper;
+
+ for (uint32_t i = 0; i < yCount; i++) {
+ float stepY = yDivs[i];
const float segment = stepY - previousStepY;
if (i & 1) {
@@ -170,15 +143,8 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
v1 += vOffset / bitmapHeight;
if (stepY > 0.0f) {
-#if DEBUG_EXPLODE_PATCHES
- y1 += i * EXPLODE_GAP;
- y2 += i * EXPLODE_GAP;
-#endif
- generateRow(vertex, y1, y2, v1, v2, stretchX, rescaleX, right - left,
- bitmapWidth, quadCount);
-#if DEBUG_EXPLODE_PATCHES
- y2 -= i * EXPLODE_GAP;
-#endif
+ generateRow(xDivs, xCount, vertex, y1, y2, v1, v2, stretchX, rescaleX,
+ right - left, bitmapWidth, quadCount);
}
y1 = y2;
@@ -189,33 +155,16 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight,
if (previousStepY != bitmapHeight) {
y2 = bottom - top;
-#if DEBUG_EXPLODE_PATCHES
- y1 += mYCount * EXPLODE_GAP;
- y2 += mYCount * EXPLODE_GAP;
-#endif
- generateRow(vertex, y1, y2, v1, 1.0f, stretchX, rescaleX, right - left,
- bitmapWidth, quadCount);
- }
-
- if (verticesCount > 0) {
- Caches& caches = Caches::getInstance();
- caches.bindMeshBuffer(meshBuffer);
- if (mAllocatedVerticesCount < verticesCount) {
- glBufferData(GL_ARRAY_BUFFER, sizeof(TextureVertex) * verticesCount,
- mVertices, GL_DYNAMIC_DRAW);
- mAllocatedVerticesCount = verticesCount;
- } else {
- glBufferSubData(GL_ARRAY_BUFFER, 0,
- sizeof(TextureVertex) * verticesCount, mVertices);
- }
- caches.resetVertexPointers();
+ generateRow(xDivs, xCount, vertex, y1, y2, v1, 1.0f, stretchX, rescaleX,
+ right - left, bitmapWidth, quadCount);
}
- PATCH_LOGD(" patch: new vertices count = %d", verticesCount);
+ return vertices;
}
-void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, float v2,
- float stretchX, float rescaleX, float width, float bitmapWidth, uint32_t& quadCount) {
+void Patch::generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex,
+ float y1, float y2, float v1, float v2, float stretchX, float rescaleX,
+ float width, float bitmapWidth, uint32_t& quadCount) {
float previousStepX = 0.0f;
float x1 = 0.0f;
@@ -223,8 +172,8 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl
float u1 = 0.0f;
// Generate the row quad by quad
- for (uint32_t i = 0; i < mXCount; i++) {
- float stepX = mXDivs[i];
+ for (uint32_t i = 0; i < xCount; i++) {
+ float stepX = xDivs[i];
const float segment = stepX - previousStepX;
if (i & 1) {
@@ -238,14 +187,7 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl
u1 += uOffset / bitmapWidth;
if (stepX > 0.0f) {
-#if DEBUG_EXPLODE_PATCHES
- x1 += i * EXPLODE_GAP;
- x2 += i * EXPLODE_GAP;
-#endif
generateQuad(vertex, x1, y1, x2, y2, u1, v1, u2, v2, quadCount);
-#if DEBUG_EXPLODE_PATCHES
- x2 -= i * EXPLODE_GAP;
-#endif
}
x1 = x2;
@@ -256,10 +198,6 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl
if (previousStepX != bitmapWidth) {
x2 = width;
-#if DEBUG_EXPLODE_PATCHES
- x1 += mXCount * EXPLODE_GAP;
- x2 += mXCount * EXPLODE_GAP;
-#endif
generateQuad(vertex, x1, y1, x2, y2, u1, v1, 1.0f, v2, quadCount);
}
}
@@ -290,18 +228,15 @@ void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, f
quads.add(bounds);
}
- // Left triangle
+ mUvMapper.map(u1, v1, u2, v2);
+
TextureVertex::set(vertex++, x1, y1, u1, v1);
TextureVertex::set(vertex++, x2, y1, u2, v1);
TextureVertex::set(vertex++, x1, y2, u1, v2);
-
- // Right triangle
- TextureVertex::set(vertex++, x1, y2, u1, v2);
- TextureVertex::set(vertex++, x2, y1, u2, v1);
TextureVertex::set(vertex++, x2, y2, u2, v2);
- // A quad is made of 2 triangles, 6 vertices
- verticesCount += 6;
+ verticesCount += 4;
+ indexCount += 6;
#if DEBUG_PATCHES_VERTICES
PATCH_LOGD(" quad %d", oldQuadCount);
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
index ee7bf70..448cf60 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -23,62 +23,52 @@
#include <utils/Vector.h>
+#include <androidfw/ResourceTypes.h>
+
#include "Rect.h"
+#include "UvMapper.h"
#include "Vertex.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#define EXPLODE_GAP 4
-
-///////////////////////////////////////////////////////////////////////////////
// 9-patch structures
///////////////////////////////////////////////////////////////////////////////
-/**
- * An OpenGL patch. This contains an array of vertices and an array of
- * indices to render the vertices.
- */
struct Patch {
- Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads);
+ Patch();
~Patch();
- void updateVertices(const float bitmapWidth, const float bitmapHeight,
- float left, float top, float right, float bottom);
-
- void updateColorKey(const uint32_t colorKey);
- void copy(const int32_t* xDivs, const int32_t* yDivs);
- bool matches(const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t colorKey, const int8_t emptyQuads);
+ /**
+ * Returns the size of this patch's mesh in bytes.
+ */
+ uint32_t getSize() const;
- GLuint meshBuffer;
uint32_t verticesCount;
+ uint32_t indexCount;
bool hasEmptyQuads;
Vector<Rect> quads;
-private:
- TextureVertex* mVertices;
- uint32_t mAllocatedVerticesCount;
-
- int32_t* mXDivs;
- int32_t* mYDivs;
- uint32_t mColorKey;
+ GLintptr offset;
+ GLintptr textureOffset;
- uint32_t mXCount;
- uint32_t mYCount;
- int8_t mEmptyQuads;
+ TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight,
+ float left, float top, float right, float bottom,
+ const Res_png_9patch* patch);
+ TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight,
+ float left, float top, float right, float bottom,
+ const UvMapper& mapper, const Res_png_9patch* patch);
- void generateRow(TextureVertex*& vertex, float y1, float y2,
- float v1, float v2, float stretchX, float rescaleX,
+private:
+ void generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex,
+ float y1, float y2, float v1, float v2, float stretchX, float rescaleX,
float width, float bitmapWidth, uint32_t& quadCount);
- void generateQuad(TextureVertex*& vertex,
- float x1, float y1, float x2, float y2,
- float u1, float v1, float u2, float v2,
- uint32_t& quadCount);
+ void generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2,
+ float u1, float v1, float u2, float v2, uint32_t& quadCount);
+
+ uint32_t mColorKey;
+ UvMapper mUvMapper;
}; // struct Patch
}; // namespace uirenderer
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index 62e38d3..c23e991 100644
--- a/libs/hwui/PatchCache.cpp
+++ b/libs/hwui/PatchCache.cpp
@@ -16,8 +16,10 @@
#define LOG_TAG "OpenGLRenderer"
+#include <utils/JenkinsHash.h>
#include <utils/Log.h>
+#include "Caches.h"
#include "PatchCache.h"
#include "Properties.h"
@@ -28,107 +30,123 @@ namespace uirenderer {
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-PatchCache::PatchCache(): mMaxEntries(DEFAULT_PATCH_CACHE_SIZE) {
-}
-
-PatchCache::PatchCache(uint32_t maxEntries): mMaxEntries(maxEntries) {
+PatchCache::PatchCache():
+ mSize(0), mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity),
+ mMeshBuffer(0), mGenerationId(0) {
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, NULL) > 0) {
+ INIT_LOGD(" Setting patch cache size to %skB", property);
+ mMaxSize = KB(atoi(property));
+ } else {
+ INIT_LOGD(" Using default patch cache size of %.2fkB", DEFAULT_PATCH_CACHE_SIZE);
+ mMaxSize = KB(DEFAULT_PATCH_CACHE_SIZE);
+ }
}
PatchCache::~PatchCache() {
clear();
}
-///////////////////////////////////////////////////////////////////////////////
-// Caching
-///////////////////////////////////////////////////////////////////////////////
-
-int PatchCache::PatchDescription::compare(
- const PatchCache::PatchDescription& lhs, const PatchCache::PatchDescription& rhs) {
- int deltaInt = lhs.bitmapWidth - rhs.bitmapWidth;
- if (deltaInt != 0) return deltaInt;
-
- deltaInt = lhs.bitmapHeight - rhs.bitmapHeight;
- if (deltaInt != 0) return deltaInt;
+void PatchCache::init(Caches& caches) {
+ bool created = false;
+ if (!mMeshBuffer) {
+ glGenBuffers(1, &mMeshBuffer);
+ created = true;
+ }
- if (lhs.pixelWidth < rhs.pixelWidth) return -1;
- if (lhs.pixelWidth > rhs.pixelWidth) return +1;
+ caches.bindMeshBuffer(mMeshBuffer);
+ caches.resetVertexPointers();
- if (lhs.pixelHeight < rhs.pixelHeight) return -1;
- if (lhs.pixelHeight > rhs.pixelHeight) return +1;
+ if (created) {
+ createVertexBuffer();
+ }
+}
- deltaInt = lhs.xCount - rhs.xCount;
- if (deltaInt != 0) return deltaInt;
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
- deltaInt = lhs.yCount - rhs.yCount;
- if (deltaInt != 0) return deltaInt;
+hash_t PatchCache::PatchDescription::hash() const {
+ uint32_t hash = JenkinsHashMix(0, android::hash_type(mPatch));
+ hash = JenkinsHashMix(hash, mBitmapWidth);
+ hash = JenkinsHashMix(hash, mBitmapHeight);
+ hash = JenkinsHashMix(hash, mPixelWidth);
+ hash = JenkinsHashMix(hash, mPixelHeight);
+ return JenkinsHashWhiten(hash);
+}
- deltaInt = lhs.emptyCount - rhs.emptyCount;
- if (deltaInt != 0) return deltaInt;
+int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
+ const PatchCache::PatchDescription& rhs) {
+ return memcmp(&lhs, &rhs, sizeof(PatchDescription));
+}
- deltaInt = lhs.colorKey - rhs.colorKey;
- if (deltaInt != 0) return deltaInt;
+void PatchCache::clear() {
+ clearCache();
- return 0;
+ if (mMeshBuffer) {
+ Caches::getInstance().unbindMeshBuffer();
+ glDeleteBuffers(1, &mMeshBuffer);
+ mMeshBuffer = 0;
+ mSize = 0;
+ }
}
-void PatchCache::clear() {
- size_t count = mCache.size();
- for (size_t i = 0; i < count; i++) {
- delete mCache.valueAt(i);
+void PatchCache::clearCache() {
+ LruCache<PatchDescription, Patch*>::Iterator i(mCache);
+ while (i.next()) {
+ delete i.value();
}
mCache.clear();
}
-Patch* PatchCache::get(const uint32_t bitmapWidth, const uint32_t bitmapHeight,
- const float pixelWidth, const float pixelHeight,
- const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors,
- const uint32_t width, const uint32_t height, const int8_t numColors) {
+void PatchCache::createVertexBuffer() {
+ glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW);
+ mSize = 0;
+ mGenerationId++;
+}
- int8_t transparentQuads = 0;
- uint32_t colorKey = 0;
+const Patch* PatchCache::get(const AssetAtlas::Entry* entry,
+ const uint32_t bitmapWidth, const uint32_t bitmapHeight,
+ const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) {
- if (uint8_t(numColors) < sizeof(uint32_t) * 4) {
- for (int8_t i = 0; i < numColors; i++) {
- if (colors[i] == 0x0) {
- transparentQuads++;
- colorKey |= 0x1 << i;
- }
+ const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
+ const Patch* mesh = mCache.get(description);
+
+ if (!mesh) {
+ Patch* newMesh = new Patch();
+ TextureVertex* vertices;
+
+ if (entry) {
+ vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
+ 0.0f, 0.0f, pixelWidth, pixelHeight, entry->uvMapper, patch);
+ } else {
+ vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
+ 0.0f, 0.0f, pixelWidth, pixelHeight, patch);
}
- }
- // If the 9patch is made of only transparent quads
- if (transparentQuads == int8_t((width + 1) * (height + 1))) {
- return NULL;
- }
+ if (vertices) {
+ // This call ensures the VBO exists and that it is bound
+ init(Caches::getInstance());
- const PatchDescription description(bitmapWidth, bitmapHeight,
- pixelWidth, pixelHeight, width, height, transparentQuads, colorKey);
+ // TODO: Simply remove the oldest items until we have enough room
+ // This will require to keep a list of free blocks in the VBO
+ uint32_t size = newMesh->getSize();
+ if (mSize + size > mMaxSize) {
+ clearCache();
+ createVertexBuffer();
+ }
- ssize_t index = mCache.indexOfKey(description);
- Patch* mesh = NULL;
- if (index >= 0) {
- mesh = mCache.valueAt(index);
- }
+ newMesh->offset = (GLintptr) mSize;
+ newMesh->textureOffset = newMesh->offset + gMeshTextureOffset;
+ mSize += size;
- if (!mesh) {
- PATCH_LOGD("New patch mesh "
- "xCount=%d yCount=%d, w=%.2f h=%.2f, bw=%.2f bh=%.2f",
- width, height, pixelWidth, pixelHeight, bitmapWidth, bitmapHeight);
-
- mesh = new Patch(width, height, transparentQuads);
- mesh->updateColorKey(colorKey);
- mesh->copy(xDivs, yDivs);
- mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f, pixelWidth, pixelHeight);
-
- if (mCache.size() >= mMaxEntries) {
- delete mCache.valueAt(mCache.size() - 1);
- mCache.removeItemsAt(mCache.size() - 1, 1);
+ glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices);
+
+ delete[] vertices;
}
- mCache.add(description, mesh);
- } else if (!mesh->matches(xDivs, yDivs, colorKey, transparentQuads)) {
- PATCH_LOGD("Patch mesh does not match, refreshing vertices");
- mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f, pixelWidth, pixelHeight);
+ mCache.put(description, newMesh);
+ return newMesh;
}
return mesh;
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index 0822cba..1829b89 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -17,8 +17,13 @@
#ifndef ANDROID_HWUI_PATCH_CACHE_H
#define ANDROID_HWUI_PATCH_CACHE_H
-#include <utils/KeyedVector.h>
+#include <GLES2/gl2.h>
+#include <utils/LruCache.h>
+
+#include <androidfw/ResourceTypes.h>
+
+#include "AssetAtlas.h"
#include "Debug.h"
#include "Patch.h"
@@ -40,45 +45,52 @@ namespace uirenderer {
// Cache
///////////////////////////////////////////////////////////////////////////////
+class Caches;
+
class PatchCache {
public:
PatchCache();
- PatchCache(uint32_t maxCapacity);
~PatchCache();
+ void init(Caches& caches);
- Patch* get(const uint32_t bitmapWidth, const uint32_t bitmapHeight,
- const float pixelWidth, const float pixelHeight,
- const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors,
- const uint32_t width, const uint32_t height, const int8_t numColors);
+ const Patch* get(const AssetAtlas::Entry* entry,
+ const uint32_t bitmapWidth, const uint32_t bitmapHeight,
+ const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch);
void clear();
uint32_t getSize() const {
- return mCache.size();
+ return mSize;
}
uint32_t getMaxSize() const {
- return mMaxEntries;
+ return mMaxSize;
+ }
+
+ GLuint getMeshBuffer() const {
+ return mMeshBuffer;
+ }
+
+ uint32_t getGenerationId() const {
+ return mGenerationId;
}
private:
- /**
- * Description of a patch.
- */
+ void clearCache();
+ void createVertexBuffer();
+
struct PatchDescription {
- PatchDescription(): bitmapWidth(0), bitmapHeight(0), pixelWidth(0), pixelHeight(0),
- xCount(0), yCount(0), emptyCount(0), colorKey(0) {
+ PatchDescription(): mPatch(NULL), mBitmapWidth(0), mBitmapHeight(0),
+ mPixelWidth(0), mPixelHeight(0) {
}
PatchDescription(const uint32_t bitmapWidth, const uint32_t bitmapHeight,
- const float pixelWidth, const float pixelHeight,
- const uint32_t xCount, const uint32_t yCount,
- const int8_t emptyCount, const uint32_t colorKey):
- bitmapWidth(bitmapWidth), bitmapHeight(bitmapHeight),
- pixelWidth(pixelWidth), pixelHeight(pixelHeight),
- xCount(xCount), yCount(yCount),
- emptyCount(emptyCount), colorKey(colorKey) {
+ const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch):
+ mPatch(patch), mBitmapWidth(bitmapWidth), mBitmapHeight(bitmapHeight),
+ mPixelWidth(pixelWidth), mPixelHeight(pixelHeight) {
}
+ hash_t hash() const;
+
static int compare(const PatchDescription& lhs, const PatchDescription& rhs);
bool operator==(const PatchDescription& other) const {
@@ -99,21 +111,27 @@ private:
return PatchDescription::compare(lhs, rhs);
}
+ friend inline hash_t hash_type(const PatchDescription& entry) {
+ return entry.hash();
+ }
+
private:
- uint32_t bitmapWidth;
- uint32_t bitmapHeight;
- float pixelWidth;
- float pixelHeight;
- uint32_t xCount;
- uint32_t yCount;
- int8_t emptyCount;
- uint32_t colorKey;
+ const Res_png_9patch* mPatch;
+ uint32_t mBitmapWidth;
+ uint32_t mBitmapHeight;
+ float mPixelWidth;
+ float mPixelHeight;
}; // struct PatchDescription
- uint32_t mMaxEntries;
- KeyedVector<PatchDescription, Patch*> mCache;
+ uint32_t mMaxSize;
+ uint32_t mSize;
+
+ LruCache<PatchDescription, Patch*> mCache;
+
+ GLuint mMeshBuffer;
+ uint32_t mGenerationId;
}; // class PatchCache
}; // namespace uirenderer
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index fdb10e2..3ab40da 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -139,7 +139,7 @@ static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
static PathTexture* createTexture(float left, float top, float offset,
uint32_t width, uint32_t height, uint32_t id) {
- PathTexture* texture = new PathTexture();
+ PathTexture* texture = new PathTexture(Caches::getInstance());
texture->left = left;
texture->top = top;
texture->offset = offset;
@@ -223,7 +223,7 @@ void PathCache::removeTexture(PathTexture* texture) {
}
if (texture->id) {
- glDeleteTextures(1, &texture->id);
+ Caches::getInstance().deleteTexture(texture->id);
}
delete texture;
}
@@ -300,7 +300,7 @@ void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) {
glGenTextures(1, &texture->id);
- glBindTexture(GL_TEXTURE_2D, texture->id);
+ Caches::getInstance().bindTexture(texture->id);
// Textures are Alpha8
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index dd1f996..a191f0e 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -58,7 +58,7 @@ class Caches;
* Alpha texture used to represent a path.
*/
struct PathTexture: public Texture {
- PathTexture(): Texture() {
+ PathTexture(Caches& caches): Texture(caches) {
}
~PathTexture() {
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 0879b1b..3970913 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -66,11 +66,11 @@ void PathTessellator::expandBoundsForStroke(SkRect& bounds, const SkPaint* paint
}
}
-inline void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
+inline static void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]);
}
-inline void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
+inline static void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha);
}
@@ -84,7 +84,7 @@ inline void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
*
* NOTE: assumes angles between normals 90 degrees or less
*/
-inline vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
+inline static vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
}
@@ -224,6 +224,20 @@ void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Ver
DEBUG_DUMP_BUFFER();
}
+static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center,
+ const vec2& normal, Vertex* buffer, int& currentIndex, bool begin) {
+ vec2 strokeOffset = normal;
+ paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
+
+ vec2 referencePoint(center.position[0], center.position[1]);
+ if (paintInfo.cap == SkPaint::kSquare_Cap) {
+ referencePoint += vec2(-strokeOffset.y, strokeOffset.x) * (begin ? -1 : 1);
+ }
+
+ Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset);
+ Vertex::set(&buffer[currentIndex++], referencePoint - strokeOffset);
+}
+
/**
* Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except:
*
@@ -235,19 +249,17 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
const int extra = paintInfo.capExtraDivisions();
const int allocSize = (vertices.size() + extra) * 2;
-
Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize);
+ const int lastIndex = vertices.size() - 1;
if (extra > 0) {
// tessellate both round caps
- const int last = vertices.size() - 1;
float beginTheta = atan2(
- - (vertices[0].position[0] - vertices[1].position[0]),
- vertices[0].position[1] - vertices[1].position[1]);
+ - (vertices[0].position[0] - vertices[1].position[0]),
+ vertices[0].position[1] - vertices[1].position[1]);
float endTheta = atan2(
- - (vertices[last].position[0] - vertices[last - 1].position[0]),
- vertices[last].position[1] - vertices[last - 1].position[1]);
-
+ - (vertices[lastIndex].position[0] - vertices[lastIndex - 1].position[0]),
+ vertices[lastIndex].position[1] - vertices[lastIndex - 1].position[1]);
const float dTheta = PI / (extra + 1);
const float radialScale = 2.0f / (1 + cos(dTheta));
@@ -270,56 +282,45 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
vec2 endRadialOffset(cos(endTheta), sin(endTheta));
paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
Vertex::set(&buffer[allocSize - 1 - capOffset],
- vertices[last].position[0] + endRadialOffset.x,
- vertices[last].position[1] + endRadialOffset.y);
+ vertices[lastIndex].position[0] + endRadialOffset.x,
+ vertices[lastIndex].position[1] + endRadialOffset.y);
}
}
int currentIndex = extra;
- const Vertex* current = &(vertices[0]);
- vec2 lastNormal;
- for (unsigned int i = 0; i < vertices.size() - 1; i++) {
+ const Vertex* last = &(vertices[0]);
+ const Vertex* current = &(vertices[1]);
+ vec2 lastNormal(current->position[1] - last->position[1],
+ last->position[0] - current->position[0]);
+ lastNormal.normalize();
+
+ storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true);
+
+ for (unsigned int i = 1; i < vertices.size() - 1; i++) {
const Vertex* next = &(vertices[i + 1]);
vec2 nextNormal(next->position[1] - current->position[1],
current->position[0] - next->position[0]);
nextNormal.normalize();
- vec2 totalOffset;
- if (i == 0) {
- totalOffset = nextNormal;
- } else {
- totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
- }
- paintInfo.scaleOffsetForStrokeWidth(totalOffset);
+ vec2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+ paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
- Vertex::set(&buffer[currentIndex++],
- current->position[0] + totalOffset.x,
- current->position[1] + totalOffset.y);
-
- Vertex::set(&buffer[currentIndex++],
- current->position[0] - totalOffset.x,
- current->position[1] - totalOffset.y);
+ vec2 center(current->position[0], current->position[1]);
+ Vertex::set(&buffer[currentIndex++], center + strokeOffset);
+ Vertex::set(&buffer[currentIndex++], center - strokeOffset);
current = next;
lastNormal = nextNormal;
}
- vec2 totalOffset = lastNormal;
- paintInfo.scaleOffsetForStrokeWidth(totalOffset);
-
- Vertex::set(&buffer[currentIndex++],
- current->position[0] + totalOffset.x,
- current->position[1] + totalOffset.y);
- Vertex::set(&buffer[currentIndex++],
- current->position[0] - totalOffset.x,
- current->position[1] - totalOffset.y);
+ storeBeginEnd(paintInfo, vertices[lastIndex], lastNormal, buffer, currentIndex, false);
DEBUG_DUMP_BUFFER();
}
/**
* Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation
- *
+ *
* 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of
* the shape (using 2 * perimeter.size() vertices)
*
@@ -389,7 +390,7 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver
* For explanation of constants and general methodoloyg, see comments for
* getStrokeVerticesFromUnclosedVerticesAA() below.
*/
-inline void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
+inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
AlphaVertex* buffer, bool isFirst, vec2 normal, int offset) {
const int extra = paintInfo.capExtraDivisions();
const int extraOffset = (extra + 1) / 2;
@@ -772,11 +773,67 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
}
}
+static void expandRectToCoverVertex(SkRect& rect, float x, float y) {
+ rect.fLeft = fminf(rect.fLeft, x);
+ rect.fTop = fminf(rect.fTop, y);
+ rect.fRight = fmaxf(rect.fRight, x);
+ rect.fBottom = fmaxf(rect.fBottom, y);
+}
static void expandRectToCoverVertex(SkRect& rect, const Vertex& vertex) {
- rect.fLeft = fminf(rect.fLeft, vertex.position[0]);
- rect.fTop = fminf(rect.fTop, vertex.position[1]);
- rect.fRight = fmaxf(rect.fRight, vertex.position[0]);
- rect.fBottom = fmaxf(rect.fBottom, vertex.position[1]);
+ expandRectToCoverVertex(rect, vertex.position[0], vertex.position[1]);
+}
+
+template <class TYPE>
+static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
+ const float* points, int count, SkRect& bounds) {
+ bounds.set(points[0], points[1], points[0], points[1]);
+
+ int numPoints = count / 2;
+ int verticesPerPoint = srcBuffer.getVertexCount();
+ dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
+
+ for (int i = 0; i < count; i += 2) {
+ expandRectToCoverVertex(bounds, points[i + 0], points[i + 1]);
+ dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
+ }
+ dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
+}
+
+void PathTessellator::tessellatePoints(const float* points, int count, SkPaint* paint,
+ const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer) {
+ const PaintInfo paintInfo(paint, transform);
+
+ // determine point shape
+ SkPath path;
+ float radius = paintInfo.halfStrokeWidth;
+ if (radius == 0.0f) radius = 0.25f;
+
+ if (paintInfo.cap == SkPaint::kRound_Cap) {
+ path.addCircle(0, 0, radius);
+ } else {
+ path.addRect(-radius, -radius, radius, radius);
+ }
+
+ // calculate outline
+ Vector<Vertex> outlineVertices;
+ approximatePathOutlineVertices(path, true,
+ paintInfo.inverseScaleX * paintInfo.inverseScaleX,
+ paintInfo.inverseScaleY * paintInfo.inverseScaleY, outlineVertices);
+
+ if (!outlineVertices.size()) return;
+
+ // tessellate, then duplicate outline across points
+ int numPoints = count / 2;
+ VertexBuffer tempBuffer;
+ if (!paintInfo.isAA) {
+ getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
+ instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds);
+ } else {
+ getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer);
+ instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds);
+ }
+
+ expandBoundsForStroke(bounds, paint, true); // force-expand bounds to incorporate stroke
}
void PathTessellator::tessellateLines(const float* points, int count, SkPaint* paint,
diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h
index 596d49d..85797fc 100644
--- a/libs/hwui/PathTessellator.h
+++ b/libs/hwui/PathTessellator.h
@@ -30,7 +30,7 @@ class VertexBuffer {
public:
VertexBuffer():
mBuffer(0),
- mSize(0),
+ mVertexCount(0),
mCleanupMethod(NULL)
{}
@@ -44,30 +44,42 @@ public:
multiple regions within a single VertexBuffer, such as with PathTessellator::tesselateLines()
*/
template <class TYPE>
- TYPE* alloc(int size) {
- if (mSize) {
+ TYPE* alloc(int vertexCount) {
+ if (mVertexCount) {
TYPE* reallocBuffer = (TYPE*)mReallocBuffer;
// already have allocated the buffer, re-allocate space within
if (mReallocBuffer != mBuffer) {
// not first re-allocation, leave space for degenerate triangles to separate strips
reallocBuffer += 2;
}
- mReallocBuffer = reallocBuffer + size;
+ mReallocBuffer = reallocBuffer + vertexCount;
return reallocBuffer;
}
- mSize = size;
- mReallocBuffer = mBuffer = (void*)new TYPE[size];
+ mVertexCount = vertexCount;
+ mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount];
mCleanupMethod = &(cleanup<TYPE>);
return (TYPE*)mBuffer;
}
- void* getBuffer() const { return mBuffer; }
- unsigned int getSize() const { return mSize; }
+ template <class TYPE>
+ void copyInto(const VertexBuffer& srcBuffer, float xOffset, float yOffset) {
+ int verticesToCopy = srcBuffer.getVertexCount();
+
+ TYPE* dst = alloc<TYPE>(verticesToCopy);
+ TYPE* src = (TYPE*)srcBuffer.getBuffer();
+
+ for (int i = 0; i < verticesToCopy; i++) {
+ TYPE::copyWithOffset(&dst[i], src[i], xOffset, yOffset);
+ }
+ }
+
+ void* getBuffer() const { return mBuffer; } // shouldn't be const, since not a const ptr?
+ unsigned int getVertexCount() const { return mVertexCount; }
template <class TYPE>
void createDegenerateSeparators(int allocSize) {
- TYPE* end = (TYPE*)mBuffer + mSize;
+ TYPE* end = (TYPE*)mBuffer + mVertexCount;
for (TYPE* degen = (TYPE*)mBuffer + allocSize; degen < end; degen += 2 + allocSize) {
memcpy(degen, degen - 1, sizeof(TYPE));
memcpy(degen + 1, degen + 2, sizeof(TYPE));
@@ -81,7 +93,7 @@ private:
}
void* mBuffer;
- unsigned int mSize;
+ unsigned int mVertexCount;
void* mReallocBuffer; // used for multi-allocation
@@ -95,6 +107,9 @@ public:
static void tessellatePath(const SkPath& path, const SkPaint* paint,
const mat4 *transform, VertexBuffer& vertexBuffer);
+ static void tessellatePoints(const float* points, int count, SkPaint* paint,
+ const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer);
+
static void tessellateLines(const float* points, int count, SkPaint* paint,
const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer);
diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp
index 8280370..29f8756 100644
--- a/libs/hwui/PixelBuffer.cpp
+++ b/libs/hwui/PixelBuffer.cpp
@@ -147,14 +147,8 @@ void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t hei
///////////////////////////////////////////////////////////////////////////////
PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) {
- bool gpuBuffer = type == kBufferType_Auto && Extensions::getInstance().getMajorGlVersion() >= 3;
- if (gpuBuffer) {
- char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, property, "false") > 0) {
- if (!strcmp(property, "true")) {
- return new GpuPixelBuffer(format, width, height);
- }
- }
+ if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) {
+ return new GpuPixelBuffer(format, width, height);
}
return new CpuPixelBuffer(format, width, height);
}
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 14a2376..9e4670e 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -15,6 +15,9 @@
*/
#define LOG_TAG "OpenGLRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
+#include <utils/Trace.h>
#include "Program.h"
@@ -25,7 +28,6 @@ namespace uirenderer {
// Base program
///////////////////////////////////////////////////////////////////////////////
-// TODO: Program instance should be created from a factory method
Program::Program(const ProgramDescription& description, const char* vertex, const char* fragment) {
mInitialized = false;
mHasColorUniform = false;
@@ -50,7 +52,9 @@ Program::Program(const ProgramDescription& description, const char* vertex, cons
texCoords = -1;
}
+ ATRACE_BEGIN("linkProgram");
glLinkProgram(mProgramId);
+ ATRACE_END();
GLint status;
glGetProgramiv(mProgramId, GL_LINK_STATUS, &status);
@@ -87,6 +91,9 @@ Program::Program(const ProgramDescription& description, const char* vertex, cons
Program::~Program() {
if (mInitialized) {
+ // This would ideally happen after linking the program
+ // but Tegra drivers, especially when perfhud is enabled,
+ // sometimes crash if we do so
glDetachShader(mProgramId, mVertexShader);
glDetachShader(mProgramId, mFragmentShader);
@@ -132,6 +139,8 @@ int Program::getUniform(const char* name) {
}
GLuint Program::buildShader(const char* source, GLenum type) {
+ ATRACE_CALL();
+
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, 0);
glCompileShader(shader);
@@ -153,20 +162,24 @@ GLuint Program::buildShader(const char* source, GLenum type) {
void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
const mat4& transformMatrix, bool offset) {
- mat4 p(projectionMatrix);
- if (offset) {
- // offset screenspace xy by an amount that compensates for typical precision
- // issues in GPU hardware that tends to paint hor/vert lines in pixels shifted
- // up and to the left.
- // This offset value is based on an assumption that some hardware may use as
- // little as 12.4 precision, so we offset by slightly more than 1/16.
- p.translate(.065, .065, 0);
+ if (projectionMatrix != mProjection) {
+ if (CC_LIKELY(!offset)) {
+ glUniformMatrix4fv(projection, 1, GL_FALSE, &projectionMatrix.data[0]);
+ } else {
+ mat4 p(projectionMatrix);
+ // offset screenspace xy by an amount that compensates for typical precision
+ // issues in GPU hardware that tends to paint hor/vert lines in pixels shifted
+ // up and to the left.
+ // This offset value is based on an assumption that some hardware may use as
+ // little as 12.4 precision, so we offset by slightly more than 1/16.
+ p.translate(.065, .065);
+ glUniformMatrix4fv(projection, 1, GL_FALSE, &p.data[0]);
+ }
+ mProjection = projectionMatrix;
}
mat4 t(transformMatrix);
t.multiply(modelViewMatrix);
-
- glUniformMatrix4fv(projection, 1, GL_FALSE, &p.data[0]);
glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]);
}
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index e8b6d47..4f94afc 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -68,23 +68,22 @@ namespace uirenderer {
#define PROGRAM_BITMAP_WRAPS_SHIFT 9
#define PROGRAM_BITMAP_WRAPT_SHIFT 11
-#define PROGRAM_GRADIENT_TYPE_SHIFT 33
+#define PROGRAM_GRADIENT_TYPE_SHIFT 33 // 2 bits for gradient type
#define PROGRAM_MODULATE_SHIFT 35
-#define PROGRAM_IS_POINT_SHIFT 36
+#define PROGRAM_HAS_AA_SHIFT 36
-#define PROGRAM_HAS_AA_SHIFT 37
+#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 37
+#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 38
-#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
-#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
+#define PROGRAM_HAS_GAMMA_CORRECTION 39
-#define PROGRAM_HAS_GAMMA_CORRECTION 40
+#define PROGRAM_IS_SIMPLE_GRADIENT 40
-#define PROGRAM_IS_SIMPLE_GRADIENT 41
+#define PROGRAM_HAS_COLORS 41
-#define PROGRAM_HAS_COLORS 42
-
-#define PROGRAM_HAS_DEBUG_HIGHLIGHT 43
+#define PROGRAM_HAS_DEBUG_HIGHLIGHT 42
+#define PROGRAM_EMULATE_STENCIL 43
///////////////////////////////////////////////////////////////////////////////
// Types
@@ -156,13 +155,11 @@ struct ProgramDescription {
SkXfermode::Mode framebufferMode;
bool swapSrcDst;
- bool isPoint;
- float pointSize;
-
bool hasGammaCorrection;
float gamma;
bool hasDebugHighlight;
+ bool emulateStencil;
/**
* Resets this description. All fields are reset back to the default
@@ -199,9 +196,6 @@ struct ProgramDescription {
framebufferMode = SkXfermode::kClear_Mode;
swapSrcDst = false;
- isPoint = false;
- pointSize = 0.0f;
-
hasGammaCorrection = false;
gamma = 2.2f;
@@ -267,7 +261,6 @@ struct ProgramDescription {
key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
- if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT;
if (isAA) key |= programid(0x1) << PROGRAM_HAS_AA_SHIFT;
if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
@@ -275,6 +268,7 @@ struct ProgramDescription {
if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT;
if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS;
if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT;
+ if (emulateStencil) key |= programid(0x1) << PROGRAM_EMULATE_STENCIL;
return key;
}
@@ -430,10 +424,13 @@ private:
bool mUse;
bool mInitialized;
+ // Uniforms caching
bool mHasColorUniform;
int mColorUniform;
bool mHasSampler;
+
+ mat4 mProjection;
}; // class Program
}; // namespace uirenderer
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 8eb85e5..a5ce6f6 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -53,8 +53,6 @@ const char* gVS_Header_Uniforms_TextureTransform =
const char* gVS_Header_Uniforms =
"uniform mat4 projection;\n" \
"uniform mat4 transform;\n";
-const char* gVS_Header_Uniforms_IsPoint =
- "uniform mediump float pointSize;\n";
const char* gVS_Header_Uniforms_HasGradient =
"uniform mat4 screenSpace;\n";
const char* gVS_Header_Uniforms_HasBitmap =
@@ -68,8 +66,6 @@ const char* gVS_Header_Varyings_IsAAVertexShape =
"varying float alpha;\n";
const char* gVS_Header_Varyings_HasBitmap =
"varying highp vec2 outBitmapTexCoords;\n";
-const char* gVS_Header_Varyings_PointHasBitmap =
- "varying highp vec2 outPointBitmapTexCoords;\n";
const char* gVS_Header_Varyings_HasGradient[6] = {
// Linear
"varying highp vec2 linear;\n"
@@ -118,12 +114,8 @@ const char* gVS_Main_OutGradient[6] = {
};
const char* gVS_Main_OutBitmapTexCoords =
" outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
-const char* gVS_Main_OutPointBitmapTexCoords =
- " outPointBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
const char* gVS_Main_Position =
" gl_Position = projection * transform * position;\n";
-const char* gVS_Main_PointSize =
- " gl_PointSize = pointSize;\n";
const char* gVS_Main_AAVertexShape =
" alpha = vtxAlpha;\n";
const char* gVS_Footer =
@@ -141,9 +133,6 @@ const char* gFS_Header =
"precision mediump float;\n\n";
const char* gFS_Uniforms_Color =
"uniform vec4 color;\n";
-const char* gFS_Header_Uniforms_PointHasBitmap =
- "uniform vec2 textureDimension;\n"
- "uniform float pointSize;\n";
const char* gFS_Uniforms_TextureSampler =
"uniform sampler2D baseSampler;\n";
const char* gFS_Uniforms_ExternalTextureSampler =
@@ -178,10 +167,6 @@ const char* gFS_Main =
"\nvoid main(void) {\n"
" lowp vec4 fragColor;\n";
-const char* gFS_Main_PointBitmapTexCoords =
- " highp vec2 outBitmapTexCoords = outPointBitmapTexCoords + "
- "((gl_PointCoord - vec2(0.5, 0.5)) * textureDimension * vec2(pointSize, pointSize));\n";
-
const char* gFS_Main_Dither[2] = {
// ES 2.0
"texture2D(ditherSampler, ditherTexCoords).a * " STR(DITHER_KERNEL_SIZE_INV_SQUARE),
@@ -342,6 +327,12 @@ const char* gFS_Main_ApplyColorOp[4] = {
};
const char* gFS_Main_DebugHighlight =
" gl_FragColor.rgb = vec3(0.0, gl_FragColor.a, 0.0);\n";
+const char* gFS_Main_EmulateStencil =
+ " gl_FragColor.rgba = vec4(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0, 1.0);\n"
+ " return;\n"
+ " /*\n";
+const char* gFS_Footer_EmulateStencil =
+ " */\n";
const char* gFS_Footer =
"}\n\n";
@@ -478,9 +469,6 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
if (description.hasBitmap) {
shader.append(gVS_Header_Uniforms_HasBitmap);
}
- if (description.isPoint) {
- shader.append(gVS_Header_Uniforms_IsPoint);
- }
// Varyings
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Varyings_HasTexture);
@@ -495,9 +483,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
}
if (description.hasBitmap) {
- shader.append(description.isPoint ?
- gVS_Header_Varyings_PointHasBitmap :
- gVS_Header_Varyings_HasBitmap);
+ shader.append(gVS_Header_Varyings_HasBitmap);
}
// Begin the shader
@@ -514,12 +500,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
shader.append(gVS_Main_OutColors);
}
if (description.hasBitmap) {
- shader.append(description.isPoint ?
- gVS_Main_OutPointBitmapTexCoords :
- gVS_Main_OutBitmapTexCoords);
- }
- if (description.isPoint) {
- shader.append(gVS_Main_PointSize);
+ shader.append(gVS_Main_OutBitmapTexCoords);
}
// Output transformed position
shader.append(gVS_Main_Position);
@@ -570,9 +551,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
}
if (description.hasBitmap) {
- shader.append(description.isPoint ?
- gVS_Header_Varyings_PointHasBitmap :
- gVS_Header_Varyings_HasBitmap);
+ shader.append(gVS_Header_Varyings_HasBitmap);
}
// Uniforms
@@ -593,9 +572,6 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient],
gFS_Uniforms_Dither);
}
- if (description.hasBitmap && description.isPoint) {
- shader.append(gFS_Header_Uniforms_PointHasBitmap);
- }
if (description.hasGammaCorrection) {
shader.append(gFS_Uniforms_Gamma);
}
@@ -603,7 +579,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
// Optimization for common cases
if (!description.isAA && !blendFramebuffer && !description.hasColors &&
description.colorOp == ProgramDescription::kColorNone &&
- !description.isPoint && !description.hasDebugHighlight) {
+ !description.hasDebugHighlight && !description.emulateStencil) {
bool fast = false;
const bool noShader = !description.hasGradient && !description.hasBitmap;
@@ -683,6 +659,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
// Begin the shader
shader.append(gFS_Main); {
+ if (description.emulateStencil) {
+ shader.append(gFS_Main_EmulateStencil);
+ }
// Stores the result in fragColor directly
if (description.hasTexture || description.hasExternalTexture) {
if (description.hasAlpha8Texture) {
@@ -703,9 +682,6 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.appendFormat(gFS_Main_AddDitherToGradient, gFS_Main_Dither[mHasES3]);
}
if (description.hasBitmap) {
- if (description.isPoint) {
- shader.append(gFS_Main_PointBitmapTexCoords);
- }
if (!description.isBitmapNpot) {
shader.append(gFS_Main_FetchBitmap);
} else {
@@ -757,6 +733,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.append(gFS_Main_DebugHighlight);
}
}
+ if (description.emulateStencil) {
+ shader.append(gFS_Footer_EmulateStencil);
+ }
// End the shader
shader.append(gFS_Footer);
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 6eea00c..dbbb956 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -71,9 +71,9 @@ enum DebugLevel {
/**
* Used to enable/disable overdraw debugging. The accepted values are
- * "true" and "false". The default value is "false".
+ * "show", "count" and "false". The default value is "false".
*/
-#define PROPERTY_DEBUG_OVERDRAW "debug.hwui.show_overdraw"
+#define PROPERTY_DEBUG_OVERDRAW "debug.hwui.overdraw"
/**
* Used to enable/disable non-rectangular clipping debugging.
@@ -123,9 +123,9 @@ enum DebugLevel {
/**
* Indicates whether PBOs can be used to back pixel buffers.
- * Accepted values are "true" and "false".
+ * Accepted values are "true" and "false". Default is true.
*/
-#define PROPERTY_ENABLE_GPU_PIXEL_BUFFERS "hwui.use_gpu_pixel_buffers"
+#define PROPERTY_ENABLE_GPU_PIXEL_BUFFERS "ro.hwui.use_gpu_pixel_buffers"
// These properties are defined in mega-bytes
#define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size"
@@ -133,6 +133,7 @@ enum DebugLevel {
#define PROPERTY_RENDER_BUFFER_CACHE_SIZE "ro.hwui.r_buffer_cache_size"
#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
#define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size"
+#define PROPERTY_PATCH_CACHE_SIZE "ro.hwui.patch_cache_size"
#define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size"
#define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size"
@@ -178,7 +179,7 @@ enum DebugLevel {
#define DEFAULT_LAYER_CACHE_SIZE 16.0f
#define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f
#define DEFAULT_PATH_CACHE_SIZE 10.0f
-#define DEFAULT_PATCH_CACHE_SIZE 512
+#define DEFAULT_PATCH_CACHE_SIZE 128 // in kB
#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
#define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f
#define DEFAULT_FBO_CACHE_SIZE 16
@@ -195,6 +196,8 @@ enum DebugLevel {
// Converts a number of mega-bytes into bytes
#define MB(s) s * 1024 * 1024
+// Converts a number of kilo-bytes into bytes
+#define KB(s) s * 1024
static DebugLevel readDebugLevel() {
char property[PROPERTY_VALUE_MAX];
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index f50ac3c..689fe6c0 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -125,11 +125,11 @@ public:
return intersect(r.left, r.top, r.right, r.bottom);
}
- inline bool contains(float l, float t, float r, float b) {
+ inline bool contains(float l, float t, float r, float b) const {
return l >= left && t >= top && r <= right && b <= bottom;
}
- inline bool contains(const Rect& r) {
+ inline bool contains(const Rect& r) const {
return contains(r.left, r.top, r.right, r.bottom);
}
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index c38eedb..797ed10 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -69,9 +69,13 @@ void SkiaShader::copyFrom(const SkiaShader& shader) {
mGenerationId = shader.mGenerationId;
}
+SkiaShader::SkiaShader(): mCaches(NULL) {
+}
+
SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
- mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend) {
+ mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend),
+ mCaches(NULL) {
setMatrix(matrix);
mGenerationId = 0;
}
@@ -87,7 +91,7 @@ void SkiaShader::setupProgram(Program* program, const mat4& modelView, const Sna
}
void SkiaShader::bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT) {
- glBindTexture(GL_TEXTURE_2D, texture->id);
+ mCaches->bindTexture(texture->id);
texture->setWrapST(wrapS, wrapT);
}
@@ -114,7 +118,7 @@ SkiaShader* SkiaBitmapShader::copy() {
}
void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
- Texture* texture = mTextureCache->get(mBitmap);
+ Texture* texture = mCaches->textureCache.get(mBitmap);
if (!texture) return;
mTexture = texture;
@@ -229,7 +233,7 @@ void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelV
GLuint textureSlot = (*textureUnit)++;
Caches::getInstance().activeTexture(textureSlot);
- Texture* texture = mGradientCache->get(mColors, mPositions, mCount);
+ Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
// Uniforms
bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
@@ -349,7 +353,7 @@ void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelVi
GLuint textureSlot = (*textureUnit)++;
Caches::getInstance().activeTexture(textureSlot);
- Texture* texture = mGradientCache->get(mColors, mPositions, mCount);
+ Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
// Uniforms
bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
@@ -359,7 +363,7 @@ void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelVi
bindUniformColor(program->getUniform("endColor"), mColors[1]);
}
- Caches::getInstance().dither.setupProgram(program, textureUnit);
+ mCaches->dither.setupProgram(program, textureUnit);
mat4 screenSpace;
computeScreenSpaceMatrix(screenSpace, modelView);
@@ -394,12 +398,6 @@ SkiaShader* SkiaComposeShader::copy() {
return copy;
}
-void SkiaComposeShader::set(TextureCache* textureCache, GradientCache* gradientCache) {
- SkiaShader::set(textureCache, gradientCache);
- mFirst->set(textureCache, gradientCache);
- mSecond->set(textureCache, gradientCache);
-}
-
void SkiaComposeShader::describe(ProgramDescription& description, const Extensions& extensions) {
mFirst->describe(description, extensions);
mSecond->describe(description, extensions);
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index bc12b0d..a63431c 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -33,6 +33,8 @@
namespace android {
namespace uirenderer {
+class Caches;
+
///////////////////////////////////////////////////////////////////////////////
// Base shader
///////////////////////////////////////////////////////////////////////////////
@@ -77,9 +79,8 @@ struct SkiaShader {
return mType;
}
- virtual void set(TextureCache* textureCache, GradientCache* gradientCache) {
- mTextureCache = textureCache;
- mGradientCache = gradientCache;
+ virtual void setCaches(Caches& caches) {
+ mCaches = &caches;
}
uint32_t getGenerationId() {
@@ -103,8 +104,7 @@ struct SkiaShader {
void computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView);
protected:
- SkiaShader() {
- }
+ SkiaShader();
/**
* The appropriate texture unit must have been activated prior to invoking
@@ -118,8 +118,7 @@ protected:
SkShader::TileMode mTileY;
bool mBlend;
- TextureCache* mTextureCache;
- GradientCache* mGradientCache;
+ Caches* mCaches;
mat4 mUnitMatrix;
mat4 mShaderMatrix;
@@ -229,7 +228,11 @@ struct SkiaComposeShader: public SkiaShader {
~SkiaComposeShader();
SkiaShader* copy();
- void set(TextureCache* textureCache, GradientCache* gradientCache);
+ void setCaches(Caches& caches) {
+ SkiaShader::setCaches(caches);
+ mFirst->setCaches(caches);
+ mSecond->setCaches(caches);
+ }
void describe(ProgramDescription& description, const Extensions& extensions);
void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index 6976eaa..0b2c130 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -18,6 +18,7 @@
#include <utils/JenkinsHash.h>
+#include "Caches.h"
#include "Debug.h"
#include "TextDropShadowCache.h"
#include "Properties.h"
@@ -154,7 +155,7 @@ void TextDropShadowCache::operator()(ShadowText& text, ShadowTexture*& texture)
ALOGD("Shadow texture deleted, size = %d", texture->bitmapSize);
}
- glDeleteTextures(1, &texture->id);
+ texture->deleteTexture();
delete texture;
}
}
@@ -182,7 +183,9 @@ ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32
return NULL;
}
- texture = new ShadowTexture;
+ Caches& caches = Caches::getInstance();
+
+ texture = new ShadowTexture(caches);
texture->left = shadow.penX;
texture->top = shadow.penY;
texture->width = shadow.width;
@@ -202,7 +205,7 @@ ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32
glGenTextures(1, &texture->id);
- glBindTexture(GL_TEXTURE_2D, texture->id);
+ caches.bindTexture(texture->id);
// Textures are Alpha8
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index 0bed72b6..04d7357 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -30,6 +30,8 @@
namespace android {
namespace uirenderer {
+class Caches;
+
struct ShadowText {
ShadowText(): len(0), radius(0.0f), textSize(0.0f), typeface(NULL),
flags(0), italicStyle(0.0f), scaleX(0), text(NULL), positions(NULL) {
@@ -114,7 +116,7 @@ inline hash_t hash_type(const ShadowText& entry) {
* Alpha texture used to represent a shadow.
*/
struct ShadowTexture: public Texture {
- ShadowTexture(): Texture() {
+ ShadowTexture(Caches& caches): Texture(caches) {
}
float left;
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
new file mode 100644
index 0000000..7923ce7
--- /dev/null
+++ b/libs/hwui/Texture.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2013 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 "OpenGLRenderer"
+
+#include <utils/Log.h>
+
+#include "Caches.h"
+#include "Texture.h"
+
+namespace android {
+namespace uirenderer {
+
+Texture::Texture(): id(0), generation(0), blend(false), width(0), height(0),
+ cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL),
+ mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
+ mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
+ mFirstFilter(true), mFirstWrap(true), mCaches(Caches::getInstance()) {
+}
+
+Texture::Texture(Caches& caches): id(0), generation(0), blend(false), width(0), height(0),
+ cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL),
+ mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
+ mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
+ mFirstFilter(true), mFirstWrap(true), mCaches(caches) {
+}
+
+void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force,
+ GLenum renderTarget) {
+
+ if (mFirstWrap || force || wrapS != mWrapS || wrapT != mWrapT) {
+ mFirstWrap = false;
+
+ mWrapS = wrapS;
+ mWrapT = wrapT;
+
+ if (bindTexture) {
+ mCaches.bindTexture(renderTarget, id);
+ }
+
+ glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS);
+ glTexParameteri(renderTarget, GL_TEXTURE_WRAP_T, wrapT);
+ }
+}
+
+void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force,
+ GLenum renderTarget) {
+
+ if (mFirstFilter || force || min != mMinFilter || mag != mMagFilter) {
+ mFirstFilter = false;
+
+ mMinFilter = min;
+ mMagFilter = mag;
+
+ if (bindTexture) {
+ mCaches.bindTexture(renderTarget, id);
+ }
+
+ if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
+
+ glTexParameteri(renderTarget, GL_TEXTURE_MIN_FILTER, min);
+ glTexParameteri(renderTarget, GL_TEXTURE_MAG_FILTER, mag);
+ }
+}
+
+void Texture::deleteTexture() const {
+ mCaches.deleteTexture(id);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index 8d88bdc..d48ec59 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -22,75 +22,39 @@
namespace android {
namespace uirenderer {
+class Caches;
+class UvMapper;
+
/**
* Represents an OpenGL texture.
*/
-struct Texture {
- Texture() {
- cleanup = false;
- bitmapSize = 0;
-
- wrapS = GL_CLAMP_TO_EDGE;
- wrapT = GL_CLAMP_TO_EDGE;
-
- minFilter = GL_NEAREST;
- magFilter = GL_NEAREST;
-
- mipMap = false;
-
- firstFilter = true;
- firstWrap = true;
+class Texture {
+public:
+ Texture();
+ Texture(Caches& caches);
- id = 0;
- }
+ virtual ~Texture() { }
- void setWrap(GLenum wrap, bool bindTexture = false, bool force = false,
+ inline void setWrap(GLenum wrap, bool bindTexture = false, bool force = false,
GLenum renderTarget = GL_TEXTURE_2D) {
setWrapST(wrap, wrap, bindTexture, force, renderTarget);
}
- void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false, bool force = false,
- GLenum renderTarget = GL_TEXTURE_2D) {
-
- if (firstWrap || force || wrapS != this->wrapS || wrapT != this->wrapT) {
- firstWrap = false;
-
- this->wrapS = wrapS;
- this->wrapT = wrapT;
-
- if (bindTexture) {
- glBindTexture(renderTarget, id);
- }
-
- glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS);
- glTexParameteri(renderTarget, GL_TEXTURE_WRAP_T, wrapT);
- }
- }
+ virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false,
+ bool force = false, GLenum renderTarget = GL_TEXTURE_2D);
- void setFilter(GLenum filter, bool bindTexture = false, bool force = false,
+ inline void setFilter(GLenum filter, bool bindTexture = false, bool force = false,
GLenum renderTarget = GL_TEXTURE_2D) {
setFilterMinMag(filter, filter, bindTexture, force, renderTarget);
}
- void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false, bool force = false,
- GLenum renderTarget = GL_TEXTURE_2D) {
+ virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false,
+ bool force = false, GLenum renderTarget = GL_TEXTURE_2D);
- if (firstFilter || force || min != minFilter || mag != magFilter) {
- firstFilter = false;
-
- minFilter = min;
- magFilter = mag;
-
- if (bindTexture) {
- glBindTexture(renderTarget, id);
- }
-
- if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
-
- glTexParameteri(renderTarget, GL_TEXTURE_MIN_FILTER, min);
- glTexParameteri(renderTarget, GL_TEXTURE_MAG_FILTER, mag);
- }
- }
+ /**
+ * Convenience method to call glDeleteTextures() on this texture's id.
+ */
+ void deleteTexture() const;
/**
* Name of the texture.
@@ -125,21 +89,28 @@ struct Texture {
*/
bool mipMap;
+ /**
+ * Optional, pointer to a texture coordinates mapper.
+ */
+ const UvMapper* uvMapper;
+
private:
/**
* Last wrap modes set on this texture. Defaults to GL_CLAMP_TO_EDGE.
*/
- GLenum wrapS;
- GLenum wrapT;
+ GLenum mWrapS;
+ GLenum mWrapT;
/**
* Last filters set on this texture. Defaults to GL_NEAREST.
*/
- GLenum minFilter;
- GLenum magFilter;
+ GLenum mMinFilter;
+ GLenum mMagFilter;
+
+ bool mFirstFilter;
+ bool mFirstWrap;
- bool firstFilter;
- bool firstWrap;
+ Caches& mCaches;
}; // struct Texture
class AutoTexture {
@@ -147,7 +118,7 @@ public:
AutoTexture(const Texture* texture): mTexture(texture) { }
~AutoTexture() {
if (mTexture && mTexture->cleanup) {
- glDeleteTextures(1, &mTexture->id);
+ mTexture->deleteTexture();
delete mTexture;
}
}
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 2378eb5..a63cac6 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -112,7 +112,7 @@ void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) {
if (mDebugEnabled) {
ALOGD("Texture deleted, size = %d", texture->bitmapSize);
}
- glDeleteTextures(1, &texture->id);
+ texture->deleteTexture();
delete texture;
}
}
@@ -139,7 +139,7 @@ Texture* TextureCache::get(SkBitmap* bitmap) {
}
}
- texture = new Texture;
+ texture = new Texture();
texture->bitmapSize = size;
generateTexture(bitmap, texture, false);
@@ -162,7 +162,7 @@ Texture* TextureCache::get(SkBitmap* bitmap) {
}
Texture* TextureCache::getTransient(SkBitmap* bitmap) {
- Texture* texture = new Texture;
+ Texture* texture = new Texture();
texture->bitmapSize = bitmap->rowBytes() * bitmap->height();
texture->cleanup = true;
@@ -235,7 +235,7 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege
texture->width = bitmap->width();
texture->height = bitmap->height();
- glBindTexture(GL_TEXTURE_2D, texture->id);
+ Caches::getInstance().bindTexture(texture->id);
switch (bitmap->getConfig()) {
case SkBitmap::kA8_Config:
diff --git a/libs/hwui/UvMapper.h b/libs/hwui/UvMapper.h
new file mode 100644
index 0000000..70428d2
--- /dev/null
+++ b/libs/hwui/UvMapper.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_UV_MAPPER_H
+#define ANDROID_HWUI_UV_MAPPER_H
+
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * This class can be used to map UV coordinates from the [0..1]
+ * range to other arbitrary ranges. All the methods below assume
+ * that the input values lie in the [0..1] range already.
+ */
+class UvMapper {
+public:
+ /**
+ * Using this constructor is equivalent to not using any mapping at all.
+ * UV coordinates in the [0..1] range remain in the [0..1] range.
+ */
+ UvMapper(): mIdentity(true), mMinU(0.0f), mMaxU(1.0f), mMinV(0.0f), mMaxV(1.0f) {
+ }
+
+ /**
+ * Creates a new mapper with the specified ranges for U and V coordinates.
+ * The parameter minU must be < maxU and minV must be < maxV.
+ */
+ UvMapper(float minU, float maxU, float minV, float maxV):
+ mMinU(minU), mMaxU(maxU), mMinV(minV), mMaxV(maxV) {
+ checkIdentity();
+ }
+
+ /**
+ * Returns true if calling the map*() methods has no effect (that is,
+ * texture coordinates remain in the 0..1 range.)
+ */
+ bool isIdentity() const {
+ return mIdentity;
+ }
+
+ /**
+ * Changes the U and V mapping ranges.
+ * The parameter minU must be < maxU and minV must be < maxV.
+ */
+ void setMapping(float minU, float maxU, float minV, float maxV) {
+ mMinU = minU;
+ mMaxU = maxU;
+ mMinV = minV;
+ mMaxV = maxV;
+ checkIdentity();
+ }
+
+ /**
+ * Maps a single value in the U range.
+ */
+ void mapU(float& u) const {
+ if (!mIdentity) u = lerp(mMinU, mMaxU, u);
+ }
+
+ /**
+ * Maps a single value in the V range.
+ */
+ void mapV(float& v) const {
+ if (!mIdentity) v = lerp(mMinV, mMaxV, v);
+ }
+
+ /**
+ * Maps the specified rectangle in place. This method assumes:
+ * - left = min. U
+ * - top = min. V
+ * - right = max. U
+ * - bottom = max. V
+ */
+ void map(Rect& texCoords) const {
+ if (!mIdentity) {
+ texCoords.left = lerp(mMinU, mMaxU, texCoords.left);
+ texCoords.right = lerp(mMinU, mMaxU, texCoords.right);
+ texCoords.top = lerp(mMinV, mMaxV, texCoords.top);
+ texCoords.bottom = lerp(mMinV, mMaxV, texCoords.bottom);
+ }
+ }
+
+ /**
+ * Maps the specified UV coordinates in place.
+ */
+ void map(float& u1, float& v1, float& u2, float& v2) const {
+ if (!mIdentity) {
+ u1 = lerp(mMinU, mMaxU, u1);
+ u2 = lerp(mMinU, mMaxU, u2);
+ v1 = lerp(mMinV, mMaxV, v1);
+ v2 = lerp(mMinV, mMaxV, v2);
+ }
+ }
+
+ void dump() const {
+ ALOGD("mapper[minU=%.2f maxU=%.2f minV=%.2f maxV=%.2f]", mMinU, mMaxU, mMinV, mMaxV);
+ }
+
+private:
+ static float lerp(float start, float stop, float amount) {
+ return start + (stop - start) * amount;
+ }
+
+ void checkIdentity() {
+ mIdentity = mMinU == 0.0f && mMaxU == 1.0f && mMinV == 0.0f && mMaxV == 1.0f;
+ }
+
+ bool mIdentity;
+ float mMinU;
+ float mMaxU;
+ float mMinV;
+ float mMaxV;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_UV_MAPPER_H
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index 523120e..c06762f 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_HWUI_VERTEX_H
#define ANDROID_HWUI_VERTEX_H
+#include "Vector.h"
+
namespace android {
namespace uirenderer {
@@ -30,6 +32,15 @@ struct Vertex {
vertex[0].position[0] = x;
vertex[0].position[1] = y;
}
+
+ static inline void set(Vertex* vertex, vec2 val) {
+ set(vertex, val.x, val.y);
+ }
+
+ static inline void copyWithOffset(Vertex* vertex, const Vertex& src, float x, float y) {
+ set(vertex, src.position[0] + x, src.position[1] + y);
+ }
+
}; // struct Vertex
/**
@@ -81,6 +92,12 @@ struct AlphaVertex : Vertex {
vertex[0].alpha = alpha;
}
+ static inline void copyWithOffset(AlphaVertex* vertex, const AlphaVertex& src,
+ float x, float y) {
+ Vertex::set(vertex, src.position[0] + x, src.position[1] + y);
+ vertex[0].alpha = src.alpha;
+ }
+
static inline void setColor(AlphaVertex* vertex, float alpha) {
vertex[0].alpha = alpha;
}
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index 6c5267d..5f15724 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -17,6 +17,7 @@
#include <SkGlyph.h>
#include "CacheTexture.h"
+#include "../Caches.h"
#include "../Debug.h"
#include "../Extensions.h"
#include "../PixelBuffer.h"
@@ -110,7 +111,8 @@ CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock* blockToRemove)
CacheTexture::CacheTexture(uint16_t width, uint16_t height, uint32_t maxQuadCount) :
mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
mLinearFiltering(false), mDirty(false), mNumGlyphs(0),
- mMesh(NULL), mCurrentQuad(0), mMaxQuadCount(maxQuadCount) {
+ mMesh(NULL), mCurrentQuad(0), mMaxQuadCount(maxQuadCount),
+ mCaches(Caches::getInstance()) {
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
@@ -154,7 +156,7 @@ void CacheTexture::releaseTexture() {
mTexture = NULL;
}
if (mTextureId) {
- glDeleteTextures(1, &mTextureId);
+ mCaches.deleteTexture(mTextureId);
mTextureId = 0;
}
mDirty = false;
@@ -166,7 +168,7 @@ void CacheTexture::setLinearFiltering(bool linearFiltering, bool bind) {
mLinearFiltering = linearFiltering;
const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
- if (bind) glBindTexture(GL_TEXTURE_2D, getTextureId());
+ if (bind) mCaches.bindTexture(getTextureId());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
}
@@ -186,7 +188,7 @@ void CacheTexture::allocateTexture() {
if (!mTextureId) {
glGenTextures(1, &mTextureId);
- glBindTexture(GL_TEXTURE_2D, mTextureId);
+ mCaches.bindTexture(mTextureId);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Initialize texture dimensions
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mWidth, mHeight, 0,
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
index ddcc836..8c3ea0b 100644
--- a/libs/hwui/font/CacheTexture.h
+++ b/libs/hwui/font/CacheTexture.h
@@ -30,6 +30,7 @@
namespace android {
namespace uirenderer {
+class Caches;
class PixelBuffer;
/**
@@ -178,9 +179,10 @@ private:
TextureVertex* mMesh;
uint32_t mCurrentQuad;
uint32_t mMaxQuadCount;
+ Caches& mCaches;
CacheBlock* mCacheBlocks;
- Rect mDirtyRect;
bool mHasES3;
+ Rect mDirtyRect;
};
}; // namespace uirenderer
diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h
index 477314b..f2a216f 100644
--- a/libs/hwui/thread/TaskManager.h
+++ b/libs/hwui/thread/TaskManager.h
@@ -43,7 +43,7 @@ public:
/**
* Returns true if this task manager can run tasks,
* false otherwise. This method will typically return
- * true on a single CPU core device.
+ * false on a single CPU core device.
*/
bool canRunTasks() const;