summaryrefslogtreecommitdiffstats
path: root/libs/hwui
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui')
-rw-r--r--libs/hwui/Android.mk29
-rw-r--r--libs/hwui/Extensions.h79
-rw-r--r--libs/hwui/FontRenderer.cpp510
-rw-r--r--libs/hwui/FontRenderer.h197
-rw-r--r--libs/hwui/GenerationCache.h230
-rw-r--r--libs/hwui/GradientCache.cpp160
-rw-r--r--libs/hwui/GradientCache.h89
-rw-r--r--libs/hwui/Layer.h99
-rw-r--r--libs/hwui/LayerCache.cpp159
-rw-r--r--libs/hwui/LayerCache.h109
-rw-r--r--libs/hwui/MODULE_LICENSE_APACHE20
-rw-r--r--libs/hwui/Matrix.cpp231
-rw-r--r--libs/hwui/Matrix.h122
-rw-r--r--libs/hwui/NOTICE190
-rw-r--r--libs/hwui/OpenGLRenderer.cpp884
-rw-r--r--libs/hwui/OpenGLRenderer.h381
-rw-r--r--libs/hwui/Patch.cpp188
-rw-r--r--libs/hwui/Patch.h85
-rw-r--r--libs/hwui/PatchCache.cpp72
-rw-r--r--libs/hwui/PatchCache.h65
-rw-r--r--libs/hwui/Program.cpp224
-rw-r--r--libs/hwui/Program.h251
-rw-r--r--libs/hwui/Properties.h35
-rw-r--r--libs/hwui/Rect.h160
-rw-r--r--libs/hwui/Snapshot.h188
-rw-r--r--libs/hwui/Texture.h54
-rw-r--r--libs/hwui/TextureCache.cpp158
-rw-r--r--libs/hwui/TextureCache.h90
-rw-r--r--libs/hwui/Vertex.h54
-rw-r--r--libs/hwui/shaders/drawColor.frag11
-rw-r--r--libs/hwui/shaders/drawColor.vert11
-rw-r--r--libs/hwui/shaders/drawLinearGradient.frag14
-rw-r--r--libs/hwui/shaders/drawLinearGradient.vert20
-rw-r--r--libs/hwui/shaders/drawText.frag14
-rw-r--r--libs/hwui/shaders/drawTexture.frag14
-rw-r--r--libs/hwui/shaders/drawTexture.vert15
36 files changed, 5192 insertions, 0 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
new file mode 100644
index 0000000..172952a
--- /dev/null
+++ b/libs/hwui/Android.mk
@@ -0,0 +1,29 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ FontRenderer.cpp \
+ GradientCache.cpp \
+ LayerCache.cpp \
+ Matrix.cpp \
+ OpenGLRenderer.cpp \
+ Patch.cpp \
+ PatchCache.cpp \
+ Program.cpp \
+ TextureCache.cpp
+
+LOCAL_C_INCLUDES += \
+ $(JNI_H_INCLUDE) \
+ $(LOCAL_PATH)/../../include/utils \
+ external/skia/include/core \
+ external/skia/include/effects \
+ external/skia/include/images \
+ external/skia/src/ports \
+ external/skia/include/utils
+
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia
+LOCAL_MODULE := libhwui
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
new file mode 100644
index 0000000..99b34dd
--- /dev/null
+++ b/libs/hwui/Extensions.h
@@ -0,0 +1,79 @@
+/*
+ * 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 "OpenGLRenderer"
+
+#ifndef ANDROID_UI_EXTENSIONS_H
+#define ANDROID_UI_EXTENSIONS_H
+
+#include <utils/SortedVector.h>
+#include <utils/String8.h>
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+namespace android {
+namespace uirenderer {
+
+class Extensions {
+public:
+ Extensions() {
+ const char* buffer = (const char*) glGetString(GL_EXTENSIONS);
+ const char* current = buffer;
+ const char* head = current;
+ do {
+ head = strchr(current, ' ');
+ String8 s(current, head ? head - current : strlen(current));
+ if (s.length()) {
+ mExtensionList.add(s);
+ }
+ current = head + 1;
+ } while (head);
+
+ mHasNPot = hasExtension("GL_OES_texture_npot");
+ mHasDrawPath = hasExtension("GL_NV_draw_path");
+ mHasCoverageSample = hasExtension("GL_NV_coverage_sample");
+
+ mExtensions = buffer;
+ }
+
+ inline bool hasNPot() const { return mHasNPot; }
+ inline bool hasDrawPath() const { return mHasDrawPath; }
+ inline bool hasCoverageSample() const { return mHasCoverageSample; }
+
+ bool hasExtension(const char* extension) const {
+ const String8 s(extension);
+ return mExtensionList.indexOf(s) >= 0;
+ }
+
+ void dump() {
+ LOGD("Supported extensions:\n%s", mExtensions);
+ }
+
+private:
+ SortedVector<String8> mExtensionList;
+
+ const char* mExtensions;
+
+ bool mHasNPot;
+ bool mHasDrawPath;
+ bool mHasCoverageSample;
+}; // class Extensions
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_EXTENSIONS_H
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
new file mode 100644
index 0000000..14ad7d7
--- /dev/null
+++ b/libs/hwui/FontRenderer.cpp
@@ -0,0 +1,510 @@
+/*
+ * 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 "OpenGLRenderer"
+
+#include <SkUtils.h>
+
+#include <cutils/properties.h>
+#include <utils/Log.h>
+
+#include "FontRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define DEFAULT_TEXT_CACHE_WIDTH 1024
+#define DEFAULT_TEXT_CACHE_HEIGHT 256
+
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
+Font::Font(FontRenderer* state, uint32_t fontId, float fontSize) :
+ mState(state), mFontId(fontId), mFontSize(fontSize) {
+}
+
+
+Font::~Font() {
+ for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
+ if (mState->mActiveFonts[ct] == this) {
+ mState->mActiveFonts.removeAt(ct);
+ break;
+ }
+ }
+
+ for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+ CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i);
+ delete glyph;
+ }
+}
+
+void Font::invalidateTextureCache() {
+ for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+ mCachedGlyphs.valueAt(i)->mIsValid = false;
+ }
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
+ int nPenX = x + glyph->mBitmapLeft;
+ int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
+
+ float u1 = glyph->mBitmapMinU;
+ float u2 = glyph->mBitmapMaxU;
+ float v1 = glyph->mBitmapMinV;
+ float v2 = glyph->mBitmapMaxV;
+
+ int width = (int) glyph->mBitmapWidth;
+ int height = (int) glyph->mBitmapHeight;
+
+ mState->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
+ nPenX + width, nPenY, 0, u2, v2,
+ nPenX + width, nPenY - height, 0, u2, v1,
+ nPenX, nPenY - height, 0, u1, v1);
+}
+
+void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y) {
+ if (numGlyphs == 0 || text == NULL || len == 0) {
+ return;
+ }
+
+ int penX = x, penY = y;
+ int glyphsLeft = 1;
+ if (numGlyphs > 0) {
+ glyphsLeft = numGlyphs;
+ }
+
+ text += start;
+
+ while (glyphsLeft > 0) {
+ int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text);
+
+ // Reached the end of the string or encountered
+ if (utfChar < 0) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueFor(utfChar);
+ if (cachedGlyph == NULL) {
+ cachedGlyph = cacheGlyph(paint, utfChar);
+ }
+
+ // Is the glyph still in texture cache?
+ if (!cachedGlyph->mIsValid) {
+ const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar);
+ updateGlyphCache(paint, skiaGlyph, cachedGlyph);
+ }
+
+ // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
+ if (cachedGlyph->mIsValid) {
+ drawCachedGlyph(cachedGlyph, penX, penY);
+ }
+
+ penX += SkFixedFloor(cachedGlyph->mAdvanceX);
+
+ // If we were given a specific number of glyphs, decrement
+ if (numGlyphs > 0) {
+ glyphsLeft--;
+ }
+ }
+}
+
+void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph) {
+ glyph->mAdvanceX = skiaGlyph.fAdvanceX;
+ glyph->mAdvanceY = skiaGlyph.fAdvanceY;
+ glyph->mBitmapLeft = skiaGlyph.fLeft;
+ glyph->mBitmapTop = skiaGlyph.fTop;
+
+ uint32_t startX = 0;
+ uint32_t startY = 0;
+
+ // Get the bitmap for the glyph
+ paint->findImage(skiaGlyph);
+ glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
+
+ if (!glyph->mIsValid) {
+ return;
+ }
+
+ uint32_t endX = startX + skiaGlyph.fWidth;
+ uint32_t endY = startY + skiaGlyph.fHeight;
+
+ glyph->mBitmapWidth = skiaGlyph.fWidth;
+ glyph->mBitmapHeight = skiaGlyph.fHeight;
+
+ uint32_t cacheWidth = mState->getCacheWidth();
+ uint32_t cacheHeight = mState->getCacheHeight();
+
+ glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
+ glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
+ glyph->mBitmapMaxU = (float) endX / (float) cacheWidth;
+ glyph->mBitmapMaxV = (float) endY / (float) cacheHeight;
+
+ mState->mUploadTexture = true;
+}
+
+Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) {
+ CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
+ mCachedGlyphs.add(glyph, newGlyph);
+
+ const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph);
+ newGlyph->mGlyphIndex = skiaGlyph.fID;
+ newGlyph->mIsValid = false;
+
+ updateGlyphCache(paint, skiaGlyph, newGlyph);
+
+ return newGlyph;
+}
+
+Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize) {
+ Vector<Font*> &activeFonts = state->mActiveFonts;
+
+ for (uint32_t i = 0; i < activeFonts.size(); i++) {
+ Font* font = activeFonts[i];
+ if (font->mFontId == fontId && font->mFontSize == fontSize) {
+ return font;
+ }
+ }
+
+ Font* newFont = new Font(state, fontId, fontSize);
+ activeFonts.push(newFont);
+ return newFont;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// FontRenderer
+///////////////////////////////////////////////////////////////////////////////
+
+FontRenderer::FontRenderer() {
+ LOGD("Creating FontRenderer");
+
+ mInitialized = false;
+ mMaxNumberOfQuads = 1024;
+ mCurrentQuadIndex = 0;
+ mTextureId = 0;
+
+ mIndexBufferID = 0;
+
+ mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
+ mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
+
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
+ LOGD(" Setting text cache width to %s pixels", property);
+ mCacheWidth = atoi(property);
+ } else {
+ LOGD(" Using default text cache width of %i pixels", mCacheWidth);
+ }
+
+ if (property_get(PROPERTY_TEXT_CACHE_HEIGHT, property, NULL) > 0) {
+ LOGD(" Setting text cache width to %s pixels", property);
+ mCacheHeight = atoi(property);
+ } else {
+ LOGD(" Using default text cache height of %i pixels", mCacheHeight);
+ }
+}
+
+FontRenderer::~FontRenderer() {
+ for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+ delete mCacheLines[i];
+ }
+ mCacheLines.clear();
+
+ delete mTextMeshPtr;
+
+ delete mTextTexture;
+ if(mTextureId) {
+ glDeleteTextures(1, &mTextureId);
+ }
+
+ Vector<Font*> fontsToDereference = mActiveFonts;
+ for (uint32_t i = 0; i < fontsToDereference.size(); i++) {
+ delete fontsToDereference[i];
+ }
+}
+
+void FontRenderer::flushAllAndInvalidate() {
+ if (mCurrentQuadIndex != 0) {
+ issueDrawCommand();
+ mCurrentQuadIndex = 0;
+ }
+ for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
+ mActiveFonts[i]->invalidateTextureCache();
+ }
+ for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+ mCacheLines[i]->mCurrentCol = 0;
+ }
+}
+
+bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
+ // If the glyph is too tall, don't cache it
+ if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
+ LOGE("Font size to large to fit in cache. width, height = %i, %i",
+ (int) glyph.fWidth, (int) glyph.fHeight);
+ return false;
+ }
+
+ // Now copy the bitmap into the cache texture
+ uint32_t startX = 0;
+ uint32_t startY = 0;
+
+ bool bitmapFit = false;
+ for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+ bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
+ if (bitmapFit) {
+ break;
+ }
+ }
+
+ // If the new glyph didn't fit, flush the state so far and invalidate everything
+ if (!bitmapFit) {
+ flushAllAndInvalidate();
+
+ // Try to fit it again
+ for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+ bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
+ if (bitmapFit) {
+ break;
+ }
+ }
+
+ // if we still don't fit, something is wrong and we shouldn't draw
+ if (!bitmapFit) {
+ LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
+ (int) glyph.fWidth, (int) glyph.fHeight);
+ return false;
+ }
+ }
+
+ *retOriginX = startX;
+ *retOriginY = startY;
+
+ uint32_t endX = startX + glyph.fWidth;
+ uint32_t endY = startY + glyph.fHeight;
+
+ uint32_t cacheWidth = mCacheWidth;
+
+ unsigned char* cacheBuffer = mTextTexture;
+ unsigned char* bitmapBuffer = (unsigned char*) glyph.fImage;
+ unsigned int stride = glyph.rowBytes();
+
+ uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
+ for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
+ for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY++) {
+ unsigned char tempCol = bitmapBuffer[bY * stride + bX];
+ cacheBuffer[cacheY * cacheWidth + cacheX] = tempCol;
+ }
+ }
+
+ return true;
+}
+
+void FontRenderer::initTextTexture() {
+ mTextTexture = new unsigned char[mCacheWidth * mCacheHeight];
+ mUploadTexture = false;
+
+ glGenTextures(1, &mTextureId);
+ glBindTexture(GL_TEXTURE_2D, mTextureId);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ // Initialize texture dimentions
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, 0);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ // Split up our cache texture into lines of certain widths
+ int nextLine = 0;
+ mCacheLines.push(new CacheTextureLine(mCacheWidth, 16, nextLine, 0));
+ nextLine += mCacheLines.top()->mMaxHeight;
+ mCacheLines.push(new CacheTextureLine(mCacheWidth, 24, nextLine, 0));
+ nextLine += mCacheLines.top()->mMaxHeight;
+ mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
+ nextLine += mCacheLines.top()->mMaxHeight;
+ mCacheLines.push(new CacheTextureLine(mCacheWidth, 32, nextLine, 0));
+ nextLine += mCacheLines.top()->mMaxHeight;
+ mCacheLines.push(new CacheTextureLine(mCacheWidth, 40, nextLine, 0));
+ nextLine += mCacheLines.top()->mMaxHeight;
+ mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
+}
+
+// Avoid having to reallocate memory and render quad by quad
+void FontRenderer::initVertexArrayBuffers() {
+ uint32_t numIndicies = mMaxNumberOfQuads * 6;
+ uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
+ uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
+
+ // Four verts, two triangles , six indices per quad
+ for (uint32_t i = 0; i < mMaxNumberOfQuads; i++) {
+ int i6 = i * 6;
+ int i4 = i * 4;
+
+ indexBufferData[i6 + 0] = i4 + 0;
+ indexBufferData[i6 + 1] = i4 + 1;
+ indexBufferData[i6 + 2] = i4 + 2;
+
+ indexBufferData[i6 + 3] = i4 + 0;
+ indexBufferData[i6 + 4] = i4 + 2;
+ indexBufferData[i6 + 5] = i4 + 3;
+ }
+
+ glGenBuffers(1, &mIndexBufferID);
+ glBindBuffer(GL_ARRAY_BUFFER, mIndexBufferID);
+ glBufferData(GL_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_DYNAMIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ free(indexBufferData);
+
+ uint32_t coordSize = 3;
+ uint32_t uvSize = 2;
+ uint32_t vertsPerQuad = 4;
+ uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
+ mTextMeshPtr = new float[vertexBufferSize];
+}
+
+// We don't want to allocate anything unless we actually draw text
+void FontRenderer::checkInit() {
+ if (mInitialized) {
+ return;
+ }
+
+ initTextTexture();
+ initVertexArrayBuffers();
+
+ mInitialized = true;
+}
+
+void FontRenderer::checkTextureUpdate() {
+ if (!mUploadTexture) {
+ return;
+ }
+
+ glBindTexture(GL_TEXTURE_2D, mTextureId);
+
+ // Iterate over all the cache lines and see which ones need to be updated
+ for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+ CacheTextureLine* cl = mCacheLines[i];
+ if(cl->mDirty) {
+ uint32_t xOffset = 0;
+ uint32_t yOffset = cl->mCurrentRow;
+ uint32_t width = mCacheWidth;
+ uint32_t height = cl->mMaxHeight;
+ void* textureData = mTextTexture + yOffset*width;
+
+ glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
+ GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
+
+ cl->mDirty = false;
+ }
+ }
+
+ mUploadTexture = false;
+}
+
+void FontRenderer::issueDrawCommand() {
+
+ checkTextureUpdate();
+
+ float* vtx = mTextMeshPtr;
+ float* tex = vtx + 3;
+
+ // position is slot 0
+ uint32_t slot = 0;
+ glVertexAttribPointer(slot, 3, GL_FLOAT, false, 20, vtx);
+
+ // texture0 is slot 1
+ slot = 1;
+ glVertexAttribPointer(slot, 2, GL_FLOAT, false, 20, tex);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
+ glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
+}
+
+void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
+ float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
+ float x4, float y4, float z4, float u4, float v4) {
+ if (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom) {
+ return;
+ }
+
+ const uint32_t vertsPerQuad = 4;
+ const uint32_t floatsPerVert = 5;
+ float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
+
+ (*currentPos++) = x1;
+ (*currentPos++) = y1;
+ (*currentPos++) = z1;
+ (*currentPos++) = u1;
+ (*currentPos++) = v1;
+
+ (*currentPos++) = x2;
+ (*currentPos++) = y2;
+ (*currentPos++) = z2;
+ (*currentPos++) = u2;
+ (*currentPos++) = v2;
+
+ (*currentPos++) = x3;
+ (*currentPos++) = y3;
+ (*currentPos++) = z3;
+ (*currentPos++) = u3;
+ (*currentPos++) = v3;
+
+ (*currentPos++) = x4;
+ (*currentPos++) = y4;
+ (*currentPos++) = z4;
+ (*currentPos++) = u4;
+ (*currentPos++) = v4;
+
+ mCurrentQuadIndex++;
+
+ if (mCurrentQuadIndex == mMaxNumberOfQuads) {
+ issueDrawCommand();
+ mCurrentQuadIndex = 0;
+ }
+}
+
+void FontRenderer::setFont(uint32_t fontId, float fontSize) {
+ mCurrentFont = Font::create(this, fontId, fontSize);
+}
+
+void FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
+ uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y) {
+ checkInit();
+
+ if (!mCurrentFont) {
+ LOGE("No font set");
+ return;
+ }
+
+ mClip = clip;
+ mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y);
+
+ if (mCurrentQuadIndex != 0) {
+ issueDrawCommand();
+ mCurrentQuadIndex = 0;
+ }
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
new file mode 100644
index 0000000..bacd3aa
--- /dev/null
+++ b/libs/hwui/FontRenderer.h
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_FONT_RENDERER_H
+#define ANDROID_UI_FONT_RENDERER_H
+
+#include <utils/String8.h>
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+
+#include <SkScalerContext.h>
+#include <SkPaint.h>
+
+#include <GLES2/gl2.h>
+
+#include "Rect.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+class FontRenderer;
+
+/**
+ * Represents a font, defined by a Skia font id and a font size. A font is used
+ * to generate glyphs and cache them in the FontState.
+ */
+class Font {
+public:
+ ~Font();
+
+ /**
+ * Renders the specified string of text.
+ */
+ void renderUTF(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y);
+ /**
+ * Creates a new font associated with the specified font state.
+ */
+ static Font* create(FontRenderer* state, uint32_t fontId, float fontSize);
+
+protected:
+ friend class FontRenderer;
+
+ struct CachedGlyphInfo {
+ // Has the cache been invalidated?
+ bool mIsValid;
+ // Location of the cached glyph in the bitmap
+ // in case we need to resize the texture
+ uint32_t mBitmapWidth;
+ uint32_t mBitmapHeight;
+ // Also cache texture coords for the quad
+ float mBitmapMinU;
+ float mBitmapMinV;
+ float mBitmapMaxU;
+ float mBitmapMaxV;
+ // Minimize how much we call freetype
+ uint32_t mGlyphIndex;
+ uint32_t mAdvanceX;
+ uint32_t mAdvanceY;
+ // Values below contain a glyph's origin in the bitmap
+ uint32_t mBitmapLeft;
+ uint32_t mBitmapTop;
+ };
+
+ Font(FontRenderer* state, uint32_t fontId, float fontSize);
+
+ DefaultKeyedVector<int32_t, CachedGlyphInfo*> mCachedGlyphs;
+
+ void invalidateTextureCache();
+
+ CachedGlyphInfo *cacheGlyph(SkPaint* paint, int32_t glyph);
+ void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo *glyph);
+ void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y);
+
+ FontRenderer* mState;
+ uint32_t mFontId;
+ float mFontSize;
+};
+
+class FontRenderer {
+public:
+ FontRenderer();
+ ~FontRenderer();
+
+ void init();
+ void deinit();
+
+ void setFont(uint32_t fontId, float fontSize);
+ void renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
+ uint32_t len, int numGlyphs, int x, int y);
+
+ GLuint getTexture() {
+ checkInit();
+ return mTextureId;
+ }
+
+protected:
+ friend class Font;
+
+ struct CacheTextureLine {
+ uint16_t mMaxHeight;
+ uint16_t mMaxWidth;
+ uint32_t mCurrentRow;
+ uint32_t mCurrentCol;
+ bool mDirty;
+
+ CacheTextureLine(uint16_t maxWidth, uint16_t maxHeight, uint32_t currentRow,
+ uint32_t currentCol):
+ mMaxHeight(maxHeight),
+ mMaxWidth(maxWidth),
+ mCurrentRow(currentRow),
+ mCurrentCol(currentCol),
+ mDirty(false) {
+ }
+
+ bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
+ if (glyph.fHeight > mMaxHeight) {
+ return false;
+ }
+
+ if (mCurrentCol + glyph.fWidth < mMaxWidth) {
+ *retOriginX = mCurrentCol;
+ *retOriginY = mCurrentRow;
+ mCurrentCol += glyph.fWidth;
+ mDirty = true;
+ return true;
+ }
+
+ return false;
+ }
+ };
+
+ uint32_t getCacheWidth() const {
+ return mCacheWidth;
+ }
+
+ uint32_t getCacheHeight() const {
+ return mCacheHeight;
+ }
+
+ void initTextTexture();
+ bool cacheBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
+
+ void flushAllAndInvalidate();
+ void initVertexArrayBuffers();
+
+ void checkInit();
+
+ void issueDrawCommand();
+ void appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2, float y2,
+ float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
+ float x4, float y4, float z4, float u4, float v4);
+
+ uint32_t mCacheWidth;
+ uint32_t mCacheHeight;
+
+ Vector<CacheTextureLine*> mCacheLines;
+
+ Font* mCurrentFont;
+ Vector<Font*> mActiveFonts;
+
+ // Texture to cache glyph bitmaps
+ unsigned char* mTextTexture;
+ GLuint mTextureId;
+ void checkTextureUpdate();
+ bool mUploadTexture;
+
+ // Pointer to vertex data to speed up frame to frame work
+ float *mTextMeshPtr;
+ uint32_t mCurrentQuadIndex;
+ uint32_t mMaxNumberOfQuads;
+
+ uint32_t mIndexBufferID;
+
+ const Rect* mClip;
+
+ bool mInitialized;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_FONT_RENDERER_H
diff --git a/libs/hwui/GenerationCache.h b/libs/hwui/GenerationCache.h
new file mode 100644
index 0000000..5c1b5e1
--- /dev/null
+++ b/libs/hwui/GenerationCache.h
@@ -0,0 +1,230 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_GENERATION_CACHE_H
+#define ANDROID_UI_GENERATION_CACHE_H
+
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+
+namespace android {
+namespace uirenderer {
+
+template<typename EntryKey, typename EntryValue>
+class OnEntryRemoved {
+public:
+ virtual ~OnEntryRemoved() { };
+ virtual void operator()(EntryKey& key, EntryValue& value) = 0;
+}; // class OnEntryRemoved
+
+template<typename EntryKey, typename EntryValue>
+struct Entry: public LightRefBase<Entry<EntryKey, EntryValue> > {
+ Entry() { }
+ Entry(const Entry<EntryKey, EntryValue>& e):
+ key(e.key), value(e.value), index(e.index), parent(e.parent), child(e.child) { }
+ Entry(sp<Entry<EntryKey, EntryValue> > e):
+ key(e->key), value(e->value), index(e->index), parent(e->parent), child(e->child) { }
+
+ EntryKey key;
+ EntryValue value;
+ ssize_t index;
+
+ sp<Entry<EntryKey, EntryValue> > parent;
+ sp<Entry<EntryKey, EntryValue> > child;
+}; // struct Entry
+
+template<typename K, typename V>
+class GenerationCache {
+public:
+ GenerationCache(uint32_t maxCapacity);
+ virtual ~GenerationCache();
+
+ enum Capacity {
+ kUnlimitedCapacity,
+ };
+
+ void setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener);
+
+ void clear();
+
+ bool contains(K key) const;
+ V get(K key);
+ void put(K key, V value);
+ V remove(K key);
+ V removeOldest();
+
+ uint32_t size() const;
+
+ void addToCache(sp<Entry<K, V> > entry, K key, V value);
+ void attachToCache(sp<Entry<K, V> > entry);
+ void detachFromCache(sp<Entry<K, V> > entry);
+
+ V removeAt(ssize_t index);
+
+ KeyedVector<K, sp<Entry<K, V> > > mCache;
+ uint32_t mMaxCapacity;
+
+ OnEntryRemoved<K, V>* mListener;
+
+ sp<Entry<K, V> > mOldest;
+ sp<Entry<K, V> > mYoungest;
+}; // class GenerationCache
+
+template<typename K, typename V>
+GenerationCache<K, V>::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), mListener(NULL) {
+};
+
+template<typename K, typename V>
+GenerationCache<K, V>::~GenerationCache() {
+ clear();
+};
+
+template<typename K, typename V>
+uint32_t GenerationCache<K, V>::size() const {
+ return mCache.size();
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
+ mListener = listener;
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::clear() {
+ if (mListener) {
+ while (mCache.size() > 0) {
+ removeOldest();
+ }
+ } else {
+ mCache.clear();
+ }
+ mYoungest.clear();
+ mOldest.clear();
+}
+
+template<typename K, typename V>
+bool GenerationCache<K, V>::contains(K key) const {
+ return mCache.indexOfKey(key) >= 0;
+}
+
+template<typename K, typename V>
+V GenerationCache<K, V>::get(K key) {
+ ssize_t index = mCache.indexOfKey(key);
+ if (index >= 0) {
+ sp<Entry<K, V> > entry = mCache.valueAt(index);
+ if (entry.get()) {
+ detachFromCache(entry);
+ attachToCache(entry);
+ return entry->value;
+ }
+ }
+
+ return NULL;
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::put(K key, V value) {
+ if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) {
+ removeOldest();
+ }
+
+ ssize_t index = mCache.indexOfKey(key);
+ if (index >= 0) {
+ sp<Entry<K, V> > entry = mCache.valueAt(index);
+ detachFromCache(entry);
+ addToCache(entry, key, value);
+ } else {
+ sp<Entry<K, V> > entry = new Entry<K, V>;
+ addToCache(entry, key, value);
+ }
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::addToCache(sp<Entry<K, V> > entry, K key, V value) {
+ entry->key = key;
+ entry->value = value;
+ entry->index = mCache.add(key, entry);
+ attachToCache(entry);
+}
+
+template<typename K, typename V>
+V GenerationCache<K, V>::remove(K key) {
+ ssize_t index = mCache.indexOfKey(key);
+ if (index >= 0) {
+ return removeAt(index);
+ }
+
+ return NULL;
+}
+
+template<typename K, typename V>
+V GenerationCache<K, V>::removeAt(ssize_t index) {
+ sp<Entry<K, V> > entry = mCache.valueAt(index);
+ if (mListener) {
+ (*mListener)(entry->key, entry->value);
+ }
+ mCache.removeItemsAt(index, 1);
+ detachFromCache(entry);
+
+ return entry->value;
+}
+
+template<typename K, typename V>
+V GenerationCache<K, V>::removeOldest() {
+ if (mOldest.get()) {
+ return removeAt(mOldest->index);
+ }
+
+ return NULL;
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::attachToCache(sp<Entry<K, V> > entry) {
+ if (!mYoungest.get()) {
+ mYoungest = mOldest = entry;
+ } else {
+ entry->parent = mYoungest;
+ mYoungest->child = entry;
+ mYoungest = entry;
+ }
+}
+
+template<typename K, typename V>
+void GenerationCache<K, V>::detachFromCache(sp<Entry<K, V> > entry) {
+ if (entry->parent.get()) {
+ entry->parent->child = entry->child;
+ }
+
+ if (entry->child.get()) {
+ entry->child->parent = entry->parent;
+ }
+
+ if (mOldest == entry) {
+ mOldest = entry->child;
+ }
+
+ if (mYoungest == entry) {
+ mYoungest = entry->parent;
+ }
+
+ entry->parent.clear();
+ entry->child.clear();
+}
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_GENERATION_CACHE_H
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
new file mode 100644
index 0000000..aeda416
--- /dev/null
+++ b/libs/hwui/GradientCache.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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 "OpenGLRenderer"
+
+#include <GLES2/gl2.h>
+
+#include <SkCanvas.h>
+#include <SkGradientShader.h>
+
+#include "GradientCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+GradientCache::GradientCache(uint32_t maxByteSize):
+ mCache(GenerationCache<SkShader*, Texture*>::kUnlimitedCapacity),
+ mSize(0), mMaxSize(maxByteSize) {
+ mCache.setOnEntryRemovedListener(this);
+}
+
+GradientCache::~GradientCache() {
+ mCache.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t GradientCache::getSize() {
+ return mSize;
+}
+
+uint32_t GradientCache::getMaxSize() {
+ return mMaxSize;
+}
+
+void GradientCache::setMaxSize(uint32_t maxSize) {
+ mMaxSize = maxSize;
+ while (mSize > mMaxSize) {
+ mCache.removeOldest();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void GradientCache::operator()(SkShader*& shader, Texture*& texture) {
+ if (shader) {
+ const uint32_t size = texture->width * texture->height * 4;
+ mSize -= size;
+ }
+
+ if (texture) {
+ glDeleteTextures(1, &texture->id);
+ delete texture;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+Texture* GradientCache::get(SkShader* shader) {
+ Texture* texture = mCache.get(shader);
+ return texture;
+}
+
+void GradientCache::remove(SkShader* shader) {
+ mCache.remove(shader);
+}
+
+void GradientCache::clear() {
+ mCache.clear();
+}
+
+Texture* GradientCache::addLinearGradient(SkShader* shader, float* bounds, uint32_t* colors,
+ float* positions, int count, SkShader::TileMode tileMode) {
+ SkBitmap bitmap;
+ bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1024, 1);
+ bitmap.allocPixels();
+ bitmap.eraseColor(0);
+
+ SkCanvas canvas(bitmap);
+
+ SkPoint points[2];
+ points[0].set(0.0f, 0.0f);
+ points[1].set(bitmap.width(), 0.0f);
+
+ SkShader* localShader = SkGradientShader::CreateLinear(points,
+ reinterpret_cast<const SkColor*>(colors), positions, count, tileMode);
+
+ SkPaint p;
+ p.setStyle(SkPaint::kStrokeAndFill_Style);
+ p.setShader(localShader)->unref();
+
+ canvas.drawRectCoords(0.0f, 0.0f, bitmap.width(), 1.0f, p);
+
+ // Asume the cache is always big enough
+ const uint32_t size = bitmap.rowBytes() * bitmap.height();
+ while (mSize + size > mMaxSize) {
+ mCache.removeOldest();
+ }
+
+ Texture* texture = new Texture;
+ generateTexture(&bitmap, texture);
+
+ mSize += size;
+ mCache.put(shader, texture);
+
+ return texture;
+}
+
+void GradientCache::generateTexture(SkBitmap* bitmap, Texture* texture) {
+ SkAutoLockPixels autoLock(*bitmap);
+ if (!bitmap->readyToDraw()) {
+ LOGE("Cannot generate texture from shader");
+ return;
+ }
+
+ texture->generation = bitmap->getGenerationID();
+ texture->width = bitmap->width();
+ texture->height = bitmap->height();
+
+ glGenTextures(1, &texture->id);
+
+ glBindTexture(GL_TEXTURE_2D, texture->id);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
+
+ texture->blend = !bitmap->isOpaque();
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h
new file mode 100644
index 0000000..12c8a23
--- /dev/null
+++ b/libs/hwui/GradientCache.h
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_GRADIENT_CACHE_H
+#define ANDROID_UI_GRADIENT_CACHE_H
+
+#include <SkShader.h>
+
+#include "Texture.h"
+#include "GenerationCache.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A simple LRU gradient cache. The cache has a maximum size expressed in bytes.
+ * 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 GradientCache: public OnEntryRemoved<SkShader*, Texture*> {
+public:
+ GradientCache(uint32_t maxByteSize);
+ ~GradientCache();
+
+ /**
+ * Used as a callback when an entry is removed from the cache.
+ * Do not invoke directly.
+ */
+ void operator()(SkShader*& shader, Texture*& texture);
+
+ /**
+ * Adds a new linear gradient to the cache. The generated texture is
+ * returned.
+ */
+ Texture* addLinearGradient(SkShader* shader, float* bounds, uint32_t* colors,
+ float* positions, int count, SkShader::TileMode tileMode);
+ /**
+ * Returns the texture associated with the specified shader.
+ */
+ Texture* get(SkShader* shader);
+ /**
+ * Removes the texture associated with the specified shader. Returns NULL
+ * if the texture cannot be found. Upon remove the texture is freed.
+ */
+ void remove(SkShader* shader);
+ /**
+ * Clears the cache. This causes all textures to be deleted.
+ */
+ void clear();
+
+ /**
+ * Sets the maximum size of the cache in bytes.
+ */
+ void setMaxSize(uint32_t maxSize);
+ /**
+ * Returns the maximum size of the cache in bytes.
+ */
+ uint32_t getMaxSize();
+ /**
+ * Returns the current size of the cache in bytes.
+ */
+ uint32_t getSize();
+
+private:
+ void generateTexture(SkBitmap* bitmap, Texture* texture);
+
+ GenerationCache<SkShader*, Texture*> mCache;
+
+ uint32_t mSize;
+ uint32_t mMaxSize;
+}; // class GradientCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_GRADIENT_CACHE_H
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
new file mode 100644
index 0000000..d4db782
--- /dev/null
+++ b/libs/hwui/Layer.h
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_LAYER_H
+#define ANDROID_UI_LAYER_H
+
+#include <sys/types.h>
+
+#include <GLES2/gl2.h>
+
+#include <SkXfermode.h>
+
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Dimensions of a layer.
+ */
+struct LayerSize {
+ LayerSize(): width(0), height(0), id(0) { }
+ LayerSize(const uint32_t width, const uint32_t height): width(width), height(height), id(0) { }
+ LayerSize(const LayerSize& size): width(size.width), height(size.height), id(size.id) { }
+
+ uint32_t width;
+ uint32_t height;
+
+ // Incremental id used by the layer cache to store multiple
+ // LayerSize with the same dimensions
+ uint32_t id;
+
+ bool operator<(const LayerSize& rhs) const {
+ if (id != 0 && rhs.id != 0) {
+ return id < rhs.id;
+ }
+ if (width == rhs.width) {
+ return height < rhs.height;
+ }
+ return width < rhs.width;
+ }
+
+ bool operator==(const LayerSize& rhs) const {
+ return width == rhs.width && height == rhs.height;
+ }
+}; // struct LayerSize
+
+/**
+ * A layer has dimensions and is backed by an OpenGL texture.
+ */
+struct Layer {
+ /**
+ * Coordinates of the layer corresponding to this snapshot.
+ * Only set when the flag kFlagIsLayer is set.
+ */
+ Rect layer;
+ /**
+ * Name of the texture used to render the layer.
+ * Only set when the flag kFlagIsLayer is set.
+ */
+ GLuint texture;
+ /**
+ * Name of the FBO used to render the layer.
+ * Only set when the flag kFlagIsLayer is set.
+ */
+ GLuint fbo;
+ /**
+ * Opacity of the layer.
+ * Only set when the flag kFlagIsLayer is set.
+ */
+ float alpha;
+ /**
+ * Blending mode of the layer.
+ * Only set when the flag kFlagIsLayer is set.
+ */
+ SkXfermode::Mode mode;
+ /**
+ * Indicates whether this layer should be blended.
+ */
+ bool blend;
+}; // struct Layer
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_LAYER_H
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
new file mode 100644
index 0000000..3d263a3
--- /dev/null
+++ b/libs/hwui/LayerCache.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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 "OpenGLRenderer"
+
+#include <GLES2/gl2.h>
+
+#include <utils/Log.h>
+
+#include "LayerCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+LayerCache::LayerCache(uint32_t maxByteSize):
+ mCache(GenerationCache<LayerSize, Layer*>::kUnlimitedCapacity),
+ mIdGenerator(1), mSize(0), mMaxSize(maxByteSize) {
+}
+
+LayerCache::~LayerCache() {
+ clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t LayerCache::getSize() {
+ return mSize;
+}
+
+uint32_t LayerCache::getMaxSize() {
+ return mMaxSize;
+}
+
+void LayerCache::setMaxSize(uint32_t maxSize) {
+ mMaxSize = maxSize;
+ while (mSize > mMaxSize) {
+ Layer* oldest = mCache.removeOldest();
+ deleteLayer(oldest);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void LayerCache::operator()(LayerSize& size, Layer*& layer) {
+ deleteLayer(layer);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+void LayerCache::deleteLayer(Layer* layer) {
+ if (layer) {
+ mSize -= layer->layer.getWidth() * layer->layer.getHeight() * 4;
+
+ glDeleteFramebuffers(1, &layer->fbo);
+ glDeleteTextures(1, &layer->texture);
+ delete layer;
+ }
+}
+
+void LayerCache::clear() {
+ mCache.setOnEntryRemovedListener(this);
+ mCache.clear();
+ mCache.setOnEntryRemovedListener(NULL);
+}
+
+Layer* LayerCache::get(LayerSize& size, GLuint previousFbo) {
+ Layer* layer = mCache.remove(size);
+ if (layer) {
+ LAYER_LOGD("Reusing layer");
+
+ mSize -= layer->layer.getWidth() * layer->layer.getHeight() * 4;
+ } else {
+ LAYER_LOGD("Creating new layer");
+
+ layer = new Layer;
+ layer->blend = true;
+
+ // Generate the FBO and attach the texture
+ glGenFramebuffers(1, &layer->fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
+
+ // Generate the texture in which the FBO will draw
+ glGenTextures(1, &layer->texture);
+ glBindTexture(GL_TEXTURE_2D, layer->texture);
+
+ // The FBO will not be scaled, so we can use lower quality filtering
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width, size.height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+
+ // Bind texture to FBO
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ layer->texture, 0);
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ LOGE("Framebuffer incomplete (GL error code 0x%x)", status);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+
+ glDeleteFramebuffers(1, &layer->fbo);
+ glDeleteTextures(1, &layer->texture);
+ delete layer;
+
+ return NULL;
+ }
+ }
+
+ return layer;
+}
+
+bool LayerCache::put(LayerSize& layerSize, Layer* layer) {
+ const uint32_t size = layerSize.width * layerSize.height * 4;
+ // Don't even try to cache a layer that's bigger than the cache
+ if (size < mMaxSize) {
+ while (mSize + size > mMaxSize) {
+ Layer* oldest = mCache.removeOldest();
+ deleteLayer(oldest);
+ }
+
+ layerSize.id = mIdGenerator++;
+ mCache.put(layerSize, layer);
+ mSize += size;
+
+ return true;
+ }
+ return false;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
new file mode 100644
index 0000000..2580551
--- /dev/null
+++ b/libs/hwui/LayerCache.h
@@ -0,0 +1,109 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_LAYER_CACHE_H
+#define ANDROID_UI_LAYER_CACHE_H
+
+#include "Layer.h"
+#include "GenerationCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_LAYERS 0
+
+// Debug
+#if DEBUG_LAYERS
+ #define LAYER_LOGD(...) LOGD(__VA_ARGS__)
+#else
+ #define LAYER_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache
+///////////////////////////////////////////////////////////////////////////////
+
+class LayerCache: public OnEntryRemoved<LayerSize, Layer*> {
+public:
+ LayerCache(uint32_t maxByteSize);
+ ~LayerCache();
+
+ /**
+ * Used as a callback when an entry is removed from the cache.
+ * Do not invoke directly.
+ */
+ void operator()(LayerSize& size, Layer*& layer);
+
+ /**
+ * Returns the layer of specified dimensions. If not suitable layer
+ * can be found, a new one is created and returned. If creating a new
+ * layer fails, NULL is returned.
+ *
+ * When a layer is obtained from the cache, it is removed and the total
+ * size of the cache goes down.
+ *
+ * @param size The dimensions of the desired layer
+ * @param previousFbo The name of the FBO to bind to if creating a new
+ * layer fails
+ */
+ Layer* get(LayerSize& size, GLuint previousFbo);
+ /**
+ * Adds the layer to the cache. The layer will not be added if there is
+ * not enough space available.
+ *
+ * @param size The dimensions of the layer
+ * @param layer The layer to add to the cache
+ *
+ * @return True if the layer was added, false otherwise.
+ */
+ bool put(LayerSize& size, Layer* layer);
+ /**
+ * Clears the cache. This causes all layers to be deleted.
+ */
+ void clear();
+
+ /**
+ * Sets the maximum size of the cache in bytes.
+ */
+ void setMaxSize(uint32_t maxSize);
+ /**
+ * Returns the maximum size of the cache in bytes.
+ */
+ uint32_t getMaxSize();
+ /**
+ * Returns the current size of the cache in bytes.
+ */
+ uint32_t getSize();
+
+private:
+ void deleteLayer(Layer* layer);
+
+ GenerationCache<LayerSize, Layer*> mCache;
+ uint32_t mIdGenerator;
+
+ uint32_t mSize;
+ uint32_t mMaxSize;
+}; // class LayerCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_LAYER_CACHE_H
diff --git a/libs/hwui/MODULE_LICENSE_APACHE2 b/libs/hwui/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/libs/hwui/MODULE_LICENSE_APACHE2
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
new file mode 100644
index 0000000..b459202
--- /dev/null
+++ b/libs/hwui/Matrix.cpp
@@ -0,0 +1,231 @@
+/*
+ * 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 "OpenGLRenderer"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/Log.h>
+
+#include <SkMatrix.h>
+
+#include "Matrix.h"
+
+namespace android {
+namespace uirenderer {
+
+void Matrix4::loadIdentity() {
+ data[0] = 1.0f;
+ data[1] = 0.0f;
+ data[2] = 0.0f;
+ data[3] = 0.0f;
+
+ data[4] = 0.0f;
+ data[5] = 1.0f;
+ data[6] = 0.0f;
+ data[7] = 0.0f;
+
+ data[8] = 0.0f;
+ data[9] = 0.0f;
+ data[10] = 1.0f;
+ data[11] = 0.0f;
+
+ data[12] = 0.0f;
+ data[13] = 0.0f;
+ data[14] = 0.0f;
+ data[15] = 1.0f;
+}
+
+void Matrix4::load(const float* v) {
+ memcpy(data, v, sizeof(data));
+}
+
+void Matrix4::load(const Matrix4& v) {
+ memcpy(data, v.data, sizeof(data));
+}
+
+void Matrix4::load(const SkMatrix& v) {
+ memset(data, 0, sizeof(data));
+
+ data[0] = v[SkMatrix::kMScaleX];
+ data[4] = v[SkMatrix::kMSkewX];
+ data[12] = v[SkMatrix::kMTransX];
+
+ data[1] = v[SkMatrix::kMSkewY];
+ data[5] = v[SkMatrix::kMScaleY];
+ data[13] = v[SkMatrix::kMTransY];
+
+ data[3] = v[SkMatrix::kMPersp0];
+ data[7] = v[SkMatrix::kMPersp1];
+ data[15] = v[SkMatrix::kMPersp2];
+
+ data[10] = 1.0f;
+}
+
+void Matrix4::copyTo(SkMatrix& v) const {
+ v.reset();
+
+ v.set(SkMatrix::kMScaleX, data[0]);
+ v.set(SkMatrix::kMSkewX, data[4]);
+ v.set(SkMatrix::kMTransX, data[12]);
+
+ v.set(SkMatrix::kMSkewY, data[1]);
+ v.set(SkMatrix::kMScaleY, data[5]);
+ v.set(SkMatrix::kMTransY, data[13]);
+
+ v.set(SkMatrix::kMPersp0, data[3]);
+ v.set(SkMatrix::kMPersp1, data[7]);
+ v.set(SkMatrix::kMPersp2, data[15]);
+}
+
+void Matrix4::loadInverse(const Matrix4& v) {
+ double scale = 1.0 /
+ (v.data[0] * ((double) v.data[5] * v.data[15] - (double) v.data[13] * v.data[7]) +
+ v.data[4] * ((double) v.data[13] * v.data[3] - (double) v.data[1] * v.data[15]) +
+ v.data[12] * ((double) v.data[1] * v.data[7] - (double) v.data[5] * v.data[3]));
+
+ data[0] = (v.data[5] * v.data[15] - v.data[13] * v.data[7]) * scale;
+ data[4] = (v.data[12] * v.data[7] - v.data[4] * v.data[15]) * scale;
+ data[12] = (v.data[4] * v.data[13] - v.data[12] * v.data[5]) * scale;
+
+ data[1] = (v.data[13] * v.data[3] - v.data[1] * v.data[15]) * scale;
+ data[5] = (v.data[0] * v.data[15] - v.data[12] * v.data[3]) * scale;
+ data[13] = (v.data[12] * v.data[1] - v.data[0] * v.data[13]) * scale;
+
+ data[3] = (v.data[1] * v.data[7] - v.data[5] * v.data[3]) * scale;
+ data[7] = (v.data[4] * v.data[3] - v.data[0] * v.data[7]) * scale;
+ data[15] = (v.data[0] * v.data[5] - v.data[4] * v.data[1]) * scale;
+}
+
+void Matrix4::copyTo(float* v) const {
+ memcpy(v, data, sizeof(data));
+}
+
+float Matrix4::getTranslateX() {
+ return data[12];
+}
+
+float Matrix4::getTranslateY() {
+ return data[13];
+}
+
+void Matrix4::loadTranslate(float x, float y, float z) {
+ loadIdentity();
+ data[12] = x;
+ data[13] = y;
+ data[14] = z;
+}
+
+void Matrix4::loadScale(float sx, float sy, float sz) {
+ loadIdentity();
+ data[0] = sx;
+ data[5] = sy;
+ data[10] = sz;
+}
+
+void Matrix4::loadRotate(float angle, float x, float y, float z) {
+ data[3] = 0.0f;
+ data[7] = 0.0f;
+ data[11] = 0.0f;
+ data[12] = 0.0f;
+ data[13] = 0.0f;
+ data[14] = 0.0f;
+ data[15] = 1.0f;
+
+ angle *= float(M_PI / 180.0f);
+ float c = cosf(angle);
+ float s = sinf(angle);
+
+ const float length = sqrtf(x * x + y * y + z * z);
+ const float nc = 1.0f - c;
+ const float xy = x * y;
+ const float yz = y * z;
+ const float zx = z * x;
+ const float xs = x * s;
+ const float ys = y * s;
+ const float zs = z * s;
+
+ data[0] = x * x * nc + c;
+ data[4] = xy * nc - zs;
+ data[8] = zx * nc + ys;
+ data[1] = xy * nc + zs;
+ data[5] = y * y * nc + c;
+ data[9] = yz * nc - xs;
+ data[2] = zx * nc - ys;
+ data[6] = yz * nc + xs;
+ data[10] = z * z * nc + c;
+}
+
+void Matrix4::loadMultiply(const Matrix4& u, const Matrix4& v) {
+ for (int i = 0 ; i < 4 ; i++) {
+ float x = 0;
+ float y = 0;
+ float z = 0;
+ float w = 0;
+
+ for (int j = 0 ; j < 4 ; j++) {
+ const float e = v.get(i, j);
+ x += u.get(j, 0) * e;
+ y += u.get(j, 1) * e;
+ z += u.get(j, 2) * e;
+ w += u.get(j, 3) * e;
+ }
+
+ set(i, 0, x);
+ set(i, 1, y);
+ set(i, 2, z);
+ set(i, 3, w);
+ }
+}
+
+void Matrix4::loadOrtho(float left, float right, float bottom, float top, float near, float far) {
+ loadIdentity();
+ data[0] = 2.0f / (right - left);
+ data[5] = 2.0f / (top - bottom);
+ data[10] = -2.0f / (far - near);
+ data[12] = -(right + left) / (right - left);
+ data[13] = -(top + bottom) / (top - bottom);
+ data[14] = -(far + near) / (far - near);
+}
+
+#define MUL_ADD_STORE(a, b, c) a = (a) * (b) + (c)
+
+void Matrix4::mapRect(Rect& r) const {
+ const float sx = data[0];
+ const float sy = data[5];
+
+ const float tx = data[12];
+ const float ty = data[13];
+
+ MUL_ADD_STORE(r.left, sx, tx);
+ MUL_ADD_STORE(r.right, sx, tx);
+ MUL_ADD_STORE(r.top, sy, ty);
+ MUL_ADD_STORE(r.bottom, sy, ty);
+}
+
+void Matrix4::dump() const {
+ LOGD("Matrix4[");
+ LOGD(" %f %f %f %f", data[0], data[4], data[ 8], data[12]);
+ LOGD(" %f %f %f %f", data[1], data[5], data[ 9], data[13]);
+ LOGD(" %f %f %f %f", data[2], data[6], data[10], data[14]);
+ LOGD(" %f %f %f %f", data[3], data[7], data[11], data[15]);
+ LOGD("]");
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
new file mode 100644
index 0000000..b8a4da7
--- /dev/null
+++ b/libs/hwui/Matrix.h
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_MATRIX_H
+#define ANDROID_UI_MATRIX_H
+
+#include <SkMatrix.h>
+
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Classes
+///////////////////////////////////////////////////////////////////////////////
+
+class Matrix4 {
+public:
+ float data[16];
+
+ Matrix4() {
+ loadIdentity();
+ }
+
+ Matrix4(const float* v) {
+ load(v);
+ }
+
+ Matrix4(const Matrix4& v) {
+ load(v);
+ }
+
+ Matrix4(const SkMatrix& v) {
+ load(v);
+ }
+
+ void loadIdentity();
+
+ void load(const float* v);
+ void load(const Matrix4& v);
+ void load(const SkMatrix& v);
+
+ void loadInverse(const Matrix4& v);
+
+ void loadTranslate(float x, float y, float z);
+ void loadScale(float sx, float sy, float sz);
+ void loadRotate(float angle, float x, float y, float z);
+ void loadMultiply(const Matrix4& u, const Matrix4& v);
+
+ void loadOrtho(float left, float right, float bottom, float top, float near, float far);
+
+ void multiply(const Matrix4& v) {
+ Matrix4 u;
+ u.loadMultiply(*this, v);
+ load(u);
+ }
+
+ void translate(float x, float y, float z) {
+ Matrix4 u;
+ u.loadTranslate(x, y, z);
+ multiply(u);
+ }
+
+ void scale(float sx, float sy, float sz) {
+ Matrix4 u;
+ u.loadScale(sx, sy, sz);
+ multiply(u);
+ }
+
+ void rotate(float angle, float x, float y, float z) {
+ Matrix4 u;
+ u.loadRotate(angle, x, y, z);
+ multiply(u);
+ }
+
+ void copyTo(float* v) const;
+ void copyTo(SkMatrix& v) const;
+
+ /**
+ * Does not apply rotations!
+ */
+ void mapRect(Rect& r) const;
+
+ float getTranslateX();
+ float getTranslateY();
+
+ void dump() const;
+
+private:
+ inline float get(int i, int j) const {
+ return data[i * 4 + j];
+ }
+
+ inline void set(int i, int j, float v) {
+ data[i * 4 + j] = v;
+ }
+}; // class Matrix4
+
+///////////////////////////////////////////////////////////////////////////////
+// Types
+///////////////////////////////////////////////////////////////////////////////
+
+typedef Matrix4 mat4;
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_MATRIX_H
diff --git a/libs/hwui/NOTICE b/libs/hwui/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/libs/hwui/NOTICE
@@ -0,0 +1,190 @@
+
+ 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/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
new file mode 100644
index 0000000..d30d718
--- /dev/null
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -0,0 +1,884 @@
+/*
+ * 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 "OpenGLRenderer"
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <SkCanvas.h>
+#include <SkTypeface.h>
+
+#include <cutils/properties.h>
+#include <utils/Log.h>
+
+#include "OpenGLRenderer.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define DEFAULT_TEXTURE_CACHE_SIZE 20.0f
+#define DEFAULT_LAYER_CACHE_SIZE 10.0f
+#define DEFAULT_PATCH_CACHE_SIZE 100
+#define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
+
+// Converts a number of mega-bytes into bytes
+#define MB(s) s * 1024 * 1024
+
+// Generates simple and textured vertices
+#define SV(x, y) { { x, y } }
+#define FV(x, y, u, v) { { x, y }, { u, v } }
+
+///////////////////////////////////////////////////////////////////////////////
+// Globals
+///////////////////////////////////////////////////////////////////////////////
+
+static const SimpleVertex gDrawColorVertices[] = {
+ SV(0.0f, 0.0f),
+ SV(1.0f, 0.0f),
+ SV(0.0f, 1.0f),
+ SV(1.0f, 1.0f)
+};
+static const GLsizei gDrawColorVertexStride = sizeof(SimpleVertex);
+static const GLsizei gDrawColorVertexCount = 4;
+
+// This array is never used directly but used as a memcpy source in the
+// OpenGLRenderer constructor
+static const TextureVertex gDrawTextureVertices[] = {
+ FV(0.0f, 0.0f, 0.0f, 0.0f),
+ FV(1.0f, 0.0f, 1.0f, 0.0f),
+ FV(0.0f, 1.0f, 0.0f, 1.0f),
+ FV(1.0f, 1.0f, 1.0f, 1.0f)
+};
+static const GLsizei gDrawTextureVertexStride = sizeof(TextureVertex);
+static const GLsizei gDrawTextureVertexCount = 4;
+
+// In this array, the index of each Blender equals the value of the first
+// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode]
+static const Blender gBlends[] = {
+ { SkXfermode::kClear_Mode, GL_ZERO, GL_ZERO },
+ { SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO },
+ { SkXfermode::kDst_Mode, GL_ZERO, GL_ONE },
+ { SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE },
+ { SkXfermode::kSrcIn_Mode, GL_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kDstIn_Mode, GL_ZERO, GL_SRC_ALPHA },
+ { SkXfermode::kSrcOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO },
+ { SkXfermode::kDstOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA },
+ { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA },
+ { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }
+};
+
+static const GLint gTileModes[] = {
+ GL_CLAMP_TO_EDGE, // == SkShader::kClamp_TileMode
+ GL_REPEAT, // == SkShader::kRepeat_Mode
+ GL_MIRRORED_REPEAT // == SkShader::kMirror_TileMode
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+OpenGLRenderer::OpenGLRenderer():
+ mBlend(false), mLastSrcMode(GL_ZERO), mLastDstMode(GL_ZERO),
+ mTextureCache(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
+ mLayerCache(MB(DEFAULT_LAYER_CACHE_SIZE)),
+ mGradientCache(MB(DEFAULT_GRADIENT_CACHE_SIZE)),
+ mPatchCache(DEFAULT_PATCH_CACHE_SIZE) {
+ LOGD("Create OpenGLRenderer");
+
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) {
+ LOGD(" Setting texture cache size to %sMB", property);
+ mTextureCache.setMaxSize(MB(atof(property)));
+ } else {
+ LOGD(" Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE);
+ }
+
+ if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, NULL) > 0) {
+ LOGD(" Setting layer cache size to %sMB", property);
+ mLayerCache.setMaxSize(MB(atof(property)));
+ } else {
+ LOGD(" Using default layer cache size of %.2fMB", DEFAULT_LAYER_CACHE_SIZE);
+ }
+
+ if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, NULL) > 0) {
+ LOGD(" Setting gradient cache size to %sMB", property);
+ mLayerCache.setMaxSize(MB(atof(property)));
+ } else {
+ LOGD(" Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE);
+ }
+
+ mDrawColorProgram = new DrawColorProgram;
+ mDrawTextureProgram = new DrawTextureProgram;
+ mDrawTextProgram = new DrawTextProgram;
+ mDrawLinearGradientProgram = new DrawLinearGradientProgram;
+ mCurrentProgram = mDrawTextureProgram;
+
+ mShader = kShaderNone;
+ mShaderTileX = GL_CLAMP_TO_EDGE;
+ mShaderTileY = GL_CLAMP_TO_EDGE;
+ mShaderMatrix = NULL;
+ mShaderBitmap = NULL;
+
+ mLastTexture = 0;
+
+ memcpy(mDrawTextureVertices, gDrawTextureVertices, sizeof(gDrawTextureVertices));
+}
+
+OpenGLRenderer::~OpenGLRenderer() {
+ LOGD("Destroy OpenGLRenderer");
+
+ mTextureCache.clear();
+ mLayerCache.clear();
+ mGradientCache.clear();
+ mPatchCache.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Setup
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::setViewport(int width, int height) {
+ glViewport(0, 0, width, height);
+
+ mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1);
+
+ mWidth = width;
+ mHeight = height;
+ mFirstSnapshot.height = height;
+}
+
+void OpenGLRenderer::prepare() {
+ mSnapshot = &mFirstSnapshot;
+ mSaveCount = 0;
+
+ glDisable(GL_SCISSOR_TEST);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(0, 0, mWidth, mHeight);
+
+ mSnapshot->clipRect.set(0.0f, 0.0f, mWidth, mHeight);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// State management
+///////////////////////////////////////////////////////////////////////////////
+
+int OpenGLRenderer::getSaveCount() const {
+ return mSaveCount;
+}
+
+int OpenGLRenderer::save(int flags) {
+ return saveSnapshot();
+}
+
+void OpenGLRenderer::restore() {
+ if (mSaveCount == 0) return;
+
+ if (restoreSnapshot()) {
+ setScissorFromClip();
+ }
+}
+
+void OpenGLRenderer::restoreToCount(int saveCount) {
+ if (saveCount <= 0 || saveCount > mSaveCount) return;
+
+ bool restoreClip = false;
+
+ while (mSaveCount != saveCount - 1) {
+ restoreClip |= restoreSnapshot();
+ }
+
+ if (restoreClip) {
+ setScissorFromClip();
+ }
+}
+
+int OpenGLRenderer::saveSnapshot() {
+ mSnapshot = new Snapshot(mSnapshot);
+ 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;
+
+ if (restoreOrtho) {
+ mOrthoMatrix.load(current->orthoMatrix);
+ }
+
+ if (restoreLayer) {
+ composeLayer(current, previous);
+ }
+
+ mSnapshot = previous;
+ mSaveCount--;
+
+ return restoreClip;
+}
+
+void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
+ if (!current->layer) {
+ LOGE("Attempting to compose a layer that does not exist");
+ return;
+ }
+
+ // Unbind current FBO and restore previous one
+ // Most of the time, previous->fbo will be 0 to bind the default buffer
+ glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
+
+ // Restore the clip from the previous snapshot
+ const Rect& clip = previous->clipRect;
+ glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight());
+
+ Layer* layer = current->layer;
+
+ // Compute the correct texture coordinates for the FBO texture
+ // The texture is currently as big as the window but drawn with
+ // a quad of the appropriate size
+ const Rect& rect = layer->layer;
+
+ drawTextureRect(rect.left, rect.top, rect.right, rect.bottom,
+ layer->texture, layer->alpha, layer->mode, layer->blend);
+
+ LayerSize size(rect.getWidth(), rect.getHeight());
+ // Failing to add the layer to the cache should happen only if the
+ // layer is too large
+ if (!mLayerCache.put(size, layer)) {
+ LAYER_LOGD("Deleting layer");
+
+ glDeleteFramebuffers(1, &layer->fbo);
+ glDeleteTextures(1, &layer->texture);
+
+ delete layer;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Layers
+///////////////////////////////////////////////////////////////////////////////
+
+int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom,
+ const SkPaint* p, int flags) {
+ int count = saveSnapshot();
+
+ int alpha = 255;
+ SkXfermode::Mode mode;
+
+ if (p) {
+ alpha = p->getAlpha();
+ const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
+ if (!isMode) {
+ // Assume SRC_OVER
+ mode = SkXfermode::kSrcOver_Mode;
+ }
+ } else {
+ mode = SkXfermode::kSrcOver_Mode;
+ }
+
+ createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags);
+
+ return count;
+}
+
+int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bottom,
+ int alpha, int flags) {
+ int count = saveSnapshot();
+ createLayer(mSnapshot, left, top, right, bottom, alpha, SkXfermode::kSrcOver_Mode, flags);
+ return count;
+}
+
+bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
+ float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) {
+
+ LAYER_LOGD("Requesting layer %fx%f", right - left, bottom - top);
+ LAYER_LOGD("Layer cache size = %d", mLayerCache.getSize());
+
+ GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0;
+ LayerSize size(right - left, bottom - top);
+
+ Layer* layer = mLayerCache.get(size, previousFbo);
+ if (!layer) {
+ return false;
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
+
+ // Clear the FBO
+ glDisable(GL_SCISSOR_TEST);
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glEnable(GL_SCISSOR_TEST);
+
+ // Save the layer in the snapshot
+ snapshot->flags |= Snapshot::kFlagIsLayer;
+ layer->mode = mode;
+ layer->alpha = alpha / 255.0f;
+ layer->layer.set(left, top, right, bottom);
+
+ snapshot->layer = layer;
+ snapshot->fbo = layer->fbo;
+
+ // Creates a new snapshot to draw into the FBO
+ saveSnapshot();
+ // TODO: This doesn't preserve other transformations (check Skia first)
+ mSnapshot->transform.loadTranslate(-left, -top, 0.0f);
+ mSnapshot->setClip(0.0f, 0.0f, right - left, bottom - top);
+ mSnapshot->height = bottom - top;
+ setScissorFromClip();
+
+ mSnapshot->flags = Snapshot::kFlagDirtyOrtho | Snapshot::kFlagClipSet;
+ mSnapshot->orthoMatrix.load(mOrthoMatrix);
+
+ // Change the ortho projection
+ mOrthoMatrix.loadOrtho(0.0f, right - left, bottom - top, 0.0f, 0.0f, 1.0f);
+
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Transforms
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::translate(float dx, float dy) {
+ mSnapshot->transform.translate(dx, dy, 0.0f);
+}
+
+void OpenGLRenderer::rotate(float degrees) {
+ mSnapshot->transform.rotate(degrees, 0.0f, 0.0f, 1.0f);
+}
+
+void OpenGLRenderer::scale(float sx, float sy) {
+ mSnapshot->transform.scale(sx, sy, 1.0f);
+}
+
+void OpenGLRenderer::setMatrix(SkMatrix* matrix) {
+ mSnapshot->transform.load(*matrix);
+}
+
+void OpenGLRenderer::getMatrix(SkMatrix* matrix) {
+ mSnapshot->transform.copyTo(*matrix);
+}
+
+void OpenGLRenderer::concatMatrix(SkMatrix* matrix) {
+ mat4 m(*matrix);
+ mSnapshot->transform.multiply(m);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Clipping
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::setScissorFromClip() {
+ const Rect& clip = mSnapshot->clipRect;
+ glScissor(clip.left, mSnapshot->height - clip.bottom, clip.getWidth(), clip.getHeight());
+}
+
+const Rect& OpenGLRenderer::getClipBounds() {
+ return mSnapshot->getLocalClip();
+}
+
+bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) {
+ Rect r(left, top, right, bottom);
+ mSnapshot->transform.mapRect(r);
+ return !mSnapshot->clipRect.intersects(r);
+}
+
+bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) {
+ bool clipped = mSnapshot->clip(left, top, right, bottom, op);
+ if (clipped) {
+ setScissorFromClip();
+ }
+ return !mSnapshot->clipRect.isEmpty();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Drawing
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::drawBitmap(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)) {
+ return;
+ }
+
+ const Texture* texture = mTextureCache.get(bitmap);
+ drawTextureRect(left, top, right, bottom, texture, paint);
+}
+
+void OpenGLRenderer::drawBitmap(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)) {
+ return;
+ }
+
+ const Texture* texture = mTextureCache.get(bitmap);
+ drawTextureRect(r.left, r.top, r.right, r.bottom, texture, paint);
+}
+
+void OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
+ float srcLeft, float srcTop, float srcRight, float srcBottom,
+ float dstLeft, float dstTop, float dstRight, float dstBottom,
+ const SkPaint* paint) {
+ if (quickReject(dstLeft, dstTop, dstRight, dstBottom)) {
+ return;
+ }
+
+ const Texture* texture = mTextureCache.get(bitmap);
+
+ const float width = texture->width;
+ const float height = texture->height;
+
+ const float u1 = srcLeft / width;
+ const float v1 = srcTop / height;
+ const float u2 = srcRight / width;
+ const float v2 = srcBottom / height;
+
+ resetDrawTextureTexCoords(u1, v1, u2, v2);
+
+ drawTextureRect(dstLeft, dstTop, dstRight, dstBottom, texture, paint);
+
+ resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
+}
+
+void OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
+ float left, float top, float right, float bottom, const SkPaint* paint) {
+ if (quickReject(left, top, right, bottom)) {
+ return;
+ }
+
+ const Texture* texture = mTextureCache.get(bitmap);
+
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
+
+ Patch* mesh = mPatchCache.get(patch);
+ mesh->updateVertices(bitmap, left, top, right, bottom,
+ &patch->xDivs[0], &patch->yDivs[0], patch->numXDivs, patch->numYDivs);
+
+ // Specify right and bottom as +1.0f from left/top to prevent scaling since the
+ // patch mesh already defines the final size
+ drawTextureMesh(left, top, left + 1.0f, top + 1.0f, texture->id, alpha / 255.0f,
+ mode, texture->blend, &mesh->vertices[0].position[0],
+ &mesh->vertices[0].texture[0], mesh->indices, mesh->indicesCount);
+}
+
+void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
+ const Rect& clip = mSnapshot->clipRect;
+ drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true);
+}
+
+void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, const SkPaint* p) {
+ if (quickReject(left, top, right, bottom)) {
+ return;
+ }
+
+ SkXfermode::Mode mode;
+
+ const bool isMode = SkXfermode::IsMode(p->getXfermode(), &mode);
+ if (!isMode) {
+ // Assume SRC_OVER
+ mode = SkXfermode::kSrcOver_Mode;
+ }
+
+ // Skia draws using the color's alpha channel if < 255
+ // Otherwise, it uses the paint's alpha
+ int color = p->getColor();
+ if (((color >> 24) & 0xff) == 255) {
+ color |= p->getAlpha() << 24;
+ }
+
+ drawColorRect(left, top, right, bottom, color, mode);
+}
+
+void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
+ float x, float y, SkPaint* paint) {
+ if (text == NULL || count == 0 || (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
+ return;
+ }
+
+ float length;
+ switch (paint->getTextAlign()) {
+ case SkPaint::kCenter_Align:
+ length = paint->measureText(text, bytesCount);
+ x -= length / 2.0f;
+ break;
+ case SkPaint::kRight_Align:
+ length = paint->measureText(text, bytesCount);
+ x -= length;
+ break;
+ default:
+ break;
+ }
+
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
+
+ uint32_t color = paint->getColor();
+ const GLfloat a = alpha / 255.0f;
+ const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f;
+ const GLfloat g = a * ((color >> 8) & 0xFF) / 255.0f;
+ const GLfloat b = a * ((color ) & 0xFF) / 255.0f;
+
+ mModelView.loadIdentity();
+
+ useProgram(mDrawTextProgram);
+ mDrawTextProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+
+ chooseBlending(true, mode);
+ bindTexture(mFontRenderer.getTexture(), GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE);
+
+ // Always premultiplied
+ glUniform4f(mDrawTextProgram->color, r, g, b, a);
+
+ // TODO: Implement scale properly
+ const Rect& clip = mSnapshot->getLocalClip();
+
+ mFontRenderer.setFont(SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize());
+ mFontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Shaders
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::resetShader() {
+ mShader = OpenGLRenderer::kShaderNone;
+ mShaderKey = NULL;
+ mShaderBlend = false;
+ mShaderTileX = GL_CLAMP_TO_EDGE;
+ mShaderTileY = GL_CLAMP_TO_EDGE;
+}
+
+void OpenGLRenderer::setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tileX,
+ SkShader::TileMode tileY, SkMatrix* matrix, bool hasAlpha) {
+ mShader = OpenGLRenderer::kShaderBitmap;
+ mShaderBlend = hasAlpha;
+ mShaderBitmap = bitmap;
+ mShaderTileX = gTileModes[tileX];
+ mShaderTileY = gTileModes[tileY];
+ mShaderMatrix = matrix;
+}
+
+void OpenGLRenderer::setupLinearGradientShader(SkShader* shader, float* bounds, uint32_t* colors,
+ float* positions, int count, SkShader::TileMode tileMode, SkMatrix* matrix,
+ bool hasAlpha) {
+ // TODO: We should use a struct to describe each shader
+ mShader = OpenGLRenderer::kShaderLinearGradient;
+ mShaderKey = shader;
+ mShaderBlend = hasAlpha;
+ mShaderTileX = gTileModes[tileMode];
+ mShaderTileY = gTileModes[tileMode];
+ mShaderMatrix = matrix;
+ mShaderBounds = bounds;
+ mShaderColors = colors;
+ mShaderPositions = positions;
+ mShaderCount = count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Drawing implementation
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
+ int color, SkXfermode::Mode mode, bool ignoreTransform) {
+ // If a shader is set, preserve only the alpha
+ if (mShader != kShaderNone) {
+ color |= 0x00ffffff;
+ }
+
+ // Render using pre-multiplied alpha
+ const int alpha = (color >> 24) & 0xFF;
+ const GLfloat a = alpha / 255.0f;
+
+ switch (mShader) {
+ case OpenGLRenderer::kShaderBitmap:
+ drawBitmapShader(left, top, right, bottom, a, mode);
+ return;
+ case OpenGLRenderer::kShaderLinearGradient:
+ drawLinearGradientShader(left, top, right, bottom, a, mode);
+ return;
+ default:
+ break;
+ }
+
+ const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f;
+ const GLfloat g = a * ((color >> 8) & 0xFF) / 255.0f;
+ const GLfloat b = a * ((color ) & 0xFF) / 255.0f;
+
+ // Pre-multiplication happens when setting the shader color
+ chooseBlending(alpha < 255 || mShaderBlend, mode);
+
+ mModelView.loadTranslate(left, top, 0.0f);
+ mModelView.scale(right - left, bottom - top, 1.0f);
+
+ if (!useProgram(mDrawColorProgram)) {
+ const GLvoid* p = &gDrawColorVertices[0].position[0];
+ glVertexAttribPointer(mDrawColorProgram->position, 2, GL_FLOAT, GL_FALSE,
+ gDrawColorVertexStride, p);
+ }
+
+ if (!ignoreTransform) {
+ mDrawColorProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+ } else {
+ mat4 identity;
+ mDrawColorProgram->set(mOrthoMatrix, mModelView, identity);
+ }
+
+ glUniform4f(mDrawColorProgram->color, r, g, b, a);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawColorVertexCount);
+}
+
+void OpenGLRenderer::drawLinearGradientShader(float left, float top, float right, float bottom,
+ float alpha, SkXfermode::Mode mode) {
+ Texture* texture = mGradientCache.get(mShaderKey);
+ if (!texture) {
+ SkShader::TileMode tileMode = SkShader::kClamp_TileMode;
+ switch (mShaderTileX) {
+ case GL_REPEAT:
+ tileMode = SkShader::kRepeat_TileMode;
+ break;
+ case GL_MIRRORED_REPEAT:
+ tileMode = SkShader::kMirror_TileMode;
+ break;
+ }
+
+ texture = mGradientCache.addLinearGradient(mShaderKey, mShaderBounds, mShaderColors,
+ mShaderPositions, mShaderCount, tileMode);
+ }
+
+ mModelView.loadTranslate(left, top, 0.0f);
+ mModelView.scale(right - left, bottom - top, 1.0f);
+
+ useProgram(mDrawLinearGradientProgram);
+ mDrawLinearGradientProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+
+ chooseBlending(mShaderBlend || alpha < 1.0f, mode);
+ bindTexture(texture->id, mShaderTileX, mShaderTileY);
+
+ Rect start(mShaderBounds[0], mShaderBounds[1], mShaderBounds[2], mShaderBounds[3]);
+ if (mShaderMatrix) {
+ mat4 shaderMatrix(*mShaderMatrix);
+ shaderMatrix.mapRect(start);
+ }
+ mSnapshot->transform.mapRect(start);
+
+ const float gradientX = start.right - start.left;
+ const float gradientY = start.bottom - start.top;
+
+ mat4 screenSpace(mSnapshot->transform);
+ screenSpace.multiply(mModelView);
+
+ // Always premultiplied
+ glUniform4f(mDrawLinearGradientProgram->color, alpha, alpha, alpha, alpha);
+ glUniform2f(mDrawLinearGradientProgram->start, start.left, start.top);
+ glUniform2f(mDrawLinearGradientProgram->gradient, gradientX, gradientY);
+ glUniform1f(mDrawLinearGradientProgram->gradientLength,
+ 1.0f / (gradientX * gradientX + gradientY * gradientY));
+ glUniformMatrix4fv(mDrawLinearGradientProgram->screenSpace, 1, GL_FALSE,
+ &screenSpace.data[0]);
+
+ glVertexAttribPointer(mDrawLinearGradientProgram->position, 2, GL_FLOAT, GL_FALSE,
+ gDrawTextureVertexStride, &mDrawTextureVertices[0].position[0]);
+
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount);
+}
+
+void OpenGLRenderer::drawBitmapShader(float left, float top, float right, float bottom,
+ float alpha, SkXfermode::Mode mode) {
+ const Texture* texture = mTextureCache.get(mShaderBitmap);
+
+ const float width = texture->width;
+ const float height = texture->height;
+
+ // This could be done in the vertex shader but we have only 4 vertices
+ float u1 = 0.0f;
+ float v1 = 0.0f;
+ float u2 = right - left;
+ float v2 = bottom - top;
+
+ // TODO: If the texture is not pow, use a shader to support repeat/mirror
+ if (mShaderMatrix) {
+ SkMatrix inverse;
+ mShaderMatrix->invert(&inverse);
+ mat4 m(inverse);
+ Rect r(u1, v1, u2, v2);
+ m.mapRect(r);
+
+ u1 = r.left;
+ u2 = r.right;
+ v1 = r.top;
+ v2 = r.bottom;
+ }
+
+ u1 /= width;
+ u2 /= width;
+ v1 /= height;
+ v2 /= height;
+
+ resetDrawTextureTexCoords(u1, v1, u2, v2);
+
+ drawTextureMesh(left, top, right, bottom, texture->id, alpha, mode, texture->blend,
+ &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
+
+ resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
+}
+
+void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
+ const Texture* texture, const SkPaint* paint) {
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
+
+ drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode, texture->blend,
+ &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
+}
+
+void OpenGLRenderer::drawTextureRect(float left, float top, float right, float bottom,
+ GLuint texture, float alpha, SkXfermode::Mode mode, bool blend) {
+ drawTextureMesh(left, top, right, bottom, texture, alpha, mode, blend,
+ &mDrawTextureVertices[0].position[0], &mDrawTextureVertices[0].texture[0], NULL);
+}
+
+void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom,
+ GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
+ GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount) {
+ mModelView.loadTranslate(left, top, 0.0f);
+ mModelView.scale(right - left, bottom - top, 1.0f);
+
+ useProgram(mDrawTextureProgram);
+ mDrawTextureProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+
+ chooseBlending(blend || alpha < 1.0f, mode);
+ bindTexture(texture, mShaderTileX, mShaderTileY);
+
+ // Always premultiplied
+ glUniform4f(mDrawTextureProgram->color, alpha, alpha, alpha, alpha);
+
+ glVertexAttribPointer(mDrawTextureProgram->position, 2, GL_FLOAT, GL_FALSE,
+ gDrawTextureVertexStride, vertices);
+ glVertexAttribPointer(mDrawTextureProgram->texCoords, 2, GL_FLOAT, GL_FALSE,
+ gDrawTextureVertexStride, texCoords);
+
+ if (!indices) {
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, gDrawTextureVertexCount);
+ } else {
+ glDrawElements(GL_TRIANGLES, elementsCount, GL_UNSIGNED_SHORT, indices);
+ }
+}
+
+void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied) {
+ // In theory we should not blend if the mode is Src, but it's rare enough
+ // that it's not worth it
+ blend = blend || mode != SkXfermode::kSrcOver_Mode;
+ if (blend) {
+ if (!mBlend) {
+ glEnable(GL_BLEND);
+ }
+
+ GLenum sourceMode = gBlends[mode].src;
+ GLenum destMode = gBlends[mode].dst;
+ if (!isPremultiplied && sourceMode == GL_ONE) {
+ sourceMode = GL_SRC_ALPHA;
+ }
+
+ if (sourceMode != mLastSrcMode || destMode != mLastDstMode) {
+ glBlendFunc(sourceMode, destMode);
+ mLastSrcMode = sourceMode;
+ mLastDstMode = destMode;
+ }
+ } else if (mBlend) {
+ glDisable(GL_BLEND);
+ }
+ mBlend = blend;
+}
+
+bool OpenGLRenderer::useProgram(const sp<Program>& program) {
+ if (!program->isInUse()) {
+ mCurrentProgram->remove();
+ program->use();
+ mCurrentProgram = program;
+ return false;
+ }
+ return true;
+}
+
+void OpenGLRenderer::resetDrawTextureTexCoords(float u1, float v1, float u2, float v2) {
+ TextureVertex* v = &mDrawTextureVertices[0];
+ TextureVertex::setUV(v++, u1, v1);
+ TextureVertex::setUV(v++, u2, v1);
+ TextureVertex::setUV(v++, u1, v2);
+ TextureVertex::setUV(v++, u2, v2);
+}
+
+void OpenGLRenderer::getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
+ if (paint) {
+ const bool isMode = SkXfermode::IsMode(paint->getXfermode(), mode);
+ if (!isMode) {
+ // Assume SRC_OVER
+ *mode = SkXfermode::kSrcOver_Mode;
+ }
+
+ // Skia draws using the color's alpha channel if < 255
+ // Otherwise, it uses the paint's alpha
+ int color = paint->getColor();
+ *alpha = (color >> 24) & 0xFF;
+ if (*alpha == 255) {
+ *alpha = paint->getAlpha();
+ }
+ } else {
+ *mode = SkXfermode::kSrcOver_Mode;
+ *alpha = 255;
+ }
+}
+
+void OpenGLRenderer::bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT) {
+ if (texture != mLastTexture) {
+ glBindTexture(GL_TEXTURE_2D, texture);
+ mLastTexture = texture;
+ }
+ // TODO: Don't set the texture parameters every time
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
new file mode 100644
index 0000000..248c9c3
--- /dev/null
+++ b/libs/hwui/OpenGLRenderer.h
@@ -0,0 +1,381 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_OPENGL_RENDERER_H
+#define ANDROID_UI_OPENGL_RENDERER_H
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <SkBitmap.h>
+#include <SkMatrix.h>
+#include <SkPaint.h>
+#include <SkRegion.h>
+#include <SkShader.h>
+#include <SkXfermode.h>
+
+#include <utils/RefBase.h>
+#include <utils/ResourceTypes.h>
+
+#include "Extensions.h"
+#include "Matrix.h"
+#include "Program.h"
+#include "Rect.h"
+#include "Snapshot.h"
+#include "TextureCache.h"
+#include "LayerCache.h"
+#include "GradientCache.h"
+#include "PatchCache.h"
+#include "Vertex.h"
+#include "FontRenderer.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Support
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Structure mapping Skia xfermodes to OpenGL blending factors.
+ */
+struct Blender {
+ SkXfermode::Mode mode;
+ GLenum src;
+ GLenum dst;
+}; // struct Blender
+
+///////////////////////////////////////////////////////////////////////////////
+// Renderer
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * OpenGL renderer used to draw accelerated 2D graphics. The API is a
+ * simplified version of Skia's Canvas API.
+ */
+class OpenGLRenderer {
+public:
+ OpenGLRenderer();
+ ~OpenGLRenderer();
+
+ void setViewport(int width, int height);
+ void prepare();
+
+ int getSaveCount() const;
+ int save(int flags);
+ void restore();
+ void restoreToCount(int saveCount);
+
+ int saveLayer(float left, float top, float right, float bottom, const SkPaint* p, int flags);
+ int saveLayerAlpha(float left, float top, float right, float bottom, int alpha, int flags);
+
+ void translate(float dx, float dy);
+ void rotate(float degrees);
+ void scale(float sx, float sy);
+
+ void setMatrix(SkMatrix* matrix);
+ void getMatrix(SkMatrix* matrix);
+ void concatMatrix(SkMatrix* matrix);
+
+ const Rect& getClipBounds();
+ bool quickReject(float left, float top, float right, float bottom);
+ bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
+
+ void drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint);
+ void drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint);
+ void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom,
+ float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint);
+ void drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, float left, float top,
+ float right, float bottom, const SkPaint* paint);
+ void drawColor(int color, SkXfermode::Mode mode);
+ void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);
+
+ void resetShader();
+ void setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tileX, SkShader::TileMode tileY,
+ SkMatrix* matrix, bool hasAlpha);
+ void setupLinearGradientShader(SkShader* shader, float* bounds, uint32_t* colors,
+ float* positions, int count, SkShader::TileMode tileMode,
+ SkMatrix* matrix, bool hasAlpha);
+
+ void drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint);
+
+private:
+ /**
+ * Type of Skia shader in use.
+ */
+ enum ShaderType {
+ kShaderNone,
+ kShaderBitmap,
+ kShaderLinearGradient,
+ kShaderCircularGradient,
+ kShaderSweepGradient
+ };
+
+ /**
+ * 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.
+ *
+ * @return The new save count. This value can be passed to #restoreToCount()
+ */
+ int saveSnapshot();
+
+ /**
+ * Restores the current snapshot; mSnapshot becomes mSnapshot->previous.
+ *
+ * @return True if the clip should be also reapplied by calling
+ * #setScissorFromClip().
+ */
+ bool restoreSnapshot();
+
+ /**
+ * Sets the clipping rectangle using glScissor. The clip is defined by
+ * the current snapshot's clipRect member.
+ */
+ void setScissorFromClip();
+
+ /**
+ * Compose the layer defined in the current snapshot with the layer
+ * defined by the previous snapshot.
+ *
+ * The current snapshot *must* be a layer (flag kFlagIsLayer set.)
+ *
+ * @param curent The current snapshot containing the layer to compose
+ * @param previous The previous snapshot to compose the current layer with
+ */
+ void composeLayer(sp<Snapshot> current, sp<Snapshot> previous);
+
+ /**
+ * Creates a new layer stored in the specified snapshot.
+ *
+ * @param snapshot The snapshot associated with the new layer
+ * @param left The left coordinate of the layer
+ * @param top The top coordinate of the layer
+ * @param right The right coordinate of the layer
+ * @param bottom The bottom coordinate of the layer
+ * @param alpha The translucency of the layer
+ * @param mode The blending mode of the layer
+ * @param flags The layer save flags
+ *
+ * @return True if the layer was successfully created, false otherwise
+ */
+ bool createLayer(sp<Snapshot> snapshot, float left, float top, float right, float bottom,
+ int alpha, SkXfermode::Mode mode, int flags);
+
+ /**
+ * Draws a colored rectangle with the specified color. The specified coordinates
+ * are transformed by the current snapshot's transform matrix.
+ *
+ * @param left The left coordinate of the rectangle
+ * @param top The top coordinate of the rectangle
+ * @param right The right coordinate of the rectangle
+ * @param bottom The bottom coordinate of the rectangle
+ * @param color The rectangle's ARGB color, defined as a packed 32 bits word
+ * @param mode The Skia xfermode to use
+ * @param ignoreTransform True if the current transform should be ignored
+ */
+ void drawColorRect(float left, float top, float right, float bottom,
+ int color, SkXfermode::Mode mode, bool ignoreTransform = false);
+
+ /**
+ * Draws a textured rectangle with the specified texture. The specified coordinates
+ * are transformed by the current snapshot's transform matrix.
+ *
+ * @param left The left coordinate of the rectangle
+ * @param top The top coordinate of the rectangle
+ * @param right The right coordinate of the rectangle
+ * @param bottom The bottom coordinate of the rectangle
+ * @param texture The texture name to map onto the rectangle
+ * @param alpha An additional translucency parameter, between 0.0f and 1.0f
+ * @param mode The blending mode
+ * @param blend True if the texture contains an alpha channel
+ */
+ void drawTextureRect(float left, float top, float right, float bottom, GLuint texture,
+ float alpha, SkXfermode::Mode mode, bool blend);
+
+ /**
+ * Draws a textured rectangle with the specified texture. The specified coordinates
+ * are transformed by the current snapshot's transform matrix.
+ *
+ * @param left The left coordinate of the rectangle
+ * @param top The top coordinate of the rectangle
+ * @param right The right coordinate of the rectangle
+ * @param bottom The bottom coordinate of the rectangle
+ * @param texture The texture to use
+ * @param paint The paint containing the alpha, blending mode, etc.
+ */
+ void drawTextureRect(float left, float top, float right, float bottom,
+ const Texture* texture, const SkPaint* paint);
+
+ /**
+ * Draws a textured mesh with the specified texture. If the indices are omitted, the
+ * mesh is drawn as a simple quad.
+ *
+ * @param left The left coordinate of the rectangle
+ * @param top The top coordinate of the rectangle
+ * @param right The right coordinate of the rectangle
+ * @param bottom The bottom coordinate of the rectangle
+ * @param texture The texture name to map onto the rectangle
+ * @param alpha An additional translucency parameter, between 0.0f and 1.0f
+ * @param mode The blending mode
+ * @param blend True if the texture contains an alpha channel
+ * @param vertices The vertices that define the mesh
+ * @param texCoords The texture coordinates of each vertex
+ * @param indices The indices of the vertices, can be NULL
+ * @param elementsCount The number of elements in the mesh, required by indices
+ */
+ void drawTextureMesh(float left, float top, float right, float bottom, GLuint texture,
+ float alpha, SkXfermode::Mode mode, bool blend,
+ GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount = 0);
+
+ /**
+ * Fills the specified rectangle with the currently set bitmap shader.
+ *
+ * @param left The left coordinate of the rectangle
+ * @param top The top coordinate of the rectangle
+ * @param right The right coordinate of the rectangle
+ * @param bottom The bottom coordinate of the rectangle
+ * @param alpha An additional translucency parameter, between 0.0f and 1.0f
+ * @param mode The blending mode
+ */
+ void drawBitmapShader(float left, float top, float right, float bottom, float alpha,
+ SkXfermode::Mode mode);
+
+ /**
+ * Fills the specified rectangle with the currently set linear gradient shader.
+ *
+ * @param left The left coordinate of the rectangle
+ * @param top The top coordinate of the rectangle
+ * @param right The right coordinate of the rectangle
+ * @param bottom The bottom coordinate of the rectangle
+ * @param alpha An additional translucency parameter, between 0.0f and 1.0f
+ * @param mode The blending mode
+ */
+ void drawLinearGradientShader(float left, float top, float right, float bottom, float alpha,
+ SkXfermode::Mode mode);
+
+ /**
+ * Resets the texture coordinates stored in mDrawTextureVertices. Setting the values
+ * back to default is achieved by calling:
+ *
+ * resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
+ *
+ * @param u1 The left coordinate of the texture
+ * @param v1 The bottom coordinate of the texture
+ * @param u2 The right coordinate of the texture
+ * @param v2 The top coordinate of the texture
+ */
+ void resetDrawTextureTexCoords(float u1, float v1, float u2, float v2);
+
+ /**
+ * Gets the alpha and xfermode out of a paint object. If the paint is null
+ * alpha will be 255 and the xfermode will be SRC_OVER.
+ *
+ * @param paint The paint to extract values from
+ * @param alpha Where to store the resulting alpha
+ * @param mode Where to store the resulting xfermode
+ */
+ inline void getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode);
+
+ /**
+ * Binds the specified texture with the specified wrap modes.
+ */
+ inline void bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT);
+
+ /**
+ * Enable or disable blending as necessary. This function sets the appropriate
+ * blend function based on the specified xfermode.
+ */
+ inline void chooseBlending(bool blend, SkXfermode::Mode mode, bool isPremultiplied = true);
+
+ /**
+ * Use the specified program with the current GL context. If the program is already
+ * in use, it will not be bound again. If it is not in use, the current program is
+ * marked unused and the specified program becomes used and becomes the new
+ * current program.
+ *
+ * @param program The program to use
+ *
+ * @return true If the specified program was already in use, false otherwise.
+ */
+ inline bool useProgram(const sp<Program>& program);
+
+ // Dimensions of the drawing surface
+ int mWidth, mHeight;
+
+ // Matrix used for ortho projection in shaders
+ mat4 mOrthoMatrix;
+
+ // Model-view matrix used to position/size objects
+ mat4 mModelView;
+
+ // Number of saved states
+ int mSaveCount;
+ // Base state
+ Snapshot mFirstSnapshot;
+ // Current state
+ sp<Snapshot> mSnapshot;
+
+ // Shaders
+ sp<Program> mCurrentProgram;
+ sp<DrawColorProgram> mDrawColorProgram;
+ sp<DrawTextureProgram> mDrawTextureProgram;
+ sp<DrawTextProgram> mDrawTextProgram;
+ sp<DrawLinearGradientProgram> mDrawLinearGradientProgram;
+
+ // Used to draw textured quads
+ TextureVertex mDrawTextureVertices[4];
+
+ // Current texture state
+ GLuint mLastTexture;
+
+ // Last known blend state
+ bool mBlend;
+ GLenum mLastSrcMode;
+ GLenum mLastDstMode;
+
+ // Skia shaders
+ ShaderType mShader;
+ SkShader* mShaderKey;
+ bool mShaderBlend;
+ GLenum mShaderTileX;
+ GLenum mShaderTileY;
+ SkMatrix* mShaderMatrix;
+ // Bitmaps
+ SkBitmap* mShaderBitmap;
+ // Gradients
+ float* mShaderBounds;
+ uint32_t* mShaderColors;
+ float* mShaderPositions;
+ int mShaderCount;
+
+ // GL extensions
+ Extensions mExtensions;
+
+ // Font renderer
+ FontRenderer mFontRenderer;
+
+ // Various caches
+ TextureCache mTextureCache;
+ LayerCache mLayerCache;
+ GradientCache mGradientCache;
+ PatchCache mPatchCache;
+}; // class OpenGLRenderer
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_OPENGL_RENDERER_H
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
new file mode 100644
index 0000000..4b6bb37
--- /dev/null
+++ b/libs/hwui/Patch.cpp
@@ -0,0 +1,188 @@
+/*
+ * 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 "OpenGLRenderer"
+
+#include <cstring>
+
+#include <utils/Log.h>
+
+#include "Patch.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+Patch::Patch(const uint32_t xCount, const uint32_t yCount):
+ xCount(xCount + 2), yCount(yCount + 2) {
+ verticesCount = (xCount + 2) * (yCount + 2);
+ vertices = new TextureVertex[verticesCount];
+
+ // 2 triangles per patch, 3 vertices per triangle
+ indicesCount = (xCount + 1) * (yCount + 1) * 2 * 3;
+ indices = new uint16_t[indicesCount];
+
+ const uint32_t xNum = xCount + 1;
+ const uint32_t yNum = yCount + 1;
+
+ uint16_t* startIndices = indices;
+ uint32_t n = 0;
+ for (uint32_t y = 0; y < yNum; y++) {
+ for (uint32_t x = 0; x < xNum; x++) {
+ *startIndices++ = n;
+ *startIndices++ = n + 1;
+ *startIndices++ = n + xNum + 2;
+
+ *startIndices++ = n;
+ *startIndices++ = n + xNum + 2;
+ *startIndices++ = n + xNum + 1;
+
+ n += 1;
+ }
+ n += 1;
+ }
+}
+
+Patch::~Patch() {
+ delete indices;
+ delete vertices;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Vertices management
+///////////////////////////////////////////////////////////////////////////////
+
+void Patch::updateVertices(const SkBitmap* bitmap, float left, float top, float right,
+ float bottom, const int32_t* xDivs, const int32_t* yDivs, const uint32_t width,
+ const uint32_t height) {
+ const uint32_t xStretchCount = (width + 1) >> 1;
+ const uint32_t yStretchCount = (height + 1) >> 1;
+
+ float xStretch = 0;
+ float yStretch = 0;
+ float xStretchTex = 0;
+ float yStretchTex = 0;
+
+ const float meshWidth = right - left;
+
+ const float bitmapWidth = float(bitmap->width());
+ const float bitmapHeight = float(bitmap->height());
+
+ if (xStretchCount > 0) {
+ uint32_t stretchSize = 0;
+ for (uint32_t i = 1; i < width; i += 2) {
+ stretchSize += xDivs[i] - xDivs[i - 1];
+ }
+ xStretchTex = (stretchSize / bitmapWidth) / xStretchCount;
+ const float fixed = bitmapWidth - stretchSize;
+ xStretch = (right - left - fixed) / xStretchCount;
+ }
+
+ if (yStretchCount > 0) {
+ uint32_t stretchSize = 0;
+ for (uint32_t i = 1; i < height; i += 2) {
+ stretchSize += yDivs[i] - yDivs[i - 1];
+ }
+ yStretchTex = (stretchSize / bitmapHeight) / yStretchCount;
+ const float fixed = bitmapHeight - stretchSize;
+ yStretch = (bottom - top - fixed) / yStretchCount;
+ }
+
+ float vy = 0.0f;
+ float ty = 0.0f;
+ TextureVertex* vertex = vertices;
+
+ generateVertices(vertex, 0.0f, 0.0f, xDivs, width, xStretch, xStretchTex,
+ meshWidth, bitmapWidth);
+ vertex += width + 2;
+
+ for (uint32_t y = 0; y < height; y++) {
+ if (y & 1) {
+ vy += yStretch;
+ ty += yStretchTex;
+ } else {
+ const float step = float(yDivs[y]);
+ vy += step;
+ ty += step / bitmapHeight;
+ }
+ generateVertices(vertex, vy, ty, xDivs, width, xStretch, xStretchTex,
+ meshWidth, bitmapWidth);
+ vertex += width + 2;
+ }
+
+ generateVertices(vertex, bottom - top, 1.0f, xDivs, width, xStretch, xStretchTex,
+ meshWidth, bitmapWidth);
+}
+
+inline void Patch::generateVertices(TextureVertex* vertex, float y, float v,
+ const int32_t xDivs[], uint32_t xCount, float xStretch, float xStretchTex,
+ float width, float widthTex) {
+ float vx = 0.0f;
+ float tx = 0.0f;
+
+ TextureVertex::set(vertex, vx, y, tx, v);
+ vertex++;
+
+ for (uint32_t x = 0; x < xCount; x++) {
+ if (x & 1) {
+ vx += xStretch;
+ tx += xStretchTex;
+ } else {
+ const float step = float(xDivs[x]);
+ vx += step;
+ tx += step / widthTex;
+ }
+
+ TextureVertex::set(vertex, vx, y, tx, v);
+ vertex++;
+ }
+
+ TextureVertex::set(vertex, width, y, 1.0f, v);
+ vertex++;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Debug tools
+///////////////////////////////////////////////////////////////////////////////
+
+void Patch::dump() {
+ LOGD("Vertices [");
+ for (uint32_t y = 0; y < yCount; y++) {
+ char buffer[512];
+ buffer[0] = '\0';
+ uint32_t offset = 0;
+ for (uint32_t x = 0; x < xCount; x++) {
+ TextureVertex* v = &vertices[y * xCount + x];
+ offset += sprintf(&buffer[offset], " (%.4f,%.4f)-(%.4f,%.4f)",
+ v->position[0], v->position[1], v->texture[0], v->texture[1]);
+ }
+ LOGD(" [%s ]", buffer);
+ }
+ LOGD("]\nIndices [ ");
+ char buffer[4096];
+ buffer[0] = '\0';
+ uint32_t offset = 0;
+ for (uint32_t i = 0; i < indicesCount; i++) {
+ offset += sprintf(&buffer[offset], "%d ", indices[i]);
+ }
+ LOGD(" %s\n]", buffer);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
new file mode 100644
index 0000000..5d3ad03
--- /dev/null
+++ b/libs/hwui/Patch.h
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_PATCH_H
+#define ANDROID_UI_PATCH_H
+
+#include <sys/types.h>
+
+#include <SkBitmap.h>
+
+#include "Vertex.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Description of a patch.
+ */
+struct PatchDescription {
+ PatchDescription(): xCount(0), yCount(0) { }
+ PatchDescription(const uint32_t xCount, const uint32_t yCount):
+ xCount(xCount), yCount(yCount) { }
+ PatchDescription(const PatchDescription& description):
+ xCount(description.xCount), yCount(description.yCount) { }
+
+ uint32_t xCount;
+ uint32_t yCount;
+
+ bool operator<(const PatchDescription& rhs) const {
+ if (xCount == rhs.xCount) {
+ return yCount < rhs.yCount;
+ }
+ return xCount < rhs.xCount;
+ }
+
+ bool operator==(const PatchDescription& rhs) const {
+ return xCount == rhs.xCount && yCount == rhs.yCount;
+ }
+}; // struct PatchDescription
+
+/**
+ * An OpenGL patch. This contains an array of vertices and an array of
+ * indices to render the vertices.
+ */
+struct Patch {
+ Patch(const uint32_t xCount, const uint32_t yCount);
+ ~Patch();
+
+ void updateVertices(const SkBitmap* bitmap, float left, float top, float right, float bottom,
+ const int32_t* xDivs, const int32_t* yDivs,
+ const uint32_t width, const uint32_t height);
+ void dump();
+
+ uint32_t xCount;
+ uint32_t yCount;
+
+ uint16_t* indices;
+ uint32_t indicesCount;
+
+ TextureVertex* vertices;
+ uint32_t verticesCount;
+
+private:
+ static inline void generateVertices(TextureVertex* vertex, float y, float v,
+ const int32_t xDivs[], uint32_t xCount, float xStretch, float xStretchTex,
+ float width, float widthTex);
+}; // struct Patch
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_PATCH_H
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
new file mode 100644
index 0000000..694e3fd
--- /dev/null
+++ b/libs/hwui/PatchCache.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "OpenGLRenderer"
+
+#include <utils/Log.h>
+#include <utils/ResourceTypes.h>
+
+#include "PatchCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+PatchCache::PatchCache(uint32_t maxEntries): mCache(maxEntries) {
+}
+
+PatchCache::~PatchCache() {
+ clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void PatchCache::operator()(PatchDescription& description, Patch*& mesh) {
+ if (mesh) delete mesh;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+void PatchCache::clear() {
+ mCache.setOnEntryRemovedListener(this);
+ mCache.clear();
+ mCache.setOnEntryRemovedListener(NULL);
+}
+
+Patch* PatchCache::get(const Res_png_9patch* patch) {
+ const uint32_t width = patch->numXDivs;
+ const uint32_t height = patch->numYDivs;
+ const PatchDescription description(width, height);
+
+ Patch* mesh = mCache.get(description);
+ if (!mesh) {
+ PATCH_LOGD("Creating new patch mesh, w=%d h=%d", width, height);
+ mesh = new Patch(width, height);
+ mCache.put(description, mesh);
+ }
+
+ return mesh;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
new file mode 100644
index 0000000..de95087
--- /dev/null
+++ b/libs/hwui/PatchCache.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_PATCH_CACHE_H
+#define ANDROID_UI_PATCH_CACHE_H
+
+#include "Patch.h"
+#include "GenerationCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_PATCHES 0
+
+// Debug
+#if DEBUG_PATCHES
+ #define PATCH_LOGD(...) LOGD(__VA_ARGS__)
+#else
+ #define PATCH_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Cache
+///////////////////////////////////////////////////////////////////////////////
+
+class PatchCache: public OnEntryRemoved<PatchDescription, Patch*> {
+public:
+ PatchCache(uint32_t maxCapacity);
+ ~PatchCache();
+
+ /**
+ * Used as a callback when an entry is removed from the cache.
+ * Do not invoke directly.
+ */
+ void operator()(PatchDescription& description, Patch*& mesh);
+
+ Patch* get(const Res_png_9patch* patch);
+ void clear();
+
+private:
+ GenerationCache<PatchDescription, Patch*> mCache;
+}; // class PatchCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_PATCH_CACHE_H
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
new file mode 100644
index 0000000..6e60808
--- /dev/null
+++ b/libs/hwui/Program.cpp
@@ -0,0 +1,224 @@
+/*
+ * 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 "OpenGLRenderer"
+
+#include "Program.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Shaders
+///////////////////////////////////////////////////////////////////////////////
+
+#define SHADER_SOURCE(name, source) const char* name = #source
+
+#include "shaders/drawColor.vert"
+#include "shaders/drawColor.frag"
+
+#include "shaders/drawTexture.vert"
+#include "shaders/drawTexture.frag"
+
+#include "shaders/drawText.frag"
+
+#include "shaders/drawLinearGradient.vert"
+#include "shaders/drawLinearGradient.frag"
+
+///////////////////////////////////////////////////////////////////////////////
+// Base program
+///////////////////////////////////////////////////////////////////////////////
+
+Program::Program(const char* vertex, const char* fragment) {
+ vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
+ fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
+
+ id = glCreateProgram();
+ glAttachShader(id, vertexShader);
+ glAttachShader(id, fragmentShader);
+ glLinkProgram(id);
+
+ GLint status;
+ glGetProgramiv(id, GL_LINK_STATUS, &status);
+ if (status != GL_TRUE) {
+ GLint infoLen = 0;
+ glGetProgramiv(id, GL_INFO_LOG_LENGTH, &infoLen);
+ if (infoLen > 1) {
+ char* log = (char*) malloc(sizeof(char) * infoLen);
+ glGetProgramInfoLog(id, infoLen, 0, log);
+ LOGE("Error while linking shaders: %s", log);
+ delete log;
+ }
+ glDeleteProgram(id);
+ }
+
+ mUse = false;
+}
+
+Program::~Program() {
+ glDeleteShader(vertexShader);
+ glDeleteShader(fragmentShader);
+ glDeleteProgram(id);
+}
+
+void Program::use() {
+ glUseProgram(id);
+ mUse = true;
+}
+
+void Program::remove() {
+ mUse = false;
+}
+
+int Program::addAttrib(const char* name) {
+ int slot = glGetAttribLocation(id, name);
+ attributes.add(name, slot);
+ return slot;
+}
+
+int Program::getAttrib(const char* name) {
+ return attributes.valueFor(name);
+}
+
+int Program::addUniform(const char* name) {
+ int slot = glGetUniformLocation(id, name);
+ uniforms.add(name, slot);
+ return slot;
+}
+
+int Program::getUniform(const char* name) {
+ return uniforms.valueFor(name);
+}
+
+GLuint Program::buildShader(const char* source, GLenum type) {
+ GLuint shader = glCreateShader(type);
+ glShaderSource(shader, 1, &source, 0);
+ glCompileShader(shader);
+
+ GLint status;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
+ if (status != GL_TRUE) {
+ // Some drivers return wrong values for GL_INFO_LOG_LENGTH
+ // use a fixed size instead
+ GLchar log[512];
+ glGetShaderInfoLog(shader, sizeof(log), 0, &log[0]);
+ LOGE("Error while compiling shader: %s", log);
+ glDeleteShader(shader);
+ }
+
+ return shader;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Draw color
+///////////////////////////////////////////////////////////////////////////////
+
+DrawColorProgram::DrawColorProgram():
+ Program(gDrawColorVertexShader, gDrawColorFragmentShader) {
+ getAttribsAndUniforms();
+}
+
+DrawColorProgram::DrawColorProgram(const char* vertex, const char* fragment):
+ Program(vertex, fragment) {
+ getAttribsAndUniforms();
+}
+
+void DrawColorProgram::getAttribsAndUniforms() {
+ position = addAttrib("position");
+ color = addUniform("color");
+ transform = addUniform("transform");
+}
+
+void DrawColorProgram::set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
+ const mat4& transformMatrix) {
+ mat4 t(projectionMatrix);
+ t.multiply(transformMatrix);
+ t.multiply(modelViewMatrix);
+
+ glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]);
+}
+
+void DrawColorProgram::use() {
+ Program::use();
+ glEnableVertexAttribArray(position);
+}
+
+void DrawColorProgram::remove() {
+ Program::remove();
+ glDisableVertexAttribArray(position);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Draw texture
+///////////////////////////////////////////////////////////////////////////////
+
+DrawTextureProgram::DrawTextureProgram():
+ DrawColorProgram(gDrawTextureVertexShader, gDrawTextureFragmentShader) {
+ texCoords = addAttrib("texCoords");
+ sampler = addUniform("sampler");
+}
+
+DrawTextureProgram::DrawTextureProgram(const char* vertex, const char* fragment):
+ DrawColorProgram(vertex, fragment) {
+ texCoords = addAttrib("texCoords");
+ sampler = addUniform("sampler");
+}
+
+void DrawTextureProgram::use() {
+ DrawColorProgram::use();
+ glActiveTexture(GL_TEXTURE0);
+ glUniform1i(sampler, 0);
+ glEnableVertexAttribArray(texCoords);
+}
+
+void DrawTextureProgram::remove() {
+ DrawColorProgram::remove();
+ glDisableVertexAttribArray(texCoords);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Draw text
+///////////////////////////////////////////////////////////////////////////////
+
+DrawTextProgram::DrawTextProgram():
+ DrawTextureProgram(gDrawTextureVertexShader, gDrawTextFragmentShader) {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Draw linear gradient
+///////////////////////////////////////////////////////////////////////////////
+
+DrawLinearGradientProgram::DrawLinearGradientProgram():
+ DrawColorProgram(gDrawLinearGradientVertexShader, gDrawLinearGradientFragmentShader) {
+ gradient = addUniform("gradient");
+ gradientLength = addUniform("gradientLength");
+ sampler = addUniform("sampler");
+ start = addUniform("start");
+ screenSpace = addUniform("screenSpace");
+}
+
+void DrawLinearGradientProgram::use() {
+ DrawColorProgram::use();
+ glActiveTexture(GL_TEXTURE0);
+ glUniform1i(sampler, 0);
+}
+
+void DrawLinearGradientProgram::remove() {
+ DrawColorProgram::remove();
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
new file mode 100644
index 0000000..824aa05
--- /dev/null
+++ b/libs/hwui/Program.h
@@ -0,0 +1,251 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_PROGRAM_H
+#define ANDROID_UI_PROGRAM_H
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <utils/KeyedVector.h>
+#include <utils/RefBase.h>
+
+#include "Matrix.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A program holds a vertex and a fragment shader. It offers several utility
+ * methods to query attributes and uniforms.
+ */
+class Program: public LightRefBase<Program> {
+public:
+ /**
+ * Creates a new program with the specified vertex and fragment
+ * shaders sources.
+ */
+ Program(const char* vertex, const char* fragment);
+ virtual ~Program();
+
+ /**
+ * Binds this program to the GL context.
+ */
+ virtual void use();
+
+ /**
+ * Marks this program as unused. This will not unbind
+ * the program from the GL context.
+ */
+ virtual void remove();
+
+ /**
+ * Indicates whether this program is currently in use with
+ * the GL context.
+ */
+ inline bool isInUse() const {
+ return mUse;
+ }
+
+protected:
+ /**
+ * Adds an attribute with the specified name.
+ *
+ * @return The OpenGL name of the attribute.
+ */
+ int addAttrib(const char* name);
+ /**
+ * Returns the OpenGL name of the specified attribute.
+ */
+ int getAttrib(const char* name);
+
+ /**
+ * Adds a uniform with the specified name.
+ *
+ * @return The OpenGL name of the uniform.
+ */
+ int addUniform(const char* name);
+ /**
+ * Returns the OpenGL name of the specified uniform.
+ */
+ int getUniform(const char* name);
+
+private:
+ /**
+ * Compiles the specified shader of the specified type.
+ *
+ * @return The name of the compiled shader.
+ */
+ GLuint buildShader(const char* source, GLenum type);
+
+ // Name of the OpenGL program
+ GLuint id;
+
+ // Name of the shaders
+ GLuint vertexShader;
+ GLuint fragmentShader;
+
+ // Keeps track of attributes and uniforms slots
+ KeyedVector<const char*, int> attributes;
+ KeyedVector<const char*, int> uniforms;
+
+ bool mUse;
+}; // class Program
+
+/**
+ * Program used to draw vertices with a simple color. The shaders must
+ * specify the following attributes:
+ * vec4 position, position of the vertex
+ * vec4 color, RGBA color of the vertex
+ *
+ * And the following uniforms:
+ * mat4 projection, the projection matrix
+ * mat4 modelView, the modelView matrix
+ * mat4 transform, an extra transformation matrix
+ */
+class DrawColorProgram: public Program {
+public:
+ DrawColorProgram();
+ DrawColorProgram(const char* vertex, const char* fragment);
+
+ /**
+ * Binds the program with the specified projection, modelView and
+ * transform matrices.
+ */
+ void set(const mat4& projectionMatrix, const mat4& modelViewMatrix,
+ const mat4& transformMatrix);
+
+ /**
+ * Binds this program to the GL context.
+ */
+ virtual void use();
+
+ /**
+ * Marks this program as unused. This will not unbind
+ * the program from the GL context.
+ */
+ virtual void remove();
+
+ /**
+ * Name of the position attribute.
+ */
+ int position;
+
+ /**
+ * Name of the color uniform.
+ */
+ int color;
+
+ /**
+ * Name of the transform uniform.
+ */
+ int transform;
+
+protected:
+ void getAttribsAndUniforms();
+};
+
+/**
+ * Program used to draw textured vertices. In addition to everything that the
+ * DrawColorProgram supports, the following two attributes must be specified:
+ * sampler2D sampler, the texture sampler
+ * vec2 texCoords, the texture coordinates of the vertex
+ */
+class DrawTextureProgram: public DrawColorProgram {
+public:
+ DrawTextureProgram();
+ DrawTextureProgram(const char* vertex, const char* fragment);
+
+ /**
+ * Binds this program to the GL context.
+ */
+ virtual void use();
+
+ /**
+ * Marks this program as unused. This will not unbind
+ * the program from the GL context.
+ */
+ virtual void remove();
+
+ /**
+ * Name of the texture sampler uniform.
+ */
+ int sampler;
+
+ /**
+ * Name of the texture coordinates attribute.
+ */
+ int texCoords;
+};
+
+class DrawTextProgram: public DrawTextureProgram {
+public:
+ DrawTextProgram();
+};
+
+/**
+ * Program used to draw linear gradients. In addition to everything that the
+ * DrawColorProgram supports, the following two attributes must be specified:
+ * vec2 gradient, the vector describing the linear gradient
+ * float gradientLength, the invert of the magnitude of the gradient vector
+ * sampler2D sampler, the texture sampler
+ */
+class DrawLinearGradientProgram: public DrawColorProgram {
+public:
+ DrawLinearGradientProgram();
+
+ /**
+ * Binds this program to the GL context.
+ */
+ virtual void use();
+
+ /**
+ * Marks this program as unused. This will not unbind
+ * the program from the GL context.
+ */
+ virtual void remove();
+
+ /**
+ * Name of the matrix used to compute the screen space coordinates
+ * of the vertices.
+ */
+ int screenSpace;
+
+ /**
+ * Name of the linear gradient start point.
+ */
+ int start;
+
+ /**
+ * Name of the linear gradient vector.
+ */
+ int gradient;
+
+ /**
+ * Name of the inverse of linear gradient vector's magnitude.
+ */
+ int gradientLength;
+
+ /**
+ * Name of the texture sampler uniform.
+ */
+ int sampler;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_PROGRAM_H
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
new file mode 100644
index 0000000..4f20a95
--- /dev/null
+++ b/libs/hwui/Properties.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_PROPERTIES_H
+#define ANDROID_UI_PROPERTIES_H
+
+/**
+ * This file contains the list of system properties used to configure
+ * the OpenGLRenderer.
+ */
+
+// These properties are defined in mega-bytes
+#define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size"
+#define PROPERTY_LAYER_CACHE_SIZE "ro.hwui.layer_cache_size"
+#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
+
+// These properties are defined in pixels
+#define PROPERTY_TEXT_CACHE_WIDTH "ro.hwui.text_cache_width"
+#define PROPERTY_TEXT_CACHE_HEIGHT "ro.hwui.text_cache_height"
+
+#endif // ANDROID_UI_PROPERTIES_H
+
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
new file mode 100644
index 0000000..7be0c34
--- /dev/null
+++ b/libs/hwui/Rect.h
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_RECT_H
+#define ANDROID_UI_RECT_H
+
+#include <utils/Log.h>
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Structs
+///////////////////////////////////////////////////////////////////////////////
+
+struct Rect {
+ float left;
+ float top;
+ float right;
+ float bottom;
+
+ Rect():
+ left(0),
+ top(0),
+ right(0),
+ bottom(0) {
+ }
+
+ Rect(float left, float top, float right, float bottom):
+ left(left),
+ top(top),
+ right(right),
+ bottom(bottom) {
+ }
+
+ Rect(const Rect& r) {
+ set(r);
+ }
+
+ Rect(Rect& r) {
+ set(r);
+ }
+
+ Rect& operator=(const Rect& r) {
+ set(r);
+ return *this;
+ }
+
+ Rect& operator=(Rect& r) {
+ set(r);
+ return *this;
+ }
+
+ friend int operator==(const Rect& a, const Rect& b) {
+ return !memcmp(&a, &b, sizeof(a));
+ }
+
+ friend int operator!=(const Rect& a, const Rect& b) {
+ return memcmp(&a, &b, sizeof(a));
+ }
+
+ bool isEmpty() const {
+ return left >= right || top >= bottom;
+ }
+
+ void setEmpty() {
+ memset(this, 0, sizeof(*this));
+ }
+
+ void set(float left, float top, float right, float bottom) {
+ this->left = left;
+ this->right = right;
+ this->top = top;
+ this->bottom = bottom;
+ }
+
+ void set(const Rect& r) {
+ set(r.left, r.top, r.right, r.bottom);
+ }
+
+ float getWidth() const {
+ return right - left;
+ }
+
+ float getHeight() const {
+ return bottom - top;
+ }
+
+ bool intersects(float left, float top, float right, float bottom) const {
+ return left < right && top < bottom &&
+ this->left < this->right && this->top < this->bottom &&
+ this->left < right && left < this->right &&
+ this->top < bottom && top < this->bottom;
+ }
+
+ bool intersects(const Rect& r) const {
+ return intersects(r.left, r.top, r.right, r.bottom);
+ }
+
+ bool intersect(float left, float top, float right, float bottom) {
+ if (left < right && top < bottom && !this->isEmpty() &&
+ this->left < right && left < this->right &&
+ this->top < bottom && top < this->bottom) {
+
+ if (this->left < left) this->left = left;
+ if (this->top < top) this->top = top;
+ if (this->right > right) this->right = right;
+ if (this->bottom > bottom) this->bottom = bottom;
+
+ return true;
+ }
+ return false;
+ }
+
+ bool intersect(const Rect& r) {
+ return intersect(r.left, r.top, r.right, r.bottom);
+ }
+
+ bool unionWith(const Rect& r) {
+ if (r.left < r.right && r.top < r.bottom) {
+ if (left < right && top < bottom) {
+ if (left > r.left) left = r.left;
+ if (top > r.top) top = r.top;
+ if (right < r.right) right = r.right;
+ if (bottom < r.bottom) bottom = r.bottom;
+ return true;
+ } else {
+ left = r.left;
+ top = r.top;
+ right = r.right;
+ bottom = r.bottom;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void dump() const {
+ LOGD("Rect[l=%f t=%f r=%f b=%f]", left, top, right, bottom);
+ }
+
+}; // struct Rect
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_RECT_H
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
new file mode 100644
index 0000000..cc7fde9
--- /dev/null
+++ b/libs/hwui/Snapshot.h
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_SNAPSHOT_H
+#define ANDROID_UI_SNAPSHOT_H
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <utils/RefBase.h>
+
+#include <SkRegion.h>
+
+#include "Layer.h"
+#include "Matrix.h"
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A snapshot holds information about the current state of the rendering
+ * surface. A snapshot is usually created whenever the user calls save()
+ * and discarded when the user calls restore(). Once a snapshot is created,
+ * it can hold information for deferred rendering.
+ *
+ * Each snapshot has a link to a previous snapshot, indicating the previous
+ * state of the renderer.
+ */
+class Snapshot: public LightRefBase<Snapshot> {
+public:
+ Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0) { }
+
+ /**
+ * Copies the specified snapshot. Only the transform and clip rectangle
+ * are copied. The layer information is set to 0 and the transform is
+ * assumed to be dirty. The specified snapshot is stored as the previous
+ * snapshot.
+ */
+ Snapshot(const sp<Snapshot> s):
+ height(s->height),
+ transform(s->transform),
+ clipRect(s->clipRect),
+ flags(0),
+ previous(s),
+ layer(NULL),
+ fbo(s->fbo),
+ localClip(s->localClip) {
+ }
+
+ /**
+ * Various flags set on #flags.
+ */
+ enum Flags {
+ /**
+ * Indicates that the clip region was modified. When this
+ * snapshot is restored so must the clip.
+ */
+ kFlagClipSet = 0x1,
+ /**
+ * Indicates that this snapshot was created when saving
+ * a new layer.
+ */
+ kFlagIsLayer = 0x2,
+ /**
+ * Indicates that this snapshot has changed the ortho matrix.
+ */
+ kFlagDirtyOrtho = 0x4,
+ /**
+ * Indicates that the local clip should be recomputed.
+ */
+ kFlagDirtyLocalClip = 0x8,
+ };
+
+ /**
+ * Intersects the current clip with the new clip rectangle.
+ */
+ bool clip(float left, float top, float right, float bottom, SkRegion::Op op) {
+ bool clipped = false;
+
+ Rect r(left, top, right, bottom);
+ transform.mapRect(r);
+
+ switch (op) {
+ case SkRegion::kDifference_Op:
+ break;
+ case SkRegion::kIntersect_Op:
+ clipped = clipRect.intersect(r);
+ break;
+ case SkRegion::kUnion_Op:
+ clipped = clipRect.unionWith(r);
+ break;
+ case SkRegion::kXOR_Op:
+ break;
+ case SkRegion::kReverseDifference_Op:
+ break;
+ case SkRegion::kReplace_Op:
+ clipRect.set(r);
+ clipped = true;
+ break;
+ }
+
+ if (clipped) {
+ flags |= Snapshot::kFlagClipSet | Snapshot::kFlagDirtyLocalClip;
+ }
+
+ return clipped;
+ }
+
+ /**
+ * Sets the current clip.
+ */
+ void setClip(float left, float top, float right, float bottom) {
+ clipRect.set(left, top, right, bottom);
+ flags |= Snapshot::kFlagClipSet | Snapshot::kFlagDirtyLocalClip;
+ }
+
+ const Rect& getLocalClip() {
+ if (flags & Snapshot::kFlagDirtyLocalClip) {
+ mat4 inverse;
+ inverse.loadInverse(transform);
+ localClip.set(clipRect);
+ inverse.mapRect(localClip);
+ flags &= ~Snapshot::kFlagDirtyLocalClip;
+ }
+ return localClip;
+ }
+
+ /**
+ * Height of the framebuffer the snapshot is rendering into.
+ */
+ int height;
+
+ /**
+ * Local transformation. Holds the current translation, scale and
+ * rotation values.
+ */
+ mat4 transform;
+
+ /**
+ * Current clip region. The clip is stored in canvas-space coordinates,
+ * (screen-space coordinates in the regular case.)
+ */
+ Rect clipRect;
+
+ /**
+ * Dirty flags.
+ */
+ int flags;
+
+ /**
+ * Previous snapshot.
+ */
+ sp<Snapshot> previous;
+
+ /**
+ * Only set when the flag kFlagIsLayer is set.
+ */
+ Layer* layer;
+ GLuint fbo;
+
+ /**
+ * Contains the previous ortho matrix.
+ */
+ mat4 orthoMatrix;
+
+private:
+ Rect localClip;
+
+}; // class Snapshot
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_SNAPSHOT_H
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
new file mode 100644
index 0000000..d37013d
--- /dev/null
+++ b/libs/hwui/Texture.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_TEXTURE_H
+#define ANDROID_UI_TEXTURE_H
+
+#include <GLES2/gl2.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Represents an OpenGL texture.
+ */
+struct Texture {
+ /**
+ * Name of the texture.
+ */
+ GLuint id;
+ /**
+ * Generation of the backing bitmap,
+ */
+ uint32_t generation;
+ /**
+ * Indicates whether the texture requires blending.
+ */
+ bool blend;
+ /**
+ * Width of the backing bitmap.
+ */
+ uint32_t width;
+ /**
+ * Height of the backing bitmap.
+ */
+ uint32_t height;
+}; // struct Texture
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_TEXTURE_H
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
new file mode 100644
index 0000000..4975edb
--- /dev/null
+++ b/libs/hwui/TextureCache.cpp
@@ -0,0 +1,158 @@
+/*
+ * 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 "OpenGLRenderer"
+
+#include <GLES2/gl2.h>
+
+#include "TextureCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+TextureCache::TextureCache(uint32_t maxByteSize):
+ mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
+ mSize(0), mMaxSize(maxByteSize) {
+ mCache.setOnEntryRemovedListener(this);
+}
+
+TextureCache::~TextureCache() {
+ mCache.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t TextureCache::getSize() {
+ return mSize;
+}
+
+uint32_t TextureCache::getMaxSize() {
+ return mMaxSize;
+}
+
+void TextureCache::setMaxSize(uint32_t maxSize) {
+ mMaxSize = maxSize;
+ while (mSize > mMaxSize) {
+ mCache.removeOldest();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) {
+ if (bitmap) {
+ const uint32_t size = bitmap->rowBytes() * bitmap->height();
+ mSize -= size;
+ }
+
+ if (texture) {
+ glDeleteTextures(1, &texture->id);
+ delete texture;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+Texture* TextureCache::get(SkBitmap* bitmap) {
+ Texture* texture = mCache.get(bitmap);
+ if (!texture) {
+ const uint32_t size = bitmap->rowBytes() * bitmap->height();
+ // Don't even try to cache a bitmap that's bigger than the cache
+ if (size < mMaxSize) {
+ while (mSize + size > mMaxSize) {
+ mCache.removeOldest();
+ }
+ }
+
+ texture = new Texture;
+ generateTexture(bitmap, texture, false);
+
+ if (size < mMaxSize) {
+ mSize += size;
+ mCache.put(bitmap, texture);
+ }
+ } else if (bitmap->getGenerationID() != texture->generation) {
+ generateTexture(bitmap, texture, true);
+ }
+ // TODO: Do something to destroy the texture object if it's too big for the cache
+ return texture;
+}
+
+void TextureCache::remove(SkBitmap* bitmap) {
+ mCache.remove(bitmap);
+}
+
+void TextureCache::clear() {
+ mCache.clear();
+}
+
+void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate) {
+ SkAutoLockPixels alp(*bitmap);
+ if (!bitmap->readyToDraw()) {
+ LOGE("Cannot generate texture from bitmap");
+ return;
+ }
+
+ if (!regenerate) {
+ texture->generation = bitmap->getGenerationID();
+ texture->width = bitmap->width();
+ texture->height = bitmap->height();
+
+ glGenTextures(1, &texture->id);
+ }
+
+ glBindTexture(GL_TEXTURE_2D, texture->id);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
+
+ switch (bitmap->getConfig()) {
+ case SkBitmap::kA8_Config:
+ texture->blend = true;
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, bitmap->getPixels());
+ break;
+ case SkBitmap::kRGB_565_Config:
+ texture->blend = false;
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, bitmap->rowBytesAsPixels(), texture->height, 0,
+ GL_RGB, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
+ break;
+ case SkBitmap::kARGB_8888_Config:
+ texture->blend = !bitmap->isOpaque();
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, bitmap->getPixels());
+ break;
+ default:
+ break;
+ }
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
new file mode 100644
index 0000000..bed1191
--- /dev/null
+++ b/libs/hwui/TextureCache.h
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_TEXTURE_CACHE_H
+#define ANDROID_UI_TEXTURE_CACHE_H
+
+#include <SkBitmap.h>
+
+#include "Texture.h"
+#include "GenerationCache.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A simple LRU texture cache. The cache has a maximum size expressed in bytes.
+ * 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*> {
+public:
+ TextureCache(uint32_t maxByteSize);
+ ~TextureCache();
+
+ /**
+ * Used as a callback when an entry is removed from the cache.
+ * Do not invoke directly.
+ */
+ void operator()(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);
+ /**
+ * Removes the texture associated with the specified bitmap. Returns NULL
+ * if the texture cannot be found. Upon remove the texture is freed.
+ */
+ void remove(SkBitmap* bitmap);
+ /**
+ * Clears the cache. This causes all textures to be deleted.
+ */
+ void clear();
+
+ /**
+ * Sets the maximum size of the cache in bytes.
+ */
+ void setMaxSize(uint32_t maxSize);
+ /**
+ * Returns the maximum size of the cache in bytes.
+ */
+ uint32_t getMaxSize();
+ /**
+ * Returns the current size of the cache in bytes.
+ */
+ uint32_t getSize();
+
+private:
+ /**
+ * Generates the texture from a bitmap into the specified texture structure.
+ *
+ * @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);
+
+ GenerationCache<SkBitmap*, Texture*> mCache;
+
+ uint32_t mSize;
+ uint32_t mMaxSize;
+}; // class TextureCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_TEXTURE_CACHE_H
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
new file mode 100644
index 0000000..ffd0633
--- /dev/null
+++ b/libs/hwui/Vertex.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_UI_VERTEX_H
+#define ANDROID_UI_VERTEX_H
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Simple structure to describe a vertex with a position.
+ * This is used to draw filled rectangles without a texture.
+ */
+struct SimpleVertex {
+ float position[2];
+}; // struct SimpleVertex
+
+/**
+ * Simple structure to describe a vertex with a position and a texture.
+ */
+struct TextureVertex {
+ float position[2];
+ float texture[2];
+
+ 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;
+ }
+
+ static inline void setUV(TextureVertex* vertex, float u, float v) {
+ vertex[0].texture[0] = u;
+ vertex[0].texture[1] = v;
+ }
+}; // struct TextureVertex
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_VERTEX_H
diff --git a/libs/hwui/shaders/drawColor.frag b/libs/hwui/shaders/drawColor.frag
new file mode 100644
index 0000000..1d6cb8b
--- /dev/null
+++ b/libs/hwui/shaders/drawColor.frag
@@ -0,0 +1,11 @@
+SHADER_SOURCE(gDrawColorFragmentShader,
+
+precision mediump float;
+
+uniform vec4 color;
+
+void main(void) {
+ gl_FragColor = color;
+}
+
+);
diff --git a/libs/hwui/shaders/drawColor.vert b/libs/hwui/shaders/drawColor.vert
new file mode 100644
index 0000000..20e2636
--- /dev/null
+++ b/libs/hwui/shaders/drawColor.vert
@@ -0,0 +1,11 @@
+SHADER_SOURCE(gDrawColorVertexShader,
+
+attribute vec4 position;
+
+uniform mat4 transform;
+
+void main(void) {
+ gl_Position = transform * position;
+}
+
+);
diff --git a/libs/hwui/shaders/drawLinearGradient.frag b/libs/hwui/shaders/drawLinearGradient.frag
new file mode 100644
index 0000000..469c662
--- /dev/null
+++ b/libs/hwui/shaders/drawLinearGradient.frag
@@ -0,0 +1,14 @@
+SHADER_SOURCE(gDrawLinearGradientFragmentShader,
+
+precision mediump float;
+
+varying float index;
+
+uniform vec4 color;
+uniform sampler2D sampler;
+
+void main(void) {
+ gl_FragColor = texture2D(sampler, vec2(index, 0.5)) * color;
+}
+
+);
diff --git a/libs/hwui/shaders/drawLinearGradient.vert b/libs/hwui/shaders/drawLinearGradient.vert
new file mode 100644
index 0000000..f5c669b
--- /dev/null
+++ b/libs/hwui/shaders/drawLinearGradient.vert
@@ -0,0 +1,20 @@
+SHADER_SOURCE(gDrawLinearGradientVertexShader,
+
+attribute vec4 position;
+
+uniform float gradientLength;
+uniform vec2 gradient;
+uniform vec2 start;
+uniform mat4 transform;
+uniform mat4 screenSpace;
+
+varying float index;
+
+void main(void) {
+ vec4 location = screenSpace * position;
+ index = dot(location.xy - start, gradient) * gradientLength;
+
+ gl_Position = transform * position;
+}
+
+);
diff --git a/libs/hwui/shaders/drawText.frag b/libs/hwui/shaders/drawText.frag
new file mode 100644
index 0000000..49532c7
--- /dev/null
+++ b/libs/hwui/shaders/drawText.frag
@@ -0,0 +1,14 @@
+SHADER_SOURCE(gDrawTextFragmentShader,
+
+precision mediump float;
+
+varying vec2 outTexCoords;
+
+uniform vec4 color;
+uniform sampler2D sampler;
+
+void main(void) {
+ gl_FragColor = color * texture2D(sampler, outTexCoords).a;
+}
+
+);
diff --git a/libs/hwui/shaders/drawTexture.frag b/libs/hwui/shaders/drawTexture.frag
new file mode 100644
index 0000000..8390d8e
--- /dev/null
+++ b/libs/hwui/shaders/drawTexture.frag
@@ -0,0 +1,14 @@
+SHADER_SOURCE(gDrawTextureFragmentShader,
+
+precision mediump float;
+
+varying vec2 outTexCoords;
+
+uniform vec4 color;
+uniform sampler2D sampler;
+
+void main(void) {
+ gl_FragColor = texture2D(sampler, outTexCoords) * color;
+}
+
+);
diff --git a/libs/hwui/shaders/drawTexture.vert b/libs/hwui/shaders/drawTexture.vert
new file mode 100644
index 0000000..240aebf
--- /dev/null
+++ b/libs/hwui/shaders/drawTexture.vert
@@ -0,0 +1,15 @@
+SHADER_SOURCE(gDrawTextureVertexShader,
+
+attribute vec4 position;
+attribute vec2 texCoords;
+
+uniform mat4 transform;
+
+varying vec2 outTexCoords;
+
+void main(void) {
+ outTexCoords = texCoords;
+ gl_Position = transform * position;
+}
+
+);