Add support for drawPoint() and drawPoints().

Change-Id: I01bef50c08ec3160f8d40dc060b2cf6c2e4d7639
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index d841419..8621de3 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -773,20 +773,25 @@
     public void drawPoint(float x, float y, Paint paint) {
         mPoint[0] = x;
         mPoint[1] = y;
-        drawPoints(mPoint, 0, 1, paint);
-    }
-
-    @Override
-    public void drawPoints(float[] pts, int offset, int count, Paint paint) {
-        // TODO: Implement
+        drawPoints(mPoint, 0, 2, paint);
     }
 
     @Override
     public void drawPoints(float[] pts, Paint paint) {
-        drawPoints(pts, 0, pts.length / 2, paint);
+        drawPoints(pts, 0, pts.length, paint);
     }
 
     @Override
+    public void drawPoints(float[] pts, int offset, int count, Paint paint) {
+        int modifiers = setupModifiers(paint);
+        nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint);
+        if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers);
+    }
+
+    private static native void nDrawPoints(int renderer, float[] points,
+            int offset, int count, int paint);
+
+    @Override
     public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
         // TODO: Implement
     }
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index d6d3e4f..a4931ac 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -366,6 +366,13 @@
     }
 }
 
+static void android_view_GLES20Canvas_drawPoints(JNIEnv* env, jobject clazz,
+        OpenGLRenderer* renderer, jfloatArray points, jint offset, jint count, SkPaint* paint) {
+    jfloat* storage = env->GetFloatArrayElements(points, NULL);
+    renderer->drawPoints(storage + offset, count, paint);
+    env->ReleaseFloatArrayElements(points, storage, 0);
+}
+
 static void android_view_GLES20Canvas_drawPath(JNIEnv* env, jobject clazz,
         OpenGLRenderer* renderer, SkPath* path, SkPaint* paint) {
     renderer->drawPath(path, paint);
@@ -374,9 +381,7 @@
 static void android_view_GLES20Canvas_drawLines(JNIEnv* env, jobject clazz,
         OpenGLRenderer* renderer, jfloatArray points, jint offset, jint count, SkPaint* paint) {
     jfloat* storage = env->GetFloatArrayElements(points, NULL);
-
     renderer->drawLines(storage + offset, count, paint);
-
     env->ReleaseFloatArrayElements(points, storage, 0);
 }
 
@@ -645,6 +650,7 @@
     { "nDrawCircle",        "(IFFFI)V",        (void*) android_view_GLES20Canvas_drawCircle },
     { "nDrawOval",          "(IFFFFI)V",       (void*) android_view_GLES20Canvas_drawOval },
     { "nDrawArc",           "(IFFFFFFZI)V",    (void*) android_view_GLES20Canvas_drawArc },
+    { "nDrawPoints",        "(I[FIII)V",       (void*) android_view_GLES20Canvas_drawPoints },
 
     { "nDrawPath",          "(III)V",          (void*) android_view_GLES20Canvas_drawPath },
     { "nDrawLines",         "(I[FIII)V",       (void*) android_view_GLES20Canvas_drawLines },
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index f2f983f..bf1182c 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -53,6 +53,7 @@
     "DrawArc",
     "DrawPath",
     "DrawLines",
+    "DrawPoints",
     "DrawText",
     "ResetShader",
     "SetupShader",
@@ -441,6 +442,13 @@
                 renderer.drawLines(points, count, getPaint());
             }
             break;
+            case DrawPoints: {
+                int count = 0;
+                float* points = getFloats(count);
+                DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
+                renderer.drawPoints(points, count, getPaint());
+            }
+            break;
             case DrawText: {
                 getText(&text);
                 int count = getInt();
@@ -787,6 +795,12 @@
     addPaint(paint);
 }
 
+void DisplayListRenderer::drawPoints(float* points, int count, SkPaint* paint) {
+    addOp(DisplayList::DrawPoints);
+    addFloats(points, count);
+    addPaint(paint);
+}
+
 void DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
         float x, float y, SkPaint* paint) {
     addOp(DisplayList::DrawText);
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 8773cb5..da57e4a 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -89,6 +89,7 @@
         DrawArc,
         DrawPath,
         DrawLines,
+        DrawPoints,
         DrawText,
         ResetShader,
         SetupShader,
@@ -264,6 +265,7 @@
             float startAngle, float sweepAngle, bool useCenter, SkPaint* paint);
     void drawPath(SkPath* path, SkPaint* paint);
     void drawLines(float* points, int count, SkPaint* paint);
+    void drawPoints(float* points, int count, SkPaint* paint);
     void drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint);
 
     void resetShader();
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index a711289..6c454a4 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -879,6 +879,11 @@
     mDescription.hasAlpha8Texture = isAlpha8;
 }
 
+void OpenGLRenderer::setupDrawPoint(float pointSize) {
+    mDescription.isPoint = true;
+    mDescription.pointSize = pointSize;
+}
+
 void OpenGLRenderer::setupDrawColor(int color) {
     setupDrawColor(color, (color >> 24) & 0xFF);
 }
@@ -987,6 +992,11 @@
     }
 }
 
+void OpenGLRenderer::setupDrawPointUniforms() {
+    int slot = mCaches.currentProgram->getUniform("pointSize");
+    glUniform1f(slot, mDescription.pointSize);
+}
+
 void OpenGLRenderer::setupDrawColorUniforms() {
     if (mColorSet || (mShader && mSetShaderColor)) {
         mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
@@ -1446,6 +1456,48 @@
     }
 }
 
+void OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) {
+    if (mSnapshot->isIgnored()) return;
+
+    // TODO: The paint's cap style defines whether the points are square or circular
+    // TODO: Handle AA for round points
+
+    // A stroke width of 0 has a special meaningin Skia:
+    // it draws an unscaled 1px point
+    const bool isHairLine = paint->getStrokeWidth() == 0.0f;
+
+    int alpha;
+    SkXfermode::Mode mode;
+    getAlphaAndMode(paint, &alpha, &mode);
+
+    int verticesCount = count >> 1;
+    int generatedVerticesCount = 0;
+
+    TextureVertex pointsData[verticesCount];
+    TextureVertex* vertex = &pointsData[0];
+
+    setupDraw();
+    setupDrawPoint(isHairLine ? 1.0f : paint->getStrokeWidth());
+    setupDrawColor(paint->getColor(), alpha);
+    setupDrawColorFilter();
+    setupDrawShader();
+    setupDrawBlending(mode);
+    setupDrawProgram();
+    setupDrawModelViewIdentity();
+    setupDrawColorUniforms();
+    setupDrawColorFilterUniforms();
+    setupDrawPointUniforms();
+    setupDrawShaderIdentityUniforms();
+    setupDrawMesh(vertex);
+
+    for (int i = 0; i < count; i += 2) {
+        TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f);
+        generatedVerticesCount++;
+    }
+
+    glDrawArrays(GL_POINTS, 0, generatedVerticesCount);
+}
+
 void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
     // No need to check against the clip, we fill the clip region
     if (mSnapshot->isIgnored()) return;
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 402563c..4b93b80 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -119,6 +119,7 @@
             float startAngle, float sweepAngle, bool useCenter, SkPaint* paint);
     virtual void drawPath(SkPath* path, SkPaint* paint);
     virtual void drawLines(float* points, int count, SkPaint* paint);
+    virtual void drawPoints(float* points, int count, SkPaint* paint);
     virtual void drawText(const char* text, int bytesCount, int count, float x, float y,
             SkPaint* paint);
 
@@ -424,6 +425,7 @@
      * Various methods to setup OpenGL rendering.
      */
     void setupDrawWithTexture(bool isAlpha8 = false);
+    void setupDrawPoint(float pointSize);
     void setupDrawColor(int color);
     void setupDrawColor(int color, int alpha);
     void setupDrawColor(float r, float g, float b, float a);
@@ -442,6 +444,7 @@
             bool ignoreTransform = false, bool ignoreModelView = false);
     void setupDrawModelViewTranslate(float left, float top, float right, float bottom,
             bool ignoreTransform = false);
+    void setupDrawPointUniforms();
     void setupDrawColorUniforms();
     void setupDrawPureColorUniforms();
     void setupDrawShaderIdentityUniforms();
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 0b6c7b5..2562306 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -41,6 +41,8 @@
         "attribute vec2 texCoords;\n";
 const char* gVS_Header_Uniforms =
         "uniform mat4 transform;\n";
+const char* gVS_Header_Uniforms_IsPoint =
+        "uniform float pointSize;\n";
 const char* gVS_Header_Uniforms_HasGradient[3] = {
         // Linear
         "uniform mat4 screenSpace;\n",
@@ -56,6 +58,8 @@
         "varying vec2 outTexCoords;\n";
 const char* gVS_Header_Varyings_HasBitmap =
         "varying vec2 outBitmapTexCoords;\n";
+const char* gVS_Header_Varyings_PointHasBitmap =
+        "varying vec2 outPointBitmapTexCoords;\n";
 const char* gVS_Header_Varyings_HasGradient[3] = {
         // Linear
         "varying vec2 linear;\n",
@@ -78,8 +82,12 @@
 };
 const char* gVS_Main_OutBitmapTexCoords =
         "    outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
+const char* gVS_Main_OutPointBitmapTexCoords =
+        "    outPointBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
 const char* gVS_Main_Position =
         "    gl_Position = transform * position;\n";
+const char* gVS_Main_PointSize =
+        "    gl_PointSize = pointSize;\n";
 const char* gVS_Footer =
         "}\n\n";
 
@@ -93,6 +101,9 @@
         "precision mediump float;\n\n";
 const char* gFS_Uniforms_Color =
         "uniform vec4 color;\n";
+const char* gFS_Header_Uniforms_PointHasBitmap =
+        "uniform vec2 textureDimension;\n"
+        "uniform float pointSize;\n";
 const char* gFS_Uniforms_TextureSampler =
         "uniform sampler2D sampler;\n";
 const char* gFS_Uniforms_GradientSampler[3] = {
@@ -121,6 +132,10 @@
         "\nvoid main(void) {\n"
         "    lowp vec4 fragColor;\n";
 
+const char* gFS_Main_PointBitmapTexCoords =
+        "    vec2 outBitmapTexCoords = outPointBitmapTexCoords + "
+        "((gl_PointCoord - vec2(0.5, 0.5)) * textureDimension * vec2(pointSize, pointSize));\n";
+
 // Fast cases
 const char* gFS_Fast_SingleColor =
         "\nvoid main(void) {\n"
@@ -347,6 +362,9 @@
     if (description.hasBitmap) {
         shader.append(gVS_Header_Uniforms_HasBitmap);
     }
+    if (description.isPoint) {
+        shader.append(gVS_Header_Uniforms_IsPoint);
+    }
     // Varyings
     if (description.hasTexture) {
         shader.append(gVS_Header_Varyings_HasTexture);
@@ -355,7 +373,9 @@
         shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
     }
     if (description.hasBitmap) {
-        shader.append(gVS_Header_Varyings_HasBitmap);
+        shader.append(description.isPoint ?
+                gVS_Header_Varyings_PointHasBitmap :
+                gVS_Header_Varyings_HasBitmap);
     }
 
     // Begin the shader
@@ -367,7 +387,12 @@
             shader.append(gVS_Main_OutGradient[description.gradientType]);
         }
         if (description.hasBitmap) {
-            shader.append(gVS_Main_OutBitmapTexCoords);
+            shader.append(description.isPoint ?
+                    gVS_Main_OutPointBitmapTexCoords :
+                    gVS_Main_OutBitmapTexCoords);
+        }
+        if (description.isPoint) {
+            shader.append(gVS_Main_PointSize);
         }
         // Output transformed position
         shader.append(gVS_Main_Position);
@@ -399,7 +424,9 @@
         shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
     }
     if (description.hasBitmap) {
-        shader.append(gVS_Header_Varyings_HasBitmap);
+        shader.append(description.isPoint ?
+                gVS_Header_Varyings_PointHasBitmap :
+                gVS_Header_Varyings_HasBitmap);
     }
 
     // Uniforms
@@ -417,9 +444,13 @@
     if (description.hasGradient) {
         shader.append(gFS_Uniforms_GradientSampler[description.gradientType]);
     }
+    if (description.hasBitmap && description.isPoint) {
+        shader.append(gFS_Header_Uniforms_PointHasBitmap);
+    }
 
     // Optimization for common cases
-    if (!blendFramebuffer && description.colorOp == ProgramDescription::kColorNone) {
+    if (!blendFramebuffer && description.colorOp == ProgramDescription::kColorNone &&
+            !description.isPoint) {
         bool fast = false;
 
         const bool noShader = !description.hasGradient && !description.hasBitmap;
@@ -507,6 +538,9 @@
             shader.append(gFS_Main_FetchGradient[description.gradientType]);
         }
         if (description.hasBitmap) {
+            if (description.isPoint) {
+                shader.append(gFS_Main_PointBitmapTexCoords);
+            }
             if (!description.isBitmapNpot) {
                 shader.append(gFS_Main_FetchBitmap);
             } else {
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index ead5b92..737d91b 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -71,7 +71,9 @@
 #define PROGRAM_BITMAP_WRAPT_SHIFT 11
 
 #define PROGRAM_GRADIENT_TYPE_SHIFT 33
-#define PROGRAM_MODULATE 35
+#define PROGRAM_MODULATE_SHIFT 35
+
+#define PROGRAM_IS_POINT_SHIFT 36
 
 ///////////////////////////////////////////////////////////////////////////////
 // Types
@@ -135,6 +137,9 @@
     SkXfermode::Mode framebufferMode;
     bool swapSrcDst;
 
+    bool isPoint;
+    float pointSize;
+
     /**
      * Resets this description. All fields are reset back to the default
      * values they hold after building a new instance.
@@ -162,6 +167,9 @@
 
         framebufferMode = SkXfermode::kClear_Mode;
         swapSrcDst = false;
+
+        isPoint = false;
+        pointSize = 0.0f;
     }
 
     /**
@@ -223,7 +231,8 @@
         }
         key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
         if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
-        if (modulate) key |= programid(0x1) << PROGRAM_MODULATE;
+        if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
+        if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT;
         return key;
     }
 
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
index 8ca842e..4233367 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/LinesActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.test.hwui;
 
+import android.animation.ObjectAnimator;
 import android.app.Activity;
 import android.content.Context;
 import android.graphics.Bitmap;
@@ -29,13 +30,26 @@
 
 @SuppressWarnings({"UnusedDeclaration"})
 public class LinesActivity extends Activity {
+    private ObjectAnimator mAnimator;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         getWindow().setBackgroundDrawable(new ColorDrawable(0xffffffff));
         final LinesView view = new LinesView(this);
-        //view.setAlpha(0.80f);
         setContentView(view);
+
+        mAnimator = ObjectAnimator.ofFloat(view, "offset", 0.0f, 15.0f);
+        mAnimator.setDuration(1500);
+        mAnimator.setRepeatCount(ObjectAnimator.INFINITE);
+        mAnimator.setRepeatMode(ObjectAnimator.REVERSE);
+        mAnimator.start();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mAnimator.cancel();
     }
 
     public static class LinesView extends View {
@@ -50,6 +64,8 @@
         private final Paint mAlphaPaint;
         private final Paint mHairLinePaint;
 
+        private float mOffset;
+
         public LinesView(Context c) {
             super(c);
 
@@ -89,11 +105,16 @@
                     352.0f, 400.0f, 352.0f, 500.0f
             };
         }
+        
+        public void setOffset(float offset) {
+            mOffset = offset;
+            invalidate();
+        }
 
         @Override
         protected void onDraw(Canvas canvas) {
             super.onDraw(canvas);
-
+            
             canvas.save();
             canvas.translate(100.0f, 20.0f);
 
@@ -103,6 +124,12 @@
 
             mLargePaint.setShader(mShader);
             canvas.drawLine(42.0f, 0.0f, 222.0f, 400.0f, mLargePaint);
+            for (int x = 0; x < 20; x++) {
+                for (int y = 0; y < 20; y++) {
+                    canvas.drawPoint(500.0f + x * (15.0f + mOffset),
+                            y * (15.0f + mOffset), mLargePaint);
+                }
+            }
             mLargePaint.setShader(null);
 
             canvas.drawLines(mPoints, mAlphaPaint);
@@ -120,6 +147,7 @@
 
             canvas.restore();
 
+            canvas.save();
             canvas.scale(10.0f, 10.0f);
             canvas.drawLine(50.0f, 40.0f, 10.0f, 40.0f, mSmallPaint);
             canvas.drawLine(10.0f, 50.0f, 50.0f, 50.0f, mSmallPaint);