Fix border-rendering in APIs < 18

Summary:
`Canvas.clipPath` isn't supported with hardware acceleration in APIs below 18. The rounded border rendering logic for Android relies on this method. Therefore, rounded borders do not render correctly on such devices.

**Screenshot of Nexus 5 running API 17 (Before these changes):**
https://pxl.cl/9rsf

**The fix**: If the API version is less than 18 and we're rendering rounded borders, I disable hardware acceleration. Otherwise, I enable it. I'm going to check to see if this has perf regressions by running a CT-Scan.

With this change, rounded borders render correctly on Android devices running versions of Android between Honeycomb to JellyBean MR2.

**Screenshot of Nexus 5 running API 17 (After these changes):**
https://pxl.cl/9rrk

Reviewed By: xiphirx

Differential Revision: D6153087

fbshipit-source-id: 16e35be096051ac817c8b8bcdd132ecff3b4b167
This commit is contained in:
Ramanpreet Nara 2017-11-21 11:07:36 -08:00 коммит произвёл Facebook Github Bot
Родитель 60828566a7
Коммит 5aa1fb3ff3
2 изменённых файлов: 42 добавлений и 5 удалений

Просмотреть файл

@ -129,16 +129,29 @@ public class ReactViewBackgroundDrawable extends Drawable {
@Override @Override
public void draw(Canvas canvas) { public void draw(Canvas canvas) {
updatePathEffect(); updatePathEffect();
boolean roundedBorders = mBorderCornerRadii != null || if (!hasRoundedBorders()) {
(!YogaConstants.isUndefined(mBorderRadius) && mBorderRadius > 0);
if (!roundedBorders) {
drawRectangularBackgroundWithBorders(canvas); drawRectangularBackgroundWithBorders(canvas);
} else { } else {
drawRoundedBackgroundWithBorders(canvas); drawRoundedBackgroundWithBorders(canvas);
} }
} }
public boolean hasRoundedBorders() {
if (!YogaConstants.isUndefined(mBorderRadius) && mBorderRadius > 0) {
return true;
}
if (mBorderCornerRadii != null) {
for (final float borderRadii : mBorderCornerRadii) {
if (!YogaConstants.isUndefined(borderRadii) && borderRadii > 0) {
return true;
}
}
}
return false;
}
@Override @Override
protected void onBoundsChange(Rect bounds) { protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds); super.onBoundsChange(bounds);
@ -620,23 +633,29 @@ public class ReactViewBackgroundDrawable extends Drawable {
* border of V will render inside O. * border of V will render inside O.
* *
* <p>Let BorderWidth = (borderTop, borderLeft, borderBottom, borderRight). * <p>Let BorderWidth = (borderTop, borderLeft, borderBottom, borderRight).
*
* <p>Let I (for inner) = O - BorderWidth. * <p>Let I (for inner) = O - BorderWidth.
* *
* <p>Then, remembering that O and I are rectangles and that I is inside O, O - I gives us the * <p>Then, remembering that O and I are rectangles and that I is inside O, O - I gives us the
* border of V. Therefore, we can use canvas.clipPath to draw V's border. * border of V. Therefore, we can use canvas.clipPath to draw V's border.
* *
* <p>canvas.clipPath(O, Region.OP.INTERSECT); * <p>canvas.clipPath(O, Region.OP.INTERSECT);
*
* <p>canvas.clipPath(I, Region.OP.DIFFERENCE); * <p>canvas.clipPath(I, Region.OP.DIFFERENCE);
*
* <p>canvas.drawRect(O, paint); * <p>canvas.drawRect(O, paint);
* *
* <p>This lets us draw non-rounded single-color borders. * <p>This lets us draw non-rounded single-color borders.
* *
* <p>To extend this algorithm to rounded single-color borders, we: * <p>To extend this algorithm to rounded single-color borders, we:
*
* <p>1. Curve the corners of O by the (border radii of V) using Path#addRoundRect. * <p>1. Curve the corners of O by the (border radii of V) using Path#addRoundRect.
*
* <p>2. Curve the corners of I by (border radii of V - border widths of V) using * <p>2. Curve the corners of I by (border radii of V - border widths of V) using
* Path#addRoundRect. * Path#addRoundRect.
* *
* <p>Let O' = curve(O, border radii of V). * <p>Let O' = curve(O, border radii of V).
*
* <p>Let I' = curve(I, border radii of V - border widths of V) * <p>Let I' = curve(I, border radii of V - border widths of V)
* *
* <p>The rationale behind this decision is the (first sentence of the) following section in the * <p>The rationale behind this decision is the (first sentence of the) following section in the
@ -647,7 +666,9 @@ public class ReactViewBackgroundDrawable extends Drawable {
* render curved single-color borders: * render curved single-color borders:
* *
* <p>canvas.clipPath(O, Region.OP.INTERSECT); * <p>canvas.clipPath(O, Region.OP.INTERSECT);
*
* <p>canvas.clipPath(I, Region.OP.DIFFERENCE); * <p>canvas.clipPath(I, Region.OP.DIFFERENCE);
*
* <p>canvas.drawRect(O, paint); * <p>canvas.drawRect(O, paint);
* *
* <p>To extend this algorithm to rendering multi-colored rounded borders, we render each side * <p>To extend this algorithm to rendering multi-colored rounded borders, we render each side
@ -655,8 +676,11 @@ public class ReactViewBackgroundDrawable extends Drawable {
* border radii are 0. Then, the four quadrilaterals would be: * border radii are 0. Then, the four quadrilaterals would be:
* *
* <p>Left: (O.left, O.top), (I.left, I.top), (I.left, I.bottom), (O.left, O.bottom) * <p>Left: (O.left, O.top), (I.left, I.top), (I.left, I.bottom), (O.left, O.bottom)
*
* <p>Top: (O.left, O.top), (I.left, I.top), (I.right, I.top), (O.right, O.top) * <p>Top: (O.left, O.top), (I.left, I.top), (I.right, I.top), (O.right, O.top)
*
* <p>Right: (O.right, O.top), (I.right, I.top), (I.right, I.bottom), (O.right, O.bottom) * <p>Right: (O.right, O.top), (I.right, I.top), (I.right, I.bottom), (O.right, O.bottom)
*
* <p>Bottom: (O.right, O.bottom), (I.right, I.bottom), (I.left, I.bottom), (O.left, O.bottom) * <p>Bottom: (O.right, O.bottom), (I.right, I.bottom), (I.left, I.bottom), (O.left, O.bottom)
* *
* <p>Now, lets consider what happens when we render a rounded border (radii != 0). For the sake * <p>Now, lets consider what happens when we render a rounded border (radii != 0). For the sake

Просмотреть файл

@ -233,7 +233,20 @@ public class ReactViewGroup extends ViewGroup implements
} }
public void setBorderRadius(float borderRadius, int position) { public void setBorderRadius(float borderRadius, int position) {
getOrCreateReactViewBackground().setRadius(borderRadius, position); ReactViewBackgroundDrawable backgroundDrawable = getOrCreateReactViewBackground();
backgroundDrawable.setRadius(borderRadius, position);
if (Build.VERSION_CODES.HONEYCOMB < Build.VERSION.SDK_INT
&& Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
final int UPDATED_LAYER_TYPE =
backgroundDrawable.hasRoundedBorders()
? View.LAYER_TYPE_SOFTWARE
: View.LAYER_TYPE_HARDWARE;
if (UPDATED_LAYER_TYPE != getLayerType()) {
setLayerType(UPDATED_LAYER_TYPE, null);
}
}
} }
public void setBorderStyle(@Nullable String style) { public void setBorderStyle(@Nullable String style) {