Font.cpp revision cf51a4199835e9604aa4c8b3854306f8fbabbf33
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "OpenGLRenderer" 18#define ATRACE_TAG ATRACE_TAG_VIEW 19 20#include <cutils/compiler.h> 21 22#include <utils/JenkinsHash.h> 23#include <utils/Trace.h> 24 25#include <SkGlyph.h> 26#include <SkUtils.h> 27 28#include "FontUtil.h" 29#include "Font.h" 30#include "../Debug.h" 31#include "../FontRenderer.h" 32#include "../PixelBuffer.h" 33#include "../Properties.h" 34 35namespace android { 36namespace uirenderer { 37 38/////////////////////////////////////////////////////////////////////////////// 39// Font 40/////////////////////////////////////////////////////////////////////////////// 41 42Font::Font(FontRenderer* state, const Font::FontDescription& desc) : 43 mState(state), mDescription(desc) { 44} 45 46Font::FontDescription::FontDescription(const SkPaint* paint, const mat4& matrix) { 47 mFontId = SkTypeface::UniqueID(paint->getTypeface()); 48 mFontSize = paint->getTextSize(); 49 mFlags = 0; 50 if (paint->isFakeBoldText()) { 51 mFlags |= Font::kFakeBold; 52 } 53 mItalicStyle = paint->getTextSkewX(); 54 mScaleX = paint->getTextScaleX(); 55 mStyle = paint->getStyle(); 56 mStrokeWidth = paint->getStrokeWidth(); 57 mAntiAliasing = paint->isAntiAlias(); 58 mLookupTransform.reset(); 59 mLookupTransform[SkMatrix::kMScaleX] = roundf(fmaxf(1.0f, matrix[mat4::kScaleX])); 60 mLookupTransform[SkMatrix::kMScaleY] = roundf(fmaxf(1.0f, matrix[mat4::kScaleY])); 61 if (!mLookupTransform.invert(&mInverseLookupTransform)) { 62 ALOGW("Could not query the inverse lookup transform for this font"); 63 } 64} 65 66Font::~Font() { 67 mState->removeFont(this); 68 69 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { 70 delete mCachedGlyphs.valueAt(i); 71 } 72} 73 74hash_t Font::FontDescription::hash() const { 75 uint32_t hash = JenkinsHashMix(0, mFontId); 76 hash = JenkinsHashMix(hash, android::hash_type(mFontSize)); 77 hash = JenkinsHashMix(hash, android::hash_type(mFlags)); 78 hash = JenkinsHashMix(hash, android::hash_type(mItalicStyle)); 79 hash = JenkinsHashMix(hash, android::hash_type(mScaleX)); 80 hash = JenkinsHashMix(hash, android::hash_type(mStyle)); 81 hash = JenkinsHashMix(hash, android::hash_type(mStrokeWidth)); 82 hash = JenkinsHashMix(hash, int(mAntiAliasing)); 83 hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleX])); 84 hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleY])); 85 return JenkinsHashWhiten(hash); 86} 87 88int Font::FontDescription::compare(const Font::FontDescription& lhs, 89 const Font::FontDescription& rhs) { 90 int deltaInt = int(lhs.mFontId) - int(rhs.mFontId); 91 if (deltaInt != 0) return deltaInt; 92 93 if (lhs.mFontSize < rhs.mFontSize) return -1; 94 if (lhs.mFontSize > rhs.mFontSize) return +1; 95 96 if (lhs.mItalicStyle < rhs.mItalicStyle) return -1; 97 if (lhs.mItalicStyle > rhs.mItalicStyle) return +1; 98 99 deltaInt = int(lhs.mFlags) - int(rhs.mFlags); 100 if (deltaInt != 0) return deltaInt; 101 102 if (lhs.mScaleX < rhs.mScaleX) return -1; 103 if (lhs.mScaleX > rhs.mScaleX) return +1; 104 105 deltaInt = int(lhs.mStyle) - int(rhs.mStyle); 106 if (deltaInt != 0) return deltaInt; 107 108 if (lhs.mStrokeWidth < rhs.mStrokeWidth) return -1; 109 if (lhs.mStrokeWidth > rhs.mStrokeWidth) return +1; 110 111 deltaInt = int(lhs.mAntiAliasing) - int(rhs.mAntiAliasing); 112 if (deltaInt != 0) return deltaInt; 113 114 if (lhs.mLookupTransform[SkMatrix::kMScaleX] < 115 rhs.mLookupTransform[SkMatrix::kMScaleX]) return -1; 116 if (lhs.mLookupTransform[SkMatrix::kMScaleX] > 117 rhs.mLookupTransform[SkMatrix::kMScaleX]) return +1; 118 119 if (lhs.mLookupTransform[SkMatrix::kMScaleY] < 120 rhs.mLookupTransform[SkMatrix::kMScaleY]) return -1; 121 if (lhs.mLookupTransform[SkMatrix::kMScaleY] > 122 rhs.mLookupTransform[SkMatrix::kMScaleY]) return +1; 123 124 return 0; 125} 126 127void Font::invalidateTextureCache(CacheTexture* cacheTexture) { 128 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { 129 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i); 130 if (!cacheTexture || cachedGlyph->mCacheTexture == cacheTexture) { 131 cachedGlyph->mIsValid = false; 132 } 133 } 134} 135 136void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, 137 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { 138 int nPenX = x + glyph->mBitmapLeft; 139 int nPenY = y + glyph->mBitmapTop; 140 141 int width = (int) glyph->mBitmapWidth; 142 int height = (int) glyph->mBitmapHeight; 143 144 if (bounds->bottom > nPenY) { 145 bounds->bottom = nPenY; 146 } 147 if (bounds->left > nPenX) { 148 bounds->left = nPenX; 149 } 150 if (bounds->right < nPenX + width) { 151 bounds->right = nPenX + width; 152 } 153 if (bounds->top < nPenY + height) { 154 bounds->top = nPenY + height; 155 } 156} 157 158void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, 159 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { 160 float nPenX = x + glyph->mBitmapLeft; 161 float nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight; 162 163 float width = (float) glyph->mBitmapWidth; 164 float height = (float) glyph->mBitmapHeight; 165 166 float u1 = glyph->mBitmapMinU; 167 float u2 = glyph->mBitmapMaxU; 168 float v1 = glyph->mBitmapMinV; 169 float v2 = glyph->mBitmapMaxV; 170 171 mState->appendMeshQuad(nPenX, nPenY, u1, v2, 172 nPenX + width, nPenY, u2, v2, 173 nPenX + width, nPenY - height, u2, v1, 174 nPenX, nPenY - height, u1, v1, glyph->mCacheTexture); 175} 176 177void Font::drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y, 178 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { 179 SkPoint p[4]; 180 p[0].iset(glyph->mBitmapLeft, glyph->mBitmapTop + glyph->mBitmapHeight); 181 p[1].iset(glyph->mBitmapLeft + glyph->mBitmapWidth, glyph->mBitmapTop + glyph->mBitmapHeight); 182 p[2].iset(glyph->mBitmapLeft + glyph->mBitmapWidth, glyph->mBitmapTop); 183 p[3].iset(glyph->mBitmapLeft, glyph->mBitmapTop); 184 185 mDescription.mInverseLookupTransform.mapPoints(p, 4); 186 187 p[0].offset(x, y); 188 p[1].offset(x, y); 189 p[2].offset(x, y); 190 p[3].offset(x, y); 191 192 float u1 = glyph->mBitmapMinU; 193 float u2 = glyph->mBitmapMaxU; 194 float v1 = glyph->mBitmapMinV; 195 float v2 = glyph->mBitmapMaxV; 196 197 mState->appendRotatedMeshQuad( 198 p[0].x(), p[0].y(), u1, v2, 199 p[1].x(), p[1].y(), u2, v2, 200 p[2].x(), p[2].y(), u2, v1, 201 p[3].x(), p[3].y(), u1, v1, glyph->mCacheTexture); 202} 203 204void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap, 205 uint32_t bitmapWidth, uint32_t bitmapHeight, Rect* bounds, const float* pos) { 206 int dstX = x + glyph->mBitmapLeft; 207 int dstY = y + glyph->mBitmapTop; 208 209 CacheTexture* cacheTexture = glyph->mCacheTexture; 210 211 uint32_t cacheWidth = cacheTexture->getWidth(); 212 uint32_t startY = glyph->mStartY * cacheWidth; 213 uint32_t endY = startY + (glyph->mBitmapHeight * cacheWidth); 214 215 PixelBuffer* pixelBuffer = cacheTexture->getPixelBuffer(); 216 const uint8_t* cacheBuffer = pixelBuffer->map(); 217 218 for (uint32_t cacheY = startY, bitmapY = dstY * bitmapWidth; cacheY < endY; 219 cacheY += cacheWidth, bitmapY += bitmapWidth) { 220 memcpy(&bitmap[bitmapY + dstX], &cacheBuffer[cacheY + glyph->mStartX], glyph->mBitmapWidth); 221 } 222} 223 224void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset, 225 SkPathMeasure& measure, SkPoint* position, SkVector* tangent) { 226 const float halfWidth = glyph->mBitmapWidth * 0.5f; 227 const float height = glyph->mBitmapHeight; 228 229 vOffset += glyph->mBitmapTop + height; 230 231 SkPoint destination[4]; 232 bool ok = measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent); 233 if (!ok) { 234 ALOGW("The path for drawTextOnPath is empty or null"); 235 } 236 237 // Move along the tangent and offset by the normal 238 destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset, 239 -tangent->fY * halfWidth + tangent->fX * vOffset); 240 destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset, 241 tangent->fY * halfWidth + tangent->fX * vOffset); 242 destination[2].set(destination[1].fX + tangent->fY * height, 243 destination[1].fY - tangent->fX * height); 244 destination[3].set(destination[0].fX + tangent->fY * height, 245 destination[0].fY - tangent->fX * height); 246 247 const float u1 = glyph->mBitmapMinU; 248 const float u2 = glyph->mBitmapMaxU; 249 const float v1 = glyph->mBitmapMinV; 250 const float v2 = glyph->mBitmapMaxV; 251 252 mState->appendRotatedMeshQuad( 253 position->x() + destination[0].x(), 254 position->y() + destination[0].y(), u1, v2, 255 position->x() + destination[1].x(), 256 position->y() + destination[1].y(), u2, v2, 257 position->x() + destination[2].x(), 258 position->y() + destination[2].y(), u2, v1, 259 position->x() + destination[3].x(), 260 position->y() + destination[3].y(), u1, v1, 261 glyph->mCacheTexture); 262} 263 264CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) { 265 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueFor(textUnit); 266 if (cachedGlyph) { 267 // Is the glyph still in texture cache? 268 if (!cachedGlyph->mIsValid) { 269 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit, 270 &mDescription.mLookupTransform); 271 updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching); 272 } 273 } else { 274 cachedGlyph = cacheGlyph(paint, textUnit, precaching); 275 } 276 277 return cachedGlyph; 278} 279 280void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, 281 int numGlyphs, int x, int y, const float* positions) { 282 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 283 0, 0, NULL, positions); 284} 285 286void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, 287 int numGlyphs, SkPath* path, float hOffset, float vOffset) { 288 if (numGlyphs == 0 || text == NULL || len == 0) { 289 return; 290 } 291 292 text += start; 293 294 int glyphsCount = 0; 295 SkFixed prevRsbDelta = 0; 296 297 float penX = 0.0f; 298 299 SkPoint position; 300 SkVector tangent; 301 302 SkPathMeasure measure(*path, false); 303 float pathLength = SkScalarToFloat(measure.getLength()); 304 305 if (paint->getTextAlign() != SkPaint::kLeft_Align) { 306 float textWidth = SkScalarToFloat(paint->measureText(text, len)); 307 float pathOffset = pathLength; 308 if (paint->getTextAlign() == SkPaint::kCenter_Align) { 309 textWidth *= 0.5f; 310 pathOffset *= 0.5f; 311 } 312 penX += pathOffset - textWidth; 313 } 314 315 while (glyphsCount < numGlyphs && penX < pathLength) { 316 glyph_t glyph = GET_GLYPH(text); 317 318 if (IS_END_OF_STRING(glyph)) { 319 break; 320 } 321 322 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); 323 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta)); 324 prevRsbDelta = cachedGlyph->mRsbDelta; 325 326 if (cachedGlyph->mIsValid && cachedGlyph->mCacheTexture) { 327 drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent); 328 } 329 330 penX += SkFixedToFloat(cachedGlyph->mAdvanceX); 331 332 glyphsCount++; 333 } 334} 335 336void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 337 int numGlyphs, Rect *bounds, const float* positions) { 338 if (bounds == NULL) { 339 ALOGE("No return rectangle provided to measure text"); 340 return; 341 } 342 bounds->set(1e6, -1e6, -1e6, 1e6); 343 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions); 344} 345 346void Font::precache(SkPaint* paint, const char* text, int numGlyphs) { 347 ATRACE_NAME("precacheText"); 348 349 if (numGlyphs == 0 || text == NULL) { 350 return; 351 } 352 353 int glyphsCount = 0; 354 while (glyphsCount < numGlyphs) { 355 glyph_t glyph = GET_GLYPH(text); 356 357 // Reached the end of the string 358 if (IS_END_OF_STRING(glyph)) { 359 break; 360 } 361 362 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph, true); 363 glyphsCount++; 364 } 365} 366 367void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, 368 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, 369 uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) { 370 if (numGlyphs == 0 || text == NULL || len == 0) { 371 return; 372 } 373 374 static RenderGlyph gRenderGlyph[] = { 375 &android::uirenderer::Font::drawCachedGlyph, 376 &android::uirenderer::Font::drawCachedGlyphTransformed, 377 &android::uirenderer::Font::drawCachedGlyphBitmap, 378 &android::uirenderer::Font::drawCachedGlyphBitmap, 379 &android::uirenderer::Font::measureCachedGlyph, 380 &android::uirenderer::Font::measureCachedGlyph 381 }; 382 RenderGlyph render = gRenderGlyph[(mode << 1) + !mIdentityTransform]; 383 384 text += start; 385 int glyphsCount = 0; 386 387 const SkPaint::Align align = paint->getTextAlign(); 388 389 while (glyphsCount < numGlyphs) { 390 glyph_t glyph = GET_GLYPH(text); 391 392 // Reached the end of the string 393 if (IS_END_OF_STRING(glyph)) { 394 break; 395 } 396 397 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); 398 399 // If it's still not valid, we couldn't cache it, so we shouldn't 400 // draw garbage; also skip empty glyphs (spaces) 401 if (cachedGlyph->mIsValid && cachedGlyph->mCacheTexture) { 402 float penX = x + positions[(glyphsCount << 1)]; 403 float penY = y + positions[(glyphsCount << 1) + 1]; 404 405 (*this.*render)(cachedGlyph, roundf(penX), roundf(penY), 406 bitmap, bitmapW, bitmapH, bounds, positions); 407 } 408 409 glyphsCount++; 410 } 411} 412 413void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph, 414 bool precaching) { 415 glyph->mAdvanceX = skiaGlyph.fAdvanceX; 416 glyph->mAdvanceY = skiaGlyph.fAdvanceY; 417 glyph->mBitmapLeft = skiaGlyph.fLeft; 418 glyph->mBitmapTop = skiaGlyph.fTop; 419 glyph->mLsbDelta = skiaGlyph.fLsbDelta; 420 glyph->mRsbDelta = skiaGlyph.fRsbDelta; 421 422 uint32_t startX = 0; 423 uint32_t startY = 0; 424 425 // Get the bitmap for the glyph 426 if (!skiaGlyph.fImage) { 427 paint->findImage(skiaGlyph, &mDescription.mLookupTransform); 428 } 429 mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching); 430 431 if (!glyph->mIsValid) { 432 return; 433 } 434 435 uint32_t endX = startX + skiaGlyph.fWidth; 436 uint32_t endY = startY + skiaGlyph.fHeight; 437 438 glyph->mStartX = startX; 439 glyph->mStartY = startY; 440 glyph->mBitmapWidth = skiaGlyph.fWidth; 441 glyph->mBitmapHeight = skiaGlyph.fHeight; 442 443 bool empty = skiaGlyph.fWidth == 0 || skiaGlyph.fHeight == 0; 444 if (!empty) { 445 uint32_t cacheWidth = glyph->mCacheTexture->getWidth(); 446 uint32_t cacheHeight = glyph->mCacheTexture->getHeight(); 447 448 glyph->mBitmapMinU = startX / (float) cacheWidth; 449 glyph->mBitmapMinV = startY / (float) cacheHeight; 450 glyph->mBitmapMaxU = endX / (float) cacheWidth; 451 glyph->mBitmapMaxV = endY / (float) cacheHeight; 452 453 mState->setTextureDirty(); 454 } 455} 456 457CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) { 458 CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); 459 mCachedGlyphs.add(glyph, newGlyph); 460 461 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph, &mDescription.mLookupTransform); 462 newGlyph->mIsValid = false; 463 newGlyph->mGlyphIndex = skiaGlyph.fID; 464 465 updateGlyphCache(paint, skiaGlyph, newGlyph, precaching); 466 467 return newGlyph; 468} 469 470Font* Font::create(FontRenderer* state, const SkPaint* paint, const mat4& matrix) { 471 FontDescription description(paint, matrix); 472 Font* font = state->mActiveFonts.get(description); 473 474 if (!font) { 475 font = new Font(state, description); 476 state->mActiveFonts.put(description, font); 477 } 478 font->mIdentityTransform = matrix.isIdentity(); 479 480 return font; 481} 482 483}; // namespace uirenderer 484}; // namespace android 485