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