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_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 mBorderTopWidth;
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
* colors differ)
* 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 boolean isBorderColorDifferentAtIntersectionPoints() {
return isFlagSet(BORDER_TOP_COLOR_SET) ||
isFlagSet(BORDER_BOTTOM_COLOR_SET) ||
isFlagSet(BORDER_LEFT_COLOR_SET) ||
isFlagSet(BORDER_RIGHT_COLOR_SET);
private static int fastBorderCompatibleColorOrZero(
float borderLeft,
float borderTop,
float borderRight,
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) {
@ -246,12 +269,53 @@ import com.facebook.react.uimanager.Spacing;
float rightInset = right - borderRight;
int rightColor = resolveBorderColor(BORDER_RIGHT_COLOR_SET, mBorderRightColor, defaultColor);
boolean isDrawPathRequired = isBorderColorDifferentAtIntersectionPoints();
if (isDrawPathRequired && mPathForBorder == null) {
// Check for fast path to border drawing.
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();
}
// 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) {
PAINT.setColor(mBackgroundColor);
canvas.drawRect(left, top, right, bottom, PAINT);
@ -260,9 +324,8 @@ import com.facebook.react.uimanager.Spacing;
// Draw top.
if (borderTop != 0 && Color.alpha(topColor) != 0) {
PAINT.setColor(topColor);
if (isDrawPathRequired) {
updatePathForTopBorder(
mPathForBorder,
top,
topInset,
left,
@ -270,17 +333,13 @@ import com.facebook.react.uimanager.Spacing;
right,
rightInset);
canvas.drawPath(mPathForBorder, PAINT);
} else {
canvas.drawRect(left, top, right, topInset, PAINT);
}
}
// Draw bottom.
if (borderBottom != 0 && Color.alpha(bottomColor) != 0) {
PAINT.setColor(bottomColor);
if (isDrawPathRequired) {
updatePathForBottomBorder(
mPathForBorder,
bottom,
bottomInset,
left,
@ -288,17 +347,13 @@ import com.facebook.react.uimanager.Spacing;
right,
rightInset);
canvas.drawPath(mPathForBorder, PAINT);
} else {
canvas.drawRect(left, bottomInset, right, bottom, PAINT);
}
}
// Draw left.
if (borderLeft != 0 && Color.alpha(leftColor) != 0) {
PAINT.setColor(leftColor);
if (isDrawPathRequired) {
updatePathForLeftBorder(
mPathForBorder,
top,
topInset,
bottom,
@ -306,17 +361,13 @@ import com.facebook.react.uimanager.Spacing;
left,
leftInset);
canvas.drawPath(mPathForBorder, PAINT);
} else {
canvas.drawRect(left, topInset, leftInset, bottomInset, PAINT);
}
}
// Draw right.
if (borderRight != 0 && Color.alpha(rightColor) != 0) {
PAINT.setColor(rightColor);
if (isDrawPathRequired) {
updatePathForRightBorder(
mPathForBorder,
top,
topInset,
bottom,
@ -324,82 +375,72 @@ import com.facebook.react.uimanager.Spacing;
right,
rightInset);
canvas.drawPath(mPathForBorder, PAINT);
} else {
canvas.drawRect(rightInset, topInset, right, bottomInset, PAINT);
}
}
}
private void updatePathForTopBorder(
private static void updatePathForTopBorder(
Path path,
float top,
float topInset,
float left,
float leftInset,
float right,
float rightInset) {
if (mPathForBorder == null) {
mPathForBorder = new Path();
}
mPathForBorder.reset();
mPathForBorder.moveTo(left, top);
mPathForBorder.lineTo(leftInset, topInset);
mPathForBorder.lineTo(rightInset, topInset);
mPathForBorder.lineTo(right, top);
mPathForBorder.lineTo(left, top);
path.reset();
path.moveTo(left, top);
path.lineTo(leftInset, topInset);
path.lineTo(rightInset, topInset);
path.lineTo(right, top);
path.lineTo(left, top);
}
private void updatePathForBottomBorder(
private static void updatePathForBottomBorder(
Path path,
float bottom,
float bottomInset,
float left,
float leftInset,
float right,
float rightInset) {
if (mPathForBorder == null) {
mPathForBorder = new Path();
}
mPathForBorder.reset();
mPathForBorder.moveTo(left, bottom);
mPathForBorder.lineTo(right, bottom);
mPathForBorder.lineTo(rightInset, bottomInset);
mPathForBorder.lineTo(leftInset, bottomInset);
mPathForBorder.lineTo(left, bottom);
path.reset();
path.moveTo(left, bottom);
path.lineTo(right, bottom);
path.lineTo(rightInset, bottomInset);
path.lineTo(leftInset, bottomInset);
path.lineTo(left, bottom);
}
private void updatePathForLeftBorder(
private static void updatePathForLeftBorder(
Path path,
float top,
float topInset,
float bottom,
float bottomInset,
float left,
float leftInset) {
if (mPathForBorder == null) {
mPathForBorder = new Path();
}
mPathForBorder.reset();
mPathForBorder.moveTo(left, top);
mPathForBorder.lineTo(leftInset, topInset);
mPathForBorder.lineTo(leftInset, bottomInset);
mPathForBorder.lineTo(left, bottom);
mPathForBorder.lineTo(left, top);
path.reset();
path.moveTo(left, top);
path.lineTo(leftInset, topInset);
path.lineTo(leftInset, bottomInset);
path.lineTo(left, bottom);
path.lineTo(left, top);
}
private void updatePathForRightBorder(
private static void updatePathForRightBorder(
Path path,
float top,
float topInset,
float bottom,
float bottomInset,
float right,
float rightInset) {
if (mPathForBorder == null) {
mPathForBorder = new Path();
}
mPathForBorder.reset();
mPathForBorder.moveTo(right, top);
mPathForBorder.lineTo(right, bottom);
mPathForBorder.lineTo(rightInset, bottomInset);
mPathForBorder.lineTo(rightInset, topInset);
mPathForBorder.lineTo(right, top);
path.reset();
path.moveTo(right, top);
path.lineTo(right, bottom);
path.lineTo(rightInset, bottomInset);
path.lineTo(rightInset, topInset);
path.lineTo(right, top);
}
private int resolveBorderColor(int flag, int color, int defaultColor) {