blob: c516ea9f84c3f5923b346df408c83c6268cec1c9 [file] [log] [blame]
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -07001
2/*
3 * Copyright (C) 2009 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#ifndef ANDROID_RS_BUILD_FOR_HOST
19#include "rsContext.h"
20#else
21#include "rsContextHostStub.h"
22#endif
23
24#include "rsFont.h"
25#include "rsProgramFragment.h"
Alex Sakhartchoukc9fa3052010-10-01 15:20:41 -070026#include <cutils/properties.h>
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -070027#include FT_BITMAP_H
28
29#include <GLES/gl.h>
30#include <GLES/glext.h>
31#include <GLES2/gl2.h>
32#include <GLES2/gl2ext.h>
33
34using namespace android;
35using namespace android::renderscript;
36
37Font::Font(Context *rsc) : ObjectBase(rsc), mCachedGlyphs(NULL)
38{
Alex Sakhartchouka1ccecd2010-06-30 12:49:27 -070039 mAllocFile = __FILE__;
40 mAllocLine = __LINE__;
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -070041 mInitialized = false;
42 mHasKerning = false;
Alex Sakhartchouk3659d942010-06-30 16:53:43 -070043 mFace = NULL;
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -070044}
45
46bool Font::init(const char *name, uint32_t fontSize, uint32_t dpi)
47{
48 if(mInitialized) {
49 LOGE("Reinitialization of fonts not supported");
50 return false;
51 }
52
53 String8 fontsDir("/fonts/");
54 String8 fullPath(getenv("ANDROID_ROOT"));
55 fullPath += fontsDir;
56 fullPath += name;
57
Alex Sakhartchouka1ccecd2010-06-30 12:49:27 -070058 FT_Error error = FT_New_Face(mRSC->mStateFont.getLib(), fullPath.string(), 0, &mFace);
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -070059 if(error) {
60 LOGE("Unable to initialize font %s", fullPath.string());
61 return false;
62 }
63
64 mFontName = name;
65 mFontSize = fontSize;
66 mDpi = dpi;
67
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -070068 error = FT_Set_Char_Size(mFace, fontSize * 64, 0, dpi, 0);
69 if(error) {
70 LOGE("Unable to set font size on %s", fullPath.string());
71 return false;
72 }
73
74 mHasKerning = FT_HAS_KERNING(mFace);
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -070075
76 mInitialized = true;
77 return true;
78}
79
80void Font::invalidateTextureCache()
81{
82 for(uint32_t i = 0; i < mCachedGlyphs.size(); i ++) {
83 mCachedGlyphs.valueAt(i)->mIsValid = false;
84 }
85}
86
87void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y)
88{
89 FontState *state = &mRSC->mStateFont;
90
91 int nPenX = x + glyph->mBitmapLeft;
92 int nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight;
93
94 state->appendMeshQuad(nPenX, nPenY, 0,
95 glyph->mBitmapMinU, glyph->mBitmapMaxV,
96
97 nPenX + (int)glyph->mBitmapWidth, nPenY, 0,
98 glyph->mBitmapMaxU, glyph->mBitmapMaxV,
99
100 nPenX + (int)glyph->mBitmapWidth, nPenY - (int)glyph->mBitmapHeight, 0,
101 glyph->mBitmapMaxU, glyph->mBitmapMinV,
102
103 nPenX, nPenY - (int)glyph->mBitmapHeight, 0,
104 glyph->mBitmapMinU, glyph->mBitmapMinV);
105}
106
107void Font::renderUTF(const char *text, uint32_t len, uint32_t start, int numGlyphs, int x, int y)
108{
109 if(!mInitialized || numGlyphs == 0 || text == NULL || len == 0) {
110 return;
111 }
112
113 int penX = x, penY = y;
114 int glyphsLeft = 1;
115 if(numGlyphs > 0) {
116 glyphsLeft = numGlyphs;
117 }
118
119 size_t index = start;
120 size_t nextIndex = 0;
121
122 while (glyphsLeft > 0) {
123
124 int32_t utfChar = utf32_at(text, len, index, &nextIndex);
125
126 // Reached the end of the string or encountered
127 if(utfChar < 0) {
128 break;
129 }
130
131 // Move to the next character in the array
132 index = nextIndex;
133
Alex Sakhartchouk01bcef62010-08-17 11:09:49 -0700134 CachedGlyphInfo *cachedGlyph = getCachedUTFChar(utfChar);
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700135
136 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
137 if(cachedGlyph->mIsValid) {
138 drawCachedGlyph(cachedGlyph, penX, penY);
139 }
140
141 penX += (cachedGlyph->mAdvance.x >> 6);
142
143 // If we were given a specific number of glyphs, decrement
144 if(numGlyphs > 0) {
145 glyphsLeft --;
146 }
147 }
148}
149
Alex Sakhartchouk01bcef62010-08-17 11:09:49 -0700150Font::CachedGlyphInfo* Font::getCachedUTFChar(int32_t utfChar) {
151
152 CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor((uint32_t)utfChar);
153 if(cachedGlyph == NULL) {
154 cachedGlyph = cacheGlyph((uint32_t)utfChar);
155 }
156 // Is the glyph still in texture cache?
157 if(!cachedGlyph->mIsValid) {
158 updateGlyphCache(cachedGlyph);
159 }
160
161 return cachedGlyph;
162}
163
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700164void Font::updateGlyphCache(CachedGlyphInfo *glyph)
165{
Alex Sakhartchouka1ccecd2010-06-30 12:49:27 -0700166 FT_Error error = FT_Load_Glyph( mFace, glyph->mGlyphIndex, FT_LOAD_RENDER );
167 if(error) {
168 LOGE("Couldn't load glyph.");
169 return;
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700170 }
171
Alex Sakhartchouka1ccecd2010-06-30 12:49:27 -0700172 glyph->mAdvance = mFace->glyph->advance;
173 glyph->mBitmapLeft = mFace->glyph->bitmap_left;
174 glyph->mBitmapTop = mFace->glyph->bitmap_top;
175
176 FT_Bitmap *bitmap = &mFace->glyph->bitmap;
177
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700178 // Now copy the bitmap into the cache texture
179 uint32_t startX = 0;
180 uint32_t startY = 0;
181
182 // Let the font state figure out where to put the bitmap
183 FontState *state = &mRSC->mStateFont;
Alex Sakhartchouka1ccecd2010-06-30 12:49:27 -0700184 glyph->mIsValid = state->cacheBitmap(bitmap, &startX, &startY);
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700185
186 if(!glyph->mIsValid) {
187 return;
188 }
189
Alex Sakhartchouka1ccecd2010-06-30 12:49:27 -0700190 uint32_t endX = startX + bitmap->width;
191 uint32_t endY = startY + bitmap->rows;
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700192
193 glyph->mBitmapMinX = startX;
194 glyph->mBitmapMinY = startY;
Alex Sakhartchouka1ccecd2010-06-30 12:49:27 -0700195 glyph->mBitmapWidth = bitmap->width;
196 glyph->mBitmapHeight = bitmap->rows;
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700197
198 uint32_t cacheWidth = state->getCacheTextureType()->getDimX();
199 uint32_t cacheHeight = state->getCacheTextureType()->getDimY();
200
201 glyph->mBitmapMinU = (float)startX / (float)cacheWidth;
202 glyph->mBitmapMinV = (float)startY / (float)cacheHeight;
203 glyph->mBitmapMaxU = (float)endX / (float)cacheWidth;
204 glyph->mBitmapMaxV = (float)endY / (float)cacheHeight;
205}
206
207Font::CachedGlyphInfo *Font::cacheGlyph(uint32_t glyph)
208{
209 CachedGlyphInfo *newGlyph = new CachedGlyphInfo();
210 mCachedGlyphs.add(glyph, newGlyph);
211
212 newGlyph->mGlyphIndex = FT_Get_Char_Index(mFace, glyph);
213 newGlyph->mIsValid = false;
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700214
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700215 updateGlyphCache(newGlyph);
216
217 return newGlyph;
218}
219
220Font * Font::create(Context *rsc, const char *name, uint32_t fontSize, uint32_t dpi)
221{
Alex Sakhartchouk35b96442010-08-18 15:46:43 -0700222 rsc->mStateFont.checkInit();
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700223 Vector<Font*> &activeFonts = rsc->mStateFont.mActiveFonts;
224
225 for(uint32_t i = 0; i < activeFonts.size(); i ++) {
226 Font *ithFont = activeFonts[i];
227 if(ithFont->mFontName == name && ithFont->mFontSize == fontSize && ithFont->mDpi == dpi) {
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700228 return ithFont;
229 }
230 }
231
232 Font *newFont = new Font(rsc);
233 bool isInitialized = newFont->init(name, fontSize, dpi);
234 if(isInitialized) {
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700235 activeFonts.push(newFont);
Alex Sakhartchouk01bcef62010-08-17 11:09:49 -0700236 rsc->mStateFont.precacheLatin(newFont);
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700237 return newFont;
238 }
239
240 delete newFont;
241 return NULL;
242
243}
244
245Font::~Font()
246{
247 if(mFace) {
248 FT_Done_Face(mFace);
249 }
250
251 for (uint32_t ct = 0; ct < mRSC->mStateFont.mActiveFonts.size(); ct++) {
252 if (mRSC->mStateFont.mActiveFonts[ct] == this) {
253 mRSC->mStateFont.mActiveFonts.removeAt(ct);
254 break;
255 }
256 }
257
258 for(uint32_t i = 0; i < mCachedGlyphs.size(); i ++) {
259 CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i);
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700260 delete glyph;
261 }
262}
263
264FontState::FontState()
265{
266 mInitialized = false;
267 mMaxNumberOfQuads = 1024;
268 mCurrentQuadIndex = 0;
269 mRSC = NULL;
Alex Sakhartchouk3659d942010-06-30 16:53:43 -0700270 mLibrary = NULL;
Alex Sakhartchoukca5a4542010-08-05 11:24:14 -0700271 setFontColor(0.1f, 0.1f, 0.1f, 1.0f);
Alex Sakhartchoukc9fa3052010-10-01 15:20:41 -0700272
273 // Get the renderer properties
274 char property[PROPERTY_VALUE_MAX];
275
276 // Get the gamma
277 float gamma = DEFAULT_TEXT_GAMMA;
278 if (property_get(PROPERTY_TEXT_GAMMA, property, NULL) > 0) {
279 LOGD(" Setting text gamma to %s", property);
280 gamma = atof(property);
281 } else {
282 LOGD(" Using default text gamma of %.2f", DEFAULT_TEXT_GAMMA);
283 }
284
285 // Get the black gamma threshold
286 int blackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD;
287 if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) {
288 LOGD(" Setting text black gamma threshold to %s", property);
289 blackThreshold = atoi(property);
290 } else {
291 LOGD(" Using default text black gamma threshold of %d",
292 DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD);
293 }
294 mBlackThreshold = (float)(blackThreshold) / 255.0f;
295
296 // Get the white gamma threshold
297 int whiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD;
298 if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) {
299 LOGD(" Setting text white gamma threshold to %s", property);
300 whiteThreshold = atoi(property);
301 } else {
302 LOGD(" Using default white black gamma threshold of %d",
303 DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD);
304 }
305 mWhiteThreshold = (float)(whiteThreshold) / 255.0f;
306
307 // Compute the gamma tables
308 mBlackGamma = gamma;
309 mWhiteGamma = 1.0f / gamma;
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700310}
311
312FontState::~FontState()
313{
314 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
315 delete mCacheLines[i];
316 }
317
318 rsAssert(!mActiveFonts.size());
319}
320
Alex Sakhartchouka1ccecd2010-06-30 12:49:27 -0700321FT_Library FontState::getLib()
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700322{
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700323 if(!mLibrary) {
Alex Sakhartchouka1ccecd2010-06-30 12:49:27 -0700324 FT_Error error = FT_Init_FreeType(&mLibrary);
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700325 if(error) {
326 LOGE("Unable to initialize freetype");
Alex Sakhartchouka1ccecd2010-06-30 12:49:27 -0700327 return NULL;
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700328 }
329 }
Alex Sakhartchouk3659d942010-06-30 16:53:43 -0700330
Alex Sakhartchouka1ccecd2010-06-30 12:49:27 -0700331 return mLibrary;
332}
333
334void FontState::init(Context *rsc)
335{
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700336 mRSC = rsc;
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700337}
338
339void FontState::flushAllAndInvalidate()
340{
341 if(mCurrentQuadIndex != 0) {
342 issueDrawCommand();
343 mCurrentQuadIndex = 0;
344 }
345 for(uint32_t i = 0; i < mActiveFonts.size(); i ++) {
346 mActiveFonts[i]->invalidateTextureCache();
347 }
348 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
349 mCacheLines[i]->mCurrentCol = 0;
350 }
351}
352
353bool FontState::cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY)
354{
355 // If the glyph is too tall, don't cache it
356 if((uint32_t)bitmap->rows > mCacheLines[mCacheLines.size()-1]->mMaxHeight) {
357 LOGE("Font size to large to fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
358 return false;
359 }
360
361 // Now copy the bitmap into the cache texture
362 uint32_t startX = 0;
363 uint32_t startY = 0;
364
365 bool bitmapFit = false;
366 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
367 bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY);
368 if(bitmapFit) {
369 break;
370 }
371 }
372
373 // If the new glyph didn't fit, flush the state so far and invalidate everything
374 if(!bitmapFit) {
375 flushAllAndInvalidate();
376
377 // Try to fit it again
378 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
379 bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY);
380 if(bitmapFit) {
381 break;
382 }
383 }
384
385 // if we still don't fit, something is wrong and we shouldn't draw
386 if(!bitmapFit) {
387 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
388 return false;
389 }
390 }
391
392 *retOriginX = startX;
393 *retOriginY = startY;
394
395 uint32_t endX = startX + bitmap->width;
396 uint32_t endY = startY + bitmap->rows;
397
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700398 uint32_t cacheWidth = getCacheTextureType()->getDimX();
399
400 unsigned char *cacheBuffer = (unsigned char*)mTextTexture->getPtr();
401 unsigned char *bitmapBuffer = bitmap->buffer;
402
403 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
404 for(cacheX = startX, bX = 0; cacheX < endX; cacheX ++, bX ++) {
405 for(cacheY = startY, bY = 0; cacheY < endY; cacheY ++, bY ++) {
406 unsigned char tempCol = bitmapBuffer[bY * bitmap->width + bX];
407 cacheBuffer[cacheY*cacheWidth + cacheX] = tempCol;
408 }
409 }
410
411 // This will dirty the texture and the shader so next time
412 // we draw it will upload the data
413 mTextTexture->deferedUploadToTexture(mRSC, false, 0);
Alex Sakhartchouk383e5b12010-09-23 16:16:33 -0700414 mFontShaderF->bindTexture(mRSC, 0, mTextTexture.get());
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700415
416 // Some debug code
417 /*for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
418 LOGE("Cache Line: H: %u Empty Space: %f",
419 mCacheLines[i]->mMaxHeight,
420 (1.0f - (float)mCacheLines[i]->mCurrentCol/(float)mCacheLines[i]->mMaxWidth)*100.0f);
421
422 }*/
423
424 return true;
425}
426
427void FontState::initRenderState()
428{
Alex Sakhartchouke7ae69f2010-09-14 09:50:43 -0700429 String8 shaderString("varying vec4 varTex0;\n");
430 shaderString.append("void main() {\n");
431 shaderString.append(" lowp vec4 col = UNI_Color;\n");
432 shaderString.append(" col.a = texture2D(UNI_Tex0, varTex0.xy).a;\n");
Alex Sakhartchoukc9fa3052010-10-01 15:20:41 -0700433 shaderString.append(" col.a = pow(col.a, UNI_Gamma);\n");
Alex Sakhartchouke7ae69f2010-09-14 09:50:43 -0700434 shaderString.append(" gl_FragColor = col;\n");
435 shaderString.append("}\n");
436
437 const Element *colorElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4);
Alex Sakhartchoukc9fa3052010-10-01 15:20:41 -0700438 const Element *gammaElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 1);
Alex Sakhartchouke7ae69f2010-09-14 09:50:43 -0700439 mRSC->mStateElement.elementBuilderBegin();
440 mRSC->mStateElement.elementBuilderAdd(colorElem, "Color", 1);
Alex Sakhartchoukc9fa3052010-10-01 15:20:41 -0700441 mRSC->mStateElement.elementBuilderAdd(gammaElem, "Gamma", 1);
Alex Sakhartchouke7ae69f2010-09-14 09:50:43 -0700442 const Element *constInput = mRSC->mStateElement.elementBuilderCreate(mRSC);
443
444 Type *inputType = new Type(mRSC);
445 inputType->setElement(constInput);
446 inputType->setDimX(1);
447 inputType->compute();
448
449 uint32_t tmp[4];
450 tmp[0] = RS_PROGRAM_PARAM_CONSTANT;
451 tmp[1] = (uint32_t)inputType;
452 tmp[2] = RS_PROGRAM_PARAM_TEXTURE_COUNT;
453 tmp[3] = 1;
454
455 mFontShaderFConstant.set(new Allocation(mRSC, inputType));
456 ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(),
457 shaderString.length(), tmp, 4);
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700458 mFontShaderF.set(pf);
Alex Sakhartchouk383e5b12010-09-23 16:16:33 -0700459 mFontShaderF->bindAllocation(mRSC, mFontShaderFConstant.get(), 0);
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700460
461 Sampler *sampler = new Sampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST,
462 RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP);
463 mFontSampler.set(sampler);
Alex Sakhartchouk383e5b12010-09-23 16:16:33 -0700464 mFontShaderF->bindSampler(mRSC, 0, sampler);
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700465
466 ProgramStore *fontStore = new ProgramStore(mRSC);
467 mFontProgramStore.set(fontStore);
468 mFontProgramStore->setDepthFunc(RS_DEPTH_FUNC_ALWAYS);
469 mFontProgramStore->setBlendFunc(RS_BLEND_SRC_SRC_ALPHA, RS_BLEND_DST_ONE_MINUS_SRC_ALPHA);
470 mFontProgramStore->setDitherEnable(false);
471 mFontProgramStore->setDepthMask(false);
472}
473
474void FontState::initTextTexture()
475{
476 const Element *alphaElem = Element::create(mRSC, RS_TYPE_UNSIGNED_8, RS_KIND_PIXEL_A, true, 1);
477
478 // We will allocate a texture to initially hold 32 character bitmaps
479 Type *texType = new Type(mRSC);
480 texType->setElement(alphaElem);
481 texType->setDimX(1024);
482 texType->setDimY(256);
483 texType->compute();
484
485 Allocation *cacheAlloc = new Allocation(mRSC, texType);
486 mTextTexture.set(cacheAlloc);
487 mTextTexture->deferedUploadToTexture(mRSC, false, 0);
488
489 // Split up our cache texture into lines of certain widths
490 int nextLine = 0;
491 mCacheLines.push(new CacheTextureLine(16, texType->getDimX(), nextLine, 0));
492 nextLine += mCacheLines.top()->mMaxHeight;
493 mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
494 nextLine += mCacheLines.top()->mMaxHeight;
Alex Sakhartchouk01bcef62010-08-17 11:09:49 -0700495 mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
496 nextLine += mCacheLines.top()->mMaxHeight;
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700497 mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
498 nextLine += mCacheLines.top()->mMaxHeight;
499 mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
500 nextLine += mCacheLines.top()->mMaxHeight;
501 mCacheLines.push(new CacheTextureLine(40, texType->getDimX(), nextLine, 0));
502 nextLine += mCacheLines.top()->mMaxHeight;
503 mCacheLines.push(new CacheTextureLine(texType->getDimY() - nextLine, texType->getDimX(), nextLine, 0));
504}
505
506// Avoid having to reallocate memory and render quad by quad
507void FontState::initVertexArrayBuffers()
508{
509 // Now lets write index data
510 const Element *indexElem = Element::create(mRSC, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1);
511 Type *indexType = new Type(mRSC);
512 uint32_t numIndicies = mMaxNumberOfQuads * 6;
513 indexType->setDimX(numIndicies);
514 indexType->setElement(indexElem);
515 indexType->compute();
516
517 Allocation *indexAlloc = new Allocation(mRSC, indexType);
518 uint16_t *indexPtr = (uint16_t*)indexAlloc->getPtr();
519
520 // Four verts, two triangles , six indices per quad
521 for(uint32_t i = 0; i < mMaxNumberOfQuads; i ++) {
522 int i6 = i * 6;
523 int i4 = i * 4;
524
525 indexPtr[i6 + 0] = i4 + 0;
526 indexPtr[i6 + 1] = i4 + 1;
527 indexPtr[i6 + 2] = i4 + 2;
528
529 indexPtr[i6 + 3] = i4 + 0;
530 indexPtr[i6 + 4] = i4 + 2;
531 indexPtr[i6 + 5] = i4 + 3;
532 }
533
534 indexAlloc->deferedUploadToBufferObject(mRSC);
535 mIndexBuffer.set(indexAlloc);
536
537 const Element *posElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3);
538 const Element *texElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2);
539
540 const Element *elemArray[2];
541 elemArray[0] = posElem;
542 elemArray[1] = texElem;
543
544 String8 posName("position");
545 String8 texName("texture0");
546
547 const char *nameArray[2];
548 nameArray[0] = posName.string();
549 nameArray[1] = texName.string();
550 size_t lengths[2];
551 lengths[0] = posName.size();
552 lengths[1] = texName.size();
Jason Sams46e45542010-09-02 17:35:23 -0700553 uint32_t arraySizes[2] = {1, 1};
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700554
Jason Sams46e45542010-09-02 17:35:23 -0700555 const Element *vertexDataElem = Element::create(mRSC, 2, elemArray, nameArray, lengths, arraySizes);
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700556
557 Type *vertexDataType = new Type(mRSC);
558 vertexDataType->setDimX(mMaxNumberOfQuads * 4);
559 vertexDataType->setElement(vertexDataElem);
560 vertexDataType->compute();
561
562 Allocation *vertexAlloc = new Allocation(mRSC, vertexDataType);
563 mTextMeshPtr = (float*)vertexAlloc->getPtr();
564
565 mVertexArray.set(vertexAlloc);
566}
567
568// We don't want to allocate anything unless we actually draw text
569void FontState::checkInit()
570{
571 if(mInitialized) {
572 return;
573 }
574
575 initTextTexture();
576 initRenderState();
577
578 initVertexArrayBuffers();
579
Alex Sakhartchouk35b96442010-08-18 15:46:43 -0700580 // We store a string with letters in a rough frequency of occurrence
581 mLatinPrecache = String8(" eisarntolcdugpmhbyfvkwzxjq");
582 mLatinPrecache += String8("EISARNTOLCDUGPMHBYFVKWZXJQ");
583 mLatinPrecache += String8(",.?!()-+@;:`'");
584 mLatinPrecache += String8("0123456789");
585
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700586 mInitialized = true;
587}
588
589void FontState::issueDrawCommand() {
590
591 ObjectBaseRef<const ProgramVertex> tmpV(mRSC->getVertex());
592 mRSC->setVertex(mRSC->getDefaultProgramVertex());
593
Alex Sakhartchoukd18c7442010-07-12 15:50:32 -0700594 ObjectBaseRef<const ProgramRaster> tmpR(mRSC->getRaster());
595 mRSC->setRaster(mRSC->getDefaultProgramRaster());
596
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700597 ObjectBaseRef<const ProgramFragment> tmpF(mRSC->getFragment());
598 mRSC->setFragment(mFontShaderF.get());
599
600 ObjectBaseRef<const ProgramStore> tmpPS(mRSC->getFragmentStore());
601 mRSC->setFragmentStore(mFontProgramStore.get());
602
Alex Sakhartchoukc9fa3052010-10-01 15:20:41 -0700603 if(mConstantsDirty) {
604 mFontShaderFConstant->data(mRSC, &mConstants, sizeof(mConstants));
605 mConstantsDirty = false;
Alex Sakhartchoukca5a4542010-08-05 11:24:14 -0700606 }
607
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700608 if (!mRSC->setupCheck()) {
609 mRSC->setVertex((ProgramVertex *)tmpV.get());
Alex Sakhartchoukd18c7442010-07-12 15:50:32 -0700610 mRSC->setRaster((ProgramRaster *)tmpR.get());
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700611 mRSC->setFragment((ProgramFragment *)tmpF.get());
612 mRSC->setFragmentStore((ProgramStore *)tmpPS.get());
613 return;
614 }
615
616 float *vtx = (float*)mVertexArray->getPtr();
617 float *tex = vtx + 3;
618
619 VertexArray va;
Alex Sakhartchouk886f11a2010-09-29 09:49:13 -0700620 va.add(GL_FLOAT, 3, 20, false, (uint32_t)vtx, "ATTRIB_position");
621 va.add(GL_FLOAT, 2, 20, false, (uint32_t)tex, "ATTRIB_texture0");
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700622 va.setupGL2(mRSC, &mRSC->mStateVertexArray, &mRSC->mShaderCache);
623
624 mIndexBuffer->uploadCheck(mRSC);
625 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID());
626 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, (uint16_t *)(0));
627
628 // Reset the state
629 mRSC->setVertex((ProgramVertex *)tmpV.get());
Alex Sakhartchoukd18c7442010-07-12 15:50:32 -0700630 mRSC->setRaster((ProgramRaster *)tmpR.get());
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700631 mRSC->setFragment((ProgramFragment *)tmpF.get());
632 mRSC->setFragmentStore((ProgramStore *)tmpPS.get());
633}
634
635void FontState::appendMeshQuad(float x1, float y1, float z1,
636 float u1, float v1,
637 float x2, float y2, float z2,
638 float u2, float v2,
639 float x3, float y3, float z3,
640 float u3, float v3,
641 float x4, float y4, float z4,
642 float u4, float v4)
643{
644 const uint32_t vertsPerQuad = 4;
645 const uint32_t floatsPerVert = 5;
646 float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
647
648 // Cull things that are off the screen
649 float width = (float)mRSC->getWidth();
650 float height = (float)mRSC->getHeight();
651
652 if(x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) {
653 return;
654 }
655
656 /*LOGE("V0 x: %f y: %f z: %f", x1, y1, z1);
657 LOGE("V1 x: %f y: %f z: %f", x2, y2, z2);
658 LOGE("V2 x: %f y: %f z: %f", x3, y3, z3);
659 LOGE("V3 x: %f y: %f z: %f", x4, y4, z4);*/
660
661 (*currentPos++) = x1;
662 (*currentPos++) = y1;
663 (*currentPos++) = z1;
664 (*currentPos++) = u1;
665 (*currentPos++) = v1;
666
667 (*currentPos++) = x2;
668 (*currentPos++) = y2;
669 (*currentPos++) = z2;
670 (*currentPos++) = u2;
671 (*currentPos++) = v2;
672
673 (*currentPos++) = x3;
674 (*currentPos++) = y3;
675 (*currentPos++) = z3;
676 (*currentPos++) = u3;
677 (*currentPos++) = v3;
678
679 (*currentPos++) = x4;
680 (*currentPos++) = y4;
681 (*currentPos++) = z4;
682 (*currentPos++) = u4;
683 (*currentPos++) = v4;
684
685 mCurrentQuadIndex ++;
686
687 if(mCurrentQuadIndex == mMaxNumberOfQuads) {
688 issueDrawCommand();
689 mCurrentQuadIndex = 0;
690 }
691}
692
Alex Sakhartchouk01bcef62010-08-17 11:09:49 -0700693uint32_t FontState::getRemainingCacheCapacity() {
694 uint32_t remainingCapacity = 0;
Alex Sakhartchouk35b96442010-08-18 15:46:43 -0700695 uint32_t totalPixels = 0;
Alex Sakhartchouk01bcef62010-08-17 11:09:49 -0700696 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
697 remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
698 totalPixels += mCacheLines[i]->mMaxWidth;
699 }
700 remainingCapacity = (remainingCapacity * 100) / totalPixels;
701 return remainingCapacity;
702}
703
704void FontState::precacheLatin(Font *font) {
705 // Remaining capacity is measured in %
706 uint32_t remainingCapacity = getRemainingCacheCapacity();
707 uint32_t precacheIdx = 0;
708 while(remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) {
709 font->getCachedUTFChar((int32_t)mLatinPrecache[precacheIdx]);
710 remainingCapacity = getRemainingCacheCapacity();
711 precacheIdx ++;
712 }
713}
714
715
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700716void FontState::renderText(const char *text, uint32_t len, uint32_t startIndex, int numGlyphs, int x, int y)
717{
718 checkInit();
719
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700720 // Render code here
721 Font *currentFont = mRSC->getFont();
Alex Sakhartchouka1ccecd2010-06-30 12:49:27 -0700722 if(!currentFont) {
723 if(!mDefault.get()) {
724 mDefault.set(Font::create(mRSC, "DroidSans.ttf", 16, 96));
725 }
726 currentFont = mDefault.get();
727 }
Alex Sakhartchouk3659d942010-06-30 16:53:43 -0700728 if(!currentFont) {
729 LOGE("Unable to initialize any fonts");
730 return;
731 }
732
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700733 currentFont->renderUTF(text, len, startIndex, numGlyphs, x, y);
734
735 if(mCurrentQuadIndex != 0) {
736 issueDrawCommand();
737 mCurrentQuadIndex = 0;
738 }
739}
740
741void FontState::renderText(const char *text, int x, int y)
742{
743 size_t textLen = strlen(text);
744 renderText(text, textLen, 0, -1, x, y);
745}
746
747void FontState::renderText(Allocation *alloc, int x, int y)
748{
749 if(!alloc) {
750 return;
751 }
752
753 const char *text = (const char *)alloc->getPtr();
754 size_t allocSize = alloc->getType()->getSizeBytes();
755 renderText(text, allocSize, 0, -1, x, y);
756}
757
758void FontState::renderText(Allocation *alloc, uint32_t start, int len, int x, int y)
759{
760 if(!alloc) {
761 return;
762 }
763
764 const char *text = (const char *)alloc->getPtr();
765 size_t allocSize = alloc->getType()->getSizeBytes();
766 renderText(text, allocSize, start, len, x, y);
767}
768
Alex Sakhartchouk9fc9f032010-08-04 14:45:48 -0700769void FontState::setFontColor(float r, float g, float b, float a) {
Alex Sakhartchoukc9fa3052010-10-01 15:20:41 -0700770 mConstants.mFontColor[0] = r;
771 mConstants.mFontColor[1] = g;
772 mConstants.mFontColor[2] = b;
773 mConstants.mFontColor[3] = a;
774
775 mConstants.mGamma = 1.0f;
776 const int luminance = (r * 2.0f + g * 5.0f + b) / 8.0f;
777 if (luminance <= mBlackThreshold) {
778 mConstants.mGamma = mBlackGamma;
779 } else if (luminance >= mWhiteThreshold) {
780 mConstants.mGamma = mWhiteGamma;
781 }
782 mConstantsDirty = true;
Alex Sakhartchouk9fc9f032010-08-04 14:45:48 -0700783}
784
Alex Sakhartchoukca5a4542010-08-05 11:24:14 -0700785void FontState::getFontColor(float *r, float *g, float *b, float *a) const {
Alex Sakhartchoukc9fa3052010-10-01 15:20:41 -0700786 *r = mConstants.mFontColor[0];
787 *g = mConstants.mFontColor[1];
788 *b = mConstants.mFontColor[2];
789 *a = mConstants.mFontColor[3];
Alex Sakhartchoukca5a4542010-08-05 11:24:14 -0700790}
791
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700792void FontState::deinit(Context *rsc)
793{
Alex Sakhartchouka1ccecd2010-06-30 12:49:27 -0700794 mInitialized = false;
795
Stephen Hines01b7d292010-09-28 15:45:45 -0700796 mFontShaderFConstant.clear();
797
Alex Sakhartchouka1ccecd2010-06-30 12:49:27 -0700798 mIndexBuffer.clear();
799 mVertexArray.clear();
800
801 mFontShaderF.clear();
802 mFontSampler.clear();
803 mFontProgramStore.clear();
804
805 mTextTexture.clear();
806 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
807 delete mCacheLines[i];
808 }
809 mCacheLines.clear();
810
811 mDefault.clear();
812
Alex Sakhartchouk3659d942010-06-30 16:53:43 -0700813 Vector<Font*> fontsToDereference = mActiveFonts;
814 for(uint32_t i = 0; i < fontsToDereference.size(); i ++) {
815 fontsToDereference[i]->zeroUserRef();
816 }
817
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700818 if(mLibrary) {
819 FT_Done_FreeType( mLibrary );
Alex Sakhartchouk3659d942010-06-30 16:53:43 -0700820 mLibrary = NULL;
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700821 }
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700822}
823
824namespace android {
825namespace renderscript {
826
827RsFont rsi_FontCreateFromFile(Context *rsc, char const *name, uint32_t fontSize, uint32_t dpi)
828{
Alex Sakhartchouka1ccecd2010-06-30 12:49:27 -0700829 Font *newFont = Font::create(rsc, name, fontSize, dpi);
830 if(newFont) {
831 newFont->incUserRef();
832 }
833 return newFont;
Alex Sakhartchoukd3e0ad42010-06-24 17:15:34 -0700834}
835
836} // renderscript
837} // android