diff options
Diffstat (limited to 'libs/hwui')
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; +} + +); |