Add anti-aliasing for android borders and backgrounds with rounded corners

This commit is contained in:
Panayot Cankov
2016-02-03 14:51:13 +02:00
parent bf96d83cc4
commit e826e99384
2 changed files with 62 additions and 42 deletions

View File

@ -38,6 +38,13 @@
<!-- All --> <!-- All -->
<Button width="40" height="40" text="71" tap="applyTap" tag="margin: 20; background-color: lightgreen; background-image: url('~/pages/test2.png'); background-repeat:repeat-x; background-position: 20% 80%; background-size: 25% 50%; border-radius: 20; border-width: 4; border-color: lightpink;"/> <Button width="40" height="40" text="71" tap="applyTap" tag="margin: 20; background-color: lightgreen; background-image: url('~/pages/test2.png'); background-repeat:repeat-x; background-position: 20% 80%; background-size: 25% 50%; border-radius: 20; border-width: 4; border-color: lightpink;"/>
<Button width="40" height="40" text="72" tap="applyTap" tag="margin: 20; background-color: lightgreen; background-image: url('~/pages/test2.png'); background-repeat:repeat-y; background-position: 80 20; background-size: 50 25; border-radius: 20; border-width: 4; border-color: lightpink; opacity: 0.5;"/> <Button width="40" height="40" text="72" tap="applyTap" tag="margin: 20; background-color: lightgreen; background-image: url('~/pages/test2.png'); background-repeat:repeat-y; background-position: 80 20; background-size: 50 25; border-radius: 20; border-width: 4; border-color: lightpink; opacity: 0.5;"/>
<!-- Antialiasing -->
<Button width="40" height="40" text="73" tap="applyTap" tag="margin: 20; background-color: lightgreen; background-image: url('~/pages/test2.png'); background-repeat:repeat-x; background-position: -15% -15%; background-size: 50% 50%; border-radius: 9; border-width: 3; border-color: black;"/>
<Button width="40" height="40" text="74" tap="applyTap" tag="margin: 20; background-color: lightgreen; background-image: url('~/pages/test2.png'); background-repeat:repeat-y; background-position: -15% -15%; background-size: 50% 50%; border-radius: 9; border-width: 3; border-color: black;"/>
<Button width="40" height="40" text="75" tap="applyTap" tag="margin: 20; background-color: lightgreen; background-image: url('~/pages/test2.png'); background-repeat:repeat; background-position: -15% -15%; background-size: 50% 50%; border-radius: 9; border-width: 3; border-color: black;"/>
<Button width="40" height="40" text="76" tap="applyTap" tag="margin: 20; background-color: lightgreen; background-image: url('~/pages/test2.png'); background-repeat:no-repeat; background-position: -15% -15%; background-size: 50% 50%; border-radius: 9; border-width: 3; border-color: black;"/>
<Button width="40" height="40" text="77" tap="applyTap" tag="margin: 20; background-color: #FF00FF00; background-image: url('~/pages/test2.png'); background-repeat:no-repeat; background-position: -15% -15%; background-size: 50% 50%; border-radius: 20; border-width: 10; border-color: #66FF0000;"/>
</WrapLayout> </WrapLayout>
</GridLayout> </GridLayout>
</Page> </Page>

View File

@ -90,72 +90,85 @@ export module ad {
} }
public draw(canvas: android.graphics.Canvas): void { public draw(canvas: android.graphics.Canvas): void {
var bounds = this.getBounds(); let bounds = this.getBounds();
var boundsF = new android.graphics.RectF(bounds); let borderWidth = this._borderWidth * this._density;
var boundsWidth = bounds.width(); let halfBorderWidth = borderWidth / 2;
var boundsHeight = bounds.height();
var radius = this._cornerRadius * this._density; // We will inset background colors and images so antialiasing will not color pixels outside the border.
var stroke = this._borderWidth * this._density; // If the border is transparent we will backoff less, and we will not backoff more than half a pixel or half the border width.
let normalizedBorderAlpha = android.graphics.Color.alpha(this._borderColor) / 255;
// set clip first let backoffAntialias = Math.min(0.5, halfBorderWidth) * normalizedBorderAlpha;
if (radius > 0) { let outerBoundsF = new android.graphics.RectF(bounds.left + backoffAntialias, bounds.top + backoffAntialias, bounds.right - backoffAntialias, bounds.bottom - backoffAntialias);
var path = new android.graphics.Path();
path.addRoundRect(boundsF, radius, radius, android.graphics.Path.Direction.CW); let outerRadius = Math.max(0, this._cornerRadius * this._density - backoffAntialias);
canvas.clipPath(path);
}
// draw background // draw background
if (this.background.color && this.background.color.android) { if (this.background.color && this.background.color.android) {
let c = this.background.color; let backgroundColorPaint = new android.graphics.Paint();
canvas.drawARGB(c.a, c.r, c.g, c.b); backgroundColorPaint.setStyle(android.graphics.Paint.Style.FILL);
backgroundColorPaint.setColor(this.background.color.android);
backgroundColorPaint.setAntiAlias(true);
canvas.drawRoundRect(outerBoundsF, outerRadius, outerRadius, backgroundColorPaint);
} }
// draw image // draw image
if (this.background.image) { if (this.background.image) {
let bitmap = this.background.image.android; let bitmap = this.background.image.android;
let params = this.background.getDrawParams(boundsWidth, boundsHeight); let params = this.background.getDrawParams(bounds.width(), bounds.height());
var matrix = new android.graphics.Matrix(); let transform = new android.graphics.Matrix();
if (params.sizeX > 0 && params.sizeY > 0) { if (params.sizeX > 0 && params.sizeY > 0) {
var scaleX = params.sizeX / bitmap.getWidth(); let scaleX = params.sizeX / bitmap.getWidth();
var scaleY = params.sizeY / bitmap.getHeight(); let scaleY = params.sizeY / bitmap.getHeight();
matrix.setScale(scaleX, scaleY, 0, 0); transform.setScale(scaleX, scaleY, 0, 0);
} } else {
else {
params.sizeX = bitmap.getWidth(); params.sizeX = bitmap.getWidth();
params.sizeY = bitmap.getHeight(); params.sizeY = bitmap.getHeight();
} }
matrix.postTranslate(params.posX, params.posY); transform.postTranslate(params.posX - backoffAntialias, params.posY - backoffAntialias);
if (!params.repeatX && !params.repeatY) { let shader = new android.graphics.BitmapShader(bitmap, android.graphics.Shader.TileMode.REPEAT, android.graphics.Shader.TileMode.REPEAT);
canvas.drawBitmap(bitmap, matrix, undefined); shader.setLocalMatrix(transform);
}
else {
var shader = new android.graphics.BitmapShader(bitmap, android.graphics.Shader.TileMode.REPEAT, android.graphics.Shader.TileMode.REPEAT);
shader.setLocalMatrix(matrix);
var paint = new android.graphics.Paint();
paint.setShader(shader);
var w = params.repeatX ? bounds.width() : params.sizeX; let backgroundImagePaint = new android.graphics.Paint();
var h = params.repeatY ? bounds.height() : params.sizeY; backgroundImagePaint.setShader(shader);
params.posX = params.repeatX ? 0 : params.posX; let imageWidth = params.repeatX ? bounds.width() : params.sizeX;
params.posY = params.repeatY ? 0 : params.posY; let imageHeight = params.repeatY ? bounds.height() : params.sizeY;
params.posX = params.repeatX ? 0 : params.posX;
params.posY = params.repeatY ? 0 : params.posY;
canvas.drawRect(params.posX, params.posY, params.posX + w, params.posY + h, paint); let supportsPathOp = android.os.Build.VERSION.SDK_INT >= 19;
if (supportsPathOp) {
// Path.Op can be used in API level 19+ to achieve the perfect geometry.
let backgroundPath = new android.graphics.Path();
backgroundPath.addRoundRect(outerBoundsF, outerRadius, outerRadius, android.graphics.Path.Direction.CCW);
let backgroundNoRepeatPath = new android.graphics.Path();
backgroundNoRepeatPath.addRect(params.posX, params.posY, params.posX + imageWidth, params.posY + imageHeight, android.graphics.Path.Direction.CCW);
(<any>backgroundPath).op(backgroundNoRepeatPath, (<any>android).graphics.Path.Op.INTERSECT);
canvas.drawPath(backgroundPath, backgroundImagePaint);
} else {
// Clipping here will not be antialiased but at least it won't shine through the rounded corners.
canvas.save();
canvas.clipRect(params.posX, params.posY, params.posX + imageWidth, params.posY + imageHeight);
canvas.drawRoundRect(outerBoundsF, outerRadius, outerRadius, backgroundImagePaint);
canvas.restore();
} }
} }
// draw border (topmost) // draw border
if (stroke > 0 && this._borderColor && this._borderColor) { if (borderWidth > 0 && this._borderColor) {
let middleRadius = Math.max(0, outerRadius - halfBorderWidth);
let middleBoundsF = new android.graphics.RectF(bounds.left + halfBorderWidth, bounds.top + halfBorderWidth, bounds.right - halfBorderWidth, bounds.bottom - halfBorderWidth);
let borderPaint = new android.graphics.Paint(); let borderPaint = new android.graphics.Paint();
borderPaint.setStyle(android.graphics.Paint.Style.STROKE); borderPaint.setStyle(android.graphics.Paint.Style.STROKE);
borderPaint.setColor(this._borderColor); borderPaint.setColor(this._borderColor);
borderPaint.setAntiAlias(true);
borderPaint.setStrokeWidth(borderWidth);
// Note: Double the stroke as the outer part will be clipped. canvas.drawRoundRect(middleBoundsF, middleRadius, middleRadius, borderPaint);
borderPaint.setStrokeWidth(stroke * 2);
canvas.drawRoundRect(boundsF, radius, radius, borderPaint)
} }
} }
} }