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