Merge "Support 3D rotations when drawing text" into jb-mr2-dev

This commit is contained in:
Romain Guy
2013-03-01 00:05:51 +00:00
committed by Android (Google) Code Review
7 changed files with 166 additions and 35 deletions

View File

@@ -406,7 +406,7 @@ status_t DisplayListRenderer::drawTextOnPath(const char* text, int bytesCount, i
if (addDrawOp(op)) {
// precache if draw operation is visible
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
fontRenderer.precache(paint, text, count, *mSnapshot->transform);
fontRenderer.precache(paint, text, count, mat4::identity());
}
return DrawGlInfo::kStatusDone;
}
@@ -423,7 +423,7 @@ status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int
if (addDrawOp(op)) {
// precache if draw operation is visible
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
fontRenderer.precache(paint, text, count, *mSnapshot->transform);
fontRenderer.precache(paint, text, count, mat4::identity());
}
return DrawGlInfo::kStatusDone;
}
@@ -442,7 +442,9 @@ status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int cou
if (addDrawOp(op)) {
// precache if draw operation is visible
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
fontRenderer.precache(paint, text, count, *mSnapshot->transform);
const bool pureTranslate = mSnapshot->transform->isPureTranslate();
fontRenderer.precache(paint, text, count,
pureTranslate ? mat4::identity() : *mSnapshot->transform);
}
return DrawGlInfo::kStatusDone;
}

View File

@@ -180,7 +180,17 @@ CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
uint32_t* retOriginX, uint32_t* retOriginY, bool precaching) {
checkInit();
// If the glyph bitmap is empty let's assum the glyph is valid
// so we can avoid doing extra work later on
if (glyph.fWidth == 0 || glyph.fHeight == 0) {
cachedGlyph->mIsValid = true;
cachedGlyph->mCacheTexture = NULL;
return;
}
cachedGlyph->mIsValid = false;
// If the glyph is too tall, don't cache it
if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {

View File

@@ -2151,17 +2151,17 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const
alpha *= mSnapshot->alpha;
mCaches.activeTexture(0);
Texture* texture = mCaches.textureCache.get(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
texture->setWrap(GL_CLAMP_TO_EDGE, true);
texture->setFilter(GL_LINEAR, true);
const Patch* mesh = mCaches.patchCache.get(bitmap->width(), bitmap->height(),
right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors);
if (CC_LIKELY(mesh && mesh->verticesCount > 0)) {
mCaches.activeTexture(0);
Texture* texture = mCaches.textureCache.get(bitmap);
if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture);
texture->setWrap(GL_CLAMP_TO_EDGE, true);
texture->setFilter(GL_LINEAR, true);
const bool pureTranslate = mSnapshot->transform->isPureTranslate();
// Mark the current layer dirty where we are going to draw the patch
if (hasLayer() && mesh->hasEmptyQuads) {
@@ -2666,6 +2666,7 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
const float oldX = x;
const float oldY = y;
const bool pureTranslate = mSnapshot->transform->isPureTranslate();
const bool isPerspective = mSnapshot->transform->isPerspective();
if (CC_LIKELY(pureTranslate)) {
x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
@@ -2687,8 +2688,7 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
fontRenderer.setFont(paint, pureTranslate ? mat4::identity() : *mSnapshot->transform);
// Pick the appropriate texture filtering
bool linearFilter = !mSnapshot->transform->isPureTranslate() ||
fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
bool linearFilter = !pureTranslate || fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
// The font renderer will always use texture unit 0
mCaches.activeTexture(0);
@@ -2701,13 +2701,13 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
setupDrawShader();
setupDrawBlending(true, mode);
setupDrawProgram();
setupDrawModelView(x, y, x, y, true, true);
setupDrawModelView(x, y, x, y, !isPerspective, true);
// See comment above; the font renderer must use texture unit 0
// assert(mTextureUnit == 0)
setupDrawTexture(fontRenderer.getTexture(linearFilter));
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms();
setupDrawShaderUniforms(true);
setupDrawShaderUniforms(!isPerspective);
setupDrawTextGammaUniforms();
const Rect* clip = mSnapshot->hasPerspectiveTransform() ? NULL : mSnapshot->clipRect;
@@ -2727,6 +2727,9 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
}
if (status && hasActiveLayer) {
if (isPerspective) {
mSnapshot->transform->mapRect(bounds);
}
dirtyLayerUnchecked(bounds, getRegion());
}

View File

@@ -52,6 +52,7 @@ Font::FontDescription::FontDescription(const SkPaint* paint, const mat4& matrix)
mStyle = paint->getStyle();
mStrokeWidth = paint->getStrokeWidth();
mAntiAliasing = paint->isAntiAlias();
mLookupTransform.reset();
mLookupTransform[SkMatrix::kMScaleX] = matrix.data[mat4::kScaleX];
mLookupTransform[SkMatrix::kMScaleY] = matrix.data[mat4::kScaleY];
mLookupTransform[SkMatrix::kMSkewX] = matrix.data[mat4::kSkewX];
@@ -165,7 +166,7 @@ void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
float nPenX = x + glyph->mBitmapLeft;
float nPenY = y + (glyph->mBitmapTop + glyph->mBitmapHeight);
float nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
float width = (float) glyph->mBitmapWidth;
float height = (float) glyph->mBitmapHeight;
@@ -181,6 +182,38 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
}
void Font::drawCachedGlyphPerspective(CachedGlyphInfo* glyph, int x, int y,
uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
SkMatrix i;
if (!mDescription.mLookupTransform.invert(&i)) {
return;
}
SkPoint p[4];
p[0].set(glyph->mBitmapLeft, glyph->mBitmapTop + glyph->mBitmapHeight);
p[1].set(glyph->mBitmapLeft + glyph->mBitmapWidth, glyph->mBitmapTop + glyph->mBitmapHeight);
p[2].set(glyph->mBitmapLeft + glyph->mBitmapWidth, glyph->mBitmapTop);
p[3].set(glyph->mBitmapLeft, glyph->mBitmapTop);
i.mapPoints(p, 4);
p[0].offset(x, y);
p[1].offset(x, y);
p[2].offset(x, y);
p[3].offset(x, y);
float u1 = glyph->mBitmapMinU;
float u2 = glyph->mBitmapMaxU;
float v1 = glyph->mBitmapMinV;
float v2 = glyph->mBitmapMaxV;
mState->appendRotatedMeshQuad(
p[0].fX, p[0].fY, u1, v2,
p[1].fX, p[1].fY, u2, v2,
p[2].fX, p[2].fY, u2, v1,
p[3].fX, p[3].fY, u1, v1, glyph->mCacheTexture);
}
void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
int nPenX = x + glyph->mBitmapLeft;
@@ -307,7 +340,7 @@ void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len
penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
prevRsbDelta = cachedGlyph->mRsbDelta;
if (cachedGlyph->mIsValid) {
if (cachedGlyph->mIsValid && cachedGlyph->mCacheTexture) {
drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
}
@@ -328,7 +361,6 @@ void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t le
}
void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
if (numGlyphs == 0 || text == NULL) {
return;
}
@@ -357,14 +389,18 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len
static RenderGlyph gRenderGlyph[] = {
&android::uirenderer::Font::drawCachedGlyph,
&android::uirenderer::Font::drawCachedGlyphPerspective,
&android::uirenderer::Font::drawCachedGlyphBitmap,
&android::uirenderer::Font::drawCachedGlyphBitmap,
&android::uirenderer::Font::measureCachedGlyph,
&android::uirenderer::Font::measureCachedGlyph
};
RenderGlyph render = gRenderGlyph[mode];
RenderGlyph render = gRenderGlyph[(mode << 1) + mTransform.isPerspective()];
text += start;
int glyphsCount = 0;
const bool applyTransform = !mTransform.isIdentity() && !mTransform.isPerspective();
const SkPaint::Align align = paint->getTextAlign();
while (glyphsCount < numGlyphs) {
@@ -377,12 +413,13 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len
CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
// If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
if (cachedGlyph->mIsValid) {
// If it's still not valid, we couldn't cache it, so we shouldn't
// draw garbage; also skip empty glyphs (spaces)
if (cachedGlyph->mIsValid && cachedGlyph->mCacheTexture) {
float penX = x + positions[(glyphsCount << 1)];
float penY = y + positions[(glyphsCount << 1) + 1];
if (!mTransform.isIdentity()) {
if (applyTransform) {
mTransform.mapPoint(penX, penY);
}
@@ -424,15 +461,18 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp
glyph->mBitmapWidth = skiaGlyph.fWidth;
glyph->mBitmapHeight = skiaGlyph.fHeight;
uint32_t cacheWidth = glyph->mCacheTexture->getWidth();
uint32_t cacheHeight = glyph->mCacheTexture->getHeight();
bool empty = skiaGlyph.fWidth == 0 || skiaGlyph.fHeight == 0;
if (!empty) {
uint32_t cacheWidth = glyph->mCacheTexture->getWidth();
uint32_t cacheHeight = glyph->mCacheTexture->getHeight();
glyph->mBitmapMinU = startX / (float) cacheWidth;
glyph->mBitmapMinV = startY / (float) cacheHeight;
glyph->mBitmapMaxU = endX / (float) cacheWidth;
glyph->mBitmapMaxV = endY / (float) cacheHeight;
glyph->mBitmapMinU = startX / (float) cacheWidth;
glyph->mBitmapMinV = startY / (float) cacheHeight;
glyph->mBitmapMaxU = endX / (float) cacheWidth;
glyph->mBitmapMaxV = endY / (float) cacheHeight;
mState->setTextureDirty();
mState->setTextureDirty();
}
}
CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
@@ -440,8 +480,8 @@ CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching
mCachedGlyphs.add(glyph, newGlyph);
const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph, &mDescription.mLookupTransform);
newGlyph->mGlyphIndex = skiaGlyph.fID;
newGlyph->mIsValid = false;
newGlyph->mGlyphIndex = skiaGlyph.fID;
updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
@@ -452,14 +492,13 @@ Font* Font::create(FontRenderer* state, const SkPaint* paint, const mat4& matrix
FontDescription description(paint, matrix);
Font* font = state->mActiveFonts.get(description);
if (font) {
font->mTransform.load(matrix);
return font;
if (!font) {
font = new Font(state, description);
state->mActiveFonts.put(description, font);
}
font->mTransform.load(matrix);
Font* newFont = new Font(state, description);
state->mActiveFonts.put(description, newFont);
return newFont;
return font;
}
}; // namespace uirenderer

View File

@@ -124,6 +124,9 @@ private:
void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
Rect* bounds, const float* pos);
void drawCachedGlyphPerspective(CachedGlyphInfo* glyph, int x, int y,
uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
Rect* bounds, const float* pos);
void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
Rect* bounds, const float* pos);

View File

@@ -50,6 +50,16 @@
</intent-filter>
</activity>
<activity
android:name="Rotate3dTextActivity"
android:label="Text/3D Rotation"
android:theme="@android:style/Theme.Holo.Light">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.test.hwui.TEST" />
</intent-filter>
</activity>
<activity
android:name="NoAATextActivity"
android:label="Text/Aliased">

View File

@@ -0,0 +1,64 @@
/*
* 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.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 Rotate3dTextActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Rotate3dTextView view = new Rotate3dTextView(this);
setContentView(view);
}
public static class Rotate3dTextView extends View {
private static final String TEXT = "Hello libhwui! ";
private final Paint mPaint;
public Rotate3dTextView(Context c) {
super(c);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setTextSize(50.0f);
mPaint.setTextAlign(Paint.Align.CENTER);
setRotationY(45.0f);
setScaleX(2.0f);
setScaleY(2.0f);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText(TEXT, getWidth() / 2.0f, getHeight() / 2.0f, mPaint);
invalidate();
}
}
}