summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Romain Guy <romainguy@google.com> 2013-02-26 19:10:14 -0800
committer Romain Guy <romainguy@google.com> 2013-02-27 15:49:57 -0800
commitc74f45a334f0e3725c23cdd270cbcb0efac4ea75 (patch)
tree1b198e8b6b7d6d4a69237116a580236836744294
parentd80806b305ce337283c24f14522cc58fea090b8c (diff)
Properly scale text
This change does not apply to drawPosText() and drawTextOnPath() yet. Prior to this change, glyphs were always rasterized based on the font size specified in the paint. All transforms were then applied on the resulting texture. This creates rather ugly results when text is scaled and/or rotated. With this change, the font renderer will apply the current transform matrix to the glyph before they are rasterized. This generates much better looking results. Change-Id: I0141b6ff18db35e1213e7a3ab9db1ecaf03d7a9c
-rw-r--r--libs/hwui/Matrix.cpp5
-rw-r--r--libs/hwui/Matrix.h2
-rw-r--r--libs/hwui/OpenGLRenderer.cpp46
-rw-r--r--libs/hwui/OpenGLRenderer.h3
-rw-r--r--libs/hwui/font/Font.cpp71
-rw-r--r--libs/hwui/font/Font.h3
-rw-r--r--tests/HwAccelerationTest/AndroidManifest.xml3
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/ScaledTextActivity.java22
8 files changed, 94 insertions, 61 deletions
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 5cec5a83c9a2..2d017dfd0691 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -39,6 +39,11 @@ static const float EPSILON = 0.0000001f;
// Matrix
///////////////////////////////////////////////////////////////////////////////
+const Matrix4& Matrix4::identity() {
+ static Matrix4 sIdentity;
+ return sIdentity;
+}
+
void Matrix4::loadIdentity() {
data[kScaleX] = 1.0f;
data[kSkewY] = 0.0f;
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 46a5597ebec4..2fe96bc8c0df 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -152,6 +152,8 @@ public:
void dump() const;
+ static const Matrix4& identity();
+
private:
mutable uint32_t mType;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index d683c272bdcc..fb77ef610fd0 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1657,13 +1657,13 @@ void OpenGLRenderer::setupDrawModelViewTranslate(float left, float top, float ri
mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
} else {
- mCaches.currentProgram->set(mOrthoMatrix, mModelView, mIdentity);
+ mCaches.currentProgram->set(mOrthoMatrix, mModelView, mat4::identity());
if (mTrackDirtyRegions) dirtyLayer(left, top, right, bottom);
}
}
void OpenGLRenderer::setupDrawModelViewIdentity(bool offset) {
- mCaches.currentProgram->set(mOrthoMatrix, mIdentity, *mSnapshot->transform, offset);
+ mCaches.currentProgram->set(mOrthoMatrix, mat4::identity(), *mSnapshot->transform, offset);
}
void OpenGLRenderer::setupDrawModelView(float left, float top, float right, float bottom,
@@ -1681,7 +1681,7 @@ void OpenGLRenderer::setupDrawModelView(float left, float top, float right, floa
dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
}
} else {
- mCaches.currentProgram->set(mOrthoMatrix, mModelView, mIdentity);
+ mCaches.currentProgram->set(mOrthoMatrix, mModelView, mat4::identity());
if (mTrackDirtyRegions && dirty) dirtyLayer(left, top, right, bottom);
}
}
@@ -1716,7 +1716,7 @@ void OpenGLRenderer::setupDrawShaderUniforms(bool ignoreTransform) {
void OpenGLRenderer::setupDrawShaderIdentityUniforms() {
if (mDrawModifiers.mShader) {
mDrawModifiers.mShader->setupProgram(mCaches.currentProgram,
- mIdentity, *mSnapshot, &mTextureUnit);
+ mat4::identity(), *mSnapshot, &mTextureUnit);
}
}
@@ -2587,7 +2587,7 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count
}
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
- fontRenderer.setFont(paint, *mSnapshot->transform);
+ fontRenderer.setFont(paint, mat4::identity());
int alpha;
SkXfermode::Mode mode;
@@ -2663,17 +2663,10 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
return DrawGlInfo::kStatusDone;
}
-#if DEBUG_GLYPHS
- ALOGD("OpenGLRenderer drawText() with FontID=%d",
- SkTypeface::UniqueID(paint->getTypeface()));
-#endif
-
- FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
- fontRenderer.setFont(paint, *mSnapshot->transform);
-
const float oldX = x;
const float oldY = y;
const bool pureTranslate = mSnapshot->transform->isPureTranslate();
+
if (CC_LIKELY(pureTranslate)) {
x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f);
y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
@@ -2683,16 +2676,19 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
+ FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
+
if (CC_UNLIKELY(mDrawModifiers.mHasShadow)) {
- drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, alpha, mode,
- oldX, oldY);
+ fontRenderer.setFont(paint, mat4::identity());
+ drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
+ alpha, mode, oldX, oldY);
}
+ fontRenderer.setFont(paint, pureTranslate ? mat4::identity() : *mSnapshot->transform);
+
// Pick the appropriate texture filtering
- bool linearFilter = mSnapshot->transform->changesBounds();
- if (pureTranslate && !linearFilter) {
- linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
- }
+ bool linearFilter = !mSnapshot->transform->isPureTranslate() ||
+ fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f;
// The font renderer will always use texture unit 0
mCaches.activeTexture(0);
@@ -2705,17 +2701,16 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
setupDrawShader();
setupDrawBlending(true, mode);
setupDrawProgram();
- setupDrawModelView(x, y, x, y, pureTranslate, true);
+ setupDrawModelView(x, y, x, y, true, true);
// See comment above; the font renderer must use texture unit 0
// assert(mTextureUnit == 0)
setupDrawTexture(fontRenderer.getTexture(linearFilter));
setupDrawPureColorUniforms();
setupDrawColorFilterUniforms();
- setupDrawShaderUniforms(pureTranslate);
+ setupDrawShaderUniforms(true);
setupDrawTextGammaUniforms();
- const Rect* clip = pureTranslate ? mSnapshot->clipRect :
- (mSnapshot->hasPerspectiveTransform() ? NULL : &mSnapshot->getLocalClip());
+ const Rect* clip = mSnapshot->hasPerspectiveTransform() ? NULL : mSnapshot->clipRect;
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
const bool hasActiveLayer = hasLayer();
@@ -2732,9 +2727,6 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
}
if (status && hasActiveLayer) {
- if (!pureTranslate) {
- mSnapshot->transform->mapRect(bounds);
- }
dirtyLayerUnchecked(bounds, getRegion());
}
@@ -2750,7 +2742,7 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co
}
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
- fontRenderer.setFont(paint, *mSnapshot->transform);
+ fontRenderer.setFont(paint, mat4::identity());
int alpha;
SkXfermode::Mode mode;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 3f472649f1af..cfad59389d7b 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -936,9 +936,6 @@ private:
// List of layers to update at the beginning of a frame
Vector<Layer*> mLayerUpdates;
- // Indentity matrix
- const mat4 mIdentity;
-
// Indicates whether the clip must be restored
bool mDirtyClip;
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index 1a75ea8d272f..ea9fd031e7eb 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -52,6 +52,10 @@ Font::FontDescription::FontDescription(const SkPaint* paint, const mat4& matrix)
mStyle = paint->getStyle();
mStrokeWidth = paint->getStrokeWidth();
mAntiAliasing = paint->isAntiAlias();
+ mLookupTransform[SkMatrix::kMScaleX] = matrix.data[mat4::kScaleX];
+ mLookupTransform[SkMatrix::kMScaleY] = matrix.data[mat4::kScaleY];
+ mLookupTransform[SkMatrix::kMSkewX] = matrix.data[mat4::kSkewX];
+ mLookupTransform[SkMatrix::kMSkewY] = matrix.data[mat4::kSkewY];
}
Font::~Font() {
@@ -71,6 +75,10 @@ hash_t Font::FontDescription::hash() const {
hash = JenkinsHashMix(hash, android::hash_type(mStyle));
hash = JenkinsHashMix(hash, android::hash_type(mStrokeWidth));
hash = JenkinsHashMix(hash, int(mAntiAliasing));
+ hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleX]));
+ hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleY]));
+ hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMSkewX]));
+ hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMSkewY]));
return JenkinsHashWhiten(hash);
}
@@ -100,6 +108,26 @@ int Font::FontDescription::compare(const Font::FontDescription& lhs,
deltaInt = int(lhs.mAntiAliasing) - int(rhs.mAntiAliasing);
if (deltaInt != 0) return deltaInt;
+ if (lhs.mLookupTransform[SkMatrix::kMScaleX] <
+ rhs.mLookupTransform[SkMatrix::kMScaleX]) return -1;
+ if (lhs.mLookupTransform[SkMatrix::kMScaleX] >
+ rhs.mLookupTransform[SkMatrix::kMScaleX]) return +1;
+
+ if (lhs.mLookupTransform[SkMatrix::kMScaleY] <
+ rhs.mLookupTransform[SkMatrix::kMScaleY]) return -1;
+ if (lhs.mLookupTransform[SkMatrix::kMScaleY] >
+ rhs.mLookupTransform[SkMatrix::kMScaleY]) return +1;
+
+ if (lhs.mLookupTransform[SkMatrix::kMSkewX] <
+ rhs.mLookupTransform[SkMatrix::kMSkewX]) return -1;
+ if (lhs.mLookupTransform[SkMatrix::kMSkewX] >
+ rhs.mLookupTransform[SkMatrix::kMSkewX]) return +1;
+
+ if (lhs.mLookupTransform[SkMatrix::kMSkewY] <
+ rhs.mLookupTransform[SkMatrix::kMSkewY]) return -1;
+ if (lhs.mLookupTransform[SkMatrix::kMSkewY] >
+ rhs.mLookupTransform[SkMatrix::kMSkewY]) return +1;
+
return 0;
}
@@ -169,12 +197,6 @@ void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
int32_t bX = 0, bY = 0;
for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
-#if DEBUG_FONT_RENDERER
- if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
- ALOGE("Skipping invalid index");
- continue;
- }
-#endif
uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
bitmap[bY * bitmapW + bX] = tempCol;
}
@@ -226,16 +248,16 @@ CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool pre
ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
if (index >= 0) {
cachedGlyph = mCachedGlyphs.valueAt(index);
+
+ // Is the glyph still in texture cache?
+ if (!cachedGlyph->mIsValid) {
+ const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit, &mDescription.mLookupTransform);
+ updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
+ }
} else {
cachedGlyph = cacheGlyph(paint, textUnit, precaching);
}
- // Is the glyph still in texture cache?
- if (!cachedGlyph->mIsValid) {
- const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit, NULL);
- updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
- }
-
return cachedGlyph;
}
@@ -357,22 +379,14 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len
// If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
if (cachedGlyph->mIsValid) {
- int penX = x + positions[(glyphsCount << 1)];
- int penY = y + positions[(glyphsCount << 1) + 1];
-
- switch (align) {
- case SkPaint::kRight_Align:
- penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
- penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
- break;
- case SkPaint::kCenter_Align:
- penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
- penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
- default:
- break;
+ float penX = x + positions[(glyphsCount << 1)];
+ float penY = y + positions[(glyphsCount << 1) + 1];
+
+ if (!mTransform.isIdentity()) {
+ mTransform.mapPoint(penX, penY);
}
- (*this.*render)(cachedGlyph, penX, penY,
+ (*this.*render)(cachedGlyph, roundf(penX), roundf(penY),
bitmap, bitmapW, bitmapH, bounds, positions);
}
@@ -394,7 +408,7 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp
// Get the bitmap for the glyph
if (!skiaGlyph.fImage) {
- paint->findImage(skiaGlyph, NULL);
+ paint->findImage(skiaGlyph, &mDescription.mLookupTransform);
}
mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
@@ -425,7 +439,7 @@ CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching
CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
mCachedGlyphs.add(glyph, newGlyph);
- const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph, NULL);
+ const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph, &mDescription.mLookupTransform);
newGlyph->mGlyphIndex = skiaGlyph.fID;
newGlyph->mIsValid = false;
@@ -439,6 +453,7 @@ Font* Font::create(FontRenderer* state, const SkPaint* paint, const mat4& matrix
Font* font = state->mActiveFonts.get(description);
if (font) {
+ font->mTransform.load(matrix);
return font;
}
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
index 7a64a67881b1..26b10afd4be8 100644
--- a/libs/hwui/font/Font.h
+++ b/libs/hwui/font/Font.h
@@ -69,6 +69,7 @@ public:
uint8_t mStyle;
float mStrokeWidth;
bool mAntiAliasing;
+ SkMatrix mLookupTransform;
};
~Font();
@@ -136,6 +137,8 @@ private:
// Cache of glyphs
DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
+
+ mat4 mTransform;
};
inline int strictly_order_type(const Font::FontDescription& lhs,
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 2a9016b84cc8..7c7d10e02a82 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -34,7 +34,8 @@
<activity
android:name="ScaledTextActivity"
- android:label="_ScaledText">
+ android:label="_ScaledText"
+ android:theme="@android:style/Theme.Holo.Light">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ScaledTextActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ScaledTextActivity.java
index e1bf3ea072a3..a4e9b52f4290 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ScaledTextActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ScaledTextActivity.java
@@ -54,6 +54,7 @@ public class ScaledTextActivity extends Activity {
public ScaledTextView(Context c) {
super(c);
+ setLayerType(LAYER_TYPE_HARDWARE, null);
mPath = makePath();
@@ -92,11 +93,28 @@ public class ScaledTextActivity extends Activity {
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
- canvas.drawARGB(255, 255, 255, 255);
canvas.drawText(TEXT, 30.0f, 30.0f, mPaint);
+ mPaint.setTextAlign(Paint.Align.CENTER);
+ canvas.drawText(TEXT, 30.0f, 50.0f, mPaint);
+ mPaint.setTextAlign(Paint.Align.RIGHT);
+ canvas.drawText(TEXT, 30.0f, 70.0f, mPaint);
- canvas.translate(0.0f, 50.0f);
+ canvas.save();
+ canvas.translate(400.0f, 0.0f);
+ canvas.scale(3.0f, 3.0f);
+ mPaint.setTextAlign(Paint.Align.LEFT);
+ mPaint.setStrikeThruText(true);
+ canvas.drawText(TEXT, 30.0f, 30.0f, mPaint);
+ mPaint.setStrikeThruText(false);
+ mPaint.setTextAlign(Paint.Align.CENTER);
+ canvas.drawText(TEXT, 30.0f, 50.0f, mPaint);
+ mPaint.setTextAlign(Paint.Align.RIGHT);
+ canvas.drawText(TEXT, 30.0f, 70.0f, mPaint);
+ canvas.restore();
+
+ mPaint.setTextAlign(Paint.Align.LEFT);
+ canvas.translate(0.0f, 100.0f);
canvas.save();
canvas.scale(mScale, mScale);