Avoid using path for normal border cases

Reviewed By: achen1

Differential Revision: D5111223

fbshipit-source-id: d85da53a9409efc2edb1ca6ecb040a30a35266b8
This commit is contained in:
Seth Kirby 2017-05-31 15:32:46 -07:00 committed by Facebook Github Bot
parent bada25d158
commit 02856938ea

View File

@ -48,6 +48,11 @@ public class ReactViewBackgroundDrawable extends Drawable {
private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
private static final int DEFAULT_BORDER_RGB = 0x00FFFFFF & DEFAULT_BORDER_COLOR;
private static final int DEFAULT_BORDER_ALPHA = (0xFF000000 & DEFAULT_BORDER_COLOR) >>> 24;
// ~0 == 0xFFFFFFFF, all bits set to 1.
private static final int ALL_BITS_SET = ~0;
// 0 == 0x00000000, all bits set to 0.
private static final int ALL_BITS_UNSET = 0;
private static enum BorderStyle {
SOLID,
@ -239,7 +244,7 @@ public class ReactViewBackgroundDrawable extends Drawable {
private void drawRoundedBackgroundWithBorders(Canvas canvas) {
updatePath();
int useColor = ColorUtil.multiplyColorAlpha(mColor, mAlpha);
if ((useColor >>> 24) != 0) { // color is not transparent
if (Color.alpha(useColor) != 0) { // color is not transparent
mPaint.setColor(useColor);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawPath(mPathForBorderRadius, mPaint);
@ -349,9 +354,38 @@ public class ReactViewBackgroundDrawable extends Drawable {
return ReactViewBackgroundDrawable.colorFromAlphaAndRGBComponents(alpha, rgb);
}
/**
* Quickly determine if all the set border colors are equal. Bitwise AND all the set colors
* together, then OR them all together. If the AND and the OR are the same, then the colors
* are compatible, so return this color.
*
* Used to avoid expensive path creation and expensive calls to canvas.drawPath
*
* @return A compatible border color, or zero if the border colors are not compatible.
*/
private static int fastBorderCompatibleColorOrZero(
int borderLeft,
int borderTop,
int borderRight,
int borderBottom,
int colorLeft,
int colorTop,
int colorRight,
int colorBottom) {
int andSmear = (borderLeft > 0 ? colorLeft : ALL_BITS_SET) &
(borderTop > 0 ? colorTop : ALL_BITS_SET) &
(borderRight > 0 ? colorRight : ALL_BITS_SET) &
(borderBottom > 0 ? colorBottom : ALL_BITS_SET);
int orSmear = (borderLeft > 0 ? colorLeft : ALL_BITS_UNSET) |
(borderTop > 0 ? colorTop : ALL_BITS_UNSET) |
(borderRight > 0 ? colorRight : ALL_BITS_UNSET) |
(borderBottom > 0 ? colorBottom : ALL_BITS_UNSET);
return andSmear == orSmear ? andSmear : 0;
}
private void drawRectangularBackgroundWithBorders(Canvas canvas) {
int useColor = ColorUtil.multiplyColorAlpha(mColor, mAlpha);
if ((useColor >>> 24) != 0) { // color is not transparent
if (Color.alpha(useColor) != 0) { // color is not transparent
mPaint.setColor(useColor);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(getBounds(), mPaint);
@ -370,69 +404,106 @@ public class ReactViewBackgroundDrawable extends Drawable {
int colorRight = getBorderColor(Spacing.RIGHT);
int colorBottom = getBorderColor(Spacing.BOTTOM);
int top = bounds.top;
int left = bounds.left;
int width = bounds.width();
int height = bounds.height();
int top = bounds.top;
// If the path drawn previously is of the same color,
// there would be a slight white space between borders
// with anti-alias set to true.
// Therefore we need to disable anti-alias, and
// after drawing is done, we will re-enable it.
// Check for fast path to border drawing.
int fastBorderColor = fastBorderCompatibleColorOrZero(
borderLeft,
borderTop,
borderRight,
borderBottom,
colorLeft,
colorTop,
colorRight,
colorBottom);
if (fastBorderColor != 0) {
if (Color.alpha(fastBorderColor) != 0) {
// Border color is not transparent.
int right = bounds.right;
int bottom = bounds.bottom;
mPaint.setAntiAlias(false);
mPaint.setColor(fastBorderColor);
if (borderLeft > 0) {
int leftInset = left + borderLeft;
canvas.drawRect(left, top, leftInset, bottom - borderBottom, mPaint);
}
if (borderTop > 0) {
int topInset = top + borderTop;
canvas.drawRect(left + borderLeft, top, right, topInset, mPaint);
}
if (borderRight > 0) {
int rightInset = right - borderRight;
canvas.drawRect(rightInset, top + borderTop, right, bottom, mPaint);
}
if (borderBottom > 0) {
int bottomInset = bottom - borderBottom;
canvas.drawRect(left, bottomInset, right - borderRight, bottom, mPaint);
}
}
} else {
if (mPathForBorder == null) {
mPathForBorder = new Path();
}
if (mPathForBorder == null) {
mPathForBorder = new Path();
// If the path drawn previously is of the same color,
// there would be a slight white space between borders
// with anti-alias set to true.
// Therefore we need to disable anti-alias, and
// after drawing is done, we will re-enable it.
mPaint.setAntiAlias(false);
int width = bounds.width();
int height = bounds.height();
if (borderLeft > 0 && colorLeft != Color.TRANSPARENT) {
mPaint.setColor(colorLeft);
mPathForBorder.reset();
mPathForBorder.moveTo(left, top);
mPathForBorder.lineTo(left + borderLeft, top + borderTop);
mPathForBorder.lineTo(left + borderLeft, top + height - borderBottom);
mPathForBorder.lineTo(left, top + height);
mPathForBorder.lineTo(left, top);
canvas.drawPath(mPathForBorder, mPaint);
}
if (borderTop > 0 && colorTop != Color.TRANSPARENT) {
mPaint.setColor(colorTop);
mPathForBorder.reset();
mPathForBorder.moveTo(left, top);
mPathForBorder.lineTo(left + borderLeft, top + borderTop);
mPathForBorder.lineTo(left + width - borderRight, top + borderTop);
mPathForBorder.lineTo(left + width, top);
mPathForBorder.lineTo(left, top);
canvas.drawPath(mPathForBorder, mPaint);
}
if (borderRight > 0 && colorRight != Color.TRANSPARENT) {
mPaint.setColor(colorRight);
mPathForBorder.reset();
mPathForBorder.moveTo(left + width, top);
mPathForBorder.lineTo(left + width, top + height);
mPathForBorder.lineTo(left + width - borderRight, top + height - borderBottom);
mPathForBorder.lineTo(left + width - borderRight, top + borderTop);
mPathForBorder.lineTo(left + width, top);
canvas.drawPath(mPathForBorder, mPaint);
}
if (borderBottom > 0 && colorBottom != Color.TRANSPARENT) {
mPaint.setColor(colorBottom);
mPathForBorder.reset();
mPathForBorder.moveTo(left, top + height);
mPathForBorder.lineTo(left + width, top + height);
mPathForBorder.lineTo(left + width - borderRight, top + height - borderBottom);
mPathForBorder.lineTo(left + borderLeft, top + height - borderBottom);
mPathForBorder.lineTo(left, top + height);
canvas.drawPath(mPathForBorder, mPaint);
}
// re-enable anti alias
mPaint.setAntiAlias(true);
}
if (borderLeft > 0 && colorLeft != Color.TRANSPARENT) {
mPaint.setColor(colorLeft);
mPathForBorder.reset();
mPathForBorder.moveTo(left, top);
mPathForBorder.lineTo(left + borderLeft, top + borderTop);
mPathForBorder.lineTo(left + borderLeft, top + height - borderBottom);
mPathForBorder.lineTo(left, top + height);
mPathForBorder.lineTo(left, top);
canvas.drawPath(mPathForBorder, mPaint);
}
if (borderTop > 0 && colorTop != Color.TRANSPARENT) {
mPaint.setColor(colorTop);
mPathForBorder.reset();
mPathForBorder.moveTo(left, top);
mPathForBorder.lineTo(left + borderLeft, top + borderTop);
mPathForBorder.lineTo(left + width - borderRight, top + borderTop);
mPathForBorder.lineTo(left + width, top);
mPathForBorder.lineTo(left, top);
canvas.drawPath(mPathForBorder, mPaint);
}
if (borderRight > 0 && colorRight != Color.TRANSPARENT) {
mPaint.setColor(colorRight);
mPathForBorder.reset();
mPathForBorder.moveTo(left + width, top);
mPathForBorder.lineTo(left + width, top + height);
mPathForBorder.lineTo(left + width - borderRight, top + height - borderBottom);
mPathForBorder.lineTo(left + width - borderRight, top + borderTop);
mPathForBorder.lineTo(left + width, top);
canvas.drawPath(mPathForBorder, mPaint);
}
if (borderBottom > 0 && colorBottom != Color.TRANSPARENT) {
mPaint.setColor(colorBottom);
mPathForBorder.reset();
mPathForBorder.moveTo(left, top + height);
mPathForBorder.lineTo(left + width, top + height);
mPathForBorder.lineTo(left + width - borderRight, top + height - borderBottom);
mPathForBorder.lineTo(left + borderLeft, top + height - borderBottom);
mPathForBorder.lineTo(left, top + height);
canvas.drawPath(mPathForBorder, mPaint);
}
// re-enable anti alias
mPaint.setAntiAlias(true);
}
}