diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java index 104ecaae2..081844690 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewBackgroundDrawable.java @@ -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); } }