Antialiasing for rectangles
Change-Id: I7ca6931606541ddd504bd5db7f8dc04b9cde8cd9
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 80a7bed..5a86fe3 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1458,6 +1458,79 @@
}
/**
+ * This function uses a similar approach to that of AA lines in the drawLines() function.
+ * We expand the rectangle by a half pixel in screen space on all sides, and use a fragment
+ * shader to compute the translucency of the color, determined by whether a given pixel is
+ * within that boundary region and how far into the region it is.
+ */
+void OpenGLRenderer::drawAARect(float left, float top, float right, float bottom,
+ int color, SkXfermode::Mode mode)
+{
+ float inverseScaleX = 1.0f;
+ float inverseScaleY = 1.0f;
+ // The quad that we use needs to account for scaling.
+ if (!mSnapshot->transform->isPureTranslate()) {
+ Matrix4 *mat = mSnapshot->transform;
+ float m00 = mat->data[Matrix4::kScaleX];
+ float m01 = mat->data[Matrix4::kSkewY];
+ float m02 = mat->data[2];
+ float m10 = mat->data[Matrix4::kSkewX];
+ float m11 = mat->data[Matrix4::kScaleX];
+ float m12 = mat->data[6];
+ float scaleX = sqrt(m00 * m00 + m01 * m01);
+ float scaleY = sqrt(m10 * m10 + m11 * m11);
+ inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0;
+ inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0;
+ }
+
+ setupDraw();
+ setupDrawAALine();
+ setupDrawColor(color);
+ setupDrawColorFilter();
+ setupDrawShader();
+ setupDrawBlending(true, mode);
+ setupDrawProgram();
+ setupDrawModelViewIdentity(true);
+ setupDrawColorUniforms();
+ setupDrawColorFilterUniforms();
+ setupDrawShaderIdentityUniforms();
+
+ AAVertex rects[4];
+ AAVertex* aaVertices = &rects[0];
+ void* widthCoords = ((GLbyte*) aaVertices) + gVertexAAWidthOffset;
+ void* lengthCoords = ((GLbyte*) aaVertices) + gVertexAALengthOffset;
+
+ float boundarySizeX = .5 * inverseScaleX;
+ float boundarySizeY = .5 * inverseScaleY;
+
+ // Adjust the rect by the AA boundary padding
+ left -= boundarySizeX;
+ right += boundarySizeX;
+ top -= boundarySizeY;
+ bottom += boundarySizeY;
+
+ float width = right - left;
+ float height = bottom - top;
+
+ float boundaryWidthProportion = (width != 0) ? (2 * boundarySizeX) / width : 0;
+ float boundaryHeightProportion = (height != 0) ? (2 * boundarySizeY) / height : 0;
+ setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords, boundaryWidthProportion);
+ int boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength");
+ int inverseBoundaryLengthSlot = mCaches.currentProgram->getUniform("inverseBoundaryLength");
+ glUniform1f(boundaryLengthSlot, boundaryHeightProportion);
+ glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryHeightProportion));
+
+ if (!quickReject(left, top, right, bottom)) {
+ AAVertex::set(aaVertices++, left, bottom, 1, 1);
+ AAVertex::set(aaVertices++, left, top, 1, 0);
+ AAVertex::set(aaVertices++, right, bottom, 0, 1);
+ AAVertex::set(aaVertices++, right, top, 0, 0);
+ dirtyLayer(left, top, right, bottom, *mSnapshot->transform);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+ }
+}
+
+/**
* We draw lines as quads (tristrips). Using GL_LINES can be difficult because the rasterization
* rules for those lines produces some unexpected results, and may vary between hardware devices.
* The basics of lines-as-quads is easy; we simply find the normal to the line and position the
@@ -1848,7 +1921,11 @@
}
int color = p->getColor();
- drawColorRect(left, top, right, bottom, color, mode);
+ if (p->isAntiAlias()) {
+ drawAARect(left, top, right, bottom, color, mode);
+ } else {
+ drawColorRect(left, top, right, bottom, color, mode);
+ }
}
void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 271e4b1..ffacbfc 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -285,6 +285,9 @@
void drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint);
+ void drawAARect(float left, float top, float right, float bottom,
+ int color, SkXfermode::Mode mode);
+
/**
* Draws a textured rectangle with the specified texture. The specified coordinates
* are transformed by the current snapshot's transform matrix.
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 6285880..c1aa215 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -93,6 +93,15 @@
</activity>
<activity
+ android:name="ColoredRectsActivity"
+ android:label="_Rects">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
android:name="SimplePatchActivity"
android:label="_SimplePatch"
android:theme="@android:style/Theme.Translucent.NoTitleBar">
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java
new file mode 100644
index 0000000..e8cdbc3
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColoredRectsActivity.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.animation.ObjectAnimator;
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class ColoredRectsActivity extends Activity {
+ private ObjectAnimator mAnimator;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ getWindow().setBackgroundDrawable(new ColorDrawable(0xff000000));
+ FrameLayout frame = new FrameLayout(this);
+ final RectsView gpuView = new RectsView(this, 0, Color.GREEN);
+ frame.addView(gpuView);
+ final RectsView swView = new RectsView(this, 400, Color.RED);
+ swView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ frame.addView(swView);
+ final RectsView hwBothView = new RectsView(this, 850, Color.GREEN);
+ // Don't actually need to render to a hw layer, but it's a good sanity-check that
+ // we're rendering to/from layers correctly
+ hwBothView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ frame.addView(hwBothView);
+ final RectsView swBothView = new RectsView(this, 854, Color.RED);
+ swBothView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ frame.addView(swBothView);
+ setContentView(frame);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ }
+
+ public static class RectsView extends View {
+
+ private float mOffset;
+ private int mColor;
+
+ public RectsView(Context c, float offset, int color) {
+ super(c);
+ mOffset = offset;
+ mColor = color;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ Paint p = new Paint();
+ p.setColor(mColor);
+ float yOffset = 10;
+
+ for (int i = 0; i < 2; ++i) {
+ canvas.save();
+ canvas.translate(mOffset, yOffset);
+ canvas.drawRect(0, 0, 20, 10, p);
+ canvas.drawRect(35, 0, 45, 20, p);
+ canvas.translate(0, -yOffset);
+ canvas.scale(2, 2);
+ canvas.translate(60, yOffset/2);
+ canvas.drawRect(0, 0, 20, 10, p);
+ canvas.translate(15, 0);
+ canvas.drawRect(35, 0, 45, 20, p);
+ canvas.restore();
+
+ yOffset += 100;
+
+ canvas.save();
+ canvas.save();
+ canvas.translate(mOffset + 10, yOffset);
+ canvas.rotate(45);
+ canvas.drawRect(0, 0, 20, 10, p);
+ canvas.restore();
+ canvas.save();
+ canvas.translate(mOffset + 70, yOffset);
+ canvas.rotate(5);
+ canvas.drawRect(0, 0, 20, 10, p);
+ canvas.restore();
+ canvas.save();
+ canvas.translate(mOffset + 140, yOffset);
+ canvas.scale(2, 2);
+ canvas.rotate(5);
+ canvas.drawRect(0, 0, 20, 10, p);
+ canvas.restore();
+ canvas.save();
+ canvas.translate(mOffset + 210, yOffset);
+ canvas.scale(2, 2);
+ canvas.rotate(45);
+ canvas.drawRect(0, 0, 20, 10, p);
+ canvas.restore();
+ canvas.restore();
+
+ yOffset += 100;
+
+ p.setAntiAlias(true);
+ }
+ }
+ }
+}