Avoid using path for normal border cases

Reviewed By: ahmedre

Differential Revision: D5111224

fbshipit-source-id: c188f83339ed011272f80b4fac35f47f8d72d30b
This commit is contained in:
Seth Kirby 2017-05-31 15:32:50 -07:00 committed by Facebook Github Bot
parent 2143df8cb6
commit 5db26380dd
1 changed files with 127 additions and 86 deletions

View File

@ -34,6 +34,11 @@ import com.facebook.react.uimanager.Spacing;
private static final int BORDER_BOTTOM_COLOR_SET = 1 << 4; private static final int BORDER_BOTTOM_COLOR_SET = 1 << 4;
private static final int BORDER_PATH_EFFECT_DIRTY = 1 << 5; private static final int BORDER_PATH_EFFECT_DIRTY = 1 << 5;
// ~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 float mBorderLeftWidth; private float mBorderLeftWidth;
private float mBorderTopWidth; private float mBorderTopWidth;
private float mBorderRightWidth; private float mBorderRightWidth;
@ -212,14 +217,32 @@ import com.facebook.react.uimanager.Spacing;
} }
/** /**
* @return true when border colors differs where two border sides meet (e.g. right and top border * Quickly determine if all the set border colors are equal. Bitwise AND all the set colors
* colors differ) * 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 boolean isBorderColorDifferentAtIntersectionPoints() { private static int fastBorderCompatibleColorOrZero(
return isFlagSet(BORDER_TOP_COLOR_SET) || float borderLeft,
isFlagSet(BORDER_BOTTOM_COLOR_SET) || float borderTop,
isFlagSet(BORDER_LEFT_COLOR_SET) || float borderRight,
isFlagSet(BORDER_RIGHT_COLOR_SET); float 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 drawRectangularBorders(Canvas canvas) { private void drawRectangularBorders(Canvas canvas) {
@ -246,12 +269,53 @@ import com.facebook.react.uimanager.Spacing;
float rightInset = right - borderRight; float rightInset = right - borderRight;
int rightColor = resolveBorderColor(BORDER_RIGHT_COLOR_SET, mBorderRightColor, defaultColor); int rightColor = resolveBorderColor(BORDER_RIGHT_COLOR_SET, mBorderRightColor, defaultColor);
boolean isDrawPathRequired = isBorderColorDifferentAtIntersectionPoints(); // Check for fast path to border drawing.
if (isDrawPathRequired && mPathForBorder == null) { int fastBorderColor = fastBorderCompatibleColorOrZero(
borderLeft,
borderTop,
borderRight,
borderBottom,
leftColor,
topColor,
rightColor,
bottomColor);
if (fastBorderColor != 0) {
// Fast border color draw.
if (Color.alpha(fastBorderColor) != 0) {
// Border color is not transparent.
// Draw center.
if (Color.alpha(mBackgroundColor) != 0) {
PAINT.setColor(mBackgroundColor);
if (Color.alpha(fastBorderColor) == 255) {
// The border will draw over the edges, so only draw the inset background.
canvas.drawRect(leftInset, topInset, rightInset, bottomInset, PAINT);
} else {
// The border is opaque, so we have to draw the entire background color.
canvas.drawRect(left, top, right, bottom, PAINT);
}
}
PAINT.setColor(fastBorderColor);
if (borderLeft > 0) {
canvas.drawRect(left, top, leftInset, bottom - borderBottom, PAINT);
}
if (borderTop > 0) {
canvas.drawRect(left + borderLeft, top, right, topInset, PAINT);
}
if (borderRight > 0) {
canvas.drawRect(rightInset, top + borderTop, right, bottom, PAINT);
}
if (borderBottom > 0) {
canvas.drawRect(left, bottomInset, right - borderRight, bottom, PAINT);
}
}
} else {
if (mPathForBorder == null) {
mPathForBorder = new Path(); mPathForBorder = new Path();
} }
// Draw center. The border might be opaque, so we need to draw this. // Draw center. Any of the borders might be opaque or transparent, so we need to draw this.
if (Color.alpha(mBackgroundColor) != 0) { if (Color.alpha(mBackgroundColor) != 0) {
PAINT.setColor(mBackgroundColor); PAINT.setColor(mBackgroundColor);
canvas.drawRect(left, top, right, bottom, PAINT); canvas.drawRect(left, top, right, bottom, PAINT);
@ -260,9 +324,8 @@ import com.facebook.react.uimanager.Spacing;
// Draw top. // Draw top.
if (borderTop != 0 && Color.alpha(topColor) != 0) { if (borderTop != 0 && Color.alpha(topColor) != 0) {
PAINT.setColor(topColor); PAINT.setColor(topColor);
if (isDrawPathRequired) {
updatePathForTopBorder( updatePathForTopBorder(
mPathForBorder,
top, top,
topInset, topInset,
left, left,
@ -270,17 +333,13 @@ import com.facebook.react.uimanager.Spacing;
right, right,
rightInset); rightInset);
canvas.drawPath(mPathForBorder, PAINT); canvas.drawPath(mPathForBorder, PAINT);
} else {
canvas.drawRect(left, top, right, topInset, PAINT);
}
} }
// Draw bottom. // Draw bottom.
if (borderBottom != 0 && Color.alpha(bottomColor) != 0) { if (borderBottom != 0 && Color.alpha(bottomColor) != 0) {
PAINT.setColor(bottomColor); PAINT.setColor(bottomColor);
if (isDrawPathRequired) {
updatePathForBottomBorder( updatePathForBottomBorder(
mPathForBorder,
bottom, bottom,
bottomInset, bottomInset,
left, left,
@ -288,17 +347,13 @@ import com.facebook.react.uimanager.Spacing;
right, right,
rightInset); rightInset);
canvas.drawPath(mPathForBorder, PAINT); canvas.drawPath(mPathForBorder, PAINT);
} else {
canvas.drawRect(left, bottomInset, right, bottom, PAINT);
}
} }
// Draw left. // Draw left.
if (borderLeft != 0 && Color.alpha(leftColor) != 0) { if (borderLeft != 0 && Color.alpha(leftColor) != 0) {
PAINT.setColor(leftColor); PAINT.setColor(leftColor);
if (isDrawPathRequired) {
updatePathForLeftBorder( updatePathForLeftBorder(
mPathForBorder,
top, top,
topInset, topInset,
bottom, bottom,
@ -306,17 +361,13 @@ import com.facebook.react.uimanager.Spacing;
left, left,
leftInset); leftInset);
canvas.drawPath(mPathForBorder, PAINT); canvas.drawPath(mPathForBorder, PAINT);
} else {
canvas.drawRect(left, topInset, leftInset, bottomInset, PAINT);
}
} }
// Draw right. // Draw right.
if (borderRight != 0 && Color.alpha(rightColor) != 0) { if (borderRight != 0 && Color.alpha(rightColor) != 0) {
PAINT.setColor(rightColor); PAINT.setColor(rightColor);
if (isDrawPathRequired) {
updatePathForRightBorder( updatePathForRightBorder(
mPathForBorder,
top, top,
topInset, topInset,
bottom, bottom,
@ -324,82 +375,72 @@ import com.facebook.react.uimanager.Spacing;
right, right,
rightInset); rightInset);
canvas.drawPath(mPathForBorder, PAINT); canvas.drawPath(mPathForBorder, PAINT);
} else {
canvas.drawRect(rightInset, topInset, right, bottomInset, PAINT);
} }
} }
} }
private void updatePathForTopBorder( private static void updatePathForTopBorder(
Path path,
float top, float top,
float topInset, float topInset,
float left, float left,
float leftInset, float leftInset,
float right, float right,
float rightInset) { float rightInset) {
if (mPathForBorder == null) { path.reset();
mPathForBorder = new Path(); path.moveTo(left, top);
} path.lineTo(leftInset, topInset);
mPathForBorder.reset(); path.lineTo(rightInset, topInset);
mPathForBorder.moveTo(left, top); path.lineTo(right, top);
mPathForBorder.lineTo(leftInset, topInset); path.lineTo(left, top);
mPathForBorder.lineTo(rightInset, topInset);
mPathForBorder.lineTo(right, top);
mPathForBorder.lineTo(left, top);
} }
private void updatePathForBottomBorder( private static void updatePathForBottomBorder(
Path path,
float bottom, float bottom,
float bottomInset, float bottomInset,
float left, float left,
float leftInset, float leftInset,
float right, float right,
float rightInset) { float rightInset) {
if (mPathForBorder == null) { path.reset();
mPathForBorder = new Path(); path.moveTo(left, bottom);
} path.lineTo(right, bottom);
mPathForBorder.reset(); path.lineTo(rightInset, bottomInset);
mPathForBorder.moveTo(left, bottom); path.lineTo(leftInset, bottomInset);
mPathForBorder.lineTo(right, bottom); path.lineTo(left, bottom);
mPathForBorder.lineTo(rightInset, bottomInset);
mPathForBorder.lineTo(leftInset, bottomInset);
mPathForBorder.lineTo(left, bottom);
} }
private void updatePathForLeftBorder( private static void updatePathForLeftBorder(
Path path,
float top, float top,
float topInset, float topInset,
float bottom, float bottom,
float bottomInset, float bottomInset,
float left, float left,
float leftInset) { float leftInset) {
if (mPathForBorder == null) { path.reset();
mPathForBorder = new Path(); path.moveTo(left, top);
} path.lineTo(leftInset, topInset);
mPathForBorder.reset(); path.lineTo(leftInset, bottomInset);
mPathForBorder.moveTo(left, top); path.lineTo(left, bottom);
mPathForBorder.lineTo(leftInset, topInset); path.lineTo(left, top);
mPathForBorder.lineTo(leftInset, bottomInset);
mPathForBorder.lineTo(left, bottom);
mPathForBorder.lineTo(left, top);
} }
private void updatePathForRightBorder( private static void updatePathForRightBorder(
Path path,
float top, float top,
float topInset, float topInset,
float bottom, float bottom,
float bottomInset, float bottomInset,
float right, float right,
float rightInset) { float rightInset) {
if (mPathForBorder == null) { path.reset();
mPathForBorder = new Path(); path.moveTo(right, top);
} path.lineTo(right, bottom);
mPathForBorder.reset(); path.lineTo(rightInset, bottomInset);
mPathForBorder.moveTo(right, top); path.lineTo(rightInset, topInset);
mPathForBorder.lineTo(right, bottom); path.lineTo(right, top);
mPathForBorder.lineTo(rightInset, bottomInset);
mPathForBorder.lineTo(rightInset, topInset);
mPathForBorder.lineTo(right, top);
} }
private int resolveBorderColor(int flag, int color, int defaultColor) { private int resolveBorderColor(int flag, int color, int defaultColor) {