Merge "Support 3D rotations when drawing text" into jb-mr2-dev
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user