diff options
Diffstat (limited to 'libs')
93 files changed, 4732 insertions, 14431 deletions
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk deleted file mode 100644 index d21197e..0000000 --- a/libs/androidfw/Android.mk +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (C) 2010 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) - -# libandroidfw is partially built for the host (used by obbtool and others) -# These files are common to host and target builds. - -commonSources := \ - Asset.cpp \ - AssetDir.cpp \ - AssetManager.cpp \ - misc.cpp \ - ObbFile.cpp \ - ResourceTypes.cpp \ - StreamingZipInflater.cpp \ - ZipFileRO.cpp \ - ZipUtils.cpp - -deviceSources := \ - $(commonSources) \ - BackupData.cpp \ - BackupHelpers.cpp \ - CursorWindow.cpp - -hostSources := \ - $(commonSources) - -# For the host -# ===================================================== - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= $(hostSources) - -LOCAL_MODULE:= libandroidfw - -LOCAL_MODULE_TAGS := optional - -LOCAL_CFLAGS += -DSTATIC_ANDROIDFW_FOR_TOOLS - -LOCAL_C_INCLUDES := \ - external/zlib - -LOCAL_STATIC_LIBRARIES := liblog libziparchive-host libutils - -include $(BUILD_HOST_STATIC_LIBRARY) - - -# For the device -# ===================================================== - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= $(deviceSources) - -LOCAL_SHARED_LIBRARIES := \ - libbinder \ - liblog \ - libcutils \ - libutils \ - libz - -LOCAL_STATIC_LIBRARIES := libziparchive - -LOCAL_C_INCLUDES := \ - external/icu4c/common \ - external/zlib \ - system/core/include - -LOCAL_MODULE:= libandroidfw - -LOCAL_MODULE_TAGS := optional - -include $(BUILD_SHARED_LIBRARY) - - -# Include subdirectory makefiles -# ============================================================ - -# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework -# team really wants is to build the stuff defined by this makefile. -ifeq (,$(ONE_SHOT_MAKEFILE)) -include $(call first-makefiles-under,$(LOCAL_PATH)) -endif diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp deleted file mode 100644 index ce6cc38..0000000 --- a/libs/androidfw/Asset.cpp +++ /dev/null @@ -1,897 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Provide access to a read-only asset. -// - -#define LOG_TAG "asset" -//#define NDEBUG 0 - -#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/threads.h> - -#include <assert.h> -#include <errno.h> -#include <fcntl.h> -#include <memory.h> -#include <string.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -using namespace android; - -#ifndef O_BINARY -# define O_BINARY 0 -#endif - -static Mutex gAssetLock; -static int32_t gCount = 0; -static Asset* gHead = NULL; -static Asset* gTail = NULL; - -int32_t Asset::getGlobalCount() -{ - AutoMutex _l(gAssetLock); - return gCount; -} - -String8 Asset::getAssetAllocations() -{ - AutoMutex _l(gAssetLock); - String8 res; - Asset* cur = gHead; - while (cur != NULL) { - if (cur->isAllocated()) { - res.append(" "); - res.append(cur->getAssetSource()); - off64_t size = (cur->getLength()+512)/1024; - char buf[64]; - sprintf(buf, ": %dK\n", (int)size); - res.append(buf); - } - cur = cur->mNext; - } - - return res; -} - -Asset::Asset(void) - : mAccessMode(ACCESS_UNKNOWN) -{ - AutoMutex _l(gAssetLock); - gCount++; - mNext = mPrev = NULL; - if (gTail == NULL) { - gHead = gTail = this; - } else { - mPrev = gTail; - gTail->mNext = this; - gTail = this; - } - //ALOGI("Creating Asset %p #%d\n", this, gCount); -} - -Asset::~Asset(void) -{ - AutoMutex _l(gAssetLock); - gCount--; - if (gHead == this) { - gHead = mNext; - } - if (gTail == this) { - gTail = mPrev; - } - if (mNext != NULL) { - mNext->mPrev = mPrev; - } - if (mPrev != NULL) { - mPrev->mNext = mNext; - } - mNext = mPrev = NULL; - //ALOGI("Destroying Asset in %p #%d\n", this, gCount); -} - -/* - * Create a new Asset from a file on disk. There is a fair chance that - * the file doesn't actually exist. - * - * We can use "mode" to decide how we want to go about it. - */ -/*static*/ Asset* Asset::createFromFile(const char* fileName, AccessMode mode) -{ - _FileAsset* pAsset; - status_t result; - off64_t length; - int fd; - - fd = open(fileName, O_RDONLY | O_BINARY); - if (fd < 0) - return NULL; - - /* - * Under Linux, the lseek fails if we actually opened a directory. To - * be correct we should test the file type explicitly, but since we - * always open things read-only it doesn't really matter, so there's - * no value in incurring the extra overhead of an fstat() call. - */ - // TODO(kroot): replace this with fstat despite the plea above. -#if 1 - length = lseek64(fd, 0, SEEK_END); - if (length < 0) { - ::close(fd); - return NULL; - } - (void) lseek64(fd, 0, SEEK_SET); -#else - struct stat st; - if (fstat(fd, &st) < 0) { - ::close(fd); - return NULL; - } - - if (!S_ISREG(st.st_mode)) { - ::close(fd); - return NULL; - } -#endif - - pAsset = new _FileAsset; - result = pAsset->openChunk(fileName, fd, 0, length); - if (result != NO_ERROR) { - delete pAsset; - return NULL; - } - - pAsset->mAccessMode = mode; - return pAsset; -} - - -/* - * Create a new Asset from a compressed file on disk. There is a fair chance - * that the file doesn't actually exist. - * - * We currently support gzip files. We might want to handle .bz2 someday. - */ -/*static*/ Asset* Asset::createFromCompressedFile(const char* fileName, - AccessMode mode) -{ - _CompressedAsset* pAsset; - status_t result; - off64_t fileLen; - bool scanResult; - long offset; - int method; - long uncompressedLen, compressedLen; - int fd; - - fd = open(fileName, O_RDONLY | O_BINARY); - if (fd < 0) - return NULL; - - fileLen = lseek(fd, 0, SEEK_END); - if (fileLen < 0) { - ::close(fd); - return NULL; - } - (void) lseek(fd, 0, SEEK_SET); - - /* want buffered I/O for the file scan; must dup so fclose() is safe */ - FILE* fp = fdopen(dup(fd), "rb"); - if (fp == NULL) { - ::close(fd); - return NULL; - } - - unsigned long crc32; - scanResult = ZipUtils::examineGzip(fp, &method, &uncompressedLen, - &compressedLen, &crc32); - offset = ftell(fp); - fclose(fp); - if (!scanResult) { - ALOGD("File '%s' is not in gzip format\n", fileName); - ::close(fd); - return NULL; - } - - pAsset = new _CompressedAsset; - result = pAsset->openChunk(fd, offset, method, uncompressedLen, - compressedLen); - if (result != NO_ERROR) { - delete pAsset; - return NULL; - } - - pAsset->mAccessMode = mode; - return pAsset; -} - - -#if 0 -/* - * Create a new Asset from part of an open file. - */ -/*static*/ Asset* Asset::createFromFileSegment(int fd, off64_t offset, - size_t length, AccessMode mode) -{ - _FileAsset* pAsset; - status_t result; - - pAsset = new _FileAsset; - result = pAsset->openChunk(NULL, fd, offset, length); - if (result != NO_ERROR) - return NULL; - - pAsset->mAccessMode = mode; - return pAsset; -} - -/* - * Create a new Asset from compressed data in an open file. - */ -/*static*/ Asset* Asset::createFromCompressedData(int fd, off64_t offset, - int compressionMethod, size_t uncompressedLen, size_t compressedLen, - AccessMode mode) -{ - _CompressedAsset* pAsset; - status_t result; - - pAsset = new _CompressedAsset; - result = pAsset->openChunk(fd, offset, compressionMethod, - uncompressedLen, compressedLen); - if (result != NO_ERROR) - return NULL; - - pAsset->mAccessMode = mode; - return pAsset; -} -#endif - -/* - * Create a new Asset from a memory mapping. - */ -/*static*/ Asset* Asset::createFromUncompressedMap(FileMap* dataMap, - AccessMode mode) -{ - _FileAsset* pAsset; - status_t result; - - pAsset = new _FileAsset; - result = pAsset->openChunk(dataMap); - if (result != NO_ERROR) - return NULL; - - pAsset->mAccessMode = mode; - return pAsset; -} - -/* - * Create a new Asset from compressed data in a memory mapping. - */ -/*static*/ Asset* Asset::createFromCompressedMap(FileMap* dataMap, - int method, size_t uncompressedLen, AccessMode mode) -{ - _CompressedAsset* pAsset; - status_t result; - - pAsset = new _CompressedAsset; - result = pAsset->openChunk(dataMap, method, uncompressedLen); - if (result != NO_ERROR) - return NULL; - - pAsset->mAccessMode = mode; - return pAsset; -} - - -/* - * Do generic seek() housekeeping. Pass in the offset/whence values from - * the seek request, along with the current chunk offset and the chunk - * length. - * - * Returns the new chunk offset, or -1 if the seek is illegal. - */ -off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t maxPosn) -{ - off64_t newOffset; - - switch (whence) { - case SEEK_SET: - newOffset = offset; - break; - case SEEK_CUR: - newOffset = curPosn + offset; - break; - case SEEK_END: - newOffset = maxPosn + offset; - break; - default: - ALOGW("unexpected whence %d\n", whence); - // this was happening due to an off64_t size mismatch - assert(false); - return (off64_t) -1; - } - - if (newOffset < 0 || newOffset > maxPosn) { - ALOGW("seek out of range: want %ld, end=%ld\n", - (long) newOffset, (long) maxPosn); - return (off64_t) -1; - } - - return newOffset; -} - - -/* - * =========================================================================== - * _FileAsset - * =========================================================================== - */ - -/* - * Constructor. - */ -_FileAsset::_FileAsset(void) - : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL) -{ -} - -/* - * Destructor. Release resources. - */ -_FileAsset::~_FileAsset(void) -{ - close(); -} - -/* - * Operate on a chunk of an uncompressed file. - * - * Zero-length chunks are allowed. - */ -status_t _FileAsset::openChunk(const char* fileName, int fd, off64_t offset, size_t length) -{ - assert(mFp == NULL); // no reopen - assert(mMap == NULL); - assert(fd >= 0); - assert(offset >= 0); - - /* - * Seek to end to get file length. - */ - off64_t fileLength; - fileLength = lseek64(fd, 0, SEEK_END); - if (fileLength == (off64_t) -1) { - // probably a bad file descriptor - ALOGD("failed lseek (errno=%d)\n", errno); - return UNKNOWN_ERROR; - } - - if ((off64_t) (offset + length) > fileLength) { - ALOGD("start (%ld) + len (%ld) > end (%ld)\n", - (long) offset, (long) length, (long) fileLength); - return BAD_INDEX; - } - - /* after fdopen, the fd will be closed on fclose() */ - mFp = fdopen(fd, "rb"); - if (mFp == NULL) - return UNKNOWN_ERROR; - - mStart = offset; - mLength = length; - assert(mOffset == 0); - - /* seek the FILE* to the start of chunk */ - if (fseek(mFp, mStart, SEEK_SET) != 0) { - assert(false); - } - - mFileName = fileName != NULL ? strdup(fileName) : NULL; - - return NO_ERROR; -} - -/* - * Create the chunk from the map. - */ -status_t _FileAsset::openChunk(FileMap* dataMap) -{ - assert(mFp == NULL); // no reopen - assert(mMap == NULL); - assert(dataMap != NULL); - - mMap = dataMap; - mStart = -1; // not used - mLength = dataMap->getDataLength(); - assert(mOffset == 0); - - return NO_ERROR; -} - -/* - * Read a chunk of data. - */ -ssize_t _FileAsset::read(void* buf, size_t count) -{ - size_t maxLen; - size_t actual; - - assert(mOffset >= 0 && mOffset <= mLength); - - if (getAccessMode() == ACCESS_BUFFER) { - /* - * On first access, read or map the entire file. The caller has - * requested buffer access, either because they're going to be - * using the buffer or because what they're doing has appropriate - * performance needs and access patterns. - */ - if (mBuf == NULL) - getBuffer(false); - } - - /* adjust count if we're near EOF */ - maxLen = mLength - mOffset; - if (count > maxLen) - count = maxLen; - - if (!count) - return 0; - - if (mMap != NULL) { - /* copy from mapped area */ - //printf("map read\n"); - memcpy(buf, (char*)mMap->getDataPtr() + mOffset, count); - actual = count; - } else if (mBuf != NULL) { - /* copy from buffer */ - //printf("buf read\n"); - memcpy(buf, (char*)mBuf + mOffset, count); - actual = count; - } else { - /* read from the file */ - //printf("file read\n"); - if (ftell(mFp) != mStart + mOffset) { - ALOGE("Hosed: %ld != %ld+%ld\n", - ftell(mFp), (long) mStart, (long) mOffset); - assert(false); - } - - /* - * This returns 0 on error or eof. We need to use ferror() or feof() - * to tell the difference, but we don't currently have those on the - * device. However, we know how much data is *supposed* to be in the - * file, so if we don't read the full amount we know something is - * hosed. - */ - actual = fread(buf, 1, count, mFp); - if (actual == 0) // something failed -- I/O error? - return -1; - - assert(actual == count); - } - - mOffset += actual; - return actual; -} - -/* - * Seek to a new position. - */ -off64_t _FileAsset::seek(off64_t offset, int whence) -{ - off64_t newPosn; - off64_t actualOffset; - - // compute new position within chunk - newPosn = handleSeek(offset, whence, mOffset, mLength); - if (newPosn == (off64_t) -1) - return newPosn; - - actualOffset = mStart + newPosn; - - if (mFp != NULL) { - if (fseek(mFp, (long) actualOffset, SEEK_SET) != 0) - return (off64_t) -1; - } - - mOffset = actualOffset - mStart; - return mOffset; -} - -/* - * Close the asset. - */ -void _FileAsset::close(void) -{ - if (mMap != NULL) { - mMap->release(); - mMap = NULL; - } - if (mBuf != NULL) { - delete[] mBuf; - mBuf = NULL; - } - - if (mFileName != NULL) { - free(mFileName); - mFileName = NULL; - } - - if (mFp != NULL) { - // can only be NULL when called from destructor - // (otherwise we would never return this object) - fclose(mFp); - mFp = NULL; - } -} - -/* - * Return a read-only pointer to a buffer. - * - * We can either read the whole thing in or map the relevant piece of - * the source file. Ideally a map would be established at a higher - * level and we'd be using a different object, but we didn't, so we - * deal with it here. - */ -const void* _FileAsset::getBuffer(bool wordAligned) -{ - /* subsequent requests just use what we did previously */ - if (mBuf != NULL) - return mBuf; - if (mMap != NULL) { - if (!wordAligned) { - return mMap->getDataPtr(); - } - return ensureAlignment(mMap); - } - - assert(mFp != NULL); - - if (mLength < kReadVsMapThreshold) { - unsigned char* buf; - long allocLen; - - /* zero-length files are allowed; not sure about zero-len allocs */ - /* (works fine with gcc + x86linux) */ - allocLen = mLength; - if (mLength == 0) - allocLen = 1; - - buf = new unsigned char[allocLen]; - if (buf == NULL) { - ALOGE("alloc of %ld bytes failed\n", (long) allocLen); - return NULL; - } - - ALOGV("Asset %p allocating buffer size %d (smaller than threshold)", this, (int)allocLen); - if (mLength > 0) { - long oldPosn = ftell(mFp); - fseek(mFp, mStart, SEEK_SET); - if (fread(buf, 1, mLength, mFp) != (size_t) mLength) { - ALOGE("failed reading %ld bytes\n", (long) mLength); - delete[] buf; - return NULL; - } - fseek(mFp, oldPosn, SEEK_SET); - } - - ALOGV(" getBuffer: loaded into buffer\n"); - - mBuf = buf; - return mBuf; - } else { - FileMap* map; - - map = new FileMap; - if (!map->create(NULL, fileno(mFp), mStart, mLength, true)) { - map->release(); - return NULL; - } - - ALOGV(" getBuffer: mapped\n"); - - mMap = map; - if (!wordAligned) { - return mMap->getDataPtr(); - } - return ensureAlignment(mMap); - } -} - -int _FileAsset::openFileDescriptor(off64_t* outStart, off64_t* outLength) const -{ - if (mMap != NULL) { - const char* fname = mMap->getFileName(); - if (fname == NULL) { - fname = mFileName; - } - if (fname == NULL) { - return -1; - } - *outStart = mMap->getDataOffset(); - *outLength = mMap->getDataLength(); - return open(fname, O_RDONLY | O_BINARY); - } - if (mFileName == NULL) { - return -1; - } - *outStart = mStart; - *outLength = mLength; - return open(mFileName, O_RDONLY | O_BINARY); -} - -const void* _FileAsset::ensureAlignment(FileMap* map) -{ - void* data = map->getDataPtr(); - if ((((size_t)data)&0x3) == 0) { - // We can return this directly if it is aligned on a word - // boundary. - ALOGV("Returning aligned FileAsset %p (%s).", this, - getAssetSource()); - return data; - } - // If not aligned on a word boundary, then we need to copy it into - // our own buffer. - ALOGV("Copying FileAsset %p (%s) to buffer size %d to make it aligned.", this, - getAssetSource(), (int)mLength); - unsigned char* buf = new unsigned char[mLength]; - if (buf == NULL) { - ALOGE("alloc of %ld bytes failed\n", (long) mLength); - return NULL; - } - memcpy(buf, data, mLength); - mBuf = buf; - return buf; -} - -/* - * =========================================================================== - * _CompressedAsset - * =========================================================================== - */ - -/* - * Constructor. - */ -_CompressedAsset::_CompressedAsset(void) - : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0), - mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL) -{ -} - -/* - * Destructor. Release resources. - */ -_CompressedAsset::~_CompressedAsset(void) -{ - close(); -} - -/* - * Open a chunk of compressed data inside a file. - * - * This currently just sets up some values and returns. On the first - * read, we expand the entire file into a buffer and return data from it. - */ -status_t _CompressedAsset::openChunk(int fd, off64_t offset, - int compressionMethod, size_t uncompressedLen, size_t compressedLen) -{ - assert(mFd < 0); // no re-open - assert(mMap == NULL); - assert(fd >= 0); - assert(offset >= 0); - assert(compressedLen > 0); - - if (compressionMethod != ZipFileRO::kCompressDeflated) { - assert(false); - return UNKNOWN_ERROR; - } - - mStart = offset; - mCompressedLen = compressedLen; - mUncompressedLen = uncompressedLen; - assert(mOffset == 0); - mFd = fd; - assert(mBuf == NULL); - - if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) { - mZipInflater = new StreamingZipInflater(mFd, offset, uncompressedLen, compressedLen); - } - - return NO_ERROR; -} - -/* - * Open a chunk of compressed data in a mapped region. - * - * Nothing is expanded until the first read call. - */ -status_t _CompressedAsset::openChunk(FileMap* dataMap, int compressionMethod, - size_t uncompressedLen) -{ - assert(mFd < 0); // no re-open - assert(mMap == NULL); - assert(dataMap != NULL); - - if (compressionMethod != ZipFileRO::kCompressDeflated) { - assert(false); - return UNKNOWN_ERROR; - } - - mMap = dataMap; - mStart = -1; // not used - mCompressedLen = dataMap->getDataLength(); - mUncompressedLen = uncompressedLen; - assert(mOffset == 0); - - if (uncompressedLen > StreamingZipInflater::OUTPUT_CHUNK_SIZE) { - mZipInflater = new StreamingZipInflater(dataMap, uncompressedLen); - } - return NO_ERROR; -} - -/* - * Read data from a chunk of compressed data. - * - * [For now, that's just copying data out of a buffer.] - */ -ssize_t _CompressedAsset::read(void* buf, size_t count) -{ - size_t maxLen; - size_t actual; - - assert(mOffset >= 0 && mOffset <= mUncompressedLen); - - /* If we're relying on a streaming inflater, go through that */ - if (mZipInflater) { - actual = mZipInflater->read(buf, count); - } else { - if (mBuf == NULL) { - if (getBuffer(false) == NULL) - return -1; - } - assert(mBuf != NULL); - - /* adjust count if we're near EOF */ - maxLen = mUncompressedLen - mOffset; - if (count > maxLen) - count = maxLen; - - if (!count) - return 0; - - /* copy from buffer */ - //printf("comp buf read\n"); - memcpy(buf, (char*)mBuf + mOffset, count); - actual = count; - } - - mOffset += actual; - return actual; -} - -/* - * Handle a seek request. - * - * If we're working in a streaming mode, this is going to be fairly - * expensive, because it requires plowing through a bunch of compressed - * data. - */ -off64_t _CompressedAsset::seek(off64_t offset, int whence) -{ - off64_t newPosn; - - // compute new position within chunk - newPosn = handleSeek(offset, whence, mOffset, mUncompressedLen); - if (newPosn == (off64_t) -1) - return newPosn; - - if (mZipInflater) { - mZipInflater->seekAbsolute(newPosn); - } - mOffset = newPosn; - return mOffset; -} - -/* - * Close the asset. - */ -void _CompressedAsset::close(void) -{ - if (mMap != NULL) { - mMap->release(); - mMap = NULL; - } - - delete[] mBuf; - mBuf = NULL; - - delete mZipInflater; - mZipInflater = NULL; - - if (mFd > 0) { - ::close(mFd); - mFd = -1; - } -} - -/* - * Get a pointer to a read-only buffer of data. - * - * The first time this is called, we expand the compressed data into a - * buffer. - */ -const void* _CompressedAsset::getBuffer(bool) -{ - unsigned char* buf = NULL; - - if (mBuf != NULL) - return mBuf; - - /* - * Allocate a buffer and read the file into it. - */ - buf = new unsigned char[mUncompressedLen]; - if (buf == NULL) { - ALOGW("alloc %ld bytes failed\n", (long) mUncompressedLen); - goto bail; - } - - if (mMap != NULL) { - if (!ZipUtils::inflateToBuffer(mMap->getDataPtr(), buf, - mUncompressedLen, mCompressedLen)) - goto bail; - } else { - assert(mFd >= 0); - - /* - * Seek to the start of the compressed data. - */ - if (lseek(mFd, mStart, SEEK_SET) != mStart) - goto bail; - - /* - * Expand the data into it. - */ - if (!ZipUtils::inflateToBuffer(mFd, buf, mUncompressedLen, - mCompressedLen)) - goto bail; - } - - /* - * Success - now that we have the full asset in RAM we - * no longer need the streaming inflater - */ - delete mZipInflater; - mZipInflater = NULL; - - mBuf = buf; - buf = NULL; - -bail: - delete[] buf; - return mBuf; -} - diff --git a/libs/androidfw/AssetDir.cpp b/libs/androidfw/AssetDir.cpp deleted file mode 100644 index 475f521..0000000 --- a/libs/androidfw/AssetDir.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Provide access to a virtual directory in "asset space". Most of the -// implementation is in the header file or in friend functions in -// AssetManager. -// -#include <androidfw/AssetDir.h> - -using namespace android; - - -/* - * Find a matching entry in a vector of FileInfo. Because it's sorted, we - * can use a binary search. - * - * Assumes the vector is sorted in ascending order. - */ -/*static*/ int AssetDir::FileInfo::findEntry(const SortedVector<FileInfo>* pVector, - const String8& fileName) -{ - FileInfo tmpInfo; - - tmpInfo.setFileName(fileName); - return pVector->indexOf(tmpInfo); - -#if 0 // don't need this after all (uses 1/2 compares of SortedVector though) - int lo, hi, cur; - - lo = 0; - hi = pVector->size() -1; - while (lo <= hi) { - int cmp; - - cur = (hi + lo) / 2; - cmp = strcmp(pVector->itemAt(cur).getFileName(), fileName); - if (cmp == 0) { - /* match, bail */ - return cur; - } else if (cmp < 0) { - /* too low */ - lo = cur + 1; - } else { - /* too high */ - hi = cur -1; - } - } - - return -1; -#endif -} - diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp deleted file mode 100644 index 503c030..0000000 --- a/libs/androidfw/AssetManager.cpp +++ /dev/null @@ -1,2036 +0,0 @@ -/* - * Copyright (C) 2006 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. - */ - -// -// Provide access to read-only assets. -// - -#define LOG_TAG "asset" -#define ATRACE_TAG ATRACE_TAG_RESOURCES -//#define LOG_NDEBUG 0 - -#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> -#ifdef HAVE_ANDROID_OS -#include <cutils/trace.h> -#endif - -#include <assert.h> -#include <dirent.h> -#include <errno.h> -#include <fcntl.h> -#include <strings.h> -#include <sys/stat.h> -#include <unistd.h> - -#ifndef TEMP_FAILURE_RETRY -/* Used to retry syscalls that can return EINTR. */ -#define TEMP_FAILURE_RETRY(exp) ({ \ - typeof (exp) _rc; \ - do { \ - _rc = (exp); \ - } while (_rc == -1 && errno == EINTR); \ - _rc; }) -#endif - -#ifdef HAVE_ANDROID_OS -#define MY_TRACE_BEGIN(x) ATRACE_BEGIN(x) -#define MY_TRACE_END() ATRACE_END() -#else -#define MY_TRACE_BEGIN(x) -#define MY_TRACE_END() -#endif - -using namespace android; - -/* - * Names for default app, locale, and vendor. We might want to change - * these to be an actual locale, e.g. always use en-US as the default. - */ -static const char* kDefaultLocale = "default"; -static const char* kDefaultVendor = "default"; -static const char* kAssetsRoot = "assets"; -static const char* kAppZipName = NULL; //"classes.jar"; -static const char* kSystemAssets = "framework/framework-res.apk"; -static const char* kIdmapCacheDir = "resource-cache"; - -static const char* kExcludeExtension = ".EXCLUDE"; - -static Asset* const kExcludedAsset = (Asset*) 0xd000000d; - -static volatile int32_t gCount = 0; - -namespace { - // Transform string /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap - String8 idmapPathForPackagePath(const String8& pkgPath) - { - const char* root = getenv("ANDROID_DATA"); - LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set"); - String8 path(root); - path.appendPath(kIdmapCacheDir); - - char buf[256]; // 256 chars should be enough for anyone... - strncpy(buf, pkgPath.string(), 255); - buf[255] = '\0'; - char* filename = buf; - while (*filename && *filename == '/') { - ++filename; - } - char* p = filename; - while (*p) { - if (*p == '/') { - *p = '@'; - } - ++p; - } - path.appendPath(filename); - path.append("@idmap"); - - return path; - } - - /* - * Like strdup(), but uses C++ "new" operator instead of malloc. - */ - static char* strdupNew(const char* str) - { - char* newStr; - int len; - - if (str == NULL) - return NULL; - - len = strlen(str); - newStr = new char[len+1]; - memcpy(newStr, str, len+1); - - return newStr; - } -} - -/* - * =========================================================================== - * AssetManager - * =========================================================================== - */ - -int32_t AssetManager::getGlobalCount() -{ - return gCount; -} - -AssetManager::AssetManager(CacheMode cacheMode) - : mLocale(NULL), mVendor(NULL), - mResources(NULL), mConfig(new ResTable_config), - mCacheMode(cacheMode), mCacheValid(false) -{ - int count = android_atomic_inc(&gCount)+1; - //ALOGI("Creating AssetManager %p #%d\n", this, count); - memset(mConfig, 0, sizeof(ResTable_config)); -} - -AssetManager::~AssetManager(void) -{ - int count = android_atomic_dec(&gCount); - //ALOGI("Destroying AssetManager in %p #%d\n", this, count); - - delete mConfig; - delete mResources; - - // don't have a String class yet, so make sure we clean up - delete[] mLocale; - delete[] mVendor; -} - -bool AssetManager::addAssetPath(const String8& path, void** cookie) -{ - AutoMutex _l(mLock); - - asset_path ap; - - String8 realPath(path); - if (kAppZipName) { - realPath.appendPath(kAppZipName); - } - ap.type = ::getFileType(realPath.string()); - if (ap.type == kFileTypeRegular) { - ap.path = realPath; - } else { - ap.path = path; - ap.type = ::getFileType(path.string()); - if (ap.type != kFileTypeDirectory && ap.type != kFileTypeRegular) { - ALOGW("Asset path %s is neither a directory nor file (type=%d).", - path.string(), (int)ap.type); - return false; - } - } - - // Skip if we have it already. - for (size_t i=0; i<mAssetPaths.size(); i++) { - if (mAssetPaths[i].path == ap.path) { - if (cookie) { - *cookie = (void*)(i+1); - } - return true; - } - } - - ALOGV("In %p Asset %s path: %s", this, - ap.type == kFileTypeDirectory ? "dir" : "zip", ap.path.string()); - - mAssetPaths.add(ap); - - // new paths are always added at the end - if (cookie) { - *cookie = (void*)mAssetPaths.size(); - } - - // add overlay packages for /system/framework; apps are handled by the - // (Java) package manager - if (strncmp(path.string(), "/system/framework/", 18) == 0) { - // When there is an environment variable for /vendor, this - // should be changed to something similar to how ANDROID_ROOT - // and ANDROID_DATA are used in this file. - String8 overlayPath("/vendor/overlay/framework/"); - overlayPath.append(path.getPathLeaf()); - if (TEMP_FAILURE_RETRY(access(overlayPath.string(), R_OK)) == 0) { - asset_path oap; - oap.path = overlayPath; - oap.type = ::getFileType(overlayPath.string()); - bool addOverlay = (oap.type == kFileTypeRegular); // only .apks supported as overlay - if (addOverlay) { - oap.idmap = idmapPathForPackagePath(overlayPath); - - if (isIdmapStaleLocked(ap.path, oap.path, oap.idmap)) { - addOverlay = createIdmapFileLocked(ap.path, oap.path, oap.idmap); - } - } - if (addOverlay) { - mAssetPaths.add(oap); - } else { - ALOGW("failed to add overlay package %s\n", overlayPath.string()); - } - } - } - - return true; -} - -bool AssetManager::isIdmapStaleLocked(const String8& originalPath, const String8& overlayPath, - const String8& idmapPath) -{ - struct stat st; - if (TEMP_FAILURE_RETRY(stat(idmapPath.string(), &st)) == -1) { - if (errno == ENOENT) { - return true; // non-existing idmap is always stale - } else { - ALOGW("failed to stat file %s: %s\n", idmapPath.string(), strerror(errno)); - return false; - } - } - if (st.st_size < ResTable::IDMAP_HEADER_SIZE_BYTES) { - ALOGW("file %s has unexpectedly small size=%zd\n", idmapPath.string(), (size_t)st.st_size); - return false; - } - int fd = TEMP_FAILURE_RETRY(::open(idmapPath.string(), O_RDONLY)); - if (fd == -1) { - ALOGW("failed to open file %s: %s\n", idmapPath.string(), strerror(errno)); - return false; - } - char buf[ResTable::IDMAP_HEADER_SIZE_BYTES]; - ssize_t bytesLeft = ResTable::IDMAP_HEADER_SIZE_BYTES; - for (;;) { - ssize_t r = TEMP_FAILURE_RETRY(read(fd, buf + ResTable::IDMAP_HEADER_SIZE_BYTES - bytesLeft, - bytesLeft)); - if (r < 0) { - TEMP_FAILURE_RETRY(close(fd)); - return false; - } - bytesLeft -= r; - if (bytesLeft == 0) { - break; - } - } - TEMP_FAILURE_RETRY(close(fd)); - - uint32_t cachedOriginalCrc, cachedOverlayCrc; - if (!ResTable::getIdmapInfo(buf, ResTable::IDMAP_HEADER_SIZE_BYTES, - &cachedOriginalCrc, &cachedOverlayCrc)) { - return false; - } - - uint32_t actualOriginalCrc, actualOverlayCrc; - if (!getZipEntryCrcLocked(originalPath, "resources.arsc", &actualOriginalCrc)) { - return false; - } - if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &actualOverlayCrc)) { - return false; - } - return cachedOriginalCrc != actualOriginalCrc || cachedOverlayCrc != actualOverlayCrc; -} - -bool AssetManager::getZipEntryCrcLocked(const String8& zipPath, const char* entryFilename, - uint32_t* pCrc) -{ - asset_path ap; - ap.path = zipPath; - const ZipFileRO* zip = getZipFileLocked(ap); - if (zip == NULL) { - return false; - } - const ZipEntryRO entry = zip->findEntryByName(entryFilename); - if (entry == NULL) { - return false; - } - - const bool gotInfo = zip->getEntryInfo(entry, NULL, NULL, NULL, NULL, NULL, (long*)pCrc); - zip->releaseEntry(entry); - - return gotInfo; -} - -bool AssetManager::createIdmapFileLocked(const String8& originalPath, const String8& overlayPath, - const String8& idmapPath) -{ - ALOGD("%s: originalPath=%s overlayPath=%s idmapPath=%s\n", - __FUNCTION__, originalPath.string(), overlayPath.string(), idmapPath.string()); - ResTable tables[2]; - const String8* paths[2] = { &originalPath, &overlayPath }; - uint32_t originalCrc, overlayCrc; - bool retval = false; - ssize_t offset = 0; - int fd = 0; - uint32_t* data = NULL; - size_t size; - - for (int i = 0; i < 2; ++i) { - asset_path ap; - ap.type = kFileTypeRegular; - ap.path = *paths[i]; - Asset* ass = openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, ap); - if (ass == NULL) { - ALOGW("failed to find resources.arsc in %s\n", ap.path.string()); - goto error; - } - tables[i].add(ass, (void*)1, false); - } - - if (!getZipEntryCrcLocked(originalPath, "resources.arsc", &originalCrc)) { - ALOGW("failed to retrieve crc for resources.arsc in %s\n", originalPath.string()); - goto error; - } - if (!getZipEntryCrcLocked(overlayPath, "resources.arsc", &overlayCrc)) { - ALOGW("failed to retrieve crc for resources.arsc in %s\n", overlayPath.string()); - goto error; - } - - if (tables[0].createIdmap(tables[1], originalCrc, overlayCrc, - (void**)&data, &size) != NO_ERROR) { - ALOGW("failed to generate idmap data for file %s\n", idmapPath.string()); - goto error; - } - - // This should be abstracted (eg replaced by a stand-alone - // application like dexopt, triggered by something equivalent to - // installd). - fd = TEMP_FAILURE_RETRY(::open(idmapPath.string(), O_WRONLY | O_CREAT | O_TRUNC, 0644)); - if (fd == -1) { - ALOGW("failed to write idmap file %s (open: %s)\n", idmapPath.string(), strerror(errno)); - goto error_free; - } - for (;;) { - ssize_t written = TEMP_FAILURE_RETRY(write(fd, data + offset, size)); - if (written < 0) { - ALOGW("failed to write idmap file %s (write: %s)\n", idmapPath.string(), - strerror(errno)); - goto error_close; - } - size -= (size_t)written; - offset += written; - if (size == 0) { - break; - } - } - - retval = true; -error_close: - TEMP_FAILURE_RETRY(close(fd)); -error_free: - free(data); -error: - return retval; -} - -bool AssetManager::addDefaultAssets() -{ - const char* root = getenv("ANDROID_ROOT"); - LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_ROOT not set"); - - String8 path(root); - path.appendPath(kSystemAssets); - - return addAssetPath(path, NULL); -} - -void* AssetManager::nextAssetPath(void* cookie) const -{ - AutoMutex _l(mLock); - size_t next = ((size_t)cookie)+1; - return next > mAssetPaths.size() ? NULL : (void*)next; -} - -String8 AssetManager::getAssetPath(void* cookie) const -{ - AutoMutex _l(mLock); - const size_t which = ((size_t)cookie)-1; - if (which < mAssetPaths.size()) { - return mAssetPaths[which].path; - } - return String8(); -} - -/* - * Set the current locale. Use NULL to indicate no locale. - * - * Close and reopen Zip archives as appropriate, and reset cached - * information in the locale-specific sections of the tree. - */ -void AssetManager::setLocale(const char* locale) -{ - AutoMutex _l(mLock); - setLocaleLocked(locale); -} - -void AssetManager::setLocaleLocked(const char* locale) -{ - if (mLocale != NULL) { - /* previously set, purge cached data */ - purgeFileNameCacheLocked(); - //mZipSet.purgeLocale(); - delete[] mLocale; - } - mLocale = strdupNew(locale); - - updateResourceParamsLocked(); -} - -/* - * Set the current vendor. Use NULL to indicate no vendor. - * - * Close and reopen Zip archives as appropriate, and reset cached - * information in the vendor-specific sections of the tree. - */ -void AssetManager::setVendor(const char* vendor) -{ - AutoMutex _l(mLock); - - if (mVendor != NULL) { - /* previously set, purge cached data */ - purgeFileNameCacheLocked(); - //mZipSet.purgeVendor(); - delete[] mVendor; - } - mVendor = strdupNew(vendor); -} - -void AssetManager::setConfiguration(const ResTable_config& config, const char* locale) -{ - AutoMutex _l(mLock); - *mConfig = config; - if (locale) { - setLocaleLocked(locale); - } else if (config.language[0] != 0) { - char spec[9]; - spec[0] = config.language[0]; - spec[1] = config.language[1]; - if (config.country[0] != 0) { - spec[2] = '_'; - spec[3] = config.country[0]; - spec[4] = config.country[1]; - spec[5] = 0; - } else { - spec[3] = 0; - } - setLocaleLocked(spec); - } else { - updateResourceParamsLocked(); - } -} - -void AssetManager::getConfiguration(ResTable_config* outConfig) const -{ - AutoMutex _l(mLock); - *outConfig = *mConfig; -} - -/* - * Open an asset. - * - * The data could be; - * - In a file on disk (assetBase + fileName). - * - In a compressed file on disk (assetBase + fileName.gz). - * - In a Zip archive, uncompressed or compressed. - * - * It can be in a number of different directories and Zip archives. - * The search order is: - * - [appname] - * - locale + vendor - * - "default" + vendor - * - locale + "default" - * - "default + "default" - * - "common" - * - (same as above) - * - * To find a particular file, we have to try up to eight paths with - * all three forms of data. - * - * We should probably reject requests for "illegal" filenames, e.g. those - * with illegal characters or "../" backward relative paths. - */ -Asset* AssetManager::open(const char* fileName, AccessMode mode) -{ - AutoMutex _l(mLock); - - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - - - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - - String8 assetName(kAssetsRoot); - assetName.appendPath(fileName); - - /* - * For each top-level asset path, search for the asset. - */ - - size_t i = mAssetPaths.size(); - while (i > 0) { - i--; - ALOGV("Looking for asset '%s' in '%s'\n", - assetName.string(), mAssetPaths.itemAt(i).path.string()); - Asset* pAsset = openNonAssetInPathLocked(assetName.string(), mode, mAssetPaths.itemAt(i)); - if (pAsset != NULL) { - return pAsset != kExcludedAsset ? pAsset : NULL; - } - } - - return NULL; -} - -/* - * Open a non-asset file as if it were an asset. - * - * The "fileName" is the partial path starting from the application - * name. - */ -Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode) -{ - AutoMutex _l(mLock); - - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - - - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - - /* - * For each top-level asset path, search for the asset. - */ - - size_t i = mAssetPaths.size(); - while (i > 0) { - i--; - ALOGV("Looking for non-asset '%s' in '%s'\n", fileName, mAssetPaths.itemAt(i).path.string()); - Asset* pAsset = openNonAssetInPathLocked( - fileName, mode, mAssetPaths.itemAt(i)); - if (pAsset != NULL) { - return pAsset != kExcludedAsset ? pAsset : NULL; - } - } - - return NULL; -} - -Asset* AssetManager::openNonAsset(void* cookie, const char* fileName, AccessMode mode) -{ - const size_t which = ((size_t)cookie)-1; - - AutoMutex _l(mLock); - - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - - - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - - if (which < mAssetPaths.size()) { - ALOGV("Looking for non-asset '%s' in '%s'\n", fileName, - mAssetPaths.itemAt(which).path.string()); - Asset* pAsset = openNonAssetInPathLocked( - fileName, mode, mAssetPaths.itemAt(which)); - if (pAsset != NULL) { - return pAsset != kExcludedAsset ? pAsset : NULL; - } - } - - return NULL; -} - -/* - * Get the type of a file in the asset namespace. - * - * This currently only works for regular files. All others (including - * directories) will return kFileTypeNonexistent. - */ -FileType AssetManager::getFileType(const char* fileName) -{ - Asset* pAsset = NULL; - - /* - * Open the asset. This is less efficient than simply finding the - * file, but it's not too bad (we don't uncompress or mmap data until - * the first read() call). - */ - pAsset = open(fileName, Asset::ACCESS_STREAMING); - delete pAsset; - - if (pAsset == NULL) - return kFileTypeNonexistent; - else - return kFileTypeRegular; -} - -const ResTable* AssetManager::getResTable(bool required) const -{ - ResTable* rt = mResources; - if (rt) { - return rt; - } - - // Iterate through all asset packages, collecting resources from each. - - AutoMutex _l(mLock); - - if (mResources != NULL) { - return mResources; - } - - if (required) { - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - } - - if (mCacheMode != CACHE_OFF && !mCacheValid) - const_cast<AssetManager*>(this)->loadFileNameCacheLocked(); - - const size_t N = mAssetPaths.size(); - for (size_t i=0; i<N; i++) { - Asset* ass = NULL; - ResTable* sharedRes = NULL; - bool shared = true; - const asset_path& ap = mAssetPaths.itemAt(i); - MY_TRACE_BEGIN(ap.path.string()); - Asset* idmap = openIdmapLocked(ap); - ALOGV("Looking for resource asset in '%s'\n", ap.path.string()); - if (ap.type != kFileTypeDirectory) { - if (i == 0) { - // The first item is typically the framework resources, - // which we want to avoid parsing every time. - sharedRes = const_cast<AssetManager*>(this)-> - mZipSet.getZipResourceTable(ap.path); - } - if (sharedRes == NULL) { - ass = const_cast<AssetManager*>(this)-> - mZipSet.getZipResourceTableAsset(ap.path); - if (ass == NULL) { - ALOGV("loading resource table %s\n", ap.path.string()); - ass = const_cast<AssetManager*>(this)-> - openNonAssetInPathLocked("resources.arsc", - Asset::ACCESS_BUFFER, - ap); - if (ass != NULL && ass != kExcludedAsset) { - ass = const_cast<AssetManager*>(this)-> - mZipSet.setZipResourceTableAsset(ap.path, ass); - } - } - - if (i == 0 && ass != NULL) { - // If this is the first resource table in the asset - // manager, then we are going to cache it so that we - // can quickly copy it out for others. - ALOGV("Creating shared resources for %s", ap.path.string()); - sharedRes = new ResTable(); - sharedRes->add(ass, (void*)(i+1), false, idmap); - sharedRes = const_cast<AssetManager*>(this)-> - mZipSet.setZipResourceTable(ap.path, sharedRes); - } - } - } else { - ALOGV("loading resource table %s\n", ap.path.string()); - ass = const_cast<AssetManager*>(this)-> - openNonAssetInPathLocked("resources.arsc", - Asset::ACCESS_BUFFER, - ap); - shared = false; - } - if ((ass != NULL || sharedRes != NULL) && ass != kExcludedAsset) { - if (rt == NULL) { - mResources = rt = new ResTable(); - updateResourceParamsLocked(); - } - ALOGV("Installing resource asset %p in to table %p\n", ass, mResources); - if (sharedRes != NULL) { - ALOGV("Copying existing resources for %s", ap.path.string()); - rt->add(sharedRes); - } else { - ALOGV("Parsing resources for %s", ap.path.string()); - rt->add(ass, (void*)(i+1), !shared, idmap); - } - - if (!shared) { - delete ass; - } - } - if (idmap != NULL) { - delete idmap; - } - MY_TRACE_END(); - } - - if (required && !rt) ALOGW("Unable to find resources file resources.arsc"); - if (!rt) { - mResources = rt = new ResTable(); - } - return rt; -} - -void AssetManager::updateResourceParamsLocked() const -{ - ResTable* res = mResources; - if (!res) { - return; - } - - size_t llen = mLocale ? strlen(mLocale) : 0; - mConfig->language[0] = 0; - mConfig->language[1] = 0; - mConfig->country[0] = 0; - mConfig->country[1] = 0; - if (llen >= 2) { - mConfig->language[0] = mLocale[0]; - mConfig->language[1] = mLocale[1]; - } - if (llen >= 5) { - mConfig->country[0] = mLocale[3]; - mConfig->country[1] = mLocale[4]; - } - mConfig->size = sizeof(*mConfig); - - res->setParameters(mConfig); -} - -Asset* AssetManager::openIdmapLocked(const struct asset_path& ap) const -{ - Asset* ass = NULL; - if (ap.idmap.size() != 0) { - ass = const_cast<AssetManager*>(this)-> - openAssetFromFileLocked(ap.idmap, Asset::ACCESS_BUFFER); - if (ass) { - ALOGV("loading idmap %s\n", ap.idmap.string()); - } else { - ALOGW("failed to load idmap %s\n", ap.idmap.string()); - } - } - return ass; -} - -const ResTable& AssetManager::getResources(bool required) const -{ - const ResTable* rt = getResTable(required); - return *rt; -} - -bool AssetManager::isUpToDate() -{ - AutoMutex _l(mLock); - return mZipSet.isUpToDate(); -} - -void AssetManager::getLocales(Vector<String8>* locales) const -{ - ResTable* res = mResources; - if (res != NULL) { - res->getLocales(locales); - } -} - -/* - * Open a non-asset file as if it were an asset, searching for it in the - * specified app. - * - * Pass in a NULL values for "appName" if the common app directory should - * be used. - */ -Asset* AssetManager::openNonAssetInPathLocked(const char* fileName, AccessMode mode, - const asset_path& ap) -{ - Asset* pAsset = NULL; - - /* look at the filesystem on disk */ - if (ap.type == kFileTypeDirectory) { - String8 path(ap.path); - path.appendPath(fileName); - - pAsset = openAssetFromFileLocked(path, mode); - - if (pAsset == NULL) { - /* try again, this time with ".gz" */ - path.append(".gz"); - pAsset = openAssetFromFileLocked(path, mode); - } - - if (pAsset != NULL) { - //printf("FOUND NA '%s' on disk\n", fileName); - pAsset->setAssetSource(path); - } - - /* look inside the zip file */ - } else { - String8 path(fileName); - - /* check the appropriate Zip file */ - ZipFileRO* pZip = getZipFileLocked(ap); - if (pZip != NULL) { - //printf("GOT zip, checking NA '%s'\n", (const char*) path); - ZipEntryRO entry = pZip->findEntryByName(path.string()); - if (entry != NULL) { - //printf("FOUND NA in Zip file for %s\n", appName ? appName : kAppCommon); - pAsset = openAssetFromZipLocked(pZip, entry, mode, path); - pZip->releaseEntry(entry); - } - } - - if (pAsset != NULL) { - /* create a "source" name, for debug/display */ - pAsset->setAssetSource( - createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), String8(""), - String8(fileName))); - } - } - - return pAsset; -} - -/* - * Open an asset, searching for it in the directory hierarchy for the - * specified app. - * - * Pass in a NULL values for "appName" if the common app directory should - * be used. - */ -Asset* AssetManager::openInPathLocked(const char* fileName, AccessMode mode, - const asset_path& ap) -{ - Asset* pAsset = NULL; - - /* - * Try various combinations of locale and vendor. - */ - if (mLocale != NULL && mVendor != NULL) - pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, mVendor); - if (pAsset == NULL && mVendor != NULL) - pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, mVendor); - if (pAsset == NULL && mLocale != NULL) - pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, NULL); - if (pAsset == NULL) - pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, NULL); - - return pAsset; -} - -/* - * Open an asset, searching for it in the directory hierarchy for the - * specified locale and vendor. - * - * We also search in "app.jar". - * - * Pass in NULL values for "appName", "locale", and "vendor" if the - * defaults should be used. - */ -Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode mode, - const asset_path& ap, const char* locale, const char* vendor) -{ - Asset* pAsset = NULL; - - if (ap.type == kFileTypeDirectory) { - if (mCacheMode == CACHE_OFF) { - /* look at the filesystem on disk */ - String8 path(createPathNameLocked(ap, locale, vendor)); - path.appendPath(fileName); - - String8 excludeName(path); - excludeName.append(kExcludeExtension); - if (::getFileType(excludeName.string()) != kFileTypeNonexistent) { - /* say no more */ - //printf("+++ excluding '%s'\n", (const char*) excludeName); - return kExcludedAsset; - } - - pAsset = openAssetFromFileLocked(path, mode); - - if (pAsset == NULL) { - /* try again, this time with ".gz" */ - path.append(".gz"); - pAsset = openAssetFromFileLocked(path, mode); - } - - if (pAsset != NULL) - pAsset->setAssetSource(path); - } else { - /* find in cache */ - String8 path(createPathNameLocked(ap, locale, vendor)); - path.appendPath(fileName); - - AssetDir::FileInfo tmpInfo; - bool found = false; - - String8 excludeName(path); - excludeName.append(kExcludeExtension); - - if (mCache.indexOf(excludeName) != NAME_NOT_FOUND) { - /* go no farther */ - //printf("+++ Excluding '%s'\n", (const char*) excludeName); - return kExcludedAsset; - } - - /* - * File compression extensions (".gz") don't get stored in the - * name cache, so we have to try both here. - */ - if (mCache.indexOf(path) != NAME_NOT_FOUND) { - found = true; - pAsset = openAssetFromFileLocked(path, mode); - if (pAsset == NULL) { - /* try again, this time with ".gz" */ - path.append(".gz"); - pAsset = openAssetFromFileLocked(path, mode); - } - } - - if (pAsset != NULL) - pAsset->setAssetSource(path); - - /* - * Don't continue the search into the Zip files. Our cached info - * said it was a file on disk; to be consistent with openDir() - * we want to return the loose asset. If the cached file gets - * removed, we fail. - * - * The alternative is to update our cache when files get deleted, - * or make some sort of "best effort" promise, but for now I'm - * taking the hard line. - */ - if (found) { - if (pAsset == NULL) - ALOGD("Expected file not found: '%s'\n", path.string()); - return pAsset; - } - } - } - - /* - * Either it wasn't found on disk or on the cached view of the disk. - * Dig through the currently-opened set of Zip files. If caching - * is disabled, the Zip file may get reopened. - */ - if (pAsset == NULL && ap.type == kFileTypeRegular) { - String8 path; - - path.appendPath((locale != NULL) ? locale : kDefaultLocale); - path.appendPath((vendor != NULL) ? vendor : kDefaultVendor); - path.appendPath(fileName); - - /* check the appropriate Zip file */ - ZipFileRO* pZip = getZipFileLocked(ap); - if (pZip != NULL) { - //printf("GOT zip, checking '%s'\n", (const char*) path); - ZipEntryRO entry = pZip->findEntryByName(path.string()); - if (entry != NULL) { - //printf("FOUND in Zip file for %s/%s-%s\n", - // appName, locale, vendor); - pAsset = openAssetFromZipLocked(pZip, entry, mode, path); - pZip->releaseEntry(entry); - } - } - - if (pAsset != NULL) { - /* create a "source" name, for debug/display */ - pAsset->setAssetSource(createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()), - String8(""), String8(fileName))); - } - } - - return pAsset; -} - -/* - * Create a "source name" for a file from a Zip archive. - */ -String8 AssetManager::createZipSourceNameLocked(const String8& zipFileName, - const String8& dirName, const String8& fileName) -{ - String8 sourceName("zip:"); - sourceName.append(zipFileName); - sourceName.append(":"); - if (dirName.length() > 0) { - sourceName.appendPath(dirName); - } - sourceName.appendPath(fileName); - return sourceName; -} - -/* - * Create a path to a loose asset (asset-base/app/locale/vendor). - */ -String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* locale, - const char* vendor) -{ - String8 path(ap.path); - path.appendPath((locale != NULL) ? locale : kDefaultLocale); - path.appendPath((vendor != NULL) ? vendor : kDefaultVendor); - return path; -} - -/* - * Create a path to a loose asset (asset-base/app/rootDir). - */ -String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* rootDir) -{ - String8 path(ap.path); - if (rootDir != NULL) path.appendPath(rootDir); - return path; -} - -/* - * Return a pointer to one of our open Zip archives. Returns NULL if no - * matching Zip file exists. - * - * Right now we have 2 possible Zip files (1 each in app/"common"). - * - * If caching is set to CACHE_OFF, to get the expected behavior we - * need to reopen the Zip file on every request. That would be silly - * and expensive, so instead we just check the file modification date. - * - * Pass in NULL values for "appName", "locale", and "vendor" if the - * generics should be used. - */ -ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap) -{ - ALOGV("getZipFileLocked() in %p\n", this); - - return mZipSet.getZip(ap.path); -} - -/* - * Try to open an asset from a file on disk. - * - * If the file is compressed with gzip, we seek to the start of the - * deflated data and pass that in (just like we would for a Zip archive). - * - * For uncompressed data, we may already have an mmap()ed version sitting - * around. If so, we want to hand that to the Asset instead. - * - * This returns NULL if the file doesn't exist, couldn't be opened, or - * claims to be a ".gz" but isn't. - */ -Asset* AssetManager::openAssetFromFileLocked(const String8& pathName, - AccessMode mode) -{ - Asset* pAsset = NULL; - - if (strcasecmp(pathName.getPathExtension().string(), ".gz") == 0) { - //printf("TRYING '%s'\n", (const char*) pathName); - pAsset = Asset::createFromCompressedFile(pathName.string(), mode); - } else { - //printf("TRYING '%s'\n", (const char*) pathName); - pAsset = Asset::createFromFile(pathName.string(), mode); - } - - return pAsset; -} - -/* - * Given an entry in a Zip archive, create a new Asset object. - * - * If the entry is uncompressed, we may want to create or share a - * slice of shared memory. - */ -Asset* AssetManager::openAssetFromZipLocked(const ZipFileRO* pZipFile, - const ZipEntryRO entry, AccessMode mode, const String8& entryName) -{ - Asset* pAsset = NULL; - - // TODO: look for previously-created shared memory slice? - int method; - size_t uncompressedLen; - - //printf("USING Zip '%s'\n", pEntry->getFileName()); - - //pZipFile->getEntryInfo(entry, &method, &uncompressedLen, &compressedLen, - // &offset); - if (!pZipFile->getEntryInfo(entry, &method, &uncompressedLen, NULL, NULL, - NULL, NULL)) - { - ALOGW("getEntryInfo failed\n"); - return NULL; - } - - FileMap* dataMap = pZipFile->createEntryFileMap(entry); - if (dataMap == NULL) { - ALOGW("create map from entry failed\n"); - return NULL; - } - - if (method == ZipFileRO::kCompressStored) { - pAsset = Asset::createFromUncompressedMap(dataMap, mode); - ALOGV("Opened uncompressed entry %s in zip %s mode %d: %p", entryName.string(), - dataMap->getFileName(), mode, pAsset); - } else { - pAsset = Asset::createFromCompressedMap(dataMap, method, - uncompressedLen, mode); - ALOGV("Opened compressed entry %s in zip %s mode %d: %p", entryName.string(), - dataMap->getFileName(), mode, pAsset); - } - if (pAsset == NULL) { - /* unexpected */ - ALOGW("create from segment failed\n"); - } - - return pAsset; -} - - - -/* - * Open a directory in the asset namespace. - * - * An "asset directory" is simply the combination of all files in all - * locations, with ".gz" stripped for loose files. With app, locale, and - * vendor defined, we have 8 directories and 2 Zip archives to scan. - * - * Pass in "" for the root dir. - */ -AssetDir* AssetManager::openDir(const char* dirName) -{ - AutoMutex _l(mLock); - - AssetDir* pDir = NULL; - SortedVector<AssetDir::FileInfo>* pMergedInfo = NULL; - - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - assert(dirName != NULL); - - //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase); - - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - - pDir = new AssetDir; - - /* - * Scan the various directories, merging what we find into a single - * vector. We want to scan them in reverse priority order so that - * the ".EXCLUDE" processing works correctly. Also, if we decide we - * want to remember where the file is coming from, we'll get the right - * version. - * - * We start with Zip archives, then do loose files. - */ - pMergedInfo = new SortedVector<AssetDir::FileInfo>; - - size_t i = mAssetPaths.size(); - while (i > 0) { - i--; - const asset_path& ap = mAssetPaths.itemAt(i); - if (ap.type == kFileTypeRegular) { - ALOGV("Adding directory %s from zip %s", dirName, ap.path.string()); - scanAndMergeZipLocked(pMergedInfo, ap, kAssetsRoot, dirName); - } else { - ALOGV("Adding directory %s from dir %s", dirName, ap.path.string()); - scanAndMergeDirLocked(pMergedInfo, ap, kAssetsRoot, dirName); - } - } - -#if 0 - printf("FILE LIST:\n"); - for (i = 0; i < (size_t) pMergedInfo->size(); i++) { - printf(" %d: (%d) '%s'\n", i, - pMergedInfo->itemAt(i).getFileType(), - (const char*) pMergedInfo->itemAt(i).getFileName()); - } -#endif - - pDir->setFileList(pMergedInfo); - return pDir; -} - -/* - * Open a directory in the non-asset namespace. - * - * An "asset directory" is simply the combination of all files in all - * locations, with ".gz" stripped for loose files. With app, locale, and - * vendor defined, we have 8 directories and 2 Zip archives to scan. - * - * Pass in "" for the root dir. - */ -AssetDir* AssetManager::openNonAssetDir(void* cookie, const char* dirName) -{ - AutoMutex _l(mLock); - - AssetDir* pDir = NULL; - SortedVector<AssetDir::FileInfo>* pMergedInfo = NULL; - - LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager"); - assert(dirName != NULL); - - //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase); - - if (mCacheMode != CACHE_OFF && !mCacheValid) - loadFileNameCacheLocked(); - - pDir = new AssetDir; - - pMergedInfo = new SortedVector<AssetDir::FileInfo>; - - const size_t which = ((size_t)cookie)-1; - - if (which < mAssetPaths.size()) { - const asset_path& ap = mAssetPaths.itemAt(which); - if (ap.type == kFileTypeRegular) { - ALOGV("Adding directory %s from zip %s", dirName, ap.path.string()); - scanAndMergeZipLocked(pMergedInfo, ap, NULL, dirName); - } else { - ALOGV("Adding directory %s from dir %s", dirName, ap.path.string()); - scanAndMergeDirLocked(pMergedInfo, ap, NULL, dirName); - } - } - -#if 0 - printf("FILE LIST:\n"); - for (i = 0; i < (size_t) pMergedInfo->size(); i++) { - printf(" %d: (%d) '%s'\n", i, - pMergedInfo->itemAt(i).getFileType(), - (const char*) pMergedInfo->itemAt(i).getFileName()); - } -#endif - - pDir->setFileList(pMergedInfo); - return pDir; -} - -/* - * Scan the contents of the specified directory and merge them into the - * "pMergedInfo" vector, removing previous entries if we find "exclude" - * directives. - * - * Returns "false" if we found nothing to contribute. - */ -bool AssetManager::scanAndMergeDirLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo, - const asset_path& ap, const char* rootDir, const char* dirName) -{ - SortedVector<AssetDir::FileInfo>* pContents; - String8 path; - - assert(pMergedInfo != NULL); - - //printf("scanAndMergeDir: %s %s %s %s\n", appName, locale, vendor,dirName); - - if (mCacheValid) { - int i, start, count; - - pContents = new SortedVector<AssetDir::FileInfo>; - - /* - * Get the basic partial path and find it in the cache. That's - * the start point for the search. - */ - path = createPathNameLocked(ap, rootDir); - if (dirName[0] != '\0') - path.appendPath(dirName); - - start = mCache.indexOf(path); - if (start == NAME_NOT_FOUND) { - //printf("+++ not found in cache: dir '%s'\n", (const char*) path); - delete pContents; - return false; - } - - /* - * The match string looks like "common/default/default/foo/bar/". - * The '/' on the end ensures that we don't match on the directory - * itself or on ".../foo/barfy/". - */ - path.append("/"); - - count = mCache.size(); - - /* - * Pick out the stuff in the current dir by examining the pathname. - * It needs to match the partial pathname prefix, and not have a '/' - * (fssep) anywhere after the prefix. - */ - for (i = start+1; i < count; i++) { - if (mCache[i].getFileName().length() > path.length() && - strncmp(mCache[i].getFileName().string(), path.string(), path.length()) == 0) - { - const char* name = mCache[i].getFileName().string(); - // XXX THIS IS BROKEN! Looks like we need to store the full - // path prefix separately from the file path. - if (strchr(name + path.length(), '/') == NULL) { - /* grab it, reducing path to just the filename component */ - AssetDir::FileInfo tmp = mCache[i]; - tmp.setFileName(tmp.getFileName().getPathLeaf()); - pContents->add(tmp); - } - } else { - /* no longer in the dir or its subdirs */ - break; - } - - } - } else { - path = createPathNameLocked(ap, rootDir); - if (dirName[0] != '\0') - path.appendPath(dirName); - pContents = scanDirLocked(path); - if (pContents == NULL) - return false; - } - - // if we wanted to do an incremental cache fill, we would do it here - - /* - * Process "exclude" directives. If we find a filename that ends with - * ".EXCLUDE", we look for a matching entry in the "merged" set, and - * remove it if we find it. We also delete the "exclude" entry. - */ - int i, count, exclExtLen; - - count = pContents->size(); - exclExtLen = strlen(kExcludeExtension); - for (i = 0; i < count; i++) { - const char* name; - int nameLen; - - name = pContents->itemAt(i).getFileName().string(); - nameLen = strlen(name); - if (nameLen > exclExtLen && - strcmp(name + (nameLen - exclExtLen), kExcludeExtension) == 0) - { - String8 match(name, nameLen - exclExtLen); - int matchIdx; - - matchIdx = AssetDir::FileInfo::findEntry(pMergedInfo, match); - if (matchIdx > 0) { - ALOGV("Excluding '%s' [%s]\n", - pMergedInfo->itemAt(matchIdx).getFileName().string(), - pMergedInfo->itemAt(matchIdx).getSourceName().string()); - pMergedInfo->removeAt(matchIdx); - } else { - //printf("+++ no match on '%s'\n", (const char*) match); - } - - ALOGD("HEY: size=%d removing %d\n", (int)pContents->size(), i); - pContents->removeAt(i); - i--; // adjust "for" loop - count--; // and loop limit - } - } - - mergeInfoLocked(pMergedInfo, pContents); - - delete pContents; - - return true; -} - -/* - * Scan the contents of the specified directory, and stuff what we find - * into a newly-allocated vector. - * - * Files ending in ".gz" will have their extensions removed. - * - * We should probably think about skipping files with "illegal" names, - * e.g. illegal characters (/\:) or excessive length. - * - * Returns NULL if the specified directory doesn't exist. - */ -SortedVector<AssetDir::FileInfo>* AssetManager::scanDirLocked(const String8& path) -{ - SortedVector<AssetDir::FileInfo>* pContents = NULL; - DIR* dir; - struct dirent* entry; - FileType fileType; - - ALOGV("Scanning dir '%s'\n", path.string()); - - dir = opendir(path.string()); - if (dir == NULL) - return NULL; - - pContents = new SortedVector<AssetDir::FileInfo>; - - while (1) { - entry = readdir(dir); - if (entry == NULL) - break; - - if (strcmp(entry->d_name, ".") == 0 || - strcmp(entry->d_name, "..") == 0) - continue; - -#ifdef _DIRENT_HAVE_D_TYPE - if (entry->d_type == DT_REG) - fileType = kFileTypeRegular; - else if (entry->d_type == DT_DIR) - fileType = kFileTypeDirectory; - else - fileType = kFileTypeUnknown; -#else - // stat the file - fileType = ::getFileType(path.appendPathCopy(entry->d_name).string()); -#endif - - if (fileType != kFileTypeRegular && fileType != kFileTypeDirectory) - continue; - - AssetDir::FileInfo info; - info.set(String8(entry->d_name), fileType); - if (strcasecmp(info.getFileName().getPathExtension().string(), ".gz") == 0) - info.setFileName(info.getFileName().getBasePath()); - info.setSourceName(path.appendPathCopy(info.getFileName())); - pContents->add(info); - } - - closedir(dir); - return pContents; -} - -/* - * Scan the contents out of the specified Zip archive, and merge what we - * find into "pMergedInfo". If the Zip archive in question doesn't exist, - * we return immediately. - * - * Returns "false" if we found nothing to contribute. - */ -bool AssetManager::scanAndMergeZipLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo, - const asset_path& ap, const char* rootDir, const char* baseDirName) -{ - ZipFileRO* pZip; - Vector<String8> dirs; - AssetDir::FileInfo info; - SortedVector<AssetDir::FileInfo> contents; - String8 sourceName, zipName, dirName; - - pZip = mZipSet.getZip(ap.path); - if (pZip == NULL) { - ALOGW("Failure opening zip %s\n", ap.path.string()); - return false; - } - - zipName = ZipSet::getPathName(ap.path.string()); - - /* convert "sounds" to "rootDir/sounds" */ - if (rootDir != NULL) dirName = rootDir; - dirName.appendPath(baseDirName); - - /* - * Scan through the list of files, looking for a match. The files in - * the Zip table of contents are not in sorted order, so we have to - * process the entire list. We're looking for a string that begins - * with the characters in "dirName", is followed by a '/', and has no - * subsequent '/' in the stuff that follows. - * - * What makes this especially fun is that directories are not stored - * explicitly in Zip archives, so we have to infer them from context. - * When we see "sounds/foo.wav" we have to leave a note to ourselves - * to insert a directory called "sounds" into the list. We store - * these in temporary vector so that we only return each one once. - * - * Name comparisons are case-sensitive to match UNIX filesystem - * semantics. - */ - int dirNameLen = dirName.length(); - void *iterationCookie; - if (!pZip->startIteration(&iterationCookie)) { - ALOGW("ZipFileRO::startIteration returned false"); - return false; - } - - ZipEntryRO entry; - while ((entry = pZip->nextEntry(iterationCookie)) != NULL) { - char nameBuf[256]; - - if (pZip->getEntryFileName(entry, nameBuf, sizeof(nameBuf)) != 0) { - // TODO: fix this if we expect to have long names - ALOGE("ARGH: name too long?\n"); - continue; - } - //printf("Comparing %s in %s?\n", nameBuf, dirName.string()); - if (dirNameLen == 0 || - (strncmp(nameBuf, dirName.string(), dirNameLen) == 0 && - nameBuf[dirNameLen] == '/')) - { - const char* cp; - const char* nextSlash; - - cp = nameBuf + dirNameLen; - if (dirNameLen != 0) - cp++; // advance past the '/' - - nextSlash = strchr(cp, '/'); -//xxx this may break if there are bare directory entries - if (nextSlash == NULL) { - /* this is a file in the requested directory */ - - info.set(String8(nameBuf).getPathLeaf(), kFileTypeRegular); - - info.setSourceName( - createZipSourceNameLocked(zipName, dirName, info.getFileName())); - - contents.add(info); - //printf("FOUND: file '%s'\n", info.getFileName().string()); - } else { - /* this is a subdir; add it if we don't already have it*/ - String8 subdirName(cp, nextSlash - cp); - size_t j; - size_t N = dirs.size(); - - for (j = 0; j < N; j++) { - if (subdirName == dirs[j]) { - break; - } - } - if (j == N) { - dirs.add(subdirName); - } - - //printf("FOUND: dir '%s'\n", subdirName.string()); - } - } - } - - pZip->endIteration(iterationCookie); - - /* - * Add the set of unique directories. - */ - for (int i = 0; i < (int) dirs.size(); i++) { - info.set(dirs[i], kFileTypeDirectory); - info.setSourceName( - createZipSourceNameLocked(zipName, dirName, info.getFileName())); - contents.add(info); - } - - mergeInfoLocked(pMergedInfo, &contents); - - return true; -} - - -/* - * Merge two vectors of FileInfo. - * - * The merged contents will be stuffed into *pMergedInfo. - * - * If an entry for a file exists in both "pMergedInfo" and "pContents", - * we use the newer "pContents" entry. - */ -void AssetManager::mergeInfoLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo, - const SortedVector<AssetDir::FileInfo>* pContents) -{ - /* - * Merge what we found in this directory with what we found in - * other places. - * - * Two basic approaches: - * (1) Create a new array that holds the unique values of the two - * arrays. - * (2) Take the elements from pContents and shove them into pMergedInfo. - * - * Because these are vectors of complex objects, moving elements around - * inside the vector requires constructing new objects and allocating - * storage for members. With approach #1, we're always adding to the - * end, whereas with #2 we could be inserting multiple elements at the - * front of the vector. Approach #1 requires a full copy of the - * contents of pMergedInfo, but approach #2 requires the same copy for - * every insertion at the front of pMergedInfo. - * - * (We should probably use a SortedVector interface that allows us to - * just stuff items in, trusting us to maintain the sort order.) - */ - SortedVector<AssetDir::FileInfo>* pNewSorted; - int mergeMax, contMax; - int mergeIdx, contIdx; - - pNewSorted = new SortedVector<AssetDir::FileInfo>; - mergeMax = pMergedInfo->size(); - contMax = pContents->size(); - mergeIdx = contIdx = 0; - - while (mergeIdx < mergeMax || contIdx < contMax) { - if (mergeIdx == mergeMax) { - /* hit end of "merge" list, copy rest of "contents" */ - pNewSorted->add(pContents->itemAt(contIdx)); - contIdx++; - } else if (contIdx == contMax) { - /* hit end of "cont" list, copy rest of "merge" */ - pNewSorted->add(pMergedInfo->itemAt(mergeIdx)); - mergeIdx++; - } else if (pMergedInfo->itemAt(mergeIdx) == pContents->itemAt(contIdx)) - { - /* items are identical, add newer and advance both indices */ - pNewSorted->add(pContents->itemAt(contIdx)); - mergeIdx++; - contIdx++; - } else if (pMergedInfo->itemAt(mergeIdx) < pContents->itemAt(contIdx)) - { - /* "merge" is lower, add that one */ - pNewSorted->add(pMergedInfo->itemAt(mergeIdx)); - mergeIdx++; - } else { - /* "cont" is lower, add that one */ - assert(pContents->itemAt(contIdx) < pMergedInfo->itemAt(mergeIdx)); - pNewSorted->add(pContents->itemAt(contIdx)); - contIdx++; - } - } - - /* - * Overwrite the "merged" list with the new stuff. - */ - *pMergedInfo = *pNewSorted; - delete pNewSorted; - -#if 0 // for Vector, rather than SortedVector - int i, j; - for (i = pContents->size() -1; i >= 0; i--) { - bool add = true; - - for (j = pMergedInfo->size() -1; j >= 0; j--) { - /* case-sensitive comparisons, to behave like UNIX fs */ - if (strcmp(pContents->itemAt(i).mFileName, - pMergedInfo->itemAt(j).mFileName) == 0) - { - /* match, don't add this entry */ - add = false; - break; - } - } - - if (add) - pMergedInfo->add(pContents->itemAt(i)); - } -#endif -} - - -/* - * Load all files into the file name cache. We want to do this across - * all combinations of { appname, locale, vendor }, performing a recursive - * directory traversal. - * - * This is not the most efficient data structure. Also, gathering the - * information as we needed it (file-by-file or directory-by-directory) - * would be faster. However, on the actual device, 99% of the files will - * live in Zip archives, so this list will be very small. The trouble - * is that we have to check the "loose" files first, so it's important - * that we don't beat the filesystem silly looking for files that aren't - * there. - * - * Note on thread safety: this is the only function that causes updates - * to mCache, and anybody who tries to use it will call here if !mCacheValid, - * so we need to employ a mutex here. - */ -void AssetManager::loadFileNameCacheLocked(void) -{ - assert(!mCacheValid); - assert(mCache.size() == 0); - -#ifdef DO_TIMINGS // need to link against -lrt for this now - DurationTimer timer; - timer.start(); -#endif - - fncScanLocked(&mCache, ""); - -#ifdef DO_TIMINGS - timer.stop(); - ALOGD("Cache scan took %.3fms\n", - timer.durationUsecs() / 1000.0); -#endif - -#if 0 - int i; - printf("CACHED FILE LIST (%d entries):\n", mCache.size()); - for (i = 0; i < (int) mCache.size(); i++) { - printf(" %d: (%d) '%s'\n", i, - mCache.itemAt(i).getFileType(), - (const char*) mCache.itemAt(i).getFileName()); - } -#endif - - mCacheValid = true; -} - -/* - * Scan up to 8 versions of the specified directory. - */ -void AssetManager::fncScanLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo, - const char* dirName) -{ - size_t i = mAssetPaths.size(); - while (i > 0) { - i--; - const asset_path& ap = mAssetPaths.itemAt(i); - fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, NULL, dirName); - if (mLocale != NULL) - fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, NULL, dirName); - if (mVendor != NULL) - fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, mVendor, dirName); - if (mLocale != NULL && mVendor != NULL) - fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, mVendor, dirName); - } -} - -/* - * Recursively scan this directory and all subdirs. - * - * This is similar to scanAndMergeDir, but we don't remove the .EXCLUDE - * files, and we prepend the extended partial path to the filenames. - */ -bool AssetManager::fncScanAndMergeDirLocked( - SortedVector<AssetDir::FileInfo>* pMergedInfo, - const asset_path& ap, const char* locale, const char* vendor, - const char* dirName) -{ - SortedVector<AssetDir::FileInfo>* pContents; - String8 partialPath; - String8 fullPath; - - // XXX This is broken -- the filename cache needs to hold the base - // asset path separately from its filename. - - partialPath = createPathNameLocked(ap, locale, vendor); - if (dirName[0] != '\0') { - partialPath.appendPath(dirName); - } - - fullPath = partialPath; - pContents = scanDirLocked(fullPath); - if (pContents == NULL) { - return false; // directory did not exist - } - - /* - * Scan all subdirectories of the current dir, merging what we find - * into "pMergedInfo". - */ - for (int i = 0; i < (int) pContents->size(); i++) { - if (pContents->itemAt(i).getFileType() == kFileTypeDirectory) { - String8 subdir(dirName); - subdir.appendPath(pContents->itemAt(i).getFileName()); - - fncScanAndMergeDirLocked(pMergedInfo, ap, locale, vendor, subdir.string()); - } - } - - /* - * To be consistent, we want entries for the root directory. If - * we're the root, add one now. - */ - if (dirName[0] == '\0') { - AssetDir::FileInfo tmpInfo; - - tmpInfo.set(String8(""), kFileTypeDirectory); - tmpInfo.setSourceName(createPathNameLocked(ap, locale, vendor)); - pContents->add(tmpInfo); - } - - /* - * We want to prepend the extended partial path to every entry in - * "pContents". It's the same value for each entry, so this will - * not change the sorting order of the vector contents. - */ - for (int i = 0; i < (int) pContents->size(); i++) { - const AssetDir::FileInfo& info = pContents->itemAt(i); - pContents->editItemAt(i).setFileName(partialPath.appendPathCopy(info.getFileName())); - } - - mergeInfoLocked(pMergedInfo, pContents); - return true; -} - -/* - * Trash the cache. - */ -void AssetManager::purgeFileNameCacheLocked(void) -{ - mCacheValid = false; - mCache.clear(); -} - -/* - * =========================================================================== - * AssetManager::SharedZip - * =========================================================================== - */ - - -Mutex AssetManager::SharedZip::gLock; -DefaultKeyedVector<String8, wp<AssetManager::SharedZip> > AssetManager::SharedZip::gOpen; - -AssetManager::SharedZip::SharedZip(const String8& path, time_t modWhen) - : mPath(path), mZipFile(NULL), mModWhen(modWhen), - mResourceTableAsset(NULL), mResourceTable(NULL) -{ - //ALOGI("Creating SharedZip %p %s\n", this, (const char*)mPath); - ALOGV("+++ opening zip '%s'\n", mPath.string()); - mZipFile = ZipFileRO::open(mPath.string()); - if (mZipFile == NULL) { - ALOGD("failed to open Zip archive '%s'\n", mPath.string()); - } -} - -sp<AssetManager::SharedZip> AssetManager::SharedZip::get(const String8& path) -{ - AutoMutex _l(gLock); - time_t modWhen = getFileModDate(path); - sp<SharedZip> zip = gOpen.valueFor(path).promote(); - if (zip != NULL && zip->mModWhen == modWhen) { - return zip; - } - zip = new SharedZip(path, modWhen); - gOpen.add(path, zip); - return zip; - -} - -ZipFileRO* AssetManager::SharedZip::getZip() -{ - return mZipFile; -} - -Asset* AssetManager::SharedZip::getResourceTableAsset() -{ - ALOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset); - return mResourceTableAsset; -} - -Asset* AssetManager::SharedZip::setResourceTableAsset(Asset* asset) -{ - { - AutoMutex _l(gLock); - if (mResourceTableAsset == NULL) { - mResourceTableAsset = asset; - // This is not thread safe the first time it is called, so - // do it here with the global lock held. - asset->getBuffer(true); - return asset; - } - } - delete asset; - return mResourceTableAsset; -} - -ResTable* AssetManager::SharedZip::getResourceTable() -{ - ALOGV("Getting from SharedZip %p resource table %p\n", this, mResourceTable); - return mResourceTable; -} - -ResTable* AssetManager::SharedZip::setResourceTable(ResTable* res) -{ - { - AutoMutex _l(gLock); - if (mResourceTable == NULL) { - mResourceTable = res; - return res; - } - } - delete res; - return mResourceTable; -} - -bool AssetManager::SharedZip::isUpToDate() -{ - time_t modWhen = getFileModDate(mPath.string()); - return mModWhen == modWhen; -} - -AssetManager::SharedZip::~SharedZip() -{ - //ALOGI("Destroying SharedZip %p %s\n", this, (const char*)mPath); - if (mResourceTable != NULL) { - delete mResourceTable; - } - if (mResourceTableAsset != NULL) { - delete mResourceTableAsset; - } - if (mZipFile != NULL) { - delete mZipFile; - ALOGV("Closed '%s'\n", mPath.string()); - } -} - -/* - * =========================================================================== - * AssetManager::ZipSet - * =========================================================================== - */ - -/* - * Constructor. - */ -AssetManager::ZipSet::ZipSet(void) -{ -} - -/* - * Destructor. Close any open archives. - */ -AssetManager::ZipSet::~ZipSet(void) -{ - size_t N = mZipFile.size(); - for (size_t i = 0; i < N; i++) - closeZip(i); -} - -/* - * Close a Zip file and reset the entry. - */ -void AssetManager::ZipSet::closeZip(int idx) -{ - mZipFile.editItemAt(idx) = NULL; -} - - -/* - * Retrieve the appropriate Zip file from the set. - */ -ZipFileRO* AssetManager::ZipSet::getZip(const String8& path) -{ - int idx = getIndex(path); - sp<SharedZip> zip = mZipFile[idx]; - if (zip == NULL) { - zip = SharedZip::get(path); - mZipFile.editItemAt(idx) = zip; - } - return zip->getZip(); -} - -Asset* AssetManager::ZipSet::getZipResourceTableAsset(const String8& path) -{ - int idx = getIndex(path); - sp<SharedZip> zip = mZipFile[idx]; - if (zip == NULL) { - zip = SharedZip::get(path); - mZipFile.editItemAt(idx) = zip; - } - return zip->getResourceTableAsset(); -} - -Asset* AssetManager::ZipSet::setZipResourceTableAsset(const String8& path, - Asset* asset) -{ - int idx = getIndex(path); - sp<SharedZip> zip = mZipFile[idx]; - // doesn't make sense to call before previously accessing. - return zip->setResourceTableAsset(asset); -} - -ResTable* AssetManager::ZipSet::getZipResourceTable(const String8& path) -{ - int idx = getIndex(path); - sp<SharedZip> zip = mZipFile[idx]; - if (zip == NULL) { - zip = SharedZip::get(path); - mZipFile.editItemAt(idx) = zip; - } - return zip->getResourceTable(); -} - -ResTable* AssetManager::ZipSet::setZipResourceTable(const String8& path, - ResTable* res) -{ - int idx = getIndex(path); - sp<SharedZip> zip = mZipFile[idx]; - // doesn't make sense to call before previously accessing. - return zip->setResourceTable(res); -} - -/* - * Generate the partial pathname for the specified archive. The caller - * gets to prepend the asset root directory. - * - * Returns something like "common/en-US-noogle.jar". - */ -/*static*/ String8 AssetManager::ZipSet::getPathName(const char* zipPath) -{ - return String8(zipPath); -} - -bool AssetManager::ZipSet::isUpToDate() -{ - const size_t N = mZipFile.size(); - for (size_t i=0; i<N; i++) { - if (mZipFile[i] != NULL && !mZipFile[i]->isUpToDate()) { - return false; - } - } - return true; -} - -/* - * Compute the zip file's index. - * - * "appName", "locale", and "vendor" should be set to NULL to indicate the - * default directory. - */ -int AssetManager::ZipSet::getIndex(const String8& zip) const -{ - const size_t N = mZipPath.size(); - for (size_t i=0; i<N; i++) { - if (mZipPath[i] == zip) { - return i; - } - } - - mZipPath.add(zip); - mZipFile.add(NULL); - - return mZipPath.size()-1; -} diff --git a/libs/androidfw/BackupData.cpp b/libs/androidfw/BackupData.cpp deleted file mode 100644 index 4e3b522..0000000 --- a/libs/androidfw/BackupData.cpp +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright (C) 2009 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 "backup_data" - -#include <androidfw/BackupHelpers.h> -#include <utils/ByteOrder.h> - -#include <stdio.h> -#include <string.h> -#include <unistd.h> - -#include <cutils/log.h> - -namespace android { - -static const bool DEBUG = false; - -/* - * File Format (v1): - * - * All ints are stored little-endian. - * - * - An app_header_v1 struct. - * - The name of the package, utf-8, null terminated, padded to 4-byte boundary. - * - A sequence of zero or more key/value paires (entities), each with - * - A entity_header_v1 struct - * - The key, utf-8, null terminated, padded to 4-byte boundary. - * - The value, padded to 4 byte boundary - */ - -const static int ROUND_UP[4] = { 0, 3, 2, 1 }; - -static inline size_t -round_up(size_t n) -{ - return n + ROUND_UP[n % 4]; -} - -static inline size_t -padding_extra(size_t n) -{ - return ROUND_UP[n % 4]; -} - -BackupDataWriter::BackupDataWriter(int fd) - :m_fd(fd), - m_status(NO_ERROR), - m_pos(0), - m_entityCount(0) -{ -} - -BackupDataWriter::~BackupDataWriter() -{ -} - -// Pad out anything they've previously written to the next 4 byte boundary. -status_t -BackupDataWriter::write_padding_for(int n) -{ - ssize_t amt; - ssize_t paddingSize; - - paddingSize = padding_extra(n); - if (paddingSize > 0) { - uint32_t padding = 0xbcbcbcbc; - if (DEBUG) ALOGI("writing %d padding bytes for %d", paddingSize, n); - amt = write(m_fd, &padding, paddingSize); - if (amt != paddingSize) { - m_status = errno; - return m_status; - } - m_pos += amt; - } - return NO_ERROR; -} - -status_t -BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) -{ - if (m_status != NO_ERROR) { - return m_status; - } - - ssize_t amt; - - amt = write_padding_for(m_pos); - if (amt != 0) { - return amt; - } - - String8 k; - if (m_keyPrefix.length() > 0) { - k = m_keyPrefix; - k += ":"; - k += key; - } else { - k = key; - } - if (DEBUG) { - ALOGD("Writing header: prefix='%s' key='%s' dataSize=%d", m_keyPrefix.string(), - key.string(), dataSize); - } - - entity_header_v1 header; - ssize_t keyLen; - - keyLen = k.length(); - - header.type = tolel(BACKUP_HEADER_ENTITY_V1); - header.keyLen = tolel(keyLen); - header.dataSize = tolel(dataSize); - - if (DEBUG) ALOGI("writing entity header, %d bytes", sizeof(entity_header_v1)); - amt = write(m_fd, &header, sizeof(entity_header_v1)); - if (amt != sizeof(entity_header_v1)) { - m_status = errno; - return m_status; - } - m_pos += amt; - - if (DEBUG) ALOGI("writing entity header key, %d bytes", keyLen+1); - amt = write(m_fd, k.string(), keyLen+1); - if (amt != keyLen+1) { - m_status = errno; - return m_status; - } - m_pos += amt; - - amt = write_padding_for(keyLen+1); - - m_entityCount++; - - return amt; -} - -status_t -BackupDataWriter::WriteEntityData(const void* data, size_t size) -{ - if (DEBUG) ALOGD("Writing data: size=%lu", (unsigned long) size); - - if (m_status != NO_ERROR) { - if (DEBUG) { - ALOGD("Not writing data - stream in error state %d (%s)", m_status, strerror(m_status)); - } - return m_status; - } - - // We don't write padding here, because they're allowed to call this several - // times with smaller buffers. We write it at the end of WriteEntityHeader - // instead. - ssize_t amt = write(m_fd, data, size); - if (amt != (ssize_t)size) { - m_status = errno; - if (DEBUG) ALOGD("write returned error %d (%s)", m_status, strerror(m_status)); - return m_status; - } - m_pos += amt; - return NO_ERROR; -} - -void -BackupDataWriter::SetKeyPrefix(const String8& keyPrefix) -{ - m_keyPrefix = keyPrefix; -} - - -BackupDataReader::BackupDataReader(int fd) - :m_fd(fd), - m_done(false), - m_status(NO_ERROR), - m_pos(0), - m_entityCount(0) -{ - memset(&m_header, 0, sizeof(m_header)); -} - -BackupDataReader::~BackupDataReader() -{ -} - -status_t -BackupDataReader::Status() -{ - return m_status; -} - -#define CHECK_SIZE(actual, expected) \ - do { \ - if ((actual) != (expected)) { \ - if ((actual) == 0) { \ - m_status = EIO; \ - m_done = true; \ - } else { \ - m_status = errno; \ - ALOGD("CHECK_SIZE(a=%ld e=%ld) failed at line %d m_status='%s'", \ - long(actual), long(expected), __LINE__, strerror(m_status)); \ - } \ - return m_status; \ - } \ - } while(0) -#define SKIP_PADDING() \ - do { \ - status_t err = skip_padding(); \ - if (err != NO_ERROR) { \ - ALOGD("SKIP_PADDING FAILED at line %d", __LINE__); \ - m_status = err; \ - return err; \ - } \ - } while(0) - -status_t -BackupDataReader::ReadNextHeader(bool* done, int* type) -{ - *done = m_done; - if (m_status != NO_ERROR) { - return m_status; - } - - int amt; - - amt = skip_padding(); - if (amt == EIO) { - *done = m_done = true; - return NO_ERROR; - } - else if (amt != NO_ERROR) { - return amt; - } - amt = read(m_fd, &m_header, sizeof(m_header)); - *done = m_done = (amt == 0); - if (*done) { - return NO_ERROR; - } - CHECK_SIZE(amt, sizeof(m_header)); - m_pos += sizeof(m_header); - if (type) { - *type = m_header.type; - } - - // validate and fix up the fields. - m_header.type = fromlel(m_header.type); - switch (m_header.type) - { - case BACKUP_HEADER_ENTITY_V1: - { - m_header.entity.keyLen = fromlel(m_header.entity.keyLen); - if (m_header.entity.keyLen <= 0) { - ALOGD("Entity header at %d has keyLen<=0: 0x%08x\n", (int)m_pos, - (int)m_header.entity.keyLen); - m_status = EINVAL; - } - m_header.entity.dataSize = fromlel(m_header.entity.dataSize); - m_entityCount++; - - // read the rest of the header (filename) - size_t size = m_header.entity.keyLen; - char* buf = m_key.lockBuffer(size); - if (buf == NULL) { - m_status = ENOMEM; - return m_status; - } - int amt = read(m_fd, buf, size+1); - CHECK_SIZE(amt, (int)size+1); - m_key.unlockBuffer(size); - m_pos += size+1; - SKIP_PADDING(); - m_dataEndPos = m_pos + m_header.entity.dataSize; - - break; - } - default: - ALOGD("Chunk header at %d has invalid type: 0x%08x", - (int)(m_pos - sizeof(m_header)), (int)m_header.type); - m_status = EINVAL; - } - - return m_status; -} - -bool -BackupDataReader::HasEntities() -{ - return m_status == NO_ERROR && m_header.type == BACKUP_HEADER_ENTITY_V1; -} - -status_t -BackupDataReader::ReadEntityHeader(String8* key, size_t* dataSize) -{ - if (m_status != NO_ERROR) { - return m_status; - } - if (m_header.type != BACKUP_HEADER_ENTITY_V1) { - return EINVAL; - } - *key = m_key; - *dataSize = m_header.entity.dataSize; - return NO_ERROR; -} - -status_t -BackupDataReader::SkipEntityData() -{ - if (m_status != NO_ERROR) { - return m_status; - } - if (m_header.type != BACKUP_HEADER_ENTITY_V1) { - return EINVAL; - } - if (m_header.entity.dataSize > 0) { - int pos = lseek(m_fd, m_dataEndPos, SEEK_SET); - if (pos == -1) { - return errno; - } - m_pos = pos; - } - SKIP_PADDING(); - return NO_ERROR; -} - -ssize_t -BackupDataReader::ReadEntityData(void* data, size_t size) -{ - if (m_status != NO_ERROR) { - return -1; - } - int remaining = m_dataEndPos - m_pos; - //ALOGD("ReadEntityData size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n", - // size, m_pos, m_dataEndPos, remaining); - if (remaining <= 0) { - return 0; - } - if (((int)size) > remaining) { - size = remaining; - } - //ALOGD(" reading %d bytes", size); - int amt = read(m_fd, data, size); - if (amt < 0) { - m_status = errno; - return -1; - } - if (amt == 0) { - m_status = EIO; - m_done = true; - } - m_pos += amt; - return amt; -} - -status_t -BackupDataReader::skip_padding() -{ - ssize_t amt; - ssize_t paddingSize; - - paddingSize = padding_extra(m_pos); - if (paddingSize > 0) { - uint32_t padding; - amt = read(m_fd, &padding, paddingSize); - CHECK_SIZE(amt, paddingSize); - m_pos += amt; - } - return NO_ERROR; -} - - -} // namespace android diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp deleted file mode 100644 index b8d3f48..0000000 --- a/libs/androidfw/BackupHelpers.cpp +++ /dev/null @@ -1,1591 +0,0 @@ -/* - * Copyright (C) 2009 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 "file_backup_helper" - -#include <androidfw/BackupHelpers.h> - -#include <utils/KeyedVector.h> -#include <utils/ByteOrder.h> -#include <utils/String8.h> - -#include <errno.h> -#include <sys/types.h> -#include <sys/uio.h> -#include <sys/stat.h> -#include <sys/time.h> // for utimes -#include <stdio.h> -#include <stdlib.h> -#include <unistd.h> -#include <utime.h> -#include <fcntl.h> -#include <zlib.h> - -#include <cutils/log.h> - -namespace android { - -#define MAGIC0 0x70616e53 // Snap -#define MAGIC1 0x656c6946 // File - -/* - * File entity data format (v1): - * - * - 4-byte version number of the metadata, little endian (0x00000001 for v1) - * - 12 bytes of metadata - * - the file data itself - * - * i.e. a 16-byte metadata header followed by the raw file data. If the - * restore code does not recognize the metadata version, it can still - * interpret the file data itself correctly. - * - * file_metadata_v1: - * - * - 4 byte version number === 0x00000001 (little endian) - * - 4-byte access mode (little-endian) - * - undefined (8 bytes) - */ - -struct file_metadata_v1 { - int version; - int mode; - int undefined_1; - int undefined_2; -}; - -const static int CURRENT_METADATA_VERSION = 1; - -#if 1 -#define LOGP(f, x...) -#else -#if TEST_BACKUP_HELPERS -#define LOGP(f, x...) printf(f "\n", x) -#else -#define LOGP(x...) ALOGD(x) -#endif -#endif - -const static int ROUND_UP[4] = { 0, 3, 2, 1 }; - -static inline int -round_up(int n) -{ - return n + ROUND_UP[n % 4]; -} - -static int -read_snapshot_file(int fd, KeyedVector<String8,FileState>* snapshot) -{ - int bytesRead = 0; - int amt; - SnapshotHeader header; - - amt = read(fd, &header, sizeof(header)); - if (amt != sizeof(header)) { - return errno; - } - bytesRead += amt; - - if (header.magic0 != MAGIC0 || header.magic1 != MAGIC1) { - ALOGW("read_snapshot_file header.magic0=0x%08x magic1=0x%08x", header.magic0, header.magic1); - return 1; - } - - for (int i=0; i<header.fileCount; i++) { - FileState file; - char filenameBuf[128]; - - amt = read(fd, &file, sizeof(FileState)); - if (amt != sizeof(FileState)) { - ALOGW("read_snapshot_file FileState truncated/error with read at %d bytes\n", bytesRead); - return 1; - } - bytesRead += amt; - - // filename is not NULL terminated, but it is padded - int nameBufSize = round_up(file.nameLen); - char* filename = nameBufSize <= (int)sizeof(filenameBuf) - ? filenameBuf - : (char*)malloc(nameBufSize); - amt = read(fd, filename, nameBufSize); - if (amt == nameBufSize) { - snapshot->add(String8(filename, file.nameLen), file); - } - bytesRead += amt; - if (filename != filenameBuf) { - free(filename); - } - if (amt != nameBufSize) { - ALOGW("read_snapshot_file filename truncated/error with read at %d bytes\n", bytesRead); - return 1; - } - } - - if (header.totalSize != bytesRead) { - ALOGW("read_snapshot_file length mismatch: header.totalSize=%d bytesRead=%d\n", - header.totalSize, bytesRead); - return 1; - } - - return 0; -} - -static int -write_snapshot_file(int fd, const KeyedVector<String8,FileRec>& snapshot) -{ - int fileCount = 0; - int bytesWritten = sizeof(SnapshotHeader); - // preflight size - const int N = snapshot.size(); - for (int i=0; i<N; i++) { - const FileRec& g = snapshot.valueAt(i); - if (!g.deleted) { - const String8& name = snapshot.keyAt(i); - bytesWritten += sizeof(FileState) + round_up(name.length()); - fileCount++; - } - } - - LOGP("write_snapshot_file fd=%d\n", fd); - - int amt; - SnapshotHeader header = { MAGIC0, fileCount, MAGIC1, bytesWritten }; - - amt = write(fd, &header, sizeof(header)); - if (amt != sizeof(header)) { - ALOGW("write_snapshot_file error writing header %s", strerror(errno)); - return errno; - } - - for (int i=0; i<N; i++) { - FileRec r = snapshot.valueAt(i); - if (!r.deleted) { - const String8& name = snapshot.keyAt(i); - int nameLen = r.s.nameLen = name.length(); - - amt = write(fd, &r.s, sizeof(FileState)); - if (amt != sizeof(FileState)) { - ALOGW("write_snapshot_file error writing header %s", strerror(errno)); - return 1; - } - - // filename is not NULL terminated, but it is padded - amt = write(fd, name.string(), nameLen); - if (amt != nameLen) { - ALOGW("write_snapshot_file error writing filename %s", strerror(errno)); - return 1; - } - int paddingLen = ROUND_UP[nameLen % 4]; - if (paddingLen != 0) { - int padding = 0xabababab; - amt = write(fd, &padding, paddingLen); - if (amt != paddingLen) { - ALOGW("write_snapshot_file error writing %d bytes of filename padding %s", - paddingLen, strerror(errno)); - return 1; - } - } - } - } - - return 0; -} - -static int -write_delete_file(BackupDataWriter* dataStream, const String8& key) -{ - LOGP("write_delete_file %s\n", key.string()); - return dataStream->WriteEntityHeader(key, -1); -} - -static int -write_update_file(BackupDataWriter* dataStream, int fd, int mode, const String8& key, - char const* realFilename) -{ - LOGP("write_update_file %s (%s) : mode 0%o\n", realFilename, key.string(), mode); - - const int bufsize = 4*1024; - int err; - int amt; - int fileSize; - int bytesLeft; - file_metadata_v1 metadata; - - char* buf = (char*)malloc(bufsize); - int crc = crc32(0L, Z_NULL, 0); - - - fileSize = lseek(fd, 0, SEEK_END); - lseek(fd, 0, SEEK_SET); - - if (sizeof(metadata) != 16) { - ALOGE("ERROR: metadata block is the wrong size!"); - } - - bytesLeft = fileSize + sizeof(metadata); - err = dataStream->WriteEntityHeader(key, bytesLeft); - if (err != 0) { - free(buf); - return err; - } - - // store the file metadata first - metadata.version = tolel(CURRENT_METADATA_VERSION); - metadata.mode = tolel(mode); - metadata.undefined_1 = metadata.undefined_2 = 0; - err = dataStream->WriteEntityData(&metadata, sizeof(metadata)); - if (err != 0) { - free(buf); - return err; - } - bytesLeft -= sizeof(metadata); // bytesLeft should == fileSize now - - // now store the file content - while ((amt = read(fd, buf, bufsize)) != 0 && bytesLeft > 0) { - bytesLeft -= amt; - if (bytesLeft < 0) { - amt += bytesLeft; // Plus a negative is minus. Don't write more than we promised. - } - err = dataStream->WriteEntityData(buf, amt); - if (err != 0) { - free(buf); - return err; - } - } - if (bytesLeft != 0) { - if (bytesLeft > 0) { - // Pad out the space we promised in the buffer. We can't corrupt the buffer, - // even though the data we're sending is probably bad. - memset(buf, 0, bufsize); - while (bytesLeft > 0) { - amt = bytesLeft < bufsize ? bytesLeft : bufsize; - bytesLeft -= amt; - err = dataStream->WriteEntityData(buf, amt); - if (err != 0) { - free(buf); - return err; - } - } - } - ALOGE("write_update_file size mismatch for %s. expected=%d actual=%d." - " You aren't doing proper locking!", realFilename, fileSize, fileSize-bytesLeft); - } - - free(buf); - return NO_ERROR; -} - -static int -write_update_file(BackupDataWriter* dataStream, const String8& key, char const* realFilename) -{ - int err; - struct stat st; - - err = stat(realFilename, &st); - if (err < 0) { - return errno; - } - - int fd = open(realFilename, O_RDONLY); - if (fd == -1) { - return errno; - } - - err = write_update_file(dataStream, fd, st.st_mode, key, realFilename); - close(fd); - return err; -} - -static int -compute_crc32(int fd) -{ - const int bufsize = 4*1024; - int amt; - - char* buf = (char*)malloc(bufsize); - int crc = crc32(0L, Z_NULL, 0); - - lseek(fd, 0, SEEK_SET); - - while ((amt = read(fd, buf, bufsize)) != 0) { - crc = crc32(crc, (Bytef*)buf, amt); - } - - free(buf); - return crc; -} - -int -back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD, - char const* const* files, char const* const* keys, int fileCount) -{ - int err; - KeyedVector<String8,FileState> oldSnapshot; - KeyedVector<String8,FileRec> newSnapshot; - - if (oldSnapshotFD != -1) { - err = read_snapshot_file(oldSnapshotFD, &oldSnapshot); - if (err != 0) { - // On an error, treat this as a full backup. - oldSnapshot.clear(); - } - } - - for (int i=0; i<fileCount; i++) { - String8 key(keys[i]); - FileRec r; - char const* file = files[i]; - r.file = file; - struct stat st; - - err = stat(file, &st); - if (err != 0) { - r.deleted = true; - } else { - r.deleted = false; - r.s.modTime_sec = st.st_mtime; - r.s.modTime_nsec = 0; // workaround sim breakage - //r.s.modTime_nsec = st.st_mtime_nsec; - r.s.mode = st.st_mode; - r.s.size = st.st_size; - // we compute the crc32 later down below, when we already have the file open. - - if (newSnapshot.indexOfKey(key) >= 0) { - LOGP("back_up_files key already in use '%s'", key.string()); - return -1; - } - } - newSnapshot.add(key, r); - } - - int n = 0; - int N = oldSnapshot.size(); - int m = 0; - - while (n<N && m<fileCount) { - const String8& p = oldSnapshot.keyAt(n); - const String8& q = newSnapshot.keyAt(m); - FileRec& g = newSnapshot.editValueAt(m); - int cmp = p.compare(q); - if (g.deleted || cmp < 0) { - // file removed - LOGP("file removed: %s", p.string()); - g.deleted = true; // They didn't mention the file, but we noticed that it's gone. - dataStream->WriteEntityHeader(p, -1); - n++; - } - else if (cmp > 0) { - // file added - LOGP("file added: %s", g.file.string()); - write_update_file(dataStream, q, g.file.string()); - m++; - } - else { - // both files exist, check them - const FileState& f = oldSnapshot.valueAt(n); - - int fd = open(g.file.string(), O_RDONLY); - if (fd < 0) { - // We can't open the file. Don't report it as a delete either. Let the - // server keep the old version. Maybe they'll be able to deal with it - // on restore. - LOGP("Unable to open file %s - skipping", g.file.string()); - } else { - g.s.crc32 = compute_crc32(fd); - - LOGP("%s", q.string()); - LOGP(" new: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x", - f.modTime_sec, f.modTime_nsec, f.mode, f.size, f.crc32); - LOGP(" old: modTime=%d,%d mode=%04o size=%-3d crc32=0x%08x", - g.s.modTime_sec, g.s.modTime_nsec, g.s.mode, g.s.size, g.s.crc32); - if (f.modTime_sec != g.s.modTime_sec || f.modTime_nsec != g.s.modTime_nsec - || f.mode != g.s.mode || f.size != g.s.size || f.crc32 != g.s.crc32) { - write_update_file(dataStream, fd, g.s.mode, p, g.file.string()); - } - - close(fd); - } - n++; - m++; - } - } - - // these were deleted - while (n<N) { - dataStream->WriteEntityHeader(oldSnapshot.keyAt(n), -1); - n++; - } - - // these were added - while (m<fileCount) { - const String8& q = newSnapshot.keyAt(m); - FileRec& g = newSnapshot.editValueAt(m); - write_update_file(dataStream, q, g.file.string()); - m++; - } - - err = write_snapshot_file(newSnapshotFD, newSnapshot); - - return 0; -} - -// Utility function, equivalent to stpcpy(): perform a strcpy, but instead of -// returning the initial dest, return a pointer to the trailing NUL. -static char* strcpy_ptr(char* dest, const char* str) { - if (dest && str) { - while ((*dest = *str) != 0) { - dest++; - str++; - } - } - return dest; -} - -static void calc_tar_checksum(char* buf) { - // [ 148 : 8 ] checksum -- to be calculated with this field as space chars - memset(buf + 148, ' ', 8); - - uint16_t sum = 0; - for (uint8_t* p = (uint8_t*) buf; p < ((uint8_t*)buf) + 512; p++) { - sum += *p; - } - - // Now write the real checksum value: - // [ 148 : 8 ] checksum: 6 octal digits [leading zeroes], NUL, SPC - sprintf(buf + 148, "%06o", sum); // the trailing space is already in place -} - -// Returns number of bytes written -static int write_pax_header_entry(char* buf, const char* key, const char* value) { - // start with the size of "1 key=value\n" - int len = strlen(key) + strlen(value) + 4; - if (len > 9) len++; - if (len > 99) len++; - if (len > 999) len++; - // since PATH_MAX is 4096 we don't expect to have to generate any single - // header entry longer than 9999 characters - - return sprintf(buf, "%d %s=%s\n", len, key, value); -} - -// Wire format to the backup manager service is chunked: each chunk is prefixed by -// a 4-byte count of its size. A chunk size of zero (four zero bytes) indicates EOD. -void send_tarfile_chunk(BackupDataWriter* writer, const char* buffer, size_t size) { - uint32_t chunk_size_no = htonl(size); - writer->WriteEntityData(&chunk_size_no, 4); - if (size != 0) writer->WriteEntityData(buffer, size); -} - -int write_tarfile(const String8& packageName, const String8& domain, - const String8& rootpath, const String8& filepath, BackupDataWriter* writer) -{ - // In the output stream everything is stored relative to the root - const char* relstart = filepath.string() + rootpath.length(); - if (*relstart == '/') relstart++; // won't be true when path == rootpath - String8 relpath(relstart); - - // If relpath is empty, it means this is the top of one of the standard named - // domain directories, so we should just skip it - if (relpath.length() == 0) { - return 0; - } - - // Too long a name for the ustar format? - // "apps/" + packagename + '/' + domainpath < 155 chars - // relpath < 100 chars - bool needExtended = false; - if ((5 + packageName.length() + 1 + domain.length() >= 155) || (relpath.length() >= 100)) { - needExtended = true; - } - - // Non-7bit-clean path also means needing pax extended format - if (!needExtended) { - for (size_t i = 0; i < filepath.length(); i++) { - if ((filepath[i] & 0x80) != 0) { - needExtended = true; - break; - } - } - } - - int err = 0; - struct stat64 s; - if (lstat64(filepath.string(), &s) != 0) { - err = errno; - ALOGE("Error %d (%s) from lstat64(%s)", err, strerror(err), filepath.string()); - return err; - } - - String8 fullname; // for pax later on - String8 prefix; - - const int isdir = S_ISDIR(s.st_mode); - if (isdir) s.st_size = 0; // directories get no actual data in the tar stream - - // !!! TODO: use mmap when possible to avoid churning the buffer cache - // !!! TODO: this will break with symlinks; need to use readlink(2) - int fd = open(filepath.string(), O_RDONLY); - if (fd < 0) { - err = errno; - ALOGE("Error %d (%s) from open(%s)", err, strerror(err), filepath.string()); - return err; - } - - // read/write up to this much at a time. - const size_t BUFSIZE = 32 * 1024; - char* buf = (char *)calloc(1,BUFSIZE); - char* paxHeader = buf + 512; // use a different chunk of it as separate scratch - char* paxData = buf + 1024; - - if (buf == NULL) { - ALOGE("Out of mem allocating transfer buffer"); - err = ENOMEM; - goto done; - } - - // Magic fields for the ustar file format - strcat(buf + 257, "ustar"); - strcat(buf + 263, "00"); - - // [ 265 : 32 ] user name, ignored on restore - // [ 297 : 32 ] group name, ignored on restore - - // [ 100 : 8 ] file mode - snprintf(buf + 100, 8, "%06o ", s.st_mode & ~S_IFMT); - - // [ 108 : 8 ] uid -- ignored in Android format; uids are remapped at restore time - // [ 116 : 8 ] gid -- ignored in Android format - snprintf(buf + 108, 8, "0%lo", s.st_uid); - snprintf(buf + 116, 8, "0%lo", s.st_gid); - - // [ 124 : 12 ] file size in bytes - if (s.st_size > 077777777777LL) { - // very large files need a pax extended size header - needExtended = true; - } - snprintf(buf + 124, 12, "%011llo", (isdir) ? 0LL : s.st_size); - - // [ 136 : 12 ] last mod time as a UTC time_t - snprintf(buf + 136, 12, "%0lo", s.st_mtime); - - // [ 156 : 1 ] link/file type - uint8_t type; - if (isdir) { - type = '5'; // tar magic: '5' == directory - } else if (S_ISREG(s.st_mode)) { - type = '0'; // tar magic: '0' == normal file - } else { - ALOGW("Error: unknown file mode 0%o [%s]", s.st_mode, filepath.string()); - goto cleanup; - } - buf[156] = type; - - // [ 157 : 100 ] name of linked file [not implemented] - - { - // Prefix and main relative path. Path lengths have been preflighted. - if (packageName.length() > 0) { - prefix = "apps/"; - prefix += packageName; - } - if (domain.length() > 0) { - prefix.appendPath(domain); - } - - // pax extended means we don't put in a prefix field, and put a different - // string in the basic name field. We can also construct the full path name - // out of the substrings we've now built. - fullname = prefix; - fullname.appendPath(relpath); - - // ustar: - // [ 0 : 100 ]; file name/path - // [ 345 : 155 ] filename path prefix - // We only use the prefix area if fullname won't fit in the path - if (fullname.length() > 100) { - strncpy(buf, relpath.string(), 100); - strncpy(buf + 345, prefix.string(), 155); - } else { - strncpy(buf, fullname.string(), 100); - } - } - - // [ 329 : 8 ] and [ 337 : 8 ] devmajor/devminor, not used - - ALOGI(" Name: %s", fullname.string()); - - // If we're using a pax extended header, build & write that here; lengths are - // already preflighted - if (needExtended) { - char sizeStr[32]; // big enough for a 64-bit unsigned value in decimal - char* p = paxData; - - // construct the pax extended header data block - memset(paxData, 0, BUFSIZE - (paxData - buf)); - int len; - - // size header -- calc len in digits by actually rendering the number - // to a string - brute force but simple - snprintf(sizeStr, sizeof(sizeStr), "%lld", s.st_size); - p += write_pax_header_entry(p, "size", sizeStr); - - // fullname was generated above with the ustar paths - p += write_pax_header_entry(p, "path", fullname.string()); - - // Now we know how big the pax data is - int paxLen = p - paxData; - - // Now build the pax *header* templated on the ustar header - memcpy(paxHeader, buf, 512); - - String8 leaf = fullname.getPathLeaf(); - memset(paxHeader, 0, 100); // rewrite the name area - snprintf(paxHeader, 100, "PaxHeader/%s", leaf.string()); - memset(paxHeader + 345, 0, 155); // rewrite the prefix area - strncpy(paxHeader + 345, prefix.string(), 155); - - paxHeader[156] = 'x'; // mark it as a pax extended header - - // [ 124 : 12 ] size of pax extended header data - memset(paxHeader + 124, 0, 12); - snprintf(paxHeader + 124, 12, "%011o", p - paxData); - - // Checksum and write the pax block header - calc_tar_checksum(paxHeader); - send_tarfile_chunk(writer, paxHeader, 512); - - // Now write the pax data itself - int paxblocks = (paxLen + 511) / 512; - send_tarfile_chunk(writer, paxData, 512 * paxblocks); - } - - // Checksum and write the 512-byte ustar file header block to the output - calc_tar_checksum(buf); - send_tarfile_chunk(writer, buf, 512); - - // Now write the file data itself, for real files. We honor tar's convention that - // only full 512-byte blocks are sent to write(). - if (!isdir) { - off64_t toWrite = s.st_size; - while (toWrite > 0) { - size_t toRead = (toWrite < BUFSIZE) ? toWrite : BUFSIZE; - ssize_t nRead = read(fd, buf, toRead); - if (nRead < 0) { - err = errno; - ALOGE("Unable to read file [%s], err=%d (%s)", filepath.string(), - err, strerror(err)); - break; - } else if (nRead == 0) { - ALOGE("EOF but expect %lld more bytes in [%s]", (long long) toWrite, - filepath.string()); - err = EIO; - break; - } - - // At EOF we might have a short block; NUL-pad that to a 512-byte multiple. This - // depends on the OS guarantee that for ordinary files, read() will never return - // less than the number of bytes requested. - ssize_t partial = (nRead+512) % 512; - if (partial > 0) { - ssize_t remainder = 512 - partial; - memset(buf + nRead, 0, remainder); - nRead += remainder; - } - send_tarfile_chunk(writer, buf, nRead); - toWrite -= nRead; - } - } - -cleanup: - free(buf); -done: - close(fd); - return err; -} -// end tarfile - - - -#define RESTORE_BUF_SIZE (8*1024) - -RestoreHelperBase::RestoreHelperBase() -{ - m_buf = malloc(RESTORE_BUF_SIZE); - m_loggedUnknownMetadata = false; -} - -RestoreHelperBase::~RestoreHelperBase() -{ - free(m_buf); -} - -status_t -RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) -{ - ssize_t err; - size_t dataSize; - String8 key; - int fd; - void* buf = m_buf; - ssize_t amt; - int mode; - int crc; - struct stat st; - FileRec r; - - err = in->ReadEntityHeader(&key, &dataSize); - if (err != NO_ERROR) { - return err; - } - - // Get the metadata block off the head of the file entity and use that to - // set up the output file - file_metadata_v1 metadata; - amt = in->ReadEntityData(&metadata, sizeof(metadata)); - if (amt != sizeof(metadata)) { - ALOGW("Could not read metadata for %s -- %ld / %s", filename.string(), - (long)amt, strerror(errno)); - return EIO; - } - metadata.version = fromlel(metadata.version); - metadata.mode = fromlel(metadata.mode); - if (metadata.version > CURRENT_METADATA_VERSION) { - if (!m_loggedUnknownMetadata) { - m_loggedUnknownMetadata = true; - ALOGW("Restoring file with unsupported metadata version %d (currently %d)", - metadata.version, CURRENT_METADATA_VERSION); - } - } - mode = metadata.mode; - - // Write the file and compute the crc - crc = crc32(0L, Z_NULL, 0); - fd = open(filename.string(), O_CREAT|O_RDWR|O_TRUNC, mode); - if (fd == -1) { - ALOGW("Could not open file %s -- %s", filename.string(), strerror(errno)); - return errno; - } - - while ((amt = in->ReadEntityData(buf, RESTORE_BUF_SIZE)) > 0) { - err = write(fd, buf, amt); - if (err != amt) { - close(fd); - ALOGW("Error '%s' writing '%s'", strerror(errno), filename.string()); - return errno; - } - crc = crc32(crc, (Bytef*)buf, amt); - } - - close(fd); - - // Record for the snapshot - err = stat(filename.string(), &st); - if (err != 0) { - ALOGW("Error stating file that we just created %s", filename.string()); - return errno; - } - - r.file = filename; - r.deleted = false; - r.s.modTime_sec = st.st_mtime; - r.s.modTime_nsec = 0; // workaround sim breakage - //r.s.modTime_nsec = st.st_mtime_nsec; - r.s.mode = st.st_mode; - r.s.size = st.st_size; - r.s.crc32 = crc; - - m_files.add(key, r); - - return NO_ERROR; -} - -status_t -RestoreHelperBase::WriteSnapshot(int fd) -{ - return write_snapshot_file(fd, m_files);; -} - -#if TEST_BACKUP_HELPERS - -#define SCRATCH_DIR "/data/backup_helper_test/" - -static int -write_text_file(const char* path, const char* data) -{ - int amt; - int fd; - int len; - - fd = creat(path, 0666); - if (fd == -1) { - fprintf(stderr, "creat %s failed\n", path); - return errno; - } - - len = strlen(data); - amt = write(fd, data, len); - if (amt != len) { - fprintf(stderr, "error (%s) writing to file %s\n", strerror(errno), path); - return errno; - } - - close(fd); - - return 0; -} - -static int -compare_file(const char* path, const unsigned char* data, int len) -{ - int fd; - int amt; - - fd = open(path, O_RDONLY); - if (fd == -1) { - fprintf(stderr, "compare_file error (%s) opening %s\n", strerror(errno), path); - return errno; - } - - unsigned char* contents = (unsigned char*)malloc(len); - if (contents == NULL) { - fprintf(stderr, "malloc(%d) failed\n", len); - return ENOMEM; - } - - bool sizesMatch = true; - amt = lseek(fd, 0, SEEK_END); - if (amt != len) { - fprintf(stderr, "compare_file file length should be %d, was %d\n", len, amt); - sizesMatch = false; - } - lseek(fd, 0, SEEK_SET); - - int readLen = amt < len ? amt : len; - amt = read(fd, contents, readLen); - if (amt != readLen) { - fprintf(stderr, "compare_file read expected %d bytes but got %d\n", len, amt); - } - - bool contentsMatch = true; - for (int i=0; i<readLen; i++) { - if (data[i] != contents[i]) { - if (contentsMatch) { - fprintf(stderr, "compare_file contents are different: (index, expected, actual)\n"); - contentsMatch = false; - } - fprintf(stderr, " [%-2d] %02x %02x\n", i, data[i], contents[i]); - } - } - - free(contents); - return contentsMatch && sizesMatch ? 0 : 1; -} - -int -backup_helper_test_empty() -{ - int err; - int fd; - KeyedVector<String8,FileRec> snapshot; - const char* filename = SCRATCH_DIR "backup_helper_test_empty.snap"; - - system("rm -r " SCRATCH_DIR); - mkdir(SCRATCH_DIR, 0777); - - // write - fd = creat(filename, 0666); - if (fd == -1) { - fprintf(stderr, "error creating %s\n", filename); - return 1; - } - - err = write_snapshot_file(fd, snapshot); - - close(fd); - - if (err != 0) { - fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err)); - return err; - } - - static const unsigned char correct_data[] = { - 0x53, 0x6e, 0x61, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x69, 0x6c, 0x65, 0x10, 0x00, 0x00, 0x00 - }; - - err = compare_file(filename, correct_data, sizeof(correct_data)); - if (err != 0) { - return err; - } - - // read - fd = open(filename, O_RDONLY); - if (fd == -1) { - fprintf(stderr, "error opening for read %s\n", filename); - return 1; - } - - KeyedVector<String8,FileState> readSnapshot; - err = read_snapshot_file(fd, &readSnapshot); - if (err != 0) { - fprintf(stderr, "read_snapshot_file failed %d\n", err); - return err; - } - - if (readSnapshot.size() != 0) { - fprintf(stderr, "readSnapshot should be length 0\n"); - return 1; - } - - return 0; -} - -int -backup_helper_test_four() -{ - int err; - int fd; - KeyedVector<String8,FileRec> snapshot; - const char* filename = SCRATCH_DIR "backup_helper_test_four.snap"; - - system("rm -r " SCRATCH_DIR); - mkdir(SCRATCH_DIR, 0777); - - // write - fd = creat(filename, 0666); - if (fd == -1) { - fprintf(stderr, "error opening %s\n", filename); - return 1; - } - - String8 filenames[4]; - FileState states[4]; - FileRec r; - r.deleted = false; - - states[0].modTime_sec = 0xfedcba98; - states[0].modTime_nsec = 0xdeadbeef; - states[0].mode = 0777; // decimal 511, hex 0x000001ff - states[0].size = 0xababbcbc; - states[0].crc32 = 0x12345678; - states[0].nameLen = -12; - r.s = states[0]; - filenames[0] = String8("bytes_of_padding"); - snapshot.add(filenames[0], r); - - states[1].modTime_sec = 0x93400031; - states[1].modTime_nsec = 0xdeadbeef; - states[1].mode = 0666; // decimal 438, hex 0x000001b6 - states[1].size = 0x88557766; - states[1].crc32 = 0x22334422; - states[1].nameLen = -1; - r.s = states[1]; - filenames[1] = String8("bytes_of_padding3"); - snapshot.add(filenames[1], r); - - states[2].modTime_sec = 0x33221144; - states[2].modTime_nsec = 0xdeadbeef; - states[2].mode = 0744; // decimal 484, hex 0x000001e4 - states[2].size = 0x11223344; - states[2].crc32 = 0x01122334; - states[2].nameLen = 0; - r.s = states[2]; - filenames[2] = String8("bytes_of_padding_2"); - snapshot.add(filenames[2], r); - - states[3].modTime_sec = 0x33221144; - states[3].modTime_nsec = 0xdeadbeef; - states[3].mode = 0755; // decimal 493, hex 0x000001ed - states[3].size = 0x11223344; - states[3].crc32 = 0x01122334; - states[3].nameLen = 0; - r.s = states[3]; - filenames[3] = String8("bytes_of_padding__1"); - snapshot.add(filenames[3], r); - - err = write_snapshot_file(fd, snapshot); - - close(fd); - - if (err != 0) { - fprintf(stderr, "write_snapshot_file reported error %d (%s)\n", err, strerror(err)); - return err; - } - - static const unsigned char correct_data[] = { - // header - 0x53, 0x6e, 0x61, 0x70, 0x04, 0x00, 0x00, 0x00, - 0x46, 0x69, 0x6c, 0x65, 0xbc, 0x00, 0x00, 0x00, - - // bytes_of_padding - 0x98, 0xba, 0xdc, 0xfe, 0xef, 0xbe, 0xad, 0xde, - 0xff, 0x01, 0x00, 0x00, 0xbc, 0xbc, 0xab, 0xab, - 0x78, 0x56, 0x34, 0x12, 0x10, 0x00, 0x00, 0x00, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, - 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, - - // bytes_of_padding3 - 0x31, 0x00, 0x40, 0x93, 0xef, 0xbe, 0xad, 0xde, - 0xb6, 0x01, 0x00, 0x00, 0x66, 0x77, 0x55, 0x88, - 0x22, 0x44, 0x33, 0x22, 0x11, 0x00, 0x00, 0x00, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, - 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, - 0x33, 0xab, 0xab, 0xab, - - // bytes of padding2 - 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde, - 0xe4, 0x01, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11, - 0x34, 0x23, 0x12, 0x01, 0x12, 0x00, 0x00, 0x00, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, - 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, - 0x5f, 0x32, 0xab, 0xab, - - // bytes of padding3 - 0x44, 0x11, 0x22, 0x33, 0xef, 0xbe, 0xad, 0xde, - 0xed, 0x01, 0x00, 0x00, 0x44, 0x33, 0x22, 0x11, - 0x34, 0x23, 0x12, 0x01, 0x13, 0x00, 0x00, 0x00, - 0x62, 0x79, 0x74, 0x65, 0x73, 0x5f, 0x6f, 0x66, - 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, - 0x5f, 0x5f, 0x31, 0xab - }; - - err = compare_file(filename, correct_data, sizeof(correct_data)); - if (err != 0) { - return err; - } - - // read - fd = open(filename, O_RDONLY); - if (fd == -1) { - fprintf(stderr, "error opening for read %s\n", filename); - return 1; - } - - - KeyedVector<String8,FileState> readSnapshot; - err = read_snapshot_file(fd, &readSnapshot); - if (err != 0) { - fprintf(stderr, "read_snapshot_file failed %d\n", err); - return err; - } - - if (readSnapshot.size() != 4) { - fprintf(stderr, "readSnapshot should be length 4 is %d\n", readSnapshot.size()); - return 1; - } - - bool matched = true; - for (size_t i=0; i<readSnapshot.size(); i++) { - const String8& name = readSnapshot.keyAt(i); - const FileState state = readSnapshot.valueAt(i); - - if (name != filenames[i] || states[i].modTime_sec != state.modTime_sec - || states[i].modTime_nsec != state.modTime_nsec || states[i].mode != state.mode - || states[i].size != state.size || states[i].crc32 != states[i].crc32) { - fprintf(stderr, "state %d expected={%d/%d, 0x%08x, %04o, 0x%08x, %3d} '%s'\n" - " actual={%d/%d, 0x%08x, %04o, 0x%08x, %3d} '%s'\n", i, - states[i].modTime_sec, states[i].modTime_nsec, states[i].mode, states[i].size, - states[i].crc32, name.length(), filenames[i].string(), - state.modTime_sec, state.modTime_nsec, state.mode, state.size, state.crc32, - state.nameLen, name.string()); - matched = false; - } - } - - return matched ? 0 : 1; -} - -// hexdump -v -e '" " 8/1 " 0x%02x," "\n"' data_writer.data -const unsigned char DATA_GOLDEN_FILE[] = { - 0x44, 0x61, 0x74, 0x61, 0x0b, 0x00, 0x00, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x6e, 0x6f, 0x5f, 0x70, - 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00, - 0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, - 0x6e, 0x67, 0x5f, 0x00, 0x44, 0x61, 0x74, 0x61, - 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, - 0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc, - 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, - 0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc, - 0x44, 0x61, 0x74, 0x61, 0x0d, 0x00, 0x00, 0x00, - 0x0e, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, - 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f, - 0x5f, 0x00, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64, - 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f, - 0x5f, 0x00, 0xbc, 0xbc, 0x44, 0x61, 0x74, 0x61, - 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, - 0x6f, 0x31, 0x00, 0xbc, 0x70, 0x61, 0x64, 0x64, - 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00 - -}; -const int DATA_GOLDEN_FILE_SIZE = sizeof(DATA_GOLDEN_FILE); - -static int -test_write_header_and_entity(BackupDataWriter& writer, const char* str) -{ - int err; - String8 text(str); - - err = writer.WriteEntityHeader(text, text.length()+1); - if (err != 0) { - fprintf(stderr, "WriteEntityHeader failed with %s\n", strerror(err)); - return err; - } - - err = writer.WriteEntityData(text.string(), text.length()+1); - if (err != 0) { - fprintf(stderr, "write failed for data '%s'\n", text.string()); - return errno; - } - - return err; -} - -int -backup_helper_test_data_writer() -{ - int err; - int fd; - const char* filename = SCRATCH_DIR "data_writer.data"; - - system("rm -r " SCRATCH_DIR); - mkdir(SCRATCH_DIR, 0777); - mkdir(SCRATCH_DIR "data", 0777); - - fd = creat(filename, 0666); - if (fd == -1) { - fprintf(stderr, "error creating: %s\n", strerror(errno)); - return errno; - } - - BackupDataWriter writer(fd); - - err = 0; - err |= test_write_header_and_entity(writer, "no_padding_"); - err |= test_write_header_and_entity(writer, "padded_to__3"); - err |= test_write_header_and_entity(writer, "padded_to_2__"); - err |= test_write_header_and_entity(writer, "padded_to1"); - - close(fd); - - err = compare_file(filename, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE); - if (err != 0) { - return err; - } - - return err; -} - -int -test_read_header_and_entity(BackupDataReader& reader, const char* str) -{ - int err; - int bufSize = strlen(str)+1; - char* buf = (char*)malloc(bufSize); - String8 string; - int cookie = 0x11111111; - size_t actualSize; - bool done; - int type; - ssize_t nRead; - - // printf("\n\n---------- test_read_header_and_entity -- %s\n\n", str); - - err = reader.ReadNextHeader(&done, &type); - if (done) { - fprintf(stderr, "should not be done yet\n"); - goto finished; - } - if (err != 0) { - fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err)); - goto finished; - } - if (type != BACKUP_HEADER_ENTITY_V1) { - err = EINVAL; - fprintf(stderr, "type=0x%08x expected 0x%08x\n", type, BACKUP_HEADER_ENTITY_V1); - } - - err = reader.ReadEntityHeader(&string, &actualSize); - if (err != 0) { - fprintf(stderr, "ReadEntityHeader failed with %s\n", strerror(err)); - goto finished; - } - if (string != str) { - fprintf(stderr, "ReadEntityHeader expected key '%s' got '%s'\n", str, string.string()); - err = EINVAL; - goto finished; - } - if ((int)actualSize != bufSize) { - fprintf(stderr, "ReadEntityHeader expected dataSize 0x%08x got 0x%08x\n", bufSize, - actualSize); - err = EINVAL; - goto finished; - } - - nRead = reader.ReadEntityData(buf, bufSize); - if (nRead < 0) { - err = reader.Status(); - fprintf(stderr, "ReadEntityData failed with %s\n", strerror(err)); - goto finished; - } - - if (0 != memcmp(buf, str, bufSize)) { - fprintf(stderr, "ReadEntityData expected '%s' but got something starting with " - "%02x %02x %02x %02x '%c%c%c%c'\n", str, buf[0], buf[1], buf[2], buf[3], - buf[0], buf[1], buf[2], buf[3]); - err = EINVAL; - goto finished; - } - - // The next read will confirm whether it got the right amount of data. - -finished: - if (err != NO_ERROR) { - fprintf(stderr, "test_read_header_and_entity failed with %s\n", strerror(err)); - } - free(buf); - return err; -} - -int -backup_helper_test_data_reader() -{ - int err; - int fd; - const char* filename = SCRATCH_DIR "data_reader.data"; - - system("rm -r " SCRATCH_DIR); - mkdir(SCRATCH_DIR, 0777); - mkdir(SCRATCH_DIR "data", 0777); - - fd = creat(filename, 0666); - if (fd == -1) { - fprintf(stderr, "error creating: %s\n", strerror(errno)); - return errno; - } - - err = write(fd, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE); - if (err != DATA_GOLDEN_FILE_SIZE) { - fprintf(stderr, "Error \"%s\" writing golden file %s\n", strerror(errno), filename); - return errno; - } - - close(fd); - - fd = open(filename, O_RDONLY); - if (fd == -1) { - fprintf(stderr, "Error \"%s\" opening golden file %s for read\n", strerror(errno), - filename); - return errno; - } - - { - BackupDataReader reader(fd); - - err = 0; - - if (err == NO_ERROR) { - err = test_read_header_and_entity(reader, "no_padding_"); - } - - if (err == NO_ERROR) { - err = test_read_header_and_entity(reader, "padded_to__3"); - } - - if (err == NO_ERROR) { - err = test_read_header_and_entity(reader, "padded_to_2__"); - } - - if (err == NO_ERROR) { - err = test_read_header_and_entity(reader, "padded_to1"); - } - } - - close(fd); - - return err; -} - -static int -get_mod_time(const char* filename, struct timeval times[2]) -{ - int err; - struct stat64 st; - err = stat64(filename, &st); - if (err != 0) { - fprintf(stderr, "stat '%s' failed: %s\n", filename, strerror(errno)); - return errno; - } - times[0].tv_sec = st.st_atime; - times[1].tv_sec = st.st_mtime; - - // If st_atime is a macro then struct stat64 uses struct timespec - // to store the access and modif time values and typically - // st_*time_nsec is not defined. In glibc, this is controlled by - // __USE_MISC. -#ifdef __USE_MISC -#if !defined(st_atime) || defined(st_atime_nsec) -#error "Check if this __USE_MISC conditional is still needed." -#endif - times[0].tv_usec = st.st_atim.tv_nsec / 1000; - times[1].tv_usec = st.st_mtim.tv_nsec / 1000; -#else - times[0].tv_usec = st.st_atime_nsec / 1000; - times[1].tv_usec = st.st_mtime_nsec / 1000; -#endif - - return 0; -} - -int -backup_helper_test_files() -{ - int err; - int oldSnapshotFD; - int dataStreamFD; - int newSnapshotFD; - - system("rm -r " SCRATCH_DIR); - mkdir(SCRATCH_DIR, 0777); - mkdir(SCRATCH_DIR "data", 0777); - - write_text_file(SCRATCH_DIR "data/b", "b\nbb\n"); - write_text_file(SCRATCH_DIR "data/c", "c\ncc\n"); - write_text_file(SCRATCH_DIR "data/d", "d\ndd\n"); - write_text_file(SCRATCH_DIR "data/e", "e\nee\n"); - write_text_file(SCRATCH_DIR "data/f", "f\nff\n"); - write_text_file(SCRATCH_DIR "data/h", "h\nhh\n"); - - char const* files_before[] = { - SCRATCH_DIR "data/b", - SCRATCH_DIR "data/c", - SCRATCH_DIR "data/d", - SCRATCH_DIR "data/e", - SCRATCH_DIR "data/f" - }; - - char const* keys_before[] = { - "data/b", - "data/c", - "data/d", - "data/e", - "data/f" - }; - - dataStreamFD = creat(SCRATCH_DIR "1.data", 0666); - if (dataStreamFD == -1) { - fprintf(stderr, "error creating: %s\n", strerror(errno)); - return errno; - } - - newSnapshotFD = creat(SCRATCH_DIR "before.snap", 0666); - if (newSnapshotFD == -1) { - fprintf(stderr, "error creating: %s\n", strerror(errno)); - return errno; - } - - { - BackupDataWriter dataStream(dataStreamFD); - - err = back_up_files(-1, &dataStream, newSnapshotFD, files_before, keys_before, 5); - if (err != 0) { - return err; - } - } - - close(dataStreamFD); - close(newSnapshotFD); - - sleep(3); - - struct timeval d_times[2]; - struct timeval e_times[2]; - - err = get_mod_time(SCRATCH_DIR "data/d", d_times); - err |= get_mod_time(SCRATCH_DIR "data/e", e_times); - if (err != 0) { - return err; - } - - write_text_file(SCRATCH_DIR "data/a", "a\naa\n"); - unlink(SCRATCH_DIR "data/c"); - write_text_file(SCRATCH_DIR "data/c", "c\ncc\n"); - write_text_file(SCRATCH_DIR "data/d", "dd\ndd\n"); - utimes(SCRATCH_DIR "data/d", d_times); - write_text_file(SCRATCH_DIR "data/e", "z\nzz\n"); - utimes(SCRATCH_DIR "data/e", e_times); - write_text_file(SCRATCH_DIR "data/g", "g\ngg\n"); - unlink(SCRATCH_DIR "data/f"); - - char const* files_after[] = { - SCRATCH_DIR "data/a", // added - SCRATCH_DIR "data/b", // same - SCRATCH_DIR "data/c", // different mod time - SCRATCH_DIR "data/d", // different size (same mod time) - SCRATCH_DIR "data/e", // different contents (same mod time, same size) - SCRATCH_DIR "data/g" // added - }; - - char const* keys_after[] = { - "data/a", // added - "data/b", // same - "data/c", // different mod time - "data/d", // different size (same mod time) - "data/e", // different contents (same mod time, same size) - "data/g" // added - }; - - oldSnapshotFD = open(SCRATCH_DIR "before.snap", O_RDONLY); - if (oldSnapshotFD == -1) { - fprintf(stderr, "error opening: %s\n", strerror(errno)); - return errno; - } - - dataStreamFD = creat(SCRATCH_DIR "2.data", 0666); - if (dataStreamFD == -1) { - fprintf(stderr, "error creating: %s\n", strerror(errno)); - return errno; - } - - newSnapshotFD = creat(SCRATCH_DIR "after.snap", 0666); - if (newSnapshotFD == -1) { - fprintf(stderr, "error creating: %s\n", strerror(errno)); - return errno; - } - - { - BackupDataWriter dataStream(dataStreamFD); - - err = back_up_files(oldSnapshotFD, &dataStream, newSnapshotFD, files_after, keys_after, 6); - if (err != 0) { - return err; - } -} - - close(oldSnapshotFD); - close(dataStreamFD); - close(newSnapshotFD); - - return 0; -} - -int -backup_helper_test_null_base() -{ - int err; - int oldSnapshotFD; - int dataStreamFD; - int newSnapshotFD; - - system("rm -r " SCRATCH_DIR); - mkdir(SCRATCH_DIR, 0777); - mkdir(SCRATCH_DIR "data", 0777); - - write_text_file(SCRATCH_DIR "data/a", "a\naa\n"); - - char const* files[] = { - SCRATCH_DIR "data/a", - }; - - char const* keys[] = { - "a", - }; - - dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666); - if (dataStreamFD == -1) { - fprintf(stderr, "error creating: %s\n", strerror(errno)); - return errno; - } - - newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666); - if (newSnapshotFD == -1) { - fprintf(stderr, "error creating: %s\n", strerror(errno)); - return errno; - } - - { - BackupDataWriter dataStream(dataStreamFD); - - err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1); - if (err != 0) { - return err; - } - } - - close(dataStreamFD); - close(newSnapshotFD); - - return 0; -} - -int -backup_helper_test_missing_file() -{ - int err; - int oldSnapshotFD; - int dataStreamFD; - int newSnapshotFD; - - system("rm -r " SCRATCH_DIR); - mkdir(SCRATCH_DIR, 0777); - mkdir(SCRATCH_DIR "data", 0777); - - write_text_file(SCRATCH_DIR "data/b", "b\nbb\n"); - - char const* files[] = { - SCRATCH_DIR "data/a", - SCRATCH_DIR "data/b", - SCRATCH_DIR "data/c", - }; - - char const* keys[] = { - "a", - "b", - "c", - }; - - dataStreamFD = creat(SCRATCH_DIR "null_base.data", 0666); - if (dataStreamFD == -1) { - fprintf(stderr, "error creating: %s\n", strerror(errno)); - return errno; - } - - newSnapshotFD = creat(SCRATCH_DIR "null_base.snap", 0666); - if (newSnapshotFD == -1) { - fprintf(stderr, "error creating: %s\n", strerror(errno)); - return errno; - } - - { - BackupDataWriter dataStream(dataStreamFD); - - err = back_up_files(-1, &dataStream, newSnapshotFD, files, keys, 1); - if (err != 0) { - return err; - } - } - - close(dataStreamFD); - close(newSnapshotFD); - - return 0; -} - - -#endif // TEST_BACKUP_HELPERS - -} diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp deleted file mode 100644 index 0f54edb..0000000 --- a/libs/androidfw/CursorWindow.cpp +++ /dev/null @@ -1,352 +0,0 @@ -/* - * Copyright (C) 2006-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. - */ - -#undef LOG_TAG -#define LOG_TAG "CursorWindow" - -#include <androidfw/CursorWindow.h> -#include <binder/Parcel.h> -#include <utils/Log.h> - -#include <cutils/ashmem.h> -#include <sys/mman.h> - -#include <assert.h> -#include <string.h> -#include <stdlib.h> - -namespace android { - -CursorWindow::CursorWindow(const String8& name, int ashmemFd, - void* data, size_t size, bool readOnly) : - mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), mReadOnly(readOnly) { - mHeader = static_cast<Header*>(mData); -} - -CursorWindow::~CursorWindow() { - ::munmap(mData, mSize); - ::close(mAshmemFd); -} - -status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** outCursorWindow) { - String8 ashmemName("CursorWindow: "); - ashmemName.append(name); - - status_t result; - int ashmemFd = ashmem_create_region(ashmemName.string(), size); - if (ashmemFd < 0) { - result = -errno; - } else { - result = ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE); - if (result >= 0) { - void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0); - if (data == MAP_FAILED) { - result = -errno; - } else { - result = ashmem_set_prot_region(ashmemFd, PROT_READ); - if (result >= 0) { - CursorWindow* window = new CursorWindow(name, ashmemFd, - data, size, false /*readOnly*/); - result = window->clear(); - if (!result) { - LOG_WINDOW("Created new CursorWindow: freeOffset=%d, " - "numRows=%d, numColumns=%d, mSize=%d, mData=%p", - window->mHeader->freeOffset, - window->mHeader->numRows, - window->mHeader->numColumns, - window->mSize, window->mData); - *outCursorWindow = window; - return OK; - } - delete window; - } - } - ::munmap(data, size); - } - ::close(ashmemFd); - } - *outCursorWindow = NULL; - return result; -} - -status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) { - String8 name = parcel->readString8(); - - status_t result; - int ashmemFd = parcel->readFileDescriptor(); - if (ashmemFd == int(BAD_TYPE)) { - result = BAD_TYPE; - } else { - ssize_t size = ashmem_get_size_region(ashmemFd); - if (size < 0) { - result = UNKNOWN_ERROR; - } else { - int dupAshmemFd = ::dup(ashmemFd); - if (dupAshmemFd < 0) { - result = -errno; - } else { - void* data = ::mmap(NULL, size, PROT_READ, MAP_SHARED, dupAshmemFd, 0); - if (data == MAP_FAILED) { - result = -errno; - } else { - CursorWindow* window = new CursorWindow(name, dupAshmemFd, - data, size, true /*readOnly*/); - LOG_WINDOW("Created CursorWindow from parcel: freeOffset=%d, " - "numRows=%d, numColumns=%d, mSize=%d, mData=%p", - window->mHeader->freeOffset, - window->mHeader->numRows, - window->mHeader->numColumns, - window->mSize, window->mData); - *outCursorWindow = window; - return OK; - } - ::close(dupAshmemFd); - } - } - } - *outCursorWindow = NULL; - return result; -} - -status_t CursorWindow::writeToParcel(Parcel* parcel) { - status_t status = parcel->writeString8(mName); - if (!status) { - status = parcel->writeDupFileDescriptor(mAshmemFd); - } - return status; -} - -status_t CursorWindow::clear() { - if (mReadOnly) { - return INVALID_OPERATION; - } - - mHeader->freeOffset = sizeof(Header) + sizeof(RowSlotChunk); - mHeader->firstChunkOffset = sizeof(Header); - mHeader->numRows = 0; - mHeader->numColumns = 0; - - RowSlotChunk* firstChunk = static_cast<RowSlotChunk*>(offsetToPtr(mHeader->firstChunkOffset)); - firstChunk->nextChunkOffset = 0; - return OK; -} - -status_t CursorWindow::setNumColumns(uint32_t numColumns) { - if (mReadOnly) { - return INVALID_OPERATION; - } - - uint32_t cur = mHeader->numColumns; - if ((cur > 0 || mHeader->numRows > 0) && cur != numColumns) { - ALOGE("Trying to go from %d columns to %d", cur, numColumns); - return INVALID_OPERATION; - } - mHeader->numColumns = numColumns; - return OK; -} - -status_t CursorWindow::allocRow() { - if (mReadOnly) { - return INVALID_OPERATION; - } - - // Fill in the row slot - RowSlot* rowSlot = allocRowSlot(); - if (rowSlot == NULL) { - return NO_MEMORY; - } - - // Allocate the slots for the field directory - size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot); - uint32_t fieldDirOffset = alloc(fieldDirSize, true /*aligned*/); - if (!fieldDirOffset) { - mHeader->numRows--; - LOG_WINDOW("The row failed, so back out the new row accounting " - "from allocRowSlot %d", mHeader->numRows); - return NO_MEMORY; - } - FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(fieldDirOffset)); - memset(fieldDir, 0, fieldDirSize); - - LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", - mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset); - rowSlot->offset = fieldDirOffset; - return OK; -} - -status_t CursorWindow::freeLastRow() { - if (mReadOnly) { - return INVALID_OPERATION; - } - - if (mHeader->numRows > 0) { - mHeader->numRows--; - } - return OK; -} - -uint32_t CursorWindow::alloc(size_t size, bool aligned) { - uint32_t padding; - if (aligned) { - // 4 byte alignment - padding = (~mHeader->freeOffset + 1) & 3; - } else { - padding = 0; - } - - uint32_t offset = mHeader->freeOffset + padding; - uint32_t nextFreeOffset = offset + size; - if (nextFreeOffset > mSize) { - ALOGW("Window is full: requested allocation %d bytes, " - "free space %d bytes, window size %d bytes", - size, freeSpace(), mSize); - return 0; - } - - mHeader->freeOffset = nextFreeOffset; - return offset; -} - -CursorWindow::RowSlot* CursorWindow::getRowSlot(uint32_t row) { - uint32_t chunkPos = row; - RowSlotChunk* chunk = static_cast<RowSlotChunk*>( - offsetToPtr(mHeader->firstChunkOffset)); - while (chunkPos >= ROW_SLOT_CHUNK_NUM_ROWS) { - chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset)); - chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS; - } - return &chunk->slots[chunkPos]; -} - -CursorWindow::RowSlot* CursorWindow::allocRowSlot() { - uint32_t chunkPos = mHeader->numRows; - RowSlotChunk* chunk = static_cast<RowSlotChunk*>( - offsetToPtr(mHeader->firstChunkOffset)); - while (chunkPos > ROW_SLOT_CHUNK_NUM_ROWS) { - chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset)); - chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS; - } - if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) { - if (!chunk->nextChunkOffset) { - chunk->nextChunkOffset = alloc(sizeof(RowSlotChunk), true /*aligned*/); - if (!chunk->nextChunkOffset) { - return NULL; - } - } - chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset)); - chunk->nextChunkOffset = 0; - chunkPos = 0; - } - mHeader->numRows += 1; - return &chunk->slots[chunkPos]; -} - -CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) { - if (row >= mHeader->numRows || column >= mHeader->numColumns) { - ALOGE("Failed to read row %d, column %d from a CursorWindow which " - "has %d rows, %d columns.", - row, column, mHeader->numRows, mHeader->numColumns); - return NULL; - } - RowSlot* rowSlot = getRowSlot(row); - if (!rowSlot) { - ALOGE("Failed to find rowSlot for row %d.", row); - return NULL; - } - FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(rowSlot->offset)); - return &fieldDir[column]; -} - -status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) { - return putBlobOrString(row, column, value, size, FIELD_TYPE_BLOB); -} - -status_t CursorWindow::putString(uint32_t row, uint32_t column, const char* value, - size_t sizeIncludingNull) { - return putBlobOrString(row, column, value, sizeIncludingNull, FIELD_TYPE_STRING); -} - -status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column, - const void* value, size_t size, int32_t type) { - if (mReadOnly) { - return INVALID_OPERATION; - } - - FieldSlot* fieldSlot = getFieldSlot(row, column); - if (!fieldSlot) { - return BAD_VALUE; - } - - uint32_t offset = alloc(size); - if (!offset) { - return NO_MEMORY; - } - - memcpy(offsetToPtr(offset), value, size); - - fieldSlot->type = type; - fieldSlot->data.buffer.offset = offset; - fieldSlot->data.buffer.size = size; - return OK; -} - -status_t CursorWindow::putLong(uint32_t row, uint32_t column, int64_t value) { - if (mReadOnly) { - return INVALID_OPERATION; - } - - FieldSlot* fieldSlot = getFieldSlot(row, column); - if (!fieldSlot) { - return BAD_VALUE; - } - - fieldSlot->type = FIELD_TYPE_INTEGER; - fieldSlot->data.l = value; - return OK; -} - -status_t CursorWindow::putDouble(uint32_t row, uint32_t column, double value) { - if (mReadOnly) { - return INVALID_OPERATION; - } - - FieldSlot* fieldSlot = getFieldSlot(row, column); - if (!fieldSlot) { - return BAD_VALUE; - } - - fieldSlot->type = FIELD_TYPE_FLOAT; - fieldSlot->data.d = value; - return OK; -} - -status_t CursorWindow::putNull(uint32_t row, uint32_t column) { - if (mReadOnly) { - return INVALID_OPERATION; - } - - FieldSlot* fieldSlot = getFieldSlot(row, column); - if (!fieldSlot) { - return BAD_VALUE; - } - - fieldSlot->type = FIELD_TYPE_NULL; - fieldSlot->data.buffer.offset = 0; - fieldSlot->data.buffer.size = 0; - return OK; -} - -}; // namespace android diff --git a/libs/androidfw/MODULE_LICENSE_APACHE2 b/libs/androidfw/MODULE_LICENSE_APACHE2 deleted file mode 100644 index e69de29..0000000 --- a/libs/androidfw/MODULE_LICENSE_APACHE2 +++ /dev/null diff --git a/libs/androidfw/NOTICE b/libs/androidfw/NOTICE deleted file mode 100644 index c5b1efa..0000000 --- a/libs/androidfw/NOTICE +++ /dev/null @@ -1,190 +0,0 @@ - - Copyright (c) 2005-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. - - 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. - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - diff --git a/libs/androidfw/ObbFile.cpp b/libs/androidfw/ObbFile.cpp deleted file mode 100644 index ec59f06..0000000 --- a/libs/androidfw/ObbFile.cpp +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright (C) 2010 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 <errno.h> -#include <fcntl.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#define LOG_TAG "ObbFile" - -#include <androidfw/ObbFile.h> -#include <utils/Compat.h> -#include <utils/Log.h> - -//#define DEBUG 1 - -#define kFooterTagSize 8 /* last two 32-bit integers */ - -#define kFooterMinSize 33 /* 32-bit signature version (4 bytes) - * 32-bit package version (4 bytes) - * 32-bit flags (4 bytes) - * 64-bit salt (8 bytes) - * 32-bit package name size (4 bytes) - * >=1-character package name (1 byte) - * 32-bit footer size (4 bytes) - * 32-bit footer marker (4 bytes) - */ - -#define kMaxBufSize 32768 /* Maximum file read buffer */ - -#define kSignature 0x01059983U /* ObbFile signature */ - -#define kSigVersion 1 /* We only know about signature version 1 */ - -/* offsets in version 1 of the header */ -#define kPackageVersionOffset 4 -#define kFlagsOffset 8 -#define kSaltOffset 12 -#define kPackageNameLenOffset 20 -#define kPackageNameOffset 24 - -/* - * TEMP_FAILURE_RETRY is defined by some, but not all, versions of - * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's - * not already defined, then define it here. - */ -#ifndef TEMP_FAILURE_RETRY -/* Used to retry syscalls that can return EINTR. */ -#define TEMP_FAILURE_RETRY(exp) ({ \ - typeof (exp) _rc; \ - do { \ - _rc = (exp); \ - } while (_rc == -1 && errno == EINTR); \ - _rc; }) -#endif - - -namespace android { - -ObbFile::ObbFile() - : mPackageName("") - , mVersion(-1) - , mFlags(0) -{ - memset(mSalt, 0, sizeof(mSalt)); -} - -ObbFile::~ObbFile() { -} - -bool ObbFile::readFrom(const char* filename) -{ - int fd; - bool success = false; - - fd = ::open(filename, O_RDONLY); - if (fd < 0) { - ALOGW("couldn't open file %s: %s", filename, strerror(errno)); - goto out; - } - success = readFrom(fd); - close(fd); - - if (!success) { - ALOGW("failed to read from %s (fd=%d)\n", filename, fd); - } - -out: - return success; -} - -bool ObbFile::readFrom(int fd) -{ - if (fd < 0) { - ALOGW("attempt to read from invalid fd\n"); - return false; - } - - return parseObbFile(fd); -} - -bool ObbFile::parseObbFile(int fd) -{ - off64_t fileLength = lseek64(fd, 0, SEEK_END); - - if (fileLength < kFooterMinSize) { - if (fileLength < 0) { - ALOGW("error seeking in ObbFile: %s\n", strerror(errno)); - } else { - ALOGW("file is only %lld (less than %d minimum)\n", fileLength, kFooterMinSize); - } - return false; - } - - ssize_t actual; - size_t footerSize; - - { - lseek64(fd, fileLength - kFooterTagSize, SEEK_SET); - - char footer[kFooterTagSize]; - actual = TEMP_FAILURE_RETRY(read(fd, footer, kFooterTagSize)); - if (actual != kFooterTagSize) { - ALOGW("couldn't read footer signature: %s\n", strerror(errno)); - return false; - } - - unsigned int fileSig = get4LE((unsigned char*)footer + sizeof(int32_t)); - if (fileSig != kSignature) { - ALOGW("footer didn't match magic string (expected 0x%08x; got 0x%08x)\n", - kSignature, fileSig); - return false; - } - - footerSize = get4LE((unsigned char*)footer); - if (footerSize > (size_t)fileLength - kFooterTagSize - || footerSize > kMaxBufSize) { - ALOGW("claimed footer size is too large (0x%08zx; file size is 0x%08llx)\n", - footerSize, fileLength); - return false; - } - - if (footerSize < (kFooterMinSize - kFooterTagSize)) { - ALOGW("claimed footer size is too small (0x%zx; minimum size is 0x%x)\n", - footerSize, kFooterMinSize - kFooterTagSize); - return false; - } - } - - off64_t fileOffset = fileLength - footerSize - kFooterTagSize; - if (lseek64(fd, fileOffset, SEEK_SET) != fileOffset) { - ALOGW("seek %lld failed: %s\n", fileOffset, strerror(errno)); - return false; - } - - mFooterStart = fileOffset; - - char* scanBuf = (char*)malloc(footerSize); - if (scanBuf == NULL) { - ALOGW("couldn't allocate scanBuf: %s\n", strerror(errno)); - return false; - } - - actual = TEMP_FAILURE_RETRY(read(fd, scanBuf, footerSize)); - // readAmount is guaranteed to be less than kMaxBufSize - if (actual != (ssize_t)footerSize) { - ALOGI("couldn't read ObbFile footer: %s\n", strerror(errno)); - free(scanBuf); - return false; - } - -#ifdef DEBUG - for (int i = 0; i < footerSize; ++i) { - ALOGI("char: 0x%02x\n", scanBuf[i]); - } -#endif - - uint32_t sigVersion = get4LE((unsigned char*)scanBuf); - if (sigVersion != kSigVersion) { - ALOGW("Unsupported ObbFile version %d\n", sigVersion); - free(scanBuf); - return false; - } - - mVersion = (int32_t) get4LE((unsigned char*)scanBuf + kPackageVersionOffset); - mFlags = (int32_t) get4LE((unsigned char*)scanBuf + kFlagsOffset); - - memcpy(&mSalt, (unsigned char*)scanBuf + kSaltOffset, sizeof(mSalt)); - - size_t packageNameLen = get4LE((unsigned char*)scanBuf + kPackageNameLenOffset); - if (packageNameLen == 0 - || packageNameLen > (footerSize - kPackageNameOffset)) { - ALOGW("bad ObbFile package name length (0x%04zx; 0x%04zx possible)\n", - packageNameLen, footerSize - kPackageNameOffset); - free(scanBuf); - return false; - } - - char* packageName = reinterpret_cast<char*>(scanBuf + kPackageNameOffset); - mPackageName = String8(const_cast<char*>(packageName), packageNameLen); - - free(scanBuf); - -#ifdef DEBUG - ALOGI("Obb scan succeeded: packageName=%s, version=%d\n", mPackageName.string(), mVersion); -#endif - - return true; -} - -bool ObbFile::writeTo(const char* filename) -{ - int fd; - bool success = false; - - fd = ::open(filename, O_WRONLY); - if (fd < 0) { - goto out; - } - success = writeTo(fd); - close(fd); - -out: - if (!success) { - ALOGW("failed to write to %s: %s\n", filename, strerror(errno)); - } - return success; -} - -bool ObbFile::writeTo(int fd) -{ - if (fd < 0) { - return false; - } - - lseek64(fd, 0, SEEK_END); - - if (mPackageName.size() == 0 || mVersion == -1) { - ALOGW("tried to write uninitialized ObbFile data\n"); - return false; - } - - unsigned char intBuf[sizeof(uint32_t)+1]; - memset(&intBuf, 0, sizeof(intBuf)); - - put4LE(intBuf, kSigVersion); - if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - ALOGW("couldn't write signature version: %s\n", strerror(errno)); - return false; - } - - put4LE(intBuf, mVersion); - if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - ALOGW("couldn't write package version\n"); - return false; - } - - put4LE(intBuf, mFlags); - if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - ALOGW("couldn't write package version\n"); - return false; - } - - if (write(fd, mSalt, sizeof(mSalt)) != (ssize_t)sizeof(mSalt)) { - ALOGW("couldn't write salt: %s\n", strerror(errno)); - return false; - } - - size_t packageNameLen = mPackageName.size(); - put4LE(intBuf, packageNameLen); - if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - ALOGW("couldn't write package name length: %s\n", strerror(errno)); - return false; - } - - if (write(fd, mPackageName.string(), packageNameLen) != (ssize_t)packageNameLen) { - ALOGW("couldn't write package name: %s\n", strerror(errno)); - return false; - } - - put4LE(intBuf, kPackageNameOffset + packageNameLen); - if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - ALOGW("couldn't write footer size: %s\n", strerror(errno)); - return false; - } - - put4LE(intBuf, kSignature); - if (write(fd, &intBuf, sizeof(uint32_t)) != (ssize_t)sizeof(uint32_t)) { - ALOGW("couldn't write footer magic signature: %s\n", strerror(errno)); - return false; - } - - return true; -} - -bool ObbFile::removeFrom(const char* filename) -{ - int fd; - bool success = false; - - fd = ::open(filename, O_RDWR); - if (fd < 0) { - goto out; - } - success = removeFrom(fd); - close(fd); - -out: - if (!success) { - ALOGW("failed to remove signature from %s: %s\n", filename, strerror(errno)); - } - return success; -} - -bool ObbFile::removeFrom(int fd) -{ - if (fd < 0) { - return false; - } - - if (!readFrom(fd)) { - return false; - } - - ftruncate(fd, mFooterStart); - - return true; -} - -} diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp deleted file mode 100644 index 1cc3563..0000000 --- a/libs/androidfw/ResourceTypes.cpp +++ /dev/null @@ -1,5796 +0,0 @@ -/* - * 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. - */ - -#define LOG_TAG "ResourceType" -//#define LOG_NDEBUG 0 - -#include <androidfw/ResourceTypes.h> -#include <utils/Atomic.h> -#include <utils/ByteOrder.h> -#include <utils/Debug.h> -#include <utils/Log.h> -#include <utils/String16.h> -#include <utils/String8.h> - -#include <stdlib.h> -#include <string.h> -#include <memory.h> -#include <ctype.h> -#include <stdint.h> - -#ifndef INT32_MAX -#define INT32_MAX ((int32_t)(2147483647)) -#endif - -#define STRING_POOL_NOISY(x) //x -#define XML_NOISY(x) //x -#define TABLE_NOISY(x) //x -#define TABLE_GETENTRY(x) //x -#define TABLE_SUPER_NOISY(x) //x -#define LOAD_TABLE_NOISY(x) //x -#define TABLE_THEME(x) //x - -namespace android { - -#ifdef HAVE_WINSOCK -#undef nhtol -#undef htonl - -#ifdef HAVE_LITTLE_ENDIAN -#define ntohl(x) ( ((x) << 24) | (((x) >> 24) & 255) | (((x) << 8) & 0xff0000) | (((x) >> 8) & 0xff00) ) -#define htonl(x) ntohl(x) -#define ntohs(x) ( (((x) << 8) & 0xff00) | (((x) >> 8) & 255) ) -#define htons(x) ntohs(x) -#else -#define ntohl(x) (x) -#define htonl(x) (x) -#define ntohs(x) (x) -#define htons(x) (x) -#endif -#endif - -#define IDMAP_MAGIC 0x706d6469 -// size measured in sizeof(uint32_t) -#define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t)) - -static void printToLogFunc(void* cookie, const char* txt) -{ - ALOGV("%s", txt); -} - -// Standard C isspace() is only required to look at the low byte of its input, so -// produces incorrect results for UTF-16 characters. For safety's sake, assume that -// any high-byte UTF-16 code point is not whitespace. -inline int isspace16(char16_t c) { - return (c < 0x0080 && isspace(c)); -} - -// range checked; guaranteed to NUL-terminate within the stated number of available slots -// NOTE: if this truncates the dst string due to running out of space, no attempt is -// made to avoid splitting surrogate pairs. -static void strcpy16_dtoh(uint16_t* dst, const uint16_t* src, size_t avail) -{ - uint16_t* last = dst + avail - 1; - while (*src && (dst < last)) { - char16_t s = dtohs(*src); - *dst++ = s; - src++; - } - *dst = 0; -} - -static status_t validate_chunk(const ResChunk_header* chunk, - size_t minSize, - const uint8_t* dataEnd, - const char* name) -{ - const uint16_t headerSize = dtohs(chunk->headerSize); - const uint32_t size = dtohl(chunk->size); - - if (headerSize >= minSize) { - if (headerSize <= size) { - if (((headerSize|size)&0x3) == 0) { - if ((ssize_t)size <= (dataEnd-((const uint8_t*)chunk))) { - return NO_ERROR; - } - ALOGW("%s data size %p extends beyond resource end %p.", - name, (void*)size, - (void*)(dataEnd-((const uint8_t*)chunk))); - return BAD_TYPE; - } - ALOGW("%s size 0x%x or headerSize 0x%x is not on an integer boundary.", - name, (int)size, (int)headerSize); - return BAD_TYPE; - } - ALOGW("%s size %p is smaller than header size %p.", - name, (void*)size, (void*)(int)headerSize); - return BAD_TYPE; - } - ALOGW("%s header size %p is too small.", - name, (void*)(int)headerSize); - return BAD_TYPE; -} - -inline void Res_value::copyFrom_dtoh(const Res_value& src) -{ - size = dtohs(src.size); - res0 = src.res0; - dataType = src.dataType; - data = dtohl(src.data); -} - -void Res_png_9patch::deviceToFile() -{ - for (int i = 0; i < numXDivs; i++) { - xDivs[i] = htonl(xDivs[i]); - } - for (int i = 0; i < numYDivs; i++) { - yDivs[i] = htonl(yDivs[i]); - } - paddingLeft = htonl(paddingLeft); - paddingRight = htonl(paddingRight); - paddingTop = htonl(paddingTop); - paddingBottom = htonl(paddingBottom); - for (int i=0; i<numColors; i++) { - colors[i] = htonl(colors[i]); - } -} - -void Res_png_9patch::fileToDevice() -{ - for (int i = 0; i < numXDivs; i++) { - xDivs[i] = ntohl(xDivs[i]); - } - for (int i = 0; i < numYDivs; i++) { - yDivs[i] = ntohl(yDivs[i]); - } - paddingLeft = ntohl(paddingLeft); - paddingRight = ntohl(paddingRight); - paddingTop = ntohl(paddingTop); - paddingBottom = ntohl(paddingBottom); - for (int i=0; i<numColors; i++) { - colors[i] = ntohl(colors[i]); - } -} - -size_t Res_png_9patch::serializedSize() -{ - // The size of this struct is 32 bytes on the 32-bit target system - // 4 * int8_t - // 4 * int32_t - // 3 * pointer - return 32 - + numXDivs * sizeof(int32_t) - + numYDivs * sizeof(int32_t) - + numColors * sizeof(uint32_t); -} - -void* Res_png_9patch::serialize() -{ - // Use calloc since we're going to leave a few holes in the data - // and want this to run cleanly under valgrind - void* newData = calloc(1, serializedSize()); - serialize(newData); - return newData; -} - -void Res_png_9patch::serialize(void * outData) -{ - char* data = (char*) outData; - memmove(data, &wasDeserialized, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors - memmove(data + 12, &paddingLeft, 16); // copy paddingXXXX - data += 32; - - memmove(data, this->xDivs, numXDivs * sizeof(int32_t)); - data += numXDivs * sizeof(int32_t); - memmove(data, this->yDivs, numYDivs * sizeof(int32_t)); - data += numYDivs * sizeof(int32_t); - memmove(data, this->colors, numColors * sizeof(uint32_t)); -} - -static void deserializeInternal(const void* inData, Res_png_9patch* outData) { - char* patch = (char*) inData; - if (inData != outData) { - memmove(&outData->wasDeserialized, patch, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors - memmove(&outData->paddingLeft, patch + 12, 4); // copy wasDeserialized, numXDivs, numYDivs, numColors - } - outData->wasDeserialized = true; - char* data = (char*)outData; - data += sizeof(Res_png_9patch); - outData->xDivs = (int32_t*) data; - data += outData->numXDivs * sizeof(int32_t); - outData->yDivs = (int32_t*) data; - data += outData->numYDivs * sizeof(int32_t); - outData->colors = (uint32_t*) data; -} - -static bool assertIdmapHeader(const uint32_t* map, size_t sizeBytes) -{ - if (sizeBytes < ResTable::IDMAP_HEADER_SIZE_BYTES) { - ALOGW("idmap assertion failed: size=%d bytes\n", (int)sizeBytes); - return false; - } - if (*map != htodl(IDMAP_MAGIC)) { // htodl: map data expected to be in correct endianess - ALOGW("idmap assertion failed: invalid magic found (is 0x%08x, expected 0x%08x)\n", - *map, htodl(IDMAP_MAGIC)); - return false; - } - return true; -} - -static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, uint32_t* outValue) -{ - // see README for details on the format of map - if (!assertIdmapHeader(map, sizeBytes)) { - return UNKNOWN_ERROR; - } - map = map + IDMAP_HEADER_SIZE; // skip ahead to data segment - // size of data block, in uint32_t - const size_t size = (sizeBytes - ResTable::IDMAP_HEADER_SIZE_BYTES) / sizeof(uint32_t); - const uint32_t type = Res_GETTYPE(key) + 1; // add one, idmap stores "public" type id - const uint32_t entry = Res_GETENTRY(key); - const uint32_t typeCount = *map; - - if (type > typeCount) { - ALOGW("Resource ID map: type=%d exceeds number of types=%d\n", type, typeCount); - return UNKNOWN_ERROR; - } - if (typeCount > size) { - ALOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, (int)size); - return UNKNOWN_ERROR; - } - const uint32_t typeOffset = map[type]; - if (typeOffset == 0) { - *outValue = 0; - return NO_ERROR; - } - if (typeOffset + 1 > size) { - ALOGW("Resource ID map: type offset=%d exceeds reasonable value, size of map=%d\n", - typeOffset, (int)size); - return UNKNOWN_ERROR; - } - const uint32_t entryCount = map[typeOffset]; - const uint32_t entryOffset = map[typeOffset + 1]; - if (entryCount == 0 || entry < entryOffset || entry - entryOffset > entryCount - 1) { - *outValue = 0; - return NO_ERROR; - } - const uint32_t index = typeOffset + 2 + entry - entryOffset; - if (index > size) { - ALOGW("Resource ID map: entry index=%d exceeds size of map=%d\n", index, (int)size); - *outValue = 0; - return NO_ERROR; - } - *outValue = map[index]; - - return NO_ERROR; -} - -static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t *outId) -{ - if (!assertIdmapHeader(map, mapSize)) { - return UNKNOWN_ERROR; - } - const uint32_t* p = map + IDMAP_HEADER_SIZE + 1; - while (*p == 0) { - ++p; - } - *outId = (map[*p + IDMAP_HEADER_SIZE + 2] >> 24) & 0x000000ff; - return NO_ERROR; -} - -Res_png_9patch* Res_png_9patch::deserialize(const void* inData) -{ - if (sizeof(void*) != sizeof(int32_t)) { - ALOGE("Cannot deserialize on non 32-bit system\n"); - return NULL; - } - deserializeInternal(inData, (Res_png_9patch*) inData); - return (Res_png_9patch*) inData; -} - -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- - -ResStringPool::ResStringPool() - : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL) -{ -} - -ResStringPool::ResStringPool(const void* data, size_t size, bool copyData) - : mError(NO_INIT), mOwnedData(NULL), mHeader(NULL), mCache(NULL) -{ - setTo(data, size, copyData); -} - -ResStringPool::~ResStringPool() -{ - uninit(); -} - -status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) -{ - if (!data || !size) { - return (mError=BAD_TYPE); - } - - uninit(); - - const bool notDeviceEndian = htods(0xf0) != 0xf0; - - if (copyData || notDeviceEndian) { - mOwnedData = malloc(size); - if (mOwnedData == NULL) { - return (mError=NO_MEMORY); - } - memcpy(mOwnedData, data, size); - data = mOwnedData; - } - - mHeader = (const ResStringPool_header*)data; - - if (notDeviceEndian) { - ResStringPool_header* h = const_cast<ResStringPool_header*>(mHeader); - h->header.headerSize = dtohs(mHeader->header.headerSize); - h->header.type = dtohs(mHeader->header.type); - h->header.size = dtohl(mHeader->header.size); - h->stringCount = dtohl(mHeader->stringCount); - h->styleCount = dtohl(mHeader->styleCount); - h->flags = dtohl(mHeader->flags); - h->stringsStart = dtohl(mHeader->stringsStart); - h->stylesStart = dtohl(mHeader->stylesStart); - } - - if (mHeader->header.headerSize > mHeader->header.size - || mHeader->header.size > size) { - ALOGW("Bad string block: header size %d or total size %d is larger than data size %d\n", - (int)mHeader->header.headerSize, (int)mHeader->header.size, (int)size); - return (mError=BAD_TYPE); - } - mSize = mHeader->header.size; - mEntries = (const uint32_t*) - (((const uint8_t*)data)+mHeader->header.headerSize); - - if (mHeader->stringCount > 0) { - if ((mHeader->stringCount*sizeof(uint32_t) < mHeader->stringCount) // uint32 overflow? - || (mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))) - > size) { - ALOGW("Bad string block: entry of %d items extends past data size %d\n", - (int)(mHeader->header.headerSize+(mHeader->stringCount*sizeof(uint32_t))), - (int)size); - return (mError=BAD_TYPE); - } - - size_t charSize; - if (mHeader->flags&ResStringPool_header::UTF8_FLAG) { - charSize = sizeof(uint8_t); - } else { - charSize = sizeof(char16_t); - } - - mStrings = (const void*) - (((const uint8_t*)data)+mHeader->stringsStart); - if (mHeader->stringsStart >= (mHeader->header.size-sizeof(uint16_t))) { - ALOGW("Bad string block: string pool starts at %d, after total size %d\n", - (int)mHeader->stringsStart, (int)mHeader->header.size); - return (mError=BAD_TYPE); - } - if (mHeader->styleCount == 0) { - mStringPoolSize = - (mHeader->header.size-mHeader->stringsStart)/charSize; - } else { - // check invariant: styles starts before end of data - if (mHeader->stylesStart >= (mHeader->header.size-sizeof(uint16_t))) { - ALOGW("Bad style block: style block starts at %d past data size of %d\n", - (int)mHeader->stylesStart, (int)mHeader->header.size); - return (mError=BAD_TYPE); - } - // check invariant: styles follow the strings - if (mHeader->stylesStart <= mHeader->stringsStart) { - ALOGW("Bad style block: style block starts at %d, before strings at %d\n", - (int)mHeader->stylesStart, (int)mHeader->stringsStart); - return (mError=BAD_TYPE); - } - mStringPoolSize = - (mHeader->stylesStart-mHeader->stringsStart)/charSize; - } - - // check invariant: stringCount > 0 requires a string pool to exist - if (mStringPoolSize == 0) { - ALOGW("Bad string block: stringCount is %d but pool size is 0\n", (int)mHeader->stringCount); - return (mError=BAD_TYPE); - } - - if (notDeviceEndian) { - size_t i; - uint32_t* e = const_cast<uint32_t*>(mEntries); - for (i=0; i<mHeader->stringCount; i++) { - e[i] = dtohl(mEntries[i]); - } - if (!(mHeader->flags&ResStringPool_header::UTF8_FLAG)) { - const char16_t* strings = (const char16_t*)mStrings; - char16_t* s = const_cast<char16_t*>(strings); - for (i=0; i<mStringPoolSize; i++) { - s[i] = dtohs(strings[i]); - } - } - } - - if ((mHeader->flags&ResStringPool_header::UTF8_FLAG && - ((uint8_t*)mStrings)[mStringPoolSize-1] != 0) || - (!mHeader->flags&ResStringPool_header::UTF8_FLAG && - ((char16_t*)mStrings)[mStringPoolSize-1] != 0)) { - ALOGW("Bad string block: last string is not 0-terminated\n"); - return (mError=BAD_TYPE); - } - } else { - mStrings = NULL; - mStringPoolSize = 0; - } - - if (mHeader->styleCount > 0) { - mEntryStyles = mEntries + mHeader->stringCount; - // invariant: integer overflow in calculating mEntryStyles - if (mEntryStyles < mEntries) { - ALOGW("Bad string block: integer overflow finding styles\n"); - return (mError=BAD_TYPE); - } - - if (((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader) > (int)size) { - ALOGW("Bad string block: entry of %d styles extends past data size %d\n", - (int)((const uint8_t*)mEntryStyles-(const uint8_t*)mHeader), - (int)size); - return (mError=BAD_TYPE); - } - mStyles = (const uint32_t*) - (((const uint8_t*)data)+mHeader->stylesStart); - if (mHeader->stylesStart >= mHeader->header.size) { - ALOGW("Bad string block: style pool starts %d, after total size %d\n", - (int)mHeader->stylesStart, (int)mHeader->header.size); - return (mError=BAD_TYPE); - } - mStylePoolSize = - (mHeader->header.size-mHeader->stylesStart)/sizeof(uint32_t); - - if (notDeviceEndian) { - size_t i; - uint32_t* e = const_cast<uint32_t*>(mEntryStyles); - for (i=0; i<mHeader->styleCount; i++) { - e[i] = dtohl(mEntryStyles[i]); - } - uint32_t* s = const_cast<uint32_t*>(mStyles); - for (i=0; i<mStylePoolSize; i++) { - s[i] = dtohl(mStyles[i]); - } - } - - const ResStringPool_span endSpan = { - { htodl(ResStringPool_span::END) }, - htodl(ResStringPool_span::END), htodl(ResStringPool_span::END) - }; - if (memcmp(&mStyles[mStylePoolSize-(sizeof(endSpan)/sizeof(uint32_t))], - &endSpan, sizeof(endSpan)) != 0) { - ALOGW("Bad string block: last style is not 0xFFFFFFFF-terminated\n"); - return (mError=BAD_TYPE); - } - } else { - mEntryStyles = NULL; - mStyles = NULL; - mStylePoolSize = 0; - } - - return (mError=NO_ERROR); -} - -status_t ResStringPool::getError() const -{ - return mError; -} - -void ResStringPool::uninit() -{ - mError = NO_INIT; - if (mHeader != NULL && mCache != NULL) { - for (size_t x = 0; x < mHeader->stringCount; x++) { - if (mCache[x] != NULL) { - free(mCache[x]); - mCache[x] = NULL; - } - } - free(mCache); - mCache = NULL; - } - if (mOwnedData) { - free(mOwnedData); - mOwnedData = NULL; - } -} - -/** - * Strings in UTF-16 format have length indicated by a length encoded in the - * stored data. It is either 1 or 2 characters of length data. This allows a - * maximum length of 0x7FFFFFF (2147483647 bytes), but if you're storing that - * much data in a string, you're abusing them. - * - * If the high bit is set, then there are two characters or 4 bytes of length - * data encoded. In that case, drop the high bit of the first character and - * add it together with the next character. - */ -static inline size_t -decodeLength(const char16_t** str) -{ - size_t len = **str; - if ((len & 0x8000) != 0) { - (*str)++; - len = ((len & 0x7FFF) << 16) | **str; - } - (*str)++; - return len; -} - -/** - * Strings in UTF-8 format have length indicated by a length encoded in the - * stored data. It is either 1 or 2 characters of length data. This allows a - * maximum length of 0x7FFF (32767 bytes), but you should consider storing - * text in another way if you're using that much data in a single string. - * - * If the high bit is set, then there are two characters or 2 bytes of length - * data encoded. In that case, drop the high bit of the first character and - * add it together with the next character. - */ -static inline size_t -decodeLength(const uint8_t** str) -{ - size_t len = **str; - if ((len & 0x80) != 0) { - (*str)++; - len = ((len & 0x7F) << 8) | **str; - } - (*str)++; - return len; -} - -const uint16_t* ResStringPool::stringAt(size_t idx, size_t* u16len) const -{ - if (mError == NO_ERROR && idx < mHeader->stringCount) { - const bool isUTF8 = (mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0; - const uint32_t off = mEntries[idx]/(isUTF8?sizeof(char):sizeof(char16_t)); - if (off < (mStringPoolSize-1)) { - if (!isUTF8) { - const char16_t* strings = (char16_t*)mStrings; - const char16_t* str = strings+off; - - *u16len = decodeLength(&str); - if ((uint32_t)(str+*u16len-strings) < mStringPoolSize) { - return str; - } else { - ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+*u16len-strings), (int)mStringPoolSize); - } - } else { - const uint8_t* strings = (uint8_t*)mStrings; - const uint8_t* u8str = strings+off; - - *u16len = decodeLength(&u8str); - size_t u8len = decodeLength(&u8str); - - // encLen must be less than 0x7FFF due to encoding. - if ((uint32_t)(u8str+u8len-strings) < mStringPoolSize) { - AutoMutex lock(mDecodeLock); - - if (mCache == NULL) { -#ifndef HAVE_ANDROID_OS - STRING_POOL_NOISY(ALOGI("CREATING STRING CACHE OF %d bytes", - mHeader->stringCount*sizeof(char16_t**))); -#else - // We do not want to be in this case when actually running Android. - ALOGW("CREATING STRING CACHE OF %d bytes", - mHeader->stringCount*sizeof(char16_t**)); -#endif - mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t**)); - if (mCache == NULL) { - ALOGW("No memory trying to allocate decode cache table of %d bytes\n", - (int)(mHeader->stringCount*sizeof(char16_t**))); - return NULL; - } - } - - if (mCache[idx] != NULL) { - return mCache[idx]; - } - - ssize_t actualLen = utf8_to_utf16_length(u8str, u8len); - if (actualLen < 0 || (size_t)actualLen != *u16len) { - ALOGW("Bad string block: string #%lld decoded length is not correct " - "%lld vs %llu\n", - (long long)idx, (long long)actualLen, (long long)*u16len); - return NULL; - } - - char16_t *u16str = (char16_t *)calloc(*u16len+1, sizeof(char16_t)); - if (!u16str) { - ALOGW("No memory when trying to allocate decode cache for string #%d\n", - (int)idx); - return NULL; - } - - STRING_POOL_NOISY(ALOGI("Caching UTF8 string: %s", u8str)); - utf8_to_utf16(u8str, u8len, u16str); - mCache[idx] = u16str; - return u16str; - } else { - ALOGW("Bad string block: string #%lld extends to %lld, past end at %lld\n", - (long long)idx, (long long)(u8str+u8len-strings), - (long long)mStringPoolSize); - } - } - } else { - ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n", - (int)idx, (int)(off*sizeof(uint16_t)), - (int)(mStringPoolSize*sizeof(uint16_t))); - } - } - return NULL; -} - -const char* ResStringPool::string8At(size_t idx, size_t* outLen) const -{ - if (mError == NO_ERROR && idx < mHeader->stringCount) { - if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) == 0) { - return NULL; - } - const uint32_t off = mEntries[idx]/sizeof(char); - if (off < (mStringPoolSize-1)) { - const uint8_t* strings = (uint8_t*)mStrings; - const uint8_t* str = strings+off; - *outLen = decodeLength(&str); - size_t encLen = decodeLength(&str); - if ((uint32_t)(str+encLen-strings) < mStringPoolSize) { - return (const char*)str; - } else { - ALOGW("Bad string block: string #%d extends to %d, past end at %d\n", - (int)idx, (int)(str+encLen-strings), (int)mStringPoolSize); - } - } else { - ALOGW("Bad string block: string #%d entry is at %d, past end at %d\n", - (int)idx, (int)(off*sizeof(uint16_t)), - (int)(mStringPoolSize*sizeof(uint16_t))); - } - } - return NULL; -} - -const String8 ResStringPool::string8ObjectAt(size_t idx) const -{ - size_t len; - const char *str = (const char*)string8At(idx, &len); - if (str != NULL) { - return String8(str); - } - return String8(stringAt(idx, &len)); -} - -const ResStringPool_span* ResStringPool::styleAt(const ResStringPool_ref& ref) const -{ - return styleAt(ref.index); -} - -const ResStringPool_span* ResStringPool::styleAt(size_t idx) const -{ - if (mError == NO_ERROR && idx < mHeader->styleCount) { - const uint32_t off = (mEntryStyles[idx]/sizeof(uint32_t)); - if (off < mStylePoolSize) { - return (const ResStringPool_span*)(mStyles+off); - } else { - ALOGW("Bad string block: style #%d entry is at %d, past end at %d\n", - (int)idx, (int)(off*sizeof(uint32_t)), - (int)(mStylePoolSize*sizeof(uint32_t))); - } - } - return NULL; -} - -ssize_t ResStringPool::indexOfString(const char16_t* str, size_t strLen) const -{ - if (mError != NO_ERROR) { - return mError; - } - - size_t len; - - if ((mHeader->flags&ResStringPool_header::UTF8_FLAG) != 0) { - STRING_POOL_NOISY(ALOGI("indexOfString UTF-8: %s", String8(str, strLen).string())); - - // The string pool contains UTF 8 strings; we don't want to cause - // temporary UTF-16 strings to be created as we search. - if (mHeader->flags&ResStringPool_header::SORTED_FLAG) { - // Do a binary search for the string... this is a little tricky, - // because the strings are sorted with strzcmp16(). So to match - // the ordering, we need to convert strings in the pool to UTF-16. - // But we don't want to hit the cache, so instead we will have a - // local temporary allocation for the conversions. - char16_t* convBuffer = (char16_t*)malloc(strLen+4); - ssize_t l = 0; - ssize_t h = mHeader->stringCount-1; - - ssize_t mid; - while (l <= h) { - mid = l + (h - l)/2; - const uint8_t* s = (const uint8_t*)string8At(mid, &len); - int c; - if (s != NULL) { - char16_t* end = utf8_to_utf16_n(s, len, convBuffer, strLen+3); - *end = 0; - c = strzcmp16(convBuffer, end-convBuffer, str, strLen); - } else { - c = -1; - } - STRING_POOL_NOISY(ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", - (const char*)s, c, (int)l, (int)mid, (int)h)); - if (c == 0) { - STRING_POOL_NOISY(ALOGI("MATCH!")); - free(convBuffer); - return mid; - } else if (c < 0) { - l = mid + 1; - } else { - h = mid - 1; - } - } - free(convBuffer); - } else { - // It is unusual to get the ID from an unsorted string block... - // most often this happens because we want to get IDs for style - // span tags; since those always appear at the end of the string - // block, start searching at the back. - String8 str8(str, strLen); - const size_t str8Len = str8.size(); - for (int i=mHeader->stringCount-1; i>=0; i--) { - const char* s = string8At(i, &len); - STRING_POOL_NOISY(ALOGI("Looking at %s, i=%d\n", - String8(s).string(), - i)); - if (s && str8Len == len && memcmp(s, str8.string(), str8Len) == 0) { - STRING_POOL_NOISY(ALOGI("MATCH!")); - return i; - } - } - } - - } else { - STRING_POOL_NOISY(ALOGI("indexOfString UTF-16: %s", String8(str, strLen).string())); - - if (mHeader->flags&ResStringPool_header::SORTED_FLAG) { - // Do a binary search for the string... - ssize_t l = 0; - ssize_t h = mHeader->stringCount-1; - - ssize_t mid; - while (l <= h) { - mid = l + (h - l)/2; - const char16_t* s = stringAt(mid, &len); - int c = s ? strzcmp16(s, len, str, strLen) : -1; - STRING_POOL_NOISY(ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n", - String8(s).string(), - c, (int)l, (int)mid, (int)h)); - if (c == 0) { - STRING_POOL_NOISY(ALOGI("MATCH!")); - return mid; - } else if (c < 0) { - l = mid + 1; - } else { - h = mid - 1; - } - } - } else { - // It is unusual to get the ID from an unsorted string block... - // most often this happens because we want to get IDs for style - // span tags; since those always appear at the end of the string - // block, start searching at the back. - for (int i=mHeader->stringCount-1; i>=0; i--) { - const char16_t* s = stringAt(i, &len); - STRING_POOL_NOISY(ALOGI("Looking at %s, i=%d\n", - String8(s).string(), - i)); - if (s && strLen == len && strzcmp16(s, len, str, strLen) == 0) { - STRING_POOL_NOISY(ALOGI("MATCH!")); - return i; - } - } - } - } - - return NAME_NOT_FOUND; -} - -size_t ResStringPool::size() const -{ - return (mError == NO_ERROR) ? mHeader->stringCount : 0; -} - -size_t ResStringPool::styleCount() const -{ - return (mError == NO_ERROR) ? mHeader->styleCount : 0; -} - -size_t ResStringPool::bytes() const -{ - return (mError == NO_ERROR) ? mHeader->header.size : 0; -} - -bool ResStringPool::isSorted() const -{ - return (mHeader->flags&ResStringPool_header::SORTED_FLAG)!=0; -} - -bool ResStringPool::isUTF8() const -{ - return (mHeader->flags&ResStringPool_header::UTF8_FLAG)!=0; -} - -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- - -ResXMLParser::ResXMLParser(const ResXMLTree& tree) - : mTree(tree), mEventCode(BAD_DOCUMENT) -{ -} - -void ResXMLParser::restart() -{ - mCurNode = NULL; - mEventCode = mTree.mError == NO_ERROR ? START_DOCUMENT : BAD_DOCUMENT; -} -const ResStringPool& ResXMLParser::getStrings() const -{ - return mTree.mStrings; -} - -ResXMLParser::event_code_t ResXMLParser::getEventType() const -{ - return mEventCode; -} - -ResXMLParser::event_code_t ResXMLParser::next() -{ - if (mEventCode == START_DOCUMENT) { - mCurNode = mTree.mRootNode; - mCurExt = mTree.mRootExt; - return (mEventCode=mTree.mRootCode); - } else if (mEventCode >= FIRST_CHUNK_CODE) { - return nextNode(); - } - return mEventCode; -} - -int32_t ResXMLParser::getCommentID() const -{ - return mCurNode != NULL ? dtohl(mCurNode->comment.index) : -1; -} - -const uint16_t* ResXMLParser::getComment(size_t* outLen) const -{ - int32_t id = getCommentID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -uint32_t ResXMLParser::getLineNumber() const -{ - return mCurNode != NULL ? dtohl(mCurNode->lineNumber) : -1; -} - -int32_t ResXMLParser::getTextID() const -{ - if (mEventCode == TEXT) { - return dtohl(((const ResXMLTree_cdataExt*)mCurExt)->data.index); - } - return -1; -} - -const uint16_t* ResXMLParser::getText(size_t* outLen) const -{ - int32_t id = getTextID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -ssize_t ResXMLParser::getTextValue(Res_value* outValue) const -{ - if (mEventCode == TEXT) { - outValue->copyFrom_dtoh(((const ResXMLTree_cdataExt*)mCurExt)->typedData); - return sizeof(Res_value); - } - return BAD_TYPE; -} - -int32_t ResXMLParser::getNamespacePrefixID() const -{ - if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { - return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->prefix.index); - } - return -1; -} - -const uint16_t* ResXMLParser::getNamespacePrefix(size_t* outLen) const -{ - int32_t id = getNamespacePrefixID(); - //printf("prefix=%d event=%p\n", id, mEventCode); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -int32_t ResXMLParser::getNamespaceUriID() const -{ - if (mEventCode == START_NAMESPACE || mEventCode == END_NAMESPACE) { - return dtohl(((const ResXMLTree_namespaceExt*)mCurExt)->uri.index); - } - return -1; -} - -const uint16_t* ResXMLParser::getNamespaceUri(size_t* outLen) const -{ - int32_t id = getNamespaceUriID(); - //printf("uri=%d event=%p\n", id, mEventCode); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -int32_t ResXMLParser::getElementNamespaceID() const -{ - if (mEventCode == START_TAG) { - return dtohl(((const ResXMLTree_attrExt*)mCurExt)->ns.index); - } - if (mEventCode == END_TAG) { - return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->ns.index); - } - return -1; -} - -const uint16_t* ResXMLParser::getElementNamespace(size_t* outLen) const -{ - int32_t id = getElementNamespaceID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -int32_t ResXMLParser::getElementNameID() const -{ - if (mEventCode == START_TAG) { - return dtohl(((const ResXMLTree_attrExt*)mCurExt)->name.index); - } - if (mEventCode == END_TAG) { - return dtohl(((const ResXMLTree_endElementExt*)mCurExt)->name.index); - } - return -1; -} - -const uint16_t* ResXMLParser::getElementName(size_t* outLen) const -{ - int32_t id = getElementNameID(); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -size_t ResXMLParser::getAttributeCount() const -{ - if (mEventCode == START_TAG) { - return dtohs(((const ResXMLTree_attrExt*)mCurExt)->attributeCount); - } - return 0; -} - -int32_t ResXMLParser::getAttributeNamespaceID(size_t idx) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - return dtohl(attr->ns.index); - } - } - return -2; -} - -const uint16_t* ResXMLParser::getAttributeNamespace(size_t idx, size_t* outLen) const -{ - int32_t id = getAttributeNamespaceID(idx); - //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode); - //XML_NOISY(printf("getAttributeNamespace 0x%x=0x%x\n", idx, id)); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -const char* ResXMLParser::getAttributeNamespace8(size_t idx, size_t* outLen) const -{ - int32_t id = getAttributeNamespaceID(idx); - //printf("attribute namespace=%d idx=%d event=%p\n", id, idx, mEventCode); - //XML_NOISY(printf("getAttributeNamespace 0x%x=0x%x\n", idx, id)); - return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL; -} - -int32_t ResXMLParser::getAttributeNameID(size_t idx) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - return dtohl(attr->name.index); - } - } - return -1; -} - -const uint16_t* ResXMLParser::getAttributeName(size_t idx, size_t* outLen) const -{ - int32_t id = getAttributeNameID(idx); - //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode); - //XML_NOISY(printf("getAttributeName 0x%x=0x%x\n", idx, id)); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -const char* ResXMLParser::getAttributeName8(size_t idx, size_t* outLen) const -{ - int32_t id = getAttributeNameID(idx); - //printf("attribute name=%d idx=%d event=%p\n", id, idx, mEventCode); - //XML_NOISY(printf("getAttributeName 0x%x=0x%x\n", idx, id)); - return id >= 0 ? mTree.mStrings.string8At(id, outLen) : NULL; -} - -uint32_t ResXMLParser::getAttributeNameResID(size_t idx) const -{ - int32_t id = getAttributeNameID(idx); - if (id >= 0 && (size_t)id < mTree.mNumResIds) { - return dtohl(mTree.mResIds[id]); - } - return 0; -} - -int32_t ResXMLParser::getAttributeValueStringID(size_t idx) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - return dtohl(attr->rawValue.index); - } - } - return -1; -} - -const uint16_t* ResXMLParser::getAttributeStringValue(size_t idx, size_t* outLen) const -{ - int32_t id = getAttributeValueStringID(idx); - //XML_NOISY(printf("getAttributeValue 0x%x=0x%x\n", idx, id)); - return id >= 0 ? mTree.mStrings.stringAt(id, outLen) : NULL; -} - -int32_t ResXMLParser::getAttributeDataType(size_t idx) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - return attr->typedValue.dataType; - } - } - return Res_value::TYPE_NULL; -} - -int32_t ResXMLParser::getAttributeData(size_t idx) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - return dtohl(attr->typedValue.data); - } - } - return 0; -} - -ssize_t ResXMLParser::getAttributeValue(size_t idx, Res_value* outValue) const -{ - if (mEventCode == START_TAG) { - const ResXMLTree_attrExt* tag = (const ResXMLTree_attrExt*)mCurExt; - if (idx < dtohs(tag->attributeCount)) { - const ResXMLTree_attribute* attr = (const ResXMLTree_attribute*) - (((const uint8_t*)tag) - + dtohs(tag->attributeStart) - + (dtohs(tag->attributeSize)*idx)); - outValue->copyFrom_dtoh(attr->typedValue); - return sizeof(Res_value); - } - } - return BAD_TYPE; -} - -ssize_t ResXMLParser::indexOfAttribute(const char* ns, const char* attr) const -{ - String16 nsStr(ns != NULL ? ns : ""); - String16 attrStr(attr); - return indexOfAttribute(ns ? nsStr.string() : NULL, ns ? nsStr.size() : 0, - attrStr.string(), attrStr.size()); -} - -ssize_t ResXMLParser::indexOfAttribute(const char16_t* ns, size_t nsLen, - const char16_t* attr, size_t attrLen) const -{ - if (mEventCode == START_TAG) { - if (attr == NULL) { - return NAME_NOT_FOUND; - } - const size_t N = getAttributeCount(); - if (mTree.mStrings.isUTF8()) { - String8 ns8, attr8; - if (ns != NULL) { - ns8 = String8(ns, nsLen); - } - attr8 = String8(attr, attrLen); - STRING_POOL_NOISY(ALOGI("indexOfAttribute UTF8 %s (%d) / %s (%d)", ns8.string(), nsLen, - attr8.string(), attrLen)); - for (size_t i=0; i<N; i++) { - size_t curNsLen = 0, curAttrLen = 0; - const char* curNs = getAttributeNamespace8(i, &curNsLen); - const char* curAttr = getAttributeName8(i, &curAttrLen); - STRING_POOL_NOISY(ALOGI(" curNs=%s (%d), curAttr=%s (%d)", curNs, curNsLen, - curAttr, curAttrLen)); - if (curAttr != NULL && curNsLen == nsLen && curAttrLen == attrLen - && memcmp(attr8.string(), curAttr, attrLen) == 0) { - if (ns == NULL) { - if (curNs == NULL) { - STRING_POOL_NOISY(ALOGI(" FOUND!")); - return i; - } - } else if (curNs != NULL) { - //printf(" --> ns=%s, curNs=%s\n", - // String8(ns).string(), String8(curNs).string()); - if (memcmp(ns8.string(), curNs, nsLen) == 0) { - STRING_POOL_NOISY(ALOGI(" FOUND!")); - return i; - } - } - } - } - } else { - STRING_POOL_NOISY(ALOGI("indexOfAttribute UTF16 %s (%d) / %s (%d)", - String8(ns, nsLen).string(), nsLen, - String8(attr, attrLen).string(), attrLen)); - for (size_t i=0; i<N; i++) { - size_t curNsLen = 0, curAttrLen = 0; - const char16_t* curNs = getAttributeNamespace(i, &curNsLen); - const char16_t* curAttr = getAttributeName(i, &curAttrLen); - STRING_POOL_NOISY(ALOGI(" curNs=%s (%d), curAttr=%s (%d)", - String8(curNs, curNsLen).string(), curNsLen, - String8(curAttr, curAttrLen).string(), curAttrLen)); - if (curAttr != NULL && curNsLen == nsLen && curAttrLen == attrLen - && (memcmp(attr, curAttr, attrLen*sizeof(char16_t)) == 0)) { - if (ns == NULL) { - if (curNs == NULL) { - STRING_POOL_NOISY(ALOGI(" FOUND!")); - return i; - } - } else if (curNs != NULL) { - //printf(" --> ns=%s, curNs=%s\n", - // String8(ns).string(), String8(curNs).string()); - if (memcmp(ns, curNs, nsLen*sizeof(char16_t)) == 0) { - STRING_POOL_NOISY(ALOGI(" FOUND!")); - return i; - } - } - } - } - } - } - - return NAME_NOT_FOUND; -} - -ssize_t ResXMLParser::indexOfID() const -{ - if (mEventCode == START_TAG) { - const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->idIndex); - if (idx > 0) return (idx-1); - } - return NAME_NOT_FOUND; -} - -ssize_t ResXMLParser::indexOfClass() const -{ - if (mEventCode == START_TAG) { - const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->classIndex); - if (idx > 0) return (idx-1); - } - return NAME_NOT_FOUND; -} - -ssize_t ResXMLParser::indexOfStyle() const -{ - if (mEventCode == START_TAG) { - const ssize_t idx = dtohs(((const ResXMLTree_attrExt*)mCurExt)->styleIndex); - if (idx > 0) return (idx-1); - } - return NAME_NOT_FOUND; -} - -ResXMLParser::event_code_t ResXMLParser::nextNode() -{ - if (mEventCode < 0) { - return mEventCode; - } - - do { - const ResXMLTree_node* next = (const ResXMLTree_node*) - (((const uint8_t*)mCurNode) + dtohl(mCurNode->header.size)); - //ALOGW("Next node: prev=%p, next=%p\n", mCurNode, next); - - if (((const uint8_t*)next) >= mTree.mDataEnd) { - mCurNode = NULL; - return (mEventCode=END_DOCUMENT); - } - - if (mTree.validateNode(next) != NO_ERROR) { - mCurNode = NULL; - return (mEventCode=BAD_DOCUMENT); - } - - mCurNode = next; - const uint16_t headerSize = dtohs(next->header.headerSize); - const uint32_t totalSize = dtohl(next->header.size); - mCurExt = ((const uint8_t*)next) + headerSize; - size_t minExtSize = 0; - event_code_t eventCode = (event_code_t)dtohs(next->header.type); - switch ((mEventCode=eventCode)) { - case RES_XML_START_NAMESPACE_TYPE: - case RES_XML_END_NAMESPACE_TYPE: - minExtSize = sizeof(ResXMLTree_namespaceExt); - break; - case RES_XML_START_ELEMENT_TYPE: - minExtSize = sizeof(ResXMLTree_attrExt); - break; - case RES_XML_END_ELEMENT_TYPE: - minExtSize = sizeof(ResXMLTree_endElementExt); - break; - case RES_XML_CDATA_TYPE: - minExtSize = sizeof(ResXMLTree_cdataExt); - break; - default: - ALOGW("Unknown XML block: header type %d in node at %d\n", - (int)dtohs(next->header.type), - (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader))); - continue; - } - - if ((totalSize-headerSize) < minExtSize) { - ALOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n", - (int)dtohs(next->header.type), - (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)), - (int)(totalSize-headerSize), (int)minExtSize); - return (mEventCode=BAD_DOCUMENT); - } - - //printf("CurNode=%p, CurExt=%p, headerSize=%d, minExtSize=%d\n", - // mCurNode, mCurExt, headerSize, minExtSize); - - return eventCode; - } while (true); -} - -void ResXMLParser::getPosition(ResXMLParser::ResXMLPosition* pos) const -{ - pos->eventCode = mEventCode; - pos->curNode = mCurNode; - pos->curExt = mCurExt; -} - -void ResXMLParser::setPosition(const ResXMLParser::ResXMLPosition& pos) -{ - mEventCode = pos.eventCode; - mCurNode = pos.curNode; - mCurExt = pos.curExt; -} - - -// -------------------------------------------------------------------- - -static volatile int32_t gCount = 0; - -ResXMLTree::ResXMLTree() - : ResXMLParser(*this) - , mError(NO_INIT), mOwnedData(NULL) -{ - //ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); - restart(); -} - -ResXMLTree::ResXMLTree(const void* data, size_t size, bool copyData) - : ResXMLParser(*this) - , mError(NO_INIT), mOwnedData(NULL) -{ - //ALOGI("Creating ResXMLTree %p #%d\n", this, android_atomic_inc(&gCount)+1); - setTo(data, size, copyData); -} - -ResXMLTree::~ResXMLTree() -{ - //ALOGI("Destroying ResXMLTree in %p #%d\n", this, android_atomic_dec(&gCount)-1); - uninit(); -} - -status_t ResXMLTree::setTo(const void* data, size_t size, bool copyData) -{ - uninit(); - mEventCode = START_DOCUMENT; - - if (!data || !size) { - return (mError=BAD_TYPE); - } - - if (copyData) { - mOwnedData = malloc(size); - if (mOwnedData == NULL) { - return (mError=NO_MEMORY); - } - memcpy(mOwnedData, data, size); - data = mOwnedData; - } - - mHeader = (const ResXMLTree_header*)data; - mSize = dtohl(mHeader->header.size); - if (dtohs(mHeader->header.headerSize) > mSize || mSize > size) { - ALOGW("Bad XML block: header size %d or total size %d is larger than data size %d\n", - (int)dtohs(mHeader->header.headerSize), - (int)dtohl(mHeader->header.size), (int)size); - mError = BAD_TYPE; - restart(); - return mError; - } - mDataEnd = ((const uint8_t*)mHeader) + mSize; - - mStrings.uninit(); - mRootNode = NULL; - mResIds = NULL; - mNumResIds = 0; - - // First look for a couple interesting chunks: the string block - // and first XML node. - const ResChunk_header* chunk = - (const ResChunk_header*)(((const uint8_t*)mHeader) + dtohs(mHeader->header.headerSize)); - const ResChunk_header* lastChunk = chunk; - while (((const uint8_t*)chunk) < (mDataEnd-sizeof(ResChunk_header)) && - ((const uint8_t*)chunk) < (mDataEnd-dtohl(chunk->size))) { - status_t err = validate_chunk(chunk, sizeof(ResChunk_header), mDataEnd, "XML"); - if (err != NO_ERROR) { - mError = err; - goto done; - } - const uint16_t type = dtohs(chunk->type); - const size_t size = dtohl(chunk->size); - XML_NOISY(printf("Scanning @ %p: type=0x%x, size=0x%x\n", - (void*)(((uint32_t)chunk)-((uint32_t)mHeader)), type, size)); - if (type == RES_STRING_POOL_TYPE) { - mStrings.setTo(chunk, size); - } else if (type == RES_XML_RESOURCE_MAP_TYPE) { - mResIds = (const uint32_t*) - (((const uint8_t*)chunk)+dtohs(chunk->headerSize)); - mNumResIds = (dtohl(chunk->size)-dtohs(chunk->headerSize))/sizeof(uint32_t); - } else if (type >= RES_XML_FIRST_CHUNK_TYPE - && type <= RES_XML_LAST_CHUNK_TYPE) { - if (validateNode((const ResXMLTree_node*)chunk) != NO_ERROR) { - mError = BAD_TYPE; - goto done; - } - mCurNode = (const ResXMLTree_node*)lastChunk; - if (nextNode() == BAD_DOCUMENT) { - mError = BAD_TYPE; - goto done; - } - mRootNode = mCurNode; - mRootExt = mCurExt; - mRootCode = mEventCode; - break; - } else { - XML_NOISY(printf("Skipping unknown chunk!\n")); - } - lastChunk = chunk; - chunk = (const ResChunk_header*) - (((const uint8_t*)chunk) + size); - } - - if (mRootNode == NULL) { - ALOGW("Bad XML block: no root element node found\n"); - mError = BAD_TYPE; - goto done; - } - - mError = mStrings.getError(); - -done: - restart(); - return mError; -} - -status_t ResXMLTree::getError() const -{ - return mError; -} - -void ResXMLTree::uninit() -{ - mError = NO_INIT; - mStrings.uninit(); - if (mOwnedData) { - free(mOwnedData); - mOwnedData = NULL; - } - restart(); -} - -status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const -{ - const uint16_t eventCode = dtohs(node->header.type); - - status_t err = validate_chunk( - &node->header, sizeof(ResXMLTree_node), - mDataEnd, "ResXMLTree_node"); - - if (err >= NO_ERROR) { - // Only perform additional validation on START nodes - if (eventCode != RES_XML_START_ELEMENT_TYPE) { - return NO_ERROR; - } - - const uint16_t headerSize = dtohs(node->header.headerSize); - const uint32_t size = dtohl(node->header.size); - const ResXMLTree_attrExt* attrExt = (const ResXMLTree_attrExt*) - (((const uint8_t*)node) + headerSize); - // check for sensical values pulled out of the stream so far... - if ((size >= headerSize + sizeof(ResXMLTree_attrExt)) - && ((void*)attrExt > (void*)node)) { - const size_t attrSize = ((size_t)dtohs(attrExt->attributeSize)) - * dtohs(attrExt->attributeCount); - if ((dtohs(attrExt->attributeStart)+attrSize) <= (size-headerSize)) { - return NO_ERROR; - } - ALOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", - (unsigned int)(dtohs(attrExt->attributeStart)+attrSize), - (unsigned int)(size-headerSize)); - } - else { - ALOGW("Bad XML start block: node header size 0x%x, size 0x%x\n", - (unsigned int)headerSize, (unsigned int)size); - } - return BAD_TYPE; - } - - return err; - -#if 0 - const bool isStart = dtohs(node->header.type) == RES_XML_START_ELEMENT_TYPE; - - const uint16_t headerSize = dtohs(node->header.headerSize); - const uint32_t size = dtohl(node->header.size); - - if (headerSize >= (isStart ? sizeof(ResXMLTree_attrNode) : sizeof(ResXMLTree_node))) { - if (size >= headerSize) { - if (((const uint8_t*)node) <= (mDataEnd-size)) { - if (!isStart) { - return NO_ERROR; - } - if ((((size_t)dtohs(node->attributeSize))*dtohs(node->attributeCount)) - <= (size-headerSize)) { - return NO_ERROR; - } - ALOGW("Bad XML block: node attributes use 0x%x bytes, only have 0x%x bytes\n", - ((int)dtohs(node->attributeSize))*dtohs(node->attributeCount), - (int)(size-headerSize)); - return BAD_TYPE; - } - ALOGW("Bad XML block: node at 0x%x extends beyond data end 0x%x\n", - (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), (int)mSize); - return BAD_TYPE; - } - ALOGW("Bad XML block: node at 0x%x header size 0x%x smaller than total size 0x%x\n", - (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), - (int)headerSize, (int)size); - return BAD_TYPE; - } - ALOGW("Bad XML block: node at 0x%x header size 0x%x too small\n", - (int)(((const uint8_t*)node)-((const uint8_t*)mHeader)), - (int)headerSize); - return BAD_TYPE; -#endif -} - -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- - -void ResTable_config::copyFromDeviceNoSwap(const ResTable_config& o) { - const size_t size = dtohl(o.size); - if (size >= sizeof(ResTable_config)) { - *this = o; - } else { - memcpy(this, &o, size); - memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size); - } -} - -void ResTable_config::copyFromDtoH(const ResTable_config& o) { - copyFromDeviceNoSwap(o); - size = sizeof(ResTable_config); - mcc = dtohs(mcc); - mnc = dtohs(mnc); - density = dtohs(density); - screenWidth = dtohs(screenWidth); - screenHeight = dtohs(screenHeight); - sdkVersion = dtohs(sdkVersion); - minorVersion = dtohs(minorVersion); - smallestScreenWidthDp = dtohs(smallestScreenWidthDp); - screenWidthDp = dtohs(screenWidthDp); - screenHeightDp = dtohs(screenHeightDp); -} - -void ResTable_config::swapHtoD() { - size = htodl(size); - mcc = htods(mcc); - mnc = htods(mnc); - density = htods(density); - screenWidth = htods(screenWidth); - screenHeight = htods(screenHeight); - sdkVersion = htods(sdkVersion); - minorVersion = htods(minorVersion); - smallestScreenWidthDp = htods(smallestScreenWidthDp); - screenWidthDp = htods(screenWidthDp); - screenHeightDp = htods(screenHeightDp); -} - -int ResTable_config::compare(const ResTable_config& o) const { - int32_t diff = (int32_t)(imsi - o.imsi); - if (diff != 0) return diff; - diff = (int32_t)(locale - o.locale); - if (diff != 0) return diff; - diff = (int32_t)(screenType - o.screenType); - if (diff != 0) return diff; - diff = (int32_t)(input - o.input); - if (diff != 0) return diff; - diff = (int32_t)(screenSize - o.screenSize); - if (diff != 0) return diff; - diff = (int32_t)(version - o.version); - if (diff != 0) return diff; - diff = (int32_t)(screenLayout - o.screenLayout); - if (diff != 0) return diff; - diff = (int32_t)(uiMode - o.uiMode); - if (diff != 0) return diff; - diff = (int32_t)(smallestScreenWidthDp - o.smallestScreenWidthDp); - if (diff != 0) return diff; - diff = (int32_t)(screenSizeDp - o.screenSizeDp); - return (int)diff; -} - -int ResTable_config::compareLogical(const ResTable_config& o) const { - if (mcc != o.mcc) { - return mcc < o.mcc ? -1 : 1; - } - if (mnc != o.mnc) { - return mnc < o.mnc ? -1 : 1; - } - if (language[0] != o.language[0]) { - return language[0] < o.language[0] ? -1 : 1; - } - if (language[1] != o.language[1]) { - return language[1] < o.language[1] ? -1 : 1; - } - if (country[0] != o.country[0]) { - return country[0] < o.country[0] ? -1 : 1; - } - if (country[1] != o.country[1]) { - return country[1] < o.country[1] ? -1 : 1; - } - if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) { - return (screenLayout & MASK_LAYOUTDIR) < (o.screenLayout & MASK_LAYOUTDIR) ? -1 : 1; - } - if (smallestScreenWidthDp != o.smallestScreenWidthDp) { - return smallestScreenWidthDp < o.smallestScreenWidthDp ? -1 : 1; - } - if (screenWidthDp != o.screenWidthDp) { - return screenWidthDp < o.screenWidthDp ? -1 : 1; - } - if (screenHeightDp != o.screenHeightDp) { - return screenHeightDp < o.screenHeightDp ? -1 : 1; - } - if (screenWidth != o.screenWidth) { - return screenWidth < o.screenWidth ? -1 : 1; - } - if (screenHeight != o.screenHeight) { - return screenHeight < o.screenHeight ? -1 : 1; - } - if (density != o.density) { - return density < o.density ? -1 : 1; - } - if (orientation != o.orientation) { - return orientation < o.orientation ? -1 : 1; - } - if (touchscreen != o.touchscreen) { - return touchscreen < o.touchscreen ? -1 : 1; - } - if (input != o.input) { - return input < o.input ? -1 : 1; - } - if (screenLayout != o.screenLayout) { - return screenLayout < o.screenLayout ? -1 : 1; - } - if (uiMode != o.uiMode) { - return uiMode < o.uiMode ? -1 : 1; - } - if (version != o.version) { - return version < o.version ? -1 : 1; - } - return 0; -} - -int ResTable_config::diff(const ResTable_config& o) const { - int diffs = 0; - if (mcc != o.mcc) diffs |= CONFIG_MCC; - if (mnc != o.mnc) diffs |= CONFIG_MNC; - if (locale != o.locale) diffs |= CONFIG_LOCALE; - if (orientation != o.orientation) diffs |= CONFIG_ORIENTATION; - if (density != o.density) diffs |= CONFIG_DENSITY; - if (touchscreen != o.touchscreen) diffs |= CONFIG_TOUCHSCREEN; - if (((inputFlags^o.inputFlags)&(MASK_KEYSHIDDEN|MASK_NAVHIDDEN)) != 0) - diffs |= CONFIG_KEYBOARD_HIDDEN; - if (keyboard != o.keyboard) diffs |= CONFIG_KEYBOARD; - if (navigation != o.navigation) diffs |= CONFIG_NAVIGATION; - if (screenSize != o.screenSize) diffs |= CONFIG_SCREEN_SIZE; - if (version != o.version) diffs |= CONFIG_VERSION; - if ((screenLayout & MASK_LAYOUTDIR) != (o.screenLayout & MASK_LAYOUTDIR)) diffs |= CONFIG_LAYOUTDIR; - if ((screenLayout & ~MASK_LAYOUTDIR) != (o.screenLayout & ~MASK_LAYOUTDIR)) diffs |= CONFIG_SCREEN_LAYOUT; - if (uiMode != o.uiMode) diffs |= CONFIG_UI_MODE; - if (smallestScreenWidthDp != o.smallestScreenWidthDp) diffs |= CONFIG_SMALLEST_SCREEN_SIZE; - if (screenSizeDp != o.screenSizeDp) diffs |= CONFIG_SCREEN_SIZE; - return diffs; -} - -bool ResTable_config::isMoreSpecificThan(const ResTable_config& o) const { - // The order of the following tests defines the importance of one - // configuration parameter over another. Those tests first are more - // important, trumping any values in those following them. - if (imsi || o.imsi) { - if (mcc != o.mcc) { - if (!mcc) return false; - if (!o.mcc) return true; - } - - if (mnc != o.mnc) { - if (!mnc) return false; - if (!o.mnc) return true; - } - } - - if (locale || o.locale) { - if (language[0] != o.language[0]) { - if (!language[0]) return false; - if (!o.language[0]) return true; - } - - if (country[0] != o.country[0]) { - if (!country[0]) return false; - if (!o.country[0]) return true; - } - } - - if (screenLayout || o.screenLayout) { - if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0) { - if (!(screenLayout & MASK_LAYOUTDIR)) return false; - if (!(o.screenLayout & MASK_LAYOUTDIR)) return true; - } - } - - if (smallestScreenWidthDp || o.smallestScreenWidthDp) { - if (smallestScreenWidthDp != o.smallestScreenWidthDp) { - if (!smallestScreenWidthDp) return false; - if (!o.smallestScreenWidthDp) return true; - } - } - - if (screenSizeDp || o.screenSizeDp) { - if (screenWidthDp != o.screenWidthDp) { - if (!screenWidthDp) return false; - if (!o.screenWidthDp) return true; - } - - if (screenHeightDp != o.screenHeightDp) { - if (!screenHeightDp) return false; - if (!o.screenHeightDp) return true; - } - } - - if (screenLayout || o.screenLayout) { - if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0) { - if (!(screenLayout & MASK_SCREENSIZE)) return false; - if (!(o.screenLayout & MASK_SCREENSIZE)) return true; - } - if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0) { - if (!(screenLayout & MASK_SCREENLONG)) return false; - if (!(o.screenLayout & MASK_SCREENLONG)) return true; - } - } - - if (orientation != o.orientation) { - if (!orientation) return false; - if (!o.orientation) return true; - } - - if (uiMode || o.uiMode) { - if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0) { - if (!(uiMode & MASK_UI_MODE_TYPE)) return false; - if (!(o.uiMode & MASK_UI_MODE_TYPE)) return true; - } - if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0) { - if (!(uiMode & MASK_UI_MODE_NIGHT)) return false; - if (!(o.uiMode & MASK_UI_MODE_NIGHT)) return true; - } - } - - // density is never 'more specific' - // as the default just equals 160 - - if (touchscreen != o.touchscreen) { - if (!touchscreen) return false; - if (!o.touchscreen) return true; - } - - if (input || o.input) { - if (((inputFlags^o.inputFlags) & MASK_KEYSHIDDEN) != 0) { - if (!(inputFlags & MASK_KEYSHIDDEN)) return false; - if (!(o.inputFlags & MASK_KEYSHIDDEN)) return true; - } - - if (((inputFlags^o.inputFlags) & MASK_NAVHIDDEN) != 0) { - if (!(inputFlags & MASK_NAVHIDDEN)) return false; - if (!(o.inputFlags & MASK_NAVHIDDEN)) return true; - } - - if (keyboard != o.keyboard) { - if (!keyboard) return false; - if (!o.keyboard) return true; - } - - if (navigation != o.navigation) { - if (!navigation) return false; - if (!o.navigation) return true; - } - } - - if (screenSize || o.screenSize) { - if (screenWidth != o.screenWidth) { - if (!screenWidth) return false; - if (!o.screenWidth) return true; - } - - if (screenHeight != o.screenHeight) { - if (!screenHeight) return false; - if (!o.screenHeight) return true; - } - } - - if (version || o.version) { - if (sdkVersion != o.sdkVersion) { - if (!sdkVersion) return false; - if (!o.sdkVersion) return true; - } - - if (minorVersion != o.minorVersion) { - if (!minorVersion) return false; - if (!o.minorVersion) return true; - } - } - return false; -} - -bool ResTable_config::isBetterThan(const ResTable_config& o, - const ResTable_config* requested) const { - if (requested) { - if (imsi || o.imsi) { - if ((mcc != o.mcc) && requested->mcc) { - return (mcc); - } - - if ((mnc != o.mnc) && requested->mnc) { - return (mnc); - } - } - - if (locale || o.locale) { - if ((language[0] != o.language[0]) && requested->language[0]) { - return (language[0]); - } - - if ((country[0] != o.country[0]) && requested->country[0]) { - return (country[0]); - } - } - - if (screenLayout || o.screenLayout) { - if (((screenLayout^o.screenLayout) & MASK_LAYOUTDIR) != 0 - && (requested->screenLayout & MASK_LAYOUTDIR)) { - int myLayoutDir = screenLayout & MASK_LAYOUTDIR; - int oLayoutDir = o.screenLayout & MASK_LAYOUTDIR; - return (myLayoutDir > oLayoutDir); - } - } - - if (smallestScreenWidthDp || o.smallestScreenWidthDp) { - // The configuration closest to the actual size is best. - // We assume that larger configs have already been filtered - // out at this point. That means we just want the largest one. - if (smallestScreenWidthDp != o.smallestScreenWidthDp) { - return smallestScreenWidthDp > o.smallestScreenWidthDp; - } - } - - if (screenSizeDp || o.screenSizeDp) { - // "Better" is based on the sum of the difference between both - // width and height from the requested dimensions. We are - // assuming the invalid configs (with smaller dimens) have - // already been filtered. Note that if a particular dimension - // is unspecified, we will end up with a large value (the - // difference between 0 and the requested dimension), which is - // good since we will prefer a config that has specified a - // dimension value. - int myDelta = 0, otherDelta = 0; - if (requested->screenWidthDp) { - myDelta += requested->screenWidthDp - screenWidthDp; - otherDelta += requested->screenWidthDp - o.screenWidthDp; - } - if (requested->screenHeightDp) { - myDelta += requested->screenHeightDp - screenHeightDp; - otherDelta += requested->screenHeightDp - o.screenHeightDp; - } - //ALOGI("Comparing this %dx%d to other %dx%d in %dx%d: myDelta=%d otherDelta=%d", - // screenWidthDp, screenHeightDp, o.screenWidthDp, o.screenHeightDp, - // requested->screenWidthDp, requested->screenHeightDp, myDelta, otherDelta); - if (myDelta != otherDelta) { - return myDelta < otherDelta; - } - } - - if (screenLayout || o.screenLayout) { - if (((screenLayout^o.screenLayout) & MASK_SCREENSIZE) != 0 - && (requested->screenLayout & MASK_SCREENSIZE)) { - // A little backwards compatibility here: undefined is - // considered equivalent to normal. But only if the - // requested size is at least normal; otherwise, small - // is better than the default. - int mySL = (screenLayout & MASK_SCREENSIZE); - int oSL = (o.screenLayout & MASK_SCREENSIZE); - int fixedMySL = mySL; - int fixedOSL = oSL; - if ((requested->screenLayout & MASK_SCREENSIZE) >= SCREENSIZE_NORMAL) { - if (fixedMySL == 0) fixedMySL = SCREENSIZE_NORMAL; - if (fixedOSL == 0) fixedOSL = SCREENSIZE_NORMAL; - } - // For screen size, the best match is the one that is - // closest to the requested screen size, but not over - // (the not over part is dealt with in match() below). - if (fixedMySL == fixedOSL) { - // If the two are the same, but 'this' is actually - // undefined, then the other is really a better match. - if (mySL == 0) return false; - return true; - } - if (fixedMySL != fixedOSL) { - return fixedMySL > fixedOSL; - } - } - if (((screenLayout^o.screenLayout) & MASK_SCREENLONG) != 0 - && (requested->screenLayout & MASK_SCREENLONG)) { - return (screenLayout & MASK_SCREENLONG); - } - } - - if ((orientation != o.orientation) && requested->orientation) { - return (orientation); - } - - if (uiMode || o.uiMode) { - if (((uiMode^o.uiMode) & MASK_UI_MODE_TYPE) != 0 - && (requested->uiMode & MASK_UI_MODE_TYPE)) { - return (uiMode & MASK_UI_MODE_TYPE); - } - if (((uiMode^o.uiMode) & MASK_UI_MODE_NIGHT) != 0 - && (requested->uiMode & MASK_UI_MODE_NIGHT)) { - return (uiMode & MASK_UI_MODE_NIGHT); - } - } - - if (screenType || o.screenType) { - if (density != o.density) { - // density is tough. Any density is potentially useful - // because the system will scale it. Scaling down - // is generally better than scaling up. - // Default density counts as 160dpi (the system default) - // TODO - remove 160 constants - int h = (density?density:160); - int l = (o.density?o.density:160); - bool bImBigger = true; - if (l > h) { - int t = h; - h = l; - l = t; - bImBigger = false; - } - - int reqValue = (requested->density?requested->density:160); - if (reqValue >= h) { - // requested value higher than both l and h, give h - return bImBigger; - } - if (l >= reqValue) { - // requested value lower than both l and h, give l - return !bImBigger; - } - // saying that scaling down is 2x better than up - if (((2 * l) - reqValue) * h > reqValue * reqValue) { - return !bImBigger; - } else { - return bImBigger; - } - } - - if ((touchscreen != o.touchscreen) && requested->touchscreen) { - return (touchscreen); - } - } - - if (input || o.input) { - const int keysHidden = inputFlags & MASK_KEYSHIDDEN; - const int oKeysHidden = o.inputFlags & MASK_KEYSHIDDEN; - if (keysHidden != oKeysHidden) { - const int reqKeysHidden = - requested->inputFlags & MASK_KEYSHIDDEN; - if (reqKeysHidden) { - - if (!keysHidden) return false; - if (!oKeysHidden) return true; - // For compatibility, we count KEYSHIDDEN_NO as being - // the same as KEYSHIDDEN_SOFT. Here we disambiguate - // these by making an exact match more specific. - if (reqKeysHidden == keysHidden) return true; - if (reqKeysHidden == oKeysHidden) return false; - } - } - - const int navHidden = inputFlags & MASK_NAVHIDDEN; - const int oNavHidden = o.inputFlags & MASK_NAVHIDDEN; - if (navHidden != oNavHidden) { - const int reqNavHidden = - requested->inputFlags & MASK_NAVHIDDEN; - if (reqNavHidden) { - - if (!navHidden) return false; - if (!oNavHidden) return true; - } - } - - if ((keyboard != o.keyboard) && requested->keyboard) { - return (keyboard); - } - - if ((navigation != o.navigation) && requested->navigation) { - return (navigation); - } - } - - if (screenSize || o.screenSize) { - // "Better" is based on the sum of the difference between both - // width and height from the requested dimensions. We are - // assuming the invalid configs (with smaller sizes) have - // already been filtered. Note that if a particular dimension - // is unspecified, we will end up with a large value (the - // difference between 0 and the requested dimension), which is - // good since we will prefer a config that has specified a - // size value. - int myDelta = 0, otherDelta = 0; - if (requested->screenWidth) { - myDelta += requested->screenWidth - screenWidth; - otherDelta += requested->screenWidth - o.screenWidth; - } - if (requested->screenHeight) { - myDelta += requested->screenHeight - screenHeight; - otherDelta += requested->screenHeight - o.screenHeight; - } - if (myDelta != otherDelta) { - return myDelta < otherDelta; - } - } - - if (version || o.version) { - if ((sdkVersion != o.sdkVersion) && requested->sdkVersion) { - return (sdkVersion > o.sdkVersion); - } - - if ((minorVersion != o.minorVersion) && - requested->minorVersion) { - return (minorVersion); - } - } - - return false; - } - return isMoreSpecificThan(o); -} - -bool ResTable_config::match(const ResTable_config& settings) const { - if (imsi != 0) { - if (mcc != 0 && mcc != settings.mcc) { - return false; - } - if (mnc != 0 && mnc != settings.mnc) { - return false; - } - } - if (locale != 0) { - if (language[0] != 0 - && (language[0] != settings.language[0] - || language[1] != settings.language[1])) { - return false; - } - if (country[0] != 0 - && (country[0] != settings.country[0] - || country[1] != settings.country[1])) { - return false; - } - } - if (screenConfig != 0) { - const int layoutDir = screenLayout&MASK_LAYOUTDIR; - const int setLayoutDir = settings.screenLayout&MASK_LAYOUTDIR; - if (layoutDir != 0 && layoutDir != setLayoutDir) { - return false; - } - - const int screenSize = screenLayout&MASK_SCREENSIZE; - const int setScreenSize = settings.screenLayout&MASK_SCREENSIZE; - // Any screen sizes for larger screens than the setting do not - // match. - if (screenSize != 0 && screenSize > setScreenSize) { - return false; - } - - const int screenLong = screenLayout&MASK_SCREENLONG; - const int setScreenLong = settings.screenLayout&MASK_SCREENLONG; - if (screenLong != 0 && screenLong != setScreenLong) { - return false; - } - - const int uiModeType = uiMode&MASK_UI_MODE_TYPE; - const int setUiModeType = settings.uiMode&MASK_UI_MODE_TYPE; - if (uiModeType != 0 && uiModeType != setUiModeType) { - return false; - } - - const int uiModeNight = uiMode&MASK_UI_MODE_NIGHT; - const int setUiModeNight = settings.uiMode&MASK_UI_MODE_NIGHT; - if (uiModeNight != 0 && uiModeNight != setUiModeNight) { - return false; - } - - if (smallestScreenWidthDp != 0 - && smallestScreenWidthDp > settings.smallestScreenWidthDp) { - return false; - } - } - if (screenSizeDp != 0) { - if (screenWidthDp != 0 && screenWidthDp > settings.screenWidthDp) { - //ALOGI("Filtering out width %d in requested %d", screenWidthDp, settings.screenWidthDp); - return false; - } - if (screenHeightDp != 0 && screenHeightDp > settings.screenHeightDp) { - //ALOGI("Filtering out height %d in requested %d", screenHeightDp, settings.screenHeightDp); - return false; - } - } - if (screenType != 0) { - if (orientation != 0 && orientation != settings.orientation) { - return false; - } - // density always matches - we can scale it. See isBetterThan - if (touchscreen != 0 && touchscreen != settings.touchscreen) { - return false; - } - } - if (input != 0) { - const int keysHidden = inputFlags&MASK_KEYSHIDDEN; - const int setKeysHidden = settings.inputFlags&MASK_KEYSHIDDEN; - if (keysHidden != 0 && keysHidden != setKeysHidden) { - // For compatibility, we count a request for KEYSHIDDEN_NO as also - // matching the more recent KEYSHIDDEN_SOFT. Basically - // KEYSHIDDEN_NO means there is some kind of keyboard available. - //ALOGI("Matching keysHidden: have=%d, config=%d\n", keysHidden, setKeysHidden); - if (keysHidden != KEYSHIDDEN_NO || setKeysHidden != KEYSHIDDEN_SOFT) { - //ALOGI("No match!"); - return false; - } - } - const int navHidden = inputFlags&MASK_NAVHIDDEN; - const int setNavHidden = settings.inputFlags&MASK_NAVHIDDEN; - if (navHidden != 0 && navHidden != setNavHidden) { - return false; - } - if (keyboard != 0 && keyboard != settings.keyboard) { - return false; - } - if (navigation != 0 && navigation != settings.navigation) { - return false; - } - } - if (screenSize != 0) { - if (screenWidth != 0 && screenWidth > settings.screenWidth) { - return false; - } - if (screenHeight != 0 && screenHeight > settings.screenHeight) { - return false; - } - } - if (version != 0) { - if (sdkVersion != 0 && sdkVersion > settings.sdkVersion) { - return false; - } - if (minorVersion != 0 && minorVersion != settings.minorVersion) { - return false; - } - } - return true; -} - -void ResTable_config::getLocale(char str[6]) const { - memset(str, 0, 6); - if (language[0]) { - str[0] = language[0]; - str[1] = language[1]; - if (country[0]) { - str[2] = '_'; - str[3] = country[0]; - str[4] = country[1]; - } - } -} - -String8 ResTable_config::toString() const { - String8 res; - - if (mcc != 0) { - if (res.size() > 0) res.append("-"); - res.appendFormat("%dmcc", dtohs(mcc)); - } - if (mnc != 0) { - if (res.size() > 0) res.append("-"); - res.appendFormat("%dmnc", dtohs(mnc)); - } - if (language[0] != 0) { - if (res.size() > 0) res.append("-"); - res.append(language, 2); - } - if (country[0] != 0) { - if (res.size() > 0) res.append("-"); - res.append(country, 2); - } - if ((screenLayout&MASK_LAYOUTDIR) != 0) { - if (res.size() > 0) res.append("-"); - switch (screenLayout&ResTable_config::MASK_LAYOUTDIR) { - case ResTable_config::LAYOUTDIR_LTR: - res.append("ldltr"); - break; - case ResTable_config::LAYOUTDIR_RTL: - res.append("ldrtl"); - break; - default: - res.appendFormat("layoutDir=%d", - dtohs(screenLayout&ResTable_config::MASK_LAYOUTDIR)); - break; - } - } - if (smallestScreenWidthDp != 0) { - if (res.size() > 0) res.append("-"); - res.appendFormat("sw%ddp", dtohs(smallestScreenWidthDp)); - } - if (screenWidthDp != 0) { - if (res.size() > 0) res.append("-"); - res.appendFormat("w%ddp", dtohs(screenWidthDp)); - } - if (screenHeightDp != 0) { - if (res.size() > 0) res.append("-"); - res.appendFormat("h%ddp", dtohs(screenHeightDp)); - } - if ((screenLayout&MASK_SCREENSIZE) != SCREENSIZE_ANY) { - if (res.size() > 0) res.append("-"); - switch (screenLayout&ResTable_config::MASK_SCREENSIZE) { - case ResTable_config::SCREENSIZE_SMALL: - res.append("small"); - break; - case ResTable_config::SCREENSIZE_NORMAL: - res.append("normal"); - break; - case ResTable_config::SCREENSIZE_LARGE: - res.append("large"); - break; - case ResTable_config::SCREENSIZE_XLARGE: - res.append("xlarge"); - break; - default: - res.appendFormat("screenLayoutSize=%d", - dtohs(screenLayout&ResTable_config::MASK_SCREENSIZE)); - break; - } - } - if ((screenLayout&MASK_SCREENLONG) != 0) { - if (res.size() > 0) res.append("-"); - switch (screenLayout&ResTable_config::MASK_SCREENLONG) { - case ResTable_config::SCREENLONG_NO: - res.append("notlong"); - break; - case ResTable_config::SCREENLONG_YES: - res.append("long"); - break; - default: - res.appendFormat("screenLayoutLong=%d", - dtohs(screenLayout&ResTable_config::MASK_SCREENLONG)); - break; - } - } - if (orientation != ORIENTATION_ANY) { - if (res.size() > 0) res.append("-"); - switch (orientation) { - case ResTable_config::ORIENTATION_PORT: - res.append("port"); - break; - case ResTable_config::ORIENTATION_LAND: - res.append("land"); - break; - case ResTable_config::ORIENTATION_SQUARE: - res.append("square"); - break; - default: - res.appendFormat("orientation=%d", dtohs(orientation)); - break; - } - } - if ((uiMode&MASK_UI_MODE_TYPE) != UI_MODE_TYPE_ANY) { - if (res.size() > 0) res.append("-"); - switch (uiMode&ResTable_config::MASK_UI_MODE_TYPE) { - case ResTable_config::UI_MODE_TYPE_DESK: - res.append("desk"); - break; - case ResTable_config::UI_MODE_TYPE_CAR: - res.append("car"); - break; - case ResTable_config::UI_MODE_TYPE_TELEVISION: - res.append("television"); - break; - case ResTable_config::UI_MODE_TYPE_APPLIANCE: - res.append("appliance"); - break; - default: - res.appendFormat("uiModeType=%d", - dtohs(screenLayout&ResTable_config::MASK_UI_MODE_TYPE)); - break; - } - } - if ((uiMode&MASK_UI_MODE_NIGHT) != 0) { - if (res.size() > 0) res.append("-"); - switch (uiMode&ResTable_config::MASK_UI_MODE_NIGHT) { - case ResTable_config::UI_MODE_NIGHT_NO: - res.append("notnight"); - break; - case ResTable_config::UI_MODE_NIGHT_YES: - res.append("night"); - break; - default: - res.appendFormat("uiModeNight=%d", - dtohs(uiMode&MASK_UI_MODE_NIGHT)); - break; - } - } - if (density != DENSITY_DEFAULT) { - if (res.size() > 0) res.append("-"); - switch (density) { - case ResTable_config::DENSITY_LOW: - res.append("ldpi"); - break; - case ResTable_config::DENSITY_MEDIUM: - res.append("mdpi"); - break; - case ResTable_config::DENSITY_TV: - res.append("tvdpi"); - break; - case ResTable_config::DENSITY_HIGH: - res.append("hdpi"); - break; - case ResTable_config::DENSITY_XHIGH: - res.append("xhdpi"); - break; - case ResTable_config::DENSITY_XXHIGH: - res.append("xxhdpi"); - break; - case ResTable_config::DENSITY_NONE: - res.append("nodpi"); - break; - default: - res.appendFormat("%ddpi", dtohs(density)); - break; - } - } - if (touchscreen != TOUCHSCREEN_ANY) { - if (res.size() > 0) res.append("-"); - switch (touchscreen) { - case ResTable_config::TOUCHSCREEN_NOTOUCH: - res.append("notouch"); - break; - case ResTable_config::TOUCHSCREEN_FINGER: - res.append("finger"); - break; - case ResTable_config::TOUCHSCREEN_STYLUS: - res.append("stylus"); - break; - default: - res.appendFormat("touchscreen=%d", dtohs(touchscreen)); - break; - } - } - if (keyboard != KEYBOARD_ANY) { - if (res.size() > 0) res.append("-"); - switch (keyboard) { - case ResTable_config::KEYBOARD_NOKEYS: - res.append("nokeys"); - break; - case ResTable_config::KEYBOARD_QWERTY: - res.append("qwerty"); - break; - case ResTable_config::KEYBOARD_12KEY: - res.append("12key"); - break; - default: - res.appendFormat("keyboard=%d", dtohs(keyboard)); - break; - } - } - if ((inputFlags&MASK_KEYSHIDDEN) != 0) { - if (res.size() > 0) res.append("-"); - switch (inputFlags&MASK_KEYSHIDDEN) { - case ResTable_config::KEYSHIDDEN_NO: - res.append("keysexposed"); - break; - case ResTable_config::KEYSHIDDEN_YES: - res.append("keyshidden"); - break; - case ResTable_config::KEYSHIDDEN_SOFT: - res.append("keyssoft"); - break; - } - } - if (navigation != NAVIGATION_ANY) { - if (res.size() > 0) res.append("-"); - switch (navigation) { - case ResTable_config::NAVIGATION_NONAV: - res.append("nonav"); - break; - case ResTable_config::NAVIGATION_DPAD: - res.append("dpad"); - break; - case ResTable_config::NAVIGATION_TRACKBALL: - res.append("trackball"); - break; - case ResTable_config::NAVIGATION_WHEEL: - res.append("wheel"); - break; - default: - res.appendFormat("navigation=%d", dtohs(navigation)); - break; - } - } - if ((inputFlags&MASK_NAVHIDDEN) != 0) { - if (res.size() > 0) res.append("-"); - switch (inputFlags&MASK_NAVHIDDEN) { - case ResTable_config::NAVHIDDEN_NO: - res.append("navsexposed"); - break; - case ResTable_config::NAVHIDDEN_YES: - res.append("navhidden"); - break; - default: - res.appendFormat("inputFlagsNavHidden=%d", - dtohs(inputFlags&MASK_NAVHIDDEN)); - break; - } - } - if (screenSize != 0) { - if (res.size() > 0) res.append("-"); - res.appendFormat("%dx%d", dtohs(screenWidth), dtohs(screenHeight)); - } - if (version != 0) { - if (res.size() > 0) res.append("-"); - res.appendFormat("v%d", dtohs(sdkVersion)); - if (minorVersion != 0) { - res.appendFormat(".%d", dtohs(minorVersion)); - } - } - - return res; -} - -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- -// -------------------------------------------------------------------- - -struct ResTable::Header -{ - Header(ResTable* _owner) : owner(_owner), ownedData(NULL), header(NULL), - resourceIDMap(NULL), resourceIDMapSize(0) { } - - ~Header() - { - free(resourceIDMap); - } - - ResTable* const owner; - void* ownedData; - const ResTable_header* header; - size_t size; - const uint8_t* dataEnd; - size_t index; - void* cookie; - - ResStringPool values; - uint32_t* resourceIDMap; - size_t resourceIDMapSize; -}; - -struct ResTable::Type -{ - Type(const Header* _header, const Package* _package, size_t count) - : header(_header), package(_package), entryCount(count), - typeSpec(NULL), typeSpecFlags(NULL) { } - const Header* const header; - const Package* const package; - const size_t entryCount; - const ResTable_typeSpec* typeSpec; - const uint32_t* typeSpecFlags; - Vector<const ResTable_type*> configs; -}; - -struct ResTable::Package -{ - Package(ResTable* _owner, const Header* _header, const ResTable_package* _package) - : owner(_owner), header(_header), package(_package) { } - ~Package() - { - size_t i = types.size(); - while (i > 0) { - i--; - delete types[i]; - } - } - - ResTable* const owner; - const Header* const header; - const ResTable_package* const package; - Vector<Type*> types; - - ResStringPool typeStrings; - ResStringPool keyStrings; - - const Type* getType(size_t idx) const { - return idx < types.size() ? types[idx] : NULL; - } -}; - -// A group of objects describing a particular resource package. -// The first in 'package' is always the root object (from the resource -// table that defined the package); the ones after are skins on top of it. -struct ResTable::PackageGroup -{ - PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id) - : owner(_owner), name(_name), id(_id), typeCount(0), bags(NULL) { } - ~PackageGroup() { - clearBagCache(); - const size_t N = packages.size(); - for (size_t i=0; i<N; i++) { - Package* pkg = packages[i]; - if (pkg->owner == owner) { - delete pkg; - } - } - } - - void clearBagCache() { - if (bags) { - TABLE_NOISY(printf("bags=%p\n", bags)); - Package* pkg = packages[0]; - TABLE_NOISY(printf("typeCount=%x\n", typeCount)); - for (size_t i=0; i<typeCount; i++) { - TABLE_NOISY(printf("type=%d\n", i)); - const Type* type = pkg->getType(i); - if (type != NULL) { - bag_set** typeBags = bags[i]; - TABLE_NOISY(printf("typeBags=%p\n", typeBags)); - if (typeBags) { - TABLE_NOISY(printf("type->entryCount=%x\n", type->entryCount)); - const size_t N = type->entryCount; - for (size_t j=0; j<N; j++) { - if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF) - free(typeBags[j]); - } - free(typeBags); - } - } - } - free(bags); - bags = NULL; - } - } - - ResTable* const owner; - String16 const name; - uint32_t const id; - Vector<Package*> packages; - - // This is for finding typeStrings and other common package stuff. - Package* basePackage; - - // For quick access. - size_t typeCount; - - // Computed attribute bags, first indexed by the type and second - // by the entry in that type. - bag_set*** bags; -}; - -struct ResTable::bag_set -{ - size_t numAttrs; // number in array - size_t availAttrs; // total space in array - uint32_t typeSpecFlags; - // Followed by 'numAttr' bag_entry structures. -}; - -ResTable::Theme::Theme(const ResTable& table) - : mTable(table) -{ - memset(mPackages, 0, sizeof(mPackages)); -} - -ResTable::Theme::~Theme() -{ - for (size_t i=0; i<Res_MAXPACKAGE; i++) { - package_info* pi = mPackages[i]; - if (pi != NULL) { - free_package(pi); - } - } -} - -void ResTable::Theme::free_package(package_info* pi) -{ - for (size_t j=0; j<pi->numTypes; j++) { - theme_entry* te = pi->types[j].entries; - if (te != NULL) { - free(te); - } - } - free(pi); -} - -ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi) -{ - package_info* newpi = (package_info*)malloc( - sizeof(package_info) + (pi->numTypes*sizeof(type_info))); - newpi->numTypes = pi->numTypes; - for (size_t j=0; j<newpi->numTypes; j++) { - size_t cnt = pi->types[j].numEntries; - newpi->types[j].numEntries = cnt; - theme_entry* te = pi->types[j].entries; - if (te != NULL) { - theme_entry* newte = (theme_entry*)malloc(cnt*sizeof(theme_entry)); - newpi->types[j].entries = newte; - memcpy(newte, te, cnt*sizeof(theme_entry)); - } else { - newpi->types[j].entries = NULL; - } - } - return newpi; -} - -status_t ResTable::Theme::applyStyle(uint32_t resID, bool force) -{ - const bag_entry* bag; - uint32_t bagTypeSpecFlags = 0; - mTable.lock(); - const ssize_t N = mTable.getBagLocked(resID, &bag, &bagTypeSpecFlags); - TABLE_NOISY(ALOGV("Applying style 0x%08x to theme %p, count=%d", resID, this, N)); - if (N < 0) { - mTable.unlock(); - return N; - } - - uint32_t curPackage = 0xffffffff; - ssize_t curPackageIndex = 0; - package_info* curPI = NULL; - uint32_t curType = 0xffffffff; - size_t numEntries = 0; - theme_entry* curEntries = NULL; - - const bag_entry* end = bag + N; - while (bag < end) { - const uint32_t attrRes = bag->map.name.ident; - const uint32_t p = Res_GETPACKAGE(attrRes); - const uint32_t t = Res_GETTYPE(attrRes); - const uint32_t e = Res_GETENTRY(attrRes); - - if (curPackage != p) { - const ssize_t pidx = mTable.getResourcePackageIndex(attrRes); - if (pidx < 0) { - ALOGE("Style contains key with bad package: 0x%08x\n", attrRes); - bag++; - continue; - } - curPackage = p; - curPackageIndex = pidx; - curPI = mPackages[pidx]; - if (curPI == NULL) { - PackageGroup* const grp = mTable.mPackageGroups[pidx]; - int cnt = grp->typeCount; - curPI = (package_info*)malloc( - sizeof(package_info) + (cnt*sizeof(type_info))); - curPI->numTypes = cnt; - memset(curPI->types, 0, cnt*sizeof(type_info)); - mPackages[pidx] = curPI; - } - curType = 0xffffffff; - } - if (curType != t) { - if (t >= curPI->numTypes) { - ALOGE("Style contains key with bad type: 0x%08x\n", attrRes); - bag++; - continue; - } - curType = t; - curEntries = curPI->types[t].entries; - if (curEntries == NULL) { - PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex]; - const Type* type = grp->packages[0]->getType(t); - int cnt = type != NULL ? type->entryCount : 0; - curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry)); - memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry)); - curPI->types[t].numEntries = cnt; - curPI->types[t].entries = curEntries; - } - numEntries = curPI->types[t].numEntries; - } - if (e >= numEntries) { - ALOGE("Style contains key with bad entry: 0x%08x\n", attrRes); - bag++; - continue; - } - theme_entry* curEntry = curEntries + e; - TABLE_NOISY(ALOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x", - attrRes, bag->map.value.dataType, bag->map.value.data, - curEntry->value.dataType)); - if (force || curEntry->value.dataType == Res_value::TYPE_NULL) { - curEntry->stringBlock = bag->stringBlock; - curEntry->typeSpecFlags |= bagTypeSpecFlags; - curEntry->value = bag->map.value; - } - - bag++; - } - - mTable.unlock(); - - //ALOGI("Applying style 0x%08x (force=%d) theme %p...\n", resID, force, this); - //dumpToLog(); - - return NO_ERROR; -} - -status_t ResTable::Theme::setTo(const Theme& other) -{ - //ALOGI("Setting theme %p from theme %p...\n", this, &other); - //dumpToLog(); - //other.dumpToLog(); - - if (&mTable == &other.mTable) { - for (size_t i=0; i<Res_MAXPACKAGE; i++) { - if (mPackages[i] != NULL) { - free_package(mPackages[i]); - } - if (other.mPackages[i] != NULL) { - mPackages[i] = copy_package(other.mPackages[i]); - } else { - mPackages[i] = NULL; - } - } - } else { - // @todo: need to really implement this, not just copy - // the system package (which is still wrong because it isn't - // fixing up resource references). - for (size_t i=0; i<Res_MAXPACKAGE; i++) { - if (mPackages[i] != NULL) { - free_package(mPackages[i]); - } - if (i == 0 && other.mPackages[i] != NULL) { - mPackages[i] = copy_package(other.mPackages[i]); - } else { - mPackages[i] = NULL; - } - } - } - - //ALOGI("Final theme:"); - //dumpToLog(); - - return NO_ERROR; -} - -ssize_t ResTable::Theme::getAttribute(uint32_t resID, Res_value* outValue, - uint32_t* outTypeSpecFlags) const -{ - int cnt = 20; - - if (outTypeSpecFlags != NULL) *outTypeSpecFlags = 0; - - do { - const ssize_t p = mTable.getResourcePackageIndex(resID); - const uint32_t t = Res_GETTYPE(resID); - const uint32_t e = Res_GETENTRY(resID); - - TABLE_THEME(ALOGI("Looking up attr 0x%08x in theme %p", resID, this)); - - if (p >= 0) { - const package_info* const pi = mPackages[p]; - TABLE_THEME(ALOGI("Found package: %p", pi)); - if (pi != NULL) { - TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, pi->numTypes)); - if (t < pi->numTypes) { - const type_info& ti = pi->types[t]; - TABLE_THEME(ALOGI("Desired entry index is %ld in avail %d", e, ti.numEntries)); - if (e < ti.numEntries) { - const theme_entry& te = ti.entries[e]; - if (outTypeSpecFlags != NULL) { - *outTypeSpecFlags |= te.typeSpecFlags; - } - TABLE_THEME(ALOGI("Theme value: type=0x%x, data=0x%08x", - te.value.dataType, te.value.data)); - const uint8_t type = te.value.dataType; - if (type == Res_value::TYPE_ATTRIBUTE) { - if (cnt > 0) { - cnt--; - resID = te.value.data; - continue; - } - ALOGW("Too many attribute references, stopped at: 0x%08x\n", resID); - return BAD_INDEX; - } else if (type != Res_value::TYPE_NULL) { - *outValue = te.value; - return te.stringBlock; - } - return BAD_INDEX; - } - } - } - } - break; - - } while (true); - - return BAD_INDEX; -} - -ssize_t ResTable::Theme::resolveAttributeReference(Res_value* inOutValue, - ssize_t blockIndex, uint32_t* outLastRef, - uint32_t* inoutTypeSpecFlags, ResTable_config* inoutConfig) const -{ - //printf("Resolving type=0x%x\n", inOutValue->dataType); - if (inOutValue->dataType == Res_value::TYPE_ATTRIBUTE) { - uint32_t newTypeSpecFlags; - blockIndex = getAttribute(inOutValue->data, inOutValue, &newTypeSpecFlags); - TABLE_THEME(ALOGI("Resolving attr reference: blockIndex=%d, type=0x%x, data=%p\n", - (int)blockIndex, (int)inOutValue->dataType, (void*)inOutValue->data)); - if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newTypeSpecFlags; - //printf("Retrieved attribute new type=0x%x\n", inOutValue->dataType); - if (blockIndex < 0) { - return blockIndex; - } - } - return mTable.resolveReference(inOutValue, blockIndex, outLastRef, - inoutTypeSpecFlags, inoutConfig); -} - -void ResTable::Theme::dumpToLog() const -{ - ALOGI("Theme %p:\n", this); - for (size_t i=0; i<Res_MAXPACKAGE; i++) { - package_info* pi = mPackages[i]; - if (pi == NULL) continue; - - ALOGI(" Package #0x%02x:\n", (int)(i+1)); - for (size_t j=0; j<pi->numTypes; j++) { - type_info& ti = pi->types[j]; - if (ti.numEntries == 0) continue; - - ALOGI(" Type #0x%02x:\n", (int)(j+1)); - for (size_t k=0; k<ti.numEntries; k++) { - theme_entry& te = ti.entries[k]; - if (te.value.dataType == Res_value::TYPE_NULL) continue; - ALOGI(" 0x%08x: t=0x%x, d=0x%08x (block=%d)\n", - (int)Res_MAKEID(i, j, k), - te.value.dataType, (int)te.value.data, (int)te.stringBlock); - } - } - } -} - -ResTable::ResTable() - : mError(NO_INIT) -{ - memset(&mParams, 0, sizeof(mParams)); - memset(mPackageMap, 0, sizeof(mPackageMap)); - //ALOGI("Creating ResTable %p\n", this); -} - -ResTable::ResTable(const void* data, size_t size, void* cookie, bool copyData) - : mError(NO_INIT) -{ - memset(&mParams, 0, sizeof(mParams)); - memset(mPackageMap, 0, sizeof(mPackageMap)); - add(data, size, cookie, copyData); - LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table"); - //ALOGI("Creating ResTable %p\n", this); -} - -ResTable::~ResTable() -{ - //ALOGI("Destroying ResTable in %p\n", this); - uninit(); -} - -inline ssize_t ResTable::getResourcePackageIndex(uint32_t resID) const -{ - return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1; -} - -status_t ResTable::add(const void* data, size_t size, void* cookie, bool copyData, - const void* idmap) -{ - return add(data, size, cookie, NULL, copyData, reinterpret_cast<const Asset*>(idmap)); -} - -status_t ResTable::add(Asset* asset, void* cookie, bool copyData, const void* idmap) -{ - const void* data = asset->getBuffer(true); - if (data == NULL) { - ALOGW("Unable to get buffer of resource asset file"); - return UNKNOWN_ERROR; - } - size_t size = (size_t)asset->getLength(); - return add(data, size, cookie, asset, copyData, reinterpret_cast<const Asset*>(idmap)); -} - -status_t ResTable::add(ResTable* src) -{ - mError = src->mError; - - for (size_t i=0; i<src->mHeaders.size(); i++) { - mHeaders.add(src->mHeaders[i]); - } - - for (size_t i=0; i<src->mPackageGroups.size(); i++) { - PackageGroup* srcPg = src->mPackageGroups[i]; - PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id); - for (size_t j=0; j<srcPg->packages.size(); j++) { - pg->packages.add(srcPg->packages[j]); - } - pg->basePackage = srcPg->basePackage; - pg->typeCount = srcPg->typeCount; - mPackageGroups.add(pg); - } - - memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap)); - - return mError; -} - -status_t ResTable::add(const void* data, size_t size, void* cookie, - Asset* asset, bool copyData, const Asset* idmap) -{ - if (!data) return NO_ERROR; - Header* header = new Header(this); - header->index = mHeaders.size(); - header->cookie = cookie; - if (idmap != NULL) { - const size_t idmap_size = idmap->getLength(); - const void* idmap_data = const_cast<Asset*>(idmap)->getBuffer(true); - header->resourceIDMap = (uint32_t*)malloc(idmap_size); - if (header->resourceIDMap == NULL) { - delete header; - return (mError = NO_MEMORY); - } - memcpy((void*)header->resourceIDMap, idmap_data, idmap_size); - header->resourceIDMapSize = idmap_size; - } - mHeaders.add(header); - - const bool notDeviceEndian = htods(0xf0) != 0xf0; - - LOAD_TABLE_NOISY( - ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%p, asset=%p, copy=%d " - "idmap=%p\n", data, size, cookie, asset, copyData, idmap)); - - if (copyData || notDeviceEndian) { - header->ownedData = malloc(size); - if (header->ownedData == NULL) { - return (mError=NO_MEMORY); - } - memcpy(header->ownedData, data, size); - data = header->ownedData; - } - - header->header = (const ResTable_header*)data; - header->size = dtohl(header->header->header.size); - //ALOGI("Got size 0x%x, again size 0x%x, raw size 0x%x\n", header->size, - // dtohl(header->header->header.size), header->header->header.size); - LOAD_TABLE_NOISY(ALOGV("Loading ResTable @%p:\n", header->header)); - LOAD_TABLE_NOISY(printHexData(2, header->header, header->size < 256 ? header->size : 256, - 16, 16, 0, false, printToLogFunc)); - if (dtohs(header->header->header.headerSize) > header->size - || header->size > size) { - ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n", - (int)dtohs(header->header->header.headerSize), - (int)header->size, (int)size); - return (mError=BAD_TYPE); - } - if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) { - ALOGW("Bad resource table: header size 0x%x or total size 0x%x is not on an integer boundary\n", - (int)dtohs(header->header->header.headerSize), - (int)header->size); - return (mError=BAD_TYPE); - } - header->dataEnd = ((const uint8_t*)header->header) + header->size; - - // Iterate through all chunks. - size_t curPackage = 0; - - const ResChunk_header* chunk = - (const ResChunk_header*)(((const uint8_t*)header->header) - + dtohs(header->header->header.headerSize)); - while (((const uint8_t*)chunk) <= (header->dataEnd-sizeof(ResChunk_header)) && - ((const uint8_t*)chunk) <= (header->dataEnd-dtohl(chunk->size))) { - status_t err = validate_chunk(chunk, sizeof(ResChunk_header), header->dataEnd, "ResTable"); - if (err != NO_ERROR) { - return (mError=err); - } - TABLE_NOISY(ALOGV("Chunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", - dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), - (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)))); - const size_t csize = dtohl(chunk->size); - const uint16_t ctype = dtohs(chunk->type); - if (ctype == RES_STRING_POOL_TYPE) { - if (header->values.getError() != NO_ERROR) { - // Only use the first string chunk; ignore any others that - // may appear. - status_t err = header->values.setTo(chunk, csize); - if (err != NO_ERROR) { - return (mError=err); - } - } else { - ALOGW("Multiple string chunks found in resource table."); - } - } else if (ctype == RES_TABLE_PACKAGE_TYPE) { - if (curPackage >= dtohl(header->header->packageCount)) { - ALOGW("More package chunks were found than the %d declared in the header.", - dtohl(header->header->packageCount)); - return (mError=BAD_TYPE); - } - uint32_t idmap_id = 0; - if (idmap != NULL) { - uint32_t tmp; - if (getIdmapPackageId(header->resourceIDMap, - header->resourceIDMapSize, - &tmp) == NO_ERROR) { - idmap_id = tmp; - } - } - if (parsePackage((ResTable_package*)chunk, header, idmap_id) != NO_ERROR) { - return mError; - } - curPackage++; - } else { - ALOGW("Unknown chunk type %p in table at %p.\n", - (void*)(int)(ctype), - (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header))); - } - chunk = (const ResChunk_header*) - (((const uint8_t*)chunk) + csize); - } - - if (curPackage < dtohl(header->header->packageCount)) { - ALOGW("Fewer package chunks (%d) were found than the %d declared in the header.", - (int)curPackage, dtohl(header->header->packageCount)); - return (mError=BAD_TYPE); - } - mError = header->values.getError(); - if (mError != NO_ERROR) { - ALOGW("No string values found in resource table!"); - } - - TABLE_NOISY(ALOGV("Returning from add with mError=%d\n", mError)); - return mError; -} - -status_t ResTable::getError() const -{ - return mError; -} - -void ResTable::uninit() -{ - mError = NO_INIT; - size_t N = mPackageGroups.size(); - for (size_t i=0; i<N; i++) { - PackageGroup* g = mPackageGroups[i]; - delete g; - } - N = mHeaders.size(); - for (size_t i=0; i<N; i++) { - Header* header = mHeaders[i]; - if (header->owner == this) { - if (header->ownedData) { - free(header->ownedData); - } - delete header; - } - } - - mPackageGroups.clear(); - mHeaders.clear(); -} - -bool ResTable::getResourceName(uint32_t resID, bool allowUtf8, resource_name* outName) const -{ - if (mError != NO_ERROR) { - return false; - } - - const ssize_t p = getResourcePackageIndex(resID); - const int t = Res_GETTYPE(resID); - const int e = Res_GETENTRY(resID); - - if (p < 0) { - if (Res_GETPACKAGE(resID)+1 == 0) { - ALOGW("No package identifier when getting name for resource number 0x%08x", resID); - } else { - ALOGW("No known package when getting name for resource number 0x%08x", resID); - } - return false; - } - if (t < 0) { - ALOGW("No type identifier when getting name for resource number 0x%08x", resID); - return false; - } - - const PackageGroup* const grp = mPackageGroups[p]; - if (grp == NULL) { - ALOGW("Bad identifier when getting name for resource number 0x%08x", resID); - return false; - } - if (grp->packages.size() > 0) { - const Package* const package = grp->packages[0]; - - const ResTable_type* type; - const ResTable_entry* entry; - ssize_t offset = getEntry(package, t, e, NULL, &type, &entry, NULL); - if (offset <= 0) { - return false; - } - - outName->package = grp->name.string(); - outName->packageLen = grp->name.size(); - if (allowUtf8) { - outName->type8 = grp->basePackage->typeStrings.string8At(t, &outName->typeLen); - outName->name8 = grp->basePackage->keyStrings.string8At( - dtohl(entry->key.index), &outName->nameLen); - } else { - outName->type8 = NULL; - outName->name8 = NULL; - } - if (outName->type8 == NULL) { - outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen); - // If we have a bad index for some reason, we should abort. - if (outName->type == NULL) { - return false; - } - } - if (outName->name8 == NULL) { - outName->name = grp->basePackage->keyStrings.stringAt( - dtohl(entry->key.index), &outName->nameLen); - // If we have a bad index for some reason, we should abort. - if (outName->name == NULL) { - return false; - } - } - - return true; - } - - return false; -} - -ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density, - uint32_t* outSpecFlags, ResTable_config* outConfig) const -{ - if (mError != NO_ERROR) { - return mError; - } - - const ssize_t p = getResourcePackageIndex(resID); - const int t = Res_GETTYPE(resID); - const int e = Res_GETENTRY(resID); - - if (p < 0) { - if (Res_GETPACKAGE(resID)+1 == 0) { - ALOGW("No package identifier when getting value for resource number 0x%08x", resID); - } else { - ALOGW("No known package when getting value for resource number 0x%08x", resID); - } - return BAD_INDEX; - } - if (t < 0) { - ALOGW("No type identifier when getting value for resource number 0x%08x", resID); - return BAD_INDEX; - } - - const Res_value* bestValue = NULL; - const Package* bestPackage = NULL; - ResTable_config bestItem; - memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up - - if (outSpecFlags != NULL) *outSpecFlags = 0; - - // Look through all resource packages, starting with the most - // recently added. - const PackageGroup* const grp = mPackageGroups[p]; - if (grp == NULL) { - ALOGW("Bad identifier when getting value for resource number 0x%08x", resID); - return BAD_INDEX; - } - - // Allow overriding density - const ResTable_config* desiredConfig = &mParams; - ResTable_config* overrideConfig = NULL; - if (density > 0) { - overrideConfig = (ResTable_config*) malloc(sizeof(ResTable_config)); - if (overrideConfig == NULL) { - ALOGE("Couldn't malloc ResTable_config for overrides: %s", strerror(errno)); - return BAD_INDEX; - } - memcpy(overrideConfig, &mParams, sizeof(ResTable_config)); - overrideConfig->density = density; - desiredConfig = overrideConfig; - } - - ssize_t rc = BAD_VALUE; - size_t ip = grp->packages.size(); - while (ip > 0) { - ip--; - int T = t; - int E = e; - - const Package* const package = grp->packages[ip]; - if (package->header->resourceIDMap) { - uint32_t overlayResID = 0x0; - status_t retval = idmapLookup(package->header->resourceIDMap, - package->header->resourceIDMapSize, - resID, &overlayResID); - if (retval == NO_ERROR && overlayResID != 0x0) { - // for this loop iteration, this is the type and entry we really want - ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); - T = Res_GETTYPE(overlayResID); - E = Res_GETENTRY(overlayResID); - } else { - // resource not present in overlay package, continue with the next package - continue; - } - } - - const ResTable_type* type; - const ResTable_entry* entry; - const Type* typeClass; - ssize_t offset = getEntry(package, T, E, desiredConfig, &type, &entry, &typeClass); - if (offset <= 0) { - // No {entry, appropriate config} pair found in package. If this - // package is an overlay package (ip != 0), this simply means the - // overlay package did not specify a default. - // Non-overlay packages are still required to provide a default. - if (offset < 0 && ip == 0) { - ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n", - resID, T, E, ip, (int)offset); - rc = offset; - goto out; - } - continue; - } - - if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) { - if (!mayBeBag) { - ALOGW("Requesting resource %p failed because it is complex\n", - (void*)resID); - } - continue; - } - - TABLE_NOISY(aout << "Resource type data: " - << HexDump(type, dtohl(type->header.size)) << endl); - - if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) { - ALOGW("ResTable_item at %d is beyond type chunk data %d", - (int)offset, dtohl(type->header.size)); - rc = BAD_TYPE; - goto out; - } - - const Res_value* item = - (const Res_value*)(((const uint8_t*)type) + offset); - ResTable_config thisConfig; - thisConfig.copyFromDtoH(type->config); - - if (outSpecFlags != NULL) { - if (typeClass->typeSpecFlags != NULL) { - *outSpecFlags |= dtohl(typeClass->typeSpecFlags[E]); - } else { - *outSpecFlags = -1; - } - } - - if (bestPackage != NULL && - (bestItem.isMoreSpecificThan(thisConfig) || bestItem.diff(thisConfig) == 0)) { - // Discard thisConfig not only if bestItem is more specific, but also if the two configs - // are identical (diff == 0), or overlay packages will not take effect. - continue; - } - - bestItem = thisConfig; - bestValue = item; - bestPackage = package; - } - - TABLE_NOISY(printf("Found result: package %p\n", bestPackage)); - - if (bestValue) { - outValue->size = dtohs(bestValue->size); - outValue->res0 = bestValue->res0; - outValue->dataType = bestValue->dataType; - outValue->data = dtohl(bestValue->data); - if (outConfig != NULL) { - *outConfig = bestItem; - } - TABLE_NOISY(size_t len; - printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n", - bestPackage->header->index, - outValue->dataType, - outValue->dataType == bestValue->TYPE_STRING - ? String8(bestPackage->header->values.stringAt( - outValue->data, &len)).string() - : "", - outValue->data)); - rc = bestPackage->header->index; - goto out; - } - -out: - if (overrideConfig != NULL) { - free(overrideConfig); - } - - return rc; -} - -ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex, - uint32_t* outLastRef, uint32_t* inoutTypeSpecFlags, - ResTable_config* outConfig) const -{ - int count=0; - while (blockIndex >= 0 && value->dataType == value->TYPE_REFERENCE - && value->data != 0 && count < 20) { - if (outLastRef) *outLastRef = value->data; - uint32_t lastRef = value->data; - uint32_t newFlags = 0; - const ssize_t newIndex = getResource(value->data, value, true, 0, &newFlags, - outConfig); - if (newIndex == BAD_INDEX) { - return BAD_INDEX; - } - TABLE_THEME(ALOGI("Resolving reference %p: newIndex=%d, type=0x%x, data=%p\n", - (void*)lastRef, (int)newIndex, (int)value->dataType, (void*)value->data)); - //printf("Getting reference 0x%08x: newIndex=%d\n", value->data, newIndex); - if (inoutTypeSpecFlags != NULL) *inoutTypeSpecFlags |= newFlags; - if (newIndex < 0) { - // This can fail if the resource being referenced is a style... - // in this case, just return the reference, and expect the - // caller to deal with. - return blockIndex; - } - blockIndex = newIndex; - count++; - } - return blockIndex; -} - -const char16_t* ResTable::valueToString( - const Res_value* value, size_t stringBlock, - char16_t tmpBuffer[TMP_BUFFER_SIZE], size_t* outLen) -{ - if (!value) { - return NULL; - } - if (value->dataType == value->TYPE_STRING) { - return getTableStringBlock(stringBlock)->stringAt(value->data, outLen); - } - // XXX do int to string conversions. - return NULL; -} - -ssize_t ResTable::lockBag(uint32_t resID, const bag_entry** outBag) const -{ - mLock.lock(); - ssize_t err = getBagLocked(resID, outBag); - if (err < NO_ERROR) { - //printf("*** get failed! unlocking\n"); - mLock.unlock(); - } - return err; -} - -void ResTable::unlockBag(const bag_entry* bag) const -{ - //printf("<<< unlockBag %p\n", this); - mLock.unlock(); -} - -void ResTable::lock() const -{ - mLock.lock(); -} - -void ResTable::unlock() const -{ - mLock.unlock(); -} - -ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, - uint32_t* outTypeSpecFlags) const -{ - if (mError != NO_ERROR) { - return mError; - } - - const ssize_t p = getResourcePackageIndex(resID); - const int t = Res_GETTYPE(resID); - const int e = Res_GETENTRY(resID); - - if (p < 0) { - ALOGW("Invalid package identifier when getting bag for resource number 0x%08x", resID); - return BAD_INDEX; - } - if (t < 0) { - ALOGW("No type identifier when getting bag for resource number 0x%08x", resID); - return BAD_INDEX; - } - - //printf("Get bag: id=0x%08x, p=%d, t=%d\n", resID, p, t); - PackageGroup* const grp = mPackageGroups[p]; - if (grp == NULL) { - ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID); - return false; - } - - if (t >= (int)grp->typeCount) { - ALOGW("Type identifier 0x%x is larger than type count 0x%x", - t+1, (int)grp->typeCount); - return BAD_INDEX; - } - - const Package* const basePackage = grp->packages[0]; - - const Type* const typeConfigs = basePackage->getType(t); - - const size_t NENTRY = typeConfigs->entryCount; - if (e >= (int)NENTRY) { - ALOGW("Entry identifier 0x%x is larger than entry count 0x%x", - e, (int)typeConfigs->entryCount); - return BAD_INDEX; - } - - // First see if we've already computed this bag... - if (grp->bags) { - bag_set** typeSet = grp->bags[t]; - if (typeSet) { - bag_set* set = typeSet[e]; - if (set) { - if (set != (bag_set*)0xFFFFFFFF) { - if (outTypeSpecFlags != NULL) { - *outTypeSpecFlags = set->typeSpecFlags; - } - *outBag = (bag_entry*)(set+1); - //ALOGI("Found existing bag for: %p\n", (void*)resID); - return set->numAttrs; - } - ALOGW("Attempt to retrieve bag 0x%08x which is invalid or in a cycle.", - resID); - return BAD_INDEX; - } - } - } - - // Bag not found, we need to compute it! - if (!grp->bags) { - grp->bags = (bag_set***)calloc(grp->typeCount, sizeof(bag_set*)); - if (!grp->bags) return NO_MEMORY; - } - - bag_set** typeSet = grp->bags[t]; - if (!typeSet) { - typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*)); - if (!typeSet) return NO_MEMORY; - grp->bags[t] = typeSet; - } - - // Mark that we are currently working on this one. - typeSet[e] = (bag_set*)0xFFFFFFFF; - - // This is what we are building. - bag_set* set = NULL; - - TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID)); - - ResTable_config bestConfig; - memset(&bestConfig, 0, sizeof(bestConfig)); - - // Now collect all bag attributes from all packages. - size_t ip = grp->packages.size(); - while (ip > 0) { - ip--; - int T = t; - int E = e; - - const Package* const package = grp->packages[ip]; - if (package->header->resourceIDMap) { - uint32_t overlayResID = 0x0; - status_t retval = idmapLookup(package->header->resourceIDMap, - package->header->resourceIDMapSize, - resID, &overlayResID); - if (retval == NO_ERROR && overlayResID != 0x0) { - // for this loop iteration, this is the type and entry we really want - ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID); - T = Res_GETTYPE(overlayResID); - E = Res_GETENTRY(overlayResID); - } else { - // resource not present in overlay package, continue with the next package - continue; - } - } - - const ResTable_type* type; - const ResTable_entry* entry; - const Type* typeClass; - ALOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E); - ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass); - ALOGV("Resulting offset=%d\n", offset); - if (offset <= 0) { - // No {entry, appropriate config} pair found in package. If this - // package is an overlay package (ip != 0), this simply means the - // overlay package did not specify a default. - // Non-overlay packages are still required to provide a default. - if (offset < 0 && ip == 0) { - if (set) free(set); - return offset; - } - continue; - } - - if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) { - ALOGW("Skipping entry %p in package table %d because it is not complex!\n", - (void*)resID, (int)ip); - continue; - } - - if (set != NULL && !type->config.isBetterThan(bestConfig, NULL)) { - continue; - } - bestConfig = type->config; - if (set) { - free(set); - set = NULL; - } - - const uint16_t entrySize = dtohs(entry->size); - const uint32_t parent = entrySize >= sizeof(ResTable_map_entry) - ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0; - const uint32_t count = entrySize >= sizeof(ResTable_map_entry) - ? dtohl(((const ResTable_map_entry*)entry)->count) : 0; - - size_t N = count; - - TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n", - entrySize, parent, count)); - - // If this map inherits from another, we need to start - // with its parent's values. Otherwise start out empty. - TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n", - entrySize, parent)); - if (parent) { - const bag_entry* parentBag; - uint32_t parentTypeSpecFlags = 0; - const ssize_t NP = getBagLocked(parent, &parentBag, &parentTypeSpecFlags); - const size_t NT = ((NP >= 0) ? NP : 0) + N; - set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT); - if (set == NULL) { - return NO_MEMORY; - } - if (NP > 0) { - memcpy(set+1, parentBag, NP*sizeof(bag_entry)); - set->numAttrs = NP; - TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP)); - } else { - TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n")); - set->numAttrs = 0; - } - set->availAttrs = NT; - set->typeSpecFlags = parentTypeSpecFlags; - } else { - set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N); - if (set == NULL) { - return NO_MEMORY; - } - set->numAttrs = 0; - set->availAttrs = N; - set->typeSpecFlags = 0; - } - - if (typeClass->typeSpecFlags != NULL) { - set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[E]); - } else { - set->typeSpecFlags = -1; - } - - // Now merge in the new attributes... - ssize_t curOff = offset; - const ResTable_map* map; - bag_entry* entries = (bag_entry*)(set+1); - size_t curEntry = 0; - uint32_t pos = 0; - TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n", - set, entries, set->availAttrs)); - while (pos < count) { - TABLE_NOISY(printf("Now at %p\n", (void*)curOff)); - - if ((size_t)curOff > (dtohl(type->header.size)-sizeof(ResTable_map))) { - ALOGW("ResTable_map at %d is beyond type chunk data %d", - (int)curOff, dtohl(type->header.size)); - return BAD_TYPE; - } - map = (const ResTable_map*)(((const uint8_t*)type) + curOff); - N++; - - const uint32_t newName = htodl(map->name.ident); - bool isInside; - uint32_t oldName = 0; - while ((isInside=(curEntry < set->numAttrs)) - && (oldName=entries[curEntry].map.name.ident) < newName) { - TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n", - curEntry, entries[curEntry].map.name.ident)); - curEntry++; - } - - if ((!isInside) || oldName != newName) { - // This is a new attribute... figure out what to do with it. - if (set->numAttrs >= set->availAttrs) { - // Need to alloc more memory... - const size_t newAvail = set->availAttrs+N; - set = (bag_set*)realloc(set, - sizeof(bag_set) - + sizeof(bag_entry)*newAvail); - if (set == NULL) { - return NO_MEMORY; - } - set->availAttrs = newAvail; - entries = (bag_entry*)(set+1); - TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n", - set, entries, set->availAttrs)); - } - if (isInside) { - // Going in the middle, need to make space. - memmove(entries+curEntry+1, entries+curEntry, - sizeof(bag_entry)*(set->numAttrs-curEntry)); - set->numAttrs++; - } - TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n", - curEntry, newName)); - } else { - TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n", - curEntry, oldName)); - } - - bag_entry* cur = entries+curEntry; - - cur->stringBlock = package->header->index; - cur->map.name.ident = newName; - cur->map.value.copyFrom_dtoh(map->value); - TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n", - curEntry, cur, cur->stringBlock, cur->map.name.ident, - cur->map.value.dataType, cur->map.value.data)); - - // On to the next! - curEntry++; - pos++; - const size_t size = dtohs(map->value.size); - curOff += size + sizeof(*map)-sizeof(map->value); - }; - if (curEntry > set->numAttrs) { - set->numAttrs = curEntry; - } - } - - // And this is it... - typeSet[e] = set; - if (set) { - if (outTypeSpecFlags != NULL) { - *outTypeSpecFlags = set->typeSpecFlags; - } - *outBag = (bag_entry*)(set+1); - TABLE_NOISY(ALOGI("Returning %d attrs\n", set->numAttrs)); - return set->numAttrs; - } - return BAD_INDEX; -} - -void ResTable::setParameters(const ResTable_config* params) -{ - mLock.lock(); - TABLE_GETENTRY(ALOGI("Setting parameters: %s\n", params->toString().string())); - mParams = *params; - for (size_t i=0; i<mPackageGroups.size(); i++) { - TABLE_NOISY(ALOGI("CLEARING BAGS FOR GROUP %d!", i)); - mPackageGroups[i]->clearBagCache(); - } - mLock.unlock(); -} - -void ResTable::getParameters(ResTable_config* params) const -{ - mLock.lock(); - *params = mParams; - mLock.unlock(); -} - -struct id_name_map { - uint32_t id; - size_t len; - char16_t name[6]; -}; - -const static id_name_map ID_NAMES[] = { - { ResTable_map::ATTR_TYPE, 5, { '^', 't', 'y', 'p', 'e' } }, - { ResTable_map::ATTR_L10N, 5, { '^', 'l', '1', '0', 'n' } }, - { ResTable_map::ATTR_MIN, 4, { '^', 'm', 'i', 'n' } }, - { ResTable_map::ATTR_MAX, 4, { '^', 'm', 'a', 'x' } }, - { ResTable_map::ATTR_OTHER, 6, { '^', 'o', 't', 'h', 'e', 'r' } }, - { ResTable_map::ATTR_ZERO, 5, { '^', 'z', 'e', 'r', 'o' } }, - { ResTable_map::ATTR_ONE, 4, { '^', 'o', 'n', 'e' } }, - { ResTable_map::ATTR_TWO, 4, { '^', 't', 'w', 'o' } }, - { ResTable_map::ATTR_FEW, 4, { '^', 'f', 'e', 'w' } }, - { ResTable_map::ATTR_MANY, 5, { '^', 'm', 'a', 'n', 'y' } }, -}; - -uint32_t ResTable::identifierForName(const char16_t* name, size_t nameLen, - const char16_t* type, size_t typeLen, - const char16_t* package, - size_t packageLen, - uint32_t* outTypeSpecFlags) const -{ - TABLE_SUPER_NOISY(printf("Identifier for name: error=%d\n", mError)); - - // Check for internal resource identifier as the very first thing, so - // that we will always find them even when there are no resources. - if (name[0] == '^') { - const int N = (sizeof(ID_NAMES)/sizeof(ID_NAMES[0])); - size_t len; - for (int i=0; i<N; i++) { - const id_name_map* m = ID_NAMES + i; - len = m->len; - if (len != nameLen) { - continue; - } - for (size_t j=1; j<len; j++) { - if (m->name[j] != name[j]) { - goto nope; - } - } - if (outTypeSpecFlags) { - *outTypeSpecFlags = ResTable_typeSpec::SPEC_PUBLIC; - } - return m->id; -nope: - ; - } - if (nameLen > 7) { - if (name[1] == 'i' && name[2] == 'n' - && name[3] == 'd' && name[4] == 'e' && name[5] == 'x' - && name[6] == '_') { - int index = atoi(String8(name + 7, nameLen - 7).string()); - if (Res_CHECKID(index)) { - ALOGW("Array resource index: %d is too large.", - index); - return 0; - } - if (outTypeSpecFlags) { - *outTypeSpecFlags = ResTable_typeSpec::SPEC_PUBLIC; - } - return Res_MAKEARRAY(index); - } - } - return 0; - } - - if (mError != NO_ERROR) { - return 0; - } - - bool fakePublic = false; - - // Figure out the package and type we are looking in... - - const char16_t* packageEnd = NULL; - const char16_t* typeEnd = NULL; - const char16_t* const nameEnd = name+nameLen; - const char16_t* p = name; - while (p < nameEnd) { - if (*p == ':') packageEnd = p; - else if (*p == '/') typeEnd = p; - p++; - } - if (*name == '@') { - name++; - if (*name == '*') { - fakePublic = true; - name++; - } - } - if (name >= nameEnd) { - return 0; - } - - if (packageEnd) { - package = name; - packageLen = packageEnd-name; - name = packageEnd+1; - } else if (!package) { - return 0; - } - - if (typeEnd) { - type = name; - typeLen = typeEnd-name; - name = typeEnd+1; - } else if (!type) { - return 0; - } - - if (name >= nameEnd) { - return 0; - } - nameLen = nameEnd-name; - - TABLE_NOISY(printf("Looking for identifier: type=%s, name=%s, package=%s\n", - String8(type, typeLen).string(), - String8(name, nameLen).string(), - String8(package, packageLen).string())); - - const size_t NG = mPackageGroups.size(); - for (size_t ig=0; ig<NG; ig++) { - const PackageGroup* group = mPackageGroups[ig]; - - if (strzcmp16(package, packageLen, - group->name.string(), group->name.size())) { - TABLE_NOISY(printf("Skipping package group: %s\n", String8(group->name).string())); - continue; - } - - const ssize_t ti = group->basePackage->typeStrings.indexOfString(type, typeLen); - if (ti < 0) { - TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string())); - continue; - } - - const ssize_t ei = group->basePackage->keyStrings.indexOfString(name, nameLen); - if (ei < 0) { - TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string())); - continue; - } - - TABLE_NOISY(printf("Search indices: type=%d, name=%d\n", ti, ei)); - - const Type* const typeConfigs = group->packages[0]->getType(ti); - if (typeConfigs == NULL || typeConfigs->configs.size() <= 0) { - TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n", - String8(group->name).string(), ti)); - } - - size_t NTC = typeConfigs->configs.size(); - for (size_t tci=0; tci<NTC; tci++) { - const ResTable_type* const ty = typeConfigs->configs[tci]; - const uint32_t typeOffset = dtohl(ty->entriesStart); - - const uint8_t* const end = ((const uint8_t*)ty) + dtohl(ty->header.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)ty) + dtohs(ty->header.headerSize)); - - const size_t NE = dtohl(ty->entryCount); - for (size_t i=0; i<NE; i++) { - uint32_t offset = dtohl(eindex[i]); - if (offset == ResTable_type::NO_ENTRY) { - continue; - } - - offset += typeOffset; - - if (offset > (dtohl(ty->header.size)-sizeof(ResTable_entry))) { - ALOGW("ResTable_entry at %d is beyond type chunk data %d", - offset, dtohl(ty->header.size)); - return 0; - } - if ((offset&0x3) != 0) { - ALOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s", - (int)offset, (int)group->id, (int)ti+1, (int)i, - String8(package, packageLen).string(), - String8(type, typeLen).string(), - String8(name, nameLen).string()); - return 0; - } - - const ResTable_entry* const entry = (const ResTable_entry*) - (((const uint8_t*)ty) + offset); - if (dtohs(entry->size) < sizeof(*entry)) { - ALOGW("ResTable_entry size %d is too small", dtohs(entry->size)); - return BAD_TYPE; - } - - TABLE_SUPER_NOISY(printf("Looking at entry #%d: want str %d, have %d\n", - i, ei, dtohl(entry->key.index))); - if (dtohl(entry->key.index) == (size_t)ei) { - if (outTypeSpecFlags) { - *outTypeSpecFlags = typeConfigs->typeSpecFlags[i]; - if (fakePublic) { - *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC; - } - } - return Res_MAKEID(group->id-1, ti, i); - } - } - } - } - - return 0; -} - -bool ResTable::expandResourceRef(const uint16_t* refStr, size_t refLen, - String16* outPackage, - String16* outType, - String16* outName, - const String16* defType, - const String16* defPackage, - const char** outErrorMsg, - bool* outPublicOnly) -{ - const char16_t* packageEnd = NULL; - const char16_t* typeEnd = NULL; - const char16_t* p = refStr; - const char16_t* const end = p + refLen; - while (p < end) { - if (*p == ':') packageEnd = p; - else if (*p == '/') { - typeEnd = p; - break; - } - p++; - } - p = refStr; - if (*p == '@') p++; - - if (outPublicOnly != NULL) { - *outPublicOnly = true; - } - if (*p == '*') { - p++; - if (outPublicOnly != NULL) { - *outPublicOnly = false; - } - } - - if (packageEnd) { - *outPackage = String16(p, packageEnd-p); - p = packageEnd+1; - } else { - if (!defPackage) { - if (outErrorMsg) { - *outErrorMsg = "No resource package specified"; - } - return false; - } - *outPackage = *defPackage; - } - if (typeEnd) { - *outType = String16(p, typeEnd-p); - p = typeEnd+1; - } else { - if (!defType) { - if (outErrorMsg) { - *outErrorMsg = "No resource type specified"; - } - return false; - } - *outType = *defType; - } - *outName = String16(p, end-p); - if(**outPackage == 0) { - if(outErrorMsg) { - *outErrorMsg = "Resource package cannot be an empty string"; - } - return false; - } - if(**outType == 0) { - if(outErrorMsg) { - *outErrorMsg = "Resource type cannot be an empty string"; - } - return false; - } - if(**outName == 0) { - if(outErrorMsg) { - *outErrorMsg = "Resource id cannot be an empty string"; - } - return false; - } - return true; -} - -static uint32_t get_hex(char c, bool* outError) -{ - if (c >= '0' && c <= '9') { - return c - '0'; - } else if (c >= 'a' && c <= 'f') { - return c - 'a' + 0xa; - } else if (c >= 'A' && c <= 'F') { - return c - 'A' + 0xa; - } - *outError = true; - return 0; -} - -struct unit_entry -{ - const char* name; - size_t len; - uint8_t type; - uint32_t unit; - float scale; -}; - -static const unit_entry unitNames[] = { - { "px", strlen("px"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PX, 1.0f }, - { "dip", strlen("dip"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f }, - { "dp", strlen("dp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_DIP, 1.0f }, - { "sp", strlen("sp"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_SP, 1.0f }, - { "pt", strlen("pt"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_PT, 1.0f }, - { "in", strlen("in"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_IN, 1.0f }, - { "mm", strlen("mm"), Res_value::TYPE_DIMENSION, Res_value::COMPLEX_UNIT_MM, 1.0f }, - { "%", strlen("%"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION, 1.0f/100 }, - { "%p", strlen("%p"), Res_value::TYPE_FRACTION, Res_value::COMPLEX_UNIT_FRACTION_PARENT, 1.0f/100 }, - { NULL, 0, 0, 0, 0 } -}; - -static bool parse_unit(const char* str, Res_value* outValue, - float* outScale, const char** outEnd) -{ - const char* end = str; - while (*end != 0 && !isspace((unsigned char)*end)) { - end++; - } - const size_t len = end-str; - - const char* realEnd = end; - while (*realEnd != 0 && isspace((unsigned char)*realEnd)) { - realEnd++; - } - if (*realEnd != 0) { - return false; - } - - const unit_entry* cur = unitNames; - while (cur->name) { - if (len == cur->len && strncmp(cur->name, str, len) == 0) { - outValue->dataType = cur->type; - outValue->data = cur->unit << Res_value::COMPLEX_UNIT_SHIFT; - *outScale = cur->scale; - *outEnd = end; - //printf("Found unit %s for %s\n", cur->name, str); - return true; - } - cur++; - } - - return false; -} - - -bool ResTable::stringToInt(const char16_t* s, size_t len, Res_value* outValue) -{ - while (len > 0 && isspace16(*s)) { - s++; - len--; - } - - if (len <= 0) { - return false; - } - - size_t i = 0; - int32_t val = 0; - bool neg = false; - - if (*s == '-') { - neg = true; - i++; - } - - if (s[i] < '0' || s[i] > '9') { - return false; - } - - // Decimal or hex? - if (s[i] == '0' && s[i+1] == 'x') { - if (outValue) - outValue->dataType = outValue->TYPE_INT_HEX; - i += 2; - bool error = false; - while (i < len && !error) { - val = (val*16) + get_hex(s[i], &error); - i++; - } - if (error) { - return false; - } - } else { - if (outValue) - outValue->dataType = outValue->TYPE_INT_DEC; - while (i < len) { - if (s[i] < '0' || s[i] > '9') { - return false; - } - val = (val*10) + s[i]-'0'; - i++; - } - } - - if (neg) val = -val; - - while (i < len && isspace16(s[i])) { - i++; - } - - if (i == len) { - if (outValue) - outValue->data = val; - return true; - } - - return false; -} - -bool ResTable::stringToFloat(const char16_t* s, size_t len, Res_value* outValue) -{ - while (len > 0 && isspace16(*s)) { - s++; - len--; - } - - if (len <= 0) { - return false; - } - - char buf[128]; - int i=0; - while (len > 0 && *s != 0 && i < 126) { - if (*s > 255) { - return false; - } - buf[i++] = *s++; - len--; - } - - if (len > 0) { - return false; - } - if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') { - return false; - } - - buf[i] = 0; - const char* end; - float f = strtof(buf, (char**)&end); - - if (*end != 0 && !isspace((unsigned char)*end)) { - // Might be a unit... - float scale; - if (parse_unit(end, outValue, &scale, &end)) { - f *= scale; - const bool neg = f < 0; - if (neg) f = -f; - uint64_t bits = (uint64_t)(f*(1<<23)+.5f); - uint32_t radix; - uint32_t shift; - if ((bits&0x7fffff) == 0) { - // Always use 23p0 if there is no fraction, just to make - // things easier to read. - radix = Res_value::COMPLEX_RADIX_23p0; - shift = 23; - } else if ((bits&0xffffffffff800000LL) == 0) { - // Magnitude is zero -- can fit in 0 bits of precision. - radix = Res_value::COMPLEX_RADIX_0p23; - shift = 0; - } else if ((bits&0xffffffff80000000LL) == 0) { - // Magnitude can fit in 8 bits of precision. - radix = Res_value::COMPLEX_RADIX_8p15; - shift = 8; - } else if ((bits&0xffffff8000000000LL) == 0) { - // Magnitude can fit in 16 bits of precision. - radix = Res_value::COMPLEX_RADIX_16p7; - shift = 16; - } else { - // Magnitude needs entire range, so no fractional part. - radix = Res_value::COMPLEX_RADIX_23p0; - shift = 23; - } - int32_t mantissa = (int32_t)( - (bits>>shift) & Res_value::COMPLEX_MANTISSA_MASK); - if (neg) { - mantissa = (-mantissa) & Res_value::COMPLEX_MANTISSA_MASK; - } - outValue->data |= - (radix<<Res_value::COMPLEX_RADIX_SHIFT) - | (mantissa<<Res_value::COMPLEX_MANTISSA_SHIFT); - //printf("Input value: %f 0x%016Lx, mult: %f, radix: %d, shift: %d, final: 0x%08x\n", - // f * (neg ? -1 : 1), bits, f*(1<<23), - // radix, shift, outValue->data); - return true; - } - return false; - } - - while (*end != 0 && isspace((unsigned char)*end)) { - end++; - } - - if (*end == 0) { - if (outValue) { - outValue->dataType = outValue->TYPE_FLOAT; - *(float*)(&outValue->data) = f; - return true; - } - } - - return false; -} - -bool ResTable::stringToValue(Res_value* outValue, String16* outString, - const char16_t* s, size_t len, - bool preserveSpaces, bool coerceType, - uint32_t attrID, - const String16* defType, - const String16* defPackage, - Accessor* accessor, - void* accessorCookie, - uint32_t attrType, - bool enforcePrivate) const -{ - bool localizationSetting = accessor != NULL && accessor->getLocalizationSetting(); - const char* errorMsg = NULL; - - outValue->size = sizeof(Res_value); - outValue->res0 = 0; - - // First strip leading/trailing whitespace. Do this before handling - // escapes, so they can be used to force whitespace into the string. - if (!preserveSpaces) { - while (len > 0 && isspace16(*s)) { - s++; - len--; - } - while (len > 0 && isspace16(s[len-1])) { - len--; - } - // If the string ends with '\', then we keep the space after it. - if (len > 0 && s[len-1] == '\\' && s[len] != 0) { - len++; - } - } - - //printf("Value for: %s\n", String8(s, len).string()); - - uint32_t l10nReq = ResTable_map::L10N_NOT_REQUIRED; - uint32_t attrMin = 0x80000000, attrMax = 0x7fffffff; - bool fromAccessor = false; - if (attrID != 0 && !Res_INTERNALID(attrID)) { - const ssize_t p = getResourcePackageIndex(attrID); - const bag_entry* bag; - ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; - //printf("For attr 0x%08x got bag of %d\n", attrID, cnt); - if (cnt >= 0) { - while (cnt > 0) { - //printf("Entry 0x%08x = 0x%08x\n", bag->map.name.ident, bag->map.value.data); - switch (bag->map.name.ident) { - case ResTable_map::ATTR_TYPE: - attrType = bag->map.value.data; - break; - case ResTable_map::ATTR_MIN: - attrMin = bag->map.value.data; - break; - case ResTable_map::ATTR_MAX: - attrMax = bag->map.value.data; - break; - case ResTable_map::ATTR_L10N: - l10nReq = bag->map.value.data; - break; - } - bag++; - cnt--; - } - unlockBag(bag); - } else if (accessor && accessor->getAttributeType(attrID, &attrType)) { - fromAccessor = true; - if (attrType == ResTable_map::TYPE_ENUM - || attrType == ResTable_map::TYPE_FLAGS - || attrType == ResTable_map::TYPE_INTEGER) { - accessor->getAttributeMin(attrID, &attrMin); - accessor->getAttributeMax(attrID, &attrMax); - } - if (localizationSetting) { - l10nReq = accessor->getAttributeL10N(attrID); - } - } - } - - const bool canStringCoerce = - coerceType && (attrType&ResTable_map::TYPE_STRING) != 0; - - if (*s == '@') { - outValue->dataType = outValue->TYPE_REFERENCE; - - // Note: we don't check attrType here because the reference can - // be to any other type; we just need to count on the client making - // sure the referenced type is correct. - - //printf("Looking up ref: %s\n", String8(s, len).string()); - - // It's a reference! - if (len == 5 && s[1]=='n' && s[2]=='u' && s[3]=='l' && s[4]=='l') { - outValue->data = 0; - return true; - } else { - bool createIfNotFound = false; - const char16_t* resourceRefName; - int resourceNameLen; - if (len > 2 && s[1] == '+') { - createIfNotFound = true; - resourceRefName = s + 2; - resourceNameLen = len - 2; - } else if (len > 2 && s[1] == '*') { - enforcePrivate = false; - resourceRefName = s + 2; - resourceNameLen = len - 2; - } else { - createIfNotFound = false; - resourceRefName = s + 1; - resourceNameLen = len - 1; - } - String16 package, type, name; - if (!expandResourceRef(resourceRefName,resourceNameLen, &package, &type, &name, - defType, defPackage, &errorMsg)) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, errorMsg); - } - return false; - } - - uint32_t specFlags = 0; - uint32_t rid = identifierForName(name.string(), name.size(), type.string(), - type.size(), package.string(), package.size(), &specFlags); - if (rid != 0) { - if (enforcePrivate) { - if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Resource is not public."); - } - return false; - } - } - if (!accessor) { - outValue->data = rid; - return true; - } - rid = Res_MAKEID( - accessor->getRemappedPackage(Res_GETPACKAGE(rid)), - Res_GETTYPE(rid), Res_GETENTRY(rid)); - TABLE_NOISY(printf("Incl %s:%s/%s: 0x%08x\n", - String8(package).string(), String8(type).string(), - String8(name).string(), rid)); - outValue->data = rid; - return true; - } - - if (accessor) { - uint32_t rid = accessor->getCustomResourceWithCreation(package, type, name, - createIfNotFound); - if (rid != 0) { - TABLE_NOISY(printf("Pckg %s:%s/%s: 0x%08x\n", - String8(package).string(), String8(type).string(), - String8(name).string(), rid)); - outValue->data = rid; - return true; - } - } - } - - if (accessor != NULL) { - accessor->reportError(accessorCookie, "No resource found that matches the given name"); - } - return false; - } - - // if we got to here, and localization is required and it's not a reference, - // complain and bail. - if (l10nReq == ResTable_map::L10N_SUGGESTED) { - if (localizationSetting) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "This attribute must be localized."); - } - } - } - - if (*s == '#') { - // It's a color! Convert to an integer of the form 0xaarrggbb. - uint32_t color = 0; - bool error = false; - if (len == 4) { - outValue->dataType = outValue->TYPE_INT_COLOR_RGB4; - color |= 0xFF000000; - color |= get_hex(s[1], &error) << 20; - color |= get_hex(s[1], &error) << 16; - color |= get_hex(s[2], &error) << 12; - color |= get_hex(s[2], &error) << 8; - color |= get_hex(s[3], &error) << 4; - color |= get_hex(s[3], &error); - } else if (len == 5) { - outValue->dataType = outValue->TYPE_INT_COLOR_ARGB4; - color |= get_hex(s[1], &error) << 28; - color |= get_hex(s[1], &error) << 24; - color |= get_hex(s[2], &error) << 20; - color |= get_hex(s[2], &error) << 16; - color |= get_hex(s[3], &error) << 12; - color |= get_hex(s[3], &error) << 8; - color |= get_hex(s[4], &error) << 4; - color |= get_hex(s[4], &error); - } else if (len == 7) { - outValue->dataType = outValue->TYPE_INT_COLOR_RGB8; - color |= 0xFF000000; - color |= get_hex(s[1], &error) << 20; - color |= get_hex(s[2], &error) << 16; - color |= get_hex(s[3], &error) << 12; - color |= get_hex(s[4], &error) << 8; - color |= get_hex(s[5], &error) << 4; - color |= get_hex(s[6], &error); - } else if (len == 9) { - outValue->dataType = outValue->TYPE_INT_COLOR_ARGB8; - color |= get_hex(s[1], &error) << 28; - color |= get_hex(s[2], &error) << 24; - color |= get_hex(s[3], &error) << 20; - color |= get_hex(s[4], &error) << 16; - color |= get_hex(s[5], &error) << 12; - color |= get_hex(s[6], &error) << 8; - color |= get_hex(s[7], &error) << 4; - color |= get_hex(s[8], &error); - } else { - error = true; - } - if (!error) { - if ((attrType&ResTable_map::TYPE_COLOR) == 0) { - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, - "Color types not allowed"); - } - return false; - } - } else { - outValue->data = color; - //printf("Color input=%s, output=0x%x\n", String8(s, len).string(), color); - return true; - } - } else { - if ((attrType&ResTable_map::TYPE_COLOR) != 0) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Color value not valid --" - " must be #rgb, #argb, #rrggbb, or #aarrggbb"); - } - #if 0 - fprintf(stderr, "%s: Color ID %s value %s is not valid\n", - "Resource File", //(const char*)in->getPrintableSource(), - String8(*curTag).string(), - String8(s, len).string()); - #endif - return false; - } - } - } - - if (*s == '?') { - outValue->dataType = outValue->TYPE_ATTRIBUTE; - - // Note: we don't check attrType here because the reference can - // be to any other type; we just need to count on the client making - // sure the referenced type is correct. - - //printf("Looking up attr: %s\n", String8(s, len).string()); - - static const String16 attr16("attr"); - String16 package, type, name; - if (!expandResourceRef(s+1, len-1, &package, &type, &name, - &attr16, defPackage, &errorMsg)) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, errorMsg); - } - return false; - } - - //printf("Pkg: %s, Type: %s, Name: %s\n", - // String8(package).string(), String8(type).string(), - // String8(name).string()); - uint32_t specFlags = 0; - uint32_t rid = - identifierForName(name.string(), name.size(), - type.string(), type.size(), - package.string(), package.size(), &specFlags); - if (rid != 0) { - if (enforcePrivate) { - if ((specFlags&ResTable_typeSpec::SPEC_PUBLIC) == 0) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Attribute is not public."); - } - return false; - } - } - if (!accessor) { - outValue->data = rid; - return true; - } - rid = Res_MAKEID( - accessor->getRemappedPackage(Res_GETPACKAGE(rid)), - Res_GETTYPE(rid), Res_GETENTRY(rid)); - //printf("Incl %s:%s/%s: 0x%08x\n", - // String8(package).string(), String8(type).string(), - // String8(name).string(), rid); - outValue->data = rid; - return true; - } - - if (accessor) { - uint32_t rid = accessor->getCustomResource(package, type, name); - if (rid != 0) { - //printf("Mine %s:%s/%s: 0x%08x\n", - // String8(package).string(), String8(type).string(), - // String8(name).string(), rid); - outValue->data = rid; - return true; - } - } - - if (accessor != NULL) { - accessor->reportError(accessorCookie, "No resource found that matches the given name"); - } - return false; - } - - if (stringToInt(s, len, outValue)) { - if ((attrType&ResTable_map::TYPE_INTEGER) == 0) { - // If this type does not allow integers, but does allow floats, - // fall through on this error case because the float type should - // be able to accept any integer value. - if (!canStringCoerce && (attrType&ResTable_map::TYPE_FLOAT) == 0) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Integer types not allowed"); - } - return false; - } - } else { - if (((int32_t)outValue->data) < ((int32_t)attrMin) - || ((int32_t)outValue->data) > ((int32_t)attrMax)) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Integer value out of range"); - } - return false; - } - return true; - } - } - - if (stringToFloat(s, len, outValue)) { - if (outValue->dataType == Res_value::TYPE_DIMENSION) { - if ((attrType&ResTable_map::TYPE_DIMENSION) != 0) { - return true; - } - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Dimension types not allowed"); - } - return false; - } - } else if (outValue->dataType == Res_value::TYPE_FRACTION) { - if ((attrType&ResTable_map::TYPE_FRACTION) != 0) { - return true; - } - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Fraction types not allowed"); - } - return false; - } - } else if ((attrType&ResTable_map::TYPE_FLOAT) == 0) { - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Float types not allowed"); - } - return false; - } - } else { - return true; - } - } - - if (len == 4) { - if ((s[0] == 't' || s[0] == 'T') && - (s[1] == 'r' || s[1] == 'R') && - (s[2] == 'u' || s[2] == 'U') && - (s[3] == 'e' || s[3] == 'E')) { - if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) { - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Boolean types not allowed"); - } - return false; - } - } else { - outValue->dataType = outValue->TYPE_INT_BOOLEAN; - outValue->data = (uint32_t)-1; - return true; - } - } - } - - if (len == 5) { - if ((s[0] == 'f' || s[0] == 'F') && - (s[1] == 'a' || s[1] == 'A') && - (s[2] == 'l' || s[2] == 'L') && - (s[3] == 's' || s[3] == 'S') && - (s[4] == 'e' || s[4] == 'E')) { - if ((attrType&ResTable_map::TYPE_BOOLEAN) == 0) { - if (!canStringCoerce) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "Boolean types not allowed"); - } - return false; - } - } else { - outValue->dataType = outValue->TYPE_INT_BOOLEAN; - outValue->data = 0; - return true; - } - } - } - - if ((attrType&ResTable_map::TYPE_ENUM) != 0) { - const ssize_t p = getResourcePackageIndex(attrID); - const bag_entry* bag; - ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; - //printf("Got %d for enum\n", cnt); - if (cnt >= 0) { - resource_name rname; - while (cnt > 0) { - if (!Res_INTERNALID(bag->map.name.ident)) { - //printf("Trying attr #%08x\n", bag->map.name.ident); - if (getResourceName(bag->map.name.ident, false, &rname)) { - #if 0 - printf("Matching %s against %s (0x%08x)\n", - String8(s, len).string(), - String8(rname.name, rname.nameLen).string(), - bag->map.name.ident); - #endif - if (strzcmp16(s, len, rname.name, rname.nameLen) == 0) { - outValue->dataType = bag->map.value.dataType; - outValue->data = bag->map.value.data; - unlockBag(bag); - return true; - } - } - - } - bag++; - cnt--; - } - unlockBag(bag); - } - - if (fromAccessor) { - if (accessor->getAttributeEnum(attrID, s, len, outValue)) { - return true; - } - } - } - - if ((attrType&ResTable_map::TYPE_FLAGS) != 0) { - const ssize_t p = getResourcePackageIndex(attrID); - const bag_entry* bag; - ssize_t cnt = p >= 0 ? lockBag(attrID, &bag) : -1; - //printf("Got %d for flags\n", cnt); - if (cnt >= 0) { - bool failed = false; - resource_name rname; - outValue->dataType = Res_value::TYPE_INT_HEX; - outValue->data = 0; - const char16_t* end = s + len; - const char16_t* pos = s; - while (pos < end && !failed) { - const char16_t* start = pos; - pos++; - while (pos < end && *pos != '|') { - pos++; - } - //printf("Looking for: %s\n", String8(start, pos-start).string()); - const bag_entry* bagi = bag; - ssize_t i; - for (i=0; i<cnt; i++, bagi++) { - if (!Res_INTERNALID(bagi->map.name.ident)) { - //printf("Trying attr #%08x\n", bagi->map.name.ident); - if (getResourceName(bagi->map.name.ident, false, &rname)) { - #if 0 - printf("Matching %s against %s (0x%08x)\n", - String8(start,pos-start).string(), - String8(rname.name, rname.nameLen).string(), - bagi->map.name.ident); - #endif - if (strzcmp16(start, pos-start, rname.name, rname.nameLen) == 0) { - outValue->data |= bagi->map.value.data; - break; - } - } - } - } - if (i >= cnt) { - // Didn't find this flag identifier. - failed = true; - } - if (pos < end) { - pos++; - } - } - unlockBag(bag); - if (!failed) { - //printf("Final flag value: 0x%lx\n", outValue->data); - return true; - } - } - - - if (fromAccessor) { - if (accessor->getAttributeFlags(attrID, s, len, outValue)) { - //printf("Final flag value: 0x%lx\n", outValue->data); - return true; - } - } - } - - if ((attrType&ResTable_map::TYPE_STRING) == 0) { - if (accessor != NULL) { - accessor->reportError(accessorCookie, "String types not allowed"); - } - return false; - } - - // Generic string handling... - outValue->dataType = outValue->TYPE_STRING; - if (outString) { - bool failed = collectString(outString, s, len, preserveSpaces, &errorMsg); - if (accessor != NULL) { - accessor->reportError(accessorCookie, errorMsg); - } - return failed; - } - - return true; -} - -bool ResTable::collectString(String16* outString, - const char16_t* s, size_t len, - bool preserveSpaces, - const char** outErrorMsg, - bool append) -{ - String16 tmp; - - char quoted = 0; - const char16_t* p = s; - while (p < (s+len)) { - while (p < (s+len)) { - const char16_t c = *p; - if (c == '\\') { - break; - } - if (!preserveSpaces) { - if (quoted == 0 && isspace16(c) - && (c != ' ' || isspace16(*(p+1)))) { - break; - } - if (c == '"' && (quoted == 0 || quoted == '"')) { - break; - } - if (c == '\'' && (quoted == 0 || quoted == '\'')) { - /* - * In practice, when people write ' instead of \' - * in a string, they are doing it by accident - * instead of really meaning to use ' as a quoting - * character. Warn them so they don't lose it. - */ - if (outErrorMsg) { - *outErrorMsg = "Apostrophe not preceded by \\"; - } - return false; - } - } - p++; - } - if (p < (s+len)) { - if (p > s) { - tmp.append(String16(s, p-s)); - } - if (!preserveSpaces && (*p == '"' || *p == '\'')) { - if (quoted == 0) { - quoted = *p; - } else { - quoted = 0; - } - p++; - } else if (!preserveSpaces && isspace16(*p)) { - // Space outside of a quote -- consume all spaces and - // leave a single plain space char. - tmp.append(String16(" ")); - p++; - while (p < (s+len) && isspace16(*p)) { - p++; - } - } else if (*p == '\\') { - p++; - if (p < (s+len)) { - switch (*p) { - case 't': - tmp.append(String16("\t")); - break; - case 'n': - tmp.append(String16("\n")); - break; - case '#': - tmp.append(String16("#")); - break; - case '@': - tmp.append(String16("@")); - break; - case '?': - tmp.append(String16("?")); - break; - case '"': - tmp.append(String16("\"")); - break; - case '\'': - tmp.append(String16("'")); - break; - case '\\': - tmp.append(String16("\\")); - break; - case 'u': - { - char16_t chr = 0; - int i = 0; - while (i < 4 && p[1] != 0) { - p++; - i++; - int c; - if (*p >= '0' && *p <= '9') { - c = *p - '0'; - } else if (*p >= 'a' && *p <= 'f') { - c = *p - 'a' + 10; - } else if (*p >= 'A' && *p <= 'F') { - c = *p - 'A' + 10; - } else { - if (outErrorMsg) { - *outErrorMsg = "Bad character in \\u unicode escape sequence"; - } - return false; - } - chr = (chr<<4) | c; - } - tmp.append(String16(&chr, 1)); - } break; - default: - // ignore unknown escape chars. - break; - } - p++; - } - } - len -= (p-s); - s = p; - } - } - - if (tmp.size() != 0) { - if (len > 0) { - tmp.append(String16(s, len)); - } - if (append) { - outString->append(tmp); - } else { - outString->setTo(tmp); - } - } else { - if (append) { - outString->append(String16(s, len)); - } else { - outString->setTo(s, len); - } - } - - return true; -} - -size_t ResTable::getBasePackageCount() const -{ - if (mError != NO_ERROR) { - return 0; - } - return mPackageGroups.size(); -} - -const char16_t* ResTable::getBasePackageName(size_t idx) const -{ - if (mError != NO_ERROR) { - return 0; - } - LOG_FATAL_IF(idx >= mPackageGroups.size(), - "Requested package index %d past package count %d", - (int)idx, (int)mPackageGroups.size()); - return mPackageGroups[idx]->name.string(); -} - -uint32_t ResTable::getBasePackageId(size_t idx) const -{ - if (mError != NO_ERROR) { - return 0; - } - LOG_FATAL_IF(idx >= mPackageGroups.size(), - "Requested package index %d past package count %d", - (int)idx, (int)mPackageGroups.size()); - return mPackageGroups[idx]->id; -} - -size_t ResTable::getTableCount() const -{ - return mHeaders.size(); -} - -const ResStringPool* ResTable::getTableStringBlock(size_t index) const -{ - return &mHeaders[index]->values; -} - -void* ResTable::getTableCookie(size_t index) const -{ - return mHeaders[index]->cookie; -} - -void ResTable::getConfigurations(Vector<ResTable_config>* configs) const -{ - const size_t I = mPackageGroups.size(); - for (size_t i=0; i<I; i++) { - const PackageGroup* packageGroup = mPackageGroups[i]; - const size_t J = packageGroup->packages.size(); - for (size_t j=0; j<J; j++) { - const Package* package = packageGroup->packages[j]; - const size_t K = package->types.size(); - for (size_t k=0; k<K; k++) { - const Type* type = package->types[k]; - if (type == NULL) continue; - const size_t L = type->configs.size(); - for (size_t l=0; l<L; l++) { - const ResTable_type* config = type->configs[l]; - const ResTable_config* cfg = &config->config; - // only insert unique - const size_t M = configs->size(); - size_t m; - for (m=0; m<M; m++) { - if (0 == (*configs)[m].compare(*cfg)) { - break; - } - } - // if we didn't find it - if (m == M) { - configs->add(*cfg); - } - } - } - } - } -} - -void ResTable::getLocales(Vector<String8>* locales) const -{ - Vector<ResTable_config> configs; - ALOGV("calling getConfigurations"); - getConfigurations(&configs); - ALOGV("called getConfigurations size=%d", (int)configs.size()); - const size_t I = configs.size(); - for (size_t i=0; i<I; i++) { - char locale[6]; - configs[i].getLocale(locale); - const size_t J = locales->size(); - size_t j; - for (j=0; j<J; j++) { - if (0 == strcmp(locale, (*locales)[j].string())) { - break; - } - } - if (j == J) { - locales->add(String8(locale)); - } - } -} - -ssize_t ResTable::getEntry( - const Package* package, int typeIndex, int entryIndex, - const ResTable_config* config, - const ResTable_type** outType, const ResTable_entry** outEntry, - const Type** outTypeClass) const -{ - ALOGV("Getting entry from package %p\n", package); - const ResTable_package* const pkg = package->package; - - const Type* allTypes = package->getType(typeIndex); - ALOGV("allTypes=%p\n", allTypes); - if (allTypes == NULL) { - ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex); - return 0; - } - - if ((size_t)entryIndex >= allTypes->entryCount) { - ALOGW("getEntry failing because entryIndex %d is beyond type entryCount %d", - entryIndex, (int)allTypes->entryCount); - return BAD_TYPE; - } - - const ResTable_type* type = NULL; - uint32_t offset = ResTable_type::NO_ENTRY; - ResTable_config bestConfig; - memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up - - const size_t NT = allTypes->configs.size(); - for (size_t i=0; i<NT; i++) { - const ResTable_type* const thisType = allTypes->configs[i]; - if (thisType == NULL) continue; - - ResTable_config thisConfig; - thisConfig.copyFromDtoH(thisType->config); - - TABLE_GETENTRY(ALOGI("Match entry 0x%x in type 0x%x (sz 0x%x): %s\n", - entryIndex, typeIndex+1, dtohl(thisType->config.size), - thisConfig.toString().string())); - - // Check to make sure this one is valid for the current parameters. - if (config && !thisConfig.match(*config)) { - TABLE_GETENTRY(ALOGI("Does not match config!\n")); - continue; - } - - // Check if there is the desired entry in this type. - - const uint8_t* const end = ((const uint8_t*)thisType) - + dtohl(thisType->header.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)thisType) + dtohs(thisType->header.headerSize)); - - uint32_t thisOffset = dtohl(eindex[entryIndex]); - if (thisOffset == ResTable_type::NO_ENTRY) { - TABLE_GETENTRY(ALOGI("Skipping because it is not defined!\n")); - continue; - } - - if (type != NULL) { - // Check if this one is less specific than the last found. If so, - // we will skip it. We check starting with things we most care - // about to those we least care about. - if (!thisConfig.isBetterThan(bestConfig, config)) { - TABLE_GETENTRY(ALOGI("This config is worse than last!\n")); - continue; - } - } - - type = thisType; - offset = thisOffset; - bestConfig = thisConfig; - TABLE_GETENTRY(ALOGI("Best entry so far -- using it!\n")); - if (!config) break; - } - - if (type == NULL) { - TABLE_GETENTRY(ALOGI("No value found for requested entry!\n")); - return BAD_INDEX; - } - - offset += dtohl(type->entriesStart); - TABLE_NOISY(aout << "Looking in resource table " << package->header->header - << ", typeOff=" - << (void*)(((const char*)type)-((const char*)package->header->header)) - << ", offset=" << (void*)offset << endl); - - if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) { - ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x", - offset, dtohl(type->header.size)); - return BAD_TYPE; - } - if ((offset&0x3) != 0) { - ALOGW("ResTable_entry at 0x%x is not on an integer boundary", - offset); - return BAD_TYPE; - } - - const ResTable_entry* const entry = (const ResTable_entry*) - (((const uint8_t*)type) + offset); - if (dtohs(entry->size) < sizeof(*entry)) { - ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size)); - return BAD_TYPE; - } - - *outType = type; - *outEntry = entry; - if (outTypeClass != NULL) { - *outTypeClass = allTypes; - } - return offset + dtohs(entry->size); -} - -status_t ResTable::parsePackage(const ResTable_package* const pkg, - const Header* const header, uint32_t idmap_id) -{ - const uint8_t* base = (const uint8_t*)pkg; - status_t err = validate_chunk(&pkg->header, sizeof(*pkg), - header->dataEnd, "ResTable_package"); - if (err != NO_ERROR) { - return (mError=err); - } - - const size_t pkgSize = dtohl(pkg->header.size); - - if (dtohl(pkg->typeStrings) >= pkgSize) { - ALOGW("ResTable_package type strings at %p are past chunk size %p.", - (void*)dtohl(pkg->typeStrings), (void*)pkgSize); - return (mError=BAD_TYPE); - } - if ((dtohl(pkg->typeStrings)&0x3) != 0) { - ALOGW("ResTable_package type strings at %p is not on an integer boundary.", - (void*)dtohl(pkg->typeStrings)); - return (mError=BAD_TYPE); - } - if (dtohl(pkg->keyStrings) >= pkgSize) { - ALOGW("ResTable_package key strings at %p are past chunk size %p.", - (void*)dtohl(pkg->keyStrings), (void*)pkgSize); - return (mError=BAD_TYPE); - } - if ((dtohl(pkg->keyStrings)&0x3) != 0) { - ALOGW("ResTable_package key strings at %p is not on an integer boundary.", - (void*)dtohl(pkg->keyStrings)); - return (mError=BAD_TYPE); - } - - Package* package = NULL; - PackageGroup* group = NULL; - uint32_t id = idmap_id != 0 ? idmap_id : dtohl(pkg->id); - // If at this point id == 0, pkg is an overlay package without a - // corresponding idmap. During regular usage, overlay packages are - // always loaded alongside their idmaps, but during idmap creation - // the package is temporarily loaded by itself. - if (id < 256) { - - package = new Package(this, header, pkg); - if (package == NULL) { - return (mError=NO_MEMORY); - } - - size_t idx = mPackageMap[id]; - if (idx == 0) { - idx = mPackageGroups.size()+1; - - char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)]; - strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t)); - group = new PackageGroup(this, String16(tmpName), id); - if (group == NULL) { - delete package; - return (mError=NO_MEMORY); - } - - err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings), - header->dataEnd-(base+dtohl(pkg->typeStrings))); - if (err != NO_ERROR) { - delete group; - delete package; - return (mError=err); - } - err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings), - header->dataEnd-(base+dtohl(pkg->keyStrings))); - if (err != NO_ERROR) { - delete group; - delete package; - return (mError=err); - } - - //printf("Adding new package id %d at index %d\n", id, idx); - err = mPackageGroups.add(group); - if (err < NO_ERROR) { - return (mError=err); - } - group->basePackage = package; - - mPackageMap[id] = (uint8_t)idx; - } else { - group = mPackageGroups.itemAt(idx-1); - if (group == NULL) { - return (mError=UNKNOWN_ERROR); - } - } - err = group->packages.add(package); - if (err < NO_ERROR) { - return (mError=err); - } - } else { - LOG_ALWAYS_FATAL("Package id out of range"); - return NO_ERROR; - } - - - // Iterate through all chunks. - size_t curPackage = 0; - - const ResChunk_header* chunk = - (const ResChunk_header*)(((const uint8_t*)pkg) - + dtohs(pkg->header.headerSize)); - const uint8_t* endPos = ((const uint8_t*)pkg) + dtohs(pkg->header.size); - while (((const uint8_t*)chunk) <= (endPos-sizeof(ResChunk_header)) && - ((const uint8_t*)chunk) <= (endPos-dtohl(chunk->size))) { - TABLE_NOISY(ALOGV("PackageChunk: type=0x%x, headerSize=0x%x, size=0x%x, pos=%p\n", - dtohs(chunk->type), dtohs(chunk->headerSize), dtohl(chunk->size), - (void*)(((const uint8_t*)chunk) - ((const uint8_t*)header->header)))); - const size_t csize = dtohl(chunk->size); - const uint16_t ctype = dtohs(chunk->type); - if (ctype == RES_TABLE_TYPE_SPEC_TYPE) { - const ResTable_typeSpec* typeSpec = (const ResTable_typeSpec*)(chunk); - err = validate_chunk(&typeSpec->header, sizeof(*typeSpec), - endPos, "ResTable_typeSpec"); - if (err != NO_ERROR) { - return (mError=err); - } - - const size_t typeSpecSize = dtohl(typeSpec->header.size); - - LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n", - (void*)(base-(const uint8_t*)chunk), - dtohs(typeSpec->header.type), - dtohs(typeSpec->header.headerSize), - (void*)typeSize)); - // look for block overrun or int overflow when multiplying by 4 - if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t)) - || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount)) - > typeSpecSize)) { - ALOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.", - (void*)(dtohs(typeSpec->header.headerSize) - +(sizeof(uint32_t)*dtohl(typeSpec->entryCount))), - (void*)typeSpecSize); - return (mError=BAD_TYPE); - } - - if (typeSpec->id == 0) { - ALOGW("ResTable_type has an id of 0."); - return (mError=BAD_TYPE); - } - - while (package->types.size() < typeSpec->id) { - package->types.add(NULL); - } - Type* t = package->types[typeSpec->id-1]; - if (t == NULL) { - t = new Type(header, package, dtohl(typeSpec->entryCount)); - package->types.editItemAt(typeSpec->id-1) = t; - } else if (dtohl(typeSpec->entryCount) != t->entryCount) { - ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d", - (int)dtohl(typeSpec->entryCount), (int)t->entryCount); - return (mError=BAD_TYPE); - } - t->typeSpecFlags = (const uint32_t*)( - ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize)); - t->typeSpec = typeSpec; - - } else if (ctype == RES_TABLE_TYPE_TYPE) { - const ResTable_type* type = (const ResTable_type*)(chunk); - err = validate_chunk(&type->header, sizeof(*type)-sizeof(ResTable_config)+4, - endPos, "ResTable_type"); - if (err != NO_ERROR) { - return (mError=err); - } - - const size_t typeSize = dtohl(type->header.size); - - LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n", - (void*)(base-(const uint8_t*)chunk), - dtohs(type->header.type), - dtohs(type->header.headerSize), - (void*)typeSize)); - if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount)) - > typeSize) { - ALOGW("ResTable_type entry index to %p extends beyond chunk end %p.", - (void*)(dtohs(type->header.headerSize) - +(sizeof(uint32_t)*dtohl(type->entryCount))), - (void*)typeSize); - return (mError=BAD_TYPE); - } - if (dtohl(type->entryCount) != 0 - && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) { - ALOGW("ResTable_type entriesStart at %p extends beyond chunk end %p.", - (void*)dtohl(type->entriesStart), (void*)typeSize); - return (mError=BAD_TYPE); - } - if (type->id == 0) { - ALOGW("ResTable_type has an id of 0."); - return (mError=BAD_TYPE); - } - - while (package->types.size() < type->id) { - package->types.add(NULL); - } - Type* t = package->types[type->id-1]; - if (t == NULL) { - t = new Type(header, package, dtohl(type->entryCount)); - package->types.editItemAt(type->id-1) = t; - } else if (dtohl(type->entryCount) != t->entryCount) { - ALOGW("ResTable_type entry count inconsistent: given %d, previously %d", - (int)dtohl(type->entryCount), (int)t->entryCount); - return (mError=BAD_TYPE); - } - - TABLE_GETENTRY( - ResTable_config thisConfig; - thisConfig.copyFromDtoH(type->config); - ALOGI("Adding config to type %d: %s\n", - type->id, thisConfig.toString().string())); - t->configs.add(type); - } else { - status_t err = validate_chunk(chunk, sizeof(ResChunk_header), - endPos, "ResTable_package:unknown"); - if (err != NO_ERROR) { - return (mError=err); - } - } - chunk = (const ResChunk_header*) - (((const uint8_t*)chunk) + csize); - } - - if (group->typeCount == 0) { - group->typeCount = package->types.size(); - } - - return NO_ERROR; -} - -status_t ResTable::createIdmap(const ResTable& overlay, uint32_t originalCrc, uint32_t overlayCrc, - void** outData, size_t* outSize) const -{ - // see README for details on the format of map - if (mPackageGroups.size() == 0) { - return UNKNOWN_ERROR; - } - if (mPackageGroups[0]->packages.size() == 0) { - return UNKNOWN_ERROR; - } - - Vector<Vector<uint32_t> > map; - const PackageGroup* pg = mPackageGroups[0]; - const Package* pkg = pg->packages[0]; - size_t typeCount = pkg->types.size(); - // starting size is header + first item (number of types in map) - *outSize = (IDMAP_HEADER_SIZE + 1) * sizeof(uint32_t); - const String16 overlayPackage(overlay.mPackageGroups[0]->packages[0]->package->name); - const uint32_t pkg_id = pkg->package->id << 24; - - for (size_t typeIndex = 0; typeIndex < typeCount; ++typeIndex) { - ssize_t first = -1; - ssize_t last = -1; - const Type* typeConfigs = pkg->getType(typeIndex); - ssize_t mapIndex = map.add(); - if (mapIndex < 0) { - return NO_MEMORY; - } - Vector<uint32_t>& vector = map.editItemAt(mapIndex); - for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) { - uint32_t resID = pkg_id - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); - resource_name resName; - if (!this->getResourceName(resID, true, &resName)) { - ALOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID); - // add dummy value, or trimming leading/trailing zeroes later will fail - vector.push(0); - continue; - } - - const String16 overlayType(resName.type, resName.typeLen); - const String16 overlayName(resName.name, resName.nameLen); - uint32_t overlayResID = overlay.identifierForName(overlayName.string(), - overlayName.size(), - overlayType.string(), - overlayType.size(), - overlayPackage.string(), - overlayPackage.size()); - if (overlayResID != 0) { - overlayResID = pkg_id | (0x00ffffff & overlayResID); - last = Res_GETENTRY(resID); - if (first == -1) { - first = Res_GETENTRY(resID); - } - } - vector.push(overlayResID); -#if 0 - if (overlayResID != 0) { - ALOGD("%s/%s 0x%08x -> 0x%08x\n", - String8(String16(resName.type)).string(), - String8(String16(resName.name)).string(), - resID, overlayResID); - } -#endif - } - - if (first != -1) { - // shave off trailing entries which lack overlay values - const size_t last_past_one = last + 1; - if (last_past_one < vector.size()) { - vector.removeItemsAt(last_past_one, vector.size() - last_past_one); - } - // shave off leading entries which lack overlay values - vector.removeItemsAt(0, first); - // store offset to first overlaid resource ID of this type - vector.insertAt((uint32_t)first, 0, 1); - // reserve space for number and offset of entries, and the actual entries - *outSize += (2 + vector.size()) * sizeof(uint32_t); - } else { - // no entries of current type defined in overlay package - vector.clear(); - // reserve space for type offset - *outSize += 1 * sizeof(uint32_t); - } - } - - if ((*outData = malloc(*outSize)) == NULL) { - return NO_MEMORY; - } - uint32_t* data = (uint32_t*)*outData; - *data++ = htodl(IDMAP_MAGIC); - *data++ = htodl(originalCrc); - *data++ = htodl(overlayCrc); - const size_t mapSize = map.size(); - *data++ = htodl(mapSize); - size_t offset = mapSize; - for (size_t i = 0; i < mapSize; ++i) { - const Vector<uint32_t>& vector = map.itemAt(i); - const size_t N = vector.size(); - if (N == 0) { - *data++ = htodl(0); - } else { - offset++; - *data++ = htodl(offset); - offset += N; - } - } - for (size_t i = 0; i < mapSize; ++i) { - const Vector<uint32_t>& vector = map.itemAt(i); - const size_t N = vector.size(); - if (N == 0) { - continue; - } - if (N == 1) { // vector expected to hold (offset) + (N > 0 entries) - ALOGW("idmap: type %d supposedly has entries, but no entries found\n", i); - return UNKNOWN_ERROR; - } - *data++ = htodl(N - 1); // do not count the offset (which is vector's first element) - for (size_t j = 0; j < N; ++j) { - const uint32_t& overlayResID = vector.itemAt(j); - *data++ = htodl(overlayResID); - } - } - - return NO_ERROR; -} - -bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes, - uint32_t* pOriginalCrc, uint32_t* pOverlayCrc) -{ - const uint32_t* map = (const uint32_t*)idmap; - if (!assertIdmapHeader(map, sizeBytes)) { - return false; - } - *pOriginalCrc = map[1]; - *pOverlayCrc = map[2]; - return true; -} - - -#define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string()) - -#define CHAR16_ARRAY_EQ(constant, var, len) \ - ((len == (sizeof(constant)/sizeof(constant[0]))) && (0 == memcmp((var), (constant), (len)))) - -static void print_complex(uint32_t complex, bool isFraction) -{ - const float MANTISSA_MULT = - 1.0f / (1<<Res_value::COMPLEX_MANTISSA_SHIFT); - const float RADIX_MULTS[] = { - 1.0f*MANTISSA_MULT, 1.0f/(1<<7)*MANTISSA_MULT, - 1.0f/(1<<15)*MANTISSA_MULT, 1.0f/(1<<23)*MANTISSA_MULT - }; - - float value = (complex&(Res_value::COMPLEX_MANTISSA_MASK - <<Res_value::COMPLEX_MANTISSA_SHIFT)) - * RADIX_MULTS[(complex>>Res_value::COMPLEX_RADIX_SHIFT) - & Res_value::COMPLEX_RADIX_MASK]; - printf("%f", value); - - if (!isFraction) { - switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) { - case Res_value::COMPLEX_UNIT_PX: printf("px"); break; - case Res_value::COMPLEX_UNIT_DIP: printf("dp"); break; - case Res_value::COMPLEX_UNIT_SP: printf("sp"); break; - case Res_value::COMPLEX_UNIT_PT: printf("pt"); break; - case Res_value::COMPLEX_UNIT_IN: printf("in"); break; - case Res_value::COMPLEX_UNIT_MM: printf("mm"); break; - default: printf(" (unknown unit)"); break; - } - } else { - switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) { - case Res_value::COMPLEX_UNIT_FRACTION: printf("%%"); break; - case Res_value::COMPLEX_UNIT_FRACTION_PARENT: printf("%%p"); break; - default: printf(" (unknown unit)"); break; - } - } -} - -// Normalize a string for output -String8 ResTable::normalizeForOutput( const char *input ) -{ - String8 ret; - char buff[2]; - buff[1] = '\0'; - - while (*input != '\0') { - switch (*input) { - // All interesting characters are in the ASCII zone, so we are making our own lives - // easier by scanning the string one byte at a time. - case '\\': - ret += "\\\\"; - break; - case '\n': - ret += "\\n"; - break; - case '"': - ret += "\\\""; - break; - default: - buff[0] = *input; - ret += buff; - break; - } - - input++; - } - - return ret; -} - -void ResTable::print_value(const Package* pkg, const Res_value& value) const -{ - if (value.dataType == Res_value::TYPE_NULL) { - printf("(null)\n"); - } else if (value.dataType == Res_value::TYPE_REFERENCE) { - printf("(reference) 0x%08x\n", value.data); - } else if (value.dataType == Res_value::TYPE_ATTRIBUTE) { - printf("(attribute) 0x%08x\n", value.data); - } else if (value.dataType == Res_value::TYPE_STRING) { - size_t len; - const char* str8 = pkg->header->values.string8At( - value.data, &len); - if (str8 != NULL) { - printf("(string8) \"%s\"\n", normalizeForOutput(str8).string()); - } else { - const char16_t* str16 = pkg->header->values.stringAt( - value.data, &len); - if (str16 != NULL) { - printf("(string16) \"%s\"\n", - normalizeForOutput(String8(str16, len).string()).string()); - } else { - printf("(string) null\n"); - } - } - } else if (value.dataType == Res_value::TYPE_FLOAT) { - printf("(float) %g\n", *(const float*)&value.data); - } else if (value.dataType == Res_value::TYPE_DIMENSION) { - printf("(dimension) "); - print_complex(value.data, false); - printf("\n"); - } else if (value.dataType == Res_value::TYPE_FRACTION) { - printf("(fraction) "); - print_complex(value.data, true); - printf("\n"); - } else if (value.dataType >= Res_value::TYPE_FIRST_COLOR_INT - || value.dataType <= Res_value::TYPE_LAST_COLOR_INT) { - printf("(color) #%08x\n", value.data); - } else if (value.dataType == Res_value::TYPE_INT_BOOLEAN) { - printf("(boolean) %s\n", value.data ? "true" : "false"); - } else if (value.dataType >= Res_value::TYPE_FIRST_INT - || value.dataType <= Res_value::TYPE_LAST_INT) { - printf("(int) 0x%08x or %d\n", value.data, value.data); - } else { - printf("(unknown type) t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)\n", - (int)value.dataType, (int)value.data, - (int)value.size, (int)value.res0); - } -} - -void ResTable::print(bool inclValues) const -{ - if (mError != 0) { - printf("mError=0x%x (%s)\n", mError, strerror(mError)); - } -#if 0 - printf("mParams=%c%c-%c%c,\n", - mParams.language[0], mParams.language[1], - mParams.country[0], mParams.country[1]); -#endif - size_t pgCount = mPackageGroups.size(); - printf("Package Groups (%d)\n", (int)pgCount); - for (size_t pgIndex=0; pgIndex<pgCount; pgIndex++) { - const PackageGroup* pg = mPackageGroups[pgIndex]; - printf("Package Group %d id=%d packageCount=%d name=%s\n", - (int)pgIndex, pg->id, (int)pg->packages.size(), - String8(pg->name).string()); - - size_t pkgCount = pg->packages.size(); - for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) { - const Package* pkg = pg->packages[pkgIndex]; - size_t typeCount = pkg->types.size(); - printf(" Package %d id=%d name=%s typeCount=%d\n", (int)pkgIndex, - pkg->package->id, String8(String16(pkg->package->name)).string(), - (int)typeCount); - for (size_t typeIndex=0; typeIndex<typeCount; typeIndex++) { - const Type* typeConfigs = pkg->getType(typeIndex); - if (typeConfigs == NULL) { - printf(" type %d NULL\n", (int)typeIndex); - continue; - } - const size_t NTC = typeConfigs->configs.size(); - printf(" type %d configCount=%d entryCount=%d\n", - (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount); - if (typeConfigs->typeSpecFlags != NULL) { - for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) { - uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); - resource_name resName; - if (this->getResourceName(resID, true, &resName)) { - String8 type8; - String8 name8; - if (resName.type8 != NULL) { - type8 = String8(resName.type8, resName.typeLen); - } else { - type8 = String8(resName.type, resName.typeLen); - } - if (resName.name8 != NULL) { - name8 = String8(resName.name8, resName.nameLen); - } else { - name8 = String8(resName.name, resName.nameLen); - } - printf(" spec resource 0x%08x %s:%s/%s: flags=0x%08x\n", - resID, - CHAR16_TO_CSTR(resName.package, resName.packageLen), - type8.string(), name8.string(), - dtohl(typeConfigs->typeSpecFlags[entryIndex])); - } else { - printf(" INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID); - } - } - } - for (size_t configIndex=0; configIndex<NTC; configIndex++) { - const ResTable_type* type = typeConfigs->configs[configIndex]; - if ((((uint64_t)type)&0x3) != 0) { - printf(" NON-INTEGER ResTable_type ADDRESS: %p\n", type); - continue; - } - String8 configStr = type->config.toString(); - printf(" config %s:\n", configStr.size() > 0 - ? configStr.string() : "(default)"); - size_t entryCount = dtohl(type->entryCount); - uint32_t entriesStart = dtohl(type->entriesStart); - if ((entriesStart&0x3) != 0) { - printf(" NON-INTEGER ResTable_type entriesStart OFFSET: %p\n", (void*)entriesStart); - continue; - } - uint32_t typeSize = dtohl(type->header.size); - if ((typeSize&0x3) != 0) { - printf(" NON-INTEGER ResTable_type header.size: %p\n", (void*)typeSize); - continue; - } - for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) { - - const uint8_t* const end = ((const uint8_t*)type) - + dtohl(type->header.size); - const uint32_t* const eindex = (const uint32_t*) - (((const uint8_t*)type) + dtohs(type->header.headerSize)); - - uint32_t thisOffset = dtohl(eindex[entryIndex]); - if (thisOffset == ResTable_type::NO_ENTRY) { - continue; - } - - uint32_t resID = (0xff000000 & ((pkg->package->id)<<24)) - | (0x00ff0000 & ((typeIndex+1)<<16)) - | (0x0000ffff & (entryIndex)); - resource_name resName; - if (this->getResourceName(resID, true, &resName)) { - String8 type8; - String8 name8; - if (resName.type8 != NULL) { - type8 = String8(resName.type8, resName.typeLen); - } else { - type8 = String8(resName.type, resName.typeLen); - } - if (resName.name8 != NULL) { - name8 = String8(resName.name8, resName.nameLen); - } else { - name8 = String8(resName.name, resName.nameLen); - } - printf(" resource 0x%08x %s:%s/%s: ", resID, - CHAR16_TO_CSTR(resName.package, resName.packageLen), - type8.string(), name8.string()); - } else { - printf(" INVALID RESOURCE 0x%08x: ", resID); - } - if ((thisOffset&0x3) != 0) { - printf("NON-INTEGER OFFSET: %p\n", (void*)thisOffset); - continue; - } - if ((thisOffset+sizeof(ResTable_entry)) > typeSize) { - printf("OFFSET OUT OF BOUNDS: %p+%p (size is %p)\n", - (void*)entriesStart, (void*)thisOffset, - (void*)typeSize); - continue; - } - - const ResTable_entry* ent = (const ResTable_entry*) - (((const uint8_t*)type) + entriesStart + thisOffset); - if (((entriesStart + thisOffset)&0x3) != 0) { - printf("NON-INTEGER ResTable_entry OFFSET: %p\n", - (void*)(entriesStart + thisOffset)); - continue; - } - - uint16_t esize = dtohs(ent->size); - if ((esize&0x3) != 0) { - printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void*)esize); - continue; - } - if ((thisOffset+esize) > typeSize) { - printf("ResTable_entry OUT OF BOUNDS: %p+%p+%p (size is %p)\n", - (void*)entriesStart, (void*)thisOffset, - (void*)esize, (void*)typeSize); - continue; - } - - const Res_value* valuePtr = NULL; - const ResTable_map_entry* bagPtr = NULL; - Res_value value; - if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { - printf("<bag>"); - bagPtr = (const ResTable_map_entry*)ent; - } else { - valuePtr = (const Res_value*) - (((const uint8_t*)ent) + esize); - value.copyFrom_dtoh(*valuePtr); - printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)", - (int)value.dataType, (int)value.data, - (int)value.size, (int)value.res0); - } - - if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) { - printf(" (PUBLIC)"); - } - printf("\n"); - - if (inclValues) { - if (valuePtr != NULL) { - printf(" "); - print_value(pkg, value); - } else if (bagPtr != NULL) { - const int N = dtohl(bagPtr->count); - const uint8_t* baseMapPtr = (const uint8_t*)ent; - size_t mapOffset = esize; - const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); - printf(" Parent=0x%08x, Count=%d\n", - dtohl(bagPtr->parent.ident), N); - for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) { - printf(" #%i (Key=0x%08x): ", - i, dtohl(mapPtr->name.ident)); - value.copyFrom_dtoh(mapPtr->value); - print_value(pkg, value); - const size_t size = dtohs(mapPtr->value.size); - mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value); - mapPtr = (ResTable_map*)(baseMapPtr+mapOffset); - } - } - } - } - } - } - } - } -} - -} // namespace android diff --git a/libs/androidfw/StreamingZipInflater.cpp b/libs/androidfw/StreamingZipInflater.cpp deleted file mode 100644 index 1dfec23..0000000 --- a/libs/androidfw/StreamingZipInflater.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/* - * Copyright (C) 2010 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_NDEBUG 0 -#define LOG_TAG "szipinf" -#include <utils/Log.h> - -#include <androidfw/StreamingZipInflater.h> -#include <utils/FileMap.h> -#include <string.h> -#include <stddef.h> -#include <assert.h> -#include <unistd.h> -#include <errno.h> - -/* - * TEMP_FAILURE_RETRY is defined by some, but not all, versions of - * <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's - * not already defined, then define it here. - */ -#ifndef TEMP_FAILURE_RETRY -/* Used to retry syscalls that can return EINTR. */ -#define TEMP_FAILURE_RETRY(exp) ({ \ - typeof (exp) _rc; \ - do { \ - _rc = (exp); \ - } while (_rc == -1 && errno == EINTR); \ - _rc; }) -#endif - -static inline size_t min_of(size_t a, size_t b) { return (a < b) ? a : b; } - -using namespace android; - -/* - * Streaming access to compressed asset data in an open fd - */ -StreamingZipInflater::StreamingZipInflater(int fd, off64_t compDataStart, - size_t uncompSize, size_t compSize) { - mFd = fd; - mDataMap = NULL; - mInFileStart = compDataStart; - mOutTotalSize = uncompSize; - mInTotalSize = compSize; - - mInBufSize = StreamingZipInflater::INPUT_CHUNK_SIZE; - mInBuf = new uint8_t[mInBufSize]; - - mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE; - mOutBuf = new uint8_t[mOutBufSize]; - - initInflateState(); -} - -/* - * Streaming access to compressed data held in an mmapped region of memory - */ -StreamingZipInflater::StreamingZipInflater(FileMap* dataMap, size_t uncompSize) { - mFd = -1; - mDataMap = dataMap; - mOutTotalSize = uncompSize; - mInTotalSize = dataMap->getDataLength(); - - mInBuf = (uint8_t*) dataMap->getDataPtr(); - mInBufSize = mInTotalSize; - - mOutBufSize = StreamingZipInflater::OUTPUT_CHUNK_SIZE; - mOutBuf = new uint8_t[mOutBufSize]; - - initInflateState(); -} - -StreamingZipInflater::~StreamingZipInflater() { - // tear down the in-flight zip state just in case - ::inflateEnd(&mInflateState); - - if (mDataMap == NULL) { - delete [] mInBuf; - } - delete [] mOutBuf; -} - -void StreamingZipInflater::initInflateState() { - ALOGV("Initializing inflate state"); - - memset(&mInflateState, 0, sizeof(mInflateState)); - mInflateState.zalloc = Z_NULL; - mInflateState.zfree = Z_NULL; - mInflateState.opaque = Z_NULL; - mInflateState.next_in = (Bytef*)mInBuf; - mInflateState.next_out = (Bytef*) mOutBuf; - mInflateState.avail_out = mOutBufSize; - mInflateState.data_type = Z_UNKNOWN; - - mOutLastDecoded = mOutDeliverable = mOutCurPosition = 0; - mInNextChunkOffset = 0; - mStreamNeedsInit = true; - - if (mDataMap == NULL) { - ::lseek(mFd, mInFileStart, SEEK_SET); - mInflateState.avail_in = 0; // set when a chunk is read in - } else { - mInflateState.avail_in = mInBufSize; - } -} - -/* - * Basic approach: - * - * 1. If we have undelivered uncompressed data, send it. At this point - * either we've satisfied the request, or we've exhausted the available - * output data in mOutBuf. - * - * 2. While we haven't sent enough data to satisfy the request: - * 0. if the request is for more data than exists, bail. - * a. if there is no input data to decode, read some into the input buffer - * and readjust the z_stream input pointers - * b. point the output to the start of the output buffer and decode what we can - * c. deliver whatever output data we can - */ -ssize_t StreamingZipInflater::read(void* outBuf, size_t count) { - uint8_t* dest = (uint8_t*) outBuf; - size_t bytesRead = 0; - size_t toRead = min_of(count, size_t(mOutTotalSize - mOutCurPosition)); - while (toRead > 0) { - // First, write from whatever we already have decoded and ready to go - size_t deliverable = min_of(toRead, mOutLastDecoded - mOutDeliverable); - if (deliverable > 0) { - if (outBuf != NULL) memcpy(dest, mOutBuf + mOutDeliverable, deliverable); - mOutDeliverable += deliverable; - mOutCurPosition += deliverable; - dest += deliverable; - bytesRead += deliverable; - toRead -= deliverable; - } - - // need more data? time to decode some. - if (toRead > 0) { - // if we don't have any data to decode, read some in. If we're working - // from mmapped data this won't happen, because the clipping to total size - // will prevent reading off the end of the mapped input chunk. - if ((mInflateState.avail_in == 0) && (mDataMap == NULL)) { - int err = readNextChunk(); - if (err < 0) { - ALOGE("Unable to access asset data: %d", err); - if (!mStreamNeedsInit) { - ::inflateEnd(&mInflateState); - initInflateState(); - } - return -1; - } - } - // we know we've drained whatever is in the out buffer now, so just - // start from scratch there, reading all the input we have at present. - mInflateState.next_out = (Bytef*) mOutBuf; - mInflateState.avail_out = mOutBufSize; - - /* - ALOGV("Inflating to outbuf: avail_in=%u avail_out=%u next_in=%p next_out=%p", - mInflateState.avail_in, mInflateState.avail_out, - mInflateState.next_in, mInflateState.next_out); - */ - int result = Z_OK; - if (mStreamNeedsInit) { - ALOGV("Initializing zlib to inflate"); - result = inflateInit2(&mInflateState, -MAX_WBITS); - mStreamNeedsInit = false; - } - if (result == Z_OK) result = ::inflate(&mInflateState, Z_SYNC_FLUSH); - if (result < 0) { - // Whoops, inflation failed - ALOGE("Error inflating asset: %d", result); - ::inflateEnd(&mInflateState); - initInflateState(); - return -1; - } else { - if (result == Z_STREAM_END) { - // we know we have to have reached the target size here and will - // not try to read any further, so just wind things up. - ::inflateEnd(&mInflateState); - } - - // Note how much data we got, and off we go - mOutDeliverable = 0; - mOutLastDecoded = mOutBufSize - mInflateState.avail_out; - } - } - } - return bytesRead; -} - -int StreamingZipInflater::readNextChunk() { - assert(mDataMap == NULL); - - if (mInNextChunkOffset < mInTotalSize) { - size_t toRead = min_of(mInBufSize, mInTotalSize - mInNextChunkOffset); - if (toRead > 0) { - ssize_t didRead = TEMP_FAILURE_RETRY(::read(mFd, mInBuf, toRead)); - //ALOGV("Reading input chunk, size %08x didread %08x", toRead, didRead); - if (didRead < 0) { - ALOGE("Error reading asset data: %s", strerror(errno)); - return didRead; - } else { - mInNextChunkOffset += didRead; - mInflateState.next_in = (Bytef*) mInBuf; - mInflateState.avail_in = didRead; - } - } - } - return 0; -} - -// seeking backwards requires uncompressing fom the beginning, so is very -// expensive. seeking forwards only requires uncompressing from the current -// position to the destination. -off64_t StreamingZipInflater::seekAbsolute(off64_t absoluteInputPosition) { - if (absoluteInputPosition < mOutCurPosition) { - // rewind and reprocess the data from the beginning - if (!mStreamNeedsInit) { - ::inflateEnd(&mInflateState); - } - initInflateState(); - read(NULL, absoluteInputPosition); - } else if (absoluteInputPosition > mOutCurPosition) { - read(NULL, absoluteInputPosition - mOutCurPosition); - } - // else if the target position *is* our current position, do nothing - return absoluteInputPosition; -} diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp deleted file mode 100644 index 1ab18ad..0000000 --- a/libs/androidfw/ZipFileRO.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/* - * 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 <ziparchive/zip_archive.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; - -class _ZipEntryRO { -public: - ZipEntry entry; - ZipEntryName name; - void *cookie; - - _ZipEntryRO() : cookie(NULL) { - } - -private: - _ZipEntryRO(const _ZipEntryRO& other); - _ZipEntryRO& operator=(const _ZipEntryRO& other); -}; - -ZipFileRO::~ZipFileRO() { - CloseArchive(mHandle); - free(mFileName); -} - -/* - * Open the specified file read-only. We memory-map the entire thing and - * close the file before returning. - */ -/* static */ ZipFileRO* ZipFileRO::open(const char* zipFileName) -{ - ZipArchiveHandle handle; - const int32_t error = OpenArchive(zipFileName, &handle); - if (error) { - ALOGW("Error opening archive %s: %s", zipFileName, ErrorCodeString(error)); - return NULL; - } - - return new ZipFileRO(handle, strdup(zipFileName)); -} - - -ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const -{ - _ZipEntryRO* data = new _ZipEntryRO; - const int32_t error = FindEntry(mHandle, entryName, &(data->entry)); - if (error) { - delete data; - return NULL; - } - - data->name.name = entryName; - data->name.name_length = strlen(entryName); - - return (ZipEntryRO) data; -} - -/* - * 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 -{ - const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); - const ZipEntry& ze = zipEntry->entry; - - if (pMethod != NULL) { - *pMethod = ze.method; - } - if (pUncompLen != NULL) { - *pUncompLen = ze.uncompressed_length; - } - if (pCompLen != NULL) { - *pCompLen = ze.compressed_length; - } - if (pOffset != NULL) { - *pOffset = ze.offset; - } - if (pModWhen != NULL) { - *pModWhen = ze.mod_time; - } - if (pCrc32 != NULL) { - *pCrc32 = ze.crc32; - } - - return true; -} - -bool ZipFileRO::startIteration(void** cookie) -{ - _ZipEntryRO* ze = new _ZipEntryRO; - int32_t error = StartIteration(mHandle, &(ze->cookie), NULL /* prefix */); - if (error) { - ALOGW("Could not start iteration over %s: %s", mFileName, ErrorCodeString(error)); - delete ze; - return false; - } - - *cookie = ze; - return true; -} - -ZipEntryRO ZipFileRO::nextEntry(void* cookie) -{ - _ZipEntryRO* ze = reinterpret_cast<_ZipEntryRO*>(cookie); - int32_t error = Next(ze->cookie, &(ze->entry), &(ze->name)); - if (error) { - if (error != -1) { - ALOGW("Error iteration over %s: %s", mFileName, ErrorCodeString(error)); - } - return NULL; - } - - return &(ze->entry); -} - -void ZipFileRO::endIteration(void* cookie) -{ - delete reinterpret_cast<_ZipEntryRO*>(cookie); -} - -void ZipFileRO::releaseEntry(ZipEntryRO entry) const -{ - delete reinterpret_cast<_ZipEntryRO*>(entry); -} - -/* - * Copy the entry's filename to the buffer. - */ -int ZipFileRO::getEntryFileName(ZipEntryRO entry, char* buffer, int bufLen) - const -{ - const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); - const uint16_t requiredSize = zipEntry->name.name_length + 1; - - if (bufLen < requiredSize) { - ALOGW("Buffer too short, requires %d bytes for entry name", requiredSize); - return requiredSize; - } - - memcpy(buffer, zipEntry->name.name, requiredSize - 1); - buffer[requiredSize - 1] = '\0'; - - return 0; -} - -/* - * Create a new FileMap object that spans the data in "entry". - */ -FileMap* ZipFileRO::createEntryFileMap(ZipEntryRO entry) const -{ - const _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); - const ZipEntry& ze = zipEntry->entry; - int fd = GetFileDescriptor(mHandle); - size_t actualLen = 0; - - if (ze.method == kCompressStored) { - actualLen = ze.uncompressed_length; - } else { - actualLen = ze.compressed_length; - } - - FileMap* newMap = new FileMap(); - if (!newMap->create(mFileName, fd, ze.offset, actualLen, 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, size_t size) const -{ - _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); - const int32_t error = ExtractToMemory(mHandle, &(zipEntry->entry), - (uint8_t*) buffer, size); - if (error) { - ALOGW("ExtractToMemory failed with %s", ErrorCodeString(error)); - return false; - } - - return true; -} - -/* - * 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 -{ - _ZipEntryRO *zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); - const int32_t error = ExtractEntryToFile(mHandle, &(zipEntry->entry), fd); - if (error) { - ALOGW("ExtractToMemory failed with %s", ErrorCodeString(error)); - return false; - } - - return true; -} diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp deleted file mode 100644 index e9ac2fe..0000000 --- a/libs/androidfw/ZipUtils.cpp +++ /dev/null @@ -1,330 +0,0 @@ -/* - * 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; - -static inline unsigned long get4LE(const unsigned char* buf) { - return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); -} - - -static const unsigned long kReadBufSize = 32768; - -/* - * 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*/ template<typename T> bool inflateToBuffer(T& reader, void* buf, - long uncompressedLen, long compressedLen) -{ - bool result = false; - - z_stream zstream; - int zerr; - unsigned long compRemaining; - - assert(uncompressedLen >= 0); - assert(compressedLen >= 0); - - 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); - - unsigned char* nextBuffer = NULL; - const unsigned long nextSize = reader.read(&nextBuffer, getSize); - - if (nextSize < getSize || nextBuffer == NULL) { - ALOGD("inflate read failed (%ld vs %ld)\n", nextSize, getSize); - goto z_bail; - } - - compRemaining -= nextSize; - - zstream.next_in = nextBuffer; - zstream.avail_in = nextSize; - } - - /* 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: - return result; -} - -class FileReader { -public: - FileReader(FILE* fp) : - mFp(fp), mReadBuf(new unsigned char[kReadBufSize]) - { - } - - ~FileReader() { - delete[] mReadBuf; - } - - long read(unsigned char** nextBuffer, long readSize) const { - *nextBuffer = mReadBuf; - return fread(mReadBuf, 1, readSize, mFp); - } - - FILE* mFp; - unsigned char* mReadBuf; -}; - -class FdReader { -public: - FdReader(int fd) : - mFd(fd), mReadBuf(new unsigned char[kReadBufSize]) - { - } - - ~FdReader() { - delete[] mReadBuf; - } - - long read(unsigned char** nextBuffer, long readSize) const { - *nextBuffer = mReadBuf; - return TEMP_FAILURE_RETRY(::read(mFd, mReadBuf, readSize)); - } - - int mFd; - unsigned char* mReadBuf; -}; - -class BufferReader { -public: - BufferReader(void* input, size_t inputSize) : - mInput(reinterpret_cast<unsigned char*>(input)), - mInputSize(inputSize), - mBufferReturned(false) - { - } - - long read(unsigned char** nextBuffer, long readSize) { - if (!mBufferReturned) { - mBufferReturned = true; - *nextBuffer = mInput; - return mInputSize; - } - - *nextBuffer = NULL; - return 0; - } - - unsigned char* mInput; - const size_t mInputSize; - bool mBufferReturned; -}; - -/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf, - long uncompressedLen, long compressedLen) -{ - FileReader reader(fp); - return ::inflateToBuffer<FileReader>(reader, buf, - uncompressedLen, compressedLen); -} - -/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf, - long uncompressedLen, long compressedLen) -{ - FdReader reader(fd); - return ::inflateToBuffer<FdReader>(reader, buf, - uncompressedLen, compressedLen); -} - -/*static*/ bool ZipUtils::inflateToBuffer(void* in, void* buf, - long uncompressedLen, long compressedLen) -{ - BufferReader reader(in, compressedLen); - return ::inflateToBuffer<BufferReader>(reader, buf, - uncompressedLen, compressedLen); -} - - - -/* - * 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 = get4LE(&buf[0]); - *pUncompressedLen = get4LE(&buf[4]); - - return true; -} diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp deleted file mode 100644 index 29686ef..0000000 --- a/libs/androidfw/misc.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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 deleted file mode 100644 index 3c55375..0000000 --- a/libs/androidfw/tests/Android.mk +++ /dev/null @@ -1,31 +0,0 @@ -# Build the unit tests. -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# Build the unit tests. -test_src_files := \ - ObbFile_test.cpp \ - ZipUtils_test.cpp - -shared_libraries := \ - libandroidfw \ - libcutils \ - libutils \ - libui \ - libstlport - -static_libraries := \ - libgtest \ - libgtest_main - -$(foreach file,$(test_src_files), \ - $(eval include $(CLEAR_VARS)) \ - $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ - $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ - $(eval LOCAL_SRC_FILES := $(file)) \ - $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ - $(eval include $(BUILD_NATIVE_TEST)) \ -) - -# Build the manual test programs. -include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/libs/androidfw/tests/ObbFile_test.cpp b/libs/androidfw/tests/ObbFile_test.cpp deleted file mode 100644 index 2c9f650..0000000 --- a/libs/androidfw/tests/ObbFile_test.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2010 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 "ObbFile_test" -#include <androidfw/ObbFile.h> -#include <utils/Log.h> -#include <utils/RefBase.h> -#include <utils/String8.h> - -#include <gtest/gtest.h> - -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <string.h> - -namespace android { - -#define TEST_FILENAME "/test.obb" - -class ObbFileTest : public testing::Test { -protected: - sp<ObbFile> mObbFile; - char* mExternalStorage; - char* mFileName; - - virtual void SetUp() { - mObbFile = new ObbFile(); - mExternalStorage = getenv("EXTERNAL_STORAGE"); - - const int totalLen = strlen(mExternalStorage) + strlen(TEST_FILENAME) + 1; - mFileName = new char[totalLen]; - snprintf(mFileName, totalLen, "%s%s", mExternalStorage, TEST_FILENAME); - - int fd = ::open(mFileName, O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); - if (fd < 0) { - FAIL() << "Couldn't create " << mFileName << " for tests"; - } - } - - virtual void TearDown() { - } -}; - -TEST_F(ObbFileTest, ReadFailure) { - EXPECT_FALSE(mObbFile->readFrom(-1)) - << "No failure on invalid file descriptor"; -} - -TEST_F(ObbFileTest, WriteThenRead) { - const char* packageName = "com.example.obbfile"; - const int32_t versionNum = 1; - - mObbFile->setPackageName(String8(packageName)); - mObbFile->setVersion(versionNum); -#define SALT_SIZE 8 - unsigned char salt[SALT_SIZE] = {0x01, 0x10, 0x55, 0xAA, 0xFF, 0x00, 0x5A, 0xA5}; - EXPECT_TRUE(mObbFile->setSalt(salt, SALT_SIZE)) - << "Salt should be successfully set"; - - EXPECT_TRUE(mObbFile->writeTo(mFileName)) - << "couldn't write to fake .obb file"; - - mObbFile = new ObbFile(); - - EXPECT_TRUE(mObbFile->readFrom(mFileName)) - << "couldn't read from fake .obb file"; - - EXPECT_EQ(versionNum, mObbFile->getVersion()) - << "version didn't come out the same as it went in"; - const char* currentPackageName = mObbFile->getPackageName().string(); - EXPECT_STREQ(packageName, currentPackageName) - << "package name didn't come out the same as it went in"; - - size_t saltLen; - const unsigned char* newSalt = mObbFile->getSalt(&saltLen); - - EXPECT_EQ(sizeof(salt), saltLen) - << "salt sizes were not the same"; - - for (int i = 0; i < sizeof(salt); i++) { - EXPECT_EQ(salt[i], newSalt[i]) - << "salt character " << i << " should be equal"; - } - EXPECT_TRUE(memcmp(newSalt, salt, sizeof(salt)) == 0) - << "salts should be the same"; -} - -} diff --git a/libs/androidfw/tests/ZipUtils_test.cpp b/libs/androidfw/tests/ZipUtils_test.cpp deleted file mode 100644 index c6038b5..0000000 --- a/libs/androidfw/tests/ZipUtils_test.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 "ZipUtils_test" -#include <utils/Log.h> -#include <androidfw/ZipUtils.h> - -#include <gtest/gtest.h> - -#include <fcntl.h> -#include <string.h> - -namespace android { - -class ZipUtilsTest : public testing::Test { -protected: - virtual void SetUp() { - } - - virtual void TearDown() { - } -}; - -TEST_F(ZipUtilsTest, ZipTimeConvertSuccess) { - struct tm t; - - // 2011-06-29 14:40:40 - long when = 0x3EDD7514; - - ZipUtils::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/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp new file mode 100644 index 0000000..1f5d26c --- /dev/null +++ b/libs/hwui/AmbientShadow.cpp @@ -0,0 +1,308 @@ +/* + * 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 <math.h> +#include <utils/Log.h> +#include <utils/Vector.h> + +#include "AmbientShadow.h" +#include "Vertex.h" + +namespace android { +namespace uirenderer { + +/** + * Calculate the shadows as a triangle strips while alpha value as the + * shadow values. + * + * @param vertices The shadow caster's polygon, which is represented in a Vector3 + * array. + * @param vertexCount The length of caster's polygon in terms of number of + * vertices. + * @param rays The number of rays shooting out from the centroid. + * @param layers The number of rings outside the polygon. + * @param strength The darkness of the shadow, the higher, the darker. + * @param heightFactor The factor showing the higher the object, the lighter the + * shadow. + * @param geomFactor The factor scaling the geometry expansion along the normal. + * + * @param shadowVertexBuffer Return an floating point array of (x, y, a) + * triangle strips mode. + */ +void AmbientShadow::createAmbientShadow(const Vector3* vertices, int vertexCount, + int rays, int layers, float strength, float heightFactor, float geomFactor, + VertexBuffer& shadowVertexBuffer) { + + // Validate the inputs. + if (strength <= 0 || heightFactor <= 0 || layers <= 0 || rays <= 0 + || geomFactor <= 0) { +#if DEBUG_SHADOW + ALOGE("Invalid input for createAmbientShadow(), early return!"); +#endif + return; + } + int rings = layers + 1; + int size = rays * rings; + Vector2 centroid; + calculatePolygonCentroid(vertices, vertexCount, centroid); + + Vector<Vector2> dir; // TODO: use C++11 unique_ptr + dir.setCapacity(rays); + float rayDist[rays]; + float rayHeight[rays]; + calculateRayDirections(rays, dir.editArray()); + + // Calculate the length and height of the points along the edge. + // + // The math here is: + // Intersect each ray (starting from the centroid) with the polygon. + for (int i = 0; i < rays; i++) { + int edgeIndex; + float edgeFraction; + float rayDistance; + calculateIntersection(vertices, vertexCount, centroid, dir[i], edgeIndex, + edgeFraction, rayDistance); + rayDist[i] = rayDistance; + if (edgeIndex < 0 || edgeIndex >= vertexCount) { +#if DEBUG_SHADOW + ALOGE("Invalid edgeIndex!"); +#endif + edgeIndex = 0; + } + float h1 = vertices[edgeIndex].z; + float h2 = vertices[((edgeIndex + 1) % vertexCount)].z; + rayHeight[i] = h1 + edgeFraction * (h2 - h1); + } + + // The output buffer length basically is roughly rays * layers, but since we + // need triangle strips, so we need to duplicate vertices to accomplish that. + const int shadowVertexCount = (2 + rays + ((layers) * 2 * (rays + 1))); + AlphaVertex* shadowVertices = shadowVertexBuffer.alloc<AlphaVertex>(shadowVertexCount); + + // Calculate the vertex of the shadows. + // + // The math here is: + // Along the edges of the polygon, for each intersection point P (generated above), + // calculate the normal N, which should be perpendicular to the edge of the + // polygon (represented by the neighbor intersection points) . + // Shadow's vertices will be generated as : P + N * scale. + int currentIndex = 0; + for (int r = 0; r < layers; r++) { + int firstInLayer = currentIndex; + for (int i = 0; i < rays; i++) { + + Vector2 normal(1.0f, 0.0f); + calculateNormal(rays, i, dir.array(), rayDist, normal); + + float opacity = strength * (0.5f) / (1 + rayHeight[i] / heightFactor); + + // The vertex should be start from rayDist[i] then scale the + // normalizeNormal! + Vector2 intersection = dir[i] * rayDist[i] + centroid; + + // Use 2 rings' vertices to complete one layer's strip + for (int j = r; j < (r + 2); j++) { + float jf = j / (float)(rings - 1); + + float expansionDist = rayHeight[i] / heightFactor * geomFactor * jf; + AlphaVertex::set(&shadowVertices[currentIndex], + intersection.x + normal.x * expansionDist, + intersection.y + normal.y * expansionDist, + (1 - jf) * opacity); + currentIndex++; + } + } + + // From one layer to the next, we need to duplicate the vertex to + // continue as a single strip. + shadowVertices[currentIndex] = shadowVertices[firstInLayer]; + currentIndex++; + shadowVertices[currentIndex] = shadowVertices[firstInLayer + 1]; + currentIndex++; + } + + // After all rings are done, we need to jump into the polygon. + // In order to keep everything in a strip, we need to duplicate the last one + // of the rings and the first one inside the polygon. + int lastInRings = currentIndex - 1; + shadowVertices[currentIndex] = shadowVertices[lastInRings]; + currentIndex++; + + // We skip one and fill it back after we finish the internal triangles. + currentIndex++; + int firstInternal = currentIndex; + + // Combine the internal area of the polygon into a triangle strip, too. + // The basic idea is zig zag between the intersection points. + // 0 -> (n - 1) -> 1 -> (n - 2) ... + for (int k = 0; k < rays; k++) { + int i = k / 2; + if ((k & 1) == 1) { // traverse the inside in a zig zag pattern for strips + i = rays - i - 1; + } + float cast = rayDist[i] * (1 + rayHeight[i] / heightFactor); + float opacity = strength * (0.5f) / (1 + rayHeight[i] / heightFactor); + float t = rayDist[i]; + + AlphaVertex::set(&shadowVertices[currentIndex], dir[i].x * t + centroid.x, + dir[i].y * t + centroid.y, opacity); + currentIndex++; + } + + currentIndex = firstInternal - 1; + shadowVertices[currentIndex] = shadowVertices[firstInternal]; +} + +/** + * Calculate the centroid of a given polygon. + * + * @param vertices The shadow caster's polygon, which is represented in a + * straight Vector3 array. + * @param vertexCount The length of caster's polygon in terms of number of vertices. + * + * @param centroid Return the centroid of the polygon. + */ +void AmbientShadow::calculatePolygonCentroid(const Vector3* vertices, int vertexCount, + Vector2& centroid) { + float sumx = 0; + float sumy = 0; + int p1 = vertexCount - 1; + float area = 0; + for (int p2 = 0; p2 < vertexCount; p2++) { + float x1 = vertices[p1].x; + float y1 = vertices[p1].y; + float x2 = vertices[p2].x; + float y2 = vertices[p2].y; + float a = (x1 * y2 - x2 * y1); + sumx += (x1 + x2) * a; + sumy += (y1 + y2) * a; + area += a; + p1 = p2; + } + + if (area == 0) { +#if DEBUG_SHADOW + ALOGE("Area is 0!"); +#endif + centroid.x = vertices[0].x; + centroid.y = vertices[0].y; + } else { + centroid.x = sumx / (3 * area); + centroid.y = sumy / (3 * area); + } +} + +/** + * Generate an array of rays' direction vectors. + * + * @param rays The number of rays shooting out from the centroid. + * @param dir Return the array of ray vectors. + */ +void AmbientShadow::calculateRayDirections(int rays, Vector2* dir) { + float deltaAngle = 2 * M_PI / rays; + + for (int i = 0; i < rays; i++) { + dir[i].x = sinf(deltaAngle * i); + dir[i].y = cosf(deltaAngle * i); + } +} + +/** + * Calculate the intersection of a ray hitting the polygon. + * + * @param vertices The shadow caster's polygon, which is represented in a + * Vector3 array. + * @param vertexCount The length of caster's polygon in terms of number of vertices. + * @param start The starting point of the ray. + * @param dir The direction vector of the ray. + * + * @param outEdgeIndex Return the index of the segment (or index of the starting + * vertex) that ray intersect with. + * @param outEdgeFraction Return the fraction offset from the segment starting + * index. + * @param outRayDist Return the ray distance from centroid to the intersection. + */ +void AmbientShadow::calculateIntersection(const Vector3* vertices, int vertexCount, + const Vector2& start, const Vector2& dir, int& outEdgeIndex, + float& outEdgeFraction, float& outRayDist) { + float startX = start.x; + float startY = start.y; + float dirX = dir.x; + float dirY = dir.y; + // Start the search from the last edge from poly[len-1] to poly[0]. + int p1 = vertexCount - 1; + + for (int p2 = 0; p2 < vertexCount; p2++) { + float p1x = vertices[p1].x; + float p1y = vertices[p1].y; + float p2x = vertices[p2].x; + float p2y = vertices[p2].y; + + // The math here is derived from: + // f(t, v) = p1x * (1 - t) + p2x * t - (startX + dirX * v) = 0; + // g(t, v) = p1y * (1 - t) + p2y * t - (startY + dirY * v) = 0; + float div = (dirX * (p1y - p2y) + dirY * p2x - dirY * p1x); + if (div != 0) { + float t = (dirX * (p1y - startY) + dirY * startX - dirY * p1x) / (div); + if (t > 0 && t <= 1) { + float t2 = (p1x * (startY - p2y) + + p2x * (p1y - startY) + + startX * (p2y - p1y)) / div; + if (t2 > 0) { + outEdgeIndex = p1; + outRayDist = t2; + outEdgeFraction = t; + return; + } + } + } + p1 = p2; + } + return; +}; + +/** + * Calculate the normal at the intersection point between a ray and the polygon. + * + * @param rays The total number of rays. + * @param currentRayIndex The index of the ray which the normal is based on. + * @param dir The array of the all the rays directions. + * @param rayDist The pre-computed ray distances array. + * + * @param normal Return the normal. + */ +void AmbientShadow::calculateNormal(int rays, int currentRayIndex, + const Vector2* dir, const float* rayDist, Vector2& normal) { + int preIndex = (currentRayIndex - 1 + rays) % rays; + int postIndex = (currentRayIndex + 1) % rays; + Vector2 p1 = dir[preIndex] * rayDist[preIndex]; + Vector2 p2 = dir[postIndex] * rayDist[postIndex]; + + // Now the V (deltaX, deltaY) is the vector going CW around the poly. + Vector2 delta = p2 - p1; + if (delta.length() != 0) { + delta.normalize(); + // Calculate the normal , which is CCW 90 rotate to the V. + // 90 degrees CCW about z-axis: (x, y, z) -> (-y, x, z) + normal.x = -delta.y; + normal.y = delta.x; + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/AmbientShadow.h b/libs/hwui/AmbientShadow.h new file mode 100644 index 0000000..079bdb7 --- /dev/null +++ b/libs/hwui/AmbientShadow.h @@ -0,0 +1,57 @@ + +/* + * 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_AMBIENT_SHADOW_H +#define ANDROID_HWUI_AMBIENT_SHADOW_H + +#include "Debug.h" +#include "Vector.h" +#include "VertexBuffer.h" + +namespace android { +namespace uirenderer { + +/** + * AmbientShadow is used to calculate the ambient shadow value around a polygon. + * + * TODO: calculateIntersection() now is O(N*M), where N is the number of + * polygon's vertics and M is the number of rays. In fact, by staring tracing + * the vertex from the previous intersection, the algorithm can be O(N + M); + */ +class AmbientShadow { +public: + static void createAmbientShadow(const Vector3* poly, int polyLength, int rays, + int layers, float strength, float heightFactor, float geomFactor, + VertexBuffer& shadowVertexBuffer); + +private: + static void calculatePolygonCentroid(const Vector3* poly, int len, Vector2& centroid); + + static void calculateRayDirections(int rays, Vector2* dir); + + static void calculateIntersection(const Vector3* poly, int nbVertices, + const Vector2& start, const Vector2& dir, int& outEdgeIndex, + float& outEdgeFraction, float& outRayDist); + + static void calculateNormal(int rays, int currentRayIndex, const Vector2* dir, + const float* rayDist, Vector2& normal); +}; // AmbientShadow + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_AMBIENT_SHADOW_H diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 411c133..88f11db 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -4,12 +4,13 @@ include $(CLEAR_VARS) # Only build libhwui when USE_OPENGL_RENDERER is # defined in the current device/board configuration ifeq ($(USE_OPENGL_RENDERER),true) - LOCAL_SRC_FILES:= \ + LOCAL_SRC_FILES := \ utils/Blur.cpp \ utils/SortedListImpl.cpp \ thread/TaskManager.cpp \ font/CacheTexture.cpp \ font/Font.cpp \ + AmbientShadow.cpp \ AssetAtlas.cpp \ FontRenderer.cpp \ GammaFontRenderer.cpp \ @@ -37,14 +38,23 @@ ifeq ($(USE_OPENGL_RENDERER),true) ProgramCache.cpp \ RenderBufferCache.cpp \ ResourceCache.cpp \ + ShadowTessellator.cpp \ SkiaColorFilter.cpp \ SkiaShader.cpp \ Snapshot.cpp \ + SpotShadow.cpp \ + StatefulBaseRenderer.cpp \ Stencil.cpp \ Texture.cpp \ TextureCache.cpp \ TextDropShadowCache.cpp + # RenderThread stuff + LOCAL_SRC_FILES += \ + renderthread/CanvasContext.cpp \ + renderthread/RenderTask.cpp \ + renderthread/RenderThread.cpp + intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,) LOCAL_C_INCLUDES += \ @@ -58,6 +68,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) external/skia/include/utils LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES + LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_MODULE_CLASS := SHARED_LIBRARIES LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui LOCAL_MODULE := libhwui diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp index eb8bb9f..fddfe90 100644 --- a/libs/hwui/AssetAtlas.cpp +++ b/libs/hwui/AssetAtlas.cpp @@ -74,12 +74,12 @@ void AssetAtlas::terminate() { // Entries /////////////////////////////////////////////////////////////////////////////// -AssetAtlas::Entry* AssetAtlas::getEntry(SkBitmap* const bitmap) const { +AssetAtlas::Entry* AssetAtlas::getEntry(const SkBitmap* bitmap) const { ssize_t index = mEntries.indexOfKey(bitmap); return index >= 0 ? mEntries.valueAt(index) : NULL; } -Texture* AssetAtlas::getEntryTexture(SkBitmap* const bitmap) const { +Texture* AssetAtlas::getEntryTexture(const SkBitmap* bitmap) const { ssize_t index = mEntries.indexOfKey(bitmap); return index >= 0 ? mEntries.valueAt(index)->texture : NULL; } diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h index a28efc6..57c8a60 100644 --- a/libs/hwui/AssetAtlas.h +++ b/libs/hwui/AssetAtlas.h @@ -160,13 +160,13 @@ public: * 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; + Entry* getEntry(const SkBitmap* 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; + Texture* getEntryTexture(const SkBitmap* bitmap) const; /** * Returns the current generation id of the atlas. @@ -186,7 +186,7 @@ private: const bool mBlendKey; const bool mOpaqueKey; - KeyedVector<SkBitmap*, Entry*> mEntries; + KeyedVector<const SkBitmap*, Entry*> mEntries; }; // class AssetAtlas }; // namespace uirenderer diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index f8d3589..2e7990e 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -55,6 +55,7 @@ Caches::Caches(): Singleton<Caches>(), initProperties(); initStaticProperties(); initExtensions(); + initTempProperties(); mDebugLevel = readDebugLevel(); ALOGD("Enabling debug mode %d", mDebugLevel); @@ -473,7 +474,7 @@ bool Caches::unbindPixelBuffer() { // Meshes and textures /////////////////////////////////////////////////////////////////////////////// -void Caches::bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei stride) { +void Caches::bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) { if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) { GLuint slot = currentProgram->position; glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices); @@ -482,7 +483,7 @@ void Caches::bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei str } } -void Caches::bindTexCoordsVertexPointer(bool force, GLvoid* vertices, GLsizei stride) { +void Caches::bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) { if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) { GLuint slot = currentProgram->texCoords; glVertexAttribPointer(slot, 2, GL_FLOAT, GL_FALSE, stride, vertices); @@ -676,5 +677,36 @@ TextureVertex* Caches::getRegionMesh() { return mRegionMesh; } +/////////////////////////////////////////////////////////////////////////////// +// Temporary Properties +/////////////////////////////////////////////////////////////////////////////// + +void Caches::initTempProperties() { + propertyDirtyViewport = false; + propertyEnable3d = false; + propertyCameraDistance = 1.0f; + propertyShadowStrength = 0x3f; +} + +void Caches::setTempProperty(const char* name, const char* value) { + ALOGD("setting property %s to %s", name, value); + if (!strcmp(name, "enable3d")) { + propertyEnable3d = !strcmp(value, "true"); + propertyDirtyViewport = true; + ALOGD("enable3d = %d", propertyEnable3d); + return; + } else if (!strcmp(name, "cameraDistance")) { + propertyCameraDistance = fmin(fmax(atof(value), 0.001), 10); + propertyDirtyViewport = true; + ALOGD("camera dist multiplier = %.2f", propertyCameraDistance); + return; + } else if (!strcmp(name, "shadowStrength")) { + propertyShadowStrength = atoi(value); + ALOGD("shadow strength = 0x%x out of 0xff", propertyShadowStrength); + return; + } + ALOGD(" failed"); +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 282aee9..01e8d84 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -62,7 +62,7 @@ namespace uirenderer { static const uint32_t gMaxNumberOfQuads = 2048; // Generates simple and textured vertices -#define FV(x, y, u, v) { { x, y }, { u, v } } +#define FV(x, y, u, v) { x, y, u, v } // This array is never used directly but used as a memcpy source in the // OpenGLRenderer constructor @@ -208,13 +208,13 @@ public: * Binds an attrib to the specified float vertex pointer. * Assumes a stride of gMeshStride and a size of 2. */ - void bindPositionVertexPointer(bool force, GLvoid* vertices, GLsizei stride = gMeshStride); + void bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride = gMeshStride); /** * Binds an attrib to the specified float vertex pointer. * Assumes a stride of gMeshStride and a size of 2. */ - void bindTexCoordsVertexPointer(bool force, GLvoid* vertices, GLsizei stride = gMeshStride); + void bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride = gMeshStride); /** * Resets the vertex pointers. @@ -353,6 +353,14 @@ public: PFNGLLABELOBJECTEXTPROC setLabel; PFNGLGETOBJECTLABELEXTPROC getLabel; + // TEMPORARY properties + void initTempProperties(); + void setTempProperty(const char* name, const char* value); + bool propertyEnable3d; + bool propertyDirtyViewport; // flag set when dirtying the viewport + float propertyCameraDistance; + int propertyShadowStrength; + private: enum OverdrawColorSet { kColorSet_Default = 0, @@ -379,9 +387,9 @@ private: GLuint mCurrentBuffer; GLuint mCurrentIndicesBuffer; GLuint mCurrentPixelBuffer; - void* mCurrentPositionPointer; + const void* mCurrentPositionPointer; GLsizei mCurrentPositionStride; - void* mCurrentTexCoordsPointer; + const void* mCurrentTexCoordsPointer; GLsizei mCurrentTexCoordsStride; bool mTexCoordsArrayEnabled; diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h index 786f12a..d6dc6ad 100644 --- a/libs/hwui/Debug.h +++ b/libs/hwui/Debug.h @@ -85,6 +85,9 @@ // Turn on to highlight drawing batches and merged batches with different colors #define DEBUG_MERGE_BEHAVIOR 0 +// Turn on to enable debugging shadow +#define DEBUG_SHADOW 0 + #if DEBUG_INIT #define INIT_LOGD(...) ALOGD(__VA_ARGS__) #else diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index 3dcbd0b..fca3588 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -79,13 +79,13 @@ public: }; class DeferredDisplayList { + friend class DeferStateStruct; // used to give access to allocator public: DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) : mBounds(bounds), mAvoidOverdraw(avoidOverdraw) { clear(); } ~DeferredDisplayList() { clear(); } - void reset(const Rect& bounds) { mBounds.set(bounds); } enum OpBatchId { kOpBatch_None = 0, // Don't batch @@ -120,6 +120,8 @@ public: void addDrawOp(OpenGLRenderer& renderer, DrawOp* op); private: + DeferredDisplayList(const DeferredDisplayList& other); // disallow copy + DeferredDisplayState* createState() { return new (mAllocator) DeferredDisplayState(); } diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index bb6526e..a156341 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -14,7 +14,12 @@ * limitations under the License. */ +#define ATRACE_TAG ATRACE_TAG_VIEW + #include <SkCanvas.h> +#include <algorithm> + +#include <utils/Trace.h> #include "Debug.h" #include "DisplayList.h" @@ -65,11 +70,6 @@ void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) { void DisplayList::clearResources() { mDisplayListData = NULL; - mClipRectOp = NULL; - mSaveLayerOp = NULL; - mSaveOp = NULL; - mRestoreToCountOp = NULL; - delete mTransformMatrix; delete mTransformCamera; delete mTransformMatrix3D; @@ -91,7 +91,7 @@ void DisplayList::clearResources() { } for (size_t i = 0; i < mOwnedBitmapResources.size(); i++) { - SkBitmap* bitmap = mOwnedBitmapResources.itemAt(i); + const SkBitmap* bitmap = mOwnedBitmapResources.itemAt(i); caches.resourceCache.decrementRefcountLocked(bitmap); caches.resourceCache.destructorLocked(bitmap); } @@ -168,33 +168,22 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde return; } - // allocate reusable ops for state-deferral - LinearAllocator& alloc = mDisplayListData->allocator; - mClipRectOp = new (alloc) ClipRectOp(); - mSaveLayerOp = new (alloc) SaveLayerOp(); - mSaveOp = new (alloc) SaveOp(); - mRestoreToCountOp = new (alloc) RestoreToCountOp(); - if (CC_UNLIKELY(!mSaveOp)) { // temporary debug logging - ALOGW("Error: %s's SaveOp not allocated, size %d", getName(), mSize); - CRASH(); - } - mFunctorCount = recorder.getFunctorCount(); Caches& caches = Caches::getInstance(); caches.registerFunctors(mFunctorCount); caches.resourceCache.lock(); - const Vector<SkBitmap*>& bitmapResources = recorder.getBitmapResources(); + const Vector<const SkBitmap*>& bitmapResources = recorder.getBitmapResources(); for (size_t i = 0; i < bitmapResources.size(); i++) { - SkBitmap* resource = bitmapResources.itemAt(i); + const SkBitmap* resource = bitmapResources.itemAt(i); mBitmapResources.add(resource); caches.resourceCache.incrementRefcountLocked(resource); } - const Vector<SkBitmap*> &ownedBitmapResources = recorder.getOwnedBitmapResources(); + const Vector<const SkBitmap*>& ownedBitmapResources = recorder.getOwnedBitmapResources(); for (size_t i = 0; i < ownedBitmapResources.size(); i++) { - SkBitmap* resource = ownedBitmapResources.itemAt(i); + const SkBitmap* resource = ownedBitmapResources.itemAt(i); mOwnedBitmapResources.add(resource); caches.resourceCache.incrementRefcountLocked(resource); } @@ -206,9 +195,9 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde caches.resourceCache.incrementRefcountLocked(resource); } - const Vector<Res_png_9patch*>& patchResources = recorder.getPatchResources(); + const Vector<const Res_png_9patch*>& patchResources = recorder.getPatchResources(); for (size_t i = 0; i < patchResources.size(); i++) { - Res_png_9patch* resource = patchResources.itemAt(i); + const Res_png_9patch* resource = patchResources.itemAt(i); mPatchResources.add(resource); caches.resourceCache.incrementRefcountLocked(resource); } @@ -220,7 +209,7 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde caches.resourceCache.incrementRefcountLocked(resource); } - const SortedVector<SkPath*>& sourcePaths = recorder.getSourcePaths(); + const SortedVector<const SkPath*>& sourcePaths = recorder.getSourcePaths(); for (size_t i = 0; i < sourcePaths.size(); i++) { mSourcePaths.add(sourcePaths.itemAt(i)); caches.resourceCache.incrementRefcountLocked(sourcePaths.itemAt(i)); @@ -249,10 +238,13 @@ void DisplayList::init() { mRight = 0; mBottom = 0; mClipToBounds = true; + mIsContainedVolume = true; + mProjectToContainedVolume = false; mAlpha = 1; mHasOverlappingRendering = true; mTranslationX = 0; mTranslationY = 0; + mTranslationZ = 0; mRotation = 0; mRotationX = 0; mRotationY= 0; @@ -320,27 +312,38 @@ void DisplayList::updateMatrix() { mPivotY = mPrevHeight / 2.0f; } } - if ((mMatrixFlags & ROTATION_3D) == 0) { + if (!Caches::getInstance().propertyEnable3d && (mMatrixFlags & ROTATION_3D) == 0) { mTransformMatrix->setTranslate(mTranslationX, mTranslationY); mTransformMatrix->preRotate(mRotation, mPivotX, mPivotY); mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY); } else { - if (!mTransformCamera) { - mTransformCamera = new Sk3DView(); - mTransformMatrix3D = new SkMatrix(); + if (Caches::getInstance().propertyEnable3d) { + mTransform.loadTranslate(mPivotX + mTranslationX, mPivotY + mTranslationY, + mTranslationZ); + mTransform.rotate(mRotationX, 1, 0, 0); + mTransform.rotate(mRotationY, 0, 1, 0); + mTransform.rotate(mRotation, 0, 0, 1); + mTransform.scale(mScaleX, mScaleY, 1); + mTransform.translate(-mPivotX, -mPivotY); + } else { + /* TODO: support this old transform approach, based on API level */ + if (!mTransformCamera) { + mTransformCamera = new Sk3DView(); + mTransformMatrix3D = new SkMatrix(); + } + mTransformMatrix->reset(); + mTransformCamera->save(); + mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY); + mTransformCamera->rotateX(mRotationX); + mTransformCamera->rotateY(mRotationY); + mTransformCamera->rotateZ(-mRotation); + mTransformCamera->getMatrix(mTransformMatrix3D); + mTransformMatrix3D->preTranslate(-mPivotX, -mPivotY); + mTransformMatrix3D->postTranslate(mPivotX + mTranslationX, + mPivotY + mTranslationY); + mTransformMatrix->postConcat(*mTransformMatrix3D); + mTransformCamera->restore(); } - mTransformMatrix->reset(); - mTransformCamera->save(); - mTransformMatrix->preScale(mScaleX, mScaleY, mPivotX, mPivotY); - mTransformCamera->rotateX(mRotationX); - mTransformCamera->rotateY(mRotationY); - mTransformCamera->rotateZ(-mRotation); - mTransformCamera->getMatrix(mTransformMatrix3D); - mTransformMatrix3D->preTranslate(-mPivotX, -mPivotY); - mTransformMatrix3D->postTranslate(mPivotX + mTranslationX, - mPivotY + mTranslationY); - mTransformMatrix->postConcat(*mTransformMatrix3D); - mTransformCamera->restore(); } } mMatrixDirty = false; @@ -415,10 +418,19 @@ void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler, renderer.concatMatrix(mAnimationMatrix); } if (mMatrixFlags != 0) { - if (mMatrixFlags == TRANSLATION) { - renderer.translate(mTranslationX, mTranslationY); + if (Caches::getInstance().propertyEnable3d) { + if (mMatrixFlags == TRANSLATION) { + renderer.translate(mTranslationX, mTranslationY, mTranslationZ); + } else { + renderer.concatMatrix(mTransform); + } } else { - renderer.concatMatrix(mTransformMatrix); + // avoid setting translationZ, use SkMatrix + if (mMatrixFlags == TRANSLATION) { + renderer.translate(mTranslationX, mTranslationY, 0); + } else { + renderer.concatMatrix(mTransformMatrix); + } } } bool clipToBoundsNeeded = mCaching ? false : mClipToBounds; @@ -436,14 +448,131 @@ void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler, saveFlags |= SkCanvas::kClipToLayer_SaveFlag; clipToBoundsNeeded = false; // clipping done by saveLayer } - handler(mSaveLayerOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, - mAlpha * 255, SkXfermode::kSrcOver_Mode, saveFlags), PROPERTY_SAVECOUNT, - mClipToBounds); + + SaveLayerOp* op = new (handler.allocator()) SaveLayerOp( + 0, 0, mRight - mLeft, mBottom - mTop, + mAlpha * 255, SkXfermode::kSrcOver_Mode, saveFlags); + handler(op, PROPERTY_SAVECOUNT, mClipToBounds); } } if (clipToBoundsNeeded) { - handler(mClipRectOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op), - PROPERTY_SAVECOUNT, mClipToBounds); + ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0, + mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op); + handler(op, PROPERTY_SAVECOUNT, mClipToBounds); + } +} + +/** + * Apply property-based transformations to input matrix + */ +void DisplayList::applyViewPropertyTransforms(mat4& matrix) { + if (mLeft != 0 || mTop != 0) { + matrix.translate(mLeft, mTop); + } + if (mStaticMatrix) { + mat4 stat(*mStaticMatrix); + matrix.multiply(stat); + } else if (mAnimationMatrix) { + mat4 anim(*mAnimationMatrix); + matrix.multiply(anim); + } + if (mMatrixFlags != 0) { + updateMatrix(); + if (mMatrixFlags == TRANSLATION) { + matrix.translate(mTranslationX, mTranslationY, mTranslationZ); + } else { + if (Caches::getInstance().propertyEnable3d) { + matrix.multiply(mTransform); + } else { + mat4 temp(*mTransformMatrix); + matrix.multiply(temp); + } + } + } +} + +/** + * Organizes the DisplayList hierarchy to prepare for Z-based draw order. + * + * This should be called before a call to defer() or drawDisplayList() + * + * Each DisplayList that serves as a 3d root builds its list of composited children, + * which are flagged to not draw in the standard draw loop. + */ +void DisplayList::computeOrdering() { + ATRACE_CALL(); + if (mDisplayListData == NULL) return; + + for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) { + DrawDisplayListOp* childOp = mDisplayListData->children[i]; + childOp->mDisplayList->computeOrderingImpl(childOp, + &m3dNodes, &mat4::identity(), + &mProjectedNodes, &mat4::identity()); + } +} + +void DisplayList::computeOrderingImpl( + DrawDisplayListOp* opState, + Vector<ZDrawDisplayListOpPair>* compositedChildrenOf3dRoot, + const mat4* transformFrom3dRoot, + Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface, + const mat4* transformFromProjectionSurface) { + + // TODO: should avoid this calculation in most cases + // TODO: just calculate single matrix, down to all leaf composited elements + Matrix4 localTransformFrom3dRoot(*transformFrom3dRoot); + localTransformFrom3dRoot.multiply(opState->mTransformFromParent); + Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface); + localTransformFromProjectionSurface.multiply(opState->mTransformFromParent); + + if (mTranslationZ != 0.0f) { // TODO: other signals for 3d compositing, such as custom matrix4 + // composited 3d layer, flag for out of order draw and save matrix... + opState->mSkipInOrderDraw = true; + opState->mTransformFromCompositingAncestor.load(localTransformFrom3dRoot); + + // ... and insert into current 3d root, keyed with pivot z for later sorting + Vector3 pivot(mPivotX, mPivotY, 0.0f); + mat4 totalTransform(localTransformFrom3dRoot); + applyViewPropertyTransforms(totalTransform); + totalTransform.mapPoint3d(pivot); + compositedChildrenOf3dRoot->add(ZDrawDisplayListOpPair(pivot.z, opState)); + } else if (mProjectToContainedVolume) { + // composited projectee, flag for out of order draw, save matrix, and store in proj surface + opState->mSkipInOrderDraw = true; + opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface); + compositedChildrenOfProjectionSurface->add(opState); + } else { + // standard in order draw + opState->mSkipInOrderDraw = false; + } + + m3dNodes.clear(); + if (mIsContainedVolume) { + // create a new 3d space for descendents by collecting them + compositedChildrenOf3dRoot = &m3dNodes; + transformFrom3dRoot = &mat4::identity(); + } else { + applyViewPropertyTransforms(localTransformFrom3dRoot); + transformFrom3dRoot = &localTransformFrom3dRoot; + } + + mProjectedNodes.clear(); + if (mDisplayListData != NULL && mDisplayListData->projectionIndex >= 0) { + // create a new projection surface for descendents by collecting them + compositedChildrenOfProjectionSurface = &mProjectedNodes; + transformFromProjectionSurface = &mat4::identity(); + } else { + applyViewPropertyTransforms(localTransformFromProjectionSurface); + transformFromProjectionSurface = &localTransformFromProjectionSurface; + } + + if (mDisplayListData != NULL && mDisplayListData->children.size() > 0) { + for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) { + DrawDisplayListOp* childOp = mDisplayListData->children[i]; + childOp->mDisplayList->computeOrderingImpl(childOp, + compositedChildrenOf3dRoot, transformFrom3dRoot, + compositedChildrenOfProjectionSurface, transformFromProjectionSurface); + } } } @@ -454,6 +583,8 @@ public: inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) { operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds); } + inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); } + private: DeferStateStruct& mDeferStruct; const int mLevel; @@ -474,6 +605,8 @@ public: #endif operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds); } + inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); } + private: ReplayStateStruct& mReplayStruct; const int mLevel; @@ -490,11 +623,77 @@ void DisplayList::replay(ReplayStateStruct& replayStruct, const int level) { replayStruct.mDrawGlStatus); } +template <class T> +void DisplayList::iterate3dChildren(ChildrenSelectMode mode, OpenGLRenderer& renderer, + T& handler, const int level) { + if (m3dNodes.size() == 0 || + (mode == kNegativeZChildren && m3dNodes[0].key > 0.0f) || + (mode == kPositiveZChildren && m3dNodes[m3dNodes.size() - 1].key < 0.0f)) { + // no 3d children to draw + return; + } + + LinearAllocator& alloc = handler.allocator(); + ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, mWidth, mHeight, + SkRegion::kIntersect_Op); // clip to 3d root bounds for now + handler(clipOp, PROPERTY_SAVECOUNT, mClipToBounds); + int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + + for (size_t i = 0; i < m3dNodes.size(); i++) { + const float zValue = m3dNodes[i].key; + DrawDisplayListOp* childOp = m3dNodes[i].value; + + if (mode == kPositiveZChildren && zValue < 0.0f) continue; + if (mode == kNegativeZChildren && zValue > 0.0f) break; + + if (mode == kPositiveZChildren && zValue > 0.0f) { + /* draw shadow with parent matrix applied, passing in the child's total matrix + * + * TODO: + * -determine and pass background shape (and possibly drawable alpha) + * -view must opt-in to shadows + * -consider shadows for other content + * -inform shadow system of ancestor transform (for use in lighting) + */ + mat4 shadowMatrix(childOp->mTransformFromCompositingAncestor); + childOp->mDisplayList->applyViewPropertyTransforms(shadowMatrix); + DisplayListOp* shadowOp = new (alloc) DrawShadowOp(shadowMatrix, + childOp->mDisplayList->mAlpha, + childOp->mDisplayList->getWidth(), childOp->mDisplayList->getHeight()); + handler(shadowOp, PROPERTY_SAVECOUNT, mClipToBounds); + } + + renderer.concatMatrix(childOp->mTransformFromCompositingAncestor); + childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone + handler(childOp, renderer.getSaveCount() - 1, mClipToBounds); + childOp->mSkipInOrderDraw = true; + } + handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, mClipToBounds); +} + +template <class T> +void DisplayList::iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level) { + LinearAllocator& alloc = handler.allocator(); + ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, mWidth, mHeight, + SkRegion::kReplace_Op); // clip to projection surface root bounds + handler(clipOp, PROPERTY_SAVECOUNT, mClipToBounds); + int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + + for (size_t i = 0; i < mProjectedNodes.size(); i++) { + DrawDisplayListOp* childOp = mProjectedNodes[i]; + renderer.concatMatrix(childOp->mTransformFromCompositingAncestor); + childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone + handler(childOp, renderer.getSaveCount() - 1, mClipToBounds); + childOp->mSkipInOrderDraw = true; + } + handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, mClipToBounds); +} + /** * This function serves both defer and replay modes, and will organize the displayList's component * operations for a single frame: * - * Every 'simple' operation that affects just the matrix and alpha (or other factors of + * Every 'simple' state operation that affects just the matrix and alpha (or other factors of * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom * defer logic) and operations in displayListOps are issued through the 'handler' which handles the * defer vs replay logic, per operation @@ -517,8 +716,9 @@ void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) clipRect->right, clipRect->bottom); #endif + LinearAllocator& alloc = handler.allocator(); int restoreTo = renderer.getSaveCount(); - handler(mSaveOp->reinit(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), + handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), PROPERTY_SAVECOUNT, mClipToBounds); DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "", @@ -526,30 +726,39 @@ void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) setViewProperties<T>(renderer, handler, level + 1); - if (mClipToBounds && renderer.quickRejectNoScissor(0, 0, mWidth, mHeight)) { - DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo); - handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT, mClipToBounds); - renderer.restoreToCount(restoreTo); - renderer.setOverrideLayerAlpha(1.0f); - return; - } + bool quickRejected = mClipToBounds && renderer.quickRejectConservative(0, 0, mWidth, mHeight); + if (!quickRejected) { + // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order) + std::stable_sort(m3dNodes.begin(), m3dNodes.end()); - DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); - int saveCount = renderer.getSaveCount() - 1; - for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { - DisplayListOp *op = mDisplayListData->displayListOps[i]; + // for 3d root, draw children with negative z values + iterate3dChildren(kNegativeZChildren, renderer, handler, level); + + DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); + const int saveCountOffset = renderer.getSaveCount() - 1; + const int projectionIndex = mDisplayListData->projectionIndex; + for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { + DisplayListOp *op = mDisplayListData->displayListOps[i]; #if DEBUG_DISPLAY_LIST - op->output(level + 1); + op->output(level + 1); #endif - logBuffer.writeCommand(level, op->name()); - handler(op, saveCount, mClipToBounds); + logBuffer.writeCommand(level, op->name()); + handler(op, saveCountOffset, mClipToBounds); + + if (CC_UNLIKELY(i == projectionIndex && mProjectedNodes.size() > 0)) { + iterateProjectedChildren(renderer, handler, level); + } + } + + // for 3d root, draw children with positive z values + iterate3dChildren(kPositiveZChildren, renderer, handler, level); } DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo); - handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT, mClipToBounds); - renderer.restoreToCount(restoreTo); + handler(new (alloc) RestoreToCountOp(restoreTo), + PROPERTY_SAVECOUNT, mClipToBounds); renderer.setOverrideLayerAlpha(1.0f); } diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index 1cd5f1c..5399185 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -26,6 +26,7 @@ #include <private/hwui/DrawGlInfo.h> +#include <utils/KeyedVector.h> #include <utils/LinearAllocator.h> #include <utils/RefBase.h> #include <utils/SortedVector.h> @@ -37,6 +38,8 @@ #include <androidfw/ResourceTypes.h> #include "Debug.h" +#include "Matrix.h" +#include "DeferredDisplayList.h" #define TRANSLATION 0x0001 #define ROTATION 0x0002 @@ -65,36 +68,76 @@ class ClipRectOp; class SaveLayerOp; class SaveOp; class RestoreToCountOp; +class DrawDisplayListOp; -struct DeferStateStruct { - DeferStateStruct(DeferredDisplayList& deferredList, OpenGLRenderer& renderer, int replayFlags) - : mDeferredList(deferredList), mRenderer(renderer), mReplayFlags(replayFlags) {} - DeferredDisplayList& mDeferredList; +/** + * Holds data used in the playback a tree of DisplayLists. + */ +class PlaybackStateStruct { +protected: + PlaybackStateStruct(OpenGLRenderer& renderer, int replayFlags, LinearAllocator* allocator) + : mRenderer(renderer), mReplayFlags(replayFlags), mAllocator(allocator){} + +public: OpenGLRenderer& mRenderer; const int mReplayFlags; + + // Allocator with the lifetime of a single frame. + // replay uses an Allocator owned by the struct, while defer shares the DeferredDisplayList's Allocator + LinearAllocator * const mAllocator; }; -struct ReplayStateStruct { +class DeferStateStruct : public PlaybackStateStruct { +public: + DeferStateStruct(DeferredDisplayList& deferredList, OpenGLRenderer& renderer, int replayFlags) + : PlaybackStateStruct(renderer, replayFlags, &(deferredList.mAllocator)), + mDeferredList(deferredList) {} + + DeferredDisplayList& mDeferredList; +}; + +class ReplayStateStruct : public PlaybackStateStruct { +public: ReplayStateStruct(OpenGLRenderer& renderer, Rect& dirty, int replayFlags) - : mRenderer(renderer), mDirty(dirty), mReplayFlags(replayFlags), - mDrawGlStatus(DrawGlInfo::kStatusDone) {} - OpenGLRenderer& mRenderer; + : PlaybackStateStruct(renderer, replayFlags, &mReplayAllocator), + mDirty(dirty), mDrawGlStatus(DrawGlInfo::kStatusDone) {} + Rect& mDirty; - const int mReplayFlags; status_t mDrawGlStatus; + LinearAllocator mReplayAllocator; }; /** - * Refcounted structure that holds data used in display list stream + * Refcounted structure that holds the list of commands used in display list stream. */ class DisplayListData : public LightRefBase<DisplayListData> { public: + DisplayListData() : projectionIndex(-1) {} + // allocator into which all ops were allocated LinearAllocator allocator; + + // pointers to all ops within display list, pointing into allocator data Vector<DisplayListOp*> displayListOps; + + // list of children display lists for quick, non-drawing traversal + Vector<DrawDisplayListOp*> children; + + // index of DisplayListOp restore, after which projected descendents should be drawn + int projectionIndex; + Matrix4 projectionTransform; }; /** - * Replays recorded drawing commands. + * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties. + * + * Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording + * functionality is split between DisplayListRenderer (which manages the recording), DisplayListData + * (which holds the actual data), and DisplayList (which holds properties and performs playback onto + * a renderer). + * + * Note that DisplayListData is swapped out from beneath an individual DisplayList when a view's + * recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay + * attached. */ class DisplayList { public: @@ -106,17 +149,17 @@ public: kReplayFlag_ClipChildren = 0x1 }; - ANDROID_API size_t getSize(); ANDROID_API static void destroyDisplayListDeferred(DisplayList* displayList); ANDROID_API static void outputLogBuffer(int fd); void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false); + void computeOrdering(); void defer(DeferStateStruct& deferStruct, const int level); void replay(ReplayStateStruct& replayStruct, const int level); - void output(uint32_t level = 0); + ANDROID_API void output(uint32_t level = 1); ANDROID_API void reset(); @@ -147,6 +190,14 @@ public: mClipToBounds = clipToBounds; } + void setIsContainedVolume(bool isContainedVolume) { + mIsContainedVolume = isContainedVolume; + } + + void setProjectToContainedVolume(bool shouldProject) { + mProjectToContainedVolume = shouldProject; + } + void setStaticMatrix(SkMatrix* matrix) { delete mStaticMatrix; mStaticMatrix = new SkMatrix(*matrix); @@ -188,12 +239,7 @@ public: void setTranslationX(float translationX) { if (translationX != mTranslationX) { mTranslationX = translationX; - mMatrixDirty = true; - if (mTranslationX == 0.0f && mTranslationY == 0.0f) { - mMatrixFlags &= ~TRANSLATION; - } else { - mMatrixFlags |= TRANSLATION; - } + onTranslationUpdate(); } } @@ -204,12 +250,7 @@ public: void setTranslationY(float translationY) { if (translationY != mTranslationY) { mTranslationY = translationY; - mMatrixDirty = true; - if (mTranslationX == 0.0f && mTranslationY == 0.0f) { - mMatrixFlags &= ~TRANSLATION; - } else { - mMatrixFlags |= TRANSLATION; - } + onTranslationUpdate(); } } @@ -217,6 +258,17 @@ public: return mTranslationY; } + void setTranslationZ(float translationZ) { + if (translationZ != mTranslationZ) { + mTranslationZ = translationZ; + onTranslationUpdate(); + } + } + + float getTranslationZ() const { + return mTranslationZ; + } + void setRotation(float rotation) { if (rotation != mRotation) { mRotation = rotation; @@ -454,12 +506,43 @@ public: } private: + typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair; + + enum ChildrenSelectMode { + kNegativeZChildren, + kPositiveZChildren + }; + + void onTranslationUpdate() { + mMatrixDirty = true; + if (mTranslationX == 0.0f && mTranslationY == 0.0f && mTranslationZ == 0.0f) { + mMatrixFlags &= ~TRANSLATION; + } else { + mMatrixFlags |= TRANSLATION; + } + } + void outputViewProperties(const int level); + void applyViewPropertyTransforms(mat4& matrix); + + void computeOrderingImpl(DrawDisplayListOp* opState, + Vector<ZDrawDisplayListOpPair>* compositedChildrenOf3dRoot, + const mat4* transformFrom3dRoot, + Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface, + const mat4* transformFromProjectionSurface); + template <class T> inline void setViewProperties(OpenGLRenderer& renderer, T& handler, const int level); template <class T> + inline void iterate3dChildren(ChildrenSelectMode mode, OpenGLRenderer& renderer, + T& handler, const int level); + + template <class T> + inline void iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level); + + template <class T> inline void iterate(OpenGLRenderer& renderer, T& handler, const int level); void init(); @@ -482,16 +565,16 @@ private: const char* mText; }; - Vector<SkBitmap*> mBitmapResources; - Vector<SkBitmap*> mOwnedBitmapResources; + Vector<const SkBitmap*> mBitmapResources; + Vector<const SkBitmap*> mOwnedBitmapResources; Vector<SkiaColorFilter*> mFilterResources; - Vector<Res_png_9patch*> mPatchResources; + Vector<const Res_png_9patch*> mPatchResources; - Vector<SkPaint*> mPaints; - Vector<SkPath*> mPaths; - SortedVector<SkPath*> mSourcePaths; - Vector<SkRegion*> mRegions; - Vector<SkMatrix*> mMatrices; + Vector<const SkPaint*> mPaints; + Vector<const SkPath*> mPaths; + SortedVector<const SkPath*> mSourcePaths; + Vector<const SkRegion*> mRegions; + Vector<const SkMatrix*> mMatrices; Vector<SkiaShader*> mShaders; Vector<Layer*> mLayers; @@ -505,11 +588,13 @@ private: String8 mName; bool mDestroyed; // used for debugging crash, TODO: remove once invalid state crash fixed - // View properties + // Rendering properties bool mClipToBounds; + bool mIsContainedVolume; + bool mProjectToContainedVolume; float mAlpha; bool mHasOverlappingRendering; - float mTranslationX, mTranslationY; + float mTranslationX, mTranslationY, mTranslationZ; float mRotation, mRotationX, mRotationY; float mScaleX, mScaleY; float mPivotX, mPivotY; @@ -526,23 +611,18 @@ private: SkMatrix* mTransformMatrix3D; SkMatrix* mStaticMatrix; SkMatrix* mAnimationMatrix; + Matrix4 mTransform; bool mCaching; /** - * State operations - needed to defer displayList property operations (for example, when setting - * an alpha causes a SaveLayerAlpha to occur). These operations point into mDisplayListData's - * allocation, or null if uninitialized. - * - * These are initialized (via friend re-constructors) when a displayList is issued in either - * replay or deferred mode. If replaying, the ops are not used until the next frame. If - * deferring, the ops may be stored in the DeferredDisplayList to be played back a second time. - * - * They should be used at most once per frame (one call to 'iterate') to avoid overwriting data + * Draw time state - these properties are only set and used during rendering */ - ClipRectOp* mClipRectOp; - SaveLayerOp* mSaveLayerOp; - SaveOp* mSaveOp; - RestoreToCountOp* mRestoreToCountOp; + + // for 3d roots, contains a z sorted list of all children items + Vector<ZDrawDisplayListOpPair> m3dNodes; + + // for projection surfaces, contains a list of all children items + Vector<DrawDisplayListOp*> mProjectedNodes; }; // class DisplayList }; // namespace uirenderer diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 326805a..e3d4e2d 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -111,7 +111,7 @@ public: class DrawOp : public DisplayListOp { friend class MergingDrawBatch; public: - DrawOp(SkPaint* paint) + DrawOp(const SkPaint* paint) : mPaint(paint), mQuickRejected(false) {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, @@ -182,13 +182,16 @@ public: } inline float strokeWidthOutset() { - float width = mPaint->getStrokeWidth(); - if (width == 0) return 0.5f; // account for hairline - return width * 0.5f; + // since anything AA stroke with less than 1.0 pixel width is drawn with an alpha-reduced + // 1.0 stroke, treat 1.0 as minimum. + + // TODO: it would be nice if this could take scale into account, but scale isn't stable + // since higher levels of the view hierarchy can change scale out from underneath it. + return fmaxf(mPaint->getStrokeWidth(), 1) * 0.5f; } protected: - SkPaint* getPaint(OpenGLRenderer& renderer) { + const SkPaint* getPaint(OpenGLRenderer& renderer) { return renderer.filterPaint(mPaint); } @@ -209,22 +212,22 @@ protected: } - SkPaint* mPaint; // should be accessed via getPaint() when applying + const SkPaint* mPaint; // should be accessed via getPaint() when applying bool mQuickRejected; }; class DrawBoundedOp : public DrawOp { public: - DrawBoundedOp(float left, float top, float right, float bottom, SkPaint* paint) + DrawBoundedOp(float left, float top, float right, float bottom, const SkPaint* paint) : DrawOp(paint), mLocalBounds(left, top, right, bottom) {} - DrawBoundedOp(const Rect& localBounds, SkPaint* paint) + DrawBoundedOp(const Rect& localBounds, const 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) - DrawBoundedOp(const float* points, int count, SkPaint* paint) + DrawBoundedOp(const float* points, int count, const SkPaint* paint) : DrawOp(paint), mLocalBounds(points[0], points[1], points[0], points[1]) { for (int i = 2; i < count; i += 2) { mLocalBounds.left = fminf(mLocalBounds.left, points[i]); @@ -235,7 +238,7 @@ public: } // default empty constructor for bounds, to be overridden in child constructor body - DrawBoundedOp(SkPaint* paint): DrawOp(paint) { } + DrawBoundedOp(const SkPaint* paint): DrawOp(paint) { } bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) { localBounds.set(mLocalBounds); @@ -259,7 +262,6 @@ protected: /////////////////////////////////////////////////////////////////////////////// class SaveOp : public StateOp { - friend class DisplayList; // give DisplayList private constructor/reinit access public: SaveOp(int flags) : mFlags(flags) {} @@ -292,7 +294,6 @@ private: }; class RestoreToCountOp : public StateOp { - friend class DisplayList; // give DisplayList private constructor/reinit access public: RestoreToCountOp(int count) : mCount(count) {} @@ -325,7 +326,6 @@ private: }; class SaveLayerOp : public StateOp { - friend class DisplayList; // give DisplayList private constructor/reinit access public: SaveLayerOp(float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode, int flags) @@ -456,7 +456,7 @@ private: class SetMatrixOp : public StateOp { public: - SetMatrixOp(SkMatrix* matrix) + SetMatrixOp(const SkMatrix* matrix) : mMatrix(matrix) {} virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { @@ -474,12 +474,12 @@ public: virtual const char* name() { return "SetMatrix"; } private: - SkMatrix* mMatrix; + const SkMatrix* mMatrix; }; class ConcatMatrixOp : public StateOp { public: - ConcatMatrixOp(SkMatrix* matrix) + ConcatMatrixOp(const SkMatrix* matrix) : mMatrix(matrix) {} virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { @@ -493,7 +493,7 @@ public: virtual const char* name() { return "ConcatMatrix"; } private: - SkMatrix* mMatrix; + const SkMatrix* mMatrix; }; class ClipOp : public StateOp { @@ -521,7 +521,6 @@ protected: }; class ClipRectOp : public ClipOp { - friend class DisplayList; // give DisplayList private constructor/reinit access public: ClipRectOp(float left, float top, float right, float bottom, SkRegion::Op op) : ClipOp(op), mArea(left, top, right, bottom) {} @@ -552,7 +551,7 @@ private: class ClipPathOp : public ClipOp { public: - ClipPathOp(SkPath* path, SkRegion::Op op) + ClipPathOp(const SkPath* path, SkRegion::Op op) : ClipOp(op), mPath(path) {} virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { @@ -568,12 +567,12 @@ public: virtual const char* name() { return "ClipPath"; } private: - SkPath* mPath; + const SkPath* mPath; }; class ClipRegionOp : public ClipOp { public: - ClipRegionOp(SkRegion* region, SkRegion::Op op) + ClipRegionOp(const SkRegion* region, SkRegion::Op op) : ClipOp(op), mRegion(region) {} virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { @@ -589,7 +588,7 @@ public: virtual const char* name() { return "ClipRegion"; } private: - SkRegion* mRegion; + const SkRegion* mRegion; }; class ResetShaderOp : public StateOp { @@ -729,7 +728,7 @@ private: class DrawBitmapOp : public DrawBoundedOp { public: - DrawBitmapOp(SkBitmap* bitmap, float left, float top, SkPaint* paint) + DrawBitmapOp(const SkBitmap* bitmap, float left, float top, const SkPaint* paint) : DrawBoundedOp(left, top, left + bitmap->width(), top + bitmap->height(), paint), mBitmap(bitmap), mAtlas(Caches::getInstance().assetAtlas) { mEntry = mAtlas.getEntry(bitmap); @@ -828,7 +827,7 @@ public: const SkBitmap* bitmap() { return mBitmap; } protected: - SkBitmap* mBitmap; + const SkBitmap* mBitmap; const AssetAtlas& mAtlas; uint32_t mEntryGenerationId; AssetAtlas::Entry* mEntry; @@ -837,7 +836,7 @@ protected: class DrawBitmapMatrixOp : public DrawBoundedOp { public: - DrawBitmapMatrixOp(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) + DrawBitmapMatrixOp(const SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint) : DrawBoundedOp(paint), mBitmap(bitmap), mMatrix(matrix) { mLocalBounds.set(0, 0, bitmap->width(), bitmap->height()); const mat4 transform(*matrix); @@ -860,14 +859,15 @@ public: } private: - SkBitmap* mBitmap; - SkMatrix* mMatrix; + const SkBitmap* mBitmap; + const SkMatrix* mMatrix; }; class DrawBitmapRectOp : public DrawBoundedOp { public: - DrawBitmapRectOp(SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, - float dstLeft, float dstTop, float dstRight, float dstBottom, SkPaint* paint) + DrawBitmapRectOp(const SkBitmap* bitmap, + float srcLeft, float srcTop, float srcRight, float srcBottom, + float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) : DrawBoundedOp(dstLeft, dstTop, dstRight, dstBottom, paint), mBitmap(bitmap), mSrc(srcLeft, srcTop, srcRight, srcBottom) {} @@ -890,13 +890,13 @@ public: } private: - SkBitmap* mBitmap; + const SkBitmap* mBitmap; Rect mSrc; }; class DrawBitmapDataOp : public DrawBitmapOp { public: - DrawBitmapDataOp(SkBitmap* bitmap, float left, float top, SkPaint* paint) + DrawBitmapDataOp(const SkBitmap* bitmap, float left, float top, const SkPaint* paint) : DrawBitmapOp(bitmap, left, top, paint) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { @@ -918,8 +918,8 @@ public: class DrawBitmapMeshOp : public DrawBoundedOp { public: - DrawBitmapMeshOp(SkBitmap* bitmap, int meshWidth, int meshHeight, - float* vertices, int* colors, SkPaint* paint) + DrawBitmapMeshOp(const SkBitmap* bitmap, int meshWidth, int meshHeight, + const float* vertices, const int* colors, const SkPaint* paint) : DrawBoundedOp(vertices, 2 * (meshWidth + 1) * (meshHeight + 1), paint), mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight), mVertices(vertices), mColors(colors) {} @@ -941,17 +941,17 @@ public: } private: - SkBitmap* mBitmap; + const SkBitmap* mBitmap; int mMeshWidth; int mMeshHeight; - float* mVertices; - int* mColors; + const float* mVertices; + const int* mColors; }; class DrawPatchOp : public DrawBoundedOp { public: - DrawPatchOp(SkBitmap* bitmap, Res_png_9patch* patch, - float left, float top, float right, float bottom, SkPaint* paint) + DrawPatchOp(const SkBitmap* bitmap, const Res_png_9patch* patch, + float left, float top, float right, float bottom, const SkPaint* paint) : DrawBoundedOp(left, top, right, bottom, paint), mBitmap(bitmap), mPatch(patch), mGenerationId(0), mMesh(NULL), mAtlas(Caches::getInstance().assetAtlas) { @@ -1028,8 +1028,8 @@ public: TextureVertex* opVertices = opMesh->vertices; for (uint32_t j = 0; j < vertexCount; j++, opVertices++) { TextureVertex::set(vertex++, - opVertices->position[0] + tx, opVertices->position[1] + ty, - opVertices->texture[0], opVertices->texture[1]); + opVertices->x + tx, opVertices->y + ty, + opVertices->u, opVertices->v); } // Dirty the current layer if possible. When the 9-patch does not @@ -1083,8 +1083,8 @@ public: } private: - SkBitmap* mBitmap; - Res_png_9patch* mPatch; + const SkBitmap* mBitmap; + const Res_png_9patch* mPatch; uint32_t mGenerationId; const Patch* mMesh; @@ -1097,7 +1097,7 @@ private: class DrawColorOp : public DrawOp { public: DrawColorOp(int color, SkXfermode::Mode mode) - : DrawOp(0), mColor(color), mMode(mode) {}; + : DrawOp(NULL), mColor(color), mMode(mode) {}; virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { return renderer.drawColor(mColor, mMode); @@ -1116,7 +1116,7 @@ private: class DrawStrokableOp : public DrawBoundedOp { public: - DrawStrokableOp(float left, float top, float right, float bottom, SkPaint* paint) + DrawStrokableOp(float left, float top, float right, float bottom, const SkPaint* paint) : DrawBoundedOp(left, top, right, bottom, paint) {}; bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) { @@ -1141,7 +1141,7 @@ public: class DrawRectOp : public DrawStrokableOp { public: - DrawRectOp(float left, float top, float right, float bottom, SkPaint* paint) + DrawRectOp(float left, float top, float right, float bottom, const SkPaint* paint) : DrawStrokableOp(left, top, right, bottom, paint) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { @@ -1165,7 +1165,7 @@ public: class DrawRectsOp : public DrawBoundedOp { public: - DrawRectsOp(const float* rects, int count, SkPaint* paint) + DrawRectsOp(const float* rects, int count, const SkPaint* paint) : DrawBoundedOp(rects, count, paint), mRects(rects), mCount(count) {} @@ -1192,7 +1192,7 @@ private: class DrawRoundRectOp : public DrawStrokableOp { public: DrawRoundRectOp(float left, float top, float right, float bottom, - float rx, float ry, SkPaint* paint) + float rx, float ry, const SkPaint* paint) : DrawStrokableOp(left, top, right, bottom, paint), mRx(rx), mRy(ry) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { @@ -1213,7 +1213,7 @@ private: class DrawCircleOp : public DrawStrokableOp { public: - DrawCircleOp(float x, float y, float radius, SkPaint* paint) + DrawCircleOp(float x, float y, float radius, const SkPaint* paint) : DrawStrokableOp(x - radius, y - radius, x + radius, y + radius, paint), mX(x), mY(y), mRadius(radius) {} @@ -1235,7 +1235,7 @@ private: class DrawOvalOp : public DrawStrokableOp { public: - DrawOvalOp(float left, float top, float right, float bottom, SkPaint* paint) + DrawOvalOp(float left, float top, float right, float bottom, const SkPaint* paint) : DrawStrokableOp(left, top, right, bottom, paint) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { @@ -1253,7 +1253,7 @@ public: class DrawArcOp : public DrawStrokableOp { public: DrawArcOp(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) + float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) : DrawStrokableOp(left, top, right, bottom, paint), mStartAngle(startAngle), mSweepAngle(sweepAngle), mUseCenter(useCenter) {} @@ -1278,7 +1278,7 @@ private: class DrawPathOp : public DrawBoundedOp { public: - DrawPathOp(SkPath* path, SkPaint* paint) + DrawPathOp(const SkPath* path, const SkPaint* paint) : DrawBoundedOp(paint), mPath(path) { float left, top, offset; uint32_t width, height; @@ -1294,7 +1294,7 @@ public: virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, const DeferredDisplayState& state) { - SkPaint* paint = getPaint(renderer); + const SkPaint* paint = getPaint(renderer); renderer.getCaches().pathCache.precache(mPath, paint); deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture; @@ -1307,12 +1307,12 @@ public: virtual const char* name() { return "DrawPath"; } private: - SkPath* mPath; + const SkPath* mPath; }; class DrawLinesOp : public DrawBoundedOp { public: - DrawLinesOp(float* points, int count, SkPaint* paint) + DrawLinesOp(const float* points, int count, const SkPaint* paint) : DrawBoundedOp(points, count, paint), mPoints(points), mCount(count) { mLocalBounds.outset(strokeWidthOutset()); @@ -1336,13 +1336,13 @@ public: } protected: - float* mPoints; + const float* mPoints; int mCount; }; class DrawPointsOp : public DrawLinesOp { public: - DrawPointsOp(float* points, int count, SkPaint* paint) + DrawPointsOp(const float* points, int count, const SkPaint* paint) : DrawLinesOp(points, count, paint) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { @@ -1358,7 +1358,7 @@ public: class DrawSomeTextOp : public DrawOp { public: - DrawSomeTextOp(const char* text, int bytesCount, int count, SkPaint* paint) + DrawSomeTextOp(const char* text, int bytesCount, int count, const SkPaint* paint) : DrawOp(paint), mText(text), mBytesCount(bytesCount), mCount(count) {}; virtual void output(int level, uint32_t logFlags) const { @@ -1367,7 +1367,7 @@ public: virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, const DeferredDisplayState& state) { - SkPaint* paint = getPaint(renderer); + const SkPaint* paint = getPaint(renderer); FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint); fontRenderer.precache(paint, mText, mCount, mat4::identity()); @@ -1385,7 +1385,7 @@ protected: class DrawTextOnPathOp : public DrawSomeTextOp { public: DrawTextOnPathOp(const char* text, int bytesCount, int count, - SkPath* path, float hOffset, float vOffset, SkPaint* paint) + const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) : DrawSomeTextOp(text, bytesCount, count, paint), mPath(path), mHOffset(hOffset), mVOffset(vOffset) { /* TODO: inherit from DrawBounded and init mLocalBounds */ @@ -1399,7 +1399,7 @@ public: virtual const char* name() { return "DrawTextOnPath"; } private: - SkPath* mPath; + const SkPath* mPath; float mHOffset; float mVOffset; }; @@ -1407,7 +1407,7 @@ private: class DrawPosTextOp : public DrawSomeTextOp { public: DrawPosTextOp(const char* text, int bytesCount, int count, - const float* positions, SkPaint* paint) + const float* positions, const SkPaint* paint) : DrawSomeTextOp(text, bytesCount, count, paint), mPositions(positions) { /* TODO: inherit from DrawBounded and init mLocalBounds */ } @@ -1425,7 +1425,7 @@ private: class DrawTextOp : public DrawBoundedOp { public: DrawTextOp(const char* text, int bytesCount, int count, float x, float y, - const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds) + const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds) : DrawBoundedOp(bounds, paint), mText(text), mBytesCount(bytesCount), mCount(count), mX(x), mY(y), mPositions(positions), mTotalAdvance(totalAdvance) { memset(&mPrecacheTransform.data[0], 0xff, 16 * sizeof(float)); @@ -1433,7 +1433,7 @@ public: virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, const DeferredDisplayState& state) { - SkPaint* paint = getPaint(renderer); + const SkPaint* paint = getPaint(renderer); FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint); const mat4& transform = renderer.findBestFontTransform(state.mMatrix); if (mPrecacheTransform != transform) { @@ -1502,7 +1502,7 @@ private: class DrawFunctorOp : public DrawOp { public: DrawFunctorOp(Functor* functor) - : DrawOp(0), mFunctor(functor) {} + : DrawOp(NULL), mFunctor(functor) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { renderer.startMark("GL functor"); @@ -1522,20 +1522,21 @@ private: }; class DrawDisplayListOp : public DrawBoundedOp { + friend class DisplayList; // grant DisplayList access to info of child public: - DrawDisplayListOp(DisplayList* displayList, int flags) + DrawDisplayListOp(DisplayList* displayList, int flags, const mat4& transformFromParent) : DrawBoundedOp(0, 0, displayList->getWidth(), displayList->getHeight(), 0), - mDisplayList(displayList), mFlags(flags) {} + mDisplayList(displayList), mFlags(flags), mTransformFromParent(transformFromParent) {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, bool useQuickReject) { - if (mDisplayList && mDisplayList->isRenderable()) { + if (mDisplayList && mDisplayList->isRenderable() && !mSkipInOrderDraw) { mDisplayList->defer(deferStruct, level + 1); } } virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level, bool useQuickReject) { - if (mDisplayList && mDisplayList->isRenderable()) { + if (mDisplayList && mDisplayList->isRenderable() && !mSkipInOrderDraw) { mDisplayList->replay(replayStruct, level + 1); } } @@ -1556,13 +1557,58 @@ public: private: DisplayList* mDisplayList; - int mFlags; + const int mFlags; + + /////////////////////////// + // Properties below are used by DisplayList::computeOrderingImpl() and iterate() + /////////////////////////// + /** + * Records transform vs parent, used for computing total transform without rerunning DL contents + */ + const mat4 mTransformFromParent; + + /** + * Holds the transformation between the 3d root OR projection surface ViewGroup and this + * DisplayList drawing instance. Represents any translations / transformations done within the + * drawing of the compositing ancestor ViewGroup's draw, before the draw of the View represented + * by this DisplayList draw instance. + * + * Note: doesn't include any transformation recorded within the DisplayList and its properties. + */ + mat4 mTransformFromCompositingAncestor; + bool mSkipInOrderDraw; +}; + +/** + * Not a canvas operation, used only by 3d / z ordering logic in DisplayList::iterate() + */ +class DrawShadowOp : public DrawOp { +public: + DrawShadowOp(const mat4& casterTransform, float casterAlpha, float width, float height) + : DrawOp(NULL), mCasterTransform(casterTransform), mCasterAlpha(casterAlpha), + mWidth(width), mHeight(height) {} + + virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { + return renderer.drawShadow(mCasterTransform, mCasterAlpha, mWidth, mHeight); + } + + virtual void output(int level, uint32_t logFlags) const { + OP_LOG("DrawShadow of width %.2f, height %.2f", mWidth, mHeight); + } + + virtual const char* name() { return "DrawShadow"; } + +private: + const mat4 mCasterTransform; + const float mCasterAlpha; + const float mWidth; + const float mHeight; }; class DrawLayerOp : public DrawOp { public: DrawLayerOp(Layer* layer, float x, float y) - : DrawOp(0), mLayer(layer), mX(x), mY(y) {} + : DrawOp(NULL), mLayer(layer), mX(x), mY(y) {} virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { return renderer.drawLayer(mLayer, mX, mY); diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 8866029..c8a6c2d 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -111,30 +111,23 @@ DisplayList* DisplayListRenderer::getDisplayList(DisplayList* displayList) { } else { displayList->initFromDisplayListRenderer(*this, true); } + // TODO: should just avoid setting the DisplayList's DisplayListData displayList->setRenderable(mHasDrawOps); return displayList; } -bool DisplayListRenderer::isDeferred() { - return true; -} - void DisplayListRenderer::setViewport(int width, int height) { - mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); + // TODO: DisplayListRenderer shouldn't have a projection matrix, as it should never be used + mViewProjMatrix.loadOrtho(0, width, height, 0, -1, 1); - mWidth = width; - mHeight = height; + initializeViewport(width, height); } status_t DisplayListRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) { - mSnapshot = new Snapshot(mFirstSnapshot, - SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); - mSaveCount = 1; + initializeSaveStack(0, 0, getWidth(), getHeight()); - mSnapshot->setClip(0.0f, 0.0f, mWidth, mHeight); mDirtyClip = opaque; - mRestoreSaveCount = -1; return DrawGlInfo::kStatusDone; // No invalidate needed at record-time @@ -160,7 +153,7 @@ status_t DisplayListRenderer::callDrawGLFunction(Functor *functor, Rect& dirty) int DisplayListRenderer::save(int flags) { addStateOp(new (alloc()) SaveOp(flags)); - return OpenGLRenderer::save(flags); + return StatefulBaseRenderer::save(flags); } void DisplayListRenderer::restore() { @@ -171,72 +164,81 @@ void DisplayListRenderer::restore() { mRestoreSaveCount--; insertTranslate(); - OpenGLRenderer::restore(); + StatefulBaseRenderer::restore(); } void DisplayListRenderer::restoreToCount(int saveCount) { mRestoreSaveCount = saveCount; insertTranslate(); - OpenGLRenderer::restoreToCount(saveCount); + StatefulBaseRenderer::restoreToCount(saveCount); +} + +void DisplayListRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) { + bool restoreProjection = removed.flags & Snapshot::kFlagProjectionTarget; + if (restoreProjection) { + mDisplayListData->projectionIndex = mDisplayListData->displayListOps.size() - 1; + mDisplayListData->projectionTransform.load(*currentTransform()); + } } int DisplayListRenderer::saveLayer(float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode, int flags) { addStateOp(new (alloc()) SaveLayerOp(left, top, right, bottom, alpha, mode, flags)); - return OpenGLRenderer::save(flags); + return StatefulBaseRenderer::save(flags); } -void DisplayListRenderer::translate(float dx, float dy) { +void DisplayListRenderer::translate(float dx, float dy, float dz) { + // ignore dz, not used at defer time mHasTranslate = true; mTranslateX += dx; mTranslateY += dy; insertRestoreToCount(); - OpenGLRenderer::translate(dx, dy); + StatefulBaseRenderer::translate(dx, dy, dz); } void DisplayListRenderer::rotate(float degrees) { addStateOp(new (alloc()) RotateOp(degrees)); - OpenGLRenderer::rotate(degrees); + StatefulBaseRenderer::rotate(degrees); } void DisplayListRenderer::scale(float sx, float sy) { addStateOp(new (alloc()) ScaleOp(sx, sy)); - OpenGLRenderer::scale(sx, sy); + StatefulBaseRenderer::scale(sx, sy); } void DisplayListRenderer::skew(float sx, float sy) { addStateOp(new (alloc()) SkewOp(sx, sy)); - OpenGLRenderer::skew(sx, sy); + StatefulBaseRenderer::skew(sx, sy); } -void DisplayListRenderer::setMatrix(SkMatrix* matrix) { +void DisplayListRenderer::setMatrix(const SkMatrix* matrix) { matrix = refMatrix(matrix); addStateOp(new (alloc()) SetMatrixOp(matrix)); - OpenGLRenderer::setMatrix(matrix); + StatefulBaseRenderer::setMatrix(matrix); } -void DisplayListRenderer::concatMatrix(SkMatrix* matrix) { +void DisplayListRenderer::concatMatrix(const SkMatrix* matrix) { matrix = refMatrix(matrix); addStateOp(new (alloc()) ConcatMatrixOp(matrix)); - OpenGLRenderer::concatMatrix(matrix); + StatefulBaseRenderer::concatMatrix(matrix); } bool DisplayListRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { addStateOp(new (alloc()) ClipRectOp(left, top, right, bottom, op)); - return OpenGLRenderer::clipRect(left, top, right, bottom, op); + return StatefulBaseRenderer::clipRect(left, top, right, bottom, op); } -bool DisplayListRenderer::clipPath(SkPath* path, SkRegion::Op op) { +bool DisplayListRenderer::clipPath(const SkPath* path, SkRegion::Op op) { path = refPath(path); addStateOp(new (alloc()) ClipPathOp(path, op)); - return OpenGLRenderer::clipPath(path, op); + return StatefulBaseRenderer::clipPath(path, op); } -bool DisplayListRenderer::clipRegion(SkRegion* region, SkRegion::Op op) { +bool DisplayListRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) { region = refRegion(region); addStateOp(new (alloc()) ClipRegionOp(region, op)); - return OpenGLRenderer::clipRegion(region, op); + return StatefulBaseRenderer::clipRegion(region, op); } status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList, @@ -248,7 +250,11 @@ status_t DisplayListRenderer::drawDisplayList(DisplayList* displayList, // resources cache, but we rely on the caller (UI toolkit) to // do the right thing for now - addDrawOp(new (alloc()) DrawDisplayListOp(displayList, flags)); + DrawDisplayListOp* op = new (alloc()) DrawDisplayListOp(displayList, + flags, *currentTransform()); + addDrawOp(op); + mDisplayListData->children.push(op); + return DrawGlInfo::kStatusDone; } @@ -258,7 +264,8 @@ status_t DisplayListRenderer::drawLayer(Layer* layer, float x, float y) { return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) { +status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint) { bitmap = refBitmap(bitmap); paint = refPaint(paint); @@ -266,7 +273,8 @@ status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) { +status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + const SkPaint* paint) { bitmap = refBitmap(bitmap); matrix = refMatrix(matrix); paint = refPaint(paint); @@ -275,9 +283,9 @@ status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkP return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, +status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, - float dstRight, float dstBottom, SkPaint* paint) { + float dstRight, float dstBottom, const SkPaint* paint) { bitmap = refBitmap(bitmap); paint = refPaint(paint); @@ -296,8 +304,8 @@ status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawBitmapData(SkBitmap* bitmap, float left, float top, - SkPaint* paint) { +status_t DisplayListRenderer::drawBitmapData(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint) { bitmap = refBitmapData(bitmap); paint = refPaint(paint); @@ -305,8 +313,8 @@ status_t DisplayListRenderer::drawBitmapData(SkBitmap* bitmap, float left, float return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight, - float* vertices, int* colors, SkPaint* paint) { +status_t DisplayListRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, + const float* vertices, const int* colors, const SkPaint* paint) { int count = (meshWidth + 1) * (meshHeight + 1) * 2; bitmap = refBitmap(bitmap); vertices = refBuffer<float>(vertices, count); @@ -318,8 +326,8 @@ status_t DisplayListRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, in return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, - float left, float top, float right, float bottom, SkPaint* paint) { +status_t DisplayListRenderer::drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, + float left, float top, float right, float bottom, const SkPaint* paint) { bitmap = refBitmap(bitmap); patch = refPatch(patch); paint = refPaint(paint); @@ -334,41 +342,41 @@ status_t DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) { } status_t DisplayListRenderer::drawRect(float left, float top, float right, float bottom, - SkPaint* paint) { + const SkPaint* paint) { paint = refPaint(paint); addDrawOp(new (alloc()) DrawRectOp(left, top, right, bottom, paint)); return DrawGlInfo::kStatusDone; } status_t DisplayListRenderer::drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, SkPaint* paint) { + float rx, float ry, const SkPaint* paint) { paint = refPaint(paint); addDrawOp(new (alloc()) DrawRoundRectOp(left, top, right, bottom, rx, ry, paint)); return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) { +status_t DisplayListRenderer::drawCircle(float x, float y, float radius, const SkPaint* paint) { paint = refPaint(paint); addDrawOp(new (alloc()) DrawCircleOp(x, y, radius, paint)); return DrawGlInfo::kStatusDone; } status_t DisplayListRenderer::drawOval(float left, float top, float right, float bottom, - SkPaint* paint) { + const SkPaint* paint) { paint = refPaint(paint); addDrawOp(new (alloc()) DrawOvalOp(left, top, right, bottom, paint)); return DrawGlInfo::kStatusDone; } status_t DisplayListRenderer::drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) { + float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) { paint = refPaint(paint); addDrawOp(new (alloc()) DrawArcOp(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint)); return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) { +status_t DisplayListRenderer::drawPath(const SkPath* path, const SkPaint* paint) { path = refPath(path); paint = refPaint(paint); @@ -376,7 +384,7 @@ status_t DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) { return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawLines(float* points, int count, SkPaint* paint) { +status_t DisplayListRenderer::drawLines(const float* points, int count, const SkPaint* paint) { points = refBuffer<float>(points, count); paint = refPaint(paint); @@ -384,7 +392,7 @@ status_t DisplayListRenderer::drawLines(float* points, int count, SkPaint* paint return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawPoints(float* points, int count, SkPaint* paint) { +status_t DisplayListRenderer::drawPoints(const float* points, int count, const SkPaint* paint) { points = refBuffer<float>(points, count); paint = refPaint(paint); @@ -393,7 +401,7 @@ status_t DisplayListRenderer::drawPoints(float* points, int count, SkPaint* pain } status_t DisplayListRenderer::drawTextOnPath(const char* text, int bytesCount, int count, - SkPath* path, float hOffset, float vOffset, SkPaint* paint) { + const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) { if (!text || count <= 0) return DrawGlInfo::kStatusDone; text = refText(text, bytesCount); @@ -407,7 +415,7 @@ status_t DisplayListRenderer::drawTextOnPath(const char* text, int bytesCount, i } status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int count, - const float* positions, SkPaint* paint) { + const float* positions, const SkPaint* paint) { if (!text || count <= 0) return DrawGlInfo::kStatusDone; text = refText(text, bytesCount); @@ -420,7 +428,7 @@ 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 x, float y, const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode) { if (!text || count <= 0) return DrawGlInfo::kStatusDone; @@ -435,7 +443,7 @@ status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int cou return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawRects(const float* rects, int count, SkPaint* paint) { +status_t DisplayListRenderer::drawRects(const float* rects, int count, const SkPaint* paint) { if (count <= 0) return DrawGlInfo::kStatusDone; rects = refBuffer<float>(rects, count); @@ -506,7 +514,7 @@ void DisplayListRenderer::addStateOp(StateOp* op) { void DisplayListRenderer::addDrawOp(DrawOp* op) { Rect localBounds; if (op->getLocalBounds(mDrawModifiers, localBounds)) { - bool rejected = quickRejectNoScissor(localBounds.left, localBounds.top, + bool rejected = quickRejectConservative(localBounds.left, localBounds.top, localBounds.right, localBounds.bottom); op->setQuickRejected(rejected); } diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index d233150..1360808 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -54,7 +54,7 @@ class DrawOp; class StateOp; /** - * Records drawing commands in a display list for latter playback. + * Records drawing commands in a display list for later playback into an OpenGLRenderer. */ class DisplayListRenderer: public OpenGLRenderer { public: @@ -63,69 +63,42 @@ public: ANDROID_API DisplayList* getDisplayList(DisplayList* displayList); - virtual bool isDeferred(); + virtual bool isRecording() const { return true; } +// ---------------------------------------------------------------------------- +// Frame state operations +// ---------------------------------------------------------------------------- virtual void setViewport(int width, int height); virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque); virtual void finish(); - - virtual status_t callDrawGLFunction(Functor *functor, Rect& dirty); - virtual void interrupt(); virtual void resume(); +// ---------------------------------------------------------------------------- +// Canvas state operations +// ---------------------------------------------------------------------------- + // Save (layer) virtual int save(int flags); virtual void restore(); virtual void restoreToCount(int saveCount); - virtual int saveLayer(float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode, int flags); - virtual void translate(float dx, float dy); + // Matrix + virtual void translate(float dx, float dy, float dz); virtual void rotate(float degrees); virtual void scale(float sx, float sy); virtual void skew(float sx, float sy); - virtual void setMatrix(SkMatrix* matrix); - virtual void concatMatrix(SkMatrix* matrix); + virtual void setMatrix(const SkMatrix* matrix); + virtual void concatMatrix(const SkMatrix* matrix); + // Clip virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); - virtual bool clipPath(SkPath* path, SkRegion::Op op); - virtual bool clipRegion(SkRegion* region, SkRegion::Op op); - - virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t flags); - virtual status_t drawLayer(Layer* layer, float x, float y); - virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, 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, - float dstRight, float dstBottom, SkPaint* paint); - 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, 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); - virtual status_t drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, SkPaint* paint); - virtual status_t drawCircle(float x, float y, float radius, SkPaint* paint); - virtual status_t drawOval(float left, float top, float right, float bottom, SkPaint* paint); - virtual status_t drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, SkPaint* paint); - virtual status_t drawPath(SkPath* path, SkPaint* paint); - virtual status_t drawLines(float* points, int count, SkPaint* paint); - virtual status_t drawPoints(float* points, int count, SkPaint* paint); - virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path, - float hOffset, float vOffset, SkPaint* paint); - 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 totalAdvance, const Rect& bounds, - DrawOpMode drawOpMode); - - virtual status_t drawRects(const float* rects, int count, SkPaint* paint); + virtual bool clipPath(const SkPath* path, SkRegion::Op op); + virtual bool clipRegion(const SkRegion* region, SkRegion::Op op); + // Misc - should be implemented with SkPaint inspection virtual void resetShader(); virtual void setupShader(SkiaShader* shader); @@ -138,17 +111,74 @@ public: virtual void resetPaintFilter(); virtual void setupPaintFilter(int clearBits, int setBits); +// ---------------------------------------------------------------------------- +// Canvas draw operations +// ---------------------------------------------------------------------------- + virtual status_t drawColor(int color, SkXfermode::Mode mode); + + // Bitmap-based + virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint); + virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + const SkPaint* paint); + virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, + float srcRight, float srcBottom, float dstLeft, float dstTop, + float dstRight, float dstBottom, const SkPaint* paint); + virtual status_t drawBitmapData(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint); + virtual status_t drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, + const float* vertices, const int* colors, const SkPaint* paint); + virtual status_t drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, + float left, float top, float right, float bottom, const SkPaint* paint); + + // Shapes + virtual status_t drawRect(float left, float top, float right, float bottom, + const SkPaint* paint); + virtual status_t drawRects(const float* rects, int count, const SkPaint* paint); + virtual status_t drawRoundRect(float left, float top, float right, float bottom, + float rx, float ry, const SkPaint* paint); + virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint); + virtual status_t drawOval(float left, float top, float right, float bottom, + const SkPaint* paint); + virtual status_t drawArc(float left, float top, float right, float bottom, + float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint); + virtual status_t drawPath(const SkPath* path, const SkPaint* paint); + virtual status_t drawLines(const float* points, int count, const SkPaint* paint); + virtual status_t drawPoints(const float* points, int count, const SkPaint* paint); + + // Text + virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y, + const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, + DrawOpMode drawOpMode = kDrawOpMode_Immediate); + virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path, + float hOffset, float vOffset, const SkPaint* paint); + virtual status_t drawPosText(const char* text, int bytesCount, int count, + const float* positions, const SkPaint* paint); + +// ---------------------------------------------------------------------------- +// Canvas draw operations - special +// ---------------------------------------------------------------------------- + virtual status_t drawLayer(Layer* layer, float x, float y); + virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, + int32_t replayFlags); + + // TODO: rename for consistency + virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty); + +// ---------------------------------------------------------------------------- +// DisplayList / resource management +// ---------------------------------------------------------------------------- ANDROID_API void reset(); sp<DisplayListData> getDisplayListData() const { return mDisplayListData; } - const Vector<SkBitmap*>& getBitmapResources() const { + const Vector<const SkBitmap*>& getBitmapResources() const { return mBitmapResources; } - const Vector<SkBitmap*>& getOwnedBitmapResources() const { + const Vector<const SkBitmap*>& getOwnedBitmapResources() const { return mOwnedBitmapResources; } @@ -156,7 +186,7 @@ public: return mFilterResources; } - const Vector<Res_png_9patch*>& getPatchResources() const { + const Vector<const Res_png_9patch*>& getPatchResources() const { return mPatchResources; } @@ -164,19 +194,19 @@ public: return mShaders; } - const Vector<SkPaint*>& getPaints() const { + const Vector<const SkPaint*>& getPaints() const { return mPaints; } - const Vector<SkPath*>& getPaths() const { + const Vector<const SkPath*>& getPaths() const { return mPaths; } - const SortedVector<SkPath*>& getSourcePaths() const { + const SortedVector<const SkPath*>& getSourcePaths() const { return mSourcePaths; } - const Vector<SkRegion*>& getRegions() const { + const Vector<const SkRegion*>& getRegions() const { return mRegions; } @@ -184,13 +214,15 @@ public: return mLayers; } - const Vector<SkMatrix*>& getMatrices() const { + const Vector<const SkMatrix*>& getMatrices() const { return mMatrices; } uint32_t getFunctorCount() const { return mFunctorCount; } +protected: + virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored); private: void insertRestoreToCount(); @@ -206,8 +238,9 @@ private: } template<class T> - inline T* refBuffer(const T* srcBuffer, int32_t count) { - if (srcBuffer == NULL) return NULL; + inline const T* refBuffer(const T* srcBuffer, int32_t count) { + if (!srcBuffer) return NULL; + T* dstBuffer = (T*) mDisplayListData->allocator.alloc(count * sizeof(T)); memcpy(dstBuffer, srcBuffer, count * sizeof(T)); return dstBuffer; @@ -217,13 +250,15 @@ private: return (char*) refBuffer<uint8_t>((uint8_t*)text, byteLength); } - inline SkPath* refPath(SkPath* path) { + inline const SkPath* refPath(const SkPath* path) { if (!path) return NULL; - SkPath* pathCopy = mPathMap.valueFor(path); + const SkPath* pathCopy = mPathMap.valueFor(path); if (pathCopy == NULL || pathCopy->getGenerationID() != path->getGenerationID()) { - pathCopy = new SkPath(*path); - pathCopy->setSourcePath(path); + SkPath* newPathCopy = new SkPath(*path); + newPathCopy->setSourcePath(path); + + pathCopy = newPathCopy; // replaceValueFor() performs an add if the entry doesn't exist mPathMap.replaceValueFor(path, pathCopy); mPaths.add(pathCopy); @@ -235,12 +270,12 @@ private: return pathCopy; } - inline SkPaint* refPaint(SkPaint* paint) { + inline const SkPaint* refPaint(const SkPaint* paint) { if (!paint) { return paint; } - SkPaint* paintCopy = mPaintMap.valueFor(paint); + const SkPaint* paintCopy = mPaintMap.valueFor(paint); if (paintCopy == NULL || paintCopy->getGenerationID() != paint->getGenerationID()) { paintCopy = new SkPaint(*paint); // replaceValueFor() performs an add if the entry doesn't exist @@ -251,12 +286,12 @@ private: return paintCopy; } - inline SkRegion* refRegion(SkRegion* region) { + inline const SkRegion* refRegion(const SkRegion* region) { if (!region) { return region; } - SkRegion* regionCopy = mRegionMap.valueFor(region); + const SkRegion* regionCopy = mRegionMap.valueFor(region); // TODO: Add generation ID to SkRegion if (regionCopy == NULL) { regionCopy = new SkRegion(*region); @@ -268,11 +303,11 @@ private: return regionCopy; } - inline SkMatrix* refMatrix(SkMatrix* matrix) { + inline const SkMatrix* refMatrix(const SkMatrix* matrix) { if (matrix) { // Copying the matrix is cheap and prevents against the user changing // the original matrix before the operation that uses it - SkMatrix* copy = new SkMatrix(*matrix); + const SkMatrix* copy = new SkMatrix(*matrix); mMatrices.add(copy); return copy; } @@ -285,7 +320,7 @@ private: return layer; } - inline SkBitmap* refBitmap(SkBitmap* bitmap) { + inline const SkBitmap* refBitmap(const SkBitmap* bitmap) { // Note that this assumes the bitmap is immutable. There are cases this won't handle // correctly, such as creating the bitmap from scratch, drawing with it, changing its // contents, and drawing again. The only fix would be to always copy it the first time, @@ -295,7 +330,7 @@ private: return bitmap; } - inline SkBitmap* refBitmapData(SkBitmap* bitmap) { + inline const SkBitmap* refBitmapData(const SkBitmap* bitmap) { mOwnedBitmapResources.add(bitmap); mCaches.resourceCache.incrementRefcount(bitmap); return bitmap; @@ -322,32 +357,33 @@ private: return colorFilter; } - inline Res_png_9patch* refPatch(Res_png_9patch* patch) { + inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) { mPatchResources.add(patch); mCaches.resourceCache.incrementRefcount(patch); return patch; } - Vector<SkBitmap*> mBitmapResources; - Vector<SkBitmap*> mOwnedBitmapResources; + // TODO: move these to DisplayListData + Vector<const SkBitmap*> mBitmapResources; + Vector<const SkBitmap*> mOwnedBitmapResources; Vector<SkiaColorFilter*> mFilterResources; - Vector<Res_png_9patch*> mPatchResources; + Vector<const Res_png_9patch*> mPatchResources; - Vector<SkPaint*> mPaints; - DefaultKeyedVector<SkPaint*, SkPaint*> mPaintMap; + Vector<const SkPaint*> mPaints; + DefaultKeyedVector<const SkPaint*, const SkPaint*> mPaintMap; - Vector<SkPath*> mPaths; - DefaultKeyedVector<SkPath*, SkPath*> mPathMap; + Vector<const SkPath*> mPaths; + DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap; - SortedVector<SkPath*> mSourcePaths; + SortedVector<const SkPath*> mSourcePaths; - Vector<SkRegion*> mRegions; - DefaultKeyedVector<SkRegion*, SkRegion*> mRegionMap; + Vector<const SkRegion*> mRegions; + DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap; Vector<SkiaShader*> mShaders; DefaultKeyedVector<SkiaShader*, SkiaShader*> mShaderMap; - Vector<SkMatrix*> mMatrices; + Vector<const SkMatrix*> mMatrices; Vector<Layer*> mLayers; diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 0be17ff..f907d64 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -76,7 +76,8 @@ status_t TextSetupFunctor::operator ()(int what, void* data) { renderer->setupDrawShader(); renderer->setupDrawBlending(true, mode); renderer->setupDrawProgram(); - renderer->setupDrawModelView(x, y, x, y, pureTranslate, true); + renderer->setupDrawModelView(kModelViewMode_Translate, false, + 0.0f, 0.0f, 0.0f, 0.0f, pureTranslate); // Calling setupDrawTexture with the name 0 will enable the // uv attributes and increase the texture unit count // texture binding will be performed by the font renderer as @@ -514,8 +515,8 @@ void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) { texture->setLinearFiltering(mLinearFiltering, false); TextureVertex* mesh = texture->mesh(); - caches.bindPositionVertexPointer(force, &mesh[0].position[0]); - caches.bindTexCoordsVertexPointer(force, &mesh[0].texture[0]); + caches.bindPositionVertexPointer(force, &mesh[0].x); + caches.bindTexCoordsVertexPointer(force, &mesh[0].u); force = false; glDrawElements(GL_TRIANGLES, texture->meshElementCount(), @@ -586,11 +587,11 @@ void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, } } -void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) { +void FontRenderer::setFont(const SkPaint* paint, const mat4& matrix) { mCurrentFont = Font::create(this, paint, matrix); } -FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, +FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) { checkInit(); @@ -675,7 +676,8 @@ void FontRenderer::finishRender() { issueDrawCommand(); } -void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) { +void FontRenderer::precache(const SkPaint* paint, const char* text, int numGlyphs, + const mat4& matrix) { Font* font = Font::create(this, paint, matrix); font->precache(paint, text, numGlyphs); } @@ -684,7 +686,7 @@ void FontRenderer::endPrecaching() { checkTextureUpdate(); } -bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, +bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds, Functor* functor, bool forceFinish) { if (!mCurrentFont) { @@ -702,8 +704,8 @@ bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *t return mDrawn; } -bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, - uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, +bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text, + uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path, float hOffset, float vOffset, Rect* bounds, Functor* functor) { if (!mCurrentFont) { ALOGE("No font set"); diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index aa7e776..9259028 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -44,8 +44,6 @@ namespace RSC { } #endif -class Functor; - namespace android { namespace uirenderer { @@ -64,7 +62,7 @@ public: }; TextSetupFunctor(OpenGLRenderer* renderer, float x, float y, bool pureTranslate, - int alpha, SkXfermode::Mode mode, SkPaint* paint): Functor(), + int alpha, SkXfermode::Mode mode, const SkPaint* paint): Functor(), renderer(renderer), x(x), y(y), pureTranslate(pureTranslate), alpha(alpha), mode(mode), paint(paint) { } @@ -78,7 +76,7 @@ public: bool pureTranslate; int alpha; SkXfermode::Mode mode; - SkPaint* paint; + const SkPaint* paint; }; /////////////////////////////////////////////////////////////////////////////// @@ -97,20 +95,20 @@ public: mGammaTable = gammaTable; } - void setFont(SkPaint* paint, const mat4& matrix); + void setFont(const SkPaint* paint, const mat4& matrix); - void precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix); + void precache(const SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix); void endPrecaching(); // bounds is an out parameter - bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, - uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds, - Functor* functor, bool forceFinish = true); + bool renderPosText(const SkPaint* paint, const Rect* clip, const char *text, + uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, const float* positions, + Rect* bounds, Functor* functor, bool forceFinish = true); // bounds is an out parameter - bool renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, - uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds, - Functor* functor); + bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text, + uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path, + float hOffset, float vOffset, Rect* bounds, Functor* functor); struct DropShadow { DropShadow() { }; @@ -130,7 +128,7 @@ public: // After renderDropShadow returns, the called owns the memory in DropShadow.image // and is responsible for releasing it when it's done with it - DropShadow renderDropShadow(SkPaint* paint, const char *text, uint32_t startIndex, + DropShadow renderDropShadow(const SkPaint* paint, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions); void setTextureFiltering(bool linearFiltering) { diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index 0916942..ffd1e8c 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -115,7 +115,7 @@ void GradientCache::setMaxSize(uint32_t maxSize) { // Callbacks /////////////////////////////////////////////////////////////////////////////// -void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) { +void GradientCache::operator()(GradientCacheEntry&, Texture*& texture) { if (texture) { const uint32_t size = texture->width * texture->height * bytesPerPixel(); mSize -= size; @@ -185,7 +185,7 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, mCache.removeOldest(); } - generateTexture(colors, positions, count, texture); + generateTexture(colors, positions, texture); mSize += size; mCache.put(gradient, texture); @@ -238,8 +238,7 @@ void GradientCache::mixFloats(GradientColor& start, GradientColor& end, float am dst += 4 * sizeof(float); } -void GradientCache::generateTexture(uint32_t* colors, float* positions, - int count, Texture* texture) { +void GradientCache::generateTexture(uint32_t* colors, float* positions, Texture* texture) { const uint32_t width = texture->width; const GLsizei rowBytes = width * bytesPerPixel(); uint8_t pixels[rowBytes * texture->height]; diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h index 43934d9..6a783b1 100644 --- a/libs/hwui/GradientCache.h +++ b/libs/hwui/GradientCache.h @@ -151,7 +151,7 @@ private: Texture* addLinearGradient(GradientCacheEntry& gradient, uint32_t* colors, float* positions, int count); - void generateTexture(uint32_t* colors, float* positions, int count, Texture* texture); + void generateTexture(uint32_t* colors, float* positions, Texture* texture); struct GradientInfo { uint32_t width; diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index bd371a3..742ffd4 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -194,11 +194,9 @@ void Layer::defer() { dirtyRect.set(0, 0, width, height); } - if (deferredList) { - deferredList->reset(dirtyRect); - } else { - deferredList = new DeferredDisplayList(dirtyRect); - } + delete deferredList; + deferredList = new DeferredDisplayList(dirtyRect); + DeferStateStruct deferredState(*deferredList, *renderer, DisplayList::kReplayFlag_ClipChildren); @@ -206,6 +204,7 @@ void Layer::defer() { renderer->setupFrameState(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); + displayList->computeOrdering(); displayList->defer(deferredState, 0); deferredUpdateScheduled = false; diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index b70042f..471a4a6 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -49,7 +49,8 @@ class DeferStateStruct; /** * A layer has dimensions and is backed by an OpenGL texture or FBO. */ -struct Layer { +class Layer { +public: Layer(const uint32_t layerWidth, const uint32_t layerHeight); ~Layer(); diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index f8076cc..0b38c4d 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -92,7 +92,7 @@ void LayerRenderer::finish() { // who will invoke OpenGLRenderer::resume() } -GLint LayerRenderer::getTargetFbo() const { +GLuint LayerRenderer::getTargetFbo() const { return mLayer->getFbo(); } @@ -117,7 +117,7 @@ void LayerRenderer::ensureStencilBuffer() { /////////////////////////////////////////////////////////////////////////////// Region* LayerRenderer::getRegion() const { - if (getSnapshot()->flags & Snapshot::kFlagFboTarget) { + if (currentSnapshot()->flags & Snapshot::kFlagFboTarget) { return OpenGLRenderer::getRegion(); } return &mLayer->region; diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h index 5f86731..f24c8d4 100644 --- a/libs/hwui/LayerRenderer.h +++ b/libs/hwui/LayerRenderer.h @@ -67,7 +67,7 @@ protected: virtual void ensureStencilBuffer(); virtual bool hasLayer() const; virtual Region* getRegion() const; - virtual GLint getTargetFbo() const; + virtual GLuint getTargetFbo() const; virtual bool suppressErrorChecks() const; private: diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp index ba22071..4f5cd26 100644 --- a/libs/hwui/Matrix.cpp +++ b/libs/hwui/Matrix.cpp @@ -89,8 +89,9 @@ uint8_t Matrix4::getType() const { float m01 = data[kSkewX]; float m10 = data[kSkewY]; float m11 = data[kScaleY]; + float m32 = data[kTranslateZ]; - if (m01 != 0.0f || m10 != 0.0f) { + if (m01 != 0.0f || m10 != 0.0f || m32 != 0.0f) { mType |= kTypeAffine; } @@ -131,11 +132,13 @@ bool Matrix4::changesBounds() const { } bool Matrix4::isPureTranslate() const { - return getGeometryType() <= kTypeTranslate; + // NOTE: temporary hack to workaround ignoreTransform behavior with Z values + // TODO: separate this into isPure2dTranslate vs isPure3dTranslate + return getGeometryType() <= kTypeTranslate && (data[kTranslateZ] == 0.0f); } bool Matrix4::isSimple() const { - return getGeometryType() <= (kTypeScale | kTypeTranslate); + return getGeometryType() <= (kTypeScale | kTypeTranslate) && (data[kTranslateZ] == 0.0f); } bool Matrix4::isIdentity() const { @@ -369,6 +372,84 @@ void Matrix4::loadMultiply(const Matrix4& u, const Matrix4& v) { mType = kTypeUnknown; } +// translated from android.opengl.Matrix#frustumM() +void Matrix4::loadFrustum(float left, float top, float right, float bottom, float near, float far) { + float r_width = 1.0f / (right - left); + float r_height = 1.0f / (top - bottom); + float r_depth = 1.0f / (near - far); + float x = 2.0f * (near * r_width); + float y = 2.0f * (near * r_height); + float A = (right + left) * r_width; + float B = (top + bottom) * r_height; + float C = (far + near) * r_depth; + float D = 2.0f * (far * near * r_depth); + + memset(&data, 0, sizeof(data)); + mType = kTypeUnknown; + + data[kScaleX] = x; + data[kScaleY] = y; + data[8] = A; + data[9] = B; + data[kScaleZ] = C; + data[kTranslateZ] = D; + data[11] = -1.0f; +} + +// translated from android.opengl.Matrix#setLookAtM() +void Matrix4::loadLookAt(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ) { + float fx = centerX - eyeX; + float fy = centerY - eyeY; + float fz = centerZ - eyeZ; + + // Normalize f + float rlf = 1.0f / sqrt(fx*fx + fy*fy + fz*fz); + fx *= rlf; + fy *= rlf; + fz *= rlf; + + // compute s = f x up (x means "cross product") + float sx = fy * upZ - fz * upY; + float sy = fz * upX - fx * upZ; + float sz = fx * upY - fy * upX; + + // and normalize s + float rls = 1.0f / sqrt(sx*sx + sy*sy + sz*sz); + sx *= rls; + sy *= rls; + sz *= rls; + + // compute u = s x f + float ux = sy * fz - sz * fy; + float uy = sz * fx - sx * fz; + float uz = sx * fy - sy * fx; + + mType = kTypeUnknown; + data[0] = sx; + data[1] = ux; + data[2] = -fx; + data[3] = 0.0f; + + data[4] = sy; + data[5] = uy; + data[6] = -fy; + data[7] = 0.0f; + + data[8] = sz; + data[9] = uz; + data[10] = -fz; + data[11] = 0.0f; + + data[12] = 0.0f; + data[13] = 0.0f; + data[14] = 0.0f; + data[15] = 1.0f; + + translate(-eyeX, -eyeY, -eyeZ); +} + void Matrix4::loadOrtho(float left, float right, float bottom, float top, float near, float far) { loadIdentity(); @@ -382,6 +463,14 @@ void Matrix4::loadOrtho(float left, float right, float bottom, float top, float mType = kTypeTranslate | kTypeScale | kTypeRectToRect; } +void Matrix4::mapPoint3d(Vector3& vec) const { + //TODO: optimize simple case + Vector3 orig(vec); + vec.x = orig.x * data[kScaleX] + orig.y * data[kSkewX] + orig.z * data[8] + data[kTranslateX]; + vec.y = orig.x * data[kSkewY] + orig.y * data[kScaleY] + orig.z * data[9] + data[kTranslateY]; + vec.z = orig.x * data[2] + orig.y * data[6] + orig.z * data[kScaleZ] + data[kTranslateZ]; +} + #define MUL_ADD_STORE(a, b, c) a = (a) * (b) + (c) void Matrix4::mapPoint(float& x, float& y) const { diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index b861ba4..00ca050 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -121,6 +121,10 @@ public: void loadRotate(float angle); void loadRotate(float angle, float x, float y, float z); void loadMultiply(const Matrix4& u, const Matrix4& v); + void loadFrustum(float left, float top, float right, float bottom, float near, float far); + void loadLookAt(float eyeX, float eyeY, float eyeZ, + float centerX, float centerY, float centerZ, + float upX, float upY, float upZ); void loadOrtho(float left, float right, float bottom, float top, float near, float far); @@ -134,17 +138,18 @@ public: void multiply(float v); - void translate(float x, float y) { + void translate(float x, float y, float z = 0) { if ((getType() & sGeometryMask) <= kTypeTranslate) { data[kTranslateX] += x; data[kTranslateY] += y; + data[kTranslateZ] += z; } else { // Doing a translation will only affect the translate bit of the type // Save the type uint8_t type = mType; Matrix4 u; - u.loadTranslate(x, y, 0.0f); + u.loadTranslate(x, y, z); multiply(u); // Restore the type and fix the translate bit @@ -190,8 +195,9 @@ public: void copyTo(float* v) const; void copyTo(SkMatrix& v) const; - void mapRect(Rect& r) const; - void mapPoint(float& x, float& y) const; + void mapPoint3d(Vector3& vec) const; + void mapPoint(float& x, float& y) const; // 2d only + void mapRect(Rect& r) const; // 2d only float getTranslateX() const; float getTranslateY() const; diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 4d76bed..8fb6e38 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -36,7 +36,9 @@ #include "Fence.h" #include "PathTessellator.h" #include "Properties.h" +#include "ShadowTessellator.h" #include "Vector.h" +#include "VertexBuffer.h" namespace android { namespace uirenderer { @@ -127,7 +129,6 @@ OpenGLRenderer::OpenGLRenderer(): memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices)); - mFirstSnapshot = new Snapshot; mFrameStarted = false; mCountOverdraw = false; @@ -154,22 +155,6 @@ void OpenGLRenderer::initProperties() { // Setup /////////////////////////////////////////////////////////////////////////////// -void OpenGLRenderer::setName(const char* name) { - if (name) { - mName.setTo(name); - } else { - mName.clear(); - } -} - -const char* OpenGLRenderer::getName() const { - return mName.string(); -} - -bool OpenGLRenderer::isDeferred() { - return false; -} - void OpenGLRenderer::setViewport(int width, int height) { initViewport(width, height); @@ -180,26 +165,31 @@ void OpenGLRenderer::setViewport(int width, int height) { } void OpenGLRenderer::initViewport(int width, int height) { - mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); - - mWidth = width; - mHeight = height; + if (mCaches.propertyEnable3d) { + // TODO: make view proj app configurable + float dist = std::max(width, height) * 1.5; + dist *= mCaches.propertyCameraDistance; + Matrix4 projection; + projection.loadFrustum(-width / 2, -height / 2, width / 2, height / 2, dist, 0); + Matrix4 view; + view.loadLookAt(0, 0, dist, + 0, 0, 0, + 0, 1, 0); + mViewProjMatrix.loadMultiply(projection, view); + mViewProjMatrix.translate(-width/2, -height/2); + } else { + mViewProjMatrix.loadOrtho(0, width, height, 0, -1, 1); + } - mFirstSnapshot->height = height; - mFirstSnapshot->viewport.set(0, 0, width, height); + initializeViewport(width, height); } void OpenGLRenderer::setupFrameState(float left, float top, float right, float bottom, bool opaque) { mCaches.clearGarbage(); + initializeSaveStack(left, top, right, bottom); mOpaque = opaque; - mSnapshot = new Snapshot(mFirstSnapshot, - SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); - mSnapshot->fbo = getTargetFbo(); - mSaveCount = 1; - - mSnapshot->setClip(left, top, right, bottom); mTilingClip.set(left, top, right, bottom); } @@ -211,14 +201,14 @@ status_t OpenGLRenderer::startFrame() { discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom); - glViewport(0, 0, mWidth, mHeight); + glViewport(0, 0, getWidth(), getHeight()); // Functors break the tiling extension in pretty spectacular ways // This ensures we don't use tiling when a functor is going to be // invoked during the frame mSuppressTiling = mCaches.hasRegisteredFunctors(); - startTiling(mSnapshot, true); + startTilingCurrentClip(true); debugOverdraw(true, true); @@ -226,10 +216,6 @@ status_t OpenGLRenderer::startFrame() { mTilingClip.right, mTilingClip.bottom, mOpaque); } -status_t OpenGLRenderer::prepare(bool opaque) { - return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque); -} - status_t OpenGLRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) { @@ -239,7 +225,7 @@ status_t OpenGLRenderer::prepareDirty(float left, float top, // The framebuffer renderer will first defer the display list // for each layer and wait until the first drawing command // to start the frame - if (mSnapshot->fbo == 0) { + if (currentSnapshot()->fbo == 0) { syncState(); updateLayers(); } else { @@ -254,7 +240,7 @@ void OpenGLRenderer::discardFramebuffer(float left, float top, float right, floa // perform a discard to let the driver know we don't need to preserve // the back buffer for this frame. if (mExtensions.hasDiscardFramebuffer() && - left <= 0.0f && top <= 0.0f && right >= mWidth && bottom >= mHeight) { + left <= 0.0f && top <= 0.0f && right >= getWidth() && bottom >= getHeight()) { const bool isFbo = getTargetFbo() == 0; const GLenum attachments[] = { isFbo ? (const GLenum) GL_COLOR_EXT : (const GLenum) GL_COLOR_ATTACHMENT0, @@ -266,7 +252,7 @@ void OpenGLRenderer::discardFramebuffer(float left, float top, float right, floa status_t OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) { if (!opaque || mCountOverdraw) { mCaches.enableScissor(); - mCaches.setScissor(left, mSnapshot->height - bottom, right - left, bottom - top); + mCaches.setScissor(left, currentSnapshot()->height - bottom, right - left, bottom - top); glClear(GL_COLOR_BUFFER_BIT); return DrawGlInfo::kStatusDrew; } @@ -283,14 +269,16 @@ void OpenGLRenderer::syncState() { } } -void OpenGLRenderer::startTiling(const sp<Snapshot>& s, bool opaque) { +void OpenGLRenderer::startTilingCurrentClip(bool opaque) { if (!mSuppressTiling) { - Rect* clip = &mTilingClip; - if (s->flags & Snapshot::kFlagFboTarget) { - clip = &(s->layer->clipRect); + const Snapshot* snapshot = currentSnapshot(); + + const Rect* clip = &mTilingClip; + if (snapshot->flags & Snapshot::kFlagFboTarget) { + clip = &(snapshot->layer->clipRect); } - startTiling(*clip, s->height, opaque); + startTiling(*clip, snapshot->height, opaque); } } @@ -369,7 +357,7 @@ void OpenGLRenderer::interrupt() { } void OpenGLRenderer::resume() { - sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot; + const Snapshot* snapshot = currentSnapshot(); glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight()); glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo); debugOverdraw(true, false); @@ -391,7 +379,7 @@ void OpenGLRenderer::resume() { } void OpenGLRenderer::resumeAfterLayer() { - sp<Snapshot> snapshot = (mSnapshot != NULL) ? mSnapshot : mFirstSnapshot; + const Snapshot* snapshot = currentSnapshot(); glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight()); glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo); debugOverdraw(true, false); @@ -447,16 +435,16 @@ status_t OpenGLRenderer::invokeFunctors(Rect& dirty) { } status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { - if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone; + if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone; detachFunctor(functor); - Rect clip(*mSnapshot->clipRect); + Rect clip(*currentClipRect()); clip.snapToPixelBoundaries(); // Since we don't know what the functor will draw, let's dirty - // tne entire clip region + // the entire clip region if (hasLayer()) { dirtyLayerUnchecked(clip, getRegion()); } @@ -467,9 +455,9 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { info.clipRight = clip.right; info.clipBottom = clip.bottom; info.isLayer = hasLayer(); - info.width = getSnapshot()->viewport.getWidth(); - info.height = getSnapshot()->height; - getSnapshot()->transform->copyTo(&info.transform[0]); + info.width = currentSnapshot()->viewport.getWidth(); + info.height = currentSnapshot()->height; + currentTransform()->copyTo(&info.transform[0]); bool dirtyClip = mDirtyClip; // setup GL state for functor @@ -532,7 +520,7 @@ void OpenGLRenderer::renderOverdraw() { const Rect* clip = &mTilingClip; mCaches.enableScissor(); - mCaches.setScissor(clip->left, mFirstSnapshot->height - clip->bottom, + mCaches.setScissor(clip->left, firstSnapshot()->height - clip->bottom, clip->right - clip->left, clip->bottom - clip->top); // 1x overdraw @@ -556,9 +544,9 @@ void OpenGLRenderer::renderOverdraw() { } void OpenGLRenderer::countOverdraw() { - size_t count = mWidth * mHeight; + size_t count = getWidth() * getHeight(); uint32_t* buffer = new uint32_t[count]; - glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]); + glReadPixels(0, 0, getWidth(), getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]); size_t total = 0; for (size_t i = 0; i < count; i++) { @@ -594,7 +582,7 @@ bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) { if (inFrame) { resumeAfterLayer(); - startTiling(mSnapshot); + startTilingCurrentClip(); } layer->debugDrawUpdate = mCaches.debugLayersUpdates; @@ -715,50 +703,17 @@ void OpenGLRenderer::flushLayerUpdates() { // State management /////////////////////////////////////////////////////////////////////////////// -int OpenGLRenderer::getSaveCount() const { - return mSaveCount; -} - -int OpenGLRenderer::save(int flags) { - return saveSnapshot(flags); -} - -void OpenGLRenderer::restore() { - if (mSaveCount > 1) { - restoreSnapshot(); - } -} - -void OpenGLRenderer::restoreToCount(int saveCount) { - if (saveCount < 1) saveCount = 1; - - while (mSaveCount > saveCount) { - restoreSnapshot(); - } -} - -int OpenGLRenderer::saveSnapshot(int flags) { - mSnapshot = new Snapshot(mSnapshot, flags); - return mSaveCount++; -} - -bool OpenGLRenderer::restoreSnapshot() { - bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet; - bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer; - bool restoreOrtho = mSnapshot->flags & Snapshot::kFlagDirtyOrtho; - - sp<Snapshot> current = mSnapshot; - sp<Snapshot> previous = mSnapshot->previous; +void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) { + bool restoreOrtho = removed.flags & Snapshot::kFlagDirtyOrtho; + bool restoreClip = removed.flags & Snapshot::kFlagClipSet; + bool restoreLayer = removed.flags & Snapshot::kFlagIsLayer; if (restoreOrtho) { - Rect& r = previous->viewport; + const Rect& r = restored.viewport; glViewport(r.left, r.top, r.right, r.bottom); - mOrthoMatrix.load(current->orthoMatrix); + mViewProjMatrix.load(removed.orthoMatrix); // TODO: should ortho be stored in 'restored'? } - mSaveCount--; - mSnapshot = previous; - if (restoreClip) { dirtyClip(); } @@ -766,11 +721,9 @@ bool OpenGLRenderer::restoreSnapshot() { if (restoreLayer) { endMark(); // Savelayer startMark("ComposeLayer"); - composeLayer(current, previous); + composeLayer(removed, restored); endMark(); } - - return restoreClip; } /////////////////////////////////////////////////////////////////////////////// @@ -779,11 +732,10 @@ bool OpenGLRenderer::restoreSnapshot() { int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode, int flags) { - const GLuint previousFbo = mSnapshot->fbo; const int count = saveSnapshot(flags); - if (!mSnapshot->isIgnored()) { - createLayer(left, top, right, bottom, alpha, mode, flags, previousFbo); + if (!currentSnapshot()->isIgnored()) { + createLayer(left, top, right, bottom, alpha, mode, flags); } return count; @@ -792,22 +744,22 @@ int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom, void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer) { const Rect untransformedBounds(bounds); - currentTransform().mapRect(bounds); + currentTransform()->mapRect(bounds); // Layers only make sense if they are in the framebuffer's bounds - if (bounds.intersect(*mSnapshot->clipRect)) { + if (bounds.intersect(*currentClipRect())) { // We cannot work with sub-pixels in this case bounds.snapToPixelBoundaries(); // When the layer is not an FBO, we may use glCopyTexImage so we // need to make sure the layer does not extend outside the bounds // of the framebuffer - if (!bounds.intersect(mSnapshot->previous->viewport)) { + if (!bounds.intersect(currentSnapshot()->previous->viewport)) { bounds.setEmpty(); } else if (fboLayer) { clip.set(bounds); mat4 inverse; - inverse.loadInverse(currentTransform()); + inverse.loadInverse(*currentTransform()); inverse.mapRect(clip); clip.snapToPixelBoundaries(); if (clip.intersect(untransformedBounds)) { @@ -835,10 +787,9 @@ void OpenGLRenderer::updateSnapshotIgnoreForLayer(const Rect& bounds, const Rect int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode, int flags) { - const GLuint previousFbo = mSnapshot->fbo; const int count = saveSnapshot(flags); - if (!mSnapshot->isIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) { + if (!currentSnapshot()->isIgnored() && (flags & SkCanvas::kClipToLayer_SaveFlag)) { // initialize the snapshot as though it almost represents an FBO layer so deferred draw // operations will be able to store and restore the current clip and transform info, and // quick rejection will be correct (for display lists) @@ -848,7 +799,7 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float calculateLayerBoundsAndClip(bounds, clip, true); updateSnapshotIgnoreForLayer(bounds, clip, true, alpha); - if (!mSnapshot->isIgnored()) { + if (!currentSnapshot()->isIgnored()) { mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); @@ -911,7 +862,7 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float * something actually gets drawn are the layers regions cleared. */ bool OpenGLRenderer::createLayer(float left, float top, float right, float bottom, - int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo) { + int alpha, SkXfermode::Mode mode, int flags) { LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top); LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize()); @@ -924,7 +875,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, alpha); // Bail out if we won't draw in this snapshot - if (mSnapshot->isIgnored()) { + if (currentSnapshot()->isIgnored()) { return false; } @@ -948,7 +899,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto startMark("SaveLayer"); if (fboLayer) { - return createFboLayer(layer, bounds, clip, previousFbo); + return createFboLayer(layer, bounds, clip); } else { // Copy the framebuffer into the layer layer->bindTexture(); @@ -974,7 +925,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto return true; } -bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLuint previousFbo) { +bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { layer->clipRect.set(clip); layer->setFbo(mCaches.fboCache.get()); @@ -986,7 +937,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLui mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); mSnapshot->height = bounds.getHeight(); - mSnapshot->orthoMatrix.load(mOrthoMatrix); + mSnapshot->orthoMatrix.load(mViewProjMatrix); endTiling(); debugOverdraw(false, false); @@ -1003,7 +954,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLui glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, layer->getTexture(), 0); - startTiling(mSnapshot, true); + startTilingCurrentClip(true); // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering mCaches.enableScissor(); @@ -1015,7 +966,9 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLui // Change the ortho projection glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); - mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); + + // TODO: determine best way to support 3d drawing within HW layers + mViewProjMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); return true; } @@ -1023,18 +976,19 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLui /** * Read the documentation of createLayer() before doing anything in this method. */ -void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { - if (!current->layer) { +void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& restored) { + if (!removed.layer) { ALOGE("Attempting to compose a layer that does not exist"); return; } - Layer* layer = current->layer; + Layer* layer = removed.layer; const Rect& rect = layer->layer; - const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer; + const bool fboLayer = removed.flags & Snapshot::kFlagIsFboLayer; bool clipRequired = false; - quickRejectNoScissor(rect, &clipRequired); // safely ignore return, should never be rejected + calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom, + &clipRequired, false); // safely ignore return, should never be rejected mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired); if (fboLayer) { @@ -1046,10 +1000,10 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { layer->removeFbo(false); // Unbind current FBO and restore previous one - glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo); + glBindFramebuffer(GL_FRAMEBUFFER, restored.fbo); debugOverdraw(true, false); - startTiling(previous); + startTilingCurrentClip(); } if (!fboLayer && layer->getAlpha() < 255) { @@ -1066,7 +1020,7 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { // When the layer is stored in an FBO, we can save a bit of fillrate by // drawing only the dirty region if (fboLayer) { - dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *previous->transform); + dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *restored.transform); if (layer->getColorFilter()) { setupColorFilter(layer->getColorFilter()); } @@ -1115,20 +1069,22 @@ void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { } else { setupDrawExternalTexture(layer->getTexture()); } - if (currentTransform().isPureTranslate() && + if (currentTransform()->isPureTranslate() && layer->getWidth() == (uint32_t) rect.getWidth() && layer->getHeight() == (uint32_t) rect.getHeight()) { - const float x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f); - const float y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f); + const float x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f); + const float y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f); layer->setFilter(GL_NEAREST); - setupDrawModelView(x, y, x + rect.getWidth(), y + rect.getHeight(), true); + setupDrawModelView(kModelViewMode_TranslateAndScale, false, + x, y, x + rect.getWidth(), y + rect.getHeight(), true); } else { layer->setFilter(GL_LINEAR); - setupDrawModelView(rect.left, rect.top, rect.right, rect.bottom); + setupDrawModelView(kModelViewMode_TranslateAndScale, false, + rect.left, rect.top, rect.right, rect.bottom); } setupDrawTextureTransformUniforms(layer->getTexTransform()); - setupDrawMesh(&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]); + setupDrawMesh(&mMeshVertices[0].x, &mMeshVertices[0].u); glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); } @@ -1141,15 +1097,15 @@ void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) float x = rect.left; float y = rect.top; - bool simpleTransform = currentTransform().isPureTranslate() && + bool simpleTransform = currentTransform()->isPureTranslate() && layer->getWidth() == (uint32_t) rect.getWidth() && layer->getHeight() == (uint32_t) rect.getHeight(); if (simpleTransform) { // When we're swapping, the layer is already in screen coordinates if (!swap) { - x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f); - y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f); + x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f); + y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f); } layer->setFilter(GL_NEAREST, true); @@ -1161,7 +1117,7 @@ void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) bool blend = layer->isBlend() || alpha < 1.0f; drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(), layer->getTexture(), alpha, layer->getMode(), blend, - &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], + &mMeshVertices[0].x, &mMeshVertices[0].u, GL_TRIANGLE_STRIP, gMeshCount, swap, swap || simpleTransform); resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); @@ -1232,17 +1188,19 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); setupDrawTexture(layer->getTexture()); - if (currentTransform().isPureTranslate()) { - const float x = (int) floorf(rect.left + currentTransform().getTranslateX() + 0.5f); - const float y = (int) floorf(rect.top + currentTransform().getTranslateY() + 0.5f); + if (currentTransform()->isPureTranslate()) { + const float x = (int) floorf(rect.left + currentTransform()->getTranslateX() + 0.5f); + const float y = (int) floorf(rect.top + currentTransform()->getTranslateY() + 0.5f); layer->setFilter(GL_NEAREST); - setupDrawModelViewTranslate(x, y, x + rect.getWidth(), y + rect.getHeight(), true); + setupDrawModelView(kModelViewMode_Translate, false, + x, y, x + rect.getWidth(), y + rect.getHeight(), true); } else { layer->setFilter(GL_LINEAR); - setupDrawModelViewTranslate(rect.left, rect.top, rect.right, rect.bottom); + setupDrawModelView(kModelViewMode_Translate, false, + rect.left, rect.top, rect.right, rect.bottom); } - setupDrawMeshIndices(&mesh[0].position[0], &mesh[0].texture[0]); + setupDrawMeshIndices(&mesh[0].x, &mesh[0].u); for (size_t i = 0; i < count; i++) { const android::Rect* r = &rects[i]; @@ -1274,15 +1232,15 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { } #if DEBUG_LAYERS_AS_REGIONS - drawRegionRects(layer->region); + drawRegionRectsDebug(layer->region); #endif layer->region.clear(); } } -void OpenGLRenderer::drawRegionRects(const Region& region) { #if DEBUG_LAYERS_AS_REGIONS +void OpenGLRenderer::drawRegionRectsDebug(const Region& region) { size_t count; const android::Rect* rects = region.getArray(&count); @@ -1304,8 +1262,8 @@ void OpenGLRenderer::drawRegionRects(const Region& region) { drawColorRect(r.left, r.top, r.right, r.bottom, colors[offset + (i & 0x1)], SkXfermode::kSrcOver_Mode); } -#endif } +#endif void OpenGLRenderer::drawRegionRects(const SkRegion& region, int color, SkXfermode::Mode mode, bool dirty) { @@ -1342,7 +1300,7 @@ void OpenGLRenderer::dirtyLayer(const float left, const float top, } void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) { - if (bounds.intersect(*mSnapshot->clipRect)) { + if (bounds.intersect(*currentClipRect())) { bounds.snapToPixelBoundaries(); android::Rect dirty(bounds.left, bounds.top, bounds.right, bounds.bottom); if (!dirty.isEmpty()) { @@ -1351,12 +1309,12 @@ void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) { } } -void OpenGLRenderer::drawIndexedQuads(Vertex* mesh, GLsizei quadsCount) { +void OpenGLRenderer::issueIndexedQuadDraw(Vertex* mesh, GLsizei quadsCount) { GLsizei elementsCount = quadsCount * 6; while (elementsCount > 0) { GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6); - setupDrawIndexedVertices(&mesh[0].position[0]); + setupDrawIndexedVertices(&mesh[0].x); glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL); elementsCount -= drawCount; @@ -1370,7 +1328,7 @@ void OpenGLRenderer::clearLayerRegions() { const size_t count = mLayers.size(); if (count == 0) return; - if (!mSnapshot->isIgnored()) { + if (!currentSnapshot()->isIgnored()) { // Doing several glScissor/glClear here can negatively impact // GPUs with a tiler architecture, instead we draw quads with // the Clear blending mode @@ -1403,9 +1361,10 @@ void OpenGLRenderer::clearLayerRegions() { setupDrawBlending(true, SkXfermode::kClear_Mode); setupDrawProgram(); setupDrawPureColorUniforms(); - setupDrawModelViewTranslate(0.0f, 0.0f, 0.0f, 0.0f, true); + setupDrawModelView(kModelViewMode_Translate, false, + 0.0f, 0.0f, 0.0f, 0.0f, true); - drawIndexedQuads(&mesh[0], count); + issueIndexedQuadDraw(&mesh[0], count); if (scissorChanged) mCaches.enableScissor(); } else { @@ -1421,58 +1380,58 @@ void OpenGLRenderer::clearLayerRegions() { /////////////////////////////////////////////////////////////////////////////// bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDeferFlags) { - const Rect& currentClip = *(mSnapshot->clipRect); - const mat4& currentMatrix = *(mSnapshot->transform); + const Rect* currentClip = currentClipRect(); + const mat4* currentMatrix = currentTransform(); if (stateDeferFlags & kStateDeferFlag_Draw) { // state has bounds initialized in local coordinates if (!state.mBounds.isEmpty()) { - currentMatrix.mapRect(state.mBounds); + currentMatrix->mapRect(state.mBounds); Rect clippedBounds(state.mBounds); // NOTE: if we ever want to use this clipping info to drive whether the scissor // is used, it should more closely duplicate the quickReject logic (in how it uses // snapToPixelBoundaries) - if(!clippedBounds.intersect(currentClip)) { + if(!clippedBounds.intersect(*currentClip)) { // quick rejected return true; } state.mClipSideFlags = kClipSide_None; - if (!currentClip.contains(state.mBounds)) { + if (!currentClip->contains(state.mBounds)) { int& flags = state.mClipSideFlags; // op partially clipped, so record which sides are clipped for clip-aware merging - if (currentClip.left > state.mBounds.left) flags |= kClipSide_Left; - if (currentClip.top > state.mBounds.top) flags |= kClipSide_Top; - if (currentClip.right < state.mBounds.right) flags |= kClipSide_Right; - if (currentClip.bottom < state.mBounds.bottom) flags |= kClipSide_Bottom; + if (currentClip->left > state.mBounds.left) flags |= kClipSide_Left; + if (currentClip->top > state.mBounds.top) flags |= kClipSide_Top; + if (currentClip->right < state.mBounds.right) flags |= kClipSide_Right; + if (currentClip->bottom < state.mBounds.bottom) flags |= kClipSide_Bottom; } state.mBounds.set(clippedBounds); } else { // Empty bounds implies size unknown. Label op as conservatively clipped to disable // overdraw avoidance (since we don't know what it overlaps) state.mClipSideFlags = kClipSide_ConservativeFull; - state.mBounds.set(currentClip); + state.mBounds.set(*currentClip); } } state.mClipValid = (stateDeferFlags & kStateDeferFlag_Clip); if (state.mClipValid) { - state.mClip.set(currentClip); + state.mClip.set(*currentClip); } // Transform, drawModifiers, and alpha always deferred, since they are used by state operations // (Note: saveLayer/restore use colorFilter and alpha, so we just save restore everything) - state.mMatrix.load(currentMatrix); + state.mMatrix.load(*currentMatrix); state.mDrawModifiers = mDrawModifiers; - state.mAlpha = mSnapshot->alpha; + state.mAlpha = currentSnapshot()->alpha; return false; } void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore) { - currentTransform().load(state.mMatrix); - mDrawModifiers = state.mDrawModifiers; + setMatrix(state.mMatrix); mSnapshot->alpha = state.mAlpha; + mDrawModifiers = state.mDrawModifiers; if (state.mClipValid && !skipClipRestore) { mSnapshot->setClip(state.mClip.left, state.mClip.top, @@ -1492,64 +1451,21 @@ void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) { if (clipRect != NULL) { mSnapshot->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom); } else { - mSnapshot->setClip(0, 0, mWidth, mHeight); + mSnapshot->setClip(0, 0, getWidth(), getHeight()); } dirtyClip(); mCaches.setScissorEnabled(clipRect != NULL || mScissorOptimizationDisabled); } /////////////////////////////////////////////////////////////////////////////// -// Transforms -/////////////////////////////////////////////////////////////////////////////// - -void OpenGLRenderer::translate(float dx, float dy) { - currentTransform().translate(dx, dy); -} - -void OpenGLRenderer::rotate(float degrees) { - currentTransform().rotate(degrees, 0.0f, 0.0f, 1.0f); -} - -void OpenGLRenderer::scale(float sx, float sy) { - currentTransform().scale(sx, sy, 1.0f); -} - -void OpenGLRenderer::skew(float sx, float sy) { - currentTransform().skew(sx, sy); -} - -void OpenGLRenderer::setMatrix(SkMatrix* matrix) { - if (matrix) { - currentTransform().load(*matrix); - } else { - currentTransform().loadIdentity(); - } -} - -bool OpenGLRenderer::hasRectToRectTransform() { - return CC_LIKELY(currentTransform().rectToRect()); -} - -void OpenGLRenderer::getMatrix(SkMatrix* matrix) { - currentTransform().copyTo(*matrix); -} - -void OpenGLRenderer::concatMatrix(SkMatrix* matrix) { - SkMatrix transform; - currentTransform().copyTo(transform); - transform.preConcat(*matrix); - currentTransform().load(transform); -} - -/////////////////////////////////////////////////////////////////////////////// // Clipping /////////////////////////////////////////////////////////////////////////////// void OpenGLRenderer::setScissorFromClip() { - Rect clip(*mSnapshot->clipRect); + Rect clip(*currentClipRect()); clip.snapToPixelBoundaries(); - if (mCaches.setScissor(clip.left, mSnapshot->height - clip.bottom, + if (mCaches.setScissor(clip.left, currentSnapshot()->height - clip.bottom, clip.getWidth(), clip.getHeight())) { mDirtyClip = false; } @@ -1560,7 +1476,7 @@ void OpenGLRenderer::ensureStencilBuffer() { // cannot attach a stencil buffer to fbo0 dynamically. Let's // just hope we have one when hasLayer() returns false. if (hasLayer()) { - attachStencilBufferToLayer(mSnapshot->layer); + attachStencilBufferToLayer(currentSnapshot()->layer); } } @@ -1582,7 +1498,7 @@ void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) { void OpenGLRenderer::setStencilFromClip() { if (!mCaches.debugOverdraw) { - if (!mSnapshot->clipRegion->isEmpty()) { + if (!currentSnapshot()->clipRegion->isEmpty()) { // NOTE: The order here is important, we must set dirtyClip to false // before any draw call to avoid calling back into this method mDirtyClip = false; @@ -1607,14 +1523,16 @@ void OpenGLRenderer::setStencilFromClip() { // The last parameter is important: we are not drawing in the color buffer // so we don't want to dirty the current layer, if any - drawRegionRects(*mSnapshot->clipRegion, 0xff000000, SkXfermode::kSrc_Mode, false); + drawRegionRects(*(currentSnapshot()->clipRegion), + 0xff000000, SkXfermode::kSrc_Mode, false); mCaches.stencil.enableTest(); // Draw the region used to generate the stencil if the appropriate debug // mode is enabled if (mCaches.debugStencilClip == Caches::kStencilShowRegion) { - drawRegionRects(*mSnapshot->clipRegion, 0x7f0000ff, SkXfermode::kSrcOver_Mode); + drawRegionRects(*(currentSnapshot()->clipRegion), + 0x7f0000ff, SkXfermode::kSrcOver_Mode); } } else { mCaches.stencil.disable(); @@ -1622,50 +1540,31 @@ void OpenGLRenderer::setStencilFromClip() { } } -const Rect& OpenGLRenderer::getClipBounds() { - return mSnapshot->getLocalClip(); -} - -bool OpenGLRenderer::quickRejectNoScissor(float left, float top, float right, float bottom, - bool snapOut, bool* clipRequired) { - if (mSnapshot->isIgnored() || bottom <= top || right <= left) { - return true; - } - - Rect r(left, top, right, bottom); - currentTransform().mapRect(r); - r.snapGeometryToPixelBoundaries(snapOut); - - Rect clipRect(*mSnapshot->clipRect); - clipRect.snapToPixelBoundaries(); - - if (!clipRect.intersects(r)) return true; - - if (clipRequired) *clipRequired = !clipRect.contains(r); - return false; -} - -bool OpenGLRenderer::quickRejectPreStroke(float left, float top, float right, float bottom, - SkPaint* paint) { - // AA geometry will likely have a ramp around it (not accounted for in local bounds). Snap out - // the final mapped rect to ensure correct clipping behavior for the ramp. - bool snapOut = paint->isAntiAlias(); +/** + * Returns false and sets scissor enable based upon bounds if drawing won't be clipped out. + * + * @param paint if not null, the bounds will be expanded to account for stroke depending on paint + * style, and tessellated AA ramp + */ +bool OpenGLRenderer::quickRejectSetupScissor(float left, float top, float right, float bottom, + const SkPaint* paint) { + bool clipRequired = false; + bool snapOut = paint && paint->isAntiAlias(); - if (paint->getStyle() != SkPaint::kFill_Style) { + if (paint && paint->getStyle() != SkPaint::kFill_Style) { float outset = paint->getStrokeWidth() * 0.5f; - return quickReject(left - outset, top - outset, right + outset, bottom + outset, snapOut); - } else { - return quickReject(left, top, right, bottom, snapOut); + left -= outset; + top -= outset; + right += outset; + bottom += outset; } -} -bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom, bool snapOut) { - bool clipRequired = false; - if (quickRejectNoScissor(left, top, right, bottom, snapOut, &clipRequired)) { + if (calculateQuickRejectForScissor(left, top, right, bottom, &clipRequired, snapOut)) { return true; } - if (!isDeferred()) { + if (!isRecording()) { + // not quick rejected, so enable the scissor if clipRequired mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired); } return false; @@ -1673,74 +1572,18 @@ bool OpenGLRenderer::quickReject(float left, float top, float right, float botto void OpenGLRenderer::debugClip() { #if DEBUG_CLIP_REGIONS - if (!isDeferred() && !mSnapshot->clipRegion->isEmpty()) { - drawRegionRects(*mSnapshot->clipRegion, 0x7f00ff00, SkXfermode::kSrcOver_Mode); + if (!isRecording() && !currentSnapshot()->clipRegion->isEmpty()) { + drawRegionRects(*(currentSnapshot()->clipRegion), 0x7f00ff00, SkXfermode::kSrcOver_Mode); } #endif } -bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { - if (CC_LIKELY(currentTransform().rectToRect())) { - bool clipped = mSnapshot->clip(left, top, right, bottom, op); - if (clipped) { - dirtyClip(); - } - return !mSnapshot->clipRect->isEmpty(); - } - - SkPath path; - path.addRect(left, top, right, bottom); - - return OpenGLRenderer::clipPath(&path, op); -} - -bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) { - SkMatrix transform; - currentTransform().copyTo(transform); - - SkPath transformed; - path->transform(transform, &transformed); - - SkRegion clip; - if (!mSnapshot->previous->clipRegion->isEmpty()) { - clip.setRegion(*mSnapshot->previous->clipRegion); - } else { - if (mSnapshot->previous == mFirstSnapshot) { - clip.setRect(0, 0, mWidth, mHeight); - } else { - Rect* bounds = mSnapshot->previous->clipRect; - clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom); - } - } - - SkRegion region; - region.setPath(transformed, clip); - - bool clipped = mSnapshot->clipRegionTransformed(region, op); - if (clipped) { - dirtyClip(); - } - return !mSnapshot->clipRect->isEmpty(); -} - -bool OpenGLRenderer::clipRegion(SkRegion* region, SkRegion::Op op) { - bool clipped = mSnapshot->clipRegionTransformed(*region, op); - if (clipped) { - dirtyClip(); - } - return !mSnapshot->clipRect->isEmpty(); -} - -Rect* OpenGLRenderer::getClipRect() { - return mSnapshot->clipRect; -} - /////////////////////////////////////////////////////////////////////////////// // Drawing commands /////////////////////////////////////////////////////////////////////////////// void OpenGLRenderer::setupDraw(bool clear) { - // TODO: It would be best if we could do this before quickReject() + // TODO: It would be best if we could do this before quickRejectSetupScissor() // changes the scissor test state if (clear) clearLayerRegions(); // Make sure setScissor & setStencil happen at the beginning of @@ -1798,7 +1641,7 @@ void OpenGLRenderer::setupDrawColor(int color, int alpha) { mColorG = mColorA * ((color >> 8) & 0xFF) / 255.0f; mColorB = mColorA * ((color ) & 0xFF) / 255.0f; mColorSet = true; - mSetShaderColor = mDescription.setColor(mColorR, mColorG, mColorB, mColorA); + mSetShaderColor = mDescription.setColorModulate(mColorA); } void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) { @@ -1807,7 +1650,7 @@ void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) { mColorG = mColorA * ((color >> 8) & 0xFF) / 255.0f; mColorB = mColorA * ((color ) & 0xFF) / 255.0f; mColorSet = true; - mSetShaderColor = mDescription.setAlpha8Color(mColorR, mColorG, mColorB, mColorA); + mSetShaderColor = mDescription.setAlpha8ColorModulate(mColorR, mColorG, mColorB, mColorA); } void OpenGLRenderer::setupDrawTextGamma(const SkPaint* paint) { @@ -1820,7 +1663,7 @@ void OpenGLRenderer::setupDrawColor(float r, float g, float b, float a) { mColorG = g; mColorB = b; mColorSet = true; - mSetShaderColor = mDescription.setColor(r, g, b, a); + mSetShaderColor = mDescription.setColorModulate(a); } void OpenGLRenderer::setupDrawShader() { @@ -1870,39 +1713,20 @@ void OpenGLRenderer::setupDrawDirtyRegionsDisabled() { mTrackDirtyRegions = false; } -void OpenGLRenderer::setupDrawModelViewTranslate(float left, float top, float right, float bottom, - bool ignoreTransform) { +void OpenGLRenderer::setupDrawModelView(ModelViewMode mode, bool offset, + float left, float top, float right, float bottom, bool ignoreTransform) { mModelView.loadTranslate(left, top, 0.0f); - if (!ignoreTransform) { - mCaches.currentProgram->set(mOrthoMatrix, mModelView, currentTransform()); - if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, currentTransform()); - } else { - mCaches.currentProgram->set(mOrthoMatrix, mModelView, mat4::identity()); - if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom); - } -} - -void OpenGLRenderer::setupDrawModelViewIdentity(bool offset) { - mCaches.currentProgram->set(mOrthoMatrix, mat4::identity(), currentTransform(), offset); -} - -void OpenGLRenderer::setupDrawModelView(float left, float top, float right, float bottom, - bool ignoreTransform, bool ignoreModelView) { - if (!ignoreModelView) { - mModelView.loadTranslate(left, top, 0.0f); + if (mode == kModelViewMode_TranslateAndScale) { mModelView.scale(right - left, bottom - top, 1.0f); - } else { - mModelView.loadIdentity(); } + bool dirty = right - left > 0.0f && bottom - top > 0.0f; if (!ignoreTransform) { - mCaches.currentProgram->set(mOrthoMatrix, mModelView, currentTransform()); - if (mTrackDirtyRegions && dirty) { - dirtyLayer(left, top, right, bottom, currentTransform()); - } + mCaches.currentProgram->set(mViewProjMatrix, mModelView, *currentTransform(), offset); + if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, *currentTransform()); } else { - mCaches.currentProgram->set(mOrthoMatrix, mModelView, mat4::identity()); - if (mTrackDirtyRegions && dirty) dirtyLayer(left, top, right, bottom); + mCaches.currentProgram->set(mViewProjMatrix, mModelView, mat4::identity(), offset); + if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom); } } @@ -1921,20 +1745,19 @@ void OpenGLRenderer::setupDrawPureColorUniforms() { void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) { if (mDrawModifiers.mShader) { if (ignoreTransform) { - mModelView.loadInverse(currentTransform()); + // if ignoreTransform=true was passed to setupDrawModelView, undo currentTransform() + // because it was built into modelView / the geometry, and the SkiaShader needs to + // compensate. + mat4 modelViewWithoutTransform; + modelViewWithoutTransform.loadInverse(*currentTransform()); + modelViewWithoutTransform.multiply(mModelView); + mModelView.load(modelViewWithoutTransform); } mDrawModifiers.mShader->setupProgram(mCaches.currentProgram, mModelView, *mSnapshot, &mTextureUnit); } } -void OpenGLRenderer::setupDrawShaderIdentityUniforms() { - if (mDrawModifiers.mShader) { - mDrawModifiers.mShader->setupProgram(mCaches.currentProgram, - mat4::identity(), *mSnapshot, &mTextureUnit); - } -} - void OpenGLRenderer::setupDrawColorFilterUniforms() { if (mDrawModifiers.mColorFilter) { mDrawModifiers.mColorFilter->setupProgram(mCaches.currentProgram); @@ -1972,7 +1795,8 @@ void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) { GL_FALSE, &transform.data[0]); } -void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) { +void OpenGLRenderer::setupDrawMesh(const GLvoid* vertices, + const GLvoid* texCoords, GLuint vbo) { bool force = false; if (!vertices || vbo) { force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo); @@ -1988,7 +1812,8 @@ void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint v mCaches.unbindIndicesBuffer(); } -void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLvoid* colors) { +void OpenGLRenderer::setupDrawMesh(const GLvoid* vertices, + const GLvoid* texCoords, const GLvoid* colors) { bool force = mCaches.unbindMeshBuffer(); GLsizei stride = sizeof(ColorTextureVertex); @@ -2005,7 +1830,8 @@ void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLvoid* mCaches.unbindIndicesBuffer(); } -void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) { +void OpenGLRenderer::setupDrawMeshIndices(const GLvoid* vertices, + const 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 @@ -2036,9 +1862,18 @@ void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) { status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t replayFlags) { status_t status; + + if (mCaches.propertyDirtyViewport) { + // force recalc of view/proj matrices + setViewport(getWidth(), getHeight()); + mCaches.propertyDirtyViewport = false; + } + // All the usual checks and setup operations (quickReject, setupDraw, etc.) // will be performed by the display list itself if (displayList && displayList->isRenderable()) { + // compute 3d ordering + displayList->computeOrdering(); if (CC_UNLIKELY(mCaches.drawDeferDisabled)) { status = startFrame(); ReplayStateStruct replayStruct(*this, dirty, replayFlags); @@ -2047,26 +1882,20 @@ status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty, } bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs! - DeferredDisplayList deferredList(*(mSnapshot->clipRect), avoidOverdraw); + DeferredDisplayList deferredList(*currentClipRect(), avoidOverdraw); DeferStateStruct deferStruct(deferredList, *this, replayFlags); displayList->defer(deferStruct, 0); flushLayers(); status = startFrame(); - return status | deferredList.flush(*this, dirty); + return deferredList.flush(*this, dirty) | status; } return DrawGlInfo::kStatusDone; } -void OpenGLRenderer::outputDisplayList(DisplayList* displayList) { - if (displayList) { - displayList->output(1); - } -} - -void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint) { +void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, const SkPaint* paint) { int alpha; SkXfermode::Mode mode; getAlphaAndMode(paint, &alpha, &mode); @@ -2079,9 +1908,9 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, Sk texture->setWrap(GL_CLAMP_TO_EDGE, true); bool ignoreTransform = false; - if (currentTransform().isPureTranslate()) { - x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f); - y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f); + if (currentTransform()->isPureTranslate()) { + x = (int) floorf(left + currentTransform()->getTranslateX() + 0.5f); + y = (int) floorf(top + currentTransform()->getTranslateY() + 0.5f); ignoreTransform = true; texture->setFilter(GL_NEAREST, true); @@ -2101,8 +1930,9 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, Sk * will not set the scissor enable or dirty the current layer, if any. * The caller is responsible for properly dirtying the current layer. */ -status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount, - TextureVertex* vertices, bool pureTranslate, const Rect& bounds, SkPaint* paint) { +status_t OpenGLRenderer::drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, + int bitmapCount, TextureVertex* vertices, bool pureTranslate, + const Rect& bounds, const SkPaint* paint) { mCaches.activeTexture(0); Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap); if (!texture) return DrawGlInfo::kStatusDone; @@ -2118,27 +1948,30 @@ status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, AssetAtlas::Entry* entry, const float x = (int) floorf(bounds.left + 0.5f); const float y = (int) floorf(bounds.top + 0.5f); - if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) { + if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) { int color = paint != NULL ? paint->getColor() : 0; drawAlpha8TextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(), texture->id, paint != NULL, color, alpha, mode, - &vertices[0].position[0], &vertices[0].texture[0], - GL_TRIANGLES, bitmapCount * 6, true, true, false); + &vertices[0].x, &vertices[0].u, + GL_TRIANGLES, bitmapCount * 6, true, + kModelViewMode_Translate, false); } else { drawTextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(), texture->id, alpha / 255.0f, mode, texture->blend, - &vertices[0].position[0], &vertices[0].texture[0], - GL_TRIANGLES, bitmapCount * 6, false, true, 0, true, false); + &vertices[0].x, &vertices[0].u, + GL_TRIANGLES, bitmapCount * 6, false, true, 0, + kModelViewMode_Translate, false); } return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) { +status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint) { const float right = left + bitmap->width(); const float bottom = top + bitmap->height(); - if (quickReject(left, top, right, bottom)) { + if (quickRejectSetupScissor(left, top, right, bottom)) { return DrawGlInfo::kStatusDone; } @@ -2147,7 +1980,7 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkP if (!texture) return DrawGlInfo::kStatusDone; const AutoTexture autoCleanup(texture); - if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) { + if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) { drawAlphaBitmap(texture, left, top, paint); } else { drawTextureRect(left, top, right, bottom, texture, paint); @@ -2156,12 +1989,13 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkP return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) { +status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + const SkPaint* paint) { Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height()); const mat4 transform(*matrix); transform.mapRect(r); - if (quickReject(r.left, r.top, r.right, r.bottom)) { + if (quickRejectSetupScissor(r.left, r.top, r.right, r.bottom)) { return DrawGlInfo::kStatusDone; } @@ -2174,7 +2008,7 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* // to the vertex shader. The save/restore is a bit overkill. save(SkCanvas::kMatrix_SaveFlag); concatMatrix(matrix); - if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) { + if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) { drawAlphaBitmap(texture, 0.0f, 0.0f, paint); } else { drawTextureRect(0.0f, 0.0f, bitmap->width(), bitmap->height(), texture, paint); @@ -2184,11 +2018,12 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint) { +status_t OpenGLRenderer::drawBitmapData(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint) { const float right = left + bitmap->width(); const float bottom = top + bitmap->height(); - if (quickReject(left, top, right, bottom)) { + if (quickRejectSetupScissor(left, top, right, bottom)) { return DrawGlInfo::kStatusDone; } @@ -2196,7 +2031,7 @@ status_t OpenGLRenderer::drawBitmapData(SkBitmap* bitmap, float left, float top, Texture* texture = mCaches.textureCache.getTransient(bitmap); const AutoTexture autoCleanup(texture); - if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) { + if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) { drawAlphaBitmap(texture, left, top, paint); } else { drawTextureRect(left, top, right, bottom, texture, paint); @@ -2205,9 +2040,9 @@ status_t OpenGLRenderer::drawBitmapData(SkBitmap* bitmap, float left, float top, return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight, - float* vertices, int* colors, SkPaint* paint) { - if (!vertices || mSnapshot->isIgnored()) { +status_t OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, + const float* vertices, const int* colors, const SkPaint* paint) { + if (!vertices || currentSnapshot()->isIgnored()) { return DrawGlInfo::kStatusDone; } @@ -2221,14 +2056,16 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes const uint32_t count = meshWidth * meshHeight * 6; - ColorTextureVertex mesh[count]; - ColorTextureVertex* vertex = mesh; + Vector<ColorTextureVertex> mesh; // TODO: use C++11 unique_ptr + mesh.setCapacity(count); + ColorTextureVertex* vertex = mesh.editArray(); bool cleanupColors = false; if (!colors) { uint32_t colorsCount = (meshWidth + 1) * (meshHeight + 1); - colors = new int[colorsCount]; - memset(colors, 0xff, colorsCount * sizeof(int)); + int* newColors = new int[colorsCount]; + memset(newColors, 0xff, colorsCount * sizeof(int)); + colors = newColors; cleanupColors = true; } @@ -2271,7 +2108,7 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes } } - if (quickReject(left, top, right, bottom)) { + if (quickRejectSetupScissor(left, top, right, bottom)) { if (cleanupColors) delete[] colors; return DrawGlInfo::kStatusDone; } @@ -2295,7 +2132,7 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes float a = alpha / 255.0f; if (hasLayer()) { - dirtyLayer(left, top, right, bottom, currentTransform()); + dirtyLayer(left, top, right, bottom, *currentTransform()); } setupDraw(); @@ -2305,11 +2142,11 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes setupDrawBlending(true, mode, false); setupDrawProgram(); setupDrawDirtyRegionsDisabled(); - setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f, false); + setupDrawModelView(kModelViewMode_TranslateAndScale, false, 0.0f, 0.0f, 1.0f, 1.0f); setupDrawTexture(texture->id); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); - setupDrawMesh(&mesh[0].position[0], &mesh[0].texture[0], &mesh[0].color[0]); + setupDrawMesh(&mesh[0].x, &mesh[0].u, &mesh[0].r); glDrawArrays(GL_TRIANGLES, 0, count); @@ -2323,11 +2160,11 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, +status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, - SkPaint* paint) { - if (quickReject(dstLeft, dstTop, dstRight, dstBottom)) { + const SkPaint* paint) { + if (quickRejectSetupScissor(dstLeft, dstTop, dstRight, dstBottom)) { return DrawGlInfo::kStatusDone; } @@ -2365,9 +2202,9 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, bool useScaleTransform = mDrawModifiers.mShader && scaled; bool ignoreTransform = false; - if (CC_LIKELY(currentTransform().isPureTranslate() && !useScaleTransform)) { - float x = (int) floorf(dstLeft + currentTransform().getTranslateX() + 0.5f); - float y = (int) floorf(dstTop + currentTransform().getTranslateY() + 0.5f); + if (CC_LIKELY(currentTransform()->isPureTranslate() && !useScaleTransform)) { + float x = (int) floorf(dstLeft + currentTransform()->getTranslateX() + 0.5f); + float y = (int) floorf(dstTop + currentTransform()->getTranslateY() + 0.5f); dstRight = x + (dstRight - dstLeft); dstBottom = y + (dstBottom - dstTop); @@ -2393,16 +2230,16 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, dstBottom = srcBottom - srcTop; } - if (CC_UNLIKELY(bitmap->getConfig() == SkBitmap::kA8_Config)) { + if (CC_UNLIKELY(bitmap->config() == SkBitmap::kA8_Config)) { int color = paint ? paint->getColor() : 0; drawAlpha8TextureMesh(dstLeft, dstTop, dstRight, dstBottom, texture->id, paint != NULL, color, alpha, mode, - &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], + &mMeshVertices[0].x, &mMeshVertices[0].u, GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform); } else { drawTextureMesh(dstLeft, dstTop, dstRight, dstBottom, texture->id, alpha / 255.0f, mode, texture->blend, - &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], + &mMeshVertices[0].x, &mMeshVertices[0].u, GL_TRIANGLE_STRIP, gMeshCount, false, ignoreTransform); } @@ -2415,9 +2252,9 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, - float left, float top, float right, float bottom, SkPaint* paint) { - if (quickReject(left, top, right, bottom)) { +status_t OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, + float left, float top, float right, float bottom, const SkPaint* paint) { + if (quickRejectSetupScissor(left, top, right, bottom)) { return DrawGlInfo::kStatusDone; } @@ -2428,9 +2265,10 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, return drawPatch(bitmap, mesh, entry, left, top, right, bottom, paint); } -status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry, - float left, float top, float right, float bottom, SkPaint* paint) { - if (quickReject(left, top, right, bottom)) { +status_t OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Patch* mesh, + AssetAtlas::Entry* entry, float left, float top, float right, float bottom, + const SkPaint* paint) { + if (quickRejectSetupScissor(left, top, right, bottom)) { return DrawGlInfo::kStatusDone; } @@ -2447,11 +2285,11 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtl SkXfermode::Mode mode; getAlphaAndMode(paint, &alpha, &mode); - const bool pureTranslate = currentTransform().isPureTranslate(); + const bool pureTranslate = currentTransform()->isPureTranslate(); // Mark the current layer dirty where we are going to draw the patch if (hasLayer() && mesh->hasEmptyQuads) { - const float offsetX = left + currentTransform().getTranslateX(); - const float offsetY = top + currentTransform().getTranslateY(); + const float offsetX = left + currentTransform()->getTranslateX(); + const float offsetY = top + currentTransform()->getTranslateY(); const size_t count = mesh->quads.size(); for (size_t i = 0; i < count; i++) { const Rect& bounds = mesh->quads.itemAt(i); @@ -2461,27 +2299,26 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtl dirtyLayer(x, y, x + bounds.getWidth(), y + bounds.getHeight()); } else { dirtyLayer(left + bounds.left, top + bounds.top, - left + bounds.right, top + bounds.bottom, currentTransform()); + left + bounds.right, top + bounds.bottom, *currentTransform()); } } } + bool ignoreTransform = false; if (CC_LIKELY(pureTranslate)) { - const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f); - const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f); + const float x = (int) floorf(left + currentTransform()->getTranslateX() + 0.5f); + const float y = (int) floorf(top + currentTransform()->getTranslateY() + 0.5f); 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 { - 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); + left = x; + top = y; + ignoreTransform = true; } + drawIndexedTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, + mode, texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset, + GL_TRIANGLES, mesh->indexCount, false, ignoreTransform, + mCaches.patchCache.getMeshBuffer(), kModelViewMode_Translate, !mesh->hasEmptyQuads); } return DrawGlInfo::kStatusDrew; @@ -2492,8 +2329,8 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtl * will not set the scissor enable or dirty the current layer, if any. * The caller is responsible for properly dirtying the current layer. */ -status_t OpenGLRenderer::drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry, - TextureVertex* vertices, uint32_t indexCount, SkPaint* paint) { +status_t OpenGLRenderer::drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry, + TextureVertex* vertices, uint32_t indexCount, const SkPaint* paint) { mCaches.activeTexture(0); Texture* texture = entry ? entry->texture : mCaches.textureCache.get(bitmap); if (!texture) return DrawGlInfo::kStatusDone; @@ -2507,14 +2344,16 @@ status_t OpenGLRenderer::drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry, getAlphaAndMode(paint, &alpha, &mode); drawIndexedTextureMesh(0.0f, 0.0f, 1.0f, 1.0f, texture->id, alpha / 255.0f, - mode, texture->blend, &vertices[0].position[0], &vertices[0].texture[0], - GL_TRIANGLES, indexCount, false, true, 0, true, false); + mode, texture->blend, &vertices[0].x, &vertices[0].u, + GL_TRIANGLES, indexCount, false, true, 0, kModelViewMode_Translate, false); return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint, +status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, const SkPaint* paint, bool useOffset) { + // not missing call to quickReject/dirtyLayer, always done at a higher level + if (!vertexBuffer.getVertexCount()) { // no vertices to draw return DrawGlInfo::kStatusDone; @@ -2532,12 +2371,12 @@ status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPa setupDrawShader(); setupDrawBlending(isAA, mode); setupDrawProgram(); - setupDrawModelViewIdentity(useOffset); + setupDrawModelView(kModelViewMode_Translate, useOffset, 0, 0, 0, 0); setupDrawColorUniforms(); setupDrawColorFilterUniforms(); - setupDrawShaderIdentityUniforms(); + setupDrawShaderUniforms(); - void* vertices = vertexBuffer.getBuffer(); + const void* vertices = vertexBuffer.getBuffer(); bool force = mCaches.unbindMeshBuffer(); mCaches.bindPositionVertexPointer(true, vertices, isAA ? gAlphaVertexStride : gVertexStride); mCaches.resetTexCoordsVertexPointer(); @@ -2571,15 +2410,15 @@ status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPa * * Doesn't yet support joins, caps, or path effects. */ -status_t OpenGLRenderer::drawConvexPath(const SkPath& path, SkPaint* paint) { +status_t OpenGLRenderer::drawConvexPath(const SkPath& path, const SkPaint* paint) { VertexBuffer vertexBuffer; // TODO: try clipping large paths to viewport - PathTessellator::tessellatePath(path, paint, mSnapshot->transform, vertexBuffer); + PathTessellator::tessellatePath(path, paint, *currentTransform(), vertexBuffer); if (hasLayer()) { SkRect bounds = path.getBounds(); - PathTessellator::expandBoundsForStroke(bounds, paint, false); - dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform()); + PathTessellator::expandBoundsForStroke(bounds, paint); + dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform()); } return drawVertexBuffer(vertexBuffer, paint); @@ -2596,39 +2435,41 @@ status_t OpenGLRenderer::drawConvexPath(const SkPath& path, SkPaint* paint) { * TODO: try using a fixed input buffer for non-capped lines as in text rendering. this may reduce * memory transfer by removing need for degenerate vertices. */ -status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) { - if (mSnapshot->isIgnored() || count < 4) return DrawGlInfo::kStatusDone; +status_t OpenGLRenderer::drawLines(const float* points, int count, const SkPaint* paint) { + if (currentSnapshot()->isIgnored() || count < 4) return DrawGlInfo::kStatusDone; count &= ~0x3; // round down to nearest four VertexBuffer buffer; SkRect bounds; - PathTessellator::tessellateLines(points, count, paint, mSnapshot->transform, bounds, buffer); + PathTessellator::tessellateLines(points, count, paint, *currentTransform(), bounds, buffer); - if (quickReject(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) { + // can't pass paint, since style would be checked for outset. outset done by tessellation. + if (quickRejectSetupScissor(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) { return DrawGlInfo::kStatusDone; } - dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform()); + dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform()); bool useOffset = !paint->isAntiAlias(); return drawVertexBuffer(buffer, paint, useOffset); } -status_t OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) { - if (mSnapshot->isIgnored() || count < 2) return DrawGlInfo::kStatusDone; +status_t OpenGLRenderer::drawPoints(const float* points, int count, const SkPaint* paint) { + if (currentSnapshot()->isIgnored() || count < 2) return DrawGlInfo::kStatusDone; count &= ~0x1; // round down to nearest two VertexBuffer buffer; SkRect bounds; - PathTessellator::tessellatePoints(points, count, paint, mSnapshot->transform, bounds, buffer); + PathTessellator::tessellatePoints(points, count, paint, *currentTransform(), bounds, buffer); - if (quickReject(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) { + // can't pass paint, since style would be checked for outset. outset done by tessellation. + if (quickRejectSetupScissor(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) { return DrawGlInfo::kStatusDone; } - dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform()); + dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, *currentTransform()); bool useOffset = !paint->isAntiAlias(); return drawVertexBuffer(buffer, paint, useOffset); @@ -2636,9 +2477,9 @@ status_t OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) { status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) { // No need to check against the clip, we fill the clip region - if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone; + if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone; - Rect& clip(*mSnapshot->clipRect); + Rect clip(*currentClipRect()); clip.snapToPixelBoundaries(); drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true); @@ -2647,7 +2488,7 @@ status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) { } status_t OpenGLRenderer::drawShape(float left, float top, const PathTexture* texture, - SkPaint* paint) { + const SkPaint* paint) { if (!texture) return DrawGlInfo::kStatusDone; const AutoTexture autoCleanup(texture); @@ -2660,8 +2501,8 @@ status_t OpenGLRenderer::drawShape(float left, float top, const PathTexture* tex } status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, SkPaint* p) { - if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p) || + float rx, float ry, const SkPaint* p) { + if (currentSnapshot()->isIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) || (p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) { return DrawGlInfo::kStatusDone; } @@ -2685,8 +2526,8 @@ status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float return drawConvexPath(path, p); } -status_t OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* p) { - if (mSnapshot->isIgnored() || quickRejectPreStroke(x - radius, y - radius, +status_t OpenGLRenderer::drawCircle(float x, float y, float radius, const SkPaint* p) { + if (currentSnapshot()->isIgnored() || quickRejectSetupScissor(x - radius, y - radius, x + radius, y + radius, p) || (p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) { return DrawGlInfo::kStatusDone; @@ -2707,8 +2548,8 @@ status_t OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* p) } status_t OpenGLRenderer::drawOval(float left, float top, float right, float bottom, - SkPaint* p) { - if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p) || + const SkPaint* p) { + if (currentSnapshot()->isIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) || (p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) { return DrawGlInfo::kStatusDone; } @@ -2729,8 +2570,8 @@ status_t OpenGLRenderer::drawOval(float left, float top, float right, float bott } status_t OpenGLRenderer::drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, SkPaint* p) { - if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p) || + float startAngle, float sweepAngle, bool useCenter, const SkPaint* p) { + if (currentSnapshot()->isIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) || (p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) { return DrawGlInfo::kStatusDone; } @@ -2766,8 +2607,9 @@ status_t OpenGLRenderer::drawArc(float left, float top, float right, float botto // See SkPaintDefaults.h #define SkPaintDefaults_MiterLimit SkIntToScalar(4) -status_t OpenGLRenderer::drawRect(float left, float top, float right, float bottom, SkPaint* p) { - if (mSnapshot->isIgnored() || quickRejectPreStroke(left, top, right, bottom, p) || +status_t OpenGLRenderer::drawRect(float left, float top, float right, float bottom, + const SkPaint* p) { + if (currentSnapshot()->isIgnored() || quickRejectSetupScissor(left, top, right, bottom, p) || (p->getAlpha() == 0 && getXfermode(p->getXfermode()) != SkXfermode::kClear_Mode)) { return DrawGlInfo::kStatusDone; } @@ -2791,7 +2633,7 @@ status_t OpenGLRenderer::drawRect(float left, float top, float right, float bott return drawConvexPath(path, p); } - if (p->isAntiAlias() && !currentTransform().isSimple()) { + if (p->isAntiAlias() && !currentTransform()->isSimple()) { SkPath path; path.addRect(left, top, right, bottom); return drawConvexPath(path, p); @@ -2801,9 +2643,9 @@ status_t OpenGLRenderer::drawRect(float left, float top, float right, float bott } } -void OpenGLRenderer::drawTextShadow(SkPaint* paint, const char* text, int bytesCount, int count, - const float* positions, FontRenderer& fontRenderer, int alpha, SkXfermode::Mode mode, - float x, float y) { +void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text, + int bytesCount, int count, const float* positions, + FontRenderer& fontRenderer, int alpha, SkXfermode::Mode mode, float x, float y) { mCaches.activeTexture(0); // NOTE: The drop shadow will not perform gamma correction @@ -2832,7 +2674,8 @@ void OpenGLRenderer::drawTextShadow(SkPaint* paint, const char* text, int bytesC setupDrawShader(); setupDrawBlending(true, mode); setupDrawProgram(); - setupDrawModelView(sx, sy, sx + shadow->width, sy + shadow->height); + setupDrawModelView(kModelViewMode_TranslateAndScale, false, + sx, sy, sx + shadow->width, sy + shadow->height); setupDrawTexture(shadow->id); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); @@ -2848,13 +2691,13 @@ bool OpenGLRenderer::canSkipText(const SkPaint* paint) const { } status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count, - const float* positions, SkPaint* paint) { - if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) { + const float* positions, const SkPaint* paint) { + if (text == NULL || count == 0 || currentSnapshot()->isIgnored() || canSkipText(paint)) { return DrawGlInfo::kStatusDone; } // NOTE: Skia does not support perspective transform on drawPosText yet - if (!currentTransform().isSimple()) { + if (!currentTransform()->isSimple()) { return DrawGlInfo::kStatusDone; } @@ -2862,10 +2705,10 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count float x = 0.0f; float y = 0.0f; - const bool pureTranslate = currentTransform().isPureTranslate(); + const bool pureTranslate = currentTransform()->isPureTranslate(); if (pureTranslate) { - x = (int) floorf(x + currentTransform().getTranslateX() + 0.5f); - y = (int) floorf(y + currentTransform().getTranslateY() + 0.5f); + x = (int) floorf(x + currentTransform()->getTranslateX() + 0.5f); + y = (int) floorf(y + currentTransform()->getTranslateY() + 0.5f); } FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint); @@ -2881,7 +2724,7 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count } // Pick the appropriate texture filtering - bool linearFilter = currentTransform().changesBounds(); + bool linearFilter = currentTransform()->changesBounds(); if (pureTranslate && !linearFilter) { linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f; } @@ -2897,7 +2740,7 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count positions, hasActiveLayer ? &bounds : NULL, &functor)) { if (hasActiveLayer) { if (!pureTranslate) { - currentTransform().mapRect(bounds); + currentTransform()->mapRect(bounds); } dirtyLayerUnchecked(bounds, getRegion()); } @@ -2915,7 +2758,7 @@ mat4 OpenGLRenderer::findBestFontTransform(const mat4& transform) const { fontTransform = mat4::identity(); } else { float sx, sy; - currentTransform().decomposeScale(sx, sy); + currentTransform()->decomposeScale(sx, sy); fontTransform.loadScale(sx, sy, 1.0f); } } @@ -2923,14 +2766,14 @@ mat4 OpenGLRenderer::findBestFontTransform(const mat4& transform) const { } 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, + const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode) { if (drawOpMode == kDrawOpMode_Immediate) { // The checks for corner-case ignorable text and quick rejection is only done for immediate // drawing as ops from DeferredDisplayList are already filtered for these - if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint) || - quickReject(bounds)) { + if (text == NULL || count == 0 || currentSnapshot()->isIgnored() || canSkipText(paint) || + quickRejectSetupScissor(bounds)) { return DrawGlInfo::kStatusDone; } } @@ -2938,7 +2781,7 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f const float oldX = x; const float oldY = y; - const mat4& transform = currentTransform(); + const mat4& transform = *currentTransform(); const bool pureTranslate = transform.isPureTranslate(); if (CC_LIKELY(pureTranslate)) { @@ -2979,7 +2822,7 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f fontRenderer.setTextureFiltering(linearFilter); // TODO: Implement better clipping for scaled/rotated text - const Rect* clip = !pureTranslate ? NULL : mSnapshot->clipRect; + const Rect* clip = !pureTranslate ? NULL : currentClipRect(); Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); bool status; @@ -3004,14 +2847,14 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, f dirtyLayerUnchecked(layerBounds, getRegion()); } - drawTextDecorations(text, bytesCount, totalAdvance, oldX, oldY, paint); + drawTextDecorations(totalAdvance, oldX, oldY, paint); return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path, - float hOffset, float vOffset, SkPaint* paint) { - if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) { +status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, + const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) { + if (text == NULL || count == 0 || currentSnapshot()->isIgnored() || canSkipText(paint)) { return DrawGlInfo::kStatusDone; } @@ -3035,7 +2878,7 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path, hOffset, vOffset, hasActiveLayer ? &bounds : NULL, &functor)) { if (hasActiveLayer) { - currentTransform().mapRect(bounds); + currentTransform()->mapRect(bounds); dirtyLayerUnchecked(bounds, getRegion()); } } @@ -3043,8 +2886,8 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) { - if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone; +status_t OpenGLRenderer::drawPath(const SkPath* path, const SkPaint* paint) { + if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone; mCaches.activeTexture(0); @@ -3070,13 +2913,13 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { transform = &layer->getTransform(); if (!transform->isIdentity()) { save(0); - currentTransform().multiply(*transform); + concatMatrix(*transform); } } bool clipRequired = false; - const bool rejected = quickRejectNoScissor(x, y, - x + layer->layer.getWidth(), y + layer->layer.getHeight(), false, &clipRequired); + const bool rejected = calculateQuickRejectForScissor(x, y, + x + layer->layer.getWidth(), y + layer->layer.getHeight(), &clipRequired, false); if (rejected) { if (transform && !transform->isIdentity()) { @@ -3108,16 +2951,16 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); setupDrawTexture(layer->getTexture()); - if (CC_LIKELY(currentTransform().isPureTranslate())) { - int tx = (int) floorf(x + currentTransform().getTranslateX() + 0.5f); - int ty = (int) floorf(y + currentTransform().getTranslateY() + 0.5f); + if (CC_LIKELY(currentTransform()->isPureTranslate())) { + int tx = (int) floorf(x + currentTransform()->getTranslateX() + 0.5f); + int ty = (int) floorf(y + currentTransform()->getTranslateY() + 0.5f); layer->setFilter(GL_NEAREST); - setupDrawModelViewTranslate(tx, ty, + setupDrawModelView(kModelViewMode_Translate, false, tx, ty, tx + layer->layer.getWidth(), ty + layer->layer.getHeight(), true); } else { layer->setFilter(GL_LINEAR); - setupDrawModelViewTranslate(x, y, + setupDrawModelView(kModelViewMode_Translate, false, x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight()); } @@ -3127,7 +2970,7 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { while (elementsCount > 0) { GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6); - setupDrawMeshIndices(&mesh[0].position[0], &mesh[0].texture[0]); + setupDrawMeshIndices(&mesh[0].x, &mesh[0].u); DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate, glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL)); @@ -3138,7 +2981,7 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { } #if DEBUG_LAYERS_AS_REGIONS - drawRegionRects(layer->region); + drawRegionRectsDebug(layer->region); #endif } @@ -3220,7 +3063,7 @@ void OpenGLRenderer::setupPaintFilter(int clearBits, int setBits) { mDrawModifiers.mPaintFilterSetBits = setBits & SkPaint::kAllFlags; } -SkPaint* OpenGLRenderer::filterPaint(SkPaint* paint) { +const SkPaint* OpenGLRenderer::filterPaint(const SkPaint* paint) { if (CC_LIKELY(!mDrawModifiers.mHasDrawFilter || !paint)) { return paint; } @@ -3238,7 +3081,7 @@ SkPaint* OpenGLRenderer::filterPaint(SkPaint* paint) { // Drawing implementation /////////////////////////////////////////////////////////////////////////////// -Texture* OpenGLRenderer::getTexture(SkBitmap* bitmap) { +Texture* OpenGLRenderer::getTexture(const SkBitmap* bitmap) { Texture* texture = mCaches.assetAtlas.getEntryTexture(bitmap); if (!texture) { return mCaches.textureCache.get(bitmap); @@ -3247,8 +3090,8 @@ Texture* OpenGLRenderer::getTexture(SkBitmap* bitmap) { } void OpenGLRenderer::drawPathTexture(const PathTexture* texture, - float x, float y, SkPaint* paint) { - if (quickReject(x, y, x + texture->width, y + texture->height)) { + float x, float y, const SkPaint* paint) { + if (quickRejectSetupScissor(x, y, x + texture->width, y + texture->height)) { return; } @@ -3263,7 +3106,8 @@ void OpenGLRenderer::drawPathTexture(const PathTexture* texture, setupDrawShader(); setupDrawBlending(true, mode); setupDrawProgram(); - setupDrawModelView(x, y, x + texture->width, y + texture->height); + setupDrawModelView(kModelViewMode_TranslateAndScale, false, + x, y, x + texture->width, y + texture->height); setupDrawTexture(texture->id); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); @@ -3278,8 +3122,8 @@ 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 underlineWidth, - float x, float y, SkPaint* paint) { +void OpenGLRenderer::drawTextDecorations(float underlineWidth, float x, float y, + const SkPaint* paint) { // Handle underline and strike-through uint32_t flags = paint->getFlags(); if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) { @@ -3323,8 +3167,8 @@ void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float } } -status_t OpenGLRenderer::drawRects(const float* rects, int count, SkPaint* paint) { - if (mSnapshot->isIgnored()) { +status_t OpenGLRenderer::drawRects(const float* rects, int count, const SkPaint* paint) { + if (currentSnapshot()->isIgnored()) { return DrawGlInfo::kStatusDone; } @@ -3338,6 +3182,32 @@ status_t OpenGLRenderer::drawRects(const float* rects, int count, SkPaint* paint return drawColorRects(rects, count, color, mode); } +status_t OpenGLRenderer::drawShadow(const mat4& casterTransform, float casterAlpha, + float width, float height) { + if (currentSnapshot()->isIgnored()) return DrawGlInfo::kStatusDone; + + // For now, always and scissor + // TODO: use quickReject + mCaches.enableScissor(); + + SkPaint paint; + paint.setColor(mCaches.propertyShadowStrength << 24); + paint.setAntiAlias(true); // want to use AlphaVertex + + VertexBuffer ambientShadowVertexBuffer; + ShadowTessellator::tessellateAmbientShadow(width, height, casterTransform, + ambientShadowVertexBuffer); + drawVertexBuffer(ambientShadowVertexBuffer, &paint); + + VertexBuffer spotShadowVertexBuffer; + ShadowTessellator::tessellateSpotShadow(width, height, + *currentTransform(), getWidth(), getHeight(), + casterTransform, spotShadowVertexBuffer); + drawVertexBuffer(spotShadowVertexBuffer, &paint); + + return DrawGlInfo::kStatusDrew; +} + status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color, SkXfermode::Mode mode, bool ignoreTransform, bool dirty, bool clip) { if (count == 0) { @@ -3369,28 +3239,29 @@ status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color bottom = fmaxf(bottom, b); } - if (clip && quickReject(left, top, right, bottom)) { + if (clip && quickRejectSetupScissor(left, top, right, bottom)) { return DrawGlInfo::kStatusDone; } setupDraw(); setupDrawNoTexture(); - setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha); + setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha); setupDrawShader(); setupDrawColorFilter(); setupDrawBlending(mode); setupDrawProgram(); setupDrawDirtyRegionsDisabled(); - setupDrawModelView(0.0f, 0.0f, 1.0f, 1.0f, ignoreTransform, true); + setupDrawModelView(kModelViewMode_Translate, false, + 0.0f, 0.0f, 0.0f, 0.0f, ignoreTransform); setupDrawColorUniforms(); setupDrawShaderUniforms(); setupDrawColorFilterUniforms(); if (dirty && hasLayer()) { - dirtyLayer(left, top, right, bottom, currentTransform()); + dirtyLayer(left, top, right, bottom, *currentTransform()); } - drawIndexedQuads(&mesh[0], count / 4); + issueIndexedQuadDraw(&mesh[0], count / 4); return DrawGlInfo::kStatusDrew; } @@ -3404,12 +3275,13 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot setupDraw(); setupDrawNoTexture(); - setupDrawColor(color, ((color >> 24) & 0xFF) * mSnapshot->alpha); + setupDrawColor(color, ((color >> 24) & 0xFF) * currentSnapshot()->alpha); setupDrawShader(); setupDrawColorFilter(); setupDrawBlending(mode); setupDrawProgram(); - setupDrawModelView(left, top, right, bottom, ignoreTransform); + setupDrawModelView(kModelViewMode_TranslateAndScale, false, + left, top, right, bottom, ignoreTransform); setupDrawColorUniforms(); setupDrawShaderUniforms(ignoreTransform); setupDrawColorFilterUniforms(); @@ -3419,7 +3291,7 @@ void OpenGLRenderer::drawColorRect(float left, float top, float right, float bot } void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom, - Texture* texture, SkPaint* paint) { + Texture* texture, const SkPaint* paint) { int alpha; SkXfermode::Mode mode; getAlphaAndMode(paint, &alpha, &mode); @@ -3430,8 +3302,8 @@ void OpenGLRenderer::drawTextureRect(float left, float top, float right, float b GLvoid* texCoords = (GLvoid*) gMeshTextureOffset; if (texture->uvMapper) { - vertices = &mMeshVertices[0].position[0]; - texCoords = &mMeshVertices[0].texture[0]; + vertices = &mMeshVertices[0].x; + texCoords = &mMeshVertices[0].u; Rect uvs(0.0f, 0.0f, 1.0f, 1.0f); texture->uvMapper->map(uvs); @@ -3439,9 +3311,9 @@ void OpenGLRenderer::drawTextureRect(float left, float top, float right, float b 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); + 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, @@ -3467,7 +3339,8 @@ void OpenGLRenderer::drawTextureRect(float left, float top, float right, float b void OpenGLRenderer::drawTextureMesh(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) { + bool swapSrcDst, bool ignoreTransform, GLuint vbo, + ModelViewMode modelViewMode, bool dirty) { setupDraw(); setupDrawWithTexture(); @@ -3476,11 +3349,7 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b setupDrawBlending(blend, mode, swapSrcDst); setupDrawProgram(); if (!dirty) setupDrawDirtyRegionsDisabled(); - if (!ignoreScale) { - setupDrawModelView(left, top, right, bottom, ignoreTransform); - } else { - setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform); - } + setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform); setupDrawTexture(texture); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); @@ -3492,7 +3361,8 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b 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) { + bool swapSrcDst, bool ignoreTransform, GLuint vbo, + ModelViewMode modelViewMode, bool dirty) { setupDraw(); setupDrawWithTexture(); @@ -3501,11 +3371,7 @@ void OpenGLRenderer::drawIndexedTextureMesh(float left, float top, float right, setupDrawBlending(blend, mode, swapSrcDst); setupDrawProgram(); if (!dirty) setupDrawDirtyRegionsDisabled(); - if (!ignoreScale) { - setupDrawModelView(left, top, right, bottom, ignoreTransform); - } else { - setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform); - } + setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform); setupDrawTexture(texture); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); @@ -3517,7 +3383,7 @@ void OpenGLRenderer::drawIndexedTextureMesh(float left, float top, float right, 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, - bool ignoreTransform, bool ignoreScale, bool dirty) { + bool ignoreTransform, ModelViewMode modelViewMode, bool dirty) { setupDraw(); setupDrawWithTexture(true); @@ -3529,15 +3395,11 @@ void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, f setupDrawBlending(true, mode); setupDrawProgram(); if (!dirty) setupDrawDirtyRegionsDisabled(); - if (!ignoreScale) { - setupDrawModelView(left, top, right, bottom, ignoreTransform); - } else { - setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform); - } + setupDrawModelView(modelViewMode, false, left, top, right, bottom, ignoreTransform); setupDrawTexture(texture); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); - setupDrawShaderUniforms(); + setupDrawShaderUniforms(ignoreTransform); setupDrawMesh(vertices, texCoords); glDrawArrays(drawMode, 0, elementsCount); @@ -3618,13 +3480,13 @@ void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, flo TextureVertex::setUV(v++, u2, v2); } -void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const { +void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const { getAlphaAndModeDirect(paint, alpha, mode); if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) { // if drawing a layer, ignore the paint's alpha *alpha = mDrawModifiers.mOverrideLayerAlpha * 255; } - *alpha *= mSnapshot->alpha; + *alpha *= currentSnapshot()->alpha; } float OpenGLRenderer::getLayerAlpha(Layer* layer) const { @@ -3634,7 +3496,7 @@ float OpenGLRenderer::getLayerAlpha(Layer* layer) const { } else { alpha = layer->getAlpha() / 255.0f; } - return alpha * mSnapshot->alpha; + return alpha * currentSnapshot()->alpha; } }; // namespace uirenderer diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 9afb7ad..fb780ce 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -41,16 +41,23 @@ #include "Matrix.h" #include "Program.h" #include "Rect.h" -#include "Snapshot.h" -#include "Vertex.h" -#include "SkiaShader.h" +#include "Renderer.h" +#include "StatefulBaseRenderer.h" #include "SkiaColorFilter.h" +#include "Snapshot.h" #include "UvMapper.h" +#include "Vertex.h" #include "Caches.h" namespace android { namespace uirenderer { +class DeferredDisplayState; +class DisplayList; +class TextSetupFunctor; +class VertexBuffer; +class SkiaShader; + struct DrawModifiers { DrawModifiers() { reset(); @@ -82,12 +89,6 @@ enum StateDeferFlags { kStateDeferFlag_Clip = 0x2 }; -enum DrawOpMode { - kDrawOpMode_Immediate, - kDrawOpMode_Defer, - kDrawOpMode_Flush -}; - enum ClipSideFlags { kClipSide_None = 0x0, kClipSide_Left = 0x1, @@ -98,105 +99,41 @@ enum ClipSideFlags { kClipSide_ConservativeFull = 0x1F }; +/** + * Defines additional transformation that should be applied by the model view matrix, beyond that of + * the currentTransform() + */ +enum ModelViewMode { + /** + * Used when the model view should simply translate geometry passed to the shader. The resulting + * matrix will be a simple translation. + */ + kModelViewMode_Translate = 0, + + /** + * Used when the model view should translate and scale geometry. The resulting matrix will be a + * translation + scale. This is frequently used together with VBO 0, the (0,0,1,1) rect. + */ + kModelViewMode_TranslateAndScale = 1, +}; + /////////////////////////////////////////////////////////////////////////////// // Renderer /////////////////////////////////////////////////////////////////////////////// - -class DeferredDisplayState; -class DisplayList; -class TextSetupFunctor; -class VertexBuffer; - /** - * OpenGL renderer used to draw accelerated 2D graphics. The API is a - * simplified version of Skia's Canvas API. + * OpenGL Renderer implementation. */ -class OpenGLRenderer { +class OpenGLRenderer : public StatefulBaseRenderer { public: ANDROID_API OpenGLRenderer(); virtual ~OpenGLRenderer(); - /** - * Sets the name of this renderer. The name is optional and - * empty by default. If the pointer is null the name is set - * to the empty string. - */ - ANDROID_API void setName(const char* name); - - /** - * Returns the name of this renderer as UTF8 string. - * The returned pointer is never null. - */ - ANDROID_API const char* getName() const; - - /** - * Read externally defined properties to control the behavior - * of the renderer. - */ ANDROID_API void initProperties(); - /** - * Indicates whether this renderer executes drawing commands immediately. - * If this method returns true, the drawing commands will be executed - * later. - */ - virtual bool isDeferred(); - - /** - * Sets the dimension of the underlying drawing surface. This method must - * be called at least once every time the drawing surface changes size. - * - * @param width The width in pixels of the underlysing surface - * @param height The height in pixels of the underlysing surface - */ virtual void setViewport(int width, int height); - - /** - * Prepares the renderer to draw a frame. This method must be invoked - * at the beginning of each frame. When this method is invoked, the - * entire drawing surface is assumed to be redrawn. - * - * @param opaque If true, the target surface is considered opaque - * and will not be cleared. If false, the target surface - * will be cleared - */ - ANDROID_API status_t prepare(bool opaque); - - /** - * Prepares the renderer to draw a frame. This method must be invoked - * at the beginning of each frame. Only the specified rectangle of the - * frame is assumed to be dirty. A clip will automatically be set to - * the specified rectangle. - * - * @param left The left coordinate of the dirty rectangle - * @param top The top coordinate of the dirty rectangle - * @param right The right coordinate of the dirty rectangle - * @param bottom The bottom coordinate of the dirty rectangle - * @param opaque If true, the target surface is considered opaque - * and will not be cleared. If false, the target surface - * will be cleared in the specified dirty rectangle - */ virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque); - - /** - * Indicates the end of a frame. This method must be invoked whenever - * the caller is done rendering a frame. - */ virtual void finish(); - - /** - * This method must be invoked before handing control over to a draw functor. - * See callDrawGLFunction() for instance. - * - * This command must not be recorded inside display lists. - */ virtual void interrupt(); - - /** - * This method must be invoked after getting control back from a draw functor. - * - * This command must not be recorded inside display lists. - */ virtual void resume(); ANDROID_API void setCountOverdrawEnabled(bool enabled) { @@ -217,11 +154,6 @@ public: ANDROID_API void clearLayerUpdates(); ANDROID_API void flushLayerUpdates(); - ANDROID_API int getSaveCount() const; - virtual int save(int flags); - virtual void restore(); - virtual void restoreToCount(int saveCount); - ANDROID_API int saveLayer(float left, float top, float right, float bottom, SkPaint* paint, int flags) { SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode; @@ -238,86 +170,51 @@ public: int saveLayerDeferred(float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode, int flags); - virtual void translate(float dx, float dy); - virtual void rotate(float degrees); - virtual void scale(float sx, float sy); - virtual void skew(float sx, float sy); - - bool hasRectToRectTransform(); - ANDROID_API void getMatrix(SkMatrix* matrix); - virtual void setMatrix(SkMatrix* matrix); - virtual void concatMatrix(SkMatrix* matrix); - - ANDROID_API const Rect& getClipBounds(); - - /** - * Performs a quick reject but adjust the bounds to account for stroke width if necessary, - * and handling snapOut for AA geometry. - */ - bool quickRejectPreStroke(float left, float top, float right, float bottom, SkPaint* paint); - - /** - * Returns false and sets scissor based upon bounds if drawing won't be clipped out - */ - bool quickReject(float left, float top, float right, float bottom, bool snapOut = false); - bool quickReject(const Rect& bounds) { - return quickReject(bounds.left, bounds.top, bounds.right, bounds.bottom); - } - - /** - * Same as quickReject, without the scissor, instead returning clipRequired through pointer. - * clipRequired will be only set if not rejected - */ - ANDROID_API bool quickRejectNoScissor(float left, float top, float right, float bottom, - bool snapOut = false, bool* clipRequired = NULL); - bool quickRejectNoScissor(const Rect& bounds, bool* clipRequired = NULL) { - return quickRejectNoScissor(bounds.left, bounds.top, bounds.right, bounds.bottom, - clipRequired); - } - - virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); - virtual bool clipPath(SkPath* path, SkRegion::Op op); - virtual bool clipRegion(SkRegion* region, SkRegion::Op op); - virtual Rect* getClipRect(); - virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, int32_t replayFlags); - virtual void outputDisplayList(DisplayList* displayList); 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, AssetAtlas::Entry* entry, int bitmapCount, - TextureVertex* vertices, bool pureTranslate, 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, + virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint); + status_t drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount, + TextureVertex* vertices, bool pureTranslate, const Rect& bounds, const SkPaint* paint); + virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + const SkPaint* paint); + virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, - float dstRight, float dstBottom, SkPaint* paint); - 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); - status_t drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry, - TextureVertex* vertices, uint32_t indexCount, SkPaint* paint); - 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 Patch* mesh, AssetAtlas::Entry* entry, - float left, float top, float right, float bottom, SkPaint* paint); + float dstRight, float dstBottom, const SkPaint* paint); + virtual status_t drawBitmapData(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint); + virtual status_t drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, + const float* vertices, const int* colors, const SkPaint* paint); + status_t drawPatches(const SkBitmap* bitmap, AssetAtlas::Entry* entry, + TextureVertex* vertices, uint32_t indexCount, const SkPaint* paint); + virtual status_t drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, + float left, float top, float right, float bottom, const SkPaint* paint); + status_t drawPatch(const SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry, + float left, float top, float right, float bottom, const 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); + virtual status_t drawRect(float left, float top, float right, float bottom, + const SkPaint* paint); virtual status_t drawRoundRect(float left, float top, float right, float bottom, - float rx, float ry, SkPaint* paint); - virtual status_t drawCircle(float x, float y, float radius, SkPaint* paint); - virtual status_t drawOval(float left, float top, float right, float bottom, SkPaint* paint); + float rx, float ry, const SkPaint* paint); + virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint); + virtual status_t drawOval(float left, float top, float right, float bottom, + const SkPaint* paint); virtual status_t drawArc(float left, float top, float right, float bottom, - float startAngle, float sweepAngle, bool useCenter, SkPaint* paint); - virtual status_t drawPath(SkPath* path, SkPaint* paint); - virtual status_t drawLines(float* points, int count, SkPaint* paint); - virtual status_t drawPoints(float* points, int count, SkPaint* paint); - virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, SkPath* path, - float hOffset, float vOffset, SkPaint* paint); + float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint); + virtual status_t drawPath(const SkPath* path, const SkPaint* paint); + virtual status_t drawLines(const float* points, int count, const SkPaint* paint); + virtual status_t drawPoints(const float* points, int count, const SkPaint* paint); + virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path, + float hOffset, float vOffset, const SkPaint* paint); virtual status_t drawPosText(const char* text, int bytesCount, int count, - const float* positions, SkPaint* paint); + const float* positions, const SkPaint* paint); virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y, - const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds, + const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode = kDrawOpMode_Immediate); - virtual status_t drawRects(const float* rects, int count, SkPaint* paint); + virtual status_t drawRects(const float* rects, int count, const SkPaint* paint); + + status_t drawShadow(const mat4& casterTransform, float casterAlpha, + float width, float height); virtual void resetShader(); virtual void setupShader(SkiaShader* shader); @@ -334,7 +231,7 @@ public: // If this value is set to < 1.0, it overrides alpha set on layer (see drawBitmap, drawLayer) void setOverrideLayerAlpha(float alpha) { mDrawModifiers.mOverrideLayerAlpha = alpha; } - SkPaint* filterPaint(SkPaint* paint); + const SkPaint* filterPaint(const SkPaint* paint); /** * Store the current display state (most importantly, the current clip and transform), and @@ -350,7 +247,7 @@ public: void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; } ANDROID_API bool isCurrentTransformSimple() { - return mSnapshot->transform->isSimple(); + return currentTransform()->isSimple(); } Caches& getCaches() { @@ -362,8 +259,8 @@ public: return mSnapshot->clipRegion->isEmpty(); } - int getViewportWidth() { return getSnapshot()->viewport.getWidth(); } - int getViewportHeight() { return getSnapshot()->viewport.getHeight(); } + int getViewportWidth() { return currentSnapshot()->viewport.getWidth(); } + int getViewportHeight() { return currentSnapshot()->viewport.getHeight(); } /** * Scales the alpha on the current snapshot. This alpha value will be modulated @@ -400,12 +297,12 @@ public: * @param alpha Where to store the resulting alpha * @param mode Where to store the resulting xfermode */ - static inline void getAlphaAndModeDirect(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) { + static inline void getAlphaAndModeDirect(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) { *mode = getXfermodeDirect(paint); *alpha = getAlphaDirect(paint); } - static inline SkXfermode::Mode getXfermodeDirect(SkPaint* paint) { + static inline SkXfermode::Mode getXfermodeDirect(const SkPaint* paint) { if (!paint) return SkXfermode::kSrcOver_Mode; return getXfermode(paint->getXfermode()); } @@ -477,6 +374,13 @@ protected: */ void attachStencilBufferToLayer(Layer* layer); + bool quickRejectSetupScissor(float left, float top, float right, float bottom, + const SkPaint* paint = NULL); + bool quickRejectSetupScissor(const Rect& bounds, const SkPaint* paint = NULL) { + return quickRejectSetupScissor(bounds.left, bounds.top, + bounds.right, bounds.bottom, paint); + } + /** * Compose the layer defined in the current snapshot with the layer * defined by the previous snapshot. @@ -486,7 +390,7 @@ protected: * @param curent The current snapshot containing the layer to compose * @param previous The previous snapshot to compose the current layer with */ - virtual void composeLayer(sp<Snapshot> current, sp<Snapshot> previous); + virtual void composeLayer(const Snapshot& current, const Snapshot& previous); /** * Marks the specified region as dirty at the specified bounds. @@ -494,13 +398,6 @@ protected: void dirtyLayerUnchecked(Rect& bounds, Region* region); /** - * Returns the current snapshot. - */ - sp<Snapshot> getSnapshot() const { - return mSnapshot; - } - - /** * Returns the region of the current layer. */ virtual Region* getRegion() const { @@ -517,7 +414,7 @@ protected: /** * Returns the name of the FBO this renderer is rendering into. */ - virtual GLint getTargetFbo() const { + virtual GLuint getTargetFbo() const { return 0; } @@ -538,7 +435,7 @@ protected: * @param alpha Where to store the resulting alpha * @param mode Where to store the resulting xfermode */ - inline void getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const; + inline void getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const; /** * Gets the alpha from a layer, accounting for snapshot alpha and overrideLayerAlpha @@ -582,12 +479,11 @@ private: /** * Tells the GPU what part of the screen is about to be redrawn. - * This method will use the clip rect that we started drawing the - * frame with. + * This method will use the current layer space clip rect. * This method needs to be invoked every time getTargetFbo() is * bound again. */ - void startTiling(const sp<Snapshot>& snapshot, bool opaque = false); + void startTilingCurrentClip(bool opaque = false); /** * Tells the GPU what part of the screen is about to be redrawn. @@ -602,23 +498,7 @@ private: */ void endTiling(); - /** - * Saves the current state of the renderer as a new snapshot. - * The new snapshot is saved in mSnapshot and the previous snapshot - * is linked from mSnapshot->previous. - * - * @param flags The save flags; see SkCanvas for more information - * - * @return The new save count. This value can be passed to #restoreToCount() - */ - int saveSnapshot(int flags); - - /** - * Restores the current snapshot; mSnapshot becomes mSnapshot->previous. - * - * @return True if the clip was modified. - */ - bool restoreSnapshot(); + void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored); /** * Sets the clipping rectangle using glScissor. The clip is defined by @@ -654,12 +534,11 @@ private: * @param alpha The translucency of the layer * @param mode The blending mode of the layer * @param flags The layer save flags - * @param previousFbo The name of the current framebuffer * * @return True if the layer was successfully created, false otherwise */ bool createLayer(float left, float top, float right, float bottom, - int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo); + int alpha, SkXfermode::Mode mode, int flags); /** * Creates a new layer stored in the specified snapshot as an FBO. @@ -667,9 +546,8 @@ private: * @param layer The layer to store as an FBO * @param snapshot The snapshot associated with the new layer * @param bounds The bounds of the layer - * @param previousFbo The name of the current framebuffer */ - bool createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLuint previousFbo); + bool createFboLayer(Layer* layer, Rect& bounds, Rect& clip); /** * Compose the specified layer as a region. @@ -751,7 +629,7 @@ private: * @param texture The texture reprsenting the shape * @param paint The paint to draw the shape with */ - status_t drawShape(float left, float top, const PathTexture* texture, SkPaint* paint); + status_t drawShape(float left, float top, const PathTexture* texture, const SkPaint* paint); /** * Draws the specified texture as an alpha bitmap. Alpha bitmaps obey @@ -762,7 +640,7 @@ private: * @param top The y coordinate of the bitmap * @param paint The paint to render with */ - void drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint); + void drawAlphaBitmap(Texture* texture, float left, float top, const SkPaint* paint); /** * Renders a strip of polygons with the specified paint, used for tessellated geometry. @@ -771,7 +649,7 @@ private: * @param paint The paint to render with * @param useOffset Offset the vertexBuffer (used in drawing non-AA lines) */ - status_t drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint, + status_t drawVertexBuffer(const VertexBuffer& vertexBuffer, const SkPaint* paint, bool useOffset = false); /** @@ -780,7 +658,7 @@ private: * @param path The hull of the path to draw * @param paint The paint to render with */ - status_t drawConvexPath(const SkPath& path, SkPaint* paint); + status_t drawConvexPath(const SkPath& path, const SkPaint* paint); /** * Draws a textured rectangle with the specified texture. The specified coordinates @@ -810,7 +688,7 @@ private: * @param paint The paint containing the alpha, blending mode, etc. */ void drawTextureRect(float left, float top, float right, float bottom, - Texture* texture, SkPaint* paint); + Texture* texture, const SkPaint* paint); /** * Draws a textured mesh with the specified texture. If the indices are omitted, @@ -831,32 +709,33 @@ private: * @param swapSrcDst Whether or not the src and dst blending operations should be swapped * @param ignoreTransform True if the current transform should be ignored * @param vbo The VBO used to draw the mesh - * @param ignoreScale True if the model view matrix should not be scaled + * @param modelViewMode Defines whether the model view matrix should be scaled * @param dirty True if calling this method should dirty the current layer */ void drawTextureMesh(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); + ModelViewMode modelViewMode = kModelViewMode_TranslateAndScale, 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); + ModelViewMode modelViewMode = kModelViewMode_TranslateAndScale, 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, - bool ignoreTransform, bool ignoreScale = false, bool dirty = true); + bool ignoreTransform, ModelViewMode modelViewMode = kModelViewMode_TranslateAndScale, + bool dirty = true); /** * Draws the specified list of vertices as quads using indexed GL_TRIANGLES. * If the number of vertices to draw exceeds the number of indices we have * pre-allocated, this method will generate several glDrawElements() calls. */ - void drawIndexedQuads(Vertex* mesh, GLsizei quadsCount); + void issueIndexedQuadDraw(Vertex* mesh, GLsizei quadsCount); /** * Draws text underline and strike-through if needed. @@ -868,8 +747,7 @@ private: * @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 totalAdvance, - float x, float y, SkPaint* paint); + void drawTextDecorations(float totalAdvance, float x, float y, const SkPaint* paint); /** * Draws shadow layer on text (with optional positions). @@ -885,7 +763,7 @@ private: * @param x The x coordinate where the shadow will be drawn * @param y The y coordinate where the shadow will be drawn */ - void drawTextShadow(SkPaint* paint, const char* text, int bytesCount, int count, + void drawTextShadow(const SkPaint* paint, const char* text, int bytesCount, int count, const float* positions, FontRenderer& fontRenderer, int alpha, SkXfermode::Mode mode, float x, float y); @@ -898,7 +776,7 @@ private: * @param y The y coordinate where the texture will be drawn * @param paint The paint to draw the texture with */ - void drawPathTexture(const PathTexture* texture, float x, float y, SkPaint* paint); + void drawPathTexture(const PathTexture* texture, float x, float y, const SkPaint* paint); /** * Resets the texture coordinates stored in mMeshVertices. Setting the values @@ -978,14 +856,26 @@ private: bool swapSrcDst = false); void setupDrawProgram(); void setupDrawDirtyRegionsDisabled(); - void setupDrawModelViewIdentity(bool offset = false); - void setupDrawModelView(float left, float top, float right, float bottom, - bool ignoreTransform = false, bool ignoreModelView = false); - void setupDrawModelViewTranslate(float left, float top, float right, float bottom, - bool ignoreTransform = false); + + /** + * Setup the current program matrices based upon the nature of the geometry. + * + * @param mode If kModelViewMode_Translate, the geometry must be translated by the left and top + * parameters. If kModelViewMode_TranslateAndScale, the geometry that exists in the (0,0, 1,1) + * space must be scaled up and translated to fill the quad provided in (l,t,r,b). These + * transformations are stored in the modelView matrix and uploaded to the shader. + * + * @param offset Set to true if the the matrix should be fudged (translated) slightly to disambiguate + * geometry pixel positioning. See Vertex::GeometryFudgeFactor(). + * + * @param ignoreTransform Set to true if l,t,r,b coordinates already in layer space, + * currentTransform() will be ignored. (e.g. when drawing clip in layer coordinates to stencil, + * or when simple translation has been extracted) + */ + void setupDrawModelView(ModelViewMode mode, bool offset, + float left, float top, float right, float bottom, bool ignoreTransform = false); void setupDrawColorUniforms(); void setupDrawPureColorUniforms(); - void setupDrawShaderIdentityUniforms(); void setupDrawShaderUniforms(bool ignoreTransform = false); void setupDrawColorFilterUniforms(); void setupDrawSimpleMesh(); @@ -994,9 +884,9 @@ private: void setupDrawTextureTransform(); void setupDrawTextureTransformUniforms(mat4& transform); 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, GLuint vbo = 0); + void setupDrawMesh(const GLvoid* vertices, const GLvoid* texCoords = NULL, GLuint vbo = 0); + void setupDrawMesh(const GLvoid* vertices, const GLvoid* texCoords, const GLvoid* colors); + void setupDrawMeshIndices(const GLvoid* vertices, const GLvoid* texCoords, GLuint vbo = 0); void setupDrawIndexedVertices(GLvoid* vertices); void accountForClear(SkXfermode::Mode mode); @@ -1004,11 +894,13 @@ private: void updateLayers(); void flushLayers(); +#if DEBUG_LAYERS_AS_REGIONS /** * Renders the specified region as a series of rectangles. This method * is used for debugging only. */ - void drawRegionRects(const Region& region); + void drawRegionRectsDebug(const Region& region); +#endif /** * Renders the specified region as a series of rectangles. The region @@ -1034,10 +926,6 @@ private: mDirtyClip = true; } - inline mat4& currentTransform() const { - return *mSnapshot->transform; - } - inline const UvMapper& getMapper(const Texture* texture) { return texture && texture->uvMapper ? *texture->uvMapper : mUvMapper; } @@ -1047,23 +935,27 @@ private: * 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; + Texture* getTexture(const SkBitmap* bitmap); - // Matrix used for ortho projection in shaders - mat4 mOrthoMatrix; + // Matrix used for view/projection in shaders + mat4 mViewProjMatrix; - // Model-view matrix used to position/size objects + /** + * Model-view matrix used to position/size objects + * + * Stores operation-local modifications to the draw matrix that aren't incorporated into the + * currentTransform(). + * + * If generated with kModelViewMode_Translate, the mModelView will reflect an x/y offset, + * e.g. the offset in drawLayer(). If generated with kModelViewMode_TranslateAndScale, + * mModelView will reflect a translation and scale, e.g. the translation and scale required to + * make VBO 0 (a rect of (0,0,1,1)) scaled to match the x,y offset, and width/height of a + * bitmap. + * + * Used as input to SkiaShader transformation. + */ mat4 mModelView; - // Number of saved states - int mSaveCount; - // Base state - sp<Snapshot> mFirstSnapshot; - // Current state - sp<Snapshot> mSnapshot; // State used to define the clipping region Rect mTilingClip; // Is the target render surface opaque @@ -1092,9 +984,6 @@ private: // List of layers to update at the beginning of a frame Vector<Layer*> mLayerUpdates; - // Indicates whether the clip must be restored - bool mDirtyClip; - // The following fields are used to setup drawing // Used to describe the shaders to generate ProgramDescription mDescription; @@ -1122,9 +1011,6 @@ private: bool mCountOverdraw; float mOverdraw; - // Optional name of the renderer - String8 mName; - friend class DisplayListRenderer; friend class Layer; friend class TextSetupFunctor; diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h index 763a785..489064b 100644 --- a/libs/hwui/Patch.h +++ b/libs/hwui/Patch.h @@ -36,7 +36,8 @@ namespace uirenderer { // 9-patch structures /////////////////////////////////////////////////////////////////////////////// -struct Patch { +class Patch { +public: Patch(); ~Patch(); diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index 5df6408..9459885 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -51,7 +51,7 @@ PathDescription::PathDescription(): memset(&shape, 0, sizeof(Shape)); } -PathDescription::PathDescription(ShapeType type, SkPaint* paint): +PathDescription::PathDescription(ShapeType type, const SkPaint* paint): type(type), join(paint->getStrokeJoin()), cap(paint->getStrokeCap()), @@ -82,7 +82,7 @@ int PathDescription::compare(const PathDescription& rhs) const { // Utilities /////////////////////////////////////////////////////////////////////////////// -bool PathCache::canDrawAsConvexPath(SkPath* path, SkPaint* paint) { +bool PathCache::canDrawAsConvexPath(SkPath* path, const SkPaint* paint) { // NOTE: This should only be used after PathTessellator handles joins properly return paint->getPathEffect() == NULL && path->getConvexity() == SkPath::kConvex_Convexity; } @@ -413,7 +413,7 @@ void PathCache::clearGarbage() { * in the cache. The source path is also used to reclaim garbage when a * Dalvik Path object is collected. */ -static SkPath* getSourcePath(SkPath* path) { +static const SkPath* getSourcePath(const SkPath* path) { const SkPath* sourcePath = path->getSourcePath(); if (sourcePath && sourcePath->getGenerationID() == path->getGenerationID()) { return const_cast<SkPath*>(sourcePath); @@ -421,7 +421,7 @@ static SkPath* getSourcePath(SkPath* path) { return path; } -PathTexture* PathCache::get(SkPath* path, SkPaint* paint) { +PathTexture* PathCache::get(const SkPath* path, const SkPaint* paint) { path = getSourcePath(path); PathDescription entry(kShapePath, paint); @@ -459,7 +459,7 @@ PathTexture* PathCache::get(SkPath* path, SkPaint* paint) { return texture; } -void PathCache::precache(SkPath* path, SkPaint* paint) { +void PathCache::precache(const SkPath* path, const SkPaint* paint) { if (!Caches::getInstance().tasks.canRunTasks()) { return; } @@ -507,7 +507,7 @@ void PathCache::precache(SkPath* path, SkPaint* paint) { /////////////////////////////////////////////////////////////////////////////// PathTexture* PathCache::getRoundRect(float width, float height, - float rx, float ry, SkPaint* paint) { + float rx, float ry, const SkPaint* paint) { PathDescription entry(kShapeRoundRect, paint); entry.shape.roundRect.mWidth = width; entry.shape.roundRect.mHeight = height; @@ -532,7 +532,7 @@ PathTexture* PathCache::getRoundRect(float width, float height, // Circles /////////////////////////////////////////////////////////////////////////////// -PathTexture* PathCache::getCircle(float radius, SkPaint* paint) { +PathTexture* PathCache::getCircle(float radius, const SkPaint* paint) { PathDescription entry(kShapeCircle, paint); entry.shape.circle.mRadius = radius; @@ -552,7 +552,7 @@ PathTexture* PathCache::getCircle(float radius, SkPaint* paint) { // Ovals /////////////////////////////////////////////////////////////////////////////// -PathTexture* PathCache::getOval(float width, float height, SkPaint* paint) { +PathTexture* PathCache::getOval(float width, float height, const SkPaint* paint) { PathDescription entry(kShapeOval, paint); entry.shape.oval.mWidth = width; entry.shape.oval.mHeight = height; @@ -575,7 +575,7 @@ PathTexture* PathCache::getOval(float width, float height, SkPaint* paint) { // Rects /////////////////////////////////////////////////////////////////////////////// -PathTexture* PathCache::getRect(float width, float height, SkPaint* paint) { +PathTexture* PathCache::getRect(float width, float height, const SkPaint* paint) { PathDescription entry(kShapeRect, paint); entry.shape.rect.mWidth = width; entry.shape.rect.mHeight = height; @@ -599,7 +599,7 @@ PathTexture* PathCache::getRect(float width, float height, SkPaint* paint) { /////////////////////////////////////////////////////////////////////////////// PathTexture* PathCache::getArc(float width, float height, - float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) { + float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) { PathDescription entry(kShapeArc, paint); entry.shape.arc.mWidth = width; entry.shape.arc.mHeight = height; diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h index 16d20a8..847853a 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -32,7 +32,7 @@ class SkBitmap; class SkCanvas; class SkPaint; class SkPath; -class SkRect; +struct SkRect; namespace android { namespace uirenderer { @@ -116,7 +116,7 @@ struct PathDescription { SkPathEffect* pathEffect; union Shape { struct Path { - SkPath* mPath; + const SkPath* mPath; } path; struct RoundRect { float mWidth; @@ -145,7 +145,7 @@ struct PathDescription { } shape; PathDescription(); - PathDescription(ShapeType shapeType, SkPaint* paint); + PathDescription(ShapeType shapeType, const SkPaint* paint); hash_t hash() const; @@ -207,13 +207,13 @@ public: */ uint32_t getSize(); - PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint); - PathTexture* getCircle(float radius, SkPaint* paint); - PathTexture* getOval(float width, float height, SkPaint* paint); - PathTexture* getRect(float width, float height, SkPaint* paint); + PathTexture* getRoundRect(float width, float height, float rx, float ry, const SkPaint* paint); + PathTexture* getCircle(float radius, const SkPaint* paint); + PathTexture* getOval(float width, float height, const SkPaint* paint); + PathTexture* getRect(float width, float height, const SkPaint* paint); PathTexture* getArc(float width, float height, float startAngle, float sweepAngle, - bool useCenter, SkPaint* paint); - PathTexture* get(SkPath* path, SkPaint* paint); + bool useCenter, const SkPaint* paint); + PathTexture* get(const SkPath* path, const SkPaint* paint); /** * Removes the specified path. This is meant to be called from threads @@ -239,9 +239,9 @@ public: /** * Precaches the specified path using background threads. */ - void precache(SkPath* path, SkPaint* paint); + void precache(const SkPath* path, const SkPaint* paint); - static bool canDrawAsConvexPath(SkPath* path, SkPaint* paint); + static bool canDrawAsConvexPath(SkPath* path, const SkPaint* paint); static void computePathBounds(const SkPath* path, const SkPaint* paint, float& left, float& top, float& offset, uint32_t& width, uint32_t& height); static void computeBounds(const SkRect& bounds, const SkPaint* paint, @@ -292,7 +292,7 @@ private: class PathTask: public Task<SkBitmap*> { public: - PathTask(SkPath* path, SkPaint* paint, PathTexture* texture): + PathTask(const SkPath* path, const SkPaint* paint, PathTexture* texture): path(path), paint(paint), texture(texture) { } @@ -300,8 +300,8 @@ private: delete future()->get(); } - SkPath* path; - SkPaint* paint; + const SkPath* path; + const SkPaint* paint; PathTexture* texture; }; diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp index 3970913..c6ce67c 100644 --- a/libs/hwui/PathTessellator.cpp +++ b/libs/hwui/PathTessellator.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "PathTessellator" +#define LOG_TAG "OpenGLRenderer" #define LOG_NDEBUG 1 #define ATRACE_TAG ATRACE_TAG_GRAPHICS @@ -24,11 +24,11 @@ #define DEBUG_DUMP_ALPHA_BUFFER() \ for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \ ALOGD("point %d at %f %f, alpha %f", \ - i, buffer[i].position[0], buffer[i].position[1], buffer[i].alpha); \ + i, buffer[i].x, buffer[i].y, buffer[i].alpha); \ } #define DEBUG_DUMP_BUFFER() \ for (unsigned int i = 0; i < vertexBuffer.getSize(); i++) { \ - ALOGD("point %d at %f %f", i, buffer[i].position[0], buffer[i].position[1]); \ + ALOGD("point %d at %f %f", i, buffer[i].x, buffer[i].y); \ } #else #define DEBUG_DUMP_ALPHA_BUFFER() @@ -57,23 +57,18 @@ namespace uirenderer { #define ROUND_CAP_THRESH 0.25f #define PI 3.1415926535897932f -void PathTessellator::expandBoundsForStroke(SkRect& bounds, const SkPaint* paint, - bool forceExpand) { - if (forceExpand || paint->getStyle() != SkPaint::kFill_Style) { +/** + * Note: this function doesn't account for the AA case with sub-pixel line thickness (not just 0 < + * width < 1.0, canvas scale factors in as well) so this can't be used for points/lines + */ +void PathTessellator::expandBoundsForStroke(SkRect& bounds, const SkPaint* paint) { + if (paint->getStyle() != SkPaint::kFill_Style) { float outset = paint->getStrokeWidth() * 0.5f; if (outset == 0) outset = 0.5f; // account for hairline bounds.outset(outset, outset); } } -inline static void copyVertex(Vertex* destPtr, const Vertex* srcPtr) { - Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]); -} - -inline static void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) { - AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha); -} - /** * Produces a pseudo-normal for a vertex, given the normals of the two incoming lines. If the offset * from each vertex in a perimeter is calculated, the resultant lines connecting the offset vertices @@ -93,16 +88,16 @@ inline static vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& norma */ struct PaintInfo { public: - PaintInfo(const SkPaint* paint, const mat4 *transform) : + PaintInfo(const SkPaint* paint, const mat4& transform) : style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()), inverseScaleX(1.0f), inverseScaleY(1.0f), halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) { // compute inverse scales - if (CC_UNLIKELY(!transform->isPureTranslate())) { - float m00 = transform->data[Matrix4::kScaleX]; - float m01 = transform->data[Matrix4::kSkewY]; - float m10 = transform->data[Matrix4::kSkewX]; - float m11 = transform->data[Matrix4::kScaleY]; + if (CC_UNLIKELY(!transform.isPureTranslate())) { + float m00 = transform.data[Matrix4::kScaleX]; + float m01 = transform.data[Matrix4::kSkewY]; + float m10 = transform.data[Matrix4::kSkewX]; + float m11 = transform.data[Matrix4::kScaleY]; float scaleX = sqrt(m00 * m00 + m01 * m01); float scaleY = sqrt(m10 * m10 + m11 * m11); inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f; @@ -159,6 +154,17 @@ public: } return 0; } + + /** + * Outset the bounds of point data (for line endpoints or points) to account for AA stroke + * geometry. + */ + void expandBoundsForStrokeAA(SkRect& bounds) const { + float outset = halfStrokeWidth; + if (outset == 0) outset = 0.5f; + bounds.outset(outset * inverseScaleX + Vertex::GeometryFudgeFactor(), + outset * inverseScaleY + Vertex::GeometryFudgeFactor()); + } }; void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& vertexBuffer) { @@ -170,9 +176,9 @@ void getFillVerticesFromPerimeter(const Vector<Vertex>& perimeter, VertexBuffer& int srcAindex = 0; int srcBindex = perimeter.size() - 1; while (srcAindex <= srcBindex) { - copyVertex(&buffer[currentIndex++], &perimeter[srcAindex]); + buffer[currentIndex++] = perimeter[srcAindex]; if (srcAindex == srcBindex) break; - copyVertex(&buffer[currentIndex++], &perimeter[srcBindex]); + buffer[currentIndex++] = perimeter[srcBindex]; srcAindex++; srcBindex--; } @@ -192,25 +198,25 @@ void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Ver int currentIndex = 0; const Vertex* last = &(perimeter[perimeter.size() - 1]); const Vertex* current = &(perimeter[0]); - vec2 lastNormal(current->position[1] - last->position[1], - last->position[0] - current->position[0]); + vec2 lastNormal(current->y - last->y, + last->x - current->x); lastNormal.normalize(); for (unsigned int i = 0; i < perimeter.size(); i++) { const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); - vec2 nextNormal(next->position[1] - current->position[1], - current->position[0] - next->position[0]); + vec2 nextNormal(next->y - current->y, + current->x - next->x); nextNormal.normalize(); vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); paintInfo.scaleOffsetForStrokeWidth(totalOffset); Vertex::set(&buffer[currentIndex++], - current->position[0] + totalOffset.x, - current->position[1] + totalOffset.y); + current->x + totalOffset.x, + current->y + totalOffset.y); Vertex::set(&buffer[currentIndex++], - current->position[0] - totalOffset.x, - current->position[1] - totalOffset.y); + current->x - totalOffset.x, + current->y - totalOffset.y); last = current; current = next; @@ -218,8 +224,8 @@ void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Ver } // wrap around to beginning - copyVertex(&buffer[currentIndex++], &buffer[0]); - copyVertex(&buffer[currentIndex++], &buffer[1]); + buffer[currentIndex++] = buffer[0]; + buffer[currentIndex++] = buffer[1]; DEBUG_DUMP_BUFFER(); } @@ -229,7 +235,7 @@ static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& cente vec2 strokeOffset = normal; paintInfo.scaleOffsetForStrokeWidth(strokeOffset); - vec2 referencePoint(center.position[0], center.position[1]); + vec2 referencePoint(center.x, center.y); if (paintInfo.cap == SkPaint::kSquare_Cap) { referencePoint += vec2(-strokeOffset.y, strokeOffset.x) * (begin ? -1 : 1); } @@ -255,11 +261,11 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, if (extra > 0) { // tessellate both round caps float beginTheta = atan2( - - (vertices[0].position[0] - vertices[1].position[0]), - vertices[0].position[1] - vertices[1].position[1]); + - (vertices[0].x - vertices[1].x), + vertices[0].y - vertices[1].y); float endTheta = atan2( - - (vertices[lastIndex].position[0] - vertices[lastIndex - 1].position[0]), - vertices[lastIndex].position[1] - vertices[lastIndex - 1].position[1]); + - (vertices[lastIndex].x - vertices[lastIndex - 1].x), + vertices[lastIndex].y - vertices[lastIndex - 1].y); const float dTheta = PI / (extra + 1); const float radialScale = 2.0f / (1 + cos(dTheta)); @@ -275,37 +281,37 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, vec2 beginRadialOffset(cos(beginTheta), sin(beginTheta)); paintInfo.scaleOffsetForStrokeWidth(beginRadialOffset); Vertex::set(&buffer[capOffset], - vertices[0].position[0] + beginRadialOffset.x, - vertices[0].position[1] + beginRadialOffset.y); + vertices[0].x + beginRadialOffset.x, + vertices[0].y + beginRadialOffset.y); endTheta += dTheta; vec2 endRadialOffset(cos(endTheta), sin(endTheta)); paintInfo.scaleOffsetForStrokeWidth(endRadialOffset); Vertex::set(&buffer[allocSize - 1 - capOffset], - vertices[lastIndex].position[0] + endRadialOffset.x, - vertices[lastIndex].position[1] + endRadialOffset.y); + vertices[lastIndex].x + endRadialOffset.x, + vertices[lastIndex].y + endRadialOffset.y); } } int currentIndex = extra; const Vertex* last = &(vertices[0]); const Vertex* current = &(vertices[1]); - vec2 lastNormal(current->position[1] - last->position[1], - last->position[0] - current->position[0]); + vec2 lastNormal(current->y - last->y, + last->x - current->x); 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]); + vec2 nextNormal(next->y - current->y, + current->x - next->x); nextNormal.normalize(); vec2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal); paintInfo.scaleOffsetForStrokeWidth(strokeOffset); - vec2 center(current->position[0], current->position[1]); + vec2 center(current->x, current->y); Vertex::set(&buffer[currentIndex++], center + strokeOffset); Vertex::set(&buffer[currentIndex++], center - strokeOffset); @@ -329,7 +335,7 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo, * 3 - zig zag back and forth inside the shape to fill it (using perimeter.size() vertices) */ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Vertex>& perimeter, - VertexBuffer& vertexBuffer) { + VertexBuffer& vertexBuffer, float maxAlpha = 1.0f) { AlphaVertex* buffer = vertexBuffer.alloc<AlphaVertex>(perimeter.size() * 3 + 2); // generate alpha points - fill Alpha vertex gaps in between each point with @@ -337,13 +343,13 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver int currentIndex = 0; const Vertex* last = &(perimeter[perimeter.size() - 1]); const Vertex* current = &(perimeter[0]); - vec2 lastNormal(current->position[1] - last->position[1], - last->position[0] - current->position[0]); + vec2 lastNormal(current->y - last->y, + last->x - current->x); lastNormal.normalize(); for (unsigned int i = 0; i < perimeter.size(); i++) { const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); - vec2 nextNormal(next->position[1] - current->position[1], - current->position[0] - next->position[0]); + vec2 nextNormal(next->y - current->y, + current->x - next->x); nextNormal.normalize(); // AA point offset from original point is that point's normal, such that each side is offset @@ -351,13 +357,13 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver vec2 totalOffset = paintInfo.deriveAAOffset(totalOffsetFromNormals(lastNormal, nextNormal)); AlphaVertex::set(&buffer[currentIndex++], - current->position[0] + totalOffset.x, - current->position[1] + totalOffset.y, + current->x + totalOffset.x, + current->y + totalOffset.y, 0.0f); AlphaVertex::set(&buffer[currentIndex++], - current->position[0] - totalOffset.x, - current->position[1] - totalOffset.y, - 1.0f); + current->x - totalOffset.x, + current->y - totalOffset.y, + maxAlpha); last = current; current = next; @@ -365,8 +371,8 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver } // wrap around to beginning - copyAlphaVertex(&buffer[currentIndex++], &buffer[0]); - copyAlphaVertex(&buffer[currentIndex++], &buffer[1]); + buffer[currentIndex++] = buffer[0]; + buffer[currentIndex++] = buffer[1]; // zig zag between all previous points on the inside of the hull to create a // triangle strip that fills the hull, repeating the first inner point to @@ -374,9 +380,9 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver int srcAindex = 0; int srcBindex = perimeter.size() - 1; while (srcAindex <= srcBindex) { - copyAlphaVertex(&buffer[currentIndex++], &buffer[srcAindex * 2 + 1]); + buffer[currentIndex++] = buffer[srcAindex * 2 + 1]; if (srcAindex == srcBindex) break; - copyAlphaVertex(&buffer[currentIndex++], &buffer[srcBindex * 2 + 1]); + buffer[currentIndex++] = buffer[srcBindex * 2 + 1]; srcAindex++; srcBindex--; } @@ -416,7 +422,7 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& // determine referencePoint, the center point for the 4 primary cap vertices const Vertex* point = isFirst ? vertices.begin() : (vertices.end() - 1); - vec2 referencePoint(point->position[0], point->position[1]); + vec2 referencePoint(point->x, point->y); if (paintInfo.cap == SkPaint::kSquare_Cap) { // To account for square cap, move the primary cap vertices (that create the AA edge) by the // stroke offset vector (rotated to be parallel to the stroke) @@ -471,8 +477,8 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& if (isFirst && i == extra - extraOffset) { //copy most recent two points to first two points - copyAlphaVertex(&buffer[0], &buffer[capPerimIndex - 2]); - copyAlphaVertex(&buffer[1], &buffer[capPerimIndex - 1]); + buffer[0] = buffer[capPerimIndex - 2]; + buffer[1] = buffer[capPerimIndex - 1]; capPerimIndex = 2; // start writing the rest of the round cap at index 2 } @@ -482,28 +488,28 @@ inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& const int startCapFillIndex = capIndex + 2 * (extra - extraOffset) + 4; int capFillIndex = startCapFillIndex; for (int i = 0; i < extra + 2; i += 2) { - copyAlphaVertex(&buffer[capFillIndex++], &buffer[1 + i]); + buffer[capFillIndex++] = buffer[1 + i]; // TODO: to support odd numbers of divisions, break here on the last iteration - copyAlphaVertex(&buffer[capFillIndex++], &buffer[startCapFillIndex - 3 - i]); + buffer[capFillIndex++] = buffer[startCapFillIndex - 3 - i]; } } else { int capFillIndex = 6 * vertices.size() + 2 + 6 * extra - (extra + 2); for (int i = 0; i < extra + 2; i += 2) { - copyAlphaVertex(&buffer[capFillIndex++], &buffer[capIndex + 1 + i]); + buffer[capFillIndex++] = buffer[capIndex + 1 + i]; // TODO: to support odd numbers of divisions, break here on the last iteration - copyAlphaVertex(&buffer[capFillIndex++], &buffer[capIndex + 3 + 2 * extra - i]); + buffer[capFillIndex++] = buffer[capIndex + 3 + 2 * extra - i]; } } return; } if (isFirst) { - copyAlphaVertex(&buffer[0], &buffer[postCapIndex + 2]); - copyAlphaVertex(&buffer[1], &buffer[postCapIndex + 3]); - copyAlphaVertex(&buffer[postCapIndex + 4], &buffer[1]); // degenerate tris (the only two!) - copyAlphaVertex(&buffer[postCapIndex + 5], &buffer[postCapIndex + 1]); + buffer[0] = buffer[postCapIndex + 2]; + buffer[1] = buffer[postCapIndex + 3]; + buffer[postCapIndex + 4] = buffer[1]; // degenerate tris (the only two!) + buffer[postCapIndex + 5] = buffer[postCapIndex + 1]; } else { - copyAlphaVertex(&buffer[6 * vertices.size()], &buffer[postCapIndex + 1]); - copyAlphaVertex(&buffer[6 * vertices.size() + 1], &buffer[postCapIndex + 3]); + buffer[6 * vertices.size()] = buffer[postCapIndex + 1]; + buffer[6 * vertices.size() + 1] = buffer[postCapIndex + 3]; } } @@ -576,8 +582,8 @@ void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo, const Vertex* last = &(vertices[0]); const Vertex* current = &(vertices[1]); - vec2 lastNormal(current->position[1] - last->position[1], - last->position[0] - current->position[0]); + vec2 lastNormal(current->y - last->y, + last->x - current->x); lastNormal.normalize(); // TODO: use normal from bezier traversal for cap, instead of from vertices @@ -585,8 +591,8 @@ void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo, 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]); + vec2 nextNormal(next->y - current->y, + current->x - next->x); nextNormal.normalize(); vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); @@ -598,30 +604,30 @@ void getStrokeVerticesFromUnclosedVerticesAA(const PaintInfo& paintInfo, innerOffset -= AAOffset; AlphaVertex::set(&buffer[currentAAOuterIndex++], - current->position[0] + outerOffset.x, - current->position[1] + outerOffset.y, + current->x + outerOffset.x, + current->y + outerOffset.y, 0.0f); AlphaVertex::set(&buffer[currentAAOuterIndex++], - current->position[0] + innerOffset.x, - current->position[1] + innerOffset.y, + current->x + innerOffset.x, + current->y + innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentStrokeIndex++], - current->position[0] + innerOffset.x, - current->position[1] + innerOffset.y, + current->x + innerOffset.x, + current->y + innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentStrokeIndex++], - current->position[0] - innerOffset.x, - current->position[1] - innerOffset.y, + current->x - innerOffset.x, + current->y - innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentAAInnerIndex--], - current->position[0] - innerOffset.x, - current->position[1] - innerOffset.y, + current->x - innerOffset.x, + current->y - innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentAAInnerIndex--], - current->position[0] - outerOffset.x, - current->position[1] - outerOffset.y, + current->x - outerOffset.x, + current->y - outerOffset.y, 0.0f); current = next; @@ -646,13 +652,13 @@ void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<V const Vertex* last = &(perimeter[perimeter.size() - 1]); const Vertex* current = &(perimeter[0]); - vec2 lastNormal(current->position[1] - last->position[1], - last->position[0] - current->position[0]); + vec2 lastNormal(current->y - last->y, + last->x - current->x); lastNormal.normalize(); for (unsigned int i = 0; i < perimeter.size(); i++) { const Vertex* next = &(perimeter[i + 1 >= perimeter.size() ? 0 : i + 1]); - vec2 nextNormal(next->position[1] - current->position[1], - current->position[0] - next->position[0]); + vec2 nextNormal(next->y - current->y, + current->x - next->x); nextNormal.normalize(); vec2 totalOffset = totalOffsetFromNormals(lastNormal, nextNormal); @@ -664,30 +670,30 @@ void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<V innerOffset -= AAOffset; AlphaVertex::set(&buffer[currentAAOuterIndex++], - current->position[0] + outerOffset.x, - current->position[1] + outerOffset.y, + current->x + outerOffset.x, + current->y + outerOffset.y, 0.0f); AlphaVertex::set(&buffer[currentAAOuterIndex++], - current->position[0] + innerOffset.x, - current->position[1] + innerOffset.y, + current->x + innerOffset.x, + current->y + innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentStrokeIndex++], - current->position[0] + innerOffset.x, - current->position[1] + innerOffset.y, + current->x + innerOffset.x, + current->y + innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentStrokeIndex++], - current->position[0] - innerOffset.x, - current->position[1] - innerOffset.y, + current->x - innerOffset.x, + current->y - innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentAAInnerIndex++], - current->position[0] - innerOffset.x, - current->position[1] - innerOffset.y, + current->x - innerOffset.x, + current->y - innerOffset.y, paintInfo.maxAlpha); AlphaVertex::set(&buffer[currentAAInnerIndex++], - current->position[0] - outerOffset.x, - current->position[1] - outerOffset.y, + current->x - outerOffset.x, + current->y - outerOffset.y, 0.0f); last = current; @@ -696,23 +702,23 @@ void getStrokeVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<V } // wrap each strip around to beginning, creating degenerate tris to bridge strips - copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[0]); - copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]); - copyAlphaVertex(&buffer[currentAAOuterIndex++], &buffer[1]); + buffer[currentAAOuterIndex++] = buffer[0]; + buffer[currentAAOuterIndex++] = buffer[1]; + buffer[currentAAOuterIndex++] = buffer[1]; - copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset]); - copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]); - copyAlphaVertex(&buffer[currentStrokeIndex++], &buffer[offset + 1]); + buffer[currentStrokeIndex++] = buffer[offset]; + buffer[currentStrokeIndex++] = buffer[offset + 1]; + buffer[currentStrokeIndex++] = buffer[offset + 1]; - copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset]); - copyAlphaVertex(&buffer[currentAAInnerIndex++], &buffer[2 * offset + 1]); + buffer[currentAAInnerIndex++] = buffer[2 * offset]; + buffer[currentAAInnerIndex++] = buffer[2 * offset + 1]; // don't need to create last degenerate tri DEBUG_DUMP_ALPHA_BUFFER(); } void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, - const mat4 *transform, VertexBuffer& vertexBuffer) { + const mat4& transform, VertexBuffer& vertexBuffer) { ATRACE_CALL(); const PaintInfo paintInfo(paint, transform); @@ -743,7 +749,7 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint, #if VERTEX_DEBUG for (unsigned int i = 0; i < tempVertices.size(); i++) { ALOGD("orig path: point at %f %f", - tempVertices[i].position[0], tempVertices[i].position[1]); + tempVertices[i].x, tempVertices[i].y); } #endif @@ -780,7 +786,7 @@ static void expandRectToCoverVertex(SkRect& rect, float x, float y) { rect.fBottom = fmaxf(rect.fBottom, y); } static void expandRectToCoverVertex(SkRect& rect, const Vertex& vertex) { - expandRectToCoverVertex(rect, vertex.position[0], vertex.position[1]); + expandRectToCoverVertex(rect, vertex.x, vertex.y); } template <class TYPE> @@ -799,8 +805,8 @@ static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer, dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint); } -void PathTessellator::tessellatePoints(const float* points, int count, SkPaint* paint, - const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer) { +void PathTessellator::tessellatePoints(const float* points, int count, const SkPaint* paint, + const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer) { const PaintInfo paintInfo(paint, transform); // determine point shape @@ -829,15 +835,18 @@ void PathTessellator::tessellatePoints(const float* points, int count, SkPaint* getFillVerticesFromPerimeter(outlineVertices, tempBuffer); instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds); } else { - getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer); + // note: pass maxAlpha directly, since we want fill to be alpha modulated + getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer, paintInfo.maxAlpha); instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds); } - expandBoundsForStroke(bounds, paint, true); // force-expand bounds to incorporate stroke + // expand bounds from vertex coords to pixel data + paintInfo.expandBoundsForStrokeAA(bounds); + } -void PathTessellator::tessellateLines(const float* points, int count, SkPaint* paint, - const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer) { +void PathTessellator::tessellateLines(const float* points, int count, const SkPaint* paint, + const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer) { ATRACE_CALL(); const PaintInfo paintInfo(paint, transform); @@ -873,14 +882,15 @@ void PathTessellator::tessellateLines(const float* points, int count, SkPaint* p expandRectToCoverVertex(bounds, tempVerticesData[1]); } - expandBoundsForStroke(bounds, paint, true); // force-expand bounds to incorporate stroke - // since multiple objects tessellated into buffer, separate them with degen tris if (paintInfo.isAA) { vertexBuffer.createDegenerateSeparators<AlphaVertex>(lineAllocSize); } else { vertexBuffer.createDegenerateSeparators<Vertex>(lineAllocSize); } + + // expand bounds from vertex coords to pixel data + paintInfo.expandBoundsForStrokeAA(bounds); } /////////////////////////////////////////////////////////////////////////////// @@ -939,8 +949,8 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo } int size = outputVertices.size(); - if (size >= 2 && outputVertices[0].position[0] == outputVertices[size - 1].position[0] && - outputVertices[0].position[1] == outputVertices[size - 1].position[1]) { + if (size >= 2 && outputVertices[0].x == outputVertices[size - 1].x && + outputVertices[0].y == outputVertices[size - 1].y) { outputVertices.pop(); return true; } diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h index 85797fc..e43b101 100644 --- a/libs/hwui/PathTessellator.h +++ b/libs/hwui/PathTessellator.h @@ -22,96 +22,23 @@ #include "Matrix.h" #include "Rect.h" #include "Vertex.h" +#include "VertexBuffer.h" namespace android { namespace uirenderer { -class VertexBuffer { -public: - VertexBuffer(): - mBuffer(0), - mVertexCount(0), - mCleanupMethod(NULL) - {} - - ~VertexBuffer() { - if (mCleanupMethod) mCleanupMethod(mBuffer); - } - - /** - This should be the only method used by the PathTessellator. Subsequent calls to alloc will - allocate space within the first allocation (useful if you want to eventually allocate - multiple regions within a single VertexBuffer, such as with PathTessellator::tesselateLines() - */ - template <class TYPE> - 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 + vertexCount; - return reallocBuffer; - } - mVertexCount = vertexCount; - mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount]; - mCleanupMethod = &(cleanup<TYPE>); - - return (TYPE*)mBuffer; - } - - 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 + 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)); - } - } - -private: - template <class TYPE> - static void cleanup(void* buffer) { - delete[] (TYPE*)buffer; - } - - void* mBuffer; - unsigned int mVertexCount; - - void* mReallocBuffer; // used for multi-allocation - - void (*mCleanupMethod)(void*); -}; - class PathTessellator { public: - static void expandBoundsForStroke(SkRect& bounds, const SkPaint* paint, bool forceExpand); + static void expandBoundsForStroke(SkRect& bounds, const SkPaint* paint); static void tessellatePath(const SkPath& path, const SkPaint* paint, - const mat4 *transform, VertexBuffer& vertexBuffer); + const mat4& transform, VertexBuffer& vertexBuffer); - static void tessellatePoints(const float* points, int count, SkPaint* paint, - const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer); + static void tessellatePoints(const float* points, int count, const 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); + static void tessellateLines(const float* points, int count, const SkPaint* paint, + const mat4& transform, SkRect& bounds, VertexBuffer& vertexBuffer); private: static bool approximatePathOutlineVertices(const SkPath &path, bool forceClose, diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp index 7814a01..a679552 100644 --- a/libs/hwui/Program.cpp +++ b/libs/hwui/Program.cpp @@ -173,7 +173,7 @@ void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix, // 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(Vertex::gGeometryFudgeFactor, Vertex::gGeometryFudgeFactor); + p.translate(Vertex::GeometryFudgeFactor(), Vertex::GeometryFudgeFactor()); glUniformMatrix4fv(projection, 1, GL_FALSE, &p.data[0]); } mProjection = projectionMatrix; diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index 4f94afc..bc0f211 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -207,7 +207,7 @@ struct ProgramDescription { * the fragment shader. When this method returns true, the program should * be provided with a modulation color. */ - bool setColor(const float r, const float g, const float b, const float a) { + bool setColorModulate(const float a) { modulate = a < COLOR_COMPONENT_THRESHOLD; return modulate; } @@ -217,7 +217,7 @@ struct ProgramDescription { * the fragment shader. When this method returns true, the program should * be provided with a modulation color. */ - bool setAlpha8Color(const float r, const float g, const float b, const float a) { + bool setAlpha8ColorModulate(const float r, const float g, const float b, const float a) { modulate = a < COLOR_COMPONENT_THRESHOLD || r > COLOR_COMPONENT_INV_THRESHOLD || g > COLOR_COMPONENT_INV_THRESHOLD || b > COLOR_COMPONENT_INV_THRESHOLD; return modulate; diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h index dabd8d4..c230149 100644 --- a/libs/hwui/Rect.h +++ b/libs/hwui/Rect.h @@ -190,19 +190,19 @@ public: * from this inset will only incur similarly small errors in output, due to transparency * in extreme outside of the geometry. */ - left = floorf(left + Vertex::gGeometryFudgeFactor); - top = floorf(top + Vertex::gGeometryFudgeFactor); - right = ceilf(right - Vertex::gGeometryFudgeFactor); - bottom = ceilf(bottom - Vertex::gGeometryFudgeFactor); + left = floorf(left + Vertex::GeometryFudgeFactor()); + top = floorf(top + Vertex::GeometryFudgeFactor()); + right = ceilf(right - Vertex::GeometryFudgeFactor()); + bottom = ceilf(bottom - Vertex::GeometryFudgeFactor()); } else { /* For other geometry, we do the regular rounding in order to snap, but also outset the * bounds by a fudge factor. This ensures that ambiguous geometry (e.g. a non-AA Rect * with top left at (0.5, 0.5)) will err on the side of a larger damage rect. */ - left = floorf(left + 0.5f - Vertex::gGeometryFudgeFactor); - top = floorf(top + 0.5f - Vertex::gGeometryFudgeFactor); - right = floorf(right + 0.5f + Vertex::gGeometryFudgeFactor); - bottom = floorf(bottom + 0.5f + Vertex::gGeometryFudgeFactor); + left = floorf(left + 0.5f - Vertex::GeometryFudgeFactor()); + top = floorf(top + 0.5f - Vertex::GeometryFudgeFactor()); + right = floorf(right + 0.5f + Vertex::GeometryFudgeFactor()); + bottom = floorf(bottom + 0.5f + Vertex::GeometryFudgeFactor()); } } @@ -213,6 +213,13 @@ public: bottom = floorf(bottom + 0.5f); } + void roundOut() { + left = floorf(left); + top = floorf(top); + right = ceilf(right); + bottom = ceilf(bottom); + } + void dump() const { ALOGD("Rect[l=%f t=%f r=%f b=%f]", left, top, right, bottom); } diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h new file mode 100644 index 0000000..9d4e83e --- /dev/null +++ b/libs/hwui/Renderer.h @@ -0,0 +1,257 @@ +/* + * 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_RENDERER_H +#define ANDROID_HWUI_RENDERER_H + +#include <SkRegion.h> + +#include <utils/String8.h> + +#include "AssetAtlas.h" +#include "SkPaint.h" + +namespace android { + +class Functor; +struct Res_png_9patch; + +namespace uirenderer { + +class DisplayList; +class Layer; +class Matrix4; +class SkiaColorFilter; +class SkiaShader; +class Patch; + +enum DrawOpMode { + kDrawOpMode_Immediate, + kDrawOpMode_Defer, + kDrawOpMode_Flush +}; + +/** + * Hwui's abstract version of Canvas. + * + * Provides methods for frame state operations, as well as the SkCanvas style transform/clip state, + * and varied drawing operations. + * + * Should at some point interact with native SkCanvas. + */ +class ANDROID_API Renderer { +public: + virtual ~Renderer() {} + + /** + * Indicates whether this renderer is recording drawing commands for later playback. + * If this method returns true, the drawing commands are deferred. + */ + virtual bool isRecording() const { + return false; + } + + /** + * Safely retrieves the mode from the specified xfermode. If the specified + * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode. + */ + static inline SkXfermode::Mode getXfermode(SkXfermode* mode) { + SkXfermode::Mode resultMode; + if (!SkXfermode::AsMode(mode, &resultMode)) { + resultMode = SkXfermode::kSrcOver_Mode; + } + return resultMode; + } + +// ---------------------------------------------------------------------------- +// Frame state operations +// ---------------------------------------------------------------------------- + /** + * Sets the dimension of the underlying drawing surface. This method must + * be called at least once every time the drawing surface changes size. + * + * @param width The width in pixels of the underlysing surface + * @param height The height in pixels of the underlysing surface + */ + virtual void setViewport(int width, int height) = 0; + + /** + * Prepares the renderer to draw a frame. This method must be invoked + * at the beginning of each frame. When this method is invoked, the + * entire drawing surface is assumed to be redrawn. + * + * @param opaque If true, the target surface is considered opaque + * and will not be cleared. If false, the target surface + * will be cleared + */ + virtual status_t prepare(bool opaque) = 0; + + /** + * Prepares the renderer to draw a frame. This method must be invoked + * at the beginning of each frame. Only the specified rectangle of the + * frame is assumed to be dirty. A clip will automatically be set to + * the specified rectangle. + * + * @param left The left coordinate of the dirty rectangle + * @param top The top coordinate of the dirty rectangle + * @param right The right coordinate of the dirty rectangle + * @param bottom The bottom coordinate of the dirty rectangle + * @param opaque If true, the target surface is considered opaque + * and will not be cleared. If false, the target surface + * will be cleared in the specified dirty rectangle + */ + virtual status_t prepareDirty(float left, float top, float right, float bottom, + bool opaque) = 0; + + /** + * Indicates the end of a frame. This method must be invoked whenever + * the caller is done rendering a frame. + */ + virtual void finish() = 0; + + /** + * This method must be invoked before handing control over to a draw functor. + * See callDrawGLFunction() for instance. + * + * This command must not be recorded inside display lists. + */ + virtual void interrupt() = 0; + + /** + * This method must be invoked after getting control back from a draw functor. + * + * This command must not be recorded inside display lists. + */ + virtual void resume() = 0; + +// ---------------------------------------------------------------------------- +// Canvas state operations +// ---------------------------------------------------------------------------- + // Save (layer) + virtual int getSaveCount() const = 0; + virtual int save(int flags) = 0; + virtual void restore() = 0; + virtual void restoreToCount(int saveCount) = 0; + + int saveLayer(float left, float top, float right, float bottom, + const SkPaint* paint, int flags) { + SkXfermode::Mode mode = SkXfermode::kSrcOver_Mode; + int alpha = 255; + if (paint) { + mode = getXfermode(paint->getXfermode()); + alpha = paint->getAlpha(); + } + return saveLayer(left, top, right, bottom, alpha, mode, flags); + } + int saveLayerAlpha(float left, float top, float right, float bottom, + int alpha, int flags) { + return saveLayer(left, top, right, bottom, alpha, SkXfermode::kSrcOver_Mode, flags); + } + virtual int saveLayer(float left, float top, float right, float bottom, + int alpha, SkXfermode::Mode mode, int flags) = 0; + + // Matrix + virtual void getMatrix(SkMatrix* outMatrix) const = 0; + virtual void translate(float dx, float dy, float dz = 0.0f) = 0; + virtual void rotate(float degrees) = 0; + virtual void scale(float sx, float sy) = 0; + virtual void skew(float sx, float sy) = 0; + + virtual void setMatrix(const SkMatrix* matrix) = 0; + virtual void concatMatrix(const SkMatrix* matrix) = 0; + + // clip + virtual const Rect& getClipBounds() const = 0; + virtual bool quickRejectConservative(float left, float top, + float right, float bottom) const = 0; + virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op) = 0; + virtual bool clipPath(const SkPath* path, SkRegion::Op op) = 0; + virtual bool clipRegion(const SkRegion* region, SkRegion::Op op) = 0; + + // Misc - should be implemented with SkPaint inspection + virtual void resetShader() = 0; + virtual void setupShader(SkiaShader* shader) = 0; + + virtual void resetColorFilter() = 0; + virtual void setupColorFilter(SkiaColorFilter* filter) = 0; + + virtual void resetShadow() = 0; + virtual void setupShadow(float radius, float dx, float dy, int color) = 0; + + virtual void resetPaintFilter() = 0; + virtual void setupPaintFilter(int clearBits, int setBits) = 0; + +// ---------------------------------------------------------------------------- +// Canvas draw operations +// ---------------------------------------------------------------------------- + virtual status_t drawColor(int color, SkXfermode::Mode mode) = 0; + + // Bitmap-based + virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint) = 0; + virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + const SkPaint* paint) = 0; + virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, + float srcRight, float srcBottom, float dstLeft, float dstTop, + float dstRight, float dstBottom, const SkPaint* paint) = 0; + virtual status_t drawBitmapData(const SkBitmap* bitmap, float left, float top, + const SkPaint* paint) = 0; + virtual status_t drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, int meshHeight, + const float* vertices, const int* colors, const SkPaint* paint) = 0; + virtual status_t drawPatch(const SkBitmap* bitmap, const Res_png_9patch* patch, + float left, float top, float right, float bottom, const SkPaint* paint) = 0; + + // Shapes + virtual status_t drawRect(float left, float top, float right, float bottom, + const SkPaint* paint) = 0; + virtual status_t drawRects(const float* rects, int count, const SkPaint* paint) = 0; + virtual status_t drawRoundRect(float left, float top, float right, float bottom, + float rx, float ry, const SkPaint* paint) = 0; + virtual status_t drawCircle(float x, float y, float radius, const SkPaint* paint) = 0; + virtual status_t drawOval(float left, float top, float right, float bottom, + const SkPaint* paint) = 0; + virtual status_t drawArc(float left, float top, float right, float bottom, + float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) = 0; + virtual status_t drawPath(const SkPath* path, const SkPaint* paint) = 0; + virtual status_t drawLines(const float* points, int count, const SkPaint* paint) = 0; + virtual status_t drawPoints(const float* points, int count, const SkPaint* paint) = 0; + + // Text + virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y, + const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, + DrawOpMode drawOpMode = kDrawOpMode_Immediate) = 0; + virtual status_t drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path, + float hOffset, float vOffset, const SkPaint* paint) = 0; + virtual status_t drawPosText(const char* text, int bytesCount, int count, + const float* positions, const SkPaint* paint) = 0; + +// ---------------------------------------------------------------------------- +// Canvas draw operations - special +// ---------------------------------------------------------------------------- + virtual status_t drawLayer(Layer* layer, float x, float y) = 0; + virtual status_t drawDisplayList(DisplayList* displayList, Rect& dirty, + int32_t replayFlags) = 0; + + // TODO: rename for consistency + virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty) = 0; + +private: +}; // class Renderer + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_RENDERER_H diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp index 3f77021..e58857c 100644 --- a/libs/hwui/ResourceCache.cpp +++ b/libs/hwui/ResourceCache.cpp @@ -40,7 +40,7 @@ void ResourceCache::logCache() { ResourceCache::ResourceCache() { Mutex::Autolock _l(mLock); - mCache = new KeyedVector<void*, ResourceReference*>(); + mCache = new KeyedVector<const void*, ResourceReference*>(); } ResourceCache::~ResourceCache() { @@ -61,13 +61,13 @@ void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) incrementRefcountLocked(resource, resourceType); } -void ResourceCache::incrementRefcount(SkBitmap* bitmapResource) { +void ResourceCache::incrementRefcount(const SkBitmap* bitmapResource) { bitmapResource->pixelRef()->globalRef(); SkSafeRef(bitmapResource->getColorTable()); incrementRefcount((void*) bitmapResource, kBitmap); } -void ResourceCache::incrementRefcount(SkPath* pathResource) { +void ResourceCache::incrementRefcount(const SkPath* pathResource) { incrementRefcount((void*) pathResource, kPath); } @@ -81,7 +81,7 @@ void ResourceCache::incrementRefcount(SkiaColorFilter* filterResource) { incrementRefcount((void*) filterResource, kColorFilter); } -void ResourceCache::incrementRefcount(Res_png_9patch* patchResource) { +void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) { incrementRefcount((void*) patchResource, kNinePatch); } @@ -99,13 +99,13 @@ void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourc ref->refCount++; } -void ResourceCache::incrementRefcountLocked(SkBitmap* bitmapResource) { +void ResourceCache::incrementRefcountLocked(const SkBitmap* bitmapResource) { bitmapResource->pixelRef()->globalRef(); SkSafeRef(bitmapResource->getColorTable()); incrementRefcountLocked((void*) bitmapResource, kBitmap); } -void ResourceCache::incrementRefcountLocked(SkPath* pathResource) { +void ResourceCache::incrementRefcountLocked(const SkPath* pathResource) { incrementRefcountLocked((void*) pathResource, kPath); } @@ -119,7 +119,7 @@ void ResourceCache::incrementRefcountLocked(SkiaColorFilter* filterResource) { incrementRefcountLocked((void*) filterResource, kColorFilter); } -void ResourceCache::incrementRefcountLocked(Res_png_9patch* patchResource) { +void ResourceCache::incrementRefcountLocked(const Res_png_9patch* patchResource) { incrementRefcountLocked((void*) patchResource, kNinePatch); } @@ -132,13 +132,13 @@ void ResourceCache::decrementRefcount(void* resource) { decrementRefcountLocked(resource); } -void ResourceCache::decrementRefcount(SkBitmap* bitmapResource) { +void ResourceCache::decrementRefcount(const SkBitmap* bitmapResource) { bitmapResource->pixelRef()->globalUnref(); SkSafeUnref(bitmapResource->getColorTable()); decrementRefcount((void*) bitmapResource); } -void ResourceCache::decrementRefcount(SkPath* pathResource) { +void ResourceCache::decrementRefcount(const SkPath* pathResource) { decrementRefcount((void*) pathResource); } @@ -152,7 +152,7 @@ void ResourceCache::decrementRefcount(SkiaColorFilter* filterResource) { decrementRefcount((void*) filterResource); } -void ResourceCache::decrementRefcount(Res_png_9patch* patchResource) { +void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) { decrementRefcount((void*) patchResource); } @@ -173,13 +173,13 @@ void ResourceCache::decrementRefcountLocked(void* resource) { } } -void ResourceCache::decrementRefcountLocked(SkBitmap* bitmapResource) { +void ResourceCache::decrementRefcountLocked(const SkBitmap* bitmapResource) { bitmapResource->pixelRef()->globalUnref(); SkSafeUnref(bitmapResource->getColorTable()); decrementRefcountLocked((void*) bitmapResource); } -void ResourceCache::decrementRefcountLocked(SkPath* pathResource) { +void ResourceCache::decrementRefcountLocked(const SkPath* pathResource) { decrementRefcountLocked((void*) pathResource); } @@ -193,7 +193,7 @@ void ResourceCache::decrementRefcountLocked(SkiaColorFilter* filterResource) { decrementRefcountLocked((void*) filterResource); } -void ResourceCache::decrementRefcountLocked(Res_png_9patch* patchResource) { +void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) { decrementRefcountLocked((void*) patchResource); } @@ -223,12 +223,12 @@ void ResourceCache::destructorLocked(SkPath* resource) { } } -void ResourceCache::destructor(SkBitmap* resource) { +void ResourceCache::destructor(const SkBitmap* resource) { Mutex::Autolock _l(mLock); destructorLocked(resource); } -void ResourceCache::destructorLocked(SkBitmap* resource) { +void ResourceCache::destructorLocked(const SkBitmap* resource) { ssize_t index = mCache->indexOfKey(resource); ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL; if (ref == NULL) { @@ -345,7 +345,7 @@ bool ResourceCache::recycleLocked(SkBitmap* resource) { * This method should only be called while the mLock mutex is held (that mutex is grabbed * by the various destructor() and recycle() methods which call this method). */ -void ResourceCache::deleteResourceReferenceLocked(void* resource, ResourceReference* ref) { +void ResourceCache::deleteResourceReferenceLocked(const void* resource, ResourceReference* ref) { if (ref->recycled && ref->resourceType == kBitmap) { ((SkBitmap*) resource)->setPixels(NULL, NULL); } diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h index ea0c1b5..c06b09b 100644 --- a/libs/hwui/ResourceCache.h +++ b/libs/hwui/ResourceCache.h @@ -70,42 +70,42 @@ public: void lock(); void unlock(); - void incrementRefcount(SkPath* resource); - void incrementRefcount(SkBitmap* resource); + void incrementRefcount(const SkPath* resource); + void incrementRefcount(const SkBitmap* resource); void incrementRefcount(SkiaShader* resource); void incrementRefcount(SkiaColorFilter* resource); - void incrementRefcount(Res_png_9patch* resource); + void incrementRefcount(const Res_png_9patch* resource); void incrementRefcount(Layer* resource); - void incrementRefcountLocked(SkPath* resource); - void incrementRefcountLocked(SkBitmap* resource); + void incrementRefcountLocked(const SkPath* resource); + void incrementRefcountLocked(const SkBitmap* resource); void incrementRefcountLocked(SkiaShader* resource); void incrementRefcountLocked(SkiaColorFilter* resource); - void incrementRefcountLocked(Res_png_9patch* resource); + void incrementRefcountLocked(const Res_png_9patch* resource); void incrementRefcountLocked(Layer* resource); - void decrementRefcount(SkBitmap* resource); - void decrementRefcount(SkPath* resource); + void decrementRefcount(const SkBitmap* resource); + void decrementRefcount(const SkPath* resource); void decrementRefcount(SkiaShader* resource); void decrementRefcount(SkiaColorFilter* resource); - void decrementRefcount(Res_png_9patch* resource); + void decrementRefcount(const Res_png_9patch* resource); void decrementRefcount(Layer* resource); - void decrementRefcountLocked(SkBitmap* resource); - void decrementRefcountLocked(SkPath* resource); + void decrementRefcountLocked(const SkBitmap* resource); + void decrementRefcountLocked(const SkPath* resource); void decrementRefcountLocked(SkiaShader* resource); void decrementRefcountLocked(SkiaColorFilter* resource); - void decrementRefcountLocked(Res_png_9patch* resource); + void decrementRefcountLocked(const Res_png_9patch* resource); void decrementRefcountLocked(Layer* resource); void destructor(SkPath* resource); - void destructor(SkBitmap* resource); + void destructor(const SkBitmap* resource); void destructor(SkiaShader* resource); void destructor(SkiaColorFilter* resource); void destructor(Res_png_9patch* resource); void destructorLocked(SkPath* resource); - void destructorLocked(SkBitmap* resource); + void destructorLocked(const SkBitmap* resource); void destructorLocked(SkiaShader* resource); void destructorLocked(SkiaColorFilter* resource); void destructorLocked(Res_png_9patch* resource); @@ -114,7 +114,7 @@ public: bool recycleLocked(SkBitmap* resource); private: - void deleteResourceReferenceLocked(void* resource, ResourceReference* ref); + void deleteResourceReferenceLocked(const void* resource, ResourceReference* ref); void incrementRefcount(void* resource, ResourceType resourceType); void incrementRefcountLocked(void* resource, ResourceType resourceType); @@ -131,7 +131,7 @@ private: */ mutable Mutex mLock; - KeyedVector<void*, ResourceReference*>* mCache; + KeyedVector<const void*, ResourceReference*>* mCache; }; }; // namespace uirenderer diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp new file mode 100644 index 0000000..b9ce872 --- /dev/null +++ b/libs/hwui/ShadowTessellator.cpp @@ -0,0 +1,119 @@ +/* + * 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 <math.h> +#include <utils/Log.h> + +#include "AmbientShadow.h" +#include "ShadowTessellator.h" +#include "SpotShadow.h" + +namespace android { +namespace uirenderer { + +template<typename T> +static inline T max(T a, T b) { + return a > b ? a : b; +} + +// TODO: Support path as the input of the polygon instead of the rect's width +// and height. And the z values need to be computed according to the +// transformation for each vertex. +/** + * Generate the polygon for the caster. + * + * @param width the width of the caster + * @param height the height of the caster + * @param casterTransform transformation info of the caster + * @param polygon return the caster's polygon + * + */ +void ShadowTessellator::generateCasterPolygon(float width, float height, + const mat4& casterTransform, int vertexCount, Vector3* polygon) { + Rect blockRect(0, 0, width, height); + polygon[0].x = blockRect.left; + polygon[0].y = blockRect.top; + polygon[0].z = 0; + polygon[1].x = blockRect.right; + polygon[1].y = blockRect.top; + polygon[1].z = 0; + polygon[2].x = blockRect.right; + polygon[2].y = blockRect.bottom; + polygon[2].z = 0; + polygon[3].x = blockRect.left; + polygon[3].y = blockRect.bottom; + polygon[3].z = 0; + casterTransform.mapPoint3d(polygon[0]); + casterTransform.mapPoint3d(polygon[1]); + casterTransform.mapPoint3d(polygon[2]); + casterTransform.mapPoint3d(polygon[3]); +} + +void ShadowTessellator::tessellateAmbientShadow(float width, float height, + const mat4& casterTransform, VertexBuffer& shadowVertexBuffer) { + + const int vertexCount = 4; + Vector3 polygon[vertexCount]; + generateCasterPolygon(width, height, casterTransform, vertexCount, polygon); + + // A bunch of parameters to tweak the shadow. + // TODO: Allow some of these changable by debug settings or APIs. + const int rays = 128; + const int layers = 2; + const float strength = 0.5; + const float heightFactor = 128; + const float geomFactor = 64; + + AmbientShadow::createAmbientShadow(polygon, vertexCount, rays, layers, strength, + heightFactor, geomFactor, shadowVertexBuffer); + +} + +void ShadowTessellator::tessellateSpotShadow(float width, float height, + const mat4& receiverTransform, int screenWidth, int screenHeight, + const mat4& casterTransform, VertexBuffer& shadowVertexBuffer) { + const int vertexCount = 4; + Vector3 polygon[vertexCount]; + generateCasterPolygon(width, height, casterTransform, vertexCount, polygon); + + // A bunch of parameters to tweak the shadow. + // TODO: Allow some of these changable by debug settings or APIs. + const int rays = 256; + const int layers = 2; + const float strength = 0.5; + int maximal = max(screenWidth, screenHeight); + Vector3 lightCenter(screenWidth / 2, 0, maximal); +#if DEBUG_SHADOW + ALOGD("light center %f %f %f", lightCenter.x, lightCenter.y, lightCenter.z); +#endif + + // light position (because it's in local space) needs to compensate for receiver transform + // TODO: should apply to light orientation, not just position + Matrix4 reverseReceiverTransform; + reverseReceiverTransform.loadInverse(receiverTransform); + reverseReceiverTransform.mapPoint3d(lightCenter); + + const float lightSize = maximal / 8; + const int lightVertexCount = 16; + + SpotShadow::createSpotShadow(polygon, vertexCount, lightCenter, lightSize, + lightVertexCount, rays, layers, strength, shadowVertexBuffer); + +} +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h new file mode 100644 index 0000000..44ac8c0 --- /dev/null +++ b/libs/hwui/ShadowTessellator.h @@ -0,0 +1,45 @@ + +/* + * 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_SHADOW_TESSELLATOR_H +#define ANDROID_HWUI_SHADOW_TESSELLATOR_H + +#include "Debug.h" +#include "Matrix.h" + +namespace android { +namespace uirenderer { + +class ShadowTessellator { +public: + static void tessellateAmbientShadow(float width, float height, + const mat4& casterTransform, VertexBuffer& shadowVertexBuffer); + + static void tessellateSpotShadow(float width, float height, + const mat4& receiverTransform, int screenWidth, int screenHeight, + const mat4& casterTransform, VertexBuffer& shadowVertexBuffer); + +private: + static void generateCasterPolygon(float width, float height, + const mat4& casterTransform, int vertexCount, Vector3* polygon); + +}; // ShadowTessellator + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_SHADOW_TESSELLATOR_H diff --git a/libs/hwui/SkiaColorFilter.h b/libs/hwui/SkiaColorFilter.h index 2feb834..c222a2d 100644 --- a/libs/hwui/SkiaColorFilter.h +++ b/libs/hwui/SkiaColorFilter.h @@ -36,7 +36,8 @@ namespace uirenderer { * Represents a Skia color filter. A color filter modifies a ProgramDescription * and sets uniforms on the resulting shaders. */ -struct SkiaColorFilter { +class SkiaColorFilter { +public: /** * Type of Skia color filter in use. */ @@ -80,7 +81,8 @@ private: /** * A color filter that multiplies the source color with a matrix and adds a vector. */ -struct SkiaColorMatrixFilter: public SkiaColorFilter { +class SkiaColorMatrixFilter: public SkiaColorFilter { +public: ANDROID_API SkiaColorMatrixFilter(SkColorFilter *skFilter, float* matrix, float* vector); ~SkiaColorMatrixFilter(); @@ -96,7 +98,8 @@ private: * A color filters that multiplies the source color with a fixed value and adds * another fixed value. Ignores the alpha channel of both arguments. */ -struct SkiaLightingFilter: public SkiaColorFilter { +class SkiaLightingFilter: public SkiaColorFilter { +public: ANDROID_API SkiaLightingFilter(SkColorFilter *skFilter, int multiply, int add); void describe(ProgramDescription& description, const Extensions& extensions); @@ -111,7 +114,8 @@ private: * A color filters that blends the source color with a specified destination color * and PorterDuff blending mode. */ -struct SkiaBlendFilter: public SkiaColorFilter { +class SkiaBlendFilter: public SkiaColorFilter { +public: ANDROID_API SkiaBlendFilter(SkColorFilter *skFilter, int color, SkXfermode::Mode mode); void describe(ProgramDescription& description, const Extensions& extensions); diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 797ed10..4f2a432 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -142,7 +142,7 @@ void SkiaBitmapShader::describe(ProgramDescription& description, const Extension } void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView, - const Snapshot& snapshot, GLuint* textureUnit) { + const Snapshot&, GLuint* textureUnit) { GLuint textureSlot = (*textureUnit)++; Caches::getInstance().activeTexture(textureSlot); @@ -228,7 +228,7 @@ void SkiaLinearGradientShader::describe(ProgramDescription& description, } void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelView, - const Snapshot& snapshot, GLuint* textureUnit) { + const Snapshot&, GLuint* textureUnit) { if (CC_UNLIKELY(!mIsSimple)) { GLuint textureSlot = (*textureUnit)++; Caches::getInstance().activeTexture(textureSlot); @@ -264,7 +264,7 @@ static void toCircularUnitMatrix(const float x, const float y, const float radiu SkiaCircularGradientShader::SkiaCircularGradientShader(float x, float y, float radius, uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend): - SkiaSweepGradientShader(kCircularGradient, x, y, colors, positions, count, key, + SkiaSweepGradientShader(kCircularGradient, colors, positions, count, key, tileMode, matrix, blend) { SkMatrix unitMatrix; toCircularUnitMatrix(x, y, radius, &unitMatrix); @@ -314,11 +314,12 @@ SkiaSweepGradientShader::SkiaSweepGradientShader(float x, float y, uint32_t* col mIsSimple = count == 2; } -SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, +SkiaSweepGradientShader::SkiaSweepGradientShader(Type type, uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend): SkiaShader(type, key, tileMode, tileMode, matrix, blend), mColors(colors), mPositions(positions), mCount(count) { + // protected method, that doesn't setup mUnitMatrix - should be handled by subclass mIsSimple = count == 2 && tileMode == SkShader::kClamp_TileMode; } diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h index cc56c50..6015761 100644 --- a/libs/hwui/SkiaShader.h +++ b/libs/hwui/SkiaShader.h @@ -43,7 +43,8 @@ class Caches; * Represents a Skia shader. A shader will modify the GL context and active * program to recreate the original effect. */ -struct SkiaShader { +class SkiaShader { +public: /** * Type of Skia shader in use. */ @@ -192,7 +193,7 @@ struct SkiaSweepGradientShader: public SkiaShader { GLuint* textureUnit); protected: - SkiaSweepGradientShader(Type type, float x, float y, uint32_t* colors, float* positions, + SkiaSweepGradientShader(Type type, uint32_t* colors, float* positions, int count, SkShader* key, SkShader::TileMode tileMode, SkMatrix* matrix, bool blend); SkiaSweepGradientShader() { } diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index d26ee38..a6ec183 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -70,6 +70,10 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags): } else { region = NULL; } + + if (saveFlags & Snapshot::kFlagProjectionTarget) { + flags |= Snapshot::kFlagProjectionTarget; + } } /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index cc6d0cd..d61d972 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -75,7 +75,13 @@ public: * Indicates that this snapshot or an ancestor snapshot is * an FBO layer. */ - kFlagFboTarget = 0x10 + kFlagFboTarget = 0x10, + /** + * Indicates that the save/restore pair encapsulates a + * projection target, and that after the restore any projected + * descendents should be drawn. + */ + kFlagProjectionTarget = 0x20 }; /** diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp new file mode 100644 index 0000000..5d489a7 --- /dev/null +++ b/libs/hwui/SpotShadow.cpp @@ -0,0 +1,839 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "OpenGLRenderer" + +#define SHADOW_SHRINK_SCALE 0.1f + +#include <math.h> +#include <utils/Log.h> + +#include "SpotShadow.h" +#include "Vertex.h" + +namespace android { +namespace uirenderer { + +/** + * Calculate the intersection of a ray with a polygon. + * It assumes the ray originates inside the polygon. + * + * @param poly The polygon, which is represented in a Vector2 array. + * @param polyLength The length of caster's polygon in terms of number of + * vertices. + * @param point the start of the ray + * @param dx the x vector of the ray + * @param dy the y vector of the ray + * @return the distance along the ray if it intersects with the polygon FP_NAN if otherwise + */ +float SpotShadow::rayIntersectPoly(const Vector2* poly, int polyLength, + const Vector2& point, float dx, float dy) { + double px = point.x; + double py = point.y; + int p1 = polyLength - 1; + for (int p2 = 0; p2 < polyLength; p2++) { + double p1x = poly[p1].x; + double p1y = poly[p1].y; + double p2x = poly[p2].x; + double p2y = poly[p2].y; + // The math below is derived from solving this formula, basically the + // intersection point should stay on both the ray and the edge of (p1, p2). + // solve([p1x+t*(p2x-p1x)=dx*t2+px,p1y+t*(p2y-p1y)=dy*t2+py],[t,t2]); + double div = (dx * (p1y - p2y) + dy * p2x - dy * p1x); + if (div != 0) { + double t = (dx * (p1y - py) + dy * px - dy * p1x) / (div); + if (t >= 0 && t <= 1) { + double t2 = (p1x * (py - p2y) + p2x * (p1y - py) + + px * (p2y - p1y)) / div; + if (t2 > 0) { + return (float)t2; + } + } + } + p1 = p2; + } + return FP_NAN; +} + +/** + * Calculate the centroid of a 2d polygon. + * + * @param poly The polygon, which is represented in a Vector2 array. + * @param polyLength The length of the polygon in terms of number of vertices. + * @return the centroid of the polygon. + */ +Vector2 SpotShadow::centroid2d(const Vector2* poly, int polyLength) { + double sumx = 0; + double sumy = 0; + int p1 = polyLength - 1; + double area = 0; + for (int p2 = 0; p2 < polyLength; p2++) { + double x1 = poly[p1].x; + double y1 = poly[p1].y; + double x2 = poly[p2].x; + double y2 = poly[p2].y; + double a = (x1 * y2 - x2 * y1); + sumx += (x1 + x2) * a; + sumy += (y1 + y2) * a; + area += a; + p1 = p2; + } + + double centroidx = sumx / (3 * area); + double centroidy = sumy / (3 * area); + return Vector2((float)centroidx, (float)centroidy); +} + +/** + * Sort points by their X coordinates + * + * @param points the points as a Vector2 array. + * @param pointsLength the number of vertices of the polygon. + */ +void SpotShadow::xsort(Vector2* points, int pointsLength) { + quicksortX(points, 0, pointsLength - 1); +} + +/** + * compute the convex hull of a collection of Points + * + * @param points the points as a Vector2 array. + * @param pointsLength the number of vertices of the polygon. + * @param retPoly pre allocated array of floats to put the vertices + * @return the number of points in the polygon 0 if no intersection + */ +int SpotShadow::hull(Vector2* points, int pointsLength, Vector2* retPoly) { + xsort(points, pointsLength); + int n = pointsLength; + Vector2 lUpper[n]; + lUpper[0] = points[0]; + lUpper[1] = points[1]; + + int lUpperSize = 2; + + for (int i = 2; i < n; i++) { + lUpper[lUpperSize] = points[i]; + lUpperSize++; + + while (lUpperSize > 2 && !rightTurn( + (double)lUpper[lUpperSize - 3].x, (double)lUpper[lUpperSize - 3].y, + (double)lUpper[lUpperSize - 2].x, (double)lUpper[lUpperSize - 2].y, + (double)lUpper[lUpperSize - 1].x, (double)lUpper[lUpperSize - 1].y)) { + // Remove the middle point of the three last + lUpper[lUpperSize - 2].x = lUpper[lUpperSize - 1].x; + lUpper[lUpperSize - 2].y = lUpper[lUpperSize - 1].y; + lUpperSize--; + } + } + + Vector2 lLower[n]; + lLower[0] = points[n - 1]; + lLower[1] = points[n - 2]; + + int lLowerSize = 2; + + for (int i = n - 3; i >= 0; i--) { + lLower[lLowerSize] = points[i]; + lLowerSize++; + + while (lLowerSize > 2 && !rightTurn( + (double)lLower[lLowerSize - 3].x, (double)lLower[lLowerSize - 3].y, + (double)lLower[lLowerSize - 2].x, (double)lLower[lLowerSize - 2].y, + (double)lLower[lLowerSize - 1].x, (double)lLower[lLowerSize - 1].y)) { + // Remove the middle point of the three last + lLower[lLowerSize - 2] = lLower[lLowerSize - 1]; + lLowerSize--; + } + } + int count = 0; + + for (int i = 0; i < lUpperSize; i++) { + retPoly[count] = lUpper[i]; + count++; + } + + for (int i = 1; i < lLowerSize - 1; i++) { + retPoly[count] = lLower[i]; + count++; + } + // TODO: Add test harness which verify that all the points are inside the hull. + return count; +} + +/** + * Test whether the 3 points form a right hand turn + * + * @param ax the x coordinate of point a + * @param ay the y coordinate of point a + * @param bx the x coordinate of point b + * @param by the y coordinate of point b + * @param cx the x coordinate of point c + * @param cy the y coordinate of point c + * @return true if a right hand turn + */ +bool SpotShadow::rightTurn(double ax, double ay, double bx, double by, + double cx, double cy) { + return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax) > EPSILON; +} + +/** + * Calculates the intersection of poly1 with poly2 and put in poly2. + * + * + * @param poly1 The 1st polygon, as a Vector2 array. + * @param poly1Length The number of vertices of 1st polygon. + * @param poly2 The 2nd and output polygon, as a Vector2 array. + * @param poly2Length The number of vertices of 2nd polygon. + * @return number of vertices in output polygon as poly2. + */ +int SpotShadow::intersection(Vector2* poly1, int poly1Length, + Vector2* poly2, int poly2Length) { + makeClockwise(poly1, poly1Length); + makeClockwise(poly2, poly2Length); + Vector2 poly[poly1Length * poly2Length + 2]; + int count = 0; + int pcount = 0; + + // If one vertex from one polygon sits inside another polygon, add it and + // count them. + for (int i = 0; i < poly1Length; i++) { + if (testPointInsidePolygon(poly1[i], poly2, poly2Length)) { + poly[count] = poly1[i]; + count++; + pcount++; + + } + } + + int insidePoly2 = pcount; + for (int i = 0; i < poly2Length; i++) { + if (testPointInsidePolygon(poly2[i], poly1, poly1Length)) { + poly[count] = poly2[i]; + count++; + } + } + + int insidePoly1 = count - insidePoly2; + // If all vertices from poly1 are inside poly2, then just return poly1. + if (insidePoly2 == poly1Length) { + memcpy(poly2, poly1, poly1Length * sizeof(Vector2)); + return poly1Length; + } + + // If all vertices from poly2 are inside poly1, then just return poly2. + if (insidePoly1 == poly2Length) { + return poly2Length; + } + + // Since neither polygon fully contain the other one, we need to add all the + // intersection points. + Vector2 intersection; + for (int i = 0; i < poly2Length; i++) { + for (int j = 0; j < poly1Length; j++) { + int poly2LineStart = i; + int poly2LineEnd = ((i + 1) % poly2Length); + int poly1LineStart = j; + int poly1LineEnd = ((j + 1) % poly1Length); + bool found = lineIntersection( + poly2[poly2LineStart].x, poly2[poly2LineStart].y, + poly2[poly2LineEnd].x, poly2[poly2LineEnd].y, + poly1[poly1LineStart].x, poly1[poly1LineStart].y, + poly1[poly1LineEnd].x, poly1[poly1LineEnd].y, + intersection); + if (found) { + poly[count].x = intersection.x; + poly[count].y = intersection.y; + count++; + } else { + Vector2 delta = poly2[i] - poly1[j]; + if (delta.lengthSquared() < 0.01) { + poly[count] = poly2[i]; + count++; + } + } + } + } + + if (count == 0) { + return 0; + } + + // Sort the result polygon around the center. + Vector2 center(0.0f, 0.0f); + for (int i = 0; i < count; i++) { + center += poly[i]; + } + center /= count; + sort(poly, count, center); + + // TODO: Verify the intersection works correctly, like any random point + // inside both poly1 and poly2 should be inside the intersection, and the + // result intersection polygon is convex. + + // Merge the vertices if they are too close. + poly2[0] = poly[0]; + int resultLength = 1; + for (int i = 1; i < count; i++) { + Vector2 delta = poly[i] - poly[i - 1]; + if (delta.lengthSquared() >= 0.01) { + poly2[resultLength] = poly[i]; + resultLength++; + } + } + + return resultLength; +} + +/** + * Sort points about a center point + * + * @param poly The in and out polyogon as a Vector2 array. + * @param polyLength The number of vertices of the polygon. + * @param center the center ctr[0] = x , ctr[1] = y to sort around. + */ +void SpotShadow::sort(Vector2* poly, int polyLength, const Vector2& center) { + quicksortCirc(poly, 0, polyLength - 1, center); +} + +/** + * Calculate the angle between and x and a y coordinate + */ +float SpotShadow::angle(const Vector2& point, const Vector2& center) { + return -(float)atan2(point.x - center.x, point.y - center.y); +} + +/** + * Swap points pointed to by i and j + */ +void SpotShadow::swap(Vector2* points, int i, int j) { + Vector2 temp = points[i]; + points[i] = points[j]; + points[j] = temp; +} + +/** + * quick sort implementation about the center. + */ +void SpotShadow::quicksortCirc(Vector2* points, int low, int high, + const Vector2& center) { + int i = low, j = high; + int p = low + (high - low) / 2; + float pivot = angle(points[p], center); + while (i <= j) { + while (angle(points[i], center) < pivot) { + i++; + } + while (angle(points[j], center) > pivot) { + j--; + } + + if (i <= j) { + swap(points, i, j); + i++; + j--; + } + } + if (low < j) quicksortCirc(points, low, j, center); + if (i < high) quicksortCirc(points, i, high, center); +} + +/** + * Sort points by x axis + * + * @param points points to sort + * @param low start index + * @param high end index + */ +void SpotShadow::quicksortX(Vector2* points, int low, int high) { + int i = low, j = high; + int p = low + (high - low) / 2; + float pivot = points[p].x; + while (i <= j) { + while (points[i].x < pivot) { + i++; + } + while (points[j].x > pivot) { + j--; + } + + if (i <= j) { + swap(points, i, j); + i++; + j--; + } + } + if (low < j) quicksortX(points, low, j); + if (i < high) quicksortX(points, i, high); +} + +/** + * Test whether a point is inside the polygon. + * + * @param testPoint the point to test + * @param poly the polygon + * @return true if the testPoint is inside the poly. + */ +bool SpotShadow::testPointInsidePolygon(const Vector2 testPoint, + const Vector2* poly, int len) { + bool c = false; + double testx = testPoint.x; + double testy = testPoint.y; + for (int i = 0, j = len - 1; i < len; j = i++) { + double startX = poly[j].x; + double startY = poly[j].y; + double endX = poly[i].x; + double endY = poly[i].y; + + if (((endY > testy) != (startY > testy)) && + (testx < (startX - endX) * (testy - endY) + / (startY - endY) + endX)) { + c = !c; + } + } + return c; +} + +/** + * Make the polygon turn clockwise. + * + * @param polygon the polygon as a Vector2 array. + * @param len the number of points of the polygon + */ +void SpotShadow::makeClockwise(Vector2* polygon, int len) { + if (polygon == 0 || len == 0) { + return; + } + if (!isClockwise(polygon, len)) { + reverse(polygon, len); + } +} + +/** + * Test whether the polygon is order in clockwise. + * + * @param polygon the polygon as a Vector2 array + * @param len the number of points of the polygon + */ +bool SpotShadow::isClockwise(Vector2* polygon, int len) { + double sum = 0; + double p1x = polygon[len - 1].x; + double p1y = polygon[len - 1].y; + for (int i = 0; i < len; i++) { + + double p2x = polygon[i].x; + double p2y = polygon[i].y; + sum += p1x * p2y - p2x * p1y; + p1x = p2x; + p1y = p2y; + } + return sum < 0; +} + +/** + * Reverse the polygon + * + * @param polygon the polygon as a Vector2 array + * @param len the number of points of the polygon + */ +void SpotShadow::reverse(Vector2* polygon, int len) { + int n = len / 2; + for (int i = 0; i < n; i++) { + Vector2 tmp = polygon[i]; + int k = len - 1 - i; + polygon[i] = polygon[k]; + polygon[k] = tmp; + } +} + +/** + * Intersects two lines in parametric form. This function is called in a tight + * loop, and we need double precision to get things right. + * + * @param x1 the x coordinate point 1 of line 1 + * @param y1 the y coordinate point 1 of line 1 + * @param x2 the x coordinate point 2 of line 1 + * @param y2 the y coordinate point 2 of line 1 + * @param x3 the x coordinate point 1 of line 2 + * @param y3 the y coordinate point 1 of line 2 + * @param x4 the x coordinate point 2 of line 2 + * @param y4 the y coordinate point 2 of line 2 + * @param ret the x,y location of the intersection + * @return true if it found an intersection + */ +inline bool SpotShadow::lineIntersection(double x1, double y1, double x2, double y2, + double x3, double y3, double x4, double y4, Vector2& ret) { + double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); + if (d == 0.0) return false; + + double dx = (x1 * y2 - y1 * x2); + double dy = (x3 * y4 - y3 * x4); + double x = (dx * (x3 - x4) - (x1 - x2) * dy) / d; + double y = (dx * (y3 - y4) - (y1 - y2) * dy) / d; + + // The intersection should be in the middle of the point 1 and point 2, + // likewise point 3 and point 4. + if (((x - x1) * (x - x2) > EPSILON) + || ((x - x3) * (x - x4) > EPSILON) + || ((y - y1) * (y - y2) > EPSILON) + || ((y - y3) * (y - y4) > EPSILON)) { + // Not interesected + return false; + } + ret.x = x; + ret.y = y; + return true; + +} + +/** + * Compute a horizontal circular polygon about point (x , y , height) of radius + * (size) + * + * @param points number of the points of the output polygon. + * @param lightCenter the center of the light. + * @param size the light size. + * @param ret result polygon. + */ +void SpotShadow::computeLightPolygon(int points, const Vector3& lightCenter, + float size, Vector3* ret) { + // TODO: Caching all the sin / cos values and store them in a look up table. + for (int i = 0; i < points; i++) { + double angle = 2 * i * M_PI / points; + ret[i].x = sinf(angle) * size + lightCenter.x; + ret[i].y = cosf(angle) * size + lightCenter.y; + ret[i].z = lightCenter.z; + } +} + +/** +* Generate the shadow from a spot light. +* +* @param poly x,y,z vertexes of a convex polygon that occludes the light source +* @param polyLength number of vertexes of the occluding polygon +* @param lightCenter the center of the light +* @param lightSize the radius of the light source +* @param lightVertexCount the vertex counter for the light polygon +* @param rays the number of vertexes to create along the edges of the shadow +* @param layers the number of layers of triangles strips to create +* @param strength the "darkness" of the shadow +* @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return +* empty strip if error. +* +*/ +void SpotShadow::createSpotShadow(const Vector3* poly, int polyLength, + const Vector3& lightCenter, float lightSize, int lightVertexCount, + int rays, int layers, float strength, VertexBuffer& retStrips) { + Vector3 light[lightVertexCount * 3]; + computeLightPolygon(lightVertexCount, lightCenter, lightSize, light); + computeSpotShadow(light, lightVertexCount, lightCenter, + poly, polyLength, rays, layers, strength, retStrips); +} + +/** + * Generate the shadow spot light of shape lightPoly and a object poly + * + * @param lightPoly x,y,z vertex of a convex polygon that is the light source + * @param lightPolyLength number of vertexes of the light source polygon + * @param poly x,y,z vertexes of a convex polygon that occludes the light source + * @param polyLength number of vertexes of the occluding polygon + * @param rays the number of vertexes to create along the edges of the shadow + * @param layers the number of layers of triangles strips to create + * @param strength the "darkness" of the shadow + * @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return + * empty strip if error. + */ +void SpotShadow::computeSpotShadow(const Vector3* lightPoly, int lightPolyLength, + const Vector3& lightCenter, const Vector3* poly, int polyLength, + int rays, int layers, float strength, VertexBuffer& shadowTriangleStrip) { + // Point clouds for all the shadowed vertices + Vector2 shadowRegion[lightPolyLength * polyLength]; + // Shadow polygon from one point light. + Vector2 outline[polyLength]; + Vector2 umbraMem[polyLength * lightPolyLength]; + Vector2* umbra = umbraMem; + + int umbraLength = 0; + + // Validate input, receiver is always at z = 0 plane. + bool inputPolyPositionValid = true; + for (int i = 0; i < polyLength; i++) { + if (poly[i].z <= 0.00001) { + inputPolyPositionValid = false; + ALOGE("polygon below the surface"); + break; + } + if (poly[i].z >= lightPoly[0].z) { + inputPolyPositionValid = false; + ALOGE("polygon above the light"); + break; + } + } + + // If the caster's position is invalid, don't draw anything. + if (!inputPolyPositionValid) { + return; + } + + // Calculate the umbra polygon based on intersections of all outlines + int k = 0; + for (int j = 0; j < lightPolyLength; j++) { + int m = 0; + for (int i = 0; i < polyLength; i++) { + float t = lightPoly[j].z - poly[i].z; + if (t == 0) { + return; + } + t = lightPoly[j].z / t; + float x = lightPoly[j].x - t * (lightPoly[j].x - poly[i].x); + float y = lightPoly[j].y - t * (lightPoly[j].y - poly[i].y); + + Vector2 newPoint = Vector2(x, y); + shadowRegion[k] = newPoint; + outline[m] = newPoint; + + k++; + m++; + } + + // For the first light polygon's vertex, use the outline as the umbra. + // Later on, use the intersection of the outline and existing umbra. + if (umbraLength == 0) { + for (int i = 0; i < polyLength; i++) { + umbra[i] = outline[i]; + } + umbraLength = polyLength; + } else { + int col = ((j * 255) / lightPolyLength); + umbraLength = intersection(outline, polyLength, umbra, umbraLength); + if (umbraLength == 0) { + break; + } + } + } + + // Generate the penumbra area using the hull of all shadow regions. + int shadowRegionLength = k; + Vector2 penumbra[k]; + int penumbraLength = hull(shadowRegion, shadowRegionLength, penumbra); + + // no real umbra make a fake one + if (umbraLength < 3) { + // The shadow from the centroid of the light polygon. + Vector2 centShadow[polyLength]; + + for (int i = 0; i < polyLength; i++) { + float t = lightCenter.z - poly[i].z; + if (t == 0) { + return; + } + t = lightCenter.z / t; + float x = lightCenter.x - t * (lightCenter.x - poly[i].x); + float y = lightCenter.y - t * (lightCenter.y - poly[i].y); + + centShadow[i].x = x; + centShadow[i].y = y; + } + + // Shrink the centroid's shadow by 10%. + // TODO: Study the magic number of 10%. + Vector2 shadowCentroid = centroid2d(centShadow, polyLength); + for (int i = 0; i < polyLength; i++) { + centShadow[i] = shadowCentroid * (1.0f - SHADOW_SHRINK_SCALE) + + centShadow[i] * SHADOW_SHRINK_SCALE; + } +#if DEBUG_SHADOW + ALOGD("No real umbra make a fake one, centroid2d = %f , %f", + shadowCentroid.x, shadowCentroid.y); +#endif + // Set the fake umbra, whose size is the same as the original polygon. + umbra = centShadow; + umbraLength = polyLength; + } + + generateTriangleStrip(penumbra, penumbraLength, umbra, umbraLength, + rays, layers, strength, shadowTriangleStrip); +} + +/** + * Generate a triangle strip given two convex polygons + * + * @param penumbra The outer polygon x,y vertexes + * @param penumbraLength The number of vertexes in the outer polygon + * @param umbra The inner outer polygon x,y vertexes + * @param umbraLength The number of vertexes in the inner polygon + * @param rays The number of points along the polygons to create + * @param layers The number of layers of triangle strips between the umbra and penumbra + * @param strength The max alpha of the umbra + * @param shadowTriangleStrip return an (x,y,alpha) triangle strip representing the shadow. Return + * empty strip if error. +**/ +void SpotShadow::generateTriangleStrip(const Vector2* penumbra, int penumbraLength, + const Vector2* umbra, int umbraLength, int rays, int layers, + float strength, VertexBuffer& shadowTriangleStrip) { + + int rings = layers + 1; + int size = rays * rings; + + float step = M_PI * 2 / rays; + // Centroid of the umbra. + Vector2 centroid = centroid2d(umbra, umbraLength); +#if DEBUG_SHADOW + ALOGD("centroid2d = %f , %f", centroid.x, centroid.y); +#endif + // Intersection to the penumbra. + float penumbraDistPerRay[rays]; + // Intersection to the umbra. + float umbraDistPerRay[rays]; + + for (int i = 0; i < rays; i++) { + // TODO: Setup a lookup table for all the sin/cos. + float dx = sinf(step * i); + float dy = cosf(step * i); + umbraDistPerRay[i] = rayIntersectPoly(umbra, umbraLength, centroid, + dx, dy); + if (isnan(umbraDistPerRay[i])) { + ALOGE("rayIntersectPoly returns NAN"); + return; + } + penumbraDistPerRay[i] = rayIntersectPoly(penumbra, penumbraLength, + centroid, dx, dy); + if (isnan(umbraDistPerRay[i])) { + ALOGE("rayIntersectPoly returns NAN"); + return; + } + } + + int stripSize = getStripSize(rays, layers); + AlphaVertex* shadowVertices = shadowTriangleStrip.alloc<AlphaVertex>(stripSize); + int currentIndex = 0; + // Calculate the vertex values in the penumbra area. + for (int r = 0; r < layers; r++) { + int firstInEachLayer = currentIndex; + for (int i = 0; i < rays; i++) { + float dx = sinf(step * i); + float dy = cosf(step * i); + + for (int j = r; j < (r + 2); j++) { + float layerRatio = j / (float)(rings - 1); + float deltaDist = layerRatio * (umbraDistPerRay[i] - penumbraDistPerRay[i]); + float currentDist = penumbraDistPerRay[i] + deltaDist; + float op = calculateOpacity(layerRatio, deltaDist); + AlphaVertex::set(&shadowVertices[currentIndex], + dx * currentDist + centroid.x, + dy * currentDist + centroid.y, + layerRatio * op * strength); + currentIndex++; + } + } + + // Duplicate the vertices from one layer to another one to make triangle + // strip. + shadowVertices[currentIndex++] = shadowVertices[firstInEachLayer]; + firstInEachLayer++; + shadowVertices[currentIndex++] = shadowVertices[firstInEachLayer]; + } + + int lastInPenumbra = currentIndex - 1; + shadowVertices[currentIndex++] = shadowVertices[lastInPenumbra]; + + // Preallocate the vertices (index as [firstInUmbra - 1]) for jumping from + // the penumbra to umbra. + currentIndex++; + int firstInUmbra = currentIndex; + + // traverse the umbra area in a zig zag pattern for strips. + for (int k = 0; k < rays; k++) { + int i = k / 2; + if ((k & 1) == 1) { + i = rays - i - 1; + } + float dx = sinf(step * i); + float dy = cosf(step * i); + + float ratio = 1.0; + float deltaDist = ratio * (umbraDistPerRay[i] - penumbraDistPerRay[i]); + float currentDist = penumbraDistPerRay[i] + deltaDist; + float op = calculateOpacity(ratio, deltaDist); + AlphaVertex::set(&shadowVertices[currentIndex], + dx * currentDist + centroid.x, dy * currentDist + centroid.y, + ratio * op * strength); + currentIndex++; + + } + + // Back fill the one vertex for jumping from penumbra to umbra. + shadowVertices[firstInUmbra - 1] = shadowVertices[firstInUmbra]; + +#if DEBUG_SHADOW + for (int i = 0; i < currentIndex; i++) { + ALOGD("shadow value: i %d, (x:%f, y:%f, a:%f)", i, shadowVertices[i].x, + shadowVertices[i].y, shadowVertices[i].alpha); + } +#endif +} + +/** + * This is only for experimental purpose. + * After intersections are calculated, we could smooth the polygon if needed. + * So far, we don't think it is more appealing yet. + * + * @param level The level of smoothness. + * @param rays The total number of rays. + * @param rayDist (In and Out) The distance for each ray. + * + */ +void SpotShadow::smoothPolygon(int level, int rays, float* rayDist) { + for (int k = 0; k < level; k++) { + for (int i = 0; i < rays; i++) { + float p1 = rayDist[(rays - 1 + i) % rays]; + float p2 = rayDist[i]; + float p3 = rayDist[(i + 1) % rays]; + rayDist[i] = (p1 + p2 * 2 + p3) / 4; + } + } +} + +/** + * Calculate the opacity according to the distance and falloff ratio. + * + * @param distRatio The distance ratio of current sample between umbra and + * penumbra area. + * @param deltaDist The distance between current sample to the penumbra area. + * @return The opacity according to the distance between umbra and penumbra. + */ +float SpotShadow::calculateOpacity(float distRatio, float deltaDist) { + // TODO: Experiment on the opacity calculation. + float falloffRatio = 1 + deltaDist * deltaDist; + return (distRatio + 1 - 1 / falloffRatio) / 2; +} + +/** + * Calculate the number of vertex we will create given a number of rays and layers + * + * @param rays number of points around the polygons you want + * @param layers number of layers of triangle strips you need + * @return number of vertex (multiply by 3 for number of floats) + */ +int SpotShadow::getStripSize(int rays, int layers) { + return (2 + rays + ((layers) * 2 * (rays + 1))); +} + +}; // namespace uirenderer +}; // namespace android + + + + diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h new file mode 100644 index 0000000..d8db43bf --- /dev/null +++ b/libs/hwui/SpotShadow.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HWUI_SPOT_SHADOW_H +#define ANDROID_HWUI_SPOT_SHADOW_H + +#include "Debug.h" +#include "Vector.h" +#include "VertexBuffer.h" + +namespace android { +namespace uirenderer { + +class SpotShadow { +public: + static void createSpotShadow(const Vector3* poly, int polyLength, + const Vector3& lightCenter, float lightSize, int lightVertexCount, + int rays, int layers, float strength, VertexBuffer& retStrips); + +private: + static void computeSpotShadow(const Vector3* lightPoly, int lightPolyLength, + const Vector3& lightCenter, const Vector3* poly, int polyLength, + int rays, int layers, float strength, VertexBuffer& retstrips); + + static void computeLightPolygon(int points, const Vector3& lightCenter, + float size, Vector3* ret); + + static int getStripSize(int rays, int layers); + static void smoothPolygon(int level, int rays, float* rayDist); + static float calculateOpacity(float jf, float deltaDist); + static float rayIntersectPoly(const Vector2* poly, int polyLength, + const Vector2& point, float dx, float dy); + + static Vector2 centroid2d(const Vector2* poly, int polyLength); + + static void xsort(Vector2* points, int pointsLength); + static int hull(Vector2* points, int pointsLength, Vector2* retPoly); + static bool rightTurn(double ax, double ay, double bx, double by, double cx, double cy); + static int intersection(Vector2* poly1, int poly1length, Vector2* poly2, int poly2length); + static void sort(Vector2* poly, int polyLength, const Vector2& center); + + static float angle(const Vector2& point, const Vector2& center); + static void swap(Vector2* points, int i, int j); + static void quicksortCirc(Vector2* points, int low, int high, const Vector2& center); + static void quicksortX(Vector2* points, int low, int high); + + static bool testPointInsidePolygon(const Vector2 testPoint, const Vector2* poly, int len); + static void makeClockwise(Vector2* polygon, int len); + static bool isClockwise(Vector2* polygon, int len); + static void reverse(Vector2* polygon, int len); + static inline bool lineIntersection(double x1, double y1, double x2, double y2, + double x3, double y3, double x4, double y4, Vector2& ret); + + static void generateTriangleStrip(const Vector2* penumbra, int penumbraLength, + const Vector2* umbra, int umbraLength, int rays, int layers, + float strength, VertexBuffer& retstrips); + + static const double EPSILON = 1e-7; +}; // SpotShadow + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_SPOT_SHADOW_H diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp new file mode 100644 index 0000000..05f6cf8 --- /dev/null +++ b/libs/hwui/StatefulBaseRenderer.cpp @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <SkCanvas.h> + +#include "StatefulBaseRenderer.h" + +namespace android { +namespace uirenderer { + +StatefulBaseRenderer::StatefulBaseRenderer() : + mDirtyClip(false), mWidth(-1), mHeight(-1), + mSaveCount(1), mFirstSnapshot(new Snapshot), mSnapshot(mFirstSnapshot) { +} + +void StatefulBaseRenderer::initializeSaveStack(float clipLeft, float clipTop, + float clipRight, float clipBottom) { + mSnapshot = new Snapshot(mFirstSnapshot, + SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + mSnapshot->setClip(clipLeft, clipTop, clipRight, clipBottom); + mSnapshot->fbo = getTargetFbo(); + mSaveCount = 1; +} + +void StatefulBaseRenderer::initializeViewport(int width, int height) { + mWidth = width; + mHeight = height; + + mFirstSnapshot->height = height; + mFirstSnapshot->viewport.set(0, 0, width, height); +} + +/////////////////////////////////////////////////////////////////////////////// +// Save (layer) +/////////////////////////////////////////////////////////////////////////////// + +/** + * Non-virtual implementation of save, guaranteed to save without side-effects + * + * The approach here and in restoreSnapshot(), allows subclasses to directly manipulate the save + * stack, and ensures restoreToCount() doesn't call back into subclass overrides. + */ +int StatefulBaseRenderer::saveSnapshot(int flags) { + mSnapshot = new Snapshot(mSnapshot, flags); + return mSaveCount++; +} + +int StatefulBaseRenderer::save(int flags) { + return saveSnapshot(flags); +} + +/** + * Non-virtual implementation of restore, guaranteed to restore without side-effects. + */ +void StatefulBaseRenderer::restoreSnapshot() { + sp<Snapshot> toRemove = mSnapshot; + sp<Snapshot> toRestore = mSnapshot->previous; + + mSaveCount--; + mSnapshot = toRestore; + + // subclass handles restore implementation + onSnapshotRestored(*toRemove, *toRestore); +} + +void StatefulBaseRenderer::restore() { + if (mSaveCount > 1) { + restoreSnapshot(); + } +} + +void StatefulBaseRenderer::restoreToCount(int saveCount) { + if (saveCount < 1) saveCount = 1; + + while (mSaveCount > saveCount) { + restoreSnapshot(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Matrix +/////////////////////////////////////////////////////////////////////////////// + +void StatefulBaseRenderer::getMatrix(SkMatrix* matrix) const { + mSnapshot->transform->copyTo(*matrix); +} + +void StatefulBaseRenderer::translate(float dx, float dy, float dz) { + mSnapshot->transform->translate(dx, dy, dz); +} + +void StatefulBaseRenderer::rotate(float degrees) { + mSnapshot->transform->rotate(degrees, 0.0f, 0.0f, 1.0f); +} + +void StatefulBaseRenderer::scale(float sx, float sy) { + mSnapshot->transform->scale(sx, sy, 1.0f); +} + +void StatefulBaseRenderer::skew(float sx, float sy) { + mSnapshot->transform->skew(sx, sy); +} + +void StatefulBaseRenderer::setMatrix(const SkMatrix* matrix) { + if (matrix) { + mSnapshot->transform->load(*matrix); + } else { + mSnapshot->transform->loadIdentity(); + } +} + +void StatefulBaseRenderer::setMatrix(const Matrix4& matrix) { + mSnapshot->transform->load(matrix); +} + +void StatefulBaseRenderer::concatMatrix(const SkMatrix* matrix) { + mat4 transform(*matrix); + mSnapshot->transform->multiply(transform); +} + +void StatefulBaseRenderer::concatMatrix(const Matrix4& matrix) { + mSnapshot->transform->multiply(matrix); +} + +/////////////////////////////////////////////////////////////////////////////// +// Clip +/////////////////////////////////////////////////////////////////////////////// + +bool StatefulBaseRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { + if (CC_LIKELY(currentTransform()->rectToRect())) { + mDirtyClip |= mSnapshot->clip(left, top, right, bottom, op); + return !mSnapshot->clipRect->isEmpty(); + } + + SkPath path; + path.addRect(left, top, right, bottom); + + return StatefulBaseRenderer::clipPath(&path, op); +} + +bool StatefulBaseRenderer::clipPath(const SkPath* path, SkRegion::Op op) { + SkMatrix transform; + currentTransform()->copyTo(transform); + + SkPath transformed; + path->transform(transform, &transformed); + + SkRegion clip; + if (!mSnapshot->previous->clipRegion->isEmpty()) { + clip.setRegion(*mSnapshot->previous->clipRegion); + } else { + if (mSnapshot->previous == firstSnapshot()) { + clip.setRect(0, 0, getWidth(), getHeight()); + } else { + Rect* bounds = mSnapshot->previous->clipRect; + clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom); + } + } + + SkRegion region; + region.setPath(transformed, clip); + + mDirtyClip |= mSnapshot->clipRegionTransformed(region, op); + return !mSnapshot->clipRect->isEmpty(); +} + +bool StatefulBaseRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) { + mDirtyClip |= mSnapshot->clipRegionTransformed(*region, op); + return !mSnapshot->clipRect->isEmpty(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Quick Rejection +/////////////////////////////////////////////////////////////////////////////// + +/** + * Calculates whether content drawn within the passed bounds would be outside of, or intersect with + * the clipRect. Does not modify the scissor. + * + * @param clipRequired if not null, will be set to true if element intersects clip + * (and wasn't rejected) + * + * @param snapOut if set, the geometry will be treated as having an AA ramp. + * See Rect::snapGeometryToPixelBoundaries() + */ +bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top, + float right, float bottom, bool* clipRequired, bool snapOut) const { + if (mSnapshot->isIgnored() || bottom <= top || right <= left) { + return true; + } + + Rect r(left, top, right, bottom); + currentTransform()->mapRect(r); + r.snapGeometryToPixelBoundaries(snapOut); + + Rect clipRect(*currentClipRect()); + clipRect.snapToPixelBoundaries(); + + if (!clipRect.intersects(r)) return true; + + // clip is required if geometry intersects clip rect + if (clipRequired) *clipRequired = !clipRect.contains(r); + return false; +} + +/** + * Returns false if drawing won't be clipped out. + * + * Makes the decision conservatively, by rounding out the mapped rect before comparing with the + * clipRect. To be used when perfect, pixel accuracy is not possible (esp. with tessellation) but + * rejection is still desired. + * + * This function, unlike quickRejectSetupScissor, should be used where precise geometry information + * isn't known (esp. when geometry adjusts based on scale). Generally, this will be first pass + * rejection where precise rejection isn't important, or precise information isn't available. + */ +bool StatefulBaseRenderer::quickRejectConservative(float left, float top, + float right, float bottom) const { + if (mSnapshot->isIgnored() || bottom <= top || right <= left) { + return true; + } + + Rect r(left, top, right, bottom); + currentTransform()->mapRect(r); + r.roundOut(); // rounded out to be conservative + + Rect clipRect(*currentClipRect()); + clipRect.snapToPixelBoundaries(); + + if (!clipRect.intersects(r)) return true; + + return false; +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h new file mode 100644 index 0000000..bf34bec --- /dev/null +++ b/libs/hwui/StatefulBaseRenderer.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HWUI_STATEFUL_BASE_RENDERER_H +#define ANDROID_HWUI_STATEFUL_BASE_RENDERER_H + +#include <utils/RefBase.h> + +#include "Renderer.h" +#include "Snapshot.h" + +namespace android { +namespace uirenderer { + +/** + * Abstract Renderer subclass, which implements Canvas state methods. + * + * Manages the Snapshot stack, implementing matrix, save/restore, and clipping methods in the + * Renderer interface. Drawing and recording classes that extend StatefulBaseRenderer will have + * different use cases: + * + * Drawing subclasses (i.e. OpenGLRenderer) can query attributes (such as transform) or hook into + * changes (e.g. save/restore) with minimal surface area for manipulating the stack itself. + * + * Recording subclasses (i.e. DisplayListRenderer) can both record and pass through state operations + * to StatefulBaseRenderer, so that not only will querying operations work (getClip/Matrix), but so + * that quickRejection can also be used. + */ +class StatefulBaseRenderer : public Renderer { +public: + StatefulBaseRenderer(); + + virtual status_t prepare(bool opaque) { + return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque); + } + void initializeViewport(int width, int height); + void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom); + + // getters + bool hasRectToRectTransform() const { + return CC_LIKELY(currentTransform()->rectToRect()); + } + + // Save (layer) + virtual int getSaveCount() const { return mSaveCount; } + virtual int save(int flags); + virtual void restore(); + virtual void restoreToCount(int saveCount); + //virtual int saveLayer(float left, float top, float right, float bottom, + // int alpha, SkXfermode::Mode mode, int flags); + + // Matrix + virtual void getMatrix(SkMatrix* outMatrix) const; + virtual void translate(float dx, float dy, float dz = 0.0f); + virtual void rotate(float degrees); + virtual void scale(float sx, float sy); + virtual void skew(float sx, float sy); + + virtual void setMatrix(const SkMatrix* matrix); + void setMatrix(const Matrix4& matrix); // internal only convenience method + virtual void concatMatrix(const SkMatrix* matrix); + void concatMatrix(const Matrix4& matrix); // internal only convenience method + + // Clip + const Rect& getClipBounds() const { return mSnapshot->getLocalClip(); } + virtual bool quickRejectConservative(float left, float top, float right, float bottom) const; + + virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); + virtual bool clipPath(const SkPath* path, SkRegion::Op op); + virtual bool clipRegion(const SkRegion* region, SkRegion::Op op); + +protected: + int getWidth() { return mWidth; } + int getHeight() { return mHeight; } + + // Save + int saveSnapshot(int flags); + void restoreSnapshot(); + + // allows subclasses to control what value is stored in snapshot's fbo field in + // initializeSaveStack + virtual GLuint getTargetFbo() const { + return -1; + } + + // Clip + bool calculateQuickRejectForScissor(float left, float top, float right, float bottom, + bool* clipRequired, bool snapOut) const; + + /** + * Called just after a restore has occurred. The 'removed' snapshot popped from the stack, + * 'restored' snapshot has become the top/current. + * + * Subclasses can override this method to handle layer restoration + */ + virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}; + + inline const Rect* currentClipRect() const { + return mSnapshot->clipRect; + } + + inline const mat4* currentTransform() const { + return mSnapshot->transform; + } + + inline const Snapshot* currentSnapshot() const { + return mSnapshot != NULL ? mSnapshot.get() : mFirstSnapshot.get(); + } + + inline const Snapshot* firstSnapshot() const { + return mFirstSnapshot.get(); + } + + // indicites that the clip has been changed since the last time it was consumed + bool mDirtyClip; + +private: + // Dimensions of the drawing surface + int mWidth, mHeight; + + // Number of saved states + int mSaveCount; + + // Base state + sp<Snapshot> mFirstSnapshot; + +protected: + // Current state + // TODO: should become private, once hooks needed by OpenGLRenderer are added + sp<Snapshot> mSnapshot; + +}; // class StatefulBaseRenderer + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_STATEFUL_BASE_RENDERER_H diff --git a/libs/hwui/Stencil.cpp b/libs/hwui/Stencil.cpp index 2764523..8ce57db 100644 --- a/libs/hwui/Stencil.cpp +++ b/libs/hwui/Stencil.cpp @@ -35,7 +35,7 @@ namespace uirenderer { Stencil::Stencil(): mState(kDisabled) { } -uint32_t Stencil::getStencilSize() { +uint8_t Stencil::getStencilSize() { return STENCIL_BUFFER_SIZE; } diff --git a/libs/hwui/Stencil.h b/libs/hwui/Stencil.h index 83ad668..c5e5186 100644 --- a/libs/hwui/Stencil.h +++ b/libs/hwui/Stencil.h @@ -40,7 +40,7 @@ public: * Returns the desired size for the stencil buffer. If the returned value * is 0, then no stencil buffer is required. */ - ANDROID_API static uint32_t getStencilSize(); + ANDROID_API static uint8_t getStencilSize(); /** * Returns the smallest stencil format accepted by render buffers. diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp index 0b2c130..4eec462 100644 --- a/libs/hwui/TextDropShadowCache.cpp +++ b/libs/hwui/TextDropShadowCache.cpp @@ -147,7 +147,7 @@ void TextDropShadowCache::setMaxSize(uint32_t maxSize) { // Callbacks /////////////////////////////////////////////////////////////////////////////// -void TextDropShadowCache::operator()(ShadowText& text, ShadowTexture*& texture) { +void TextDropShadowCache::operator()(ShadowText&, ShadowTexture*& texture) { if (texture) { mSize -= texture->bitmapSize; @@ -168,7 +168,7 @@ void TextDropShadowCache::clear() { mCache.clear(); } -ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32_t len, +ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* text, uint32_t len, int numGlyphs, float radius, const float* positions) { ShadowText entry(paint, radius, len, text, positions); ShadowTexture* texture = mCache.get(entry); diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h index 04d7357..54b930b 100644 --- a/libs/hwui/TextDropShadowCache.h +++ b/libs/hwui/TextDropShadowCache.h @@ -38,7 +38,7 @@ struct ShadowText { } // len is the number of bytes in text - ShadowText(SkPaint* paint, float radius, uint32_t len, const char* srcText, + ShadowText(const SkPaint* paint, float radius, uint32_t len, const char* srcText, const float* positions): len(len), radius(radius), positions(positions) { // TODO: Propagate this through the API, we should not cast here @@ -135,7 +135,7 @@ public: */ void operator()(ShadowText& text, ShadowTexture*& texture); - ShadowTexture* get(SkPaint* paint, const char* text, uint32_t len, + ShadowTexture* get(const SkPaint* paint, const char* text, uint32_t len, int numGlyphs, float radius, const float* positions); /** diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index ed0a79a..467f6ca 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -34,7 +34,7 @@ namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// TextureCache::TextureCache(): - mCache(LruCache<SkBitmap*, Texture*>::kUnlimitedCapacity), + mCache(LruCache<const SkBitmap*, Texture*>::kUnlimitedCapacity), mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)), mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) { char property[PROPERTY_VALUE_MAX]; @@ -58,7 +58,7 @@ TextureCache::TextureCache(): } TextureCache::TextureCache(uint32_t maxByteSize): - mCache(LruCache<SkBitmap*, Texture*>::kUnlimitedCapacity), + mCache(LruCache<const SkBitmap*, Texture*>::kUnlimitedCapacity), mSize(0), mMaxSize(maxByteSize) { init(); } @@ -103,7 +103,7 @@ void TextureCache::setFlushRate(float flushRate) { // Callbacks /////////////////////////////////////////////////////////////////////////////// -void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) { +void TextureCache::operator()(const SkBitmap*&, Texture*& texture) { // This will be called already locked if (texture) { mSize -= texture->bitmapSize; @@ -121,7 +121,7 @@ void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) { // Caching /////////////////////////////////////////////////////////////////////////////// -Texture* TextureCache::get(SkBitmap* bitmap) { +Texture* TextureCache::get(const SkBitmap* bitmap) { Texture* texture = mCache.get(bitmap); if (!texture) { @@ -161,7 +161,7 @@ Texture* TextureCache::get(SkBitmap* bitmap) { return texture; } -Texture* TextureCache::getTransient(SkBitmap* bitmap) { +Texture* TextureCache::getTransient(const SkBitmap* bitmap) { Texture* texture = new Texture(); texture->bitmapSize = bitmap->rowBytes() * bitmap->height(); texture->cleanup = true; @@ -171,11 +171,11 @@ Texture* TextureCache::getTransient(SkBitmap* bitmap) { return texture; } -void TextureCache::remove(SkBitmap* bitmap) { +void TextureCache::remove(const SkBitmap* bitmap) { mCache.remove(bitmap); } -void TextureCache::removeDeferred(SkBitmap* bitmap) { +void TextureCache::removeDeferred(const SkBitmap* bitmap) { Mutex::Autolock _l(mLock); mGarbage.push(bitmap); } @@ -209,7 +209,7 @@ void TextureCache::flush() { } } -void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate) { +void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate) { SkAutoLockPixels alp(*bitmap); if (!bitmap->readyToDraw()) { @@ -237,7 +237,7 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege Caches::getInstance().bindTexture(texture->id); - switch (bitmap->getConfig()) { + switch (bitmap->config()) { case SkBitmap::kA8_Config: glPixelStorei(GL_UNPACK_ALIGNMENT, 1); uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), @@ -265,7 +265,7 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege texture->blend = !bitmap->isOpaque(); break; default: - ALOGW("Unsupported bitmap config: %d", bitmap->getConfig()); + ALOGW("Unsupported bitmap config: %d", bitmap->config()); break; } @@ -282,13 +282,12 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege } } -void TextureCache::uploadLoFiTexture(bool resize, SkBitmap* bitmap, +void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap, uint32_t width, uint32_t height) { SkBitmap rgbaBitmap; - rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height); + rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0, bitmap->alphaType()); rgbaBitmap.allocPixels(); rgbaBitmap.eraseColor(0); - rgbaBitmap.setIsOpaque(bitmap->isOpaque()); SkCanvas canvas(rgbaBitmap); canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL); diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index 57fc19a..e33c60d 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -49,7 +49,7 @@ namespace uirenderer { * Any texture added to the cache causing the cache to grow beyond the maximum * allowed size will also cause the oldest texture to be kicked out. */ -class TextureCache: public OnEntryRemoved<SkBitmap*, Texture*> { +class TextureCache: public OnEntryRemoved<const SkBitmap*, Texture*> { public: TextureCache(); TextureCache(uint32_t maxByteSize); @@ -59,28 +59,28 @@ public: * Used as a callback when an entry is removed from the cache. * Do not invoke directly. */ - void operator()(SkBitmap*& bitmap, Texture*& texture); + void operator()(const SkBitmap*& bitmap, Texture*& texture); /** * Returns the texture associated with the specified bitmap. If the texture * cannot be found in the cache, a new texture is generated. */ - Texture* get(SkBitmap* bitmap); + Texture* get(const SkBitmap* bitmap); /** * Returns the texture associated with the specified bitmap. The generated * texture is not kept in the cache. The caller must destroy the texture. */ - Texture* getTransient(SkBitmap* bitmap); + Texture* getTransient(const SkBitmap* bitmap); /** * Removes the texture associated with the specified bitmap. * Upon remove the texture is freed. */ - void remove(SkBitmap* bitmap); + void remove(const SkBitmap* bitmap); /** * Removes the texture associated with the specified bitmap. This is meant * to be called from threads that are not the EGL context thread. */ - void removeDeferred(SkBitmap* bitmap); + void removeDeferred(const SkBitmap* bitmap); /** * Process deferred removals. */ @@ -122,15 +122,15 @@ private: * @param regenerate If true, the bitmap data is reuploaded into the texture, but * no new texture is generated. */ - void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false); + void generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate = false); - void uploadLoFiTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height); + void uploadLoFiTexture(bool resize, const SkBitmap* bitmap, uint32_t width, uint32_t height); void uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei width, GLsizei height, GLenum type, const GLvoid * data); void init(); - LruCache<SkBitmap*, Texture*> mCache; + LruCache<const SkBitmap*, Texture*> mCache; uint32_t mSize; uint32_t mMaxSize; @@ -140,7 +140,7 @@ private: bool mDebugEnabled; - Vector<SkBitmap*> mGarbage; + Vector<const SkBitmap*> mGarbage; mutable Mutex mLock; }; // class TextureCache diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h index 497924e..15b9d6b 100644 --- a/libs/hwui/Vector.h +++ b/libs/hwui/Vector.h @@ -36,6 +36,10 @@ struct Vector2 { x(px), y(py) { } + float lengthSquared() const { + return x * x + y * y; + } + float length() const { return sqrt(x * x + y * y); } @@ -107,6 +111,21 @@ struct Vector2 { } }; // class Vector2 +class Vector3 { +public: + float x; + float y; + float z; + + Vector3() : + x(0.0f), y(0.0f), z(0.0f) { + } + + Vector3(float px, float py, float pz) : + x(px), y(py), z(pz) { + } +}; + /////////////////////////////////////////////////////////////////////////////// // Types /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h index 790d4fc..5d7a199 100644 --- a/libs/hwui/Vertex.h +++ b/libs/hwui/Vertex.h @@ -33,13 +33,14 @@ struct Vertex { * Program::set()), and used to make geometry damage rect calculation conservative (see * Rect::snapGeometryToPixelBoundaries()) */ - static const float gGeometryFudgeFactor = 0.0656f; + static float GeometryFudgeFactor() { return 0.0656f; } - float position[2]; + + float x, y; static inline void set(Vertex* vertex, float x, float y) { - vertex[0].position[0] = x; - vertex[0].position[1] = y; + vertex[0].x = x; + vertex[0].y = y; } static inline void set(Vertex* vertex, vec2 val) { @@ -47,7 +48,7 @@ struct Vertex { } static inline void copyWithOffset(Vertex* vertex, const Vertex& src, float x, float y) { - set(vertex, src.position[0] + x, src.position[1] + y); + set(vertex, src.x + x, src.y + y); } }; // struct Vertex @@ -56,19 +57,19 @@ struct Vertex { * Simple structure to describe a vertex with a position and texture UV. */ struct TextureVertex { - float position[2]; - float texture[2]; + float x, y; + float u, v; static inline void set(TextureVertex* vertex, float x, float y, float u, float v) { - vertex[0].position[0] = x; - vertex[0].position[1] = y; - vertex[0].texture[0] = u; - vertex[0].texture[1] = v; + vertex[0].x = x; + vertex[0].y = y; + vertex[0].u = u; + vertex[0].v = v; } static inline void setUV(TextureVertex* vertex, float u, float v) { - vertex[0].texture[0] = u; - vertex[0].texture[1] = v; + vertex[0].u = u; + vertex[0].v = v; } }; // struct TextureVertex @@ -76,17 +77,17 @@ struct TextureVertex { * Simple structure to describe a vertex with a position, texture UV and ARGB color. */ struct ColorTextureVertex : TextureVertex { - float color[4]; + float r, g, b, a; static inline void set(ColorTextureVertex* vertex, float x, float y, float u, float v, int color) { TextureVertex::set(vertex, x, y, u, v); const float a = ((color >> 24) & 0xff) / 255.0f; - vertex[0].color[0] = a * ((color >> 16) & 0xff) / 255.0f; - vertex[0].color[1] = a * ((color >> 8) & 0xff) / 255.0f; - vertex[0].color[2] = a * ((color ) & 0xff) / 255.0f; - vertex[0].color[3] = a; + vertex[0].r = a * ((color >> 16) & 0xff) / 255.0f; + vertex[0].g = a * ((color >> 8) & 0xff) / 255.0f; + vertex[0].b = a * ((color ) & 0xff) / 255.0f; + vertex[0].a = a; } }; // struct ColorTextureVertex @@ -103,7 +104,7 @@ struct AlphaVertex : Vertex { 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::set(vertex, src.x + x, src.y + y); vertex[0].alpha = src.alpha; } diff --git a/libs/hwui/VertexBuffer.h b/libs/hwui/VertexBuffer.h new file mode 100644 index 0000000..8b6872e --- /dev/null +++ b/libs/hwui/VertexBuffer.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2012 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_VERTEX_BUFFER_H +#define ANDROID_HWUI_VERTEX_BUFFER_H + + +namespace android { +namespace uirenderer { + +class VertexBuffer { +public: + VertexBuffer(): + mBuffer(0), + mVertexCount(0), + mCleanupMethod(NULL) + {} + + ~VertexBuffer() { + if (mCleanupMethod) mCleanupMethod(mBuffer); + } + + /** + This should be the only method used by the Tessellator. Subsequent calls to + alloc will allocate space within the first allocation (useful if you want to + eventually allocate multiple regions within a single VertexBuffer, such as + with PathTessellator::tesselateLines()) + */ + template <class TYPE> + 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 + vertexCount; + return reallocBuffer; + } + mVertexCount = vertexCount; + mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount]; + mCleanupMethod = &(cleanup<TYPE>); + + return (TYPE*)mBuffer; + } + + 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); + } + } + + const void* getBuffer() const { return mBuffer; } + unsigned int getVertexCount() const { return mVertexCount; } + + template <class TYPE> + void createDegenerateSeparators(int allocSize) { + 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)); + } + } + +private: + template <class TYPE> + static void cleanup(void* buffer) { + delete[] (TYPE*)buffer; + } + + void* mBuffer; + unsigned int mVertexCount; + + void* mReallocBuffer; // used for multi-allocation + + void (*mCleanupMethod)(void*); +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_VERTEX_BUFFER_H diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp index d5f38b5..24ffb80 100644 --- a/libs/hwui/font/CacheTexture.cpp +++ b/libs/hwui/font/CacheTexture.cpp @@ -114,7 +114,7 @@ CacheTexture::CacheTexture(uint16_t width, uint16_t height, GLenum format, uint3 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); + mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE); // OpenGL ES 3.0+ lets us specify the row length for unpack operations such // as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture. @@ -143,7 +143,7 @@ void CacheTexture::init() { // reset, then create a new remainder space to start again reset(); mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE, - mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true); + mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE); } void CacheTexture::releaseMesh() { diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h index 61b38f8..4cc4f22 100644 --- a/libs/hwui/font/CacheTexture.h +++ b/libs/hwui/font/CacheTexture.h @@ -54,7 +54,7 @@ struct CacheBlock { CacheBlock* mNext; CacheBlock* mPrev; - CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height, bool empty = false): + CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height): mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL) { } diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp index 18983d8..b0945c6 100644 --- a/libs/hwui/font/Font.cpp +++ b/libs/hwui/font/Font.cpp @@ -23,6 +23,7 @@ #include <utils/Trace.h> #include <SkGlyph.h> +#include <SkGlyphCache.h> #include <SkUtils.h> #include "FontUtil.h" @@ -266,14 +267,14 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float glyph->mCacheTexture); } -CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) { +CachedGlyphInfo* Font::getCachedGlyph(const SkPaint* paint, glyph_t textUnit, bool precaching) { CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueFor(textUnit); if (cachedGlyph) { // Is the glyph still in texture cache? if (!cachedGlyph->mIsValid) { - const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit, - &mDescription.mLookupTransform); - updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching); + SkAutoGlyphCache autoCache(*paint, NULL, &mDescription.mLookupTransform); + const SkGlyph& skiaGlyph = GET_METRICS(autoCache.getCache(), textUnit); + updateGlyphCache(paint, skiaGlyph, autoCache.getCache(), cachedGlyph, precaching); } } else { cachedGlyph = cacheGlyph(paint, textUnit, precaching); @@ -282,14 +283,14 @@ CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool pre return cachedGlyph; } -void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, +void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, const float* positions) { render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL, positions); } -void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, - int numGlyphs, SkPath* path, float hOffset, float vOffset) { +void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len, + int numGlyphs, const SkPath* path, float hOffset, float vOffset) { if (numGlyphs == 0 || text == NULL || len == 0) { return; } @@ -338,7 +339,7 @@ void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len } } -void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len, +void Font::measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, Rect *bounds, const float* positions) { if (bounds == NULL) { ALOGE("No return rectangle provided to measure text"); @@ -348,7 +349,7 @@ void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t le render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions); } -void Font::precache(SkPaint* paint, const char* text, int numGlyphs) { +void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) { ATRACE_NAME("precacheText"); if (numGlyphs == 0 || text == NULL) { @@ -369,7 +370,7 @@ void Font::precache(SkPaint* paint, const char* text, int numGlyphs) { } } -void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, +void Font::render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) { if (numGlyphs == 0 || text == NULL || len == 0) { @@ -415,8 +416,8 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len } } -void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph, - bool precaching) { +void Font::updateGlyphCache(const SkPaint* paint, const SkGlyph& skiaGlyph, + SkGlyphCache* skiaGlyphCache, CachedGlyphInfo* glyph, bool precaching) { glyph->mAdvanceX = skiaGlyph.fAdvanceX; glyph->mAdvanceY = skiaGlyph.fAdvanceY; glyph->mBitmapLeft = skiaGlyph.fLeft; @@ -429,7 +430,7 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp // Get the bitmap for the glyph if (!skiaGlyph.fImage) { - paint->findImage(skiaGlyph, &mDescription.mLookupTransform); + skiaGlyphCache->findImage(skiaGlyph); } mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching); @@ -459,15 +460,16 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp } } -CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) { +CachedGlyphInfo* Font::cacheGlyph(const SkPaint* paint, glyph_t glyph, bool precaching) { CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); mCachedGlyphs.add(glyph, newGlyph); - const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph, &mDescription.mLookupTransform); + SkAutoGlyphCache autoCache(*paint, NULL, &mDescription.mLookupTransform); + const SkGlyph& skiaGlyph = GET_METRICS(autoCache.getCache(), glyph); newGlyph->mIsValid = false; newGlyph->mGlyphIndex = skiaGlyph.fID; - updateGlyphCache(paint, skiaGlyph, newGlyph, precaching); + updateGlyphCache(paint, skiaGlyph, autoCache.getCache(), newGlyph, precaching); return newGlyph; } diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h index 9e7ec2d..02197bc 100644 --- a/libs/hwui/font/Font.h +++ b/libs/hwui/font/Font.h @@ -19,6 +19,7 @@ #include <utils/KeyedVector.h> +#include <SkGlyphCache.h> #include <SkScalerContext.h> #include <SkPaint.h> #include <SkPathMeasure.h> @@ -76,11 +77,11 @@ public: ~Font(); - void render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, + void render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, const float* positions); - void render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, - int numGlyphs, SkPath* path, float hOffset, float vOffset); + void render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, + int numGlyphs, const SkPath* path, float hOffset, float vOffset); const Font::FontDescription& getDescription() const { return mDescription; @@ -105,20 +106,20 @@ private: MEASURE, }; - void precache(SkPaint* paint, const char* text, int numGlyphs); + void precache(const SkPaint* paint, const char* text, int numGlyphs); - void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, + void render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions); - void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len, + void measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, Rect *bounds, const float* positions); void invalidateTextureCache(CacheTexture* cacheTexture = NULL); - CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching); - void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph, - bool precaching); + CachedGlyphInfo* cacheGlyph(const SkPaint* paint, glyph_t glyph, bool precaching); + void updateGlyphCache(const SkPaint* paint, const SkGlyph& skiaGlyph, + SkGlyphCache* skiaGlyphCache, CachedGlyphInfo* glyph, bool precaching); void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, @@ -135,7 +136,8 @@ private: void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset, SkPathMeasure& measure, SkPoint* position, SkVector* tangent); - CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false); + CachedGlyphInfo* getCachedGlyph(const SkPaint* paint, glyph_t textUnit, + bool precaching = false); FontRenderer* mState; FontDescription mDescription; diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h index cdcb23c..c2fd5f5 100644 --- a/libs/hwui/font/FontUtil.h +++ b/libs/hwui/font/FontUtil.h @@ -40,7 +40,7 @@ #if RENDER_TEXT_AS_GLYPHS typedef uint16_t glyph_t; #define TO_GLYPH(g) g - #define GET_METRICS(paint, glyph, matrix) paint->getGlyphMetrics(glyph, matrix) + #define GET_METRICS(cache, glyph) cache->getGlyphIDMetrics(glyph) #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text) #define IS_END_OF_STRING(glyph) false @@ -53,7 +53,7 @@ #else typedef SkUnichar glyph_t; #define TO_GLYPH(g) ((SkUnichar) g) - #define GET_METRICS(paint, glyph, matrix) paint->getUnicharMetrics(glyph, matrix) + #define GET_METRICS(cache, glyph) cache->getUnicharMetrics(glyph) #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text) #define IS_END_OF_STRING(glyph) glyph < 0 #endif diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp new file mode 100644 index 0000000..ffb8a32 --- /dev/null +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -0,0 +1,326 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "CanvasContext" + +#include "CanvasContext.h" + +#include <cutils/properties.h> +#include <strings.h> + +#include "../Caches.h" +#include "../Stencil.h" + +#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions" +#define GLES_VERSION 2 + +namespace android { +namespace uirenderer { +namespace renderthread { + +#define ERROR_CASE(x) case x: return #x; +static const char* egl_error_str(EGLint error) { + switch (error) { + ERROR_CASE(EGL_SUCCESS) + ERROR_CASE(EGL_NOT_INITIALIZED) + ERROR_CASE(EGL_BAD_ACCESS) + ERROR_CASE(EGL_BAD_ALLOC) + ERROR_CASE(EGL_BAD_ATTRIBUTE) + ERROR_CASE(EGL_BAD_CONFIG) + ERROR_CASE(EGL_BAD_CONTEXT) + ERROR_CASE(EGL_BAD_CURRENT_SURFACE) + ERROR_CASE(EGL_BAD_DISPLAY) + ERROR_CASE(EGL_BAD_MATCH) + ERROR_CASE(EGL_BAD_NATIVE_PIXMAP) + ERROR_CASE(EGL_BAD_NATIVE_WINDOW) + ERROR_CASE(EGL_BAD_PARAMETER) + ERROR_CASE(EGL_BAD_SURFACE) + ERROR_CASE(EGL_CONTEXT_LOST) + default: + return "Unknown error"; + } +} +static const char* egl_error_str() { + return egl_error_str(eglGetError()); +} + +static bool load_dirty_regions_property() { + char buf[PROPERTY_VALUE_MAX]; + int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true"); + return !strncasecmp("true", buf, len); +} + +// This class contains the shared global EGL objects, such as EGLDisplay +// and EGLConfig, which are re-used by CanvasContext +class GlobalContext { +public: + static GlobalContext* get(); + + // Returns true if EGL was initialized, + // false if it was already initialized + bool initialize(); + + bool usePBufferSurface(); + EGLSurface createSurface(EGLNativeWindowType window); + void destroySurface(EGLSurface surface); + + void destroy(); + + bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; } + bool makeCurrent(EGLSurface surface); + bool swapBuffers(EGLSurface surface); + + bool enableDirtyRegions(EGLSurface surface); + +private: + GlobalContext(); + // GlobalContext is never destroyed, method is purposely not implemented + ~GlobalContext(); + + bool loadConfig(); + bool createContext(); + + static GlobalContext* sContext; + + EGLDisplay mEglDisplay; + EGLConfig mEglConfig; + EGLContext mEglContext; + EGLSurface mPBufferSurface; + + const bool mRequestDirtyRegions; + bool mCanSetDirtyRegions; + + EGLSurface mCurrentSurface; +}; + +GlobalContext* GlobalContext::sContext = 0; + +GlobalContext* GlobalContext::get() { + if (!sContext) { + sContext = new GlobalContext(); + } + return sContext; +} + +GlobalContext::GlobalContext() + : mEglDisplay(EGL_NO_DISPLAY) + , mEglConfig(0) + , mEglContext(EGL_NO_CONTEXT) + , mPBufferSurface(EGL_NO_SURFACE) + , mRequestDirtyRegions(load_dirty_regions_property()) + , mCurrentSurface(EGL_NO_SURFACE) { + mCanSetDirtyRegions = mRequestDirtyRegions; + ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false"); +} + +bool GlobalContext::initialize() { + if (mEglDisplay != EGL_NO_DISPLAY) return false; + + mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (mEglDisplay == EGL_NO_DISPLAY) { + ALOGE("Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str()); + return false; + } + + EGLint major, minor; + if (eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE) { + ALOGE("Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str()); + return false; + } + ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor); + + if (!loadConfig()) { + return false; + } + if (!createContext()) { + return false; + } + + return true; +} + +bool GlobalContext::loadConfig() { + EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; + EGLint attribs[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 0, + EGL_CONFIG_CAVEAT, EGL_NONE, + EGL_STENCIL_SIZE, Stencil::getStencilSize(), + EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, + EGL_NONE + }; + + EGLint num_configs = 1; + if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs) + || num_configs != 1) { + // Failed to get a valid config + if (mCanSetDirtyRegions) { + ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); + // Try again without dirty regions enabled + mCanSetDirtyRegions = false; + loadConfig(); + } else { + ALOGE("Failed to choose config, error = %s", egl_error_str()); + return false; + } + } + return true; +} + +bool GlobalContext::createContext() { + EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE }; + mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs); + if (mEglContext == EGL_NO_CONTEXT) { + ALOGE("Failed to create context, error = %s", egl_error_str()); + return false; + } + return true; +} + +bool GlobalContext::usePBufferSurface() { + if (mEglDisplay == EGL_NO_DISPLAY) return false; + + if (mPBufferSurface == EGL_NO_SURFACE) { + EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; + mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs); + } + return makeCurrent(mPBufferSurface); +} + +EGLSurface GlobalContext::createSurface(EGLNativeWindowType window) { + initialize(); + return eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL); +} + +void GlobalContext::destroySurface(EGLSurface surface) { + if (isCurrent(surface)) { + makeCurrent(EGL_NO_SURFACE); + } + if (!eglDestroySurface(mEglDisplay, surface)) { + ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str()); + } +} + +void GlobalContext::destroy() { + if (mEglDisplay == EGL_NO_DISPLAY) return; + + usePBufferSurface(); + if (Caches::hasInstance()) { + Caches::getInstance().terminate(); + } + + eglDestroyContext(mEglDisplay, mEglContext); + eglDestroySurface(mEglDisplay, mPBufferSurface); + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(mEglDisplay); + eglReleaseThread(); + + mEglDisplay = EGL_NO_DISPLAY; + mEglContext = EGL_NO_CONTEXT; + mPBufferSurface = EGL_NO_SURFACE; + mCurrentSurface = EGL_NO_SURFACE; +} + +bool GlobalContext::makeCurrent(EGLSurface surface) { + if (isCurrent(surface)) return true; + + if (surface == EGL_NO_SURFACE) { + // If we are setting EGL_NO_SURFACE we don't care about any of the potential + // return errors, which would only happen if mEglDisplay had already been + // destroyed in which case the current context is already NO_CONTEXT + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) { + ALOGE("Failed to make current on surface %p, error=%s", (void*)surface, egl_error_str()); + return false; + } + mCurrentSurface = surface; + return true; +} + +bool GlobalContext::swapBuffers(EGLSurface surface) { + if (!eglSwapBuffers(mEglDisplay, surface)) { + ALOGW("eglSwapBuffers failed on surface %p, error=%s", (void*)surface, egl_error_str()); + return false; + } + return true; +} + +bool GlobalContext::enableDirtyRegions(EGLSurface surface) { + if (!mRequestDirtyRegions) return false; + + if (mCanSetDirtyRegions) { + if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) { + ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s", + (void*) surface, egl_error_str()); + return false; + } + return true; + } + // Perhaps it is already enabled? + EGLint value; + if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) { + ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p", + (void*) surface, egl_error_str()); + return false; + } + return value == EGL_BUFFER_PRESERVED; +} + +CanvasContext::CanvasContext() + : mEglSurface(EGL_NO_SURFACE) + , mDirtyRegionsEnabled(false) { + mGlobalContext = GlobalContext::get(); +} + +CanvasContext::~CanvasContext() { + setSurface(NULL); +} + +bool CanvasContext::setSurface(EGLNativeWindowType window) { + if (mEglSurface != EGL_NO_SURFACE) { + mGlobalContext->destroySurface(mEglSurface); + mEglSurface = EGL_NO_SURFACE; + } + + if (window) { + mEglSurface = mGlobalContext->createSurface(window); + } + + if (mEglSurface != EGL_NO_SURFACE) { + mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface); + } + return !window || mEglSurface != EGL_NO_SURFACE; +} + +bool CanvasContext::swapBuffers() { + return mGlobalContext->swapBuffers(mEglSurface); +} + +bool CanvasContext::makeCurrent() { + return mGlobalContext->makeCurrent(mEglSurface); +} + +bool CanvasContext::useGlobalPBufferSurface() { + return GlobalContext::get()->usePBufferSurface(); +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h new file mode 100644 index 0000000..77ae737 --- /dev/null +++ b/libs/hwui/renderthread/CanvasContext.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CANVASCONTEXT_H_ +#define CANVASCONTEXT_H_ + +#include <cutils/compiler.h> +#include <EGL/egl.h> + +namespace android { +namespace uirenderer { +namespace renderthread { + +class GlobalContext; + +// This per-renderer class manages the bridge between the global EGL context +// and the render surface. +class CanvasContext { +public: + ANDROID_API CanvasContext(); + ANDROID_API ~CanvasContext(); + + ANDROID_API bool setSurface(EGLNativeWindowType window); + ANDROID_API bool swapBuffers(); + ANDROID_API bool makeCurrent(); + + ANDROID_API static bool useGlobalPBufferSurface(); + +private: + + GlobalContext* mGlobalContext; + EGLSurface mEglSurface; + bool mDirtyRegionsEnabled; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ +#endif /* CANVASCONTEXT_H_ */ diff --git a/libs/hwui/renderthread/RenderTask.cpp b/libs/hwui/renderthread/RenderTask.cpp new file mode 100644 index 0000000..2da91c5 --- /dev/null +++ b/libs/hwui/renderthread/RenderTask.cpp @@ -0,0 +1,35 @@ +/* + * 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 "RenderTask" + +#include "RenderTask.h" + +#include <utils/Log.h> + +namespace android { +namespace uirenderer { +namespace renderthread { + +RenderTask::RenderTask() : mNext(0) { +} + +RenderTask::~RenderTask() { +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/RenderTask.h b/libs/hwui/renderthread/RenderTask.h new file mode 100644 index 0000000..865b1e6 --- /dev/null +++ b/libs/hwui/renderthread/RenderTask.h @@ -0,0 +1,39 @@ +/* + * 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 RENDERTASK_H_ +#define RENDERTASK_H_ + +#include <cutils/compiler.h> + +namespace android { +namespace uirenderer { +namespace renderthread { + +class ANDROID_API RenderTask { +public: + ANDROID_API RenderTask(); + ANDROID_API virtual ~RenderTask(); + + ANDROID_API virtual void run() = 0; + + RenderTask* mNext; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ +#endif /* RENDERTASK_H_ */ diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp new file mode 100644 index 0000000..bccd6e6 --- /dev/null +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -0,0 +1,86 @@ +/* + * 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 "RenderThread" + +#include "RenderThread.h" + +#include <utils/Log.h> + +namespace android { +using namespace uirenderer::renderthread; +ANDROID_SINGLETON_STATIC_INSTANCE(RenderThread); + +namespace uirenderer { +namespace renderthread { + +RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>() + , mQueueHead(0), mQueueTail(0) { + mLooper = new Looper(false); + run("RenderThread"); +} + +RenderThread::~RenderThread() { +} + +bool RenderThread::threadLoop() { + for (;;) { + int result = mLooper->pollAll(-1); + if (result == Looper::POLL_ERROR) { + // TODO Something? + break; + } + // Process our queue, if we have anything + while (RenderTask* task = nextTask()) { + task->run(); + delete task; + } + } + + return false; +} + +void RenderThread::queue(RenderTask* task) { + AutoMutex _lock(mLock); + if (mQueueTail) { + mQueueTail->mNext = task; + } else { + mQueueHead = task; + } + mQueueTail = task; + if (mQueueHead == task) { + // Only wake if this is the first task + mLooper->wake(); + } +} + +RenderTask* RenderThread::nextTask() { + AutoMutex _lock(mLock); + RenderTask* ret = mQueueHead; + if (ret) { + if (mQueueTail == mQueueHead) { + mQueueTail = mQueueHead = 0; + } else { + mQueueHead = ret->mNext; + } + ret->mNext = 0; + } + return ret; +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h new file mode 100644 index 0000000..4edd575 --- /dev/null +++ b/libs/hwui/renderthread/RenderThread.h @@ -0,0 +1,58 @@ +/* + * 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 RENDERTHREAD_H_ +#define RENDERTHREAD_H_ + +#include "RenderTask.h" +#include <cutils/compiler.h> +#include <utils/Looper.h> +#include <utils/Mutex.h> +#include <utils/Singleton.h> +#include <utils/Thread.h> + +namespace android { +namespace uirenderer { +namespace renderthread { + +class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> { +public: + // RenderThread takes complete ownership of tasks that are queued + // and will delete them after they are run + ANDROID_API void queue(RenderTask* task); + +protected: + virtual bool threadLoop(); + +private: + friend class Singleton<RenderThread>; + + RenderThread(); + virtual ~RenderThread(); + + RenderTask* nextTask(); + + sp<Looper> mLooper; + Mutex mLock; + + RenderTask* mQueueHead; + RenderTask* mQueueTail; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ +#endif /* RENDERTHREAD_H_ */ diff --git a/libs/input/Android.mk b/libs/input/Android.mk index 6e944ef..eb2bebe 100644 --- a/libs/input/Android.mk +++ b/libs/input/Android.mk @@ -42,6 +42,8 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_C_INCLUDES := \ external/skia/include/core +LOCAL_CFLAGS += -Wno-unused-parameter + LOCAL_MODULE:= libinputservice LOCAL_MODULE_TAGS := optional diff --git a/libs/input/EventHub.cpp b/libs/input/EventHub.cpp index fc64656..e2efd17 100644 --- a/libs/input/EventHub.cpp +++ b/libs/input/EventHub.cpp @@ -49,6 +49,7 @@ #include <sys/ioctl.h> #include <sys/limits.h> #include <sys/sha1.h> +#include <sys/utsname.h> /* this macro is used to tell if "bit" is set in "array" * it selects a byte from the array, and does a boolean AND @@ -93,32 +94,12 @@ static String8 sha1(const String8& in) { return out; } -static void setDescriptor(InputDeviceIdentifier& identifier) { - // Compute a device descriptor that uniquely identifies the device. - // The descriptor is assumed to be a stable identifier. Its value should not - // change between reboots, reconnections, firmware updates or new releases of Android. - // Ideally, we also want the descriptor to be short and relatively opaque. - String8 rawDescriptor; - rawDescriptor.appendFormat(":%04x:%04x:", identifier.vendor, identifier.product); - if (!identifier.uniqueId.isEmpty()) { - rawDescriptor.append("uniqueId:"); - rawDescriptor.append(identifier.uniqueId); - } if (identifier.vendor == 0 && identifier.product == 0) { - // If we don't know the vendor and product id, then the device is probably - // built-in so we need to rely on other information to uniquely identify - // the input device. Usually we try to avoid relying on the device name or - // location but for built-in input device, they are unlikely to ever change. - if (!identifier.name.isEmpty()) { - rawDescriptor.append("name:"); - rawDescriptor.append(identifier.name); - } else if (!identifier.location.isEmpty()) { - rawDescriptor.append("location:"); - rawDescriptor.append(identifier.location); - } +static void getLinuxRelease(int* major, int* minor) { + struct utsname info; + if (uname(&info) || sscanf(info.release, "%d.%d", major, minor) <= 0) { + *major = 0, *minor = 0; + ALOGE("Could not get linux version: %s", strerror(errno)); } - identifier.descriptor = sha1(rawDescriptor); - ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.string(), - identifier.descriptor.string()); } // --- Global Functions --- @@ -236,6 +217,11 @@ EventHub::EventHub(void) : result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", errno); + + int major, minor; + getLinuxRelease(&major, &minor); + // EPOLLWAKEUP was introduced in kernel 3.5 + mUsingEpollWakeup = major > 3 || (major == 3 && minor >= 5); } EventHub::~EventHub(void) { @@ -509,8 +495,9 @@ bool EventHub::hasScanCode(int32_t deviceId, int32_t scanCode) const { bool EventHub::hasLed(int32_t deviceId, int32_t led) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && led >= 0 && led <= LED_MAX) { - if (test_bit(led, device->ledBitmask)) { + int32_t sc; + if (device && mapLed(device, led, &sc) == NO_ERROR) { + if (test_bit(sc, device->ledBitmask)) { return true; } } @@ -520,12 +507,17 @@ bool EventHub::hasLed(int32_t deviceId, int32_t led) const { void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device && !device->isVirtual() && led >= 0 && led <= LED_MAX) { + setLedStateLocked(device, led, on); +} + +void EventHub::setLedStateLocked(Device* device, int32_t led, bool on) { + int32_t sc; + if (device && !device->isVirtual() && mapLed(device, led, &sc) != NAME_NOT_FOUND) { struct input_event ev; ev.time.tv_sec = 0; ev.time.tv_usec = 0; ev.type = EV_LED; - ev.code = led; + ev.code = sc; ev.value = on ? 1 : 0; ssize_t nWrite; @@ -570,6 +562,57 @@ bool EventHub::setKeyboardLayoutOverlay(int32_t deviceId, return false; } +static String8 generateDescriptor(InputDeviceIdentifier& identifier) { + String8 rawDescriptor; + rawDescriptor.appendFormat(":%04x:%04x:", identifier.vendor, + identifier.product); + // TODO add handling for USB devices to not uniqueify kbs that show up twice + if (!identifier.uniqueId.isEmpty()) { + rawDescriptor.append("uniqueId:"); + rawDescriptor.append(identifier.uniqueId); + } else if (identifier.nonce != 0) { + rawDescriptor.appendFormat("nonce:%04x", identifier.nonce); + } + + if (identifier.vendor == 0 && identifier.product == 0) { + // If we don't know the vendor and product id, then the device is probably + // built-in so we need to rely on other information to uniquely identify + // the input device. Usually we try to avoid relying on the device name or + // location but for built-in input device, they are unlikely to ever change. + if (!identifier.name.isEmpty()) { + rawDescriptor.append("name:"); + rawDescriptor.append(identifier.name); + } else if (!identifier.location.isEmpty()) { + rawDescriptor.append("location:"); + rawDescriptor.append(identifier.location); + } + } + identifier.descriptor = sha1(rawDescriptor); + return rawDescriptor; +} + +void EventHub::assignDescriptorLocked(InputDeviceIdentifier& identifier) { + // Compute a device descriptor that uniquely identifies the device. + // The descriptor is assumed to be a stable identifier. Its value should not + // change between reboots, reconnections, firmware updates or new releases + // of Android. In practice we sometimes get devices that cannot be uniquely + // identified. In this case we enforce uniqueness between connected devices. + // Ideally, we also want the descriptor to be short and relatively opaque. + + identifier.nonce = 0; + String8 rawDescriptor = generateDescriptor(identifier); + if (identifier.uniqueId.isEmpty()) { + // If it didn't have a unique id check for conflicts and enforce + // uniqueness if necessary. + while(getDeviceByDescriptorLocked(identifier.descriptor) != NULL) { + identifier.nonce++; + rawDescriptor = generateDescriptor(identifier); + } + } + ALOGV("Created descriptor: raw=%s, cooked=%s", rawDescriptor.string(), + identifier.descriptor.string()); +} + void EventHub::vibrate(int32_t deviceId, nsecs_t duration) { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); @@ -626,6 +669,17 @@ void EventHub::cancelVibrate(int32_t deviceId) { } } +EventHub::Device* EventHub::getDeviceByDescriptorLocked(String8& descriptor) const { + size_t size = mDevices.size(); + for (size_t i = 0; i < size; i++) { + Device* device = mDevices.valueAt(i); + if (descriptor.compare(device->identifier.descriptor) == 0) { + return device; + } + } + return NULL; +} + EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const { if (deviceId == BUILT_IN_KEYBOARD_ID) { deviceId = mBuiltInKeyboardId; @@ -1065,7 +1119,7 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { } // Fill in the descriptor. - setDescriptor(identifier); + assignDescriptorLocked(identifier); // Make file descriptor non-blocking for use with poll(). if (fcntl(fd, F_SETFL, O_NONBLOCK)) { @@ -1239,12 +1293,13 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { if (device->classes & (INPUT_DEVICE_CLASS_JOYSTICK | INPUT_DEVICE_CLASS_GAMEPAD)) { device->controllerNumber = getNextControllerNumberLocked(device); + setLedForController(device); } // Register with epoll. struct epoll_event eventItem; memset(&eventItem, 0, sizeof(eventItem)); - eventItem.events = EPOLLIN; + eventItem.events = mUsingEpollWakeup ? EPOLLIN : EPOLLIN | EPOLLWAKEUP; eventItem.data.u32 = deviceId; if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) { ALOGE("Could not add device fd to epoll instance. errno=%d", errno); @@ -1252,15 +1307,20 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { return -1; } - // Enable wake-lock behavior on kernels that support it. - // TODO: Only need this for devices that can really wake the system. + String8 wakeMechanism("EPOLLWAKEUP"); + if (!mUsingEpollWakeup) { #ifndef EVIOCSSUSPENDBLOCK - // uapi headers don't include EVIOCSSUSPENDBLOCK, and future kernels - // will use an epoll flag instead, so as long as we want to support - // this feature, we need to be prepared to define the ioctl ourselves. + // uapi headers don't include EVIOCSSUSPENDBLOCK, and future kernels + // will use an epoll flag instead, so as long as we want to support + // this feature, we need to be prepared to define the ioctl ourselves. #define EVIOCSSUSPENDBLOCK _IOW('E', 0x91, int) #endif - bool usingSuspendBlockIoctl = !ioctl(fd, EVIOCSSUSPENDBLOCK, 1); + if (ioctl(fd, EVIOCSSUSPENDBLOCK, 1)) { + wakeMechanism = "<none>"; + } else { + wakeMechanism = "EVIOCSSUSPENDBLOCK"; + } + } // Tell the kernel that we want to use the monotonic clock for reporting timestamps // associated with input events. This is important because the input system @@ -1282,14 +1342,14 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { ALOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, " "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s, " - "usingSuspendBlockIoctl=%s, usingClockIoctl=%s", + "wakeMechanism=%s, usingClockIoctl=%s", deviceId, fd, devicePath, device->identifier.name.string(), device->classes, device->configurationFile.string(), device->keyMap.keyLayoutFile.string(), device->keyMap.keyCharacterMapFile.string(), toString(mBuiltInKeyboardId == deviceId), - toString(usingSuspendBlockIoctl), toString(usingClockIoctl)); + wakeMechanism.string(), toString(usingClockIoctl)); addDeviceLocked(device); return 0; @@ -1299,7 +1359,7 @@ void EventHub::createVirtualKeyboardLocked() { InputDeviceIdentifier identifier; identifier.name = "Virtual"; identifier.uniqueId = "<virtual>"; - setDescriptor(identifier); + assignDescriptorLocked(identifier); Device* device = new Device(-1, VIRTUAL_KEYBOARD_ID, String8("<virtual>"), identifier); device->classes = INPUT_DEVICE_CLASS_KEYBOARD @@ -1378,6 +1438,11 @@ void EventHub::releaseControllerNumberLocked(Device* device) { mControllerNumbers.clearBit(static_cast<uint32_t>(num - 1)); } +void EventHub::setLedForController(Device* device) { + for (int i = 0; i < MAX_CONTROLLER_LEDS; i++) { + setLedStateLocked(device, ALED_CONTROLLER_1 + i, device->controllerNumber == i + 1); + } +} bool EventHub::hasKeycodeLocked(Device* device, int keycode) const { if (!device->keyMap.haveKeyLayout() || !device->keyBitmask) { @@ -1397,6 +1462,21 @@ bool EventHub::hasKeycodeLocked(Device* device, int keycode) const { return false; } +status_t EventHub::mapLed(Device* device, int32_t led, int32_t* outScanCode) const { + if (!device->keyMap.haveKeyLayout() || !device->ledBitmask) { + return NAME_NOT_FOUND; + } + + int32_t scanCode; + if(device->keyMap.keyLayoutMap->findScanCodeForLed(led, &scanCode) != NAME_NOT_FOUND) { + if(scanCode >= 0 && scanCode <= LED_MAX && test_bit(scanCode, device->ledBitmask)) { + *outScanCode = scanCode; + return NO_ERROR; + } + } + return NAME_NOT_FOUND; +} + status_t EventHub::closeDeviceByPathLocked(const char *devicePath) { Device* device = getDeviceByPathLocked(devicePath); if (device) { diff --git a/libs/input/EventHub.h b/libs/input/EventHub.h index ae28f01..20179ae 100644 --- a/libs/input/EventHub.h +++ b/libs/input/EventHub.h @@ -231,6 +231,8 @@ public: uint8_t* outFlags) const = 0; virtual bool hasScanCode(int32_t deviceId, int32_t scanCode) const = 0; + + /* LED related functions expect Android LED constants, not scan codes or HID usages */ virtual bool hasLed(int32_t deviceId, int32_t led) const = 0; virtual void setLedState(int32_t deviceId, int32_t led, bool on) = 0; @@ -371,6 +373,7 @@ private: status_t openDeviceLocked(const char *devicePath); void createVirtualKeyboardLocked(); void addDeviceLocked(Device* device); + void assignDescriptorLocked(InputDeviceIdentifier& identifier); status_t closeDeviceByPathLocked(const char *devicePath); void closeDeviceLocked(Device* device); @@ -380,6 +383,7 @@ private: void scanDevicesLocked(); status_t readNotifyLocked(); + Device* getDeviceByDescriptorLocked(String8& descriptor) const; Device* getDeviceLocked(int32_t deviceId) const; Device* getDeviceByPathLocked(const char* devicePath) const; @@ -393,6 +397,10 @@ private: int32_t getNextControllerNumberLocked(Device* device); void releaseControllerNumberLocked(Device* device); + void setLedForController(Device* device); + + status_t mapLed(Device* device, int32_t led, int32_t* outScanCode) const; + void setLedStateLocked(Device* device, int32_t led, bool on); // Protect all internal state. mutable Mutex mLock; @@ -440,6 +448,8 @@ private: size_t mPendingEventCount; size_t mPendingEventIndex; bool mPendingINotify; + + bool mUsingEpollWakeup; }; }; // namespace android diff --git a/libs/input/InputReader.cpp b/libs/input/InputReader.cpp index 03852a5..94e2a80 100644 --- a/libs/input/InputReader.cpp +++ b/libs/input/InputReader.cpp @@ -911,7 +911,7 @@ void InputDevice::configure(nsecs_t when, const InputReaderConfiguration* config if (!changes || (changes & InputReaderConfiguration::CHANGE_KEYBOARD_LAYOUTS)) { if (!(mClasses & INPUT_DEVICE_CLASS_VIRTUAL)) { sp<KeyCharacterMap> keyboardLayout = - mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier.descriptor); + mContext->getPolicy()->getKeyboardLayoutOverlay(mIdentifier); if (mContext->getEventHub()->setKeyboardLayoutOverlay(mId, keyboardLayout)) { bumpGeneration(); } @@ -2197,9 +2197,9 @@ int32_t KeyboardInputMapper::getMetaState() { } void KeyboardInputMapper::resetLedState() { - initializeLedState(mCapsLockLedState, LED_CAPSL); - initializeLedState(mNumLockLedState, LED_NUML); - initializeLedState(mScrollLockLedState, LED_SCROLLL); + initializeLedState(mCapsLockLedState, ALED_CAPS_LOCK); + initializeLedState(mNumLockLedState, ALED_NUM_LOCK); + initializeLedState(mScrollLockLedState, ALED_SCROLL_LOCK); updateLedState(true); } @@ -2210,11 +2210,11 @@ void KeyboardInputMapper::initializeLedState(LedState& ledState, int32_t led) { } void KeyboardInputMapper::updateLedState(bool reset) { - updateLedStateForModifier(mCapsLockLedState, LED_CAPSL, + updateLedStateForModifier(mCapsLockLedState, ALED_CAPS_LOCK, AMETA_CAPS_LOCK_ON, reset); - updateLedStateForModifier(mNumLockLedState, LED_NUML, + updateLedStateForModifier(mNumLockLedState, ALED_NUM_LOCK, AMETA_NUM_LOCK_ON, reset); - updateLedStateForModifier(mScrollLockLedState, LED_SCROLLL, + updateLedStateForModifier(mScrollLockLedState, ALED_SCROLL_LOCK, AMETA_SCROLL_LOCK_ON, reset); } @@ -6283,7 +6283,7 @@ void JoystickInputMapper::configure(nsecs_t when, // To eliminate noise while the joystick is at rest, filter out small variations // in axis values up front. - axis.filter = axis.flat * 0.25f; + axis.filter = axis.fuzz ? axis.fuzz : axis.flat * 0.25f; mAxes.add(abs, axis); } diff --git a/libs/input/InputReader.h b/libs/input/InputReader.h index a8bb636..674f67d 100644 --- a/libs/input/InputReader.h +++ b/libs/input/InputReader.h @@ -281,7 +281,8 @@ public: virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices) = 0; /* Gets the keyboard layout for a particular input device. */ - virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor) = 0; + virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay( + const InputDeviceIdentifier& identifier) = 0; /* Gets a user-supplied alias for a particular input device, or an empty string if none. */ virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier) = 0; @@ -574,8 +575,8 @@ public: private: InputReaderContext* mContext; int32_t mId; - int32_t mControllerNumber; int32_t mGeneration; + int32_t mControllerNumber; InputDeviceIdentifier mIdentifier; String8 mAlias; uint32_t mClasses; diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp index fd9c66b..2667a72 100644 --- a/libs/input/SpriteController.cpp +++ b/libs/input/SpriteController.cpp @@ -216,12 +216,12 @@ void SpriteController::doUpdateSprites() { paint.setXfermodeMode(SkXfermode::kSrc_Mode); surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint); - if (outBuffer.width > uint32_t(update.state.icon.bitmap.width())) { + if (outBuffer.width > update.state.icon.bitmap.width()) { paint.setColor(0); // transparent fill color surfaceCanvas.drawRectCoords(update.state.icon.bitmap.width(), 0, outBuffer.width, update.state.icon.bitmap.height(), paint); } - if (outBuffer.height > uint32_t(update.state.icon.bitmap.height())) { + if (outBuffer.height > update.state.icon.bitmap.height()) { paint.setColor(0); // transparent fill color surfaceCanvas.drawRectCoords(0, update.state.icon.bitmap.height(), outBuffer.width, outBuffer.height, paint); diff --git a/libs/input/tests/InputReader_test.cpp b/libs/input/tests/InputReader_test.cpp index f068732..aaa973d 100644 --- a/libs/input/tests/InputReader_test.cpp +++ b/libs/input/tests/InputReader_test.cpp @@ -186,7 +186,7 @@ private: mInputDevices = inputDevices; } - virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const String8& inputDeviceDescriptor) { + virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier) { return NULL; } |