From 9b949fce39f0f39ce9275b71d7c347210775e7a8 Mon Sep 17 00:00:00 2001 From: Alex Sakhartchouk Date: Thu, 24 Jun 2010 17:15:34 -0700 Subject: [PATCH] Adding freetype font rendering to renderscript. Change-Id: I3a10ffe27092a41df156341c9cb3f7aa19c49f19 --- .../java/android/renderscript/Allocation.java | 15 + graphics/java/android/renderscript/Font.java | 59 ++ .../android/renderscript/RenderScript.java | 2 + .../jni/android_renderscript_RenderScript.cpp | 24 +- libs/rs/Android.mk | 16 +- libs/rs/RenderScript.h | 1 + libs/rs/ScriptC_Modelviewer.java | 118 +++ .../java/ModelViewer/res/raw/modelviewer.rs | 17 +- .../ModelViewer/res/raw/modelviewer_bc.bc | Bin 1688 -> 6220 bytes .../android/modelviewer/ModelViewerRS.java | 16 +- .../android/modelviewer/ModelViewerView.java | 1 - .../modelviewer/ScriptC_Modelviewer.java | 22 + libs/rs/rs.spec | 11 + libs/rs/rsContext.cpp | 23 + libs/rs/rsContext.h | 9 +- libs/rs/rsFileA3D.cpp | 1 + libs/rs/rsFont.cpp | 689 ++++++++++++++++++ libs/rs/rsFont.h | 209 ++++++ libs/rs/rsScriptC_LibGL.cpp | 23 + libs/rs/rsType.cpp | 4 +- libs/rs/scriptc/rs_graphics.rsh | 4 + libs/rs/scriptc/rs_types.rsh | 1 + 22 files changed, 1250 insertions(+), 15 deletions(-) create mode 100644 graphics/java/android/renderscript/Font.java create mode 100644 libs/rs/ScriptC_Modelviewer.java create mode 100644 libs/rs/rsFont.cpp create mode 100644 libs/rs/rsFont.h diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java index d32a0b5993440..ddb2abf7d7a43 100644 --- a/graphics/java/android/renderscript/Allocation.java +++ b/graphics/java/android/renderscript/Allocation.java @@ -386,6 +386,21 @@ public class Allocation extends BaseObj { Bitmap b = BitmapFactory.decodeResource(res, id, mBitmapOptions); return createFromBitmapBoxed(rs, b, dstFmt, genMips); } + + static public Allocation createFromString(RenderScript rs, String str) + throws IllegalArgumentException { + byte[] allocArray = null; + try { + allocArray = str.getBytes("UTF-8"); + Allocation alloc = Allocation.createSized(rs, Element.U8(rs), allocArray.length); + alloc.data(allocArray); + return alloc; + } + catch (Exception e) { + Log.e("rs", "could not convert string to utf-8"); + } + return null; + } } diff --git a/graphics/java/android/renderscript/Font.java b/graphics/java/android/renderscript/Font.java new file mode 100644 index 0000000000000..41f8827bef58e --- /dev/null +++ b/graphics/java/android/renderscript/Font.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.renderscript; + +import java.io.IOException; +import java.io.InputStream; + +import android.content.res.Resources; +import android.content.res.AssetManager; +import android.util.Log; +import android.util.TypedValue; + +/** + * @hide + * + **/ +public class Font extends BaseObj { + + Font(int id, RenderScript rs) { + super(rs); + mID = id; + } + + static public Font create(RenderScript rs, Resources res, String fileName, int size) + throws IllegalArgumentException { + + rs.validate(); + try { + int dpi = res.getDisplayMetrics().densityDpi; + int fontId = rs.nFontCreateFromFile(fileName, size, dpi); + + if(fontId == 0) { + throw new IllegalStateException("Load loading a font"); + } + Font rsFont = new Font(fontId, rs); + + return rsFont; + + } catch (Exception e) { + // Ignore + } + + return null; + } +} diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java index 5f2050e25aae9..b2e5b02cadec7 100644 --- a/graphics/java/android/renderscript/RenderScript.java +++ b/graphics/java/android/renderscript/RenderScript.java @@ -123,6 +123,8 @@ public class RenderScript { native void nFileA3DGetIndexEntries(int fileA3D, int numEntries, int[] IDs, String[] names); native int nFileA3DGetEntryByIndex(int fileA3D, int index); + native int nFontCreateFromFile(String fileName, int size, int dpi); + native void nAdapter1DBindAllocation(int ad, int alloc); native void nAdapter1DSetConstraint(int ad, int dim, int value); native void nAdapter1DData(int ad, int[] d); diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp index 67c25a84e2236..c61a215950844 100644 --- a/graphics/jni/android_renderscript_RenderScript.cpp +++ b/graphics/jni/android_renderscript_RenderScript.cpp @@ -762,6 +762,19 @@ nFileA3DGetEntryByIndex(JNIEnv *_env, jobject _this, jint fileA3D, jint index) return id; } +// ----------------------------------- + +static int +nFontCreateFromFile(JNIEnv *_env, jobject _this, jstring fileName, jint fontSize, jint dpi) +{ + RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); + const char* fileNameUTF = _env->GetStringUTFChars(fileName, NULL); + + jint id = (jint)rsFontCreateFromFile(con, fileNameUTF, fontSize, dpi); + return id; +} + + // ----------------------------------- static void @@ -1395,6 +1408,12 @@ static JNINativeMethod methods[] = { {"nContextDeinitToClient", "()V", (void*)nContextDeinitToClient }, {"nFileOpen", "([B)I", (void*)nFileOpen }, +{"nFileA3DCreateFromAssetStream", "(I)I", (void*)nFileA3DCreateFromAssetStream }, +{"nFileA3DGetNumIndexEntries", "(I)I", (void*)nFileA3DGetNumIndexEntries }, +{"nFileA3DGetIndexEntries", "(II[I[Ljava/lang/String;)V", (void*)nFileA3DGetIndexEntries }, +{"nFileA3DGetEntryByIndex", "(II)I", (void*)nFileA3DGetEntryByIndex }, + +{"nFontCreateFromFile", "(Ljava/lang/String;II)I", (void*)nFontCreateFromFile }, {"nElementCreate", "(IIZI)I", (void*)nElementCreate }, {"nElementCreate2", "([I[Ljava/lang/String;)I", (void*)nElementCreate2 }, @@ -1494,11 +1513,6 @@ static JNINativeMethod methods[] = { {"nSimpleMeshBindVertex", "(III)V", (void*)nSimpleMeshBindVertex }, {"nSimpleMeshBindIndex", "(II)V", (void*)nSimpleMeshBindIndex }, -{"nFileA3DCreateFromAssetStream", "(I)I", (void*)nFileA3DCreateFromAssetStream }, -{"nFileA3DGetNumIndexEntries", "(I)I", (void*)nFileA3DGetNumIndexEntries }, -{"nFileA3DGetIndexEntries", "(II[I[Ljava/lang/String;)V", (void*)nFileA3DGetIndexEntries }, -{"nFileA3DGetEntryByIndex", "(II)I", (void*)nFileA3DGetEntryByIndex }, - }; static int registerFuncs(JNIEnv *_env) diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk index 3f014157a1821..7afc4f24c59ce 100644 --- a/libs/rs/Android.mk +++ b/libs/rs/Android.mk @@ -81,13 +81,14 @@ LOCAL_SRC_FILES:= \ rsContext.cpp \ rsDevice.cpp \ rsElement.cpp \ - rsFileA3D.cpp \ + rsFileA3D.cpp \ + rsFont.cpp \ rsLight.cpp \ rsLocklessFifo.cpp \ rsObjectBase.cpp \ rsMatrix.cpp \ - rsMesh.cpp \ - rsMutex.cpp \ + rsMesh.cpp \ + rsMutex.cpp \ rsProgram.cpp \ rsProgramFragment.cpp \ rsProgramStore.cpp \ @@ -99,16 +100,21 @@ LOCAL_SRC_FILES:= \ rsScriptC_Lib.cpp \ rsScriptC_LibCL.cpp \ rsScriptC_LibGL.cpp \ - rsShaderCache.cpp \ + rsShaderCache.cpp \ rsSignal.cpp \ rsSimpleMesh.cpp \ - rsStream.cpp \ + rsStream.cpp \ rsThreadIO.cpp \ rsType.cpp \ rsVertexArray.cpp LOCAL_SHARED_LIBRARIES += libcutils libutils libEGL libGLESv1_CM libGLESv2 libui libbcc + +LOCAL_STATIC_LIBRARIES := libft2 + +LOCAL_C_INCLUDES += external/freetype/include + LOCAL_LDLIBS := -lpthread -ldl LOCAL_MODULE:= libRS LOCAL_MODULE_TAGS := optional diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h index 6302b906c8dc3..14283183e970b 100644 --- a/libs/rs/RenderScript.h +++ b/libs/rs/RenderScript.h @@ -35,6 +35,7 @@ typedef void * RsContext; typedef void * RsDevice; typedef void * RsElement; typedef void * RsFile; +typedef void * RsFont; typedef void * RsSampler; typedef void * RsScript; typedef void * RsSimpleMesh; diff --git a/libs/rs/ScriptC_Modelviewer.java b/libs/rs/ScriptC_Modelviewer.java new file mode 100644 index 0000000000000..083fb607f4183 --- /dev/null +++ b/libs/rs/ScriptC_Modelviewer.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.modelviewer; + +import android.renderscript.*; +import android.content.res.Resources; +import android.util.Log; + +public class ScriptC_Modelviewer extends ScriptC { + // Constructor + public ScriptC_Modelviewer(RenderScript rs, Resources resources, int id, boolean isRoot) { + super(rs, resources, id, isRoot); + } + + private final static int mExportVarIdx_gPVBackground = 0; + private ProgramVertex mExportVar_gPVBackground; + public void set_gPVBackground(ProgramVertex v) { + mExportVar_gPVBackground = v; + setVar(mExportVarIdx_gPVBackground, (v == null) ? 0 : v.getID()); + } + + public ProgramVertex get_gPVBackground() { + return mExportVar_gPVBackground; + } + + private final static int mExportVarIdx_gPFBackground = 1; + private ProgramFragment mExportVar_gPFBackground; + public void set_gPFBackground(ProgramFragment v) { + mExportVar_gPFBackground = v; + setVar(mExportVarIdx_gPFBackground, (v == null) ? 0 : v.getID()); + } + + public ProgramFragment get_gPFBackground() { + return mExportVar_gPFBackground; + } + + private final static int mExportVarIdx_gTGrid = 2; + private Allocation mExportVar_gTGrid; + public void set_gTGrid(Allocation v) { + mExportVar_gTGrid = v; + setVar(mExportVarIdx_gTGrid, (v == null) ? 0 : v.getID()); + } + + public Allocation get_gTGrid() { + return mExportVar_gTGrid; + } + + private final static int mExportVarIdx_gTestMesh = 3; + private SimpleMesh mExportVar_gTestMesh; + public void set_gTestMesh(SimpleMesh v) { + mExportVar_gTestMesh = v; + setVar(mExportVarIdx_gTestMesh, (v == null) ? 0 : v.getID()); + } + + public SimpleMesh get_gTestMesh() { + return mExportVar_gTestMesh; + } + + private final static int mExportVarIdx_gPFSBackground = 4; + private ProgramStore mExportVar_gPFSBackground; + public void set_gPFSBackground(ProgramStore v) { + mExportVar_gPFSBackground = v; + setVar(mExportVarIdx_gPFSBackground, (v == null) ? 0 : v.getID()); + } + + public ProgramStore get_gPFSBackground() { + return mExportVar_gPFSBackground; + } + + private final static int mExportVarIdx_gRotate = 5; + private float mExportVar_gRotate; + public void set_gRotate(float v) { + mExportVar_gRotate = v; + setVar(mExportVarIdx_gRotate, v); + } + + public float get_gRotate() { + return mExportVar_gRotate; + } + + private final static int mExportVarIdx_gItalic = 6; + private Font mExportVar_gItalic; + public void set_gItalic(Font v) { + mExportVar_gItalic = v; + setVar(mExportVarIdx_gItalic, v); + } + + public Font get_gItalic() { + return mExportVar_gItalic; + } + + private final static int mExportVarIdx_gTextAlloc = 7; + private Allocation mExportVar_gTextAlloc; + public void set_gTextAlloc(Allocation v) { + mExportVar_gTextAlloc = v; + setVar(mExportVarIdx_gTextAlloc, (v == null) ? 0 : v.getID()); + } + + public Allocation get_gTextAlloc() { + return mExportVar_gTextAlloc; + } + +} + diff --git a/libs/rs/java/ModelViewer/res/raw/modelviewer.rs b/libs/rs/java/ModelViewer/res/raw/modelviewer.rs index 6a98f90d60518..91194e8279127 100644 --- a/libs/rs/java/ModelViewer/res/raw/modelviewer.rs +++ b/libs/rs/java/ModelViewer/res/raw/modelviewer.rs @@ -14,6 +14,8 @@ #pragma version(1) +#pragma rs java_package_name(com.android.modelviewer) + #include "../../../../scriptc/rs_types.rsh" #include "../../../../scriptc/rs_math.rsh" #include "../../../../scriptc/rs_graphics.rsh" @@ -28,7 +30,13 @@ rs_program_store gPFSBackground; float gRotate; -#pragma rs export_var(gPVBackground, gPFBackground, gTGrid, gTestMesh, gPFSBackground, gRotate) +rs_font gItalic; +rs_allocation gTextAlloc; + +#pragma rs export_var(gPVBackground, gPFBackground, gTGrid, gTestMesh, gPFSBackground, gRotate, gItalic, gTextAlloc) + +float gDT; +int64_t gLastTime; void init() { gRotate = 0.0f; @@ -50,10 +58,17 @@ int root(int launchID) { // Position our model on the screen rsMatrixTranslate(&matrix, 0.0f, -0.3f, 1.2f); rsMatrixScale(&matrix, 0.2f, 0.2f, 0.2f); + rsMatrixRotate(&matrix, -25.0f, 1.0f, 0.0f, 0.0f); rsMatrixRotate(&matrix, gRotate, 0.0f, 1.0f, 0.0f); rsgProgramVertexLoadModelMatrix(&matrix); rsgDrawSimpleMesh(gTestMesh); + color(0.3f, 0.3f, 0.3f, 1.0f); + rsgDrawText("Renderscript model test", 30, 695); + + rsgBindFont(gItalic); + rsgDrawText(gTextAlloc, 30, 730); + return 10; } diff --git a/libs/rs/java/ModelViewer/res/raw/modelviewer_bc.bc b/libs/rs/java/ModelViewer/res/raw/modelviewer_bc.bc index 367a3f4ad26a44b20c3ba2c01963b1c9dd94146b..a64e7258b50bf5737f74e64f2cd6e423e8d40f7c 100644 GIT binary patch literal 6220 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 R`8#ln{}n}xMNb}pzX0;Kv*Z8( diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerRS.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerRS.java index e581520c6483e..b6485dcdfbb5e 100644 --- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerRS.java +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerRS.java @@ -57,6 +57,9 @@ public class ModelViewerRS { private SimpleMesh mMesh; + private Font mItalic; + private Allocation mTextAlloc; + private ScriptC_Modelviewer mScript; int mLastX; @@ -123,11 +126,17 @@ public class ModelViewerRS { private void loadImage() { mGridImage = Allocation.createFromBitmapResourceBoxed(mRS, mRes, R.drawable.robot, Element.RGB_565(mRS), true); - mGridImage.uploadToTexture(1); + mGridImage.uploadToTexture(0); mScript.set_gTGrid(mGridImage); } + private void initTextAllocation() { + String allocString = "Displaying file: R.raw.robot"; + mTextAlloc = Allocation.createFromString(mRS, allocString); + mScript.set_gTextAlloc(mTextAlloc); + } + private void initRS() { mScript = new ScriptC_Modelviewer(mRS, mRes, R.raw.modelviewer_bc, true); @@ -148,6 +157,11 @@ public class ModelViewerRS { mScript.set_gTestMesh(mMesh); } + mItalic = Font.create(mRS, mRes, "DroidSerif-Italic.ttf", 10); + mScript.set_gItalic(mItalic); + + initTextAllocation(); + mRS.contextBindRootScript(mScript); } } diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerView.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerView.java index ce76e1a513116..061cf8eeb3b72 100644 --- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerView.java +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/ModelViewerView.java @@ -86,7 +86,6 @@ public class ModelViewerView extends RSSurfaceView { ret = false; } - Log.v("rs", "Values " + (int)ev.getX() + " " + (int)ev.getY()); mRender.touchEvent((int)ev.getX(), (int)ev.getY()); return ret; } diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ScriptC_Modelviewer.java b/libs/rs/java/ModelViewer/src/com/android/modelviewer/ScriptC_Modelviewer.java index 7a965b8bd9ae9..d3a2a292e7d6a 100644 --- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/ScriptC_Modelviewer.java +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/ScriptC_Modelviewer.java @@ -92,5 +92,27 @@ public class ScriptC_Modelviewer extends ScriptC { return mExportVar_gRotate; } + private final static int mExportVarIdx_gItalic = 6; + private Font mExportVar_gItalic; + public void set_gItalic(Font v) { + mExportVar_gItalic = v; + setVar(mExportVarIdx_gItalic, (v == null) ? 0 : v.getID()); + } + + public Font get_gItalic() { + return mExportVar_gItalic; + } + + private final static int mExportVarIdx_gTextAlloc = 7; + private Allocation mExportVar_gTextAlloc; + public void set_gTextAlloc(Allocation v) { + mExportVar_gTextAlloc = v; + setVar(mExportVarIdx_gTextAlloc, (v == null) ? 0 : v.getID()); + } + + public Allocation get_gTextAlloc() { + return mExportVar_gTextAlloc; + } + } diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec index 5f76486c3aa35..0a168f4877376 100644 --- a/libs/rs/rs.spec +++ b/libs/rs/rs.spec @@ -23,6 +23,10 @@ ContextBindProgramRaster { param RsProgramRaster pgm } +ContextBindFont { + param RsFont pgm + } + ContextPause { } @@ -467,6 +471,13 @@ FileA3DGetEntryByIndex { ret RsObjectBase } +FontCreateFromFile { + param const char *name + param uint32_t fontSize + param uint32_t dpi + ret RsFont + } + SimpleMeshCreate { ret RsSimpleMesh param RsAllocation prim diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index b9349bd7f27d8..d8c3861da60ee 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -133,6 +133,7 @@ uint32_t Context::runScript(Script *s) ObjectBaseRef vtx(mVertex); ObjectBaseRef store(mFragmentStore); ObjectBaseRef raster(mRaster); + ObjectBaseRef font(mFont); uint32_t ret = s->run(this); @@ -140,6 +141,7 @@ uint32_t Context::runScript(Script *s) mVertex.set(vtx); mFragmentStore.set(store); mRaster.set(raster); + mFont.set(font); return ret; } @@ -290,6 +292,8 @@ void * Context::threadProc(void *vrsc) rsc->setFragment(NULL); rsc->mStateFragmentStore.init(rsc); rsc->setFragmentStore(NULL); + rsc->mStateFont.init(rsc); + rsc->setFont(NULL); rsc->mStateVertexArray.init(rsc); } @@ -328,11 +332,13 @@ void * Context::threadProc(void *vrsc) rsc->mFragment.clear(); rsc->mVertex.clear(); rsc->mFragmentStore.clear(); + rsc->mFont.clear(); rsc->mRootScript.clear(); rsc->mStateRaster.deinit(rsc); rsc->mStateVertex.deinit(rsc); rsc->mStateFragment.deinit(rsc); rsc->mStateFragmentStore.deinit(rsc); + rsc->mStateFont.deinit(rsc); } ObjectBase::zeroAllUserRef(rsc); @@ -597,6 +603,16 @@ void Context::setVertex(ProgramVertex *pv) } } +void Context::setFont(Font *f) +{ + rsAssert(mIsGraphicsContext); + if (f == NULL) { + mFont.set(mStateFont.mDefault); + } else { + mFont.set(f); + } +} + void Context::assignName(ObjectBase *obj, const char *name, uint32_t len) { rsAssert(!obj->getName()); @@ -807,6 +823,13 @@ void rsi_ContextBindProgramVertex(Context *rsc, RsProgramVertex vpv) rsc->setVertex(pv); } +void rsi_ContextBindFont(Context *rsc, RsFont vfont) +{ + Font *font = static_cast(vfont); + rsc->setFont(font); +} + + void rsi_AssignName(Context *rsc, void * obj, const char *name, uint32_t len) { ObjectBase *ob = static_cast(obj); diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h index 31bf5d60c2b2e..1485d8a4a06af 100644 --- a/libs/rs/rsContext.h +++ b/libs/rs/rsContext.h @@ -32,6 +32,7 @@ #include "rsAdapter.h" #include "rsSampler.h" #include "rsLight.h" +#include "rsFont.h" #include "rsProgramFragment.h" #include "rsProgramStore.h" #include "rsProgramRaster.h" @@ -76,6 +77,7 @@ public: ProgramVertexState mStateVertex; LightState mStateLight; VertexArrayState mStateVertexArray; + FontState mStateFont; ScriptCState mScriptC; ShaderCache mShaderCache; @@ -86,6 +88,7 @@ public: void setVertex(ProgramVertex *); void setFragment(ProgramFragment *); void setFragmentStore(ProgramStore *); + void setFont(Font *); void updateSurface(void *sur); @@ -93,6 +96,7 @@ public: const ProgramStore * getFragmentStore() {return mFragmentStore.get();} const ProgramRaster * getRaster() {return mRaster.get();} const ProgramVertex * getVertex() {return mVertex.get();} + Font * getFont() {return mFont.get();} bool setupCheck(); bool checkDriver() const {return mEGL.mSurface != 0;} @@ -124,6 +128,9 @@ public: ProgramRaster * getDefaultProgramRaster() const { return mStateRaster.mDefault.get(); } + Font* getDefaultFont() const { + return mStateFont.mDefault.get(); + } uint32_t getWidth() const {return mWidth;} uint32_t getHeight() const {return mHeight;} @@ -221,7 +228,7 @@ protected: ObjectBaseRef mVertex; ObjectBaseRef mFragmentStore; ObjectBaseRef mRaster; - + ObjectBaseRef mFont; struct ObjDestroyOOB { Mutex mMutex; diff --git a/libs/rs/rsFileA3D.cpp b/libs/rs/rsFileA3D.cpp index 23c0cf5b9364c..4fac421e4da0d 100644 --- a/libs/rs/rsFileA3D.cpp +++ b/libs/rs/rsFileA3D.cpp @@ -26,6 +26,7 @@ #include "rsMesh.h" #include "rsAnimation.h" + using namespace android; using namespace android::renderscript; diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp new file mode 100644 index 0000000000000..5740e658cd8e8 --- /dev/null +++ b/libs/rs/rsFont.cpp @@ -0,0 +1,689 @@ + +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_BUILD_FOR_HOST +#include "rsContext.h" +#else +#include "rsContextHostStub.h" +#endif + +#include "rsFont.h" +#include "rsProgramFragment.h" +#include FT_BITMAP_H + +#include +#include +#include +#include + +using namespace android; +using namespace android::renderscript; + +Font::Font(Context *rsc) : ObjectBase(rsc), mCachedGlyphs(NULL) +{ + mInitialized = false; + mHasKerning = false; +} + +bool Font::init(const char *name, uint32_t fontSize, uint32_t dpi) +{ + if(mInitialized) { + LOGE("Reinitialization of fonts not supported"); + return false; + } + + String8 fontsDir("/fonts/"); + String8 fullPath(getenv("ANDROID_ROOT")); + fullPath += fontsDir; + fullPath += name; + + FT_Error error = FT_New_Face(mRSC->mStateFont.mLibrary, fullPath.string(), 0, &mFace); + if(error) { + LOGE("Unable to initialize font %s", fullPath.string()); + return false; + } + + mFontName = name; + mFontSize = fontSize; + mDpi = dpi; + + //LOGE("Font initialized: %s", fullPath.string()); + + error = FT_Set_Char_Size(mFace, fontSize * 64, 0, dpi, 0); + if(error) { + LOGE("Unable to set font size on %s", fullPath.string()); + return false; + } + + mHasKerning = FT_HAS_KERNING(mFace); + LOGE("Kerning: %i", mHasKerning); + + mInitialized = true; + return true; +} + +void Font::invalidateTextureCache() +{ + for(uint32_t i = 0; i < mCachedGlyphs.size(); i ++) { + mCachedGlyphs.valueAt(i)->mIsValid = false; + } +} + +void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y) +{ + FontState *state = &mRSC->mStateFont; + + int nPenX = x + glyph->mBitmapLeft; + int nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight; + + state->appendMeshQuad(nPenX, nPenY, 0, + glyph->mBitmapMinU, glyph->mBitmapMaxV, + + nPenX + (int)glyph->mBitmapWidth, nPenY, 0, + glyph->mBitmapMaxU, glyph->mBitmapMaxV, + + nPenX + (int)glyph->mBitmapWidth, nPenY - (int)glyph->mBitmapHeight, 0, + glyph->mBitmapMaxU, glyph->mBitmapMinV, + + nPenX, nPenY - (int)glyph->mBitmapHeight, 0, + glyph->mBitmapMinU, glyph->mBitmapMinV); +} + +void Font::renderUTF(const char *text, uint32_t len, uint32_t start, int numGlyphs, int x, int y) +{ + if(!mInitialized || numGlyphs == 0 || text == NULL || len == 0) { + return; + } + + int penX = x, penY = y; + int glyphsLeft = 1; + if(numGlyphs > 0) { + glyphsLeft = numGlyphs; + } + + size_t index = start; + size_t nextIndex = 0; + + while (glyphsLeft > 0) { + + int32_t utfChar = utf32_at(text, len, index, &nextIndex); + + // Reached the end of the string or encountered + if(utfChar < 0) { + break; + } + + // Move to the next character in the array + index = nextIndex; + + CachedGlyphInfo *cachedGlyph = mCachedGlyphs.valueFor((uint32_t)utfChar); + + if(cachedGlyph == NULL) { + cachedGlyph = cacheGlyph((uint32_t)utfChar); + } + // Is the glyph still in texture cache? + if(!cachedGlyph->mIsValid) { + updateGlyphCache(cachedGlyph); + } + + // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage + if(cachedGlyph->mIsValid) { + drawCachedGlyph(cachedGlyph, penX, penY); + } + + penX += (cachedGlyph->mAdvance.x >> 6); + + // If we were given a specific number of glyphs, decrement + if(numGlyphs > 0) { + glyphsLeft --; + } + } +} + +void Font::updateGlyphCache(CachedGlyphInfo *glyph) +{ + if(!glyph->mBitmapValid) { + + FT_Error error = FT_Load_Glyph( mFace, glyph->mGlyphIndex, FT_LOAD_RENDER ); + if(error) { + LOGE("Couldn't load glyph."); + return; + } + + glyph->mAdvance = mFace->glyph->advance; + glyph->mBitmapLeft = mFace->glyph->bitmap_left; + glyph->mBitmapTop = mFace->glyph->bitmap_top; + + FT_Bitmap *bitmap = &mFace->glyph->bitmap; + + FT_Bitmap_New(&glyph->mBitmap); + FT_Bitmap_Copy(mRSC->mStateFont.mLibrary, bitmap, &glyph->mBitmap); + + glyph->mBitmapValid = true; + } + + // Now copy the bitmap into the cache texture + uint32_t startX = 0; + uint32_t startY = 0; + + // Let the font state figure out where to put the bitmap + FontState *state = &mRSC->mStateFont; + glyph->mIsValid = state->cacheBitmap(&glyph->mBitmap, &startX, &startY); + + if(!glyph->mIsValid) { + return; + } + + uint32_t endX = startX + glyph->mBitmap.width; + uint32_t endY = startY + glyph->mBitmap.rows; + + glyph->mBitmapMinX = startX; + glyph->mBitmapMinY = startY; + glyph->mBitmapWidth = glyph->mBitmap.width; + glyph->mBitmapHeight = glyph->mBitmap.rows; + + uint32_t cacheWidth = state->getCacheTextureType()->getDimX(); + uint32_t cacheHeight = state->getCacheTextureType()->getDimY(); + + glyph->mBitmapMinU = (float)startX / (float)cacheWidth; + glyph->mBitmapMinV = (float)startY / (float)cacheHeight; + glyph->mBitmapMaxU = (float)endX / (float)cacheWidth; + glyph->mBitmapMaxV = (float)endY / (float)cacheHeight; +} + +Font::CachedGlyphInfo *Font::cacheGlyph(uint32_t glyph) +{ + CachedGlyphInfo *newGlyph = new CachedGlyphInfo(); + mCachedGlyphs.add(glyph, newGlyph); + + newGlyph->mGlyphIndex = FT_Get_Char_Index(mFace, glyph); + newGlyph->mIsValid = false; + newGlyph->mBitmapValid = false; + + //LOGE("Glyph = %c, face index: %u", (unsigned char)glyph, newGlyph->mGlyphIndex); + + updateGlyphCache(newGlyph); + + return newGlyph; +} + +Font * Font::create(Context *rsc, const char *name, uint32_t fontSize, uint32_t dpi) +{ + Vector &activeFonts = rsc->mStateFont.mActiveFonts; + + for(uint32_t i = 0; i < activeFonts.size(); i ++) { + Font *ithFont = activeFonts[i]; + if(ithFont->mFontName == name && ithFont->mFontSize == fontSize && ithFont->mDpi == dpi) { + ithFont->incUserRef(); + return ithFont; + } + } + + Font *newFont = new Font(rsc); + bool isInitialized = newFont->init(name, fontSize, dpi); + if(isInitialized) { + newFont->incUserRef(); + activeFonts.push(newFont); + return newFont; + } + + delete newFont; + return NULL; + +} + +Font::~Font() +{ + if(mFace) { + FT_Done_Face(mFace); + } + + for (uint32_t ct = 0; ct < mRSC->mStateFont.mActiveFonts.size(); ct++) { + if (mRSC->mStateFont.mActiveFonts[ct] == this) { + mRSC->mStateFont.mActiveFonts.removeAt(ct); + break; + } + } + + for(uint32_t i = 0; i < mCachedGlyphs.size(); i ++) { + CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i); + if(glyph->mBitmapValid) { + FT_Bitmap_Done(mRSC->mStateFont.mLibrary, &glyph->mBitmap); + } + delete glyph; + } +} + +FontState::FontState() +{ + mInitialized = false; + mMaxNumberOfQuads = 1024; + mCurrentQuadIndex = 0; + mRSC = NULL; +} + +FontState::~FontState() +{ + for(uint32_t i = 0; i < mCacheLines.size(); i ++) { + delete mCacheLines[i]; + } + + rsAssert(!mActiveFonts.size()); +} + +void FontState::init(Context *rsc) +{ + FT_Error error; + + if(!mLibrary) { + error = FT_Init_FreeType(&mLibrary); + if(error) { + LOGE("Unable to initialize freetype"); + return; + } + } + + mRSC = rsc; + + mDefault.set(Font::create(rsc, "DroidSans.ttf", 16, 96)); +} + +void FontState::flushAllAndInvalidate() +{ + if(mCurrentQuadIndex != 0) { + issueDrawCommand(); + mCurrentQuadIndex = 0; + } + for(uint32_t i = 0; i < mActiveFonts.size(); i ++) { + mActiveFonts[i]->invalidateTextureCache(); + } + for(uint32_t i = 0; i < mCacheLines.size(); i ++) { + mCacheLines[i]->mCurrentCol = 0; + } +} + +bool FontState::cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) +{ + // If the glyph is too tall, don't cache it + if((uint32_t)bitmap->rows > mCacheLines[mCacheLines.size()-1]->mMaxHeight) { + LOGE("Font size to large to fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows); + return false; + } + + // Now copy the bitmap into the cache texture + uint32_t startX = 0; + uint32_t startY = 0; + + bool bitmapFit = false; + for(uint32_t i = 0; i < mCacheLines.size(); i ++) { + bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY); + if(bitmapFit) { + break; + } + } + + // If the new glyph didn't fit, flush the state so far and invalidate everything + if(!bitmapFit) { + flushAllAndInvalidate(); + + // Try to fit it again + for(uint32_t i = 0; i < mCacheLines.size(); i ++) { + bitmapFit = mCacheLines[i]->fitBitmap(bitmap, &startX, &startY); + if(bitmapFit) { + break; + } + } + + // if we still don't fit, something is wrong and we shouldn't draw + if(!bitmapFit) { + LOGE("Bitmap doesn't fit in cache. width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows); + return false; + } + } + + *retOriginX = startX; + *retOriginY = startY; + + uint32_t endX = startX + bitmap->width; + uint32_t endY = startY + bitmap->rows; + + //LOGE("Bitmap width, height = %i, %i", (int)bitmap->width, (int)bitmap->rows); + + uint32_t cacheWidth = getCacheTextureType()->getDimX(); + + unsigned char *cacheBuffer = (unsigned char*)mTextTexture->getPtr(); + unsigned char *bitmapBuffer = bitmap->buffer; + + uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0; + for(cacheX = startX, bX = 0; cacheX < endX; cacheX ++, bX ++) { + for(cacheY = startY, bY = 0; cacheY < endY; cacheY ++, bY ++) { + unsigned char tempCol = bitmapBuffer[bY * bitmap->width + bX]; + cacheBuffer[cacheY*cacheWidth + cacheX] = tempCol; + } + } + + // This will dirty the texture and the shader so next time + // we draw it will upload the data + mTextTexture->deferedUploadToTexture(mRSC, false, 0); + mFontShaderF->bindTexture(0, mTextTexture.get()); + + // Some debug code + /*for(uint32_t i = 0; i < mCacheLines.size(); i ++) { + LOGE("Cache Line: H: %u Empty Space: %f", + mCacheLines[i]->mMaxHeight, + (1.0f - (float)mCacheLines[i]->mCurrentCol/(float)mCacheLines[i]->mMaxWidth)*100.0f); + + }*/ + + return true; +} + +void FontState::initRenderState() +{ + uint32_t tmp[5] = { + RS_TEX_ENV_MODE_REPLACE, 1, + RS_TEX_ENV_MODE_NONE, 0, + 0 + }; + ProgramFragment *pf = new ProgramFragment(mRSC, tmp, 5); + mFontShaderF.set(pf); + mFontShaderF->init(mRSC); + + Sampler *sampler = new Sampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST, + RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP); + mFontSampler.set(sampler); + mFontShaderF->bindSampler(0, sampler); + + ProgramStore *fontStore = new ProgramStore(mRSC); + mFontProgramStore.set(fontStore); + mFontProgramStore->setDepthFunc(RS_DEPTH_FUNC_ALWAYS); + mFontProgramStore->setBlendFunc(RS_BLEND_SRC_SRC_ALPHA, RS_BLEND_DST_ONE_MINUS_SRC_ALPHA); + mFontProgramStore->setDitherEnable(false); + mFontProgramStore->setDepthMask(false); +} + +void FontState::initTextTexture() +{ + const Element *alphaElem = Element::create(mRSC, RS_TYPE_UNSIGNED_8, RS_KIND_PIXEL_A, true, 1); + + // We will allocate a texture to initially hold 32 character bitmaps + Type *texType = new Type(mRSC); + texType->setElement(alphaElem); + texType->setDimX(1024); + texType->setDimY(256); + texType->compute(); + + Allocation *cacheAlloc = new Allocation(mRSC, texType); + mTextTexture.set(cacheAlloc); + mTextTexture->deferedUploadToTexture(mRSC, false, 0); + + // Split up our cache texture into lines of certain widths + int nextLine = 0; + mCacheLines.push(new CacheTextureLine(16, texType->getDimX(), nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(32, texType->getDimX(), nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(40, texType->getDimX(), nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + mCacheLines.push(new CacheTextureLine(texType->getDimY() - nextLine, texType->getDimX(), nextLine, 0)); +} + +// Avoid having to reallocate memory and render quad by quad +void FontState::initVertexArrayBuffers() +{ + // Now lets write index data + const Element *indexElem = Element::create(mRSC, RS_TYPE_UNSIGNED_16, RS_KIND_USER, false, 1); + Type *indexType = new Type(mRSC); + uint32_t numIndicies = mMaxNumberOfQuads * 6; + indexType->setDimX(numIndicies); + indexType->setElement(indexElem); + indexType->compute(); + + Allocation *indexAlloc = new Allocation(mRSC, indexType); + uint16_t *indexPtr = (uint16_t*)indexAlloc->getPtr(); + + // Four verts, two triangles , six indices per quad + for(uint32_t i = 0; i < mMaxNumberOfQuads; i ++) { + int i6 = i * 6; + int i4 = i * 4; + + indexPtr[i6 + 0] = i4 + 0; + indexPtr[i6 + 1] = i4 + 1; + indexPtr[i6 + 2] = i4 + 2; + + indexPtr[i6 + 3] = i4 + 0; + indexPtr[i6 + 4] = i4 + 2; + indexPtr[i6 + 5] = i4 + 3; + } + + indexAlloc->deferedUploadToBufferObject(mRSC); + mIndexBuffer.set(indexAlloc); + + const Element *posElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 3); + const Element *texElem = Element::create(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 2); + + const Element *elemArray[2]; + elemArray[0] = posElem; + elemArray[1] = texElem; + + String8 posName("position"); + String8 texName("texture0"); + + const char *nameArray[2]; + nameArray[0] = posName.string(); + nameArray[1] = texName.string(); + size_t lengths[2]; + lengths[0] = posName.size(); + lengths[1] = texName.size(); + + const Element *vertexDataElem = Element::create(mRSC, 2, elemArray, nameArray, lengths); + + Type *vertexDataType = new Type(mRSC); + vertexDataType->setDimX(mMaxNumberOfQuads * 4); + vertexDataType->setElement(vertexDataElem); + vertexDataType->compute(); + + Allocation *vertexAlloc = new Allocation(mRSC, vertexDataType); + mTextMeshPtr = (float*)vertexAlloc->getPtr(); + + mVertexArray.set(vertexAlloc); +} + +// We don't want to allocate anything unless we actually draw text +void FontState::checkInit() +{ + if(mInitialized) { + return; + } + + initTextTexture(); + initRenderState(); + + initVertexArrayBuffers(); + + /*mTextMeshRefs = new ObjectBaseRef[mNumMeshes]; + + for(uint32_t i = 0; i < mNumMeshes; i ++){ + SimpleMesh *textMesh = createTextMesh(); + mTextMeshRefs[i].set(textMesh); + }*/ + + mInitialized = true; +} + +void FontState::issueDrawCommand() { + + ObjectBaseRef tmpV(mRSC->getVertex()); + mRSC->setVertex(mRSC->getDefaultProgramVertex()); + + ObjectBaseRef tmpF(mRSC->getFragment()); + mRSC->setFragment(mFontShaderF.get()); + + ObjectBaseRef tmpPS(mRSC->getFragmentStore()); + mRSC->setFragmentStore(mFontProgramStore.get()); + + if (!mRSC->setupCheck()) { + mRSC->setVertex((ProgramVertex *)tmpV.get()); + mRSC->setFragment((ProgramFragment *)tmpF.get()); + mRSC->setFragmentStore((ProgramStore *)tmpPS.get()); + return; + } + + float *vtx = (float*)mVertexArray->getPtr(); + float *tex = vtx + 3; + + VertexArray va; + va.add(GL_FLOAT, 3, 20, false, (uint32_t)vtx, "position"); + va.add(GL_FLOAT, 2, 20, false, (uint32_t)tex, "texture0"); + va.setupGL2(mRSC, &mRSC->mStateVertexArray, &mRSC->mShaderCache); + + mIndexBuffer->uploadCheck(mRSC); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer->getBufferObjectID()); + glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, (uint16_t *)(0)); + + // Reset the state + mRSC->setVertex((ProgramVertex *)tmpV.get()); + mRSC->setFragment((ProgramFragment *)tmpF.get()); + mRSC->setFragmentStore((ProgramStore *)tmpPS.get()); +} + +void FontState::appendMeshQuad(float x1, float y1, float z1, + float u1, float v1, + float x2, float y2, float z2, + float u2, float v2, + float x3, float y3, float z3, + float u3, float v3, + float x4, float y4, float z4, + float u4, float v4) +{ + const uint32_t vertsPerQuad = 4; + const uint32_t floatsPerVert = 5; + float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; + + // Cull things that are off the screen + float width = (float)mRSC->getWidth(); + float height = (float)mRSC->getHeight(); + + if(x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) { + return; + } + + /*LOGE("V0 x: %f y: %f z: %f", x1, y1, z1); + LOGE("V1 x: %f y: %f z: %f", x2, y2, z2); + LOGE("V2 x: %f y: %f z: %f", x3, y3, z3); + LOGE("V3 x: %f y: %f z: %f", x4, y4, z4);*/ + + (*currentPos++) = x1; + (*currentPos++) = y1; + (*currentPos++) = z1; + (*currentPos++) = u1; + (*currentPos++) = v1; + + (*currentPos++) = x2; + (*currentPos++) = y2; + (*currentPos++) = z2; + (*currentPos++) = u2; + (*currentPos++) = v2; + + (*currentPos++) = x3; + (*currentPos++) = y3; + (*currentPos++) = z3; + (*currentPos++) = u3; + (*currentPos++) = v3; + + (*currentPos++) = x4; + (*currentPos++) = y4; + (*currentPos++) = z4; + (*currentPos++) = u4; + (*currentPos++) = v4; + + mCurrentQuadIndex ++; + + if(mCurrentQuadIndex == mMaxNumberOfQuads) { + issueDrawCommand(); + mCurrentQuadIndex = 0; + } +} + +void FontState::renderText(const char *text, uint32_t len, uint32_t startIndex, int numGlyphs, int x, int y) +{ + checkInit(); + + String8 text8(text); + + // Render code here + Font *currentFont = mRSC->getFont(); + currentFont->renderUTF(text, len, startIndex, numGlyphs, x, y); + + if(mCurrentQuadIndex != 0) { + issueDrawCommand(); + mCurrentQuadIndex = 0; + } +} + +void FontState::renderText(const char *text, int x, int y) +{ + size_t textLen = strlen(text); + renderText(text, textLen, 0, -1, x, y); +} + +void FontState::renderText(Allocation *alloc, int x, int y) +{ + if(!alloc) { + return; + } + + const char *text = (const char *)alloc->getPtr(); + size_t allocSize = alloc->getType()->getSizeBytes(); + renderText(text, allocSize, 0, -1, x, y); +} + +void FontState::renderText(Allocation *alloc, uint32_t start, int len, int x, int y) +{ + if(!alloc) { + return; + } + + const char *text = (const char *)alloc->getPtr(); + size_t allocSize = alloc->getType()->getSizeBytes(); + renderText(text, allocSize, start, len, x, y); +} + +void FontState::deinit(Context *rsc) +{ + if(mLibrary) { + FT_Done_FreeType( mLibrary ); + } + + delete mDefault.get(); + mDefault.clear(); +} + +namespace android { +namespace renderscript { + +RsFont rsi_FontCreateFromFile(Context *rsc, char const *name, uint32_t fontSize, uint32_t dpi) +{ + return Font::create(rsc, name, fontSize, dpi); +} + +} // renderscript +} // android diff --git a/libs/rs/rsFont.h b/libs/rs/rsFont.h new file mode 100644 index 0000000000000..8d1556dd2847b --- /dev/null +++ b/libs/rs/rsFont.h @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_RS_FONT_H +#define ANDROID_RS_FONT_H + +#include "RenderScript.h" +#include "rsStream.h" +#include +#include +#include + +#include +#include FT_FREETYPE_H + +// --------------------------------------------------------------------------- +namespace android { + +namespace renderscript { + +class FontState; + +class Font : public ObjectBase +{ +public: + ~Font(); + + // Pointer to the utf data, length of data, where to start, number of glyphs ot read + // (each glyph may be longer than a char because we are dealing with utf data) + // Last two variables are the initial pen position + void renderUTF(const char *text, uint32_t len, uint32_t start, int numGlyphs, int x, int y); + + // Currently files do not get serialized, + // but we need to inherit from ObjectBase for ref tracking + virtual void serialize(OStream *stream) const { + } + virtual RsA3DClassID getClassId() const { + return RS_A3D_CLASS_ID_UNKNOWN; + } + + static Font * create(Context *rsc, const char *name, uint32_t fontSize, uint32_t dpi); + +protected: + + friend class FontState; + + void invalidateTextureCache(); + struct CachedGlyphInfo + { + // Has the cache been invalidated? + bool mIsValid; + // Location of the cached glyph in the bitmap + // in case we need to resize the texture + uint32_t mBitmapMinX; + uint32_t mBitmapMinY; + uint32_t mBitmapWidth; + uint32_t mBitmapHeight; + // Also cache texture coords for the quad + float mBitmapMinU; + float mBitmapMinV; + float mBitmapMaxU; + float mBitmapMaxV; + // Minimize how much we call freetype + FT_UInt mGlyphIndex; + FT_Vector mAdvance; + // Values below contain a glyph's origin in the bitmap + FT_Int mBitmapLeft; + FT_Int mBitmapTop; + // Hold on to the bitmap in case cache is invalidated + FT_Bitmap mBitmap; + bool mBitmapValid; + }; + + String8 mFontName; + uint32_t mFontSize; + uint32_t mDpi; + + Font(Context *rsc); + bool init(const char *name, uint32_t fontSize, uint32_t dpi); + + FT_Face mFace; + bool mInitialized; + bool mHasKerning; + + DefaultKeyedVector mCachedGlyphs; + + CachedGlyphInfo *cacheGlyph(uint32_t glyph); + void updateGlyphCache(CachedGlyphInfo *glyph); + void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y); +}; + +class FontState +{ +public: + FontState(); + ~FontState(); + + void init(Context *rsc); + void deinit(Context *rsc); + + ObjectBaseRef mDefault; + ObjectBaseRef mLast; + + void renderText(const char *text, uint32_t len, uint32_t startIndex, int numGlyphs, int x, int y); + void renderText(const char *text, int x, int y); + void renderText(Allocation *alloc, int x, int y); + void renderText(Allocation *alloc, uint32_t start, int len, int x, int y); + +protected: + + friend class Font; + + struct CacheTextureLine + { + uint32_t mMaxHeight; + uint32_t mMaxWidth; + uint32_t mCurrentRow; + uint32_t mCurrentCol; + + CacheTextureLine(uint32_t maxHeight, uint32_t maxWidth, uint32_t currentRow, uint32_t currentCol) : + mMaxHeight(maxHeight), mMaxWidth(maxWidth), mCurrentRow(currentRow), mCurrentCol(currentCol) { + } + + bool fitBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) { + if((uint32_t)bitmap->rows > mMaxHeight) { + return false; + } + + if(mCurrentCol + (uint32_t)bitmap->width < mMaxWidth) { + *retOriginX = mCurrentCol; + *retOriginY = mCurrentRow; + mCurrentCol += bitmap->width; + return true; + } + + return false; + } + }; + + Vector mCacheLines; + + Context *mRSC; + + // Free type library, we only need one copy + FT_Library mLibrary; + Vector mActiveFonts; + + // Render state for the font + ObjectBaseRef mFontShaderF; + ObjectBaseRef mFontSampler; + ObjectBaseRef mFontProgramStore; + void initRenderState(); + + // Texture to cache glyph bitmaps + ObjectBaseRef mTextTexture; + void initTextTexture(); + + bool cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY); + const Type* getCacheTextureType() { + return mTextTexture->getType(); + } + + void flushAllAndInvalidate(); + + // Pointer to vertex data to speed up frame to frame work + float *mTextMeshPtr; + uint32_t mCurrentQuadIndex; + uint32_t mMaxNumberOfQuads; + + void initVertexArrayBuffers(); + ObjectBaseRef mIndexBuffer; + ObjectBaseRef mVertexArray; + + + bool mInitialized; + + void checkInit(); + + void issueDrawCommand(); + + void appendMeshQuad(float x1, float y1, float z1, + float u1, float v1, + float x2, float y2, float z2, + float u2, float v2, + float x3, float y3, float z3, + float u3, float v3, + float x4, float y4, float z4, + float u4, float v4); + +}; + + +} +} + +#endif diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp index 052e14471c3f0..dbd398e34c42b 100644 --- a/libs/rs/rsScriptC_LibGL.cpp +++ b/libs/rs/rsScriptC_LibGL.cpp @@ -309,6 +309,24 @@ static uint32_t SC_getHeight() return rsc->getHeight(); } +static void SC_DrawTextAlloc(RsAllocation va, int x, int y) +{ + GET_TLS(); + Allocation *alloc = static_cast(va); + rsc->mStateFont.renderText(alloc, x, y); +} + +static void SC_DrawText(const char *text, int x, int y) +{ + GET_TLS(); + rsc->mStateFont.renderText(text, x, y); +} + +static void SC_BindFont(RsFont font) +{ + GET_TLS(); + rsi_ContextBindFont(rsc, font); +} ////////////////////////////////////////////////////////////////////////////// // Class implementation @@ -360,6 +378,11 @@ static ScriptCState::SymbolTable_t gSyms[] = { { "rsgClearColor", (void *)&SC_ClearColor }, { "rsgClearDepth", (void *)&SC_ClearDepth }, + { "_Z11rsgDrawTextPKcii", (void *)&SC_DrawText }, + { "_Z11rsgDrawText13rs_allocationii", (void *)&SC_DrawTextAlloc }, + + { "rsgBindFont", (void *)&SC_BindFont }, + ////////////////////////////////////// // IO diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp index 75abc767da837..8dca0fe2a28bc 100644 --- a/libs/rs/rsType.cpp +++ b/libs/rs/rsType.cpp @@ -89,7 +89,9 @@ void Type::compute() mLODCount = 1; } if (mLODCount != oldLODCount) { - delete [] mLODs; + if(mLODs){ + delete [] mLODs; + } mLODs = new LOD[mLODCount]; } diff --git a/libs/rs/scriptc/rs_graphics.rsh b/libs/rs/scriptc/rs_graphics.rsh index c9667d037cbb3..fba3d6d0acd00 100644 --- a/libs/rs/scriptc/rs_graphics.rsh +++ b/libs/rs/scriptc/rs_graphics.rsh @@ -35,6 +35,10 @@ extern void __attribute__((overloadable)) rsgDrawSimpleMesh(rs_mesh ism, int sta extern void rsgClearColor(float, float, float, float); extern void rsgClearDepth(float); +extern void __attribute__((overloadable)) rsgDrawText(const char *, int x, int y); +extern void __attribute__((overloadable)) rsgDrawText(rs_allocation, int x, int y); +extern void rsgBindFont(rs_font); + /////////////////////////////////////////////////////// // misc extern void color(float, float, float, float); diff --git a/libs/rs/scriptc/rs_types.rsh b/libs/rs/scriptc/rs_types.rsh index 1d8f1bdacbc76..69e1aedef2f7a 100644 --- a/libs/rs/scriptc/rs_types.rsh +++ b/libs/rs/scriptc/rs_types.rsh @@ -24,6 +24,7 @@ typedef struct { int* p; } __attribute__((packed, aligned(4))) rs_program_fragme typedef struct { int* p; } __attribute__((packed, aligned(4))) rs_program_vertex; typedef struct { int* p; } __attribute__((packed, aligned(4))) rs_program_raster; typedef struct { int* p; } __attribute__((packed, aligned(4))) rs_program_store; +typedef struct { int* p; } __attribute__((packed, aligned(4))) rs_font; typedef float float2 __attribute__((ext_vector_type(2))); typedef float float3 __attribute__((ext_vector_type(3)));