Merge "Add plumbing for better text scaling"

This commit is contained in:
Romain Guy
2013-01-08 15:26:45 -08:00
committed by Android (Google) Code Review
12 changed files with 264 additions and 233 deletions

View File

@ -1775,7 +1775,7 @@ status_t DisplayListRenderer::drawTextOnPath(const char* text, int bytesCount, i
paint->setAntiAlias(true); paint->setAntiAlias(true);
SkPaint* addedPaint = addPaint(paint); SkPaint* addedPaint = addPaint(paint);
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint); FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
fontRenderer.precache(addedPaint, text, count); fontRenderer.precache(addedPaint, text, count, *mSnapshot->transform);
return DrawGlInfo::kStatusDone; return DrawGlInfo::kStatusDone;
} }
@ -1789,7 +1789,7 @@ status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int
paint->setAntiAlias(true); paint->setAntiAlias(true);
SkPaint* addedPaint = addPaint(paint); SkPaint* addedPaint = addPaint(paint);
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint); FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
fontRenderer.precache(addedPaint, text, count); fontRenderer.precache(addedPaint, text, count, *mSnapshot->transform);
return DrawGlInfo::kStatusDone; return DrawGlInfo::kStatusDone;
} }
@ -1823,7 +1823,7 @@ status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int cou
SkPaint* addedPaint = addPaint(paint); SkPaint* addedPaint = addPaint(paint);
if (!reject) { if (!reject) {
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint); FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
fontRenderer.precache(addedPaint, text, count); fontRenderer.precache(addedPaint, text, count, *mSnapshot->transform);
} }
addFloat(length); addFloat(length);
addSkip(location); addSkip(location);

View File

@ -36,7 +36,9 @@ namespace uirenderer {
static bool sLogFontRendererCreate = true; static bool sLogFontRendererCreate = true;
FontRenderer::FontRenderer() { FontRenderer::FontRenderer() :
mActiveFonts(LruCache<Font::FontDescription, Font*>::kUnlimitedCapacity) {
if (sLogFontRendererCreate) { if (sLogFontRendererCreate) {
INIT_LOGD("Creating FontRenderer"); INIT_LOGD("Creating FontRenderer");
} }
@ -107,10 +109,11 @@ FontRenderer::~FontRenderer() {
delete[] mTextMesh; delete[] mTextMesh;
} }
Vector<Font*> fontsToDereference = mActiveFonts; LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
for (uint32_t i = 0; i < fontsToDereference.size(); i++) { while (it.next()) {
delete fontsToDereference[i]; delete it.value();
} }
mActiveFonts.clear();
} }
void FontRenderer::flushAllAndInvalidate() { void FontRenderer::flushAllAndInvalidate() {
@ -118,8 +121,9 @@ void FontRenderer::flushAllAndInvalidate() {
issueDrawCommand(); issueDrawCommand();
} }
for (uint32_t i = 0; i < mActiveFonts.size(); i++) { LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
mActiveFonts[i]->invalidateTextureCache(); while (it.next()) {
it.value()->invalidateTextureCache();
} }
for (uint32_t i = 0; i < mCacheTextures.size(); i++) { for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
@ -146,8 +150,9 @@ void FontRenderer::flushLargeCaches() {
CacheTexture* cacheTexture = mCacheTextures[i]; CacheTexture* cacheTexture = mCacheTextures[i];
if (cacheTexture->getTexture()) { if (cacheTexture->getTexture()) {
cacheTexture->init(); cacheTexture->init();
for (uint32_t j = 0; j < mActiveFonts.size(); j++) { LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
mActiveFonts[j]->invalidateTextureCache(cacheTexture); while (it.next()) {
it.value()->invalidateTextureCache(cacheTexture);
} }
cacheTexture->releaseTexture(); cacheTexture->releaseTexture();
} }
@ -480,22 +485,8 @@ void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
} }
} }
void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { void FontRenderer::setFont(SkPaint* paint, const mat4& matrix) {
int flags = 0; mCurrentFont = Font::create(this, paint, matrix);
if (paint->isFakeBoldText()) {
flags |= Font::kFakeBold;
}
const float skewX = paint->getTextSkewX();
uint32_t italicStyle = *(uint32_t*) &skewX;
const float scaleXFloat = paint->getTextScaleX();
uint32_t scaleX = *(uint32_t*) &scaleXFloat;
SkPaint::Style style = paint->getStyle();
const float strokeWidthFloat = paint->getStrokeWidth();
uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
scaleX, style, strokeWidth);
} }
FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text, FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
@ -561,39 +552,11 @@ void FontRenderer::finishRender() {
} }
} }
void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) { void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix) {
int flags = 0; Font* font = Font::create(this, paint, matrix);
if (paint->isFakeBoldText()) {
flags |= Font::kFakeBold;
}
const float skewX = paint->getTextSkewX();
uint32_t italicStyle = *(uint32_t*) &skewX;
const float scaleXFloat = paint->getTextScaleX();
uint32_t scaleX = *(uint32_t*) &scaleXFloat;
SkPaint::Style style = paint->getStyle();
const float strokeWidthFloat = paint->getStrokeWidth();
uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
float fontSize = paint->getTextSize();
Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
fontSize, flags, italicStyle, scaleX, style, strokeWidth);
font->precache(paint, text, numGlyphs); font->precache(paint, text, numGlyphs);
} }
bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
if (!mCurrentFont) {
ALOGE("No font set");
return false;
}
initRender(clip, bounds);
mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
finishRender();
return mDrawn;
}
bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text, bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
const float* positions, Rect* bounds) { const float* positions, Rect* bounds) {
@ -625,12 +588,7 @@ bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char
} }
void FontRenderer::removeFont(const Font* font) { void FontRenderer::removeFont(const Font* font) {
for (uint32_t ct = 0; ct < mActiveFonts.size(); ct++) { mActiveFonts.remove(font->getDescription());
if (mActiveFonts[ct] == font) {
mActiveFonts.removeAt(ct);
break;
}
}
if (mCurrentFont == font) { if (mCurrentFont == font) {
mCurrentFont = NULL; mCurrentFont = NULL;

View File

@ -17,6 +17,7 @@
#ifndef ANDROID_HWUI_FONT_RENDERER_H #ifndef ANDROID_HWUI_FONT_RENDERER_H
#define ANDROID_HWUI_FONT_RENDERER_H #define ANDROID_HWUI_FONT_RENDERER_H
#include <utils/LruCache.h>
#include <utils/Vector.h> #include <utils/Vector.h>
#include <SkPaint.h> #include <SkPaint.h>
@ -27,6 +28,7 @@
#include "font/CacheTexture.h" #include "font/CacheTexture.h"
#include "font/CachedGlyphInfo.h" #include "font/CachedGlyphInfo.h"
#include "font/Font.h" #include "font/Font.h"
#include "Matrix.h"
#include "Properties.h" #include "Properties.h"
namespace android { namespace android {
@ -47,13 +49,10 @@ public:
mGammaTable = gammaTable; mGammaTable = gammaTable;
} }
void setFont(SkPaint* paint, uint32_t fontId, float fontSize); void setFont(SkPaint* paint, const mat4& matrix);
void precache(SkPaint* paint, const char* text, int numGlyphs); void precache(SkPaint* paint, const char* text, int numGlyphs, const mat4& matrix);
// bounds is an out parameter
bool renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
uint32_t len, int numGlyphs, int x, int y, Rect* bounds);
// bounds is an out parameter // bounds is an out parameter
bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds); uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds);
@ -153,7 +152,7 @@ private:
Vector<CacheTexture*> mCacheTextures; Vector<CacheTexture*> mCacheTextures;
Font* mCurrentFont; Font* mCurrentFont;
Vector<Font*> mActiveFonts; LruCache<Font::FontDescription, Font*> mActiveFonts;
CacheTexture* mCurrentCacheTexture; CacheTexture* mCurrentCacheTexture;

View File

@ -67,6 +67,14 @@ void LayerCache::setMaxSize(uint32_t maxSize) {
// Caching // Caching
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
int LayerCache::LayerEntry::compare(const LayerCache::LayerEntry& lhs,
const LayerCache::LayerEntry& rhs) {
int deltaInt = int(lhs.mWidth) - int(rhs.mWidth);
if (deltaInt != 0) return deltaInt;
return int(lhs.mHeight) - int(rhs.mHeight);
}
void LayerCache::deleteLayer(Layer* layer) { void LayerCache::deleteLayer(Layer* layer) {
if (layer) { if (layer) {
LAYER_LOGD("Destroying layer %dx%d, fbo %d", layer->getWidth(), layer->getHeight(), LAYER_LOGD("Destroying layer %dx%d, fbo %d", layer->getWidth(), layer->getHeight(),

View File

@ -102,9 +102,6 @@ public:
*/ */
void dump(); void dump();
private:
void deleteLayer(Layer* layer);
struct LayerEntry { struct LayerEntry {
LayerEntry(): LayerEntry():
mLayer(NULL), mWidth(0), mHeight(0) { mLayer(NULL), mWidth(0), mHeight(0) {
@ -119,15 +116,14 @@ private:
mLayer(layer), mWidth(layer->getWidth()), mHeight(layer->getHeight()) { mLayer(layer), mWidth(layer->getWidth()), mHeight(layer->getHeight()) {
} }
bool operator<(const LayerEntry& rhs) const { static int compare(const LayerEntry& lhs, const LayerEntry& rhs);
if (mWidth == rhs.mWidth) {
return mHeight < rhs.mHeight; bool operator==(const LayerEntry& other) const {
} return compare(*this, other) == 0;
return mWidth < rhs.mWidth;
} }
bool operator==(const LayerEntry& rhs) const { bool operator!=(const LayerEntry& other) const {
return mWidth == rhs.mWidth && mHeight == rhs.mHeight; return compare(*this, other) != 0;
} }
Layer* mLayer; Layer* mLayer;
@ -135,12 +131,24 @@ private:
uint32_t mHeight; uint32_t mHeight;
}; // struct LayerEntry }; // struct LayerEntry
private:
void deleteLayer(Layer* layer);
SortedList<LayerEntry> mCache; SortedList<LayerEntry> mCache;
uint32_t mSize; uint32_t mSize;
uint32_t mMaxSize; uint32_t mMaxSize;
}; // class LayerCache }; // class LayerCache
inline int strictly_order_type(const LayerCache::LayerEntry& lhs,
const LayerCache::LayerEntry& rhs) {
return LayerCache::LayerEntry::compare(lhs, rhs) < 0;
}
inline int compare_type(const LayerCache::LayerEntry& lhs, const LayerCache::LayerEntry& rhs) {
return LayerCache::LayerEntry::compare(lhs, rhs);
}
}; // namespace uirenderer }; // namespace uirenderer
}; // namespace android }; // namespace android

View File

@ -1716,7 +1716,6 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes
return DrawGlInfo::kStatusDone; return DrawGlInfo::kStatusDone;
} }
// TODO: We should compute the bounding box when recording the display list
float left = FLT_MAX; float left = FLT_MAX;
float top = FLT_MAX; float top = FLT_MAX;
float right = FLT_MIN; float right = FLT_MIN;
@ -1754,7 +1753,6 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes
TextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1); TextureVertex::set(vertex++, vertices[cx], vertices[cy], u2, v1);
TextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2); TextureVertex::set(vertex++, vertices[dx], vertices[dy], u2, v2);
// TODO: This could be optimized to avoid unnecessary ops
left = fminf(left, fminf(vertices[ax], fminf(vertices[bx], vertices[cx]))); left = fminf(left, fminf(vertices[ax], fminf(vertices[bx], vertices[cx])));
top = fminf(top, fminf(vertices[ay], fminf(vertices[by], vertices[cy]))); top = fminf(top, fminf(vertices[ay], fminf(vertices[by], vertices[cy])));
right = fmaxf(right, fmaxf(vertices[ax], fmaxf(vertices[bx], vertices[cx]))); right = fmaxf(right, fmaxf(vertices[ax], fmaxf(vertices[bx], vertices[cx])));
@ -2453,7 +2451,8 @@ status_t OpenGLRenderer::drawArc(float left, float top, float right, float botto
} }
// TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180) // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || p->getStrokeCap() != SkPaint::kButt_Cap || useCenter) { if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 ||
p->getStrokeCap() != SkPaint::kButt_Cap || useCenter) {
mCaches.activeTexture(0); mCaches.activeTexture(0);
const PathTexture* texture = mCaches.arcShapeCache.getArc(right - left, bottom - top, const PathTexture* texture = mCaches.arcShapeCache.getArc(right - left, bottom - top,
startAngle, sweepAngle, useCenter, p); startAngle, sweepAngle, useCenter, p);
@ -2577,16 +2576,15 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count
} }
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint); FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), fontRenderer.setFont(paint, *mSnapshot->transform);
paint->getTextSize());
int alpha; int alpha;
SkXfermode::Mode mode; SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode); getAlphaAndMode(paint, &alpha, &mode);
if (CC_UNLIKELY(mHasShadow)) { if (CC_UNLIKELY(mHasShadow)) {
drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, alpha, mode, drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer,
0.0f, 0.0f); alpha, mode, 0.0f, 0.0f);
} }
// Pick the appropriate texture filtering // Pick the appropriate texture filtering
@ -2655,6 +2653,14 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
return DrawGlInfo::kStatusDone; 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 oldX = x;
const float oldY = y; const float oldY = y;
const bool pureTranslate = mSnapshot->transform->isPureTranslate(); const bool pureTranslate = mSnapshot->transform->isPureTranslate();
@ -2663,15 +2669,6 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f); y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f);
} }
#if DEBUG_GLYPHS
ALOGD("OpenGLRenderer drawText() with FontID=%d",
SkTypeface::UniqueID(paint->getTypeface()));
#endif
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()),
paint->getTextSize());
int alpha; int alpha;
SkXfermode::Mode mode; SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode); getAlphaAndMode(paint, &alpha, &mode);
@ -2744,8 +2741,7 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co
} }
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint); FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), fontRenderer.setFont(paint, *mSnapshot->transform);
paint->getTextSize());
int alpha; int alpha;
SkXfermode::Mode mode; SkXfermode::Mode mode;
@ -2789,7 +2785,6 @@ status_t OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
mCaches.activeTexture(0); mCaches.activeTexture(0);
// TODO: Perform early clip test before we rasterize the path
const PathTexture* texture = mCaches.pathCache.get(path, paint); const PathTexture* texture = mCaches.pathCache.get(path, paint);
if (!texture) return DrawGlInfo::kStatusDone; if (!texture) return DrawGlInfo::kStatusDone;
const AutoTexture autoCleanup(texture); const AutoTexture autoCleanup(texture);

View File

@ -93,9 +93,6 @@ PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
PathCacheEntry entry(path, paint); PathCacheEntry entry(path, paint);
PathTexture* texture = mCache.get(entry); PathTexture* texture = mCache.get(entry);
float left, top, offset;
uint32_t width, height;
if (!texture) { if (!texture) {
texture = addTexture(entry, path, paint); texture = addTexture(entry, path, paint);
} else if (path->getGenerationID() != texture->generation) { } else if (path->getGenerationID() != texture->generation) {

View File

@ -18,6 +18,8 @@
#include <cutils/compiler.h> #include <cutils/compiler.h>
#include <utils/JenkinsHash.h>
#include <SkUtils.h> #include <SkUtils.h>
#include "Debug.h" #include "Debug.h"
@ -33,14 +35,22 @@ namespace uirenderer {
// Font // Font
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
Font::Font(FontRenderer* state, uint32_t fontId, float fontSize, Font::Font(FontRenderer* state, const Font::FontDescription& desc) :
int flags, uint32_t italicStyle, uint32_t scaleX, mState(state), mDescription(desc) {
SkPaint::Style style, uint32_t strokeWidth) :
mState(state), mFontId(fontId), mFontSize(fontSize),
mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
mStyle(style), mStrokeWidth(mStrokeWidth) {
} }
Font::FontDescription::FontDescription(const SkPaint* paint, const mat4& matrix) {
mFontId = SkTypeface::UniqueID(paint->getTypeface());
mFontSize = paint->getTextSize();
mFlags = 0;
if (paint->isFakeBoldText()) {
mFlags |= Font::kFakeBold;
}
mItalicStyle = paint->getTextSkewX();
mScaleX = paint->getTextScaleX();
mStyle = paint->getStyle();
mStrokeWidth = paint->getStrokeWidth();
}
Font::~Font() { Font::~Font() {
mState->removeFont(this); mState->removeFont(this);
@ -50,6 +60,43 @@ Font::~Font() {
} }
} }
hash_t Font::FontDescription::hash() const {
uint32_t hash = JenkinsHashMix(0, mFontId);
hash = JenkinsHashMix(hash, android::hash_type(mFontSize));
hash = JenkinsHashMix(hash, android::hash_type(mFlags));
hash = JenkinsHashMix(hash, android::hash_type(mItalicStyle));
hash = JenkinsHashMix(hash, android::hash_type(mScaleX));
hash = JenkinsHashMix(hash, android::hash_type(mStyle));
hash = JenkinsHashMix(hash, android::hash_type(mStrokeWidth));
return JenkinsHashWhiten(hash);
}
int Font::FontDescription::compare(const Font::FontDescription& lhs,
const Font::FontDescription& rhs) {
int deltaInt = int(lhs.mFontId) - int(rhs.mFontId);
if (deltaInt != 0) return deltaInt;
if (lhs.mFontSize < rhs.mFontSize) return -1;
if (lhs.mFontSize > rhs.mFontSize) return +1;
if (lhs.mItalicStyle < rhs.mItalicStyle) return -1;
if (lhs.mItalicStyle > rhs.mItalicStyle) return +1;
deltaInt = int(lhs.mFlags) - int(rhs.mFlags);
if (deltaInt != 0) return deltaInt;
if (lhs.mScaleX < rhs.mScaleX) return -1;
if (lhs.mScaleX > rhs.mScaleX) return +1;
deltaInt = int(lhs.mStyle) - int(rhs.mStyle);
if (deltaInt != 0) return deltaInt;
if (lhs.mStrokeWidth < rhs.mStrokeWidth) return -1;
if (lhs.mStrokeWidth > rhs.mStrokeWidth) return +1;
return 0;
}
void Font::invalidateTextureCache(CacheTexture* cacheTexture) { void Font::invalidateTextureCache(CacheTexture* cacheTexture) {
for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i); CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
@ -83,17 +130,17 @@ void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
void Font::drawCachedGlyph(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) { uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
int nPenX = x + glyph->mBitmapLeft; float nPenX = x + glyph->mBitmapLeft;
int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight; float nPenY = y + (glyph->mBitmapTop + glyph->mBitmapHeight);
float width = (float) glyph->mBitmapWidth;
float height = (float) glyph->mBitmapHeight;
float u1 = glyph->mBitmapMinU; float u1 = glyph->mBitmapMinU;
float u2 = glyph->mBitmapMaxU; float u2 = glyph->mBitmapMaxU;
float v1 = glyph->mBitmapMinV; float v1 = glyph->mBitmapMinV;
float v2 = glyph->mBitmapMaxV; float v2 = glyph->mBitmapMaxV;
int width = (int) glyph->mBitmapWidth;
int height = (int) glyph->mBitmapHeight;
mState->appendMeshQuad(nPenX, nPenY, u1, v2, mState->appendMeshQuad(nPenX, nPenY, u1, v2,
nPenX + width, nPenY, u2, v2, nPenX + width, nPenY, u2, v2,
nPenX + width, nPenY - height, u2, v1, nPenX + width, nPenY - height, u2, v1,
@ -136,7 +183,7 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float
vOffset += glyph->mBitmapTop + height; vOffset += glyph->mBitmapTop + height;
SkPoint destination[4]; SkPoint destination[4];
measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent); measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
// Move along the tangent and offset by the normal // Move along the tangent and offset by the normal
destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset, destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
@ -176,24 +223,13 @@ CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool pre
// Is the glyph still in texture cache? // Is the glyph still in texture cache?
if (!cachedGlyph->mIsValid) { if (!cachedGlyph->mIsValid) {
const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit); const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit, NULL);
updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching); updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
} }
return cachedGlyph; return cachedGlyph;
} }
void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
bitmapW, bitmapH, NULL, NULL);
} else {
render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
0, 0, NULL, NULL);
}
}
void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
int numGlyphs, int x, int y, const float* positions) { int numGlyphs, int x, int y, const float* positions) {
render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
@ -298,71 +334,40 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len
text += start; text += start;
int glyphsCount = 0; int glyphsCount = 0;
if (CC_LIKELY(positions == NULL)) { const SkPaint::Align align = paint->getTextAlign();
SkFixed prevRsbDelta = 0;
float penX = x + 0.5f; while (glyphsCount < numGlyphs) {
int penY = y; glyph_t glyph = GET_GLYPH(text);
while (glyphsCount < numGlyphs) { // Reached the end of the string
glyph_t glyph = GET_GLYPH(text); if (IS_END_OF_STRING(glyph)) {
break;
// Reached the end of the string
if (IS_END_OF_STRING(glyph)) {
break;
}
CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
prevRsbDelta = cachedGlyph->mRsbDelta;
// If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
if (cachedGlyph->mIsValid) {
(*this.*render)(cachedGlyph, (int) floorf(penX), penY,
bitmap, bitmapW, bitmapH, bounds, positions);
}
penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
glyphsCount++;
} }
} else {
const SkPaint::Align align = paint->getTextAlign();
// This is for renderPosText() CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
while (glyphsCount < numGlyphs) {
glyph_t glyph = GET_GLYPH(text);
// Reached the end of the string // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
if (IS_END_OF_STRING(glyph)) { if (cachedGlyph->mIsValid) {
break; 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;
} }
CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); (*this.*render)(cachedGlyph, penX, penY,
bitmap, bitmapW, bitmapH, bounds, positions);
// 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;
}
(*this.*render)(cachedGlyph, penX, penY,
bitmap, bitmapW, bitmapH, bounds, positions);
}
glyphsCount++;
} }
glyphsCount++;
} }
} }
@ -379,7 +384,7 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp
uint32_t startY = 0; uint32_t startY = 0;
// Get the bitmap for the glyph // Get the bitmap for the glyph
paint->findImage(skiaGlyph); paint->findImage(skiaGlyph, NULL);
mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching); mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
if (!glyph->mIsValid) { if (!glyph->mIsValid) {
@ -409,7 +414,7 @@ CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching
CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
mCachedGlyphs.add(glyph, newGlyph); mCachedGlyphs.add(glyph, newGlyph);
const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph); const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph, NULL);
newGlyph->mGlyphIndex = skiaGlyph.fID; newGlyph->mGlyphIndex = skiaGlyph.fID;
newGlyph->mIsValid = false; newGlyph->mIsValid = false;
@ -418,24 +423,16 @@ CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching
return newGlyph; return newGlyph;
} }
Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize, Font* Font::create(FontRenderer* state, const SkPaint* paint, const mat4& matrix) {
int flags, uint32_t italicStyle, uint32_t scaleX, FontDescription description(paint, matrix);
SkPaint::Style style, uint32_t strokeWidth) { Font* font = state->mActiveFonts.get(description);
Vector<Font*> &activeFonts = state->mActiveFonts;
for (uint32_t i = 0; i < activeFonts.size(); i++) { if (font) {
Font* font = activeFonts[i]; return font;
if (font->mFontId == fontId && font->mFontSize == fontSize &&
font->mFlags == flags && font->mItalicStyle == italicStyle &&
font->mScaleX == scaleX && font->mStyle == style &&
(style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
return font;
}
} }
Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle, Font* newFont = new Font(state, description);
scaleX, style, strokeWidth); state->mActiveFonts.put(description, newFont);
activeFonts.push(newFont);
return newFont; return newFont;
} }

View File

@ -25,6 +25,7 @@
#include "CachedGlyphInfo.h" #include "CachedGlyphInfo.h"
#include "../Rect.h" #include "../Rect.h"
#include "../Matrix.h"
namespace android { namespace android {
namespace uirenderer { namespace uirenderer {
@ -45,31 +46,52 @@ public:
kFakeBold = 1 kFakeBold = 1
}; };
struct FontDescription {
FontDescription(const SkPaint* paint, const mat4& matrix);
static int compare(const FontDescription& lhs, const FontDescription& rhs);
hash_t hash() const;
bool operator==(const FontDescription& other) const {
return compare(*this, other) == 0;
}
bool operator!=(const FontDescription& other) const {
return compare(*this, other) != 0;
}
SkFontID mFontId;
float mFontSize;
int mFlags;
float mItalicStyle;
float mScaleX;
uint8_t mStyle;
float mStrokeWidth;
};
~Font(); ~Font();
/** void render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
* Renders the specified string of text.
* If bitmap is specified, it will be used as the render target
*/
void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
int numGlyphs, int x, int y, uint8_t *bitmap = NULL,
uint32_t bitmapW = 0, uint32_t bitmapH = 0);
void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
int numGlyphs, int x, int y, const float* positions); int numGlyphs, int x, int y, const float* positions);
void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, void render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
int numGlyphs, SkPath* path, float hOffset, float vOffset); int numGlyphs, SkPath* path, float hOffset, float vOffset);
const Font::FontDescription& getDescription() const {
return mDescription;
}
/** /**
* Creates a new font associated with the specified font state. * Creates a new font associated with the specified font state.
*/ */
static Font* create(FontRenderer* state, uint32_t fontId, float fontSize, static Font* create(FontRenderer* state, const SkPaint* paint, const mat4& matrix);
int flags, uint32_t italicStyle, uint32_t scaleX, SkPaint::Style style,
uint32_t strokeWidth);
private: private:
friend class FontRenderer; friend class FontRenderer;
Font(FontRenderer* state, const Font::FontDescription& desc);
typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*, typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
uint32_t, uint32_t, Rect*, const float*); uint32_t, uint32_t, Rect*, const float*);
@ -88,12 +110,6 @@ private:
void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len, void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
int numGlyphs, Rect *bounds, const float* positions); int numGlyphs, Rect *bounds, const float* positions);
Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
// Cache of glyphs
DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
void invalidateTextureCache(CacheTexture* cacheTexture = NULL); void invalidateTextureCache(CacheTexture* cacheTexture = NULL);
CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching); CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching);
@ -115,15 +131,25 @@ private:
CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false); CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false);
FontRenderer* mState; FontRenderer* mState;
uint32_t mFontId; FontDescription mDescription;
float mFontSize;
int mFlags; // Cache of glyphs
uint32_t mItalicStyle; DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
uint32_t mScaleX;
SkPaint::Style mStyle;
uint32_t mStrokeWidth;
}; };
inline int strictly_order_type(const Font::FontDescription& lhs,
const Font::FontDescription& rhs) {
return Font::FontDescription::compare(lhs, rhs) < 0;
}
inline int compare_type(const Font::FontDescription& lhs, const Font::FontDescription& rhs) {
return Font::FontDescription::compare(lhs, rhs);
}
inline hash_t hash_type(const Font::FontDescription& entry) {
return entry.hash();
}
}; // namespace uirenderer }; // namespace uirenderer
}; // namespace android }; // namespace android

View File

@ -37,7 +37,7 @@
#if RENDER_TEXT_AS_GLYPHS #if RENDER_TEXT_AS_GLYPHS
typedef uint16_t glyph_t; typedef uint16_t glyph_t;
#define TO_GLYPH(g) g #define TO_GLYPH(g) g
#define GET_METRICS(paint, glyph) paint->getGlyphMetrics(glyph) #define GET_METRICS(paint, glyph, matrix) paint->getGlyphMetrics(glyph, matrix)
#define GET_GLYPH(text) nextGlyph((const uint16_t**) &text) #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
#define IS_END_OF_STRING(glyph) false #define IS_END_OF_STRING(glyph) false
@ -50,7 +50,7 @@
#else #else
typedef SkUnichar glyph_t; typedef SkUnichar glyph_t;
#define TO_GLYPH(g) ((SkUnichar) g) #define TO_GLYPH(g) ((SkUnichar) g)
#define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph) #define GET_METRICS(paint, glyph, matrix) paint->getUnicharMetrics(glyph, matrix)
#define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text) #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
#define IS_END_OF_STRING(glyph) glyph < 0 #define IS_END_OF_STRING(glyph) glyph < 0
#endif #endif

View File

@ -21,6 +21,7 @@ import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Path;
import android.os.Bundle; import android.os.Bundle;
import android.view.View; import android.view.View;
@ -43,15 +44,27 @@ public class ScaledTextActivity extends Activity {
} }
public static class ScaledTextView extends View { public static class ScaledTextView extends View {
private static final String TEXT = "Hello libhwui! ";
private final Paint mPaint; private final Paint mPaint;
private final Paint mShadowPaint;
private final Path mPath;
private float mScale = 1.0f; private float mScale = 1.0f;
public ScaledTextView(Context c) { public ScaledTextView(Context c) {
super(c); super(c);
mPath = makePath();
mPaint = new Paint(); mPaint = new Paint();
mPaint.setAntiAlias(true); mPaint.setAntiAlias(true);
mPaint.setTextSize(20.0f); mPaint.setTextSize(20.0f);
mShadowPaint = new Paint();
mShadowPaint.setAntiAlias(true);
mShadowPaint.setShadowLayer(3.0f, 0.0f, 3.0f, 0xff000000);
mShadowPaint.setTextSize(20.0f);
} }
public float getTextScale() { public float getTextScale() {
@ -63,17 +76,47 @@ public class ScaledTextActivity extends Activity {
invalidate(); invalidate();
} }
private static Path makePath() {
Path path = new Path();
buildPath(path);
return path;
}
private static void buildPath(Path path) {
path.moveTo(0.0f, 0.0f);
path.cubicTo(0.0f, 0.0f, 100.0f, 150.0f, 100.0f, 200.0f);
path.cubicTo(100.0f, 200.0f, 50.0f, 300.0f, -80.0f, 200.0f);
path.cubicTo(-80.0f, 200.0f, 100.0f, 200.0f, 200.0f, 0.0f);
}
@Override @Override
protected void onDraw(Canvas canvas) { protected void onDraw(Canvas canvas) {
super.onDraw(canvas); super.onDraw(canvas);
canvas.drawARGB(255, 255, 255, 255); canvas.drawARGB(255, 255, 255, 255);
canvas.drawText("Hello libhwui!", 30.0f, 30.0f, mPaint); canvas.drawText(TEXT, 30.0f, 30.0f, mPaint);
canvas.translate(0.0f, 50.0f); canvas.translate(0.0f, 50.0f);
canvas.save(); canvas.save();
canvas.scale(mScale, mScale); canvas.scale(mScale, mScale);
canvas.drawText("Hello libhwui!", 30.0f, 30.0f, mPaint); canvas.drawText(TEXT, 30.0f, 30.0f, mPaint);
canvas.restore(); canvas.restore();
canvas.translate(0.0f, 250.0f);
canvas.save();
canvas.scale(3.0f, 3.0f);
canvas.drawText(TEXT, 30.0f, 30.0f, mShadowPaint);
canvas.translate(100.0f, 0.0f);
// canvas.drawTextOnPath(TEXT + TEXT + TEXT, mPath, 0.0f, 0.0f, mPaint);
canvas.restore();
float width = mPaint.measureText(TEXT);
canvas.translate(500.0f, 0.0f);
canvas.rotate(45.0f, width * 3.0f / 2.0f, 0.0f);
canvas.scale(3.0f, 3.0f);
canvas.drawText(TEXT, 30.0f, 30.0f, mPaint);
} }
} }
} }

View File

@ -41,26 +41,26 @@ public class TextOnPathActivity extends Activity {
setContentView(view); setContentView(view);
} }
private Path makePath() { private static Path makePath() {
Path path = new Path(); Path path = new Path();
buildPath(path); buildPath(path);
return path; return path;
} }
private void buildPath(Path path) { private static void buildPath(Path path) {
path.moveTo(0.0f, 0.0f); path.moveTo(0.0f, 0.0f);
path.cubicTo(0.0f, 0.0f, 100.0f, 150.0f, 100.0f, 200.0f); path.cubicTo(0.0f, 0.0f, 100.0f, 150.0f, 100.0f, 200.0f);
path.cubicTo(100.0f, 200.0f, 50.0f, 300.0f, -80.0f, 200.0f); path.cubicTo(100.0f, 200.0f, 50.0f, 300.0f, -80.0f, 200.0f);
path.cubicTo(-80.0f, 200.0f, 100.0f, 200.0f, 200.0f, 0.0f); path.cubicTo(-80.0f, 200.0f, 100.0f, 200.0f, 200.0f, 0.0f);
} }
private Path makeStraightPath() { private static Path makeStraightPath() {
Path path = new Path(); Path path = new Path();
buildStraightPath(path); buildStraightPath(path);
return path; return path;
} }
private void buildStraightPath(Path path) { private static void buildStraightPath(Path path) {
path.moveTo(0.0f, 0.0f); path.moveTo(0.0f, 0.0f);
path.lineTo(400.0f, 0.0f); path.lineTo(400.0f, 0.0f);
} }