blob: 5740e658cd8e87e1b3c8c0a9b6fb64d628c4c450 [file] [log] [blame]
Alex Sakhartchouk9b949fc2010-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"
26#include FT_BITMAP_H
27
28#include <GLES/gl.h>
29#include <GLES/glext.h>
30#include <GLES2/gl2.h>
31#include <GLES2/gl2ext.h>
32
33using namespace android;
34using namespace android::renderscript;
35
36Font::Font(Context *rsc) : ObjectBase(rsc), mCachedGlyphs(NULL)
37{
38 mInitialized = false;
39 mHasKerning = false;
40}
41
42bool Font::init(const char *name, uint32_t fontSize, uint32_t dpi)
43{
44 if(mInitialized) {
45 LOGE("Reinitialization of fonts not supported");
46 return false;
47 }
48
49 String8 fontsDir("/fonts/");
50 String8 fullPath(getenv("ANDROID_ROOT"));
51 fullPath += fontsDir;
52 fullPath += name;
53
54 FT_Error error = FT_New_Face(mRSC->mStateFont.mLibrary, fullPath.string(), 0, &mFace);
55 if(error) {
56 LOGE("Unable to initialize font %s", fullPath.string());
57 return false;
58 }
59
60 mFontName = name;
61 mFontSize = fontSize;
62 mDpi = dpi;
63
64 //LOGE("Font initialized: %s", fullPath.string());
65
66 error = FT_Set_Char_Size(mFace, fontSize * 64, 0, dpi, 0);
67 if(error) {
68 LOGE("Unable to set font size on %s", fullPath.string());
69 return false;
70 }
71
72 mHasKerning = FT_HAS_KERNING(mFace);
73 LOGE("Kerning: %i", mHasKerning);
74
75 mInitialized = true;
76 return true;
77}
78
79void Font::invalidateTextureCache()
80{
81 for(uint32_t i = 0; i < mCachedGlyphs.size(); i ++) {
82 mCachedGlyphs.valueAt(i)->mIsValid = false;
83 }
84}
85
86void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y)
87{
88 FontState *state = &mRSC->mStateFont;
89
90 int nPenX = x + glyph->mBitmapLeft;
91 int nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight;
92
93 state->appendMeshQuad(nPenX, nPenY, 0,
94 glyph->mBitmapMinU, glyph->mBitmapMaxV,
95
96 nPenX + (int)glyph->mBitmapWidth, nPenY, 0,
97 glyph->mBitmapMaxU, glyph->mBitmapMaxV,
98
99 nPenX + (int)glyph->mBitmapWidth, nPenY - (int)glyph->mBitmapHeight, 0,
100 glyph->mBitmapMaxU, glyph->mBitmapMinV,
101
102 nPenX, nPenY - (int)glyph->mBitmapHeight, 0,
103 glyph->mBitmapMinU, glyph->mBitmapMinV);
104}
105
106void Font::renderUTF(const char *text, uint32_t len, uint32_t start, int numGlyphs, int x, int y)
107{
108 if(!mInitialized || numGlyphs == 0 || text == NULL || len == 0) {
109 return;
110 }
111
112 int penX = x, penY = y;
113 int glyphsLeft = 1;
114 if(numGlyphs > 0) {
115 glyphsLeft = numGlyphs;
116 }
117
118 size_t index = start;
119 size_t nextIndex = 0;
120
121 while (glyphsLeft > 0) {
122
123 int32_t utfChar = utf32_at(text, len, index, &nextIndex);
124
125 // Reached the end of the string or encountered
126 if(utfChar < 0) {
127 break;
128 }
129
130 // Move to the next character in the array
131 index = nextIndex;
132
133 CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor((uint32_t)utfChar);
134
135 if(cachedGlyph == NULL) {
136 cachedGlyph = cacheGlyph((uint32_t)utfChar);
137 }
138 // Is the glyph still in texture cache?
139 if(!cachedGlyph->mIsValid) {
140 updateGlyphCache(cachedGlyph);
141 }
142
143 // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
144 if(cachedGlyph->mIsValid) {
145 drawCachedGlyph(cachedGlyph, penX, penY);
146 }
147
148 penX += (cachedGlyph->mAdvance.x >> 6);
149
150 // If we were given a specific number of glyphs, decrement
151 if(numGlyphs > 0) {
152 glyphsLeft --;
153 }
154 }
155}
156
157void Font::updateGlyphCache(CachedGlyphInfo *glyph)
158{
159 if(!glyph->mBitmapValid) {
160
161 FT_Error error = FT_Load_Glyph( mFace, glyph->mGlyphIndex, FT_LOAD_RENDER );
162 if(error) {
163 LOGE("Couldn't load glyph.");
164 return;
165 }
166
167 glyph->mAdvance = mFace->glyph->advance;
168 glyph->mBitmapLeft = mFace->glyph->bitmap_left;
169 glyph->mBitmapTop = mFace->glyph->bitmap_top;
170
171 FT_Bitmap *bitmap = &mFace->glyph->bitmap;
172
173 FT_Bitmap_New(&glyph->mBitmap);
174 FT_Bitmap_Copy(mRSC->mStateFont.mLibrary, bitmap, &glyph->mBitmap);
175
176 glyph->mBitmapValid = true;
177 }
178
179 // Now copy the bitmap into the cache texture
180 uint32_t startX = 0;
181 uint32_t startY = 0;
182
183 // Let the font state figure out where to put the bitmap
184 FontState *state = &mRSC->mStateFont;
185 glyph->mIsValid = state->cacheBitmap(&glyph->mBitmap, &startX, &startY);
186
187 if(!glyph->mIsValid) {
188 return;
189 }
190
191 uint32_t endX = startX + glyph->mBitmap.width;
192 uint32_t endY = startY + glyph->mBitmap.rows;
193
194 glyph->mBitmapMinX = startX;
195 glyph->mBitmapMinY = startY;
196 glyph->mBitmapWidth = glyph->mBitmap.width;
197 glyph->mBitmapHeight = glyph->mBitmap.rows;
198
199 uint32_t cacheWidth = state->getCacheTextureType()->getDimX();
200 uint32_t cacheHeight = state->getCacheTextureType()->getDimY();
201
202 glyph->mBitmapMinU = (float)startX / (float)cacheWidth;
203 glyph->mBitmapMinV = (float)startY / (float)cacheHeight;
204 glyph->mBitmapMaxU = (float)endX / (float)cacheWidth;
205 glyph->mBitmapMaxV = (float)endY / (float)cacheHeight;
206}
207
208Font::CachedGlyphInfo *Font::cacheGlyph(uint32_t glyph)
209{
210 CachedGlyphInfo *newGlyph = new CachedGlyphInfo();
211 mCachedGlyphs.add(glyph, newGlyph);
212
213 newGlyph->mGlyphIndex = FT_Get_Char_Index(mFace, glyph);
214 newGlyph->mIsValid = false;
215 newGlyph->mBitmapValid = false;
216
217 //LOGE("Glyph = %c, face index: %u", (unsigned char)glyph, newGlyph->mGlyphIndex);
218
219 updateGlyphCache(newGlyph);
220
221 return newGlyph;
222}
223
224Font * Font::create(Context *rsc, const char *name, uint32_t fontSize, uint32_t dpi)
225{
226 Vector<Font*> &activeFonts = rsc->mStateFont.mActiveFonts;
227
228 for(uint32_t i = 0; i < activeFonts.size(); i ++) {
229 Font *ithFont = activeFonts[i];
230 if(ithFont->mFontName == name && ithFont->mFontSize == fontSize && ithFont->mDpi == dpi) {
231 ithFont->incUserRef();
232 return ithFont;
233 }
234 }
235
236 Font *newFont = new Font(rsc);
237 bool isInitialized = newFont->init(name, fontSize, dpi);
238 if(isInitialized) {
239 newFont->incUserRef();
240 activeFonts.push(newFont);
241 return newFont;
242 }
243
244 delete newFont;
245 return NULL;
246
247}
248
249Font::~Font()
250{
251 if(mFace) {
252 FT_Done_Face(mFace);
253 }
254
255 for (uint32_t ct = 0; ct < mRSC->mStateFont.mActiveFonts.size(); ct++) {
256 if (mRSC->mStateFont.mActiveFonts[ct] == this) {
257 mRSC->mStateFont.mActiveFonts.removeAt(ct);
258 break;
259 }
260 }
261
262 for(uint32_t i = 0; i < mCachedGlyphs.size(); i ++) {
263 CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i);
264 if(glyph->mBitmapValid) {
265 FT_Bitmap_Done(mRSC->mStateFont.mLibrary, &glyph->mBitmap);
266 }
267 delete glyph;
268 }
269}
270
271FontState::FontState()
272{
273 mInitialized = false;
274 mMaxNumberOfQuads = 1024;
275 mCurrentQuadIndex = 0;
276 mRSC = NULL;
277}
278
279FontState::~FontState()
280{
281 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
282 delete mCacheLines[i];
283 }
284
285 rsAssert(!mActiveFonts.size());
286}
287
288void FontState::init(Context *rsc)
289{
290 FT_Error error;
291
292 if(!mLibrary) {
293 error = FT_Init_FreeType(&mLibrary);
294 if(error) {
295 LOGE("Unable to initialize freetype");
296 return;
297 }
298 }
299
300 mRSC = rsc;
301
302 mDefault.set(Font::create(rsc, "DroidSans.ttf", 16, 96));
303}
304
305void FontState::flushAllAndInvalidate()
306{
307 if(mCurrentQuadIndex != 0) {
308 issueDrawCommand();
309 mCurrentQuadIndex = 0;
310 }
311 for(uint32_t i = 0; i < mActiveFonts.size(); i ++) {
312 mActiveFonts[i]->invalidateTextureCache();
313 }
314 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
315 mCacheLines[i]->mCurrentCol = 0;
316 }
317}
318
319bool FontState::cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY)
320{
321 // If the glyph is too tall, don't cache it
322 if((uint32_t)bitmap->rows > mCacheLines[mCacheLines.size()-1]->mMaxHeight) {
323 LOGE("Font size to large to fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
324 return false;
325 }
326
327 // Now copy the bitmap into the cache texture
328 uint32_t startX = 0;
329 uint32_t startY = 0;
330
331 bool bitmapFit = false;
332 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
333 bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY);
334 if(bitmapFit) {
335 break;
336 }
337 }
338
339 // If the new glyph didn't fit, flush the state so far and invalidate everything
340 if(!bitmapFit) {
341 flushAllAndInvalidate();
342
343 // Try to fit it again
344 for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
345 bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY);
346 if(bitmapFit) {
347 break;
348 }
349 }
350
351 // if we still don't fit, something is wrong and we shouldn't draw
352 if(!bitmapFit) {
353 LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
354 return false;
355 }
356 }
357
358 *retOriginX = startX;
359 *retOriginY = startY;
360
361 uint32_t endX = startX + bitmap->width;
362 uint32_t endY = startY + bitmap->rows;
363
364 //LOGE("Bitmap width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows);
365
366 uint32_t cacheWidth = getCacheTextureType()->getDimX();
367
368 unsigned char *cacheBuffer = (unsigned char*)mTextTexture->getPtr();
369 unsigned char *bitmapBuffer = bitmap->buffer;
370
371 uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
372 for(cacheX = startX, bX = 0; cacheX < endX; cacheX ++, bX ++) {
373 for(cacheY = startY, bY = 0; cacheY < endY; cacheY ++, bY ++) {
374 unsigned char tempCol = bitmapBuffer[bY * bitmap->width + bX];
375 cacheBuffer[cacheY*cacheWidth + cacheX] = tempCol;
376 }
377 }
378
379 // This will dirty the texture and the shader so next time
380 // we draw it will upload the data
381 mTextTexture->deferedUploadToTexture(mRSC, false, 0);
382 mFontShaderF->bindTexture(0, mTextTexture.get());
383
384 // Some debug code
385 /*for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
386 LOGE("Cache Line: H: %u Empty Space: %f",
387 mCacheLines[i]->mMaxHeight,
388 (1.0f - (float)mCacheLines[i]->mCurrentCol/(float)mCacheLines[i]->mMaxWidth)*100.0f);
389
390 }*/
391
392 return true;
393}
394
395void FontState::initRenderState()
396{
397 uint32_t tmp[5] = {
398 RS_TEX_ENV_MODE_REPLACE, 1,
399 RS_TEX_ENV_MODE_NONE, 0,
400 0
401 };
402 ProgramFragment *pf = new ProgramFragment(mRSC, tmp, 5);
403 mFontShaderF.set(pf);
404 mFontShaderF->init(mRSC);
405
406 Sampler *sampler = new Sampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST,
407 RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP);
408 mFontSampler.set(sampler);
409 mFontShaderF->bindSampler(0, sampler);
410
411 ProgramStore *fontStore = new ProgramStore(mRSC);
412 mFontProgramStore.set(fontStore);
413 mFontProgramStore->setDepthFunc(RS_DEPTH_FUNC_ALWAYS);
414 mFontProgramStore->setBlendFunc(RS_BLEND_SRC_SRC_ALPHA, RS_BLEND_DST_ONE_MINUS_SRC_ALPHA);
415 mFontProgramStore->setDitherEnable(false);
416 mFontProgramStore->setDepthMask(false);
417}
418
419void FontState::initTextTexture()
420{
421 const Element *alphaElem = Element::create(mRSC, RS_TYPE_UNSIGNED_8, RS_KIND_PIXEL_A, true, 1);
422
423 // We will allocate a texture to initially hold 32 character bitmaps
424 Type *texType = new Type(mRSC);
425 texType->setElement(alphaElem);
426 texType->setDimX(1024);
427 texType->setDimY(256);
428 texType->compute();
429
430 Allocation *cacheAlloc = new Allocation(mRSC, texType);
431 mTextTexture.set(cacheAlloc);
432 mTextTexture->deferedUploadToTexture(mRSC, false, 0);
433
434 // Split up our cache texture into lines of certain widths
435 int nextLine = 0;
436 mCacheLines.push(new CacheTextureLine(16, texType->getDimX(), nextLine, 0));
437 nextLine += mCacheLines.top()->mMaxHeight;
438 mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
439 nextLine += mCacheLines.top()->mMaxHeight;
440 mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
441 nextLine += mCacheLines.top()->mMaxHeight;
442 mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0));
443 nextLine += mCacheLines.top()->mMaxHeight;
444 mCacheLines.push(new CacheTextureLine(40, texType->getDimX(), nextLine, 0));
445 nextLine += mCacheLines.top()->mMaxHeight;
446 mCacheLines.push(new CacheTextureLine(texType->getDimY() - nextLine, texType->getDimX(), nextLine, 0));
447}
448
449// Avoid having to reallocate memory and render quad by quad
450void FontState::initVertexArrayBuffers()
451{
452 // Now lets write index data
453 const Element *indexElem = Element::create(mRSC, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1);
454 Type *indexType = new Type(mRSC);
455 uint32_t numIndicies = mMaxNumberOfQuads * 6;
456 indexType->setDimX(numIndicies);
457 indexType->setElement(indexElem);
458 indexType->compute();
459
460 Allocation *indexAlloc = new Allocation(mRSC, indexType);
461 uint16_t *indexPtr = (uint16_t*)indexAlloc->getPtr();
462
463 // Four verts, two triangles , six indices per quad
464 for(uint32_t i = 0; i < mMaxNumberOfQuads; i ++) {
465 int i6 = i * 6;
466 int i4 = i * 4;
467
468 indexPtr[i6 + 0] = i4 + 0;
469 indexPtr[i6 + 1] = i4 + 1;
470 indexPtr[i6 + 2] = i4 + 2;
471
472 indexPtr[i6 + 3] = i4 + 0;
473 indexPtr[i6 + 4] = i4 + 2;
474 indexPtr[i6 + 5] = i4 + 3;
475 }
476
477 indexAlloc->deferedUploadToBufferObject(mRSC);
478 mIndexBuffer.set(indexAlloc);
479
480 const Element *posElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3);
481 const Element *texElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2);
482
483 const Element *elemArray[2];
484 elemArray[0] = posElem;
485 elemArray[1] = texElem;
486
487 String8 posName("position");
488 String8 texName("texture0");
489
490 const char *nameArray[2];
491 nameArray[0] = posName.string();
492 nameArray[1] = texName.string();
493 size_t lengths[2];
494 lengths[0] = posName.size();
495 lengths[1] = texName.size();
496
497 const Element *vertexDataElem = Element::create(mRSC, 2, elemArray, nameArray, lengths);
498
499 Type *vertexDataType = new Type(mRSC);
500 vertexDataType->setDimX(mMaxNumberOfQuads * 4);
501 vertexDataType->setElement(vertexDataElem);
502 vertexDataType->compute();
503
504 Allocation *vertexAlloc = new Allocation(mRSC, vertexDataType);
505 mTextMeshPtr = (float*)vertexAlloc->getPtr();
506
507 mVertexArray.set(vertexAlloc);
508}
509
510// We don't want to allocate anything unless we actually draw text
511void FontState::checkInit()
512{
513 if(mInitialized) {
514 return;
515 }
516
517 initTextTexture();
518 initRenderState();
519
520 initVertexArrayBuffers();
521
522 /*mTextMeshRefs = new ObjectBaseRef<SimpleMesh>[mNumMeshes];
523
524 for(uint32_t i = 0; i < mNumMeshes; i ++){
525 SimpleMesh *textMesh = createTextMesh();
526 mTextMeshRefs[i].set(textMesh);
527 }*/
528
529 mInitialized = true;
530}
531
532void FontState::issueDrawCommand() {
533
534 ObjectBaseRef<const ProgramVertex> tmpV(mRSC->getVertex());
535 mRSC->setVertex(mRSC->getDefaultProgramVertex());
536
537 ObjectBaseRef<const ProgramFragment> tmpF(mRSC->getFragment());
538 mRSC->setFragment(mFontShaderF.get());
539
540 ObjectBaseRef<const ProgramStore> tmpPS(mRSC->getFragmentStore());
541 mRSC->setFragmentStore(mFontProgramStore.get());
542
543 if (!mRSC->setupCheck()) {
544 mRSC->setVertex((ProgramVertex *)tmpV.get());
545 mRSC->setFragment((ProgramFragment *)tmpF.get());
546 mRSC->setFragmentStore((ProgramStore *)tmpPS.get());
547 return;
548 }
549
550 float *vtx = (float*)mVertexArray->getPtr();
551 float *tex = vtx + 3;
552
553 VertexArray va;
554 va.add(GL_FLOAT, 3, 20, false, (uint32_t)vtx, "position");
555 va.add(GL_FLOAT, 2, 20, false, (uint32_t)tex, "texture0");
556 va.setupGL2(mRSC, &mRSC->mStateVertexArray, &mRSC->mShaderCache);
557
558 mIndexBuffer->uploadCheck(mRSC);
559 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID());
560 glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, (uint16_t *)(0));
561
562 // Reset the state
563 mRSC->setVertex((ProgramVertex *)tmpV.get());
564 mRSC->setFragment((ProgramFragment *)tmpF.get());
565 mRSC->setFragmentStore((ProgramStore *)tmpPS.get());
566}
567
568void FontState::appendMeshQuad(float x1, float y1, float z1,
569 float u1, float v1,
570 float x2, float y2, float z2,
571 float u2, float v2,
572 float x3, float y3, float z3,
573 float u3, float v3,
574 float x4, float y4, float z4,
575 float u4, float v4)
576{
577 const uint32_t vertsPerQuad = 4;
578 const uint32_t floatsPerVert = 5;
579 float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
580
581 // Cull things that are off the screen
582 float width = (float)mRSC->getWidth();
583 float height = (float)mRSC->getHeight();
584
585 if(x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) {
586 return;
587 }
588
589 /*LOGE("V0 x: %f y: %f z: %f", x1, y1, z1);
590 LOGE("V1 x: %f y: %f z: %f", x2, y2, z2);
591 LOGE("V2 x: %f y: %f z: %f", x3, y3, z3);
592 LOGE("V3 x: %f y: %f z: %f", x4, y4, z4);*/
593
594 (*currentPos++) = x1;
595 (*currentPos++) = y1;
596 (*currentPos++) = z1;
597 (*currentPos++) = u1;
598 (*currentPos++) = v1;
599
600 (*currentPos++) = x2;
601 (*currentPos++) = y2;
602 (*currentPos++) = z2;
603 (*currentPos++) = u2;
604 (*currentPos++) = v2;
605
606 (*currentPos++) = x3;
607 (*currentPos++) = y3;
608 (*currentPos++) = z3;
609 (*currentPos++) = u3;
610 (*currentPos++) = v3;
611
612 (*currentPos++) = x4;
613 (*currentPos++) = y4;
614 (*currentPos++) = z4;
615 (*currentPos++) = u4;
616 (*currentPos++) = v4;
617
618 mCurrentQuadIndex ++;
619
620 if(mCurrentQuadIndex == mMaxNumberOfQuads) {
621 issueDrawCommand();
622 mCurrentQuadIndex = 0;
623 }
624}
625
626void FontState::renderText(const char *text, uint32_t len, uint32_t startIndex, int numGlyphs, int x, int y)
627{
628 checkInit();
629
630 String8 text8(text);
631
632 // Render code here
633 Font *currentFont = mRSC->getFont();
634 currentFont->renderUTF(text, len, startIndex, numGlyphs, x, y);
635
636 if(mCurrentQuadIndex != 0) {
637 issueDrawCommand();
638 mCurrentQuadIndex = 0;
639 }
640}
641
642void FontState::renderText(const char *text, int x, int y)
643{
644 size_t textLen = strlen(text);
645 renderText(text, textLen, 0, -1, x, y);
646}
647
648void FontState::renderText(Allocation *alloc, int x, int y)
649{
650 if(!alloc) {
651 return;
652 }
653
654 const char *text = (const char *)alloc->getPtr();
655 size_t allocSize = alloc->getType()->getSizeBytes();
656 renderText(text, allocSize, 0, -1, x, y);
657}
658
659void FontState::renderText(Allocation *alloc, uint32_t start, int len, int x, int y)
660{
661 if(!alloc) {
662 return;
663 }
664
665 const char *text = (const char *)alloc->getPtr();
666 size_t allocSize = alloc->getType()->getSizeBytes();
667 renderText(text, allocSize, start, len, x, y);
668}
669
670void FontState::deinit(Context *rsc)
671{
672 if(mLibrary) {
673 FT_Done_FreeType( mLibrary );
674 }
675
676 delete mDefault.get();
677 mDefault.clear();
678}
679
680namespace android {
681namespace renderscript {
682
683RsFont rsi_FontCreateFromFile(Context *rsc, char const *name, uint32_t fontSize, uint32_t dpi)
684{
685 return Font::create(rsc, name, fontSize, dpi);
686}
687
688} // renderscript
689} // android