Line endcaps for AA lines are now antialiased.

Also fixed other minor issues with AA and line rendering.

Change-Id: Icd4638d27c70e2ee0f28b5d9a2b97d8b29e8ac4d
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index faecadd..596781e 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -62,8 +62,10 @@
 static const GLsizei gMeshStride = sizeof(TextureVertex);
 static const GLsizei gVertexStride = sizeof(Vertex);
 static const GLsizei gAlphaVertexStride = sizeof(AlphaVertex);
+static const GLsizei gAAVertexStride = sizeof(AAVertex);
 static const GLsizei gMeshTextureOffset = 2 * sizeof(float);
-static const GLsizei gVertexAlphaOffset = 2 * sizeof(float);
+static const GLsizei gVertexAAWidthOffset = 2 * sizeof(float);
+static const GLsizei gVertexAALengthOffset = 3 * sizeof(float);
 static const GLsizei gMeshCount = 4;
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 34d8fd3..049e9b7 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -915,7 +915,7 @@
 }
 
 void OpenGLRenderer::setupDrawAALine() {
-    mDescription.hasWidth = true;
+    mDescription.isAA = true;
 }
 
 void OpenGLRenderer::setupDrawPoint(float pointSize) {
@@ -1121,25 +1121,30 @@
 
 /**
  * Sets up the shader to draw an AA line. We draw AA lines with quads, where there is an
- * outer boundary that fades out to 0. The variables set in the shader define the width of the
- * core line primitive ("width") and the width of the fading boundary ("boundaryWidth"). The
- * "vtxDistance" attribute (one per vertex) is a value from zero to one that tells the fragment
- * shader where the fragment is in relation to the line width overall; this value is then used
- * to compute the proper color, based on whether the fragment lies in the fading AA region of
- * the line.
+ * outer boundary that fades out to 0. The variables set in the shader define the proportion of
+ * the width and length of the primitive occupied by the AA region. The vtxWidth and vtxLength
+ * attributes (one per vertex) are values from zero to one that tells the fragment
+ * shader where the fragment is in relation to the line width/length overall; these values are
+ * then used to compute the proper color, based on whether the fragment lies in the fading AA
+ * region of the line.
+ * Note that we only pass down the width values in this setup function. The length coordinates
+ * are set up for each individual segment.
  */
-void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, float strokeWidth) {
+void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* widthCoords,
+        GLvoid* lengthCoords, float strokeWidth) {
     mCaches.unbindMeshBuffer();
     glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE,
-            gAlphaVertexStride, vertices);
-    int distanceSlot = mCaches.currentProgram->getAttrib("vtxDistance");
-    glEnableVertexAttribArray(distanceSlot);
-    glVertexAttribPointer(distanceSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, distanceCoords);
-    int widthSlot = mCaches.currentProgram->getUniform("width");
+            gAAVertexStride, vertices);
+    int widthSlot = mCaches.currentProgram->getAttrib("vtxWidth");
+    glEnableVertexAttribArray(widthSlot);
+    glVertexAttribPointer(widthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, widthCoords);
+    int lengthSlot = mCaches.currentProgram->getAttrib("vtxLength");
+    glEnableVertexAttribArray(lengthSlot);
+    glVertexAttribPointer(lengthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, lengthCoords);
     int boundaryWidthSlot = mCaches.currentProgram->getUniform("boundaryWidth");
+    // Setting the inverse value saves computations per-fragment in the shader
     int inverseBoundaryWidthSlot = mCaches.currentProgram->getUniform("inverseBoundaryWidth");
     float boundaryWidth = (1 - strokeWidth) / 2;
-    glUniform1f(widthSlot, strokeWidth);
     glUniform1f(boundaryWidthSlot, boundaryWidth);
     glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidth));
 }
@@ -1480,20 +1485,21 @@
     }
     Vertex lines[verticesCount];
     Vertex* vertices = &lines[0];
-    AlphaVertex wLines[verticesCount];
-    AlphaVertex* aaVertices = &wLines[0];
+    AAVertex wLines[verticesCount];
+    AAVertex* aaVertices = &wLines[0];
     if (!isAA) {
         setupDrawVertices(vertices);
     } else {
-        void* alphaCoords = ((GLbyte*) aaVertices) + gVertexAlphaOffset;
+        void* widthCoords = ((GLbyte*) aaVertices) + gVertexAAWidthOffset;
+        void* lengthCoords = ((GLbyte*) aaVertices) + gVertexAALengthOffset;
         // innerProportion is the ratio of the inner (non-AA) port of the line to the total
         // AA stroke width (the base stroke width expanded by a half pixel on either side).
         // This value is used in the fragment shader to determine how to fill fragments.
         float innerProportion = fmax(strokeWidth - 1.0f, 0) / (strokeWidth + .5f);
-        setupDrawAALine((void*) aaVertices, alphaCoords, innerProportion);
+        setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords, innerProportion);
     }
 
-    AlphaVertex *prevAAVertex = NULL;
+    AAVertex *prevAAVertex = NULL;
     Vertex *prevVertex = NULL;
     float inverseScaleX = 1.0f;
     float inverseScaleY = 1.0f;
@@ -1516,15 +1522,17 @@
         }
     }
 
+    int boundaryLengthSlot = -1;
+    int inverseBoundaryLengthSlot = -1;
     for (int i = 0; i < count; i += 4) {
         // a = start point, b = end point
         vec2 a(points[i], points[i + 1]);
         vec2 b(points[i + 2], points[i + 3]);
+        float length = 0;
 
         // Find the normal to the line
         vec2 n = (b - a).copyNormalized() * strokeWidth;
         if (isHairLine) {
-            n *= inverseScaleX;
             if (isAA) {
                 float wideningFactor;
                 if (fabs(n.x) >= fabs(n.y)) {
@@ -1534,27 +1542,35 @@
                 }
                 n *= wideningFactor;
             }
+            n.x *= inverseScaleX;
+            n.y *= inverseScaleY;
         }
         float x = n.x;
         n.x = -n.y;
         n.y = x;
 
+        // aa lines expand the endpoint vertices to encompass the AA boundary
+        if (isAA) {
+            vec2 abVector = (b - a);
+            length = abVector.length();
+            abVector.normalize();
+            a -= abVector;
+            b += abVector;
+        }
+
         // Four corners of the rectangle defining a thick line
         vec2 p1 = a - n;
         vec2 p2 = a + n;
         vec2 p3 = b + n;
         vec2 p4 = b - n;
 
+
         const float left = fmin(p1.x, fmin(p2.x, fmin(p3.x, p4.x)));
         const float right = fmax(p1.x, fmax(p2.x, fmax(p3.x, p4.x)));
         const float top = fmin(p1.y, fmin(p2.y, fmin(p3.y, p4.y)));
         const float bottom = fmax(p1.y, fmax(p2.y, fmax(p3.y, p4.y)));
 
         if (!quickReject(left, top, right, bottom)) {
-            // Draw the line as 2 triangles, could be optimized
-            // by using only 4 vertices and the correct indices
-            // Also we should probably used non textured vertices
-            // when line AA is disabled to save on bandwidth
             if (!isAA) {
                 if (prevVertex != NULL) {
                     // Issue two repeat vertices to create degenerate triangles to bridge
@@ -1572,24 +1588,36 @@
                 prevVertex = vertices - 1;
                 generatedVerticesCount += 4;
             } else {
+                if (boundaryLengthSlot < 0) {
+                    boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
+                    inverseBoundaryLengthSlot =
+                            mCaches.currentProgram->getUniform("inverseBoundaryLength");
+                }
+                float innerProportion = (length) / (length + 2);
+                float boundaryLength = (1 - innerProportion) / 2;
+                glUniform1f(boundaryLengthSlot, boundaryLength);
+                glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryLength));
+
                 if (prevAAVertex != NULL) {
                     // Issue two repeat vertices to create degenerate triangles to bridge
                     // between the previous line and the new one. This is necessary because
                     // we are creating a single triangle_strip which will contain
                     // potentially discontinuous line segments.
-                    AlphaVertex::set(aaVertices++,prevAAVertex->position[0],
-                            prevAAVertex->position[1], prevAAVertex->alpha);
-                    AlphaVertex::set(aaVertices++, p4.x, p4.y, 1);
+                    AAVertex::set(aaVertices++,prevAAVertex->position[0],
+                            prevAAVertex->position[1], prevAAVertex->width, prevAAVertex->length);
+                    AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1);
                     generatedVerticesCount += 2;
                 }
-                AlphaVertex::set(aaVertices++, p4.x, p4.y, 1);
-                AlphaVertex::set(aaVertices++, p1.x, p1.y, 1);
-                AlphaVertex::set(aaVertices++, p3.x, p3.y, 0);
-                AlphaVertex::set(aaVertices++, p2.x, p2.y, 0);
+                AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1);
+                AAVertex::set(aaVertices++, p1.x, p1.y, 1, 0);
+                AAVertex::set(aaVertices++, p3.x, p3.y, 0, 1);
+                AAVertex::set(aaVertices++, p2.x, p2.y, 0, 0);
                 prevAAVertex = aaVertices - 1;
                 generatedVerticesCount += 4;
             }
-            dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
+            dirtyLayer(a.x == b.x ? left - 1 : left, a.y == b.y ? top - 1 : top,
+                    a.x == b.x ? right: right, a.y == b.y ? bottom: bottom,
+                    *mSnapshot->transform);
         }
     }
     if (generatedVerticesCount > 0) {
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 0ffd70b..6ffd931 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -468,7 +468,8 @@
     void setupDrawTextureTransform(mat4& transform);
     void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0);
     void setupDrawVertices(GLvoid* vertices);
-    void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, float strokeWidth);
+    void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, GLvoid* lengthCoords,
+            float strokeWidth);
     void finishDrawTexture();
 
     void drawRegionRects(const Region& region);
diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp
index 62ac2ba..5bfe7a3 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -39,8 +39,9 @@
         "attribute vec4 position;\n";
 const char* gVS_Header_Attributes_TexCoords =
         "attribute vec2 texCoords;\n";
-const char* gVS_Header_Attributes_Distance =
-        "attribute float vtxDistance;\n";
+const char* gVS_Header_Attributes_AAParameters =
+        "attribute float vtxWidth;\n"
+        "attribute float vtxLength;\n";
 const char* gVS_Header_Uniforms_TextureTransform =
         "uniform mat4 mainTextureTransform;\n";
 const char* gVS_Header_Uniforms =
@@ -60,8 +61,9 @@
         "uniform mediump vec2 textureDimension;\n";
 const char* gVS_Header_Varyings_HasTexture =
         "varying vec2 outTexCoords;\n";
-const char* gVS_Header_Varyings_HasWidth =
-        "varying float distance;\n";
+const char* gVS_Header_Varyings_IsAA =
+        "varying float widthProportion;\n"
+        "varying float lengthProportion;\n";
 const char* gVS_Header_Varyings_HasBitmap =
         "varying vec2 outBitmapTexCoords;\n";
 const char* gVS_Header_Varyings_PointHasBitmap =
@@ -96,8 +98,9 @@
         "    gl_Position = transform * position;\n";
 const char* gVS_Main_PointSize =
         "    gl_PointSize = pointSize;\n";
-const char* gVS_Main_Width =
-        "    distance = vtxDistance;\n";
+const char* gVS_Main_AA =
+        "    widthProportion = vtxWidth;\n"
+        "    lengthProportion = vtxLength;\n";
 const char* gVS_Footer =
         "}\n\n";
 
@@ -113,10 +116,11 @@
         "precision mediump float;\n\n";
 const char* gFS_Uniforms_Color =
         "uniform vec4 color;\n";
-const char* gFS_Uniforms_Width =
-        "uniform float width;\n"
+const char* gFS_Uniforms_AA =
         "uniform float boundaryWidth;\n"
-        "uniform float inverseBoundaryWidth;\n";
+        "uniform float inverseBoundaryWidth;\n"
+        "uniform float boundaryLength;\n"
+        "uniform float inverseBoundaryLength;\n";
 const char* gFS_Header_Uniforms_PointHasBitmap =
         "uniform vec2 textureDimension;\n"
         "uniform float pointSize;\n";
@@ -189,11 +193,16 @@
         "    fragColor = color;\n";
 const char* gFS_Main_ModulateColor =
         "    fragColor *= color.a;\n";
-const char* gFS_Main_AccountForWidth =
-        "    if (distance < boundaryWidth) {\n"
-        "        fragColor *= (distance * inverseBoundaryWidth);\n"
-        "    } else if (distance > (1.0 - boundaryWidth)) {\n"
-        "        fragColor *= ((1.0 - distance) * inverseBoundaryWidth);\n"
+const char* gFS_Main_AccountForAA =
+        "    if (widthProportion < boundaryWidth) {\n"
+        "        fragColor *= (widthProportion * inverseBoundaryWidth);\n"
+        "    } else if (widthProportion > (1.0 - boundaryWidth)) {\n"
+        "        fragColor *= ((1.0 - widthProportion) * inverseBoundaryWidth);\n"
+        "    }\n"
+        "    if (lengthProportion < boundaryLength) {\n"
+        "        fragColor *= (lengthProportion * inverseBoundaryLength);\n"
+        "    } else if (lengthProportion > (1.0 - boundaryLength)) {\n"
+        "        fragColor *= ((1.0 - lengthProportion) * inverseBoundaryLength);\n"
         "    }\n";
 const char* gFS_Main_FetchTexture[2] = {
         // Don't modulate
@@ -380,8 +389,8 @@
     if (description.hasTexture || description.hasExternalTexture) {
         shader.append(gVS_Header_Attributes_TexCoords);
     }
-    if (description.hasWidth) {
-        shader.append(gVS_Header_Attributes_Distance);
+    if (description.isAA) {
+        shader.append(gVS_Header_Attributes_AAParameters);
     }
     // Uniforms
     shader.append(gVS_Header_Uniforms);
@@ -401,8 +410,8 @@
     if (description.hasTexture || description.hasExternalTexture) {
         shader.append(gVS_Header_Varyings_HasTexture);
     }
-    if (description.hasWidth) {
-        shader.append(gVS_Header_Varyings_HasWidth);
+    if (description.isAA) {
+        shader.append(gVS_Header_Varyings_IsAA);
     }
     if (description.hasGradient) {
         shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
@@ -421,8 +430,8 @@
         if (description.hasExternalTexture) {
             shader.append(gVS_Main_OutTransformedTexCoords);
         }
-        if (description.hasWidth) {
-            shader.append(gVS_Main_Width);
+        if (description.isAA) {
+            shader.append(gVS_Main_AA);
         }
         if (description.hasGradient) {
             shader.append(gVS_Main_OutGradient[description.gradientType]);
@@ -464,8 +473,8 @@
     if (description.hasTexture || description.hasExternalTexture) {
         shader.append(gVS_Header_Varyings_HasTexture);
     }
-    if (description.hasWidth) {
-        shader.append(gVS_Header_Varyings_HasWidth);
+    if (description.isAA) {
+        shader.append(gVS_Header_Varyings_IsAA);
     }
     if (description.hasGradient) {
         shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]);
@@ -491,8 +500,8 @@
     if (description.hasExternalTexture) {
         shader.append(gFS_Uniforms_ExternalTextureSampler);
     }
-    if (description.hasWidth) {
-        shader.append(gFS_Uniforms_Width);
+    if (description.isAA) {
+        shader.append(gFS_Uniforms_AA);
     }
     if (description.hasGradient) {
         shader.append(gFS_Uniforms_GradientSampler[description.gradientType]);
@@ -502,7 +511,7 @@
     }
 
     // Optimization for common cases
-    if (!description.hasWidth && !blendFramebuffer &&
+    if (!description.isAA && !blendFramebuffer &&
             description.colorOp == ProgramDescription::kColorNone && !description.isPoint) {
         bool fast = false;
 
@@ -587,8 +596,8 @@
                 shader.append(gFS_Main_FetchColor);
             }
         }
-        if (description.hasWidth) {
-            shader.append(gFS_Main_AccountForWidth);
+        if (description.isAA) {
+            shader.append(gFS_Main_AccountForAA);
         }
         if (description.hasGradient) {
             shader.append(gFS_Main_FetchGradient[description.gradientType]);
diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h
index 70909fd..2586636 100644
--- a/libs/hwui/ProgramCache.h
+++ b/libs/hwui/ProgramCache.h
@@ -75,7 +75,7 @@
 
 #define PROGRAM_IS_POINT_SHIFT 36
 
-#define PROGRAM_HAS_WIDTH_SHIFT 37
+#define PROGRAM_HAS_AA_SHIFT 37
 
 #define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
 
@@ -124,7 +124,7 @@
     bool hasBitmap;
     bool isBitmapNpot;
 
-    bool hasWidth;
+    bool isAA;
 
     bool hasGradient;
     Gradient gradientType;
@@ -156,7 +156,7 @@
         hasAlpha8Texture = false;
         hasExternalTexture = false;
 
-        hasWidth = false;
+        isAA = false;
 
         modulate = false;
 
@@ -243,7 +243,7 @@
         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 (hasWidth) key |= programid(0x1) << PROGRAM_HAS_WIDTH_SHIFT;
+        if (isAA) key |= programid(0x1) << PROGRAM_HAS_AA_SHIFT;
         if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
         return key;
     }
diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h
index c120428..38455dc 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -68,6 +68,25 @@
     }
 }; // struct AlphaVertex
 
+/**
+ * Simple structure to describe a vertex with a position and an alpha value.
+ */
+struct AAVertex : Vertex {
+    float width;
+    float length;
+
+    static inline void set(AAVertex* vertex, float x, float y, float width, float length) {
+        Vertex::set(vertex, x, y);
+        vertex[0].width = width;
+        vertex[0].length = length;
+    }
+
+    static inline void setColor(AAVertex* vertex, float width, float length) {
+        vertex[0].width = width;
+        vertex[0].length = length;
+    }
+}; // struct AlphaVertex
+
 }; // namespace uirenderer
 }; // namespace android
 
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
index 55fab3f..7173a85 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java
@@ -86,6 +86,14 @@
             canvas.drawLines(copyPoints, 0, 12, p);
         }
 
+        private void drawVerticalLine(Canvas canvas, Paint p, float length, float x, float y) {
+            canvas.drawLine(x, y, x, y + length, p);
+        }
+
+        private void drawDiagonalLine(Canvas canvas, Paint p, float length, float x, float y) {
+            canvas.drawLine(x, y, x + length, y + length, p);
+        }
+
         @Override
         protected void onDraw(Canvas canvas) {
             super.onDraw(canvas);
@@ -145,6 +153,99 @@
             canvas.translate(60, 0);
             drawLines(canvas, p, mOffset/2, yOffset/2);
             canvas.restore();
+
+            yOffset += 100;
+            canvas.save();
+            p.setStrokeWidth(1);
+            float x = 10 + mOffset;
+            for (float length = 1; length <= 10; length +=1 ) {
+                p.setAntiAlias(false);
+                drawVerticalLine(canvas, p, length, x, yOffset);
+                x += 5;
+                p.setAntiAlias(true);
+                drawVerticalLine(canvas, p, length, x, yOffset);
+                x += 5;
+            }
+            p.setStrokeWidth(5);
+            for (float length = 1; length <= 10; length +=1 ) {
+                p.setAntiAlias(false);
+                drawVerticalLine(canvas, p, length, x, yOffset);
+                x += 10;
+                p.setAntiAlias(true);
+                drawVerticalLine(canvas, p, length, x, yOffset);
+                x += 10;
+            }
+            canvas.restore();
+
+            yOffset += 20;
+            canvas.save();
+            p.setStrokeWidth(1);
+            x = 10 + mOffset;
+            for (float length = 1; length <= 10; length +=1 ) {
+                p.setAntiAlias(false);
+                drawDiagonalLine(canvas, p, length, x, yOffset);
+                x += 5;
+                p.setAntiAlias(true);
+                drawDiagonalLine(canvas, p, length, x, yOffset);
+                x += 5;
+            }
+            p.setStrokeWidth(2);
+            for (float length = 1; length <= 10; length +=1 ) {
+                p.setAntiAlias(false);
+                drawDiagonalLine(canvas, p, length, x, yOffset);
+                x += 10;
+                p.setAntiAlias(true);
+                drawDiagonalLine(canvas, p, length, x, yOffset);
+                x += 10;
+            }
+            canvas.restore();
+
+            yOffset += 20;
+            canvas.save();
+            p.setStrokeWidth(1);
+            x = 10 + mOffset;
+            for (float length = 1; length <= 10; length +=1 ) {
+                p.setAntiAlias(false);
+                canvas.drawLine(x, yOffset, x + 1, yOffset + length, p);
+                x += 5;
+                p.setAntiAlias(true);
+                canvas.drawLine(x, yOffset, x + 1, yOffset + length, p);
+                x += 5;
+            }
+            p.setStrokeWidth(2);
+            for (float length = 1; length <= 10; length +=1 ) {
+                p.setAntiAlias(false);
+                canvas.drawLine(x, yOffset, x + 1, yOffset + length, p);
+                x += 10;
+                p.setAntiAlias(true);
+                canvas.drawLine(x, yOffset, x + 1, yOffset + length, p);
+                x += 10;
+            }
+            canvas.restore();
+
+            yOffset += 20;
+            canvas.save();
+            p.setStrokeWidth(1);
+            x = 10 + mOffset;
+            for (float length = 1; length <= 10; length +=1 ) {
+                p.setAntiAlias(false);
+                canvas.drawLine(x, yOffset, x + length, yOffset + 1, p);
+                x += 5;
+                p.setAntiAlias(true);
+                canvas.drawLine(x, yOffset, x + length, yOffset + 1, p);
+                x += 5;
+            }
+            p.setStrokeWidth(2);
+            for (float length = 1; length <= 10; length +=1 ) {
+                p.setAntiAlias(false);
+                canvas.drawLine(x, yOffset, x + length, yOffset + 1, p);
+                x += 10;
+                p.setAntiAlias(true);
+                canvas.drawLine(x, yOffset, x + length, yOffset + 1, p);
+                x += 10;
+            }
+            canvas.restore();
+
         }
     }
 }