Merge "DO NOT MERGE Improved anti-aliasing for circular display mask" into cw-f-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
2cfae5a656
@@ -21,6 +21,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
|
||||
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
@@ -85,12 +86,115 @@ class CircularDisplayMask {
|
||||
mSurfaceControl = ctrl;
|
||||
mDrawNeeded = true;
|
||||
mPaint = new Paint();
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
||||
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
|
||||
mScreenOffset = screenOffset;
|
||||
mMaskThickness = maskThickness;
|
||||
}
|
||||
|
||||
static private double distanceFromCenterSquared(double x, double y) {
|
||||
return x*x + y*y;
|
||||
}
|
||||
|
||||
static private double distanceFromCenter(double x, double y) {
|
||||
return Math.sqrt(distanceFromCenterSquared(x, y));
|
||||
}
|
||||
|
||||
static private double verticalLineIntersectsCircle(double x, double radius) {
|
||||
return Math.sqrt(radius*radius - x*x);
|
||||
}
|
||||
|
||||
static private double horizontalLineIntersectsCircle(double y, double radius) {
|
||||
return Math.sqrt(radius*radius - y*y);
|
||||
}
|
||||
|
||||
static private double triangleArea(double width, double height) {
|
||||
return width * height / 2.0;
|
||||
}
|
||||
|
||||
static private double trapezoidArea(double width, double height1, double height2) {
|
||||
return width * (height1 + height2) / 2.0;
|
||||
}
|
||||
|
||||
static private double areaUnderChord(double radius, double chordLength) {
|
||||
double isocelesHeight = Math.sqrt(radius*radius - chordLength * chordLength / 4.0);
|
||||
double areaUnderIsoceles = isocelesHeight * chordLength / 2.0;
|
||||
double halfAngle = Math.asin(chordLength / (2.0 * radius));
|
||||
double areaUnderArc = halfAngle * radius * radius;
|
||||
|
||||
return areaUnderArc - triangleArea(chordLength, isocelesHeight);
|
||||
}
|
||||
|
||||
// Returns the fraction of the pixel at (px, py) covered by
|
||||
// the circle with center (cx, cy) and radius 'radius'
|
||||
static private double calcPixelShading(double cx, double cy, double px,
|
||||
double py, double radius) {
|
||||
// Translate so the center is at the origin
|
||||
px -= cx;
|
||||
py -= cy;
|
||||
|
||||
// Reflect across the axis so the point is in the first quadrant
|
||||
px = Math.abs(px);
|
||||
py = Math.abs(py);
|
||||
|
||||
// One more transformation which simplifies the logic later
|
||||
if (py > px) {
|
||||
double temp;
|
||||
|
||||
temp = px;
|
||||
px = py;
|
||||
py = temp;
|
||||
}
|
||||
|
||||
double left = px - 0.5;
|
||||
double right = px + 0.5;
|
||||
double bottom = py - 0.5;
|
||||
double top = py + 0.5;
|
||||
|
||||
if (distanceFromCenterSquared(left, bottom) > radius*radius) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if (distanceFromCenterSquared(right, top) < radius*radius) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
// Check if only the bottom-left corner of the pixel is inside the circle
|
||||
if (distanceFromCenterSquared(left, top) > radius*radius) {
|
||||
double triangleWidth = horizontalLineIntersectsCircle(bottom, radius) - left;
|
||||
double triangleHeight = verticalLineIntersectsCircle(left, radius) - bottom;
|
||||
double chordLength = distanceFromCenter(triangleWidth, triangleHeight);
|
||||
|
||||
return triangleArea(triangleWidth, triangleHeight)
|
||||
+ areaUnderChord(radius, chordLength);
|
||||
|
||||
}
|
||||
|
||||
// Check if only the top-right corner of the pixel is outside the circle
|
||||
if (distanceFromCenterSquared(right, bottom) < radius*radius) {
|
||||
double triangleWidth = right - horizontalLineIntersectsCircle(top, radius);
|
||||
double triangleHeight = top - verticalLineIntersectsCircle(right, radius);
|
||||
double chordLength = distanceFromCenter(triangleWidth, triangleHeight);
|
||||
|
||||
return 1 - triangleArea(triangleWidth, triangleHeight)
|
||||
+ areaUnderChord(radius, chordLength);
|
||||
}
|
||||
|
||||
// It must be that the top-left and bottom-left corners are inside the circle
|
||||
double trapezoidWidth1 = horizontalLineIntersectsCircle(top, radius) - left;
|
||||
double trapezoidWidth2 = horizontalLineIntersectsCircle(bottom, radius) - left;
|
||||
double chordLength = distanceFromCenter(1, trapezoidWidth2 - trapezoidWidth1);
|
||||
double shading = trapezoidArea(1.0, trapezoidWidth1, trapezoidWidth2)
|
||||
+ areaUnderChord(radius, chordLength);
|
||||
|
||||
// When top >= 0 and bottom <= 0 it's possible for the circle to intersect the pixel 4 times.
|
||||
// If so, remove the area of the section which crosses the right-hand edge.
|
||||
if (top >= 0 && bottom <= 0 && radius > right) {
|
||||
shading -= areaUnderChord(radius, 2 * verticalLineIntersectsCircle(right, radius));
|
||||
}
|
||||
|
||||
return shading;
|
||||
}
|
||||
|
||||
private void drawIfNeeded() {
|
||||
if (!mDrawNeeded || !mVisible || mDimensionsUnequal) {
|
||||
return;
|
||||
@@ -123,11 +227,41 @@ class CircularDisplayMask {
|
||||
break;
|
||||
}
|
||||
|
||||
int circleRadius = mScreenSize.x / 2;
|
||||
c.drawColor(Color.BLACK);
|
||||
|
||||
// The radius is reduced by mMaskThickness to provide an anti aliasing effect on the display edges.
|
||||
c.drawCircle(circleRadius, circleRadius, circleRadius - mMaskThickness, mPaint);
|
||||
int maskWidth = mScreenSize.x - 2*mMaskThickness;
|
||||
int maskHeight;
|
||||
|
||||
// Don't render the whole mask if it is partly offscreen.
|
||||
if (maskWidth > mScreenSize.y) {
|
||||
maskHeight = mScreenSize.y;
|
||||
} else {
|
||||
// To ensure the mask can be properly centered on the canvas the
|
||||
// bitmap dimensions must have the same parity as those of the canvas.
|
||||
maskHeight = mScreenSize.y - ((mScreenSize.y - maskWidth) & ~1);
|
||||
}
|
||||
|
||||
double cx = (maskWidth - 1.0) / 2.0;
|
||||
double cy = (maskHeight - 1.0) / 2.0;
|
||||
double radius = maskWidth / 2.0;
|
||||
int[] pixels = new int[maskWidth * maskHeight];
|
||||
|
||||
for (int py=0; py<maskHeight; py++) {
|
||||
for (int px=0; px<maskWidth; px++) {
|
||||
double shading = calcPixelShading(cx, cy, px, py, radius);
|
||||
pixels[maskWidth*py + px] =
|
||||
Color.argb(255 - (int)Math.round(255.0*shading), 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
Bitmap transparency = Bitmap.createBitmap(pixels, maskWidth, maskHeight,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
|
||||
c.drawBitmap(transparency,
|
||||
(float)mMaskThickness,
|
||||
(float)((mScreenSize.y - maskHeight) / 2),
|
||||
mPaint);
|
||||
|
||||
mSurface.unlockCanvasAndPost(c);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user