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
19 #include <cutils/compiler.h>
20
21 #include <SkUtils.h>
22
23 #include "Debug.h"
24 #include "FontUtil.h"
25 #include "Font.h"
26 #include "FontRenderer.h"
27 #include "Properties.h"
28
29 namespace android {
30 namespace uirenderer {
31
32 ///////////////////////////////////////////////////////////////////////////////
33 // Font
34 ///////////////////////////////////////////////////////////////////////////////
35
Font(FontRenderer * state,uint32_t fontId,float fontSize,int flags,uint32_t italicStyle,uint32_t scaleX,SkPaint::Style style,uint32_t strokeWidth)36 Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
37 int flags, uint32_t italicStyle, uint32_t scaleX,
38 SkPaint::Style style, uint32_t strokeWidth) :
39 mState(state), mFontId(fontId), mFontSize(fontSize),
40 mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
41 mStyle(style), mStrokeWidth(mStrokeWidth) {
42 }
43
44
~Font()45 Font::~Font() {
46 mState->removeFont(this);
47
48 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
49 delete mCachedGlyphs.valueAt(i);
50 }
51 }
52
invalidateTextureCache(CacheTexture * cacheTexture)53 void Font::invalidateTextureCache(CacheTexture* cacheTexture) {
54 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
55 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
56 if (!cacheTexture || cachedGlyph->mCacheTexture == cacheTexture) {
57 cachedGlyph->mIsValid = false;
58 }
59 }
60 }
61
measureCachedGlyph(CachedGlyphInfo * glyph,int x,int y,uint8_t * bitmap,uint32_t bitmapW,uint32_t bitmapH,Rect * bounds,const float * pos)62 void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
63 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
64 int nPenX = x + glyph->mBitmapLeft;
65 int nPenY = y + glyph->mBitmapTop;
66
67 int width = (int) glyph->mBitmapWidth;
68 int height = (int) glyph->mBitmapHeight;
69
70 if (bounds->bottom > nPenY) {
71 bounds->bottom = nPenY;
72 }
73 if (bounds->left > nPenX) {
74 bounds->left = nPenX;
75 }
76 if (bounds->right < nPenX + width) {
77 bounds->right = nPenX + width;
78 }
79 if (bounds->top < nPenY + height) {
80 bounds->top = nPenY + height;
81 }
82 }
83
drawCachedGlyph(CachedGlyphInfo * glyph,int x,int y,uint8_t * bitmap,uint32_t bitmapW,uint32_t bitmapH,Rect * bounds,const float * pos)84 void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
85 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
86 int nPenX = x + glyph->mBitmapLeft;
87 int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
88
89 float u1 = glyph->mBitmapMinU;
90 float u2 = glyph->mBitmapMaxU;
91 float v1 = glyph->mBitmapMinV;
92 float v2 = glyph->mBitmapMaxV;
93
94 int width = (int) glyph->mBitmapWidth;
95 int height = (int) glyph->mBitmapHeight;
96
97 mState->appendMeshQuad(nPenX, nPenY, u1, v2,
98 nPenX + width, nPenY, u2, v2,
99 nPenX + width, nPenY - height, u2, v1,
100 nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
101 }
102
drawCachedGlyphBitmap(CachedGlyphInfo * glyph,int x,int y,uint8_t * bitmap,uint32_t bitmapW,uint32_t bitmapH,Rect * bounds,const float * pos)103 void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
104 uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
105 int nPenX = x + glyph->mBitmapLeft;
106 int nPenY = y + glyph->mBitmapTop;
107
108 uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
109 uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
110
111 CacheTexture* cacheTexture = glyph->mCacheTexture;
112 uint32_t cacheWidth = cacheTexture->getWidth();
113 const uint8_t* cacheBuffer = cacheTexture->getTexture();
114
115 uint32_t cacheX = 0, cacheY = 0;
116 int32_t bX = 0, bY = 0;
117 for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
118 for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
119 #if DEBUG_FONT_RENDERER
120 if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
121 ALOGE("Skipping invalid index");
122 continue;
123 }
124 #endif
125 uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
126 bitmap[bY * bitmapW + bX] = tempCol;
127 }
128 }
129 }
130
drawCachedGlyph(CachedGlyphInfo * glyph,float x,float hOffset,float vOffset,SkPathMeasure & measure,SkPoint * position,SkVector * tangent)131 void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
132 SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
133 const float halfWidth = glyph->mBitmapWidth * 0.5f;
134 const float height = glyph->mBitmapHeight;
135
136 vOffset += glyph->mBitmapTop + height;
137
138 SkPoint destination[4];
139 measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
140
141 // Move along the tangent and offset by the normal
142 destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
143 -tangent->fY * halfWidth + tangent->fX * vOffset);
144 destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
145 tangent->fY * halfWidth + tangent->fX * vOffset);
146 destination[2].set(destination[1].fX + tangent->fY * height,
147 destination[1].fY - tangent->fX * height);
148 destination[3].set(destination[0].fX + tangent->fY * height,
149 destination[0].fY - tangent->fX * height);
150
151 const float u1 = glyph->mBitmapMinU;
152 const float u2 = glyph->mBitmapMaxU;
153 const float v1 = glyph->mBitmapMinV;
154 const float v2 = glyph->mBitmapMaxV;
155
156 mState->appendRotatedMeshQuad(
157 position->fX + destination[0].fX,
158 position->fY + destination[0].fY, u1, v2,
159 position->fX + destination[1].fX,
160 position->fY + destination[1].fY, u2, v2,
161 position->fX + destination[2].fX,
162 position->fY + destination[2].fY, u2, v1,
163 position->fX + destination[3].fX,
164 position->fY + destination[3].fY, u1, v1,
165 glyph->mCacheTexture);
166 }
167
getCachedGlyph(SkPaint * paint,glyph_t textUnit,bool precaching)168 CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) {
169 CachedGlyphInfo* cachedGlyph = NULL;
170 ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
171 if (index >= 0) {
172 cachedGlyph = mCachedGlyphs.valueAt(index);
173 } else {
174 cachedGlyph = cacheGlyph(paint, textUnit, precaching);
175 }
176
177 // Is the glyph still in texture cache?
178 if (!cachedGlyph->mIsValid) {
179 const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
180 updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
181 }
182
183 return cachedGlyph;
184 }
185
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)186 void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
187 int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
188 if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
189 render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
190 bitmapW, bitmapH, NULL, NULL);
191 } else {
192 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
193 0, 0, NULL, NULL);
194 }
195 }
196
render(SkPaint * paint,const char * text,uint32_t start,uint32_t len,int numGlyphs,int x,int y,const float * positions)197 void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
198 int numGlyphs, int x, int y, const float* positions) {
199 render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
200 0, 0, NULL, positions);
201 }
202
render(SkPaint * paint,const char * text,uint32_t start,uint32_t len,int numGlyphs,SkPath * path,float hOffset,float vOffset)203 void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
204 int numGlyphs, SkPath* path, float hOffset, float vOffset) {
205 if (numGlyphs == 0 || text == NULL || len == 0) {
206 return;
207 }
208
209 text += start;
210
211 int glyphsCount = 0;
212 SkFixed prevRsbDelta = 0;
213
214 float penX = 0.0f;
215
216 SkPoint position;
217 SkVector tangent;
218
219 SkPathMeasure measure(*path, false);
220 float pathLength = SkScalarToFloat(measure.getLength());
221
222 if (paint->getTextAlign() != SkPaint::kLeft_Align) {
223 float textWidth = SkScalarToFloat(paint->measureText(text, len));
224 float pathOffset = pathLength;
225 if (paint->getTextAlign() == SkPaint::kCenter_Align) {
226 textWidth *= 0.5f;
227 pathOffset *= 0.5f;
228 }
229 penX += pathOffset - textWidth;
230 }
231
232 while (glyphsCount < numGlyphs && penX < pathLength) {
233 glyph_t glyph = GET_GLYPH(text);
234
235 if (IS_END_OF_STRING(glyph)) {
236 break;
237 }
238
239 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
240 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
241 prevRsbDelta = cachedGlyph->mRsbDelta;
242
243 if (cachedGlyph->mIsValid) {
244 drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
245 }
246
247 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
248
249 glyphsCount++;
250 }
251 }
252
measure(SkPaint * paint,const char * text,uint32_t start,uint32_t len,int numGlyphs,Rect * bounds,const float * positions)253 void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
254 int numGlyphs, Rect *bounds, const float* positions) {
255 if (bounds == NULL) {
256 ALOGE("No return rectangle provided to measure text");
257 return;
258 }
259 bounds->set(1e6, -1e6, -1e6, 1e6);
260 render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
261 }
262
precache(SkPaint * paint,const char * text,int numGlyphs)263 void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
264
265 if (numGlyphs == 0 || text == NULL) {
266 return;
267 }
268 int glyphsCount = 0;
269
270 while (glyphsCount < numGlyphs) {
271 glyph_t glyph = GET_GLYPH(text);
272
273 // Reached the end of the string
274 if (IS_END_OF_STRING(glyph)) {
275 break;
276 }
277
278 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph, true);
279
280 glyphsCount++;
281 }
282 }
283
render(SkPaint * paint,const char * text,uint32_t start,uint32_t len,int numGlyphs,int x,int y,RenderMode mode,uint8_t * bitmap,uint32_t bitmapW,uint32_t bitmapH,Rect * bounds,const float * positions)284 void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
285 int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
286 uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) {
287 if (numGlyphs == 0 || text == NULL || len == 0) {
288 return;
289 }
290
291 static RenderGlyph gRenderGlyph[] = {
292 &android::uirenderer::Font::drawCachedGlyph,
293 &android::uirenderer::Font::drawCachedGlyphBitmap,
294 &android::uirenderer::Font::measureCachedGlyph
295 };
296 RenderGlyph render = gRenderGlyph[mode];
297
298 text += start;
299 int glyphsCount = 0;
300
301 if (CC_LIKELY(positions == NULL)) {
302 SkFixed prevRsbDelta = 0;
303
304 float penX = x + 0.5f;
305 int penY = y;
306
307 while (glyphsCount < numGlyphs) {
308 glyph_t glyph = GET_GLYPH(text);
309
310 // Reached the end of the string
311 if (IS_END_OF_STRING(glyph)) {
312 break;
313 }
314
315 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
316 penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
317 prevRsbDelta = cachedGlyph->mRsbDelta;
318
319 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
320 if (cachedGlyph->mIsValid) {
321 (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
322 bitmap, bitmapW, bitmapH, bounds, positions);
323 }
324
325 penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
326
327 glyphsCount++;
328 }
329 } else {
330 const SkPaint::Align align = paint->getTextAlign();
331
332 // This is for renderPosText()
333 while (glyphsCount < numGlyphs) {
334 glyph_t glyph = GET_GLYPH(text);
335
336 // Reached the end of the string
337 if (IS_END_OF_STRING(glyph)) {
338 break;
339 }
340
341 CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
342
343 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
344 if (cachedGlyph->mIsValid) {
345 int penX = x + positions[(glyphsCount << 1)];
346 int penY = y + positions[(glyphsCount << 1) + 1];
347
348 switch (align) {
349 case SkPaint::kRight_Align:
350 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
351 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
352 break;
353 case SkPaint::kCenter_Align:
354 penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
355 penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
356 default:
357 break;
358 }
359
360 (*this.*render)(cachedGlyph, penX, penY,
361 bitmap, bitmapW, bitmapH, bounds, positions);
362 }
363
364 glyphsCount++;
365 }
366 }
367 }
368
updateGlyphCache(SkPaint * paint,const SkGlyph & skiaGlyph,CachedGlyphInfo * glyph,bool precaching)369 void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
370 bool precaching) {
371 glyph->mAdvanceX = skiaGlyph.fAdvanceX;
372 glyph->mAdvanceY = skiaGlyph.fAdvanceY;
373 glyph->mBitmapLeft = skiaGlyph.fLeft;
374 glyph->mBitmapTop = skiaGlyph.fTop;
375 glyph->mLsbDelta = skiaGlyph.fLsbDelta;
376 glyph->mRsbDelta = skiaGlyph.fRsbDelta;
377
378 uint32_t startX = 0;
379 uint32_t startY = 0;
380
381 // Get the bitmap for the glyph
382 paint->findImage(skiaGlyph);
383 mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
384
385 if (!glyph->mIsValid) {
386 return;
387 }
388
389 uint32_t endX = startX + skiaGlyph.fWidth;
390 uint32_t endY = startY + skiaGlyph.fHeight;
391
392 glyph->mStartX = startX;
393 glyph->mStartY = startY;
394 glyph->mBitmapWidth = skiaGlyph.fWidth;
395 glyph->mBitmapHeight = skiaGlyph.fHeight;
396
397 uint32_t cacheWidth = glyph->mCacheTexture->getWidth();
398 uint32_t cacheHeight = glyph->mCacheTexture->getHeight();
399
400 glyph->mBitmapMinU = startX / (float) cacheWidth;
401 glyph->mBitmapMinV = startY / (float) cacheHeight;
402 glyph->mBitmapMaxU = endX / (float) cacheWidth;
403 glyph->mBitmapMaxV = endY / (float) cacheHeight;
404
405 mState->setTextureDirty();
406 }
407
cacheGlyph(SkPaint * paint,glyph_t glyph,bool precaching)408 CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
409 CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
410 mCachedGlyphs.add(glyph, newGlyph);
411
412 const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
413 newGlyph->mGlyphIndex = skiaGlyph.fID;
414 newGlyph->mIsValid = false;
415
416 updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
417
418 return newGlyph;
419 }
420
create(FontRenderer * state,uint32_t fontId,float fontSize,int flags,uint32_t italicStyle,uint32_t scaleX,SkPaint::Style style,uint32_t strokeWidth)421 Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
422 int flags, uint32_t italicStyle, uint32_t scaleX,
423 SkPaint::Style style, uint32_t strokeWidth) {
424 Vector<Font*> &activeFonts = state->mActiveFonts;
425
426 for (uint32_t i = 0; i < activeFonts.size(); i++) {
427 Font* font = activeFonts[i];
428 if (font->mFontId == fontId && font->mFontSize == fontSize &&
429 font->mFlags == flags && font->mItalicStyle == italicStyle &&
430 font->mScaleX == scaleX && font->mStyle == style &&
431 (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
432 return font;
433 }
434 }
435
436 Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
437 scaleX, style, strokeWidth);
438 activeFonts.push(newFont);
439 return newFont;
440 }
441
442 }; // namespace uirenderer
443 }; // namespace android
444