blob: 7bfa63d850c109577af97f75ef139a3b6d351dc1 [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
Romain Guya3dc55f2012-09-28 13:55:44 -070017#define LOG_TAG "OpenGLRenderer"
18
Romain Guy9f5dab32012-09-04 12:55:44 -070019#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
29namespace android {
30namespace uirenderer {
31
32///////////////////////////////////////////////////////////////////////////////
33// Font
34///////////////////////////////////////////////////////////////////////////////
35
36Font::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
45Font::~Font() {
Romain Guy9b1204b2012-09-04 15:22:57 -070046 mState->removeFont(this);
Romain Guy9f5dab32012-09-04 12:55:44 -070047
48 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
49 delete mCachedGlyphs.valueAt(i);
50 }
51}
52
Romain Guy80872462012-09-04 16:42:01 -070053void Font::invalidateTextureCache(CacheTexture* cacheTexture) {
Romain Guy9f5dab32012-09-04 12:55:44 -070054 for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
55 CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
Romain Guy521dc512012-09-04 19:10:33 -070056 if (!cacheTexture || cachedGlyph->mCacheTexture == cacheTexture) {
Romain Guy9f5dab32012-09-04 12:55:44 -070057 cachedGlyph->mIsValid = false;
58 }
59 }
60}
61
62void 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
84void 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
103void 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
Romain Guy80872462012-09-04 16:42:01 -0700111 CacheTexture* cacheTexture = glyph->mCacheTexture;
112 uint32_t cacheWidth = cacheTexture->getWidth();
113 const uint8_t* cacheBuffer = cacheTexture->getTexture();
Romain Guy9f5dab32012-09-04 12:55:44 -0700114
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
131void 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
168CachedGlyphInfo* 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
186void 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
197void 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
203void 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
253void 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
263void 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
284void 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
369void 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
Romain Guy80872462012-09-04 16:42:01 -0700397 uint32_t cacheWidth = glyph->mCacheTexture->getWidth();
398 uint32_t cacheHeight = glyph->mCacheTexture->getHeight();
Romain Guy9f5dab32012-09-04 12:55:44 -0700399
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
Romain Guy9b1204b2012-09-04 15:22:57 -0700405 mState->setTextureDirty();
Romain Guy9f5dab32012-09-04 12:55:44 -0700406}
407
408CachedGlyphInfo* 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
421Font* 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