diff options
| -rw-r--r-- | core/java/android/view/GLES20Canvas.java | 21 | ||||
| -rw-r--r-- | core/jni/android_view_GLES20Canvas.cpp | 6 | ||||
| -rw-r--r-- | graphics/java/android/graphics/Paint.java | 11 | ||||
| -rw-r--r-- | libs/hwui/Android.mk | 1 | ||||
| -rw-r--r-- | libs/hwui/FontRenderer.cpp | 467 | ||||
| -rw-r--r-- | libs/hwui/FontRenderer.h | 184 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 37 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.h | 7 | ||||
| -rw-r--r-- | libs/hwui/Program.cpp | 16 | ||||
| -rw-r--r-- | libs/hwui/Program.h | 6 | ||||
| -rw-r--r-- | libs/hwui/shaders/drawText.frag | 14 | ||||
| -rw-r--r-- | tests/HwAccelerationTest/AndroidManifest.xml | 10 | ||||
| -rw-r--r-- | tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java | 59 |
13 files changed, 825 insertions, 14 deletions
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 142463868743..78648ff142f5 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -410,9 +410,21 @@ class GLES20Canvas extends Canvas { public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) { // Shaders are ignored when drawing bitmaps final int nativePaint = paint == null ? 0 : paint.mNativePaint; - nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom, - dst.left, dst.top, dst.right, dst.bottom, nativePaint - ); + + int left, top, right, bottom; + if (src == null) { + left = top = 0; + right = bitmap.getWidth(); + bottom = bitmap.getHeight(); + } else { + left = src.left; + right = src.right; + top = src.top; + bottom = src.bottom; + } + + nDrawBitmap(mRenderer, bitmap.mNativeBitmap, left, top, right, bottom, + dst.left, dst.top, dst.right, dst.bottom, nativePaint); } @Override @@ -420,8 +432,7 @@ class GLES20Canvas extends Canvas { // Shaders are ignored when drawing bitmaps final int nativePaint = paint == null ? 0 : paint.mNativePaint; nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom, - dst.left, dst.top, dst.right, dst.bottom, nativePaint - ); + dst.left, dst.top, dst.right, dst.bottom, nativePaint); } private native void nDrawBitmap(int renderer, int bitmap, diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index fa4d23cff77e..2f1dcb650ff5 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -255,7 +255,8 @@ static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject canvas, OpenGLRenderer* renderer, jcharArray text, int index, int count, jfloat x, jfloat y, int flags, SkPaint* paint) { jchar* textArray = env->GetCharArrayElements(text, NULL); - // TODO: draw from textArray + index + // TODO: Prepare the text for RTL + renderer->drawText((const char*) (textArray + index), count, x, y, paint); env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); } @@ -263,7 +264,8 @@ static void android_view_GLES20Canvas_drawText(JNIEnv* env, jobject canvas, OpenGLRenderer* renderer, jstring text, int start, int end, jfloat x, jfloat y, int flags, SkPaint* paint) { const jchar* textArray = env->GetStringChars(text, NULL); - // TODO: draw from textArray + start + // TODO: Prepare the text for RTL + renderer->drawText((const char*) (textArray + start), end - start, x, y, paint); env->ReleaseStringChars(text, textArray); } diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 3d63aa66aacc..6349cb33bda0 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -931,15 +931,14 @@ public class Paint { } /** - * Temporary API to expose layer drawing. This draws a shadow layer below - * the main layer, with the specified offset and color, and blur radius. - * If radius is 0, then the shadow layer is removed. + * This draws a shadow layer below the main layer, with the specified + * offset and color, and blur radius. If radius is 0, then the shadow + * layer is removed. */ - public native void setShadowLayer(float radius, float dx, float dy, - int color); + public native void setShadowLayer(float radius, float dx, float dy, int color); /** - * Temporary API to clear the shadow layer. + * Clear the shadow layer. */ public void clearShadowLayer() { setShadowLayer(0, 0, 0, 0); diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 1bdb9d3a44e5..172952a8f49d 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -2,6 +2,7 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + FontRenderer.cpp \ GradientCache.cpp \ LayerCache.cpp \ Matrix.cpp \ diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp new file mode 100644 index 000000000000..8557b87ec924 --- /dev/null +++ b/libs/hwui/FontRenderer.cpp @@ -0,0 +1,467 @@ +/* + * 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 "FontRenderer.h" + +#include <SkUtils.h> + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// 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) { + FontRenderer *state = mState; + + int nPenX = x + glyph->mBitmapLeft; + int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight; + + state->appendMeshQuad(nPenX, nPenY, 0, glyph->mBitmapMinU, glyph->mBitmapMaxV, + nPenX + (int) glyph->mBitmapWidth, nPenY, 0, glyph->mBitmapMaxU, glyph->mBitmapMaxV, + nPenX + (int) glyph->mBitmapWidth, nPenY - (int) glyph->mBitmapHeight, + 0, glyph->mBitmapMaxU, glyph->mBitmapMinV, nPenX, nPenY - (int) glyph->mBitmapHeight, + 0, glyph->mBitmapMinU, glyph->mBitmapMinV); +} + +void Font::renderUTF(SkPaint* paint, const char *text, uint32_t len, uint32_t start, 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; + } + + //size_t index = start; + //size_t nextIndex = 0; + + text += start; + + while (glyphsLeft > 0) { + //int32_t utfChar = utf32_at(text, len, index, &nextIndex); + int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text); + + // Reached the end of the string or encountered + if (utfChar < 0) { + break; + } + + // Move to the next character in the array + //index = nextIndex; + + 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); + } + + // TODO: Check how to do this conversion + penX += SkFixedRound(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; + + // Let the font state figure out where to put the bitmap + FontRenderer *state = mState; + // Get the bitmap for the glyph + paint->findImage(skiaGlyph); + glyph->mIsValid = state->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 = state->getCacheWidth(); + uint32_t cacheHeight = state->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; + + state->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 *ithFont = activeFonts[i]; + if (ithFont->mFontId == fontId && ithFont->mFontSize == fontSize) { + return ithFont; + } + } + + Font* newFont = new Font(state, fontId, fontSize); + activeFonts.push(newFont); + return newFont; +} + +/////////////////////////////////////////////////////////////////////////////// +// FontRenderer +/////////////////////////////////////////////////////////////////////////////// + +FontRenderer::FontRenderer() { + mInitialized = false; + mMaxNumberOfQuads = 1024; + mCurrentQuadIndex = 0; + + mIndexBufferID = 0; + + mCacheWidth = 1024; + mCacheHeight = 256; +} + +FontRenderer::~FontRenderer() { + for (uint32_t i = 0; i < mCacheLines.size(); i++) { + delete mCacheLines[i]; + } + mCacheLines.clear(); + + delete mTextTexture; + + 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); + + 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(16, mCacheWidth, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(24, mCacheWidth, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(32, mCacheWidth, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(32, mCacheWidth, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(40, mCacheWidth, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(mCacheHeight - nextLine, mCacheWidth, 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 vertexBufferSizeBytes = mMaxNumberOfQuads * vertsPerQuad * coordSize * + uvSize * sizeof(float); + mTextMeshPtr = (float*) malloc(vertexBufferSizeBytes); +} + +// We don't want to allocate anything unless we actually draw text +void FontRenderer::checkInit() { + if (mInitialized) { + return; + } + + initTextTexture(); + initVertexArrayBuffers(); + + mInitialized = true; +} + +void FontRenderer::issueDrawCommand() { + if (mUploadTexture) { + glBindTexture(GL_TEXTURE_2D, mTextureId); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, GL_ALPHA, + GL_UNSIGNED_BYTE, mTextTexture); + mUploadTexture = false; + } + + 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) { + const uint32_t vertsPerQuad = 4; + const uint32_t floatsPerVert = 5; + float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; + + // TODO: Cull things that are off the screen + // float width = (float)mRSC->getWidth(); + // float height = (float)mRSC->getHeight(); + // + // if(x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) { + // return; + // } + + (*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 char *text, uint32_t len, uint32_t startIndex, + int numGlyphs, int x, int y) { + checkInit(); + + // Render code here + Font *currentFont = mCurrentFont; + if (!currentFont) { + LOGE("Unable to initialize any fonts"); + return; + } + + currentFont->renderUTF(paint, text, len, startIndex, numGlyphs, x, y); + + if (mCurrentQuadIndex != 0) { + issueDrawCommand(); + mCurrentQuadIndex = 0; + } +} + +void FontRenderer::renderText(SkPaint* paint, const char *text, int x, int y) { + size_t textLen = strlen(text); + renderText(paint, text, textLen, 0, -1, x, y); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h new file mode 100644 index 000000000000..c18327a99ee2 --- /dev/null +++ b/libs/hwui/FontRenderer.h @@ -0,0 +1,184 @@ +/* + * 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> + +namespace android { +namespace uirenderer { + +class FontRenderer; + +class Font { +public: + ~Font(); + + // Pointer to the utf data, length of data, where to start, number of glyphs ot read + // (each glyph may be longer than a char because we are dealing with utf data) + // Last two variables are the initial pen position + void renderUTF(SkPaint* paint, const char *text, uint32_t len, uint32_t start, + int numGlyphs, int x, int y); + + static Font* create(FontRenderer* state, uint32_t fontId, float fontSize); + +protected: + + friend class FontRenderer; + + void invalidateTextureCache(); + 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; + }; + + FontRenderer* mState; + uint32_t mFontId; + float mFontSize; + + Font(FontRenderer* state, uint32_t fontId, float fontSize); + + DefaultKeyedVector<int32_t, CachedGlyphInfo*> mCachedGlyphs; + + CachedGlyphInfo *cacheGlyph(SkPaint* paint, int32_t glyph); + void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo *glyph); + void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y); +}; + +class FontRenderer { +public: + FontRenderer(); + ~FontRenderer(); + + void init(); + void deinit(); + + void setFont(uint32_t fontId, float fontSize); + void renderText(SkPaint* paint, const char *text, uint32_t len, uint32_t startIndex, + int numGlyphs, int x, int y); + void renderText(SkPaint* paint, const char *text, 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; + + CacheTextureLine(uint16_t maxHeight, uint16_t maxWidth, uint32_t currentRow, + uint32_t currentCol): + mMaxHeight(maxHeight), mMaxWidth(maxWidth), mCurrentRow(currentRow), + mCurrentCol(currentCol) { + } + + 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; + 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; + + Font* mCurrentFont; + + Vector<CacheTextureLine*> mCacheLines; + + Vector<Font*> mActiveFonts; + + // Texture to cache glyph bitmaps + unsigned char* mTextTexture; + GLuint mTextureId; + bool mUploadTexture; + + // Pointer to vertex data to speed up frame to frame work + float *mTextMeshPtr; + uint32_t mCurrentQuadIndex; + uint32_t mMaxNumberOfQuads; + + uint32_t mIndexBufferID; + + bool mInitialized; +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_UI_FONT_RENDERER_H diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 1fa76d26cfa3..8f04d92c0981 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -21,6 +21,7 @@ #include <sys/types.h> #include <SkCanvas.h> +#include <SkTypeface.h> #include <cutils/properties.h> #include <utils/Log.h> @@ -134,6 +135,7 @@ OpenGLRenderer::OpenGLRenderer(): mDrawColorProgram = new DrawColorProgram; mDrawTextureProgram = new DrawTextureProgram; + mDrawTextProgram = new DrawTextProgram; mDrawLinearGradientProgram = new DrawLinearGradientProgram; mCurrentProgram = mDrawTextureProgram; @@ -527,6 +529,39 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, drawColorRect(left, top, right, bottom, color, mode); } +void OpenGLRenderer::drawText(const char* text, int count, float x, float y, SkPaint* paint) { + // TODO: Support paint's text alignments, proper clipping + if (quickReject(x, y, x + 1, y +1)) { + return; + } + + 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); + + mFontRenderer.setFont(SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize()); + mFontRenderer.renderText(paint, text, count, 0, count, x, y); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + /////////////////////////////////////////////////////////////////////////////// // Shaders /////////////////////////////////////////////////////////////////////////////// @@ -687,6 +722,7 @@ void OpenGLRenderer::drawBitmapShader(float left, float top, float right, float 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); @@ -742,7 +778,6 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b bindTexture(texture, mShaderTileX, mShaderTileY); // Always premultiplied - //glUniform4f(mDrawTextureProgram->color, alpha, alpha, alpha, alpha); glUniform4f(mDrawTextureProgram->color, alpha, alpha, alpha, alpha); glVertexAttribPointer(mDrawTextureProgram->position, 2, GL_FLOAT, GL_FALSE, diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 9dc2a43ecac7..b82366bdc338 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -39,6 +39,7 @@ #include "GradientCache.h" #include "PatchCache.h" #include "Vertex.h" +#include "FontRenderer.h" namespace android { namespace uirenderer { @@ -108,6 +109,8 @@ public: float* positions, int count, SkShader::TileMode tileMode, SkMatrix* matrix, bool hasAlpha); + void drawText(const char* text, int count, float x, float y, SkPaint* paint); + private: /** * Type of Skia shader in use. @@ -329,6 +332,7 @@ private: sp<Program> mCurrentProgram; sp<DrawColorProgram> mDrawColorProgram; sp<DrawTextureProgram> mDrawTextureProgram; + sp<DrawTextProgram> mDrawTextProgram; sp<DrawLinearGradientProgram> mDrawLinearGradientProgram; // Used to draw textured quads @@ -357,6 +361,9 @@ private: float* mShaderPositions; int mShaderCount; + // Font renderer + FontRenderer mFontRenderer; + // Various caches TextureCache mTextureCache; LayerCache mLayerCache; diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp index 841b6c842f9a..6e608084f5bb 100644 --- a/libs/hwui/Program.cpp +++ b/libs/hwui/Program.cpp @@ -33,6 +33,8 @@ namespace uirenderer { #include "shaders/drawTexture.vert" #include "shaders/drawTexture.frag" +#include "shaders/drawText.frag" + #include "shaders/drawLinearGradient.vert" #include "shaders/drawLinearGradient.frag" @@ -169,6 +171,12 @@ DrawTextureProgram::DrawTextureProgram(): 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); @@ -182,6 +190,14 @@ void DrawTextureProgram::remove() { } /////////////////////////////////////////////////////////////////////////////// +// Draw text +/////////////////////////////////////////////////////////////////////////////// + +DrawTextProgram::DrawTextProgram(): + DrawTextureProgram(gDrawTextureVertexShader, gDrawTextFragmentShader) { +} + +/////////////////////////////////////////////////////////////////////////////// // Draw linear gradient /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index 18a8e925460b..824aa0525e44 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -167,6 +167,7 @@ protected: class DrawTextureProgram: public DrawColorProgram { public: DrawTextureProgram(); + DrawTextureProgram(const char* vertex, const char* fragment); /** * Binds this program to the GL context. @@ -190,6 +191,11 @@ public: 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: diff --git a/libs/hwui/shaders/drawText.frag b/libs/hwui/shaders/drawText.frag new file mode 100644 index 000000000000..49532c777a19 --- /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/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 098359c73ec0..8cb9e0dd378f 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -105,6 +105,16 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + + <activity + android:name="TextActivity" + android:label="_Text" + android:theme="@android:style/Theme.NoTitleBar"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> </application> </manifest> diff --git a/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java new file mode 100644 index 000000000000..6665ef52b0c7 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/google/android/test/hwui/TextActivity.java @@ -0,0 +1,59 @@ +/* + * 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. + */ + +package com.google.android.test.hwui; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.os.Bundle; +import android.view.View; + +@SuppressWarnings({"UnusedDeclaration"}) +public class TextActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(new CustomTextView(this)); + } + + static class CustomTextView extends View { + private final Paint mMediumPaint; + private final Paint mLargePaint; + + CustomTextView(Context c) { + super(c); + + mMediumPaint = new Paint(); + mMediumPaint.setAntiAlias(true); + mMediumPaint.setColor(0xffff0000); + mLargePaint = new Paint(); + mLargePaint.setAntiAlias(true); + mLargePaint.setTextSize(36.0f); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + canvas.drawRGB(255, 255, 255); + + canvas.drawText("Hello OpenGL renderer!", 100, 100, mMediumPaint); + canvas.drawText("Hello OpenGL renderer!", 100, 200, mLargePaint); + } + } +}
\ No newline at end of file |