Add preliminary support for text rendering.

Change-Id: I547eb631dbda24d13960d54b4144fb8908fd8a49
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index b811b7b..1424638 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -32,6 +32,11 @@
 import android.graphics.Region;
 import android.graphics.Shader;
 import android.graphics.SweepGradient;
+import android.graphics.TemporaryBuffer;
+import android.text.GraphicsOperations;
+import android.text.SpannableString;
+import android.text.SpannedString;
+import android.text.TextUtils;
 
 import javax.microedition.khronos.opengles.GL;
 
@@ -574,22 +579,46 @@
 
     @Override
     public void drawText(char[] text, int index, int count, float x, float y, Paint paint) {
-        // TODO: Implement
+        if ((index | count | (index + count) | (text.length - index - count)) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        nDrawText(mRenderer, text, index, count, x, y, paint.mBidiFlags, paint.mNativePaint);
     }
+    
+    private native void nDrawText(int renderer, char[] text, int index, int count, float x, float y,
+            int bidiFlags, int paint);
 
     @Override
     public void drawText(CharSequence text, int start, int end, float x, float y, Paint paint) {
-        // TODO: Implement
+        if (text instanceof String || text instanceof SpannedString ||
+                text instanceof SpannableString) {
+            nDrawText(mRenderer, text.toString(), start, end, x, y, paint.mBidiFlags,
+                    paint.mNativePaint);
+        } else if (text instanceof GraphicsOperations) {
+            ((GraphicsOperations) text).drawText(this, start, end, x, y,
+                                                     paint);
+        } else {
+            char[] buf = TemporaryBuffer.obtain(end - start);
+            TextUtils.getChars(text, start, end, buf, 0);
+            nDrawText(mRenderer, buf, 0, end - start, x, y, paint.mBidiFlags, paint.mNativePaint);
+            TemporaryBuffer.recycle(buf);
+        }
     }
 
     @Override
     public void drawText(String text, int start, int end, float x, float y, Paint paint) {
-        // TODO: Implement
+        if ((start | end | (end - start) | (text.length() - end)) < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+        nDrawText(mRenderer, text, start, end, x, y, paint.mBidiFlags, paint.mNativePaint);
     }
 
+    private native void nDrawText(int renderer, String text, int start, int end, float x, float y,
+            int bidiFlags, int paint);
+
     @Override
     public void drawText(String text, float x, float y, Paint paint) {
-        drawText(text, 0, text.length(), x, y, paint);
+        nDrawText(mRenderer, text, 0, text.length(), x, y, paint.mBidiFlags, paint.mNativePaint);
     }
 
     @Override
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 7c7bfeb..1fe72e6 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -130,7 +130,13 @@
         return NULL;
     }
     
-    return SkTypeface::CreateFromStream(new AssetStream(asset, true));
+    SkStream* stream = new AssetStream(asset, true);
+    SkTypeface* face = SkTypeface::CreateFromStream(stream);
+    // SkTypeFace::CreateFromStream calls ref() on the stream, so we
+    // need to unref it here or it won't be freed later on
+    stream->unref();
+
+    return face;
 }
 
 static SkTypeface* Typeface_createFromFile(JNIEnv* env, jobject, jstring jpath) {
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index d177e1a..fa4d23c 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "OpenGLRenderer"
+
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
@@ -24,6 +26,7 @@
 #include <SkMatrix.h>
 #include <SkPaint.h>
 #include <SkRegion.h>
+#include <SkScalerContext.h>
 #include <SkXfermode.h>
 
 #include <OpenGLRenderer.h>
@@ -207,7 +210,6 @@
 
     renderer->drawPatch(bitmap, patch, left, top, right, bottom, paint);
 
-    // TODO: make sure that 0 is correct for the flags
     env->ReleaseByteArrayElements(chunks, storage, 0);
 }
 
@@ -246,6 +248,26 @@
 }
 
 // ----------------------------------------------------------------------------
+// Text
+// ----------------------------------------------------------------------------
+
+static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jcharArray text, int index, int count,
+        jfloat x, jfloat y, int flags, SkPaint* paint) {
+    jchar* textArray = env->GetCharArrayElements(text, NULL);
+    // TODO: draw from textArray + index
+    env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
+}
+
+static void android_view_GLES20Canvas_drawText(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, jstring text, int start, int end,
+        jfloat x, jfloat y, int flags, SkPaint* paint) {
+    const jchar* textArray = env->GetStringChars(text, NULL);
+    // TODO: draw from textArray + start
+    env->ReleaseStringChars(text, textArray);
+}
+
+// ----------------------------------------------------------------------------
 // JNI Glue
 // ----------------------------------------------------------------------------
 
@@ -288,6 +310,10 @@
     {   "nSetupBitmapShader", "(IIIIII)V",       (void*) android_view_GLES20Canvas_setupBitmapShader },
     {   "nSetupLinearShader", "(IIIIIIII)V",     (void*) android_view_GLES20Canvas_setupLinearShader },
 
+    {   "nDrawText",          "(I[CIIFFII)V",    (void*) android_view_GLES20Canvas_drawTextArray },
+    {   "nDrawText",          "(ILjava/lang/String;IIFFII)V",
+            (void*) android_view_GLES20Canvas_drawText },
+
     {   "nGetClipBounds",     "(ILandroid/graphics/Rect;)Z",
             (void*) android_view_GLES20Canvas_getClipBounds },
 };
diff --git a/core/jni/android_view_HardwareRenderer.cpp b/core/jni/android_view_HardwareRenderer.cpp
index abd788b..6d20c9d 100644
--- a/core/jni/android_view_HardwareRenderer.cpp
+++ b/core/jni/android_view_HardwareRenderer.cpp
@@ -34,13 +34,11 @@
 const char* const kClassPathName = "android/view/HardwareRenderer";
 
 static JNINativeMethod gMethods[] = {
-    {   "nativeAbandonGlCaches", "()V", 
-                                (void*)android_view_HardwareRenderer_abandonGlCaches },
+    {   "nativeAbandonGlCaches", "()V", (void*)android_view_HardwareRenderer_abandonGlCaches },
 };
 
 int register_android_view_HardwareRenderer(JNIEnv* env) {
-    return AndroidRuntime::registerNativeMethods(env,
-            kClassPathName, gMethods, NELEM(gMethods));
+    return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
 }
 
 };
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 9b4d3a8..3d63aa6 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -43,7 +43,10 @@
     private boolean     mHasCompatScaling;
     private float       mCompatScaling;
     private float       mInvCompatScaling;
-    /* package */ int   mBidiFlags = BIDI_DEFAULT_LTR;
+    /**
+     * @hide
+     */
+    public  int         mBidiFlags = BIDI_DEFAULT_LTR;
     
     private static final Style[] sStyleArray = {
         Style.FILL, Style.STROKE, Style.FILL_AND_STROKE
diff --git a/graphics/java/android/graphics/TemporaryBuffer.java b/graphics/java/android/graphics/TemporaryBuffer.java
index 1d7fe01..c5b8143 100644
--- a/graphics/java/android/graphics/TemporaryBuffer.java
+++ b/graphics/java/android/graphics/TemporaryBuffer.java
@@ -18,9 +18,11 @@
 
 import com.android.internal.util.ArrayUtils;
 
-/* package */ class TemporaryBuffer
-{
-    /* package */ static char[] obtain(int len) {
+/**
+ * @hide
+ */
+public class TemporaryBuffer {
+    public static char[] obtain(int len) {
         char[] buf;
 
         synchronized (TemporaryBuffer.class) {
@@ -28,15 +30,15 @@
             sTemp = null;
         }
 
-        if (buf == null || buf.length < len)
+        if (buf == null || buf.length < len) {
             buf = new char[ArrayUtils.idealCharArraySize(len)];
+        }
 
         return buf;
     }
 
-    /* package */ static void recycle(char[] temp) {
-        if (temp.length > 1000)
-            return;
+    public static void recycle(char[] temp) {
+        if (temp.length > 1000) return;
 
         synchronized (TemporaryBuffer.class) {
             sTemp = temp;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index e39385a..1fa76d2 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -138,8 +138,8 @@
     mCurrentProgram = mDrawTextureProgram;
 
     mShader = kShaderNone;
-    mShaderTileX = SkShader::kClamp_TileMode;
-    mShaderTileY = SkShader::kClamp_TileMode;
+    mShaderTileX = GL_CLAMP_TO_EDGE;
+    mShaderTileY = GL_CLAMP_TO_EDGE;
     mShaderMatrix = NULL;
     mShaderBitmap = NULL;
 
@@ -535,8 +535,8 @@
     mShader = OpenGLRenderer::kShaderNone;
     mShaderKey = NULL;
     mShaderBlend = false;
-    mShaderTileX = SkShader::kClamp_TileMode;
-    mShaderTileY = SkShader::kClamp_TileMode;
+    mShaderTileX = GL_CLAMP_TO_EDGE;
+    mShaderTileY = GL_CLAMP_TO_EDGE;
 }
 
 void OpenGLRenderer::setupBitmapShader(SkBitmap* bitmap, SkShader::TileMode tileX,
@@ -544,8 +544,8 @@
     mShader = OpenGLRenderer::kShaderBitmap;
     mShaderBlend = hasAlpha;
     mShaderBitmap = bitmap;
-    mShaderTileX = tileX;
-    mShaderTileY = tileY;
+    mShaderTileX = gTileModes[tileX];
+    mShaderTileY = gTileModes[tileY];
     mShaderMatrix = matrix;
 }
 
@@ -556,8 +556,8 @@
     mShader = OpenGLRenderer::kShaderLinearGradient;
     mShaderKey = shader;
     mShaderBlend = hasAlpha;
-    mShaderTileX = tileMode;
-    mShaderTileY = tileMode;
+    mShaderTileX = gTileModes[tileMode];
+    mShaderTileY = gTileModes[tileMode];
     mShaderMatrix = matrix;
     mShaderBounds = bounds;
     mShaderColors = colors;
@@ -623,8 +623,18 @@
         float alpha, SkXfermode::Mode mode) {
     Texture* texture = mGradientCache.get(mShaderKey);
     if (!texture) {
+        SkShader::TileMode tileMode = SkShader::kClamp_TileMode;
+        switch (mShaderTileX) {
+            case GL_REPEAT:
+                tileMode = SkShader::kRepeat_TileMode;
+                break;
+            case GL_MIRRORED_REPEAT:
+                tileMode = SkShader::kMirror_TileMode;
+                break;
+        }
+
         texture = mGradientCache.addLinearGradient(mShaderKey, mShaderBounds, mShaderColors,
-                mShaderPositions, mShaderCount, mShaderTileX);
+                mShaderPositions, mShaderCount, tileMode);
     }
 
     mModelView.loadTranslate(left, top, 0.0f);
@@ -634,14 +644,7 @@
     mDrawLinearGradientProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
 
     chooseBlending(mShaderBlend || alpha < 1.0f, mode);
-
-    if (texture->id != mLastTexture) {
-        glBindTexture(GL_TEXTURE_2D, texture->id);
-        mLastTexture = texture->id;
-    }
-    // TODO: Don't set the texture parameters every time
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gTileModes[mShaderTileX]);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gTileModes[mShaderTileX]);
+    bindTexture(texture->id, mShaderTileX, mShaderTileY);
 
     Rect start(mShaderBounds[0], mShaderBounds[1], mShaderBounds[2], mShaderBounds[3]);
     if (mShaderMatrix) {
@@ -736,14 +739,7 @@
     mDrawTextureProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
 
     chooseBlending(blend || alpha < 1.0f, mode);
-
-    if (texture != mLastTexture) {
-        glBindTexture(GL_TEXTURE_2D, texture);
-        mLastTexture = texture;
-    }
-    // TODO: Don't set the texture parameters every time
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, gTileModes[mShaderTileX]);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, gTileModes[mShaderTileY]);
+    bindTexture(texture, mShaderTileX, mShaderTileY);
 
     // Always premultiplied
     //glUniform4f(mDrawTextureProgram->color, alpha, alpha, alpha, alpha);
@@ -826,5 +822,15 @@
     }
 }
 
+void OpenGLRenderer::bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT) {
+    if (texture != mLastTexture) {
+        glBindTexture(GL_TEXTURE_2D, texture);
+        mLastTexture = texture;
+    }
+    // TODO: Don't set the texture parameters every time
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT);
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index dd7999d..9dc2a43 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -287,6 +287,11 @@
     inline void getAlphaAndMode(const SkPaint* paint, int* alpha, SkXfermode::Mode* mode);
 
     /**
+     * Binds the specified texture with the specified wrap modes.
+     */
+    inline void bindTexture(GLuint texture, GLenum wrapS, GLenum wrapT);
+
+    /**
      * Enable or disable blending as necessary. This function sets the appropriate
      * blend function based on the specified xfermode.
      */
@@ -341,8 +346,8 @@
     ShaderType mShader;
     SkShader* mShaderKey;
     bool mShaderBlend;
-    SkShader::TileMode mShaderTileX;
-    SkShader::TileMode mShaderTileY;
+    GLenum mShaderTileX;
+    GLenum mShaderTileY;
     SkMatrix* mShaderMatrix;
     // Bitmaps
     SkBitmap* mShaderBitmap;