Loading services/core/java/com/android/server/wm/CircularDisplayMask.java +139 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); } Loading Loading
services/core/java/com/android/server/wm/CircularDisplayMask.java +139 −5 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); } Loading