diff options
author | Chet Haase <chet@google.com> | 2011-05-02 15:00:16 -0700 |
---|---|---|
committer | Chet Haase <chet@google.com> | 2011-05-02 15:08:38 -0700 |
commit | 99585adeb4167ca357a72eb866f34c1af944f4b9 (patch) | |
tree | ba993a2167fb880c98e29e4d2bd46933e182b1cf | |
parent | 71a0dab1ca962c2827be0221a7fbcc065dc3cbb8 (diff) | |
download | frameworks_base-99585adeb4167ca357a72eb866f34c1af944f4b9.zip frameworks_base-99585adeb4167ca357a72eb866f34c1af944f4b9.tar.gz frameworks_base-99585adeb4167ca357a72eb866f34c1af944f4b9.tar.bz2 |
Line endcaps for AA lines are now antialiased.
Also fixed other minor issues with AA and line rendering.
Change-Id: Icd4638d27c70e2ee0f28b5d9a2b97d8b29e8ac4d
-rw-r--r-- | libs/hwui/Caches.h | 4 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 92 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.h | 3 | ||||
-rw-r--r-- | libs/hwui/ProgramCache.cpp | 63 | ||||
-rw-r--r-- | libs/hwui/ProgramCache.h | 8 | ||||
-rw-r--r-- | libs/hwui/Vertex.h | 19 | ||||
-rw-r--r-- | tests/HwAccelerationTest/src/com/android/test/hwui/Lines2Activity.java | 101 |
7 files changed, 225 insertions, 65 deletions
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 TextureVertex gMeshVertices[] = { 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::setupDrawWithExternalTexture() { } void OpenGLRenderer::setupDrawAALine() { - mDescription.hasWidth = true; + mDescription.isAA = true; } void OpenGLRenderer::setupDrawPoint(float pointSize) { @@ -1121,25 +1121,30 @@ void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) { /** * 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 @@ void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) { } 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 @@ void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) { } } + 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 @@ void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) { } 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 @@ void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) { 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 @@ private: 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 @@ const char* gVS_Header_Attributes = "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 @@ const char* gVS_Header_Uniforms_HasBitmap = "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 @@ const char* gVS_Main_Position = " 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 @@ const char* gFS_Header = "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 @@ const char* gFS_Main_FetchColor = " 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 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description 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 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description 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 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description 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 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti 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 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti 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 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } // 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 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti 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 @@ namespace uirenderer { #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 @@ struct ProgramDescription { bool hasBitmap; bool isBitmapNpot; - bool hasWidth; + bool isAA; bool hasGradient; Gradient gradientType; @@ -156,7 +156,7 @@ struct ProgramDescription { hasAlpha8Texture = false; hasExternalTexture = false; - hasWidth = false; + isAA = false; modulate = false; @@ -243,7 +243,7 @@ struct ProgramDescription { 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 : Vertex { } }; // 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 @@ public class Lines2Activity extends Activity { 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 @@ public class Lines2Activity extends Activity { 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(); + } } } |