Add tessellation path for points

bug:4351353
bug:8185479

Point tessellation is similar to line special case, except that we
only tessellate one point (as a circle or rect) and duplicate it
across other instances.

Additionally:

Fixes square caps for AA=false lines

Cleanup in CanvasCompare, disabling interpolation on zoomed-in
comparison view

Change-Id: I0756fcc4b20f77878fed0d8057297c80e82ed9dc
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index f220e4f..038df07 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1703,11 +1703,6 @@
     mDescription.isAA = true;
 }
 
-void OpenGLRenderer::setupDrawPoint(float pointSize) {
-    mDescription.isPoint = true;
-    mDescription.pointSize = pointSize;
-}
-
 void OpenGLRenderer::setupDrawColor(int color, int alpha) {
     mColorA = alpha / 255.0f;
     mColorR = mColorA * ((color >> 16) & 0xFF) / 255.0f;
@@ -1822,11 +1817,6 @@
     }
 }
 
-void OpenGLRenderer::setupDrawPointUniforms() {
-    int slot = mCaches.currentProgram->getUniform("pointSize");
-    glUniform1f(slot, mDescription.pointSize);
-}
-
 void OpenGLRenderer::setupDrawColorUniforms() {
     if ((mColorSet && !mDrawModifiers.mShader) || (mDrawModifiers.mShader && mSetShaderColor)) {
         mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
@@ -2409,7 +2399,7 @@
 
 status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint,
         bool useOffset) {
-    if (!vertexBuffer.getSize()) {
+    if (!vertexBuffer.getVertexCount()) {
         // no vertices to draw
         return DrawGlInfo::kStatusDone;
     }
@@ -2447,7 +2437,7 @@
         glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
     }
 
-    glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getSize());
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount());
 
     if (isAA) {
         glDisableVertexAttribArray(alphaSlot);
@@ -2510,65 +2500,22 @@
 }
 
 status_t OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) {
-    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
+    if (mSnapshot->isIgnored() || count < 2) return DrawGlInfo::kStatusDone;
 
-    // TODO: The paint's cap style defines whether the points are square or circular
-    // TODO: Handle AA for round points
+    count &= ~0x1; // round down to nearest two
 
-    // A stroke width of 0 has a special meaning in Skia:
-    // it draws an unscaled 1px point
-    float strokeWidth = paint->getStrokeWidth();
-    const bool isHairLine = paint->getStrokeWidth() == 0.0f;
-    if (isHairLine) {
-        // Now that we know it's hairline, we can set the effective width, to be used later
-        strokeWidth = 1.0f;
-    }
-    const float halfWidth = strokeWidth / 2;
+    VertexBuffer buffer;
+    SkRect bounds;
+    PathTessellator::tessellatePoints(points, count, paint, mSnapshot->transform, bounds, buffer);
 
-    int alpha;
-    SkXfermode::Mode mode;
-    getAlphaAndMode(paint, &alpha, &mode);
-
-    int verticesCount = count >> 1;
-    int generatedVerticesCount = 0;
-
-    TextureVertex pointsData[verticesCount];
-    TextureVertex* vertex = &pointsData[0];
-
-    // TODO: We should optimize this method to not generate vertices for points
-    // that lie outside of the clip.
-    mCaches.enableScissor();
-
-    setupDraw();
-    setupDrawNoTexture();
-    setupDrawPoint(strokeWidth);
-    setupDrawColor(paint->getColor(), alpha);
-    setupDrawColorFilter();
-    setupDrawShader();
-    setupDrawBlending(mode);
-    setupDrawProgram();
-    setupDrawModelViewIdentity(true);
-    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++;
-
-        float left = points[i] - halfWidth;
-        float right = points[i] + halfWidth;
-        float top = points[i + 1] - halfWidth;
-        float bottom = points [i + 1] + halfWidth;
-
-        dirtyLayer(left, top, right, bottom, currentTransform());
+    if (quickReject(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) {
+        return DrawGlInfo::kStatusDone;
     }
 
-    glDrawArrays(GL_POINTS, 0, generatedVerticesCount);
+    dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
 
-    return DrawGlInfo::kStatusDrew;
+    bool useOffset = !paint->isAntiAlias();
+    return drawVertexBuffer(buffer, paint, useOffset);
 }
 
 status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index df275d7..597e458 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -927,7 +927,6 @@
     void setupDrawWithExternalTexture();
     void setupDrawNoTexture();
     void setupDrawAA();
-    void setupDrawPoint(float pointSize);
     void setupDrawColor(int color, int alpha);
     void setupDrawColor(float r, float g, float b, float a);
     void setupDrawAlpha8Color(int color, int alpha);
@@ -945,7 +944,6 @@
             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/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 0879b1b..3970913 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -66,11 +66,11 @@
     }
 }
 
-inline void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
+inline static void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
     Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]);
 }
 
-inline void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
+inline static void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
     AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha);
 }
 
@@ -84,7 +84,7 @@
  *
  * NOTE: assumes angles between normals 90 degrees or less
  */
-inline vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
+inline static vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
     return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
 }
 
@@ -224,6 +224,20 @@
     DEBUG_DUMP_BUFFER();
 }
 
+static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center,
+        const vec2& normal, Vertex* buffer, int& currentIndex, bool begin) {
+    vec2 strokeOffset = normal;
+    paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
+
+    vec2 referencePoint(center.position[0], center.position[1]);
+    if (paintInfo.cap == SkPaint::kSquare_Cap) {
+        referencePoint += vec2(-strokeOffset.y, strokeOffset.x) * (begin ? -1 : 1);
+    }
+
+    Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset);
+    Vertex::set(&buffer[currentIndex++], referencePoint - strokeOffset);
+}
+
 /**
  * Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except:
  *
@@ -235,19 +249,17 @@
         const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
     const int extra = paintInfo.capExtraDivisions();
     const int allocSize = (vertices.size() + extra) * 2;
-
     Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize);
 
+    const int lastIndex = vertices.size() - 1;
     if (extra > 0) {
         // tessellate both round caps
-        const int last = vertices.size() - 1;
         float beginTheta = atan2(
-                - (vertices[0].position[0] - vertices[1].position[0]),
-                vertices[0].position[1] - vertices[1].position[1]);
+                    - (vertices[0].position[0] - vertices[1].position[0]),
+                    vertices[0].position[1] - vertices[1].position[1]);
         float endTheta = atan2(
-                - (vertices[last].position[0] - vertices[last - 1].position[0]),
-                vertices[last].position[1] - vertices[last - 1].position[1]);
-
+                    - (vertices[lastIndex].position[0] - vertices[lastIndex - 1].position[0]),
+                    vertices[lastIndex].position[1] - vertices[lastIndex - 1].position[1]);
         const float dTheta = PI / (extra + 1);
         const float radialScale = 2.0f / (1 + cos(dTheta));
 
@@ -270,56 +282,45 @@
             vec2 endRadialOffset(cos(endTheta), sin(endTheta));
             paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
             Vertex::set(&buffer[allocSize - 1 - capOffset],
-                    vertices[last].position[0] + endRadialOffset.x,
-                    vertices[last].position[1] + endRadialOffset.y);
+                    vertices[lastIndex].position[0] + endRadialOffset.x,
+                    vertices[lastIndex].position[1] + endRadialOffset.y);
         }
     }
 
     int currentIndex = extra;
-    const Vertex* current = &(vertices[0]);
-    vec2 lastNormal;
-    for (unsigned int i = 0; i < vertices.size() - 1; i++) {
+    const Vertex* last = &(vertices[0]);
+    const Vertex* current = &(vertices[1]);
+    vec2 lastNormal(current->position[1] - last->position[1],
+                last->position[0] - current->position[0]);
+    lastNormal.normalize();
+
+    storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true);
+
+    for (unsigned int i = 1; i < vertices.size() - 1; i++) {
         const Vertex* next = &(vertices[i + 1]);
         vec2 nextNormal(next->position[1] - current->position[1],
                 current->position[0] - next->position[0]);
         nextNormal.normalize();
 
-        vec2 totalOffset;
-        if (i == 0) {
-            totalOffset = nextNormal;
-        } else {
-            totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
-        }
-        paintInfo.scaleOffsetForStrokeWidth(totalOffset);
+        vec2 strokeOffset  = totalOffsetFromNormals(lastNormal, nextNormal);
+        paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
 
-        Vertex::set(&buffer[currentIndex++],
-                current->position[0] + totalOffset.x,
-                current->position[1] + totalOffset.y);
-
-        Vertex::set(&buffer[currentIndex++],
-                current->position[0] - totalOffset.x,
-                current->position[1] - totalOffset.y);
+        vec2 center(current->position[0], current->position[1]);
+        Vertex::set(&buffer[currentIndex++], center + strokeOffset);
+        Vertex::set(&buffer[currentIndex++], center - strokeOffset);
 
         current = next;
         lastNormal = nextNormal;
     }
 
-    vec2 totalOffset = lastNormal;
-    paintInfo.scaleOffsetForStrokeWidth(totalOffset);
-
-    Vertex::set(&buffer[currentIndex++],
-            current->position[0] + totalOffset.x,
-            current->position[1] + totalOffset.y);
-    Vertex::set(&buffer[currentIndex++],
-            current->position[0] - totalOffset.x,
-            current->position[1] - totalOffset.y);
+    storeBeginEnd(paintInfo, vertices[lastIndex], lastNormal, buffer, currentIndex, false);
 
     DEBUG_DUMP_BUFFER();
 }
 
 /**
  * Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation
- * 
+ *
  * 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of
  * the shape (using 2 * perimeter.size() vertices)
  *
@@ -389,7 +390,7 @@
  * For explanation of constants and general methodoloyg, see comments for
  * getStrokeVerticesFromUnclosedVerticesAA() below.
  */
-inline void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
+inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
         AlphaVertex* buffer, bool isFirst, vec2 normal, int offset) {
     const int extra = paintInfo.capExtraDivisions();
     const int extraOffset = (extra + 1) / 2;
@@ -772,11 +773,67 @@
     }
 }
 
+static void expandRectToCoverVertex(SkRect& rect, float x, float y) {
+    rect.fLeft = fminf(rect.fLeft, x);
+    rect.fTop = fminf(rect.fTop, y);
+    rect.fRight = fmaxf(rect.fRight, x);
+    rect.fBottom = fmaxf(rect.fBottom, y);
+}
 static void expandRectToCoverVertex(SkRect& rect, const Vertex& vertex) {
-    rect.fLeft = fminf(rect.fLeft, vertex.position[0]);
-    rect.fTop = fminf(rect.fTop, vertex.position[1]);
-    rect.fRight = fmaxf(rect.fRight, vertex.position[0]);
-    rect.fBottom = fmaxf(rect.fBottom, vertex.position[1]);
+    expandRectToCoverVertex(rect, vertex.position[0], vertex.position[1]);
+}
+
+template <class TYPE>
+static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
+        const float* points, int count, SkRect& bounds) {
+    bounds.set(points[0], points[1], points[0], points[1]);
+
+    int numPoints = count / 2;
+    int verticesPerPoint = srcBuffer.getVertexCount();
+    dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
+
+    for (int i = 0; i < count; i += 2) {
+        expandRectToCoverVertex(bounds, points[i + 0], points[i + 1]);
+        dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
+    }
+    dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
+}
+
+void PathTessellator::tessellatePoints(const float* points, int count, SkPaint* paint,
+        const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer) {
+    const PaintInfo paintInfo(paint, transform);
+
+    // determine point shape
+    SkPath path;
+    float radius = paintInfo.halfStrokeWidth;
+    if (radius == 0.0f) radius = 0.25f;
+
+    if (paintInfo.cap == SkPaint::kRound_Cap) {
+        path.addCircle(0, 0, radius);
+    } else {
+        path.addRect(-radius, -radius, radius, radius);
+    }
+
+    // calculate outline
+    Vector<Vertex> outlineVertices;
+    approximatePathOutlineVertices(path, true,
+            paintInfo.inverseScaleX * paintInfo.inverseScaleX,
+            paintInfo.inverseScaleY * paintInfo.inverseScaleY, outlineVertices);
+
+    if (!outlineVertices.size()) return;
+
+    // tessellate, then duplicate outline across points
+    int numPoints = count / 2;
+    VertexBuffer tempBuffer;
+    if (!paintInfo.isAA) {
+        getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
+        instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds);
+    } else {
+        getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer);
+        instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds);
+    }
+
+    expandBoundsForStroke(bounds, paint, true); // force-expand bounds to incorporate stroke
 }
 
 void PathTessellator::tessellateLines(const float* points, int count, SkPaint* paint,
diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h
index 596d49d..85797fc 100644
--- a/libs/hwui/PathTessellator.h
+++ b/libs/hwui/PathTessellator.h
@@ -30,7 +30,7 @@
 public:
     VertexBuffer():
         mBuffer(0),
-        mSize(0),
+        mVertexCount(0),
         mCleanupMethod(NULL)
     {}
 
@@ -44,30 +44,42 @@
        multiple regions within a single VertexBuffer, such as with PathTessellator::tesselateLines()
      */
     template <class TYPE>
-    TYPE* alloc(int size) {
-        if (mSize) {
+    TYPE* alloc(int vertexCount) {
+        if (mVertexCount) {
             TYPE* reallocBuffer = (TYPE*)mReallocBuffer;
             // already have allocated the buffer, re-allocate space within
             if (mReallocBuffer != mBuffer) {
                 // not first re-allocation, leave space for degenerate triangles to separate strips
                 reallocBuffer += 2;
             }
-            mReallocBuffer = reallocBuffer + size;
+            mReallocBuffer = reallocBuffer + vertexCount;
             return reallocBuffer;
         }
-        mSize = size;
-        mReallocBuffer = mBuffer = (void*)new TYPE[size];
+        mVertexCount = vertexCount;
+        mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount];
         mCleanupMethod = &(cleanup<TYPE>);
 
         return (TYPE*)mBuffer;
     }
 
-    void* getBuffer() const { return mBuffer; }
-    unsigned int getSize() const { return mSize; }
+    template <class TYPE>
+    void copyInto(const VertexBuffer& srcBuffer, float xOffset, float yOffset) {
+        int verticesToCopy = srcBuffer.getVertexCount();
+
+        TYPE* dst = alloc<TYPE>(verticesToCopy);
+        TYPE* src = (TYPE*)srcBuffer.getBuffer();
+
+        for (int i = 0; i < verticesToCopy; i++) {
+            TYPE::copyWithOffset(&dst[i], src[i], xOffset, yOffset);
+        }
+    }
+
+    void* getBuffer() const { return mBuffer; } // shouldn't be const, since not a const ptr?
+    unsigned int getVertexCount() const { return mVertexCount; }
 
     template <class TYPE>
     void createDegenerateSeparators(int allocSize) {
-        TYPE* end = (TYPE*)mBuffer + mSize;
+        TYPE* end = (TYPE*)mBuffer + mVertexCount;
         for (TYPE* degen = (TYPE*)mBuffer + allocSize; degen < end; degen += 2 + allocSize) {
             memcpy(degen, degen - 1, sizeof(TYPE));
             memcpy(degen + 1, degen + 2, sizeof(TYPE));
@@ -81,7 +93,7 @@
     }
 
     void* mBuffer;
-    unsigned int mSize;
+    unsigned int mVertexCount;
 
     void* mReallocBuffer; // used for multi-allocation
 
@@ -95,6 +107,9 @@
     static void tessellatePath(const SkPath& path, const SkPaint* paint,
             const mat4 *transform, VertexBuffer& vertexBuffer);
 
+    static void tessellatePoints(const float* points, int count, SkPaint* paint,
+            const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer);
+
     static void tessellateLines(const float* points, int count, SkPaint* paint,
             const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer);
 
diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h
index dd1aaa2..4f94afc 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -68,24 +68,22 @@
 #define PROGRAM_BITMAP_WRAPS_SHIFT 9
 #define PROGRAM_BITMAP_WRAPT_SHIFT 11
 
-#define PROGRAM_GRADIENT_TYPE_SHIFT 33
+#define PROGRAM_GRADIENT_TYPE_SHIFT 33 // 2 bits for gradient type
 #define PROGRAM_MODULATE_SHIFT 35
 
-#define PROGRAM_IS_POINT_SHIFT 36
+#define PROGRAM_HAS_AA_SHIFT 36
 
-#define PROGRAM_HAS_AA_SHIFT 37
+#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 37
+#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 38
 
-#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
-#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
+#define PROGRAM_HAS_GAMMA_CORRECTION 39
 
-#define PROGRAM_HAS_GAMMA_CORRECTION 40
+#define PROGRAM_IS_SIMPLE_GRADIENT 40
 
-#define PROGRAM_IS_SIMPLE_GRADIENT 41
+#define PROGRAM_HAS_COLORS 41
 
-#define PROGRAM_HAS_COLORS 42
-
-#define PROGRAM_HAS_DEBUG_HIGHLIGHT 43
-#define PROGRAM_EMULATE_STENCIL 44
+#define PROGRAM_HAS_DEBUG_HIGHLIGHT 42
+#define PROGRAM_EMULATE_STENCIL 43
 
 ///////////////////////////////////////////////////////////////////////////////
 // Types
@@ -157,9 +155,6 @@
     SkXfermode::Mode framebufferMode;
     bool swapSrcDst;
 
-    bool isPoint;
-    float pointSize;
-
     bool hasGammaCorrection;
     float gamma;
 
@@ -201,9 +196,6 @@
         framebufferMode = SkXfermode::kClear_Mode;
         swapSrcDst = false;
 
-        isPoint = false;
-        pointSize = 0.0f;
-
         hasGammaCorrection = false;
         gamma = 2.2f;
 
@@ -269,7 +261,6 @@
         key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
         if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
         if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
-        if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT;
         if (isAA) key |= programid(0x1) << PROGRAM_HAS_AA_SHIFT;
         if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
         if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 367294c..a5ce6f6 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -53,8 +53,6 @@
 const char* gVS_Header_Uniforms =
         "uniform mat4 projection;\n" \
         "uniform mat4 transform;\n";
-const char* gVS_Header_Uniforms_IsPoint =
-        "uniform mediump float pointSize;\n";
 const char* gVS_Header_Uniforms_HasGradient =
         "uniform mat4 screenSpace;\n";
 const char* gVS_Header_Uniforms_HasBitmap =
@@ -68,8 +66,6 @@
         "varying float alpha;\n";
 const char* gVS_Header_Varyings_HasBitmap =
         "varying highp vec2 outBitmapTexCoords;\n";
-const char* gVS_Header_Varyings_PointHasBitmap =
-        "varying highp vec2 outPointBitmapTexCoords;\n";
 const char* gVS_Header_Varyings_HasGradient[6] = {
         // Linear
         "varying highp vec2 linear;\n"
@@ -118,12 +114,8 @@
 };
 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 = projection * transform * position;\n";
-const char* gVS_Main_PointSize =
-        "    gl_PointSize = pointSize;\n";
 const char* gVS_Main_AAVertexShape =
         "    alpha = vtxAlpha;\n";
 const char* gVS_Footer =
@@ -141,9 +133,6 @@
         "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 baseSampler;\n";
 const char* gFS_Uniforms_ExternalTextureSampler =
@@ -178,10 +167,6 @@
         "\nvoid main(void) {\n"
         "    lowp vec4 fragColor;\n";
 
-const char* gFS_Main_PointBitmapTexCoords =
-        "    highp vec2 outBitmapTexCoords = outPointBitmapTexCoords + "
-        "((gl_PointCoord - vec2(0.5, 0.5)) * textureDimension * vec2(pointSize, pointSize));\n";
-
 const char* gFS_Main_Dither[2] = {
         // ES 2.0
         "texture2D(ditherSampler, ditherTexCoords).a * " STR(DITHER_KERNEL_SIZE_INV_SQUARE),
@@ -484,9 +469,6 @@
     if (description.hasBitmap) {
         shader.append(gVS_Header_Uniforms_HasBitmap);
     }
-    if (description.isPoint) {
-        shader.append(gVS_Header_Uniforms_IsPoint);
-    }
     // Varyings
     if (description.hasTexture || description.hasExternalTexture) {
         shader.append(gVS_Header_Varyings_HasTexture);
@@ -501,9 +483,7 @@
         shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
     }
     if (description.hasBitmap) {
-        shader.append(description.isPoint ?
-                gVS_Header_Varyings_PointHasBitmap :
-                gVS_Header_Varyings_HasBitmap);
+        shader.append(gVS_Header_Varyings_HasBitmap);
     }
 
     // Begin the shader
@@ -520,12 +500,7 @@
             shader.append(gVS_Main_OutColors);
         }
         if (description.hasBitmap) {
-            shader.append(description.isPoint ?
-                    gVS_Main_OutPointBitmapTexCoords :
-                    gVS_Main_OutBitmapTexCoords);
-        }
-        if (description.isPoint) {
-            shader.append(gVS_Main_PointSize);
+            shader.append(gVS_Main_OutBitmapTexCoords);
         }
         // Output transformed position
         shader.append(gVS_Main_Position);
@@ -576,9 +551,7 @@
         shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
     }
     if (description.hasBitmap) {
-        shader.append(description.isPoint ?
-                gVS_Header_Varyings_PointHasBitmap :
-                gVS_Header_Varyings_HasBitmap);
+        shader.append(gVS_Header_Varyings_HasBitmap);
     }
 
     // Uniforms
@@ -599,9 +572,6 @@
         shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient],
                 gFS_Uniforms_Dither);
     }
-    if (description.hasBitmap && description.isPoint) {
-        shader.append(gFS_Header_Uniforms_PointHasBitmap);
-    }
     if (description.hasGammaCorrection) {
         shader.append(gFS_Uniforms_Gamma);
     }
@@ -609,8 +579,7 @@
     // Optimization for common cases
     if (!description.isAA && !blendFramebuffer && !description.hasColors &&
             description.colorOp == ProgramDescription::kColorNone &&
-            !description.isPoint && !description.hasDebugHighlight &&
-            !description.emulateStencil) {
+            !description.hasDebugHighlight && !description.emulateStencil) {
         bool fast = false;
 
         const bool noShader = !description.hasGradient && !description.hasBitmap;
@@ -713,9 +682,6 @@
             shader.appendFormat(gFS_Main_AddDitherToGradient, gFS_Main_Dither[mHasES3]);
         }
         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/Vertex.h b/libs/hwui/Vertex.h
index 523120e..c06762f 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_HWUI_VERTEX_H
 #define ANDROID_HWUI_VERTEX_H
 
+#include "Vector.h"
+
 namespace android {
 namespace uirenderer {
 
@@ -30,6 +32,15 @@
         vertex[0].position[0] = x;
         vertex[0].position[1] = y;
     }
+
+    static inline void set(Vertex* vertex, vec2 val) {
+        set(vertex, val.x, val.y);
+    }
+
+    static inline void copyWithOffset(Vertex* vertex, const Vertex& src, float x, float y) {
+        set(vertex, src.position[0] + x, src.position[1] + y);
+    }
+
 }; // struct Vertex
 
 /**
@@ -81,6 +92,12 @@
         vertex[0].alpha = alpha;
     }
 
+    static inline void copyWithOffset(AlphaVertex* vertex, const AlphaVertex& src,
+            float x, float y) {
+        Vertex::set(vertex, src.position[0] + x, src.position[1] + y);
+        vertex[0].alpha = src.alpha;
+    }
+
     static inline void setColor(AlphaVertex* vertex, float alpha) {
         vertex[0].alpha = alpha;
     }