Use clipPath for rounding corners on JB/KK

We lose antialiasing, as well as 100% rounded corners on JB, but gain huge wins in memory and CPU.

Future work could fix the 100% rounded corners if we want to import a bunch of very complex path generation logic from BorderDrawable.

PiperOrigin-RevId: 241798960
Change-Id: Ibc149ffdb0d85a40a5c10ede307f40eeac105306
diff --git a/src/main/java/com/google/android/libraries/feed/piet/AdapterParameters.java b/src/main/java/com/google/android/libraries/feed/piet/AdapterParameters.java
index 7f2174b..ba4a2ae 100644
--- a/src/main/java/com/google/android/libraries/feed/piet/AdapterParameters.java
+++ b/src/main/java/com/google/android/libraries/feed/piet/AdapterParameters.java
@@ -44,6 +44,7 @@
   final Clock clock;
   final PietStylesHelperFactory pietStylesHelperFactory;
   final RoundedCornerMaskCache roundedCornerMaskCache;
+  final boolean useLegacyRoundedCornerImpl;
 
   // Doesn't like passing "this" to the new ElementAdapterFactory; however, nothing in the factory's
   // construction will reference the elementAdapterFactory member of this, so should be safe.
@@ -52,7 +53,8 @@
       Context context,
       Supplier</*@Nullable*/ ViewGroup> parentViewSupplier,
       HostProviders hostProviders,
-      Clock clock) {
+      Clock clock,
+      boolean useLegacyRoundedCornerImpl) {
     this.context = context;
     this.parentViewSupplier = parentViewSupplier;
     this.hostProviders = hostProviders;
@@ -69,6 +71,8 @@
 
     this.pietStylesHelperFactory = new PietStylesHelperFactory();
     this.roundedCornerMaskCache = new RoundedCornerMaskCache();
+
+    this.useLegacyRoundedCornerImpl = useLegacyRoundedCornerImpl;
   }
 
   /** Testing-only constructor for mocking the internally-constructed objects. */
@@ -90,7 +94,8 @@
         templateBinder,
         clock,
         new PietStylesHelperFactory(),
-        new RoundedCornerMaskCache());
+        new RoundedCornerMaskCache(),
+        true);
   }
 
   /** Testing-only constructor for mocking the internally-constructed objects. */
@@ -104,7 +109,8 @@
       TemplateBinder templateBinder,
       Clock clock,
       PietStylesHelperFactory pietStylesHelperFactory,
-      RoundedCornerMaskCache maskCache) {
+      RoundedCornerMaskCache maskCache,
+      boolean useLegacyRoundedCornerImpl) {
     this.context = context;
     this.parentViewSupplier = parentViewSupplier;
     this.hostProviders = hostProviders;
@@ -116,5 +122,6 @@
     this.clock = clock;
     this.pietStylesHelperFactory = pietStylesHelperFactory;
     this.roundedCornerMaskCache = maskCache;
+    this.useLegacyRoundedCornerImpl = useLegacyRoundedCornerImpl;
   }
 }
diff --git a/src/main/java/com/google/android/libraries/feed/piet/ElementAdapter.java b/src/main/java/com/google/android/libraries/feed/piet/ElementAdapter.java
index 2bd4c6a..e706f14 100644
--- a/src/main/java/com/google/android/libraries/feed/piet/ElementAdapter.java
+++ b/src/main/java/com/google/android/libraries/feed/piet/ElementAdapter.java
@@ -174,7 +174,8 @@
     }
 
     FrameLayout wrapper =
-        elementStyle.createWrapperView(context, parameters.roundedCornerMaskCache);
+        elementStyle.createWrapperView(
+            context, parameters.roundedCornerMaskCache, parameters.useLegacyRoundedCornerImpl);
     wrapper.addView(getBaseView());
 
     if (baseElement.getOverlaysCount() > 0) {
diff --git a/src/main/java/com/google/android/libraries/feed/piet/PietManager.java b/src/main/java/com/google/android/libraries/feed/piet/PietManager.java
index 23618fc..c166623 100644
--- a/src/main/java/com/google/android/libraries/feed/piet/PietManager.java
+++ b/src/main/java/com/google/android/libraries/feed/piet/PietManager.java
@@ -72,6 +72,7 @@
     /*@MonotonicNonNull*/ private CustomElementProvider customElementProvider = null;
     /*@MonotonicNonNull*/ private HostBindingProvider hostBindingProvider = null;
     /*@MonotonicNonNull*/ private Clock clock = null;
+    private boolean useLegacyRoundedCornerImpl = false;
 
     private Builder() {}
 
@@ -95,6 +96,15 @@
       return this;
     }
 
+    /**
+     * Use the rounded corner optimizations on JB/KK for better performance at the expense of
+     * antialiasing.
+     */
+    public Builder setUseLegacyRoundedCornerImpl(boolean useLegacyRoundedCornerImpl) {
+      this.useLegacyRoundedCornerImpl = useLegacyRoundedCornerImpl;
+      return this;
+    }
+
     // AssetProvider-related setters
     public Builder setImageLoader(ImageLoader imageLoader) {
       this.imageLoader = imageLoader;
@@ -152,7 +162,12 @@
               typefaceProvider);
 
       return new PietManagerImpl(
-          debugBehavior, assetProvider, customElementProvider, hostBindingProvider, clock);
+          debugBehavior,
+          assetProvider,
+          customElementProvider,
+          hostBindingProvider,
+          clock,
+          useLegacyRoundedCornerImpl);
     }
   }
 
diff --git a/src/main/java/com/google/android/libraries/feed/piet/PietManagerImpl.java b/src/main/java/com/google/android/libraries/feed/piet/PietManagerImpl.java
index 4f20fe2..321f60b 100644
--- a/src/main/java/com/google/android/libraries/feed/piet/PietManagerImpl.java
+++ b/src/main/java/com/google/android/libraries/feed/piet/PietManagerImpl.java
@@ -34,6 +34,7 @@
   private final CustomElementProvider customElementProvider;
   private final HostBindingProvider hostBindingProvider;
   private final Clock clock;
+  private final boolean useLegacyRoundedCornerImpl;
   @VisibleForTesting /*@Nullable*/ AdapterParameters adapterParameters = null;
 
   PietManagerImpl(
@@ -41,12 +42,14 @@
       AssetProvider assetProvider,
       CustomElementProvider customElementProvider,
       HostBindingProvider hostBindingProvider,
-      Clock clock) {
+      Clock clock,
+      boolean useLegacyRoundedCornerImpl) {
     this.debugBehavior = debugBehavior;
     this.assetProvider = assetProvider;
     this.customElementProvider = customElementProvider;
     this.hostBindingProvider = hostBindingProvider;
     this.clock = clock;
+    this.useLegacyRoundedCornerImpl = useLegacyRoundedCornerImpl;
   }
 
   @Override
@@ -73,7 +76,8 @@
               context,
               cardViewProducer,
               new HostProviders(assetProvider, customElementProvider, hostBindingProvider),
-              clock);
+              clock,
+              useLegacyRoundedCornerImpl);
     }
     return adapterParameters;
   }
diff --git a/src/main/java/com/google/android/libraries/feed/piet/StyleProvider.java b/src/main/java/com/google/android/libraries/feed/piet/StyleProvider.java
index c060e4c..dc41b40 100644
--- a/src/main/java/com/google/android/libraries/feed/piet/StyleProvider.java
+++ b/src/main/java/com/google/android/libraries/feed/piet/StyleProvider.java
@@ -18,21 +18,24 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
+import android.os.Build.VERSION_CODES;
 import android.support.v4.view.ViewCompat;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewGroup.MarginLayoutParams;
+import android.view.ViewOutlineProvider;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.ImageView.ScaleType;
 import com.google.android.libraries.feed.common.ui.LayoutUtils;
 import com.google.android.libraries.feed.piet.host.AssetProvider;
+import com.google.android.libraries.feed.piet.ui.BitmapMaskingRoundedCornerWrapperView;
 import com.google.android.libraries.feed.piet.ui.BorderDrawable;
 import com.google.android.libraries.feed.piet.ui.GradientDrawable;
+import com.google.android.libraries.feed.piet.ui.LegacyRoundedCornerWrapperView;
 import com.google.android.libraries.feed.piet.ui.RoundedCornerMaskCache;
 import com.google.android.libraries.feed.piet.ui.RoundedCornerViewHelper;
-import com.google.android.libraries.feed.piet.ui.RoundedCornerWrapperView;
 import com.google.search.now.ui.piet.ErrorsProto.ErrorCode;
 import com.google.search.now.ui.piet.GradientsProto.Fill;
 import com.google.search.now.ui.piet.RoundedCornersProto.RoundedCorners;
@@ -329,14 +332,13 @@
     }
 
     // Apply appearance styles
-    view.setBackground(createBackground());
+    baseView.setBackground(createBackground());
     if (style.getShadow().hasElevationShadow()) {
       ViewCompat.setElevation(view, style.getShadow().getElevationShadow().getElevation());
     } else {
       ViewCompat.setElevation(view, 0.0f);
     }
     if (view != baseView) {
-      baseView.setBackground(null);
       ViewCompat.setElevation(baseView, 0.0f);
     }
 
@@ -408,19 +410,34 @@
     }
   }
 
-  FrameLayout createWrapperView(Context context, RoundedCornerMaskCache maskCache) {
+  FrameLayout createWrapperView(
+      Context context, RoundedCornerMaskCache maskCache, boolean useLegacyImpl) {
     if (!hasRoundedCorners()) {
-      return new FrameLayout(context);
+      FrameLayout view = new FrameLayout(context);
+      if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
+        // The wrapper view gets elevation; set an outline so it can cast a shadow.
+        view.setOutlineProvider(ViewOutlineProvider.BOUNDS);
+      }
+      return view;
     }
     int radiusOverride =
         getRoundedCorners().getUseHostRadiusOverride() ? assetProvider.getDefaultCornerRadius() : 0;
-    return new RoundedCornerWrapperView(
-        context,
-        getRoundedCorners(),
-        maskCache,
-        assetProvider.isRtLSupplier(),
-        radiusOverride,
-        getBorders());
+    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP || !useLegacyImpl) {
+      return new BitmapMaskingRoundedCornerWrapperView(
+          context,
+          getRoundedCorners(),
+          maskCache,
+          assetProvider.isRtLSupplier(),
+          radiusOverride,
+          getBorders());
+    } else {
+      return new LegacyRoundedCornerWrapperView(
+          context,
+          getRoundedCorners(),
+          assetProvider.isRtLSupplier(),
+          radiusOverride,
+          getBorders());
+    }
   }
 
   /**
diff --git a/src/main/java/com/google/android/libraries/feed/piet/ui/BitmapMaskingRoundedCornerWrapperView.java b/src/main/java/com/google/android/libraries/feed/piet/ui/BitmapMaskingRoundedCornerWrapperView.java
new file mode 100644
index 0000000..b530c98
--- /dev/null
+++ b/src/main/java/com/google/android/libraries/feed/piet/ui/BitmapMaskingRoundedCornerWrapperView.java
@@ -0,0 +1,254 @@
+// Copyright 2018 The Feed Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.android.libraries.feed.piet.ui;
+
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Outline;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.shapes.RoundRectShape;
+import android.os.Build;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.view.ViewParent;
+import com.google.android.libraries.feed.common.functional.Supplier;
+import com.google.android.libraries.feed.piet.ui.RoundedCornerMaskCache.Corner;
+import com.google.android.libraries.feed.piet.ui.RoundedCornerMaskCache.RoundedCornerBitmaps;
+import com.google.search.now.ui.piet.RoundedCornersProto.RoundedCorners;
+import com.google.search.now.ui.piet.RoundedCornersProto.RoundedCorners.Corners;
+import com.google.search.now.ui.piet.StylesProto.Borders;
+
+/**
+ * Generic wrapper for {@link View} instances in Piet that require rounded corners.
+ *
+ * <p>This implementation should work in all cases for all SDKs, but has the worst performance in
+ * CPU and memory usage.
+ */
+public class BitmapMaskingRoundedCornerWrapperView extends RoundedCornerWrapperView {
+
+  private final Paint paint;
+  private final Paint maskPaint;
+  private final RoundedCornerMaskCache maskCache;
+  private final Canvas offscreenCanvas;
+
+  /*@Nullable*/ private RoundRectShape outlineShape = null;
+
+  // Masks for each of the corners of the view; null if that corner is not rounded.
+  /*@Nullable*/ private Bitmap cornerTL = null;
+  /*@Nullable*/ private Bitmap cornerTR = null;
+  /*@Nullable*/ private Bitmap cornerBL = null;
+  /*@Nullable*/ private Bitmap cornerBR = null;
+
+  // Keep track of current mask configuration so we can use cached values if nothing has changed.
+  private int lastRadius = -1;
+  private boolean lastRtL;
+
+  // Doesn't like the call to setOutlineProvider
+  @SuppressWarnings("initialization")
+  public BitmapMaskingRoundedCornerWrapperView(
+      Context context,
+      RoundedCorners roundedCorners,
+      RoundedCornerMaskCache maskCache,
+      Supplier<Boolean> isRtLSupplier,
+      int radiusOverride,
+      Borders borders) {
+    super(context, roundedCorners, isRtLSupplier, radiusOverride, borders);
+
+    this.maskCache = maskCache;
+    offscreenCanvas = new Canvas();
+    lastRtL = !isRtLSupplier.get(); // Flip this so we must update the layout on the first time.
+
+    this.paint = maskCache.getPaint();
+    this.maskPaint = maskCache.getMaskPaint();
+
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+      if (hasRoundedCorners()) {
+        super.setOutlineProvider(
+            new ViewOutlineProvider() {
+              @Override
+              public void getOutline(View view, Outline outline) {
+                RoundRectShape localOutlineShape = outlineShape;
+                if (localOutlineShape == null
+                    || localOutlineShape.getHeight() != view.getHeight()
+                    || localOutlineShape.getWidth() != view.getWidth()) {
+                  int radius = getRadius(view.getWidth(), view.getHeight());
+                  float[] radii =
+                      RoundedCornerViewHelper.createRoundedCornerMask(
+                          radius, roundedCorners.getBitmask(), isRtLSupplier.get());
+                  localOutlineShape = new RoundRectShape(radii, null, null);
+                  localOutlineShape.resize(view.getWidth(), view.getHeight());
+                  outlineShape = localOutlineShape;
+                }
+                localOutlineShape.getOutline(outline);
+              }
+            });
+      } else {
+        super.setOutlineProvider(ViewOutlineProvider.BOUNDS);
+      }
+    }
+
+    setWillNotDraw(false);
+  }
+
+  private void initCornerMasks(int radius, boolean isRtL) {
+    if (radius < 1) {
+      return;
+    }
+    RoundedCornerBitmaps masks = maskCache.getMasks(radius);
+
+    if ((shouldRoundCorner(Corners.TOP_START) && !isRtL)
+        || (shouldRoundCorner(Corners.TOP_END) && isRtL)) {
+      cornerTL = masks.get(Corner.TOP_LEFT);
+    } else {
+      cornerTL = null;
+    }
+
+    if ((shouldRoundCorner(Corners.TOP_END) && !isRtL)
+        || (shouldRoundCorner(Corners.TOP_START) && isRtL)) {
+      cornerTR = masks.get(Corner.TOP_RIGHT);
+    } else {
+      cornerTR = null;
+    }
+
+    if ((shouldRoundCorner(Corners.BOTTOM_START) && !isRtL)
+        || (shouldRoundCorner(Corners.BOTTOM_END) && isRtL)) {
+      cornerBL = masks.get(Corner.BOTTOM_LEFT);
+    } else {
+      cornerBL = null;
+    }
+
+    if ((shouldRoundCorner(Corners.BOTTOM_END) && !isRtL)
+        || (shouldRoundCorner(Corners.BOTTOM_START) && isRtL)) {
+      cornerBR = masks.get(Corner.BOTTOM_RIGHT);
+    } else {
+      cornerBR = null;
+    }
+  }
+
+  /**
+   * Creates corner masks (which cover the parts of the corners that should not be shown) and
+   * borders, as necessary. Both must be created after the radius is known. However, if the size of
+   * the view and the LtR remain the same, it will only create the masks and borders once.
+   */
+  private void setupCornerMasksAndBorders(int radius) {
+    if (!hasRoundedCorners() || radius == 0) {
+      return;
+    }
+    boolean isRtL = isRtLSupplier.get();
+    if (radius == lastRadius && isRtL == lastRtL) {
+      return;
+    }
+
+    initCornerMasks(radius, isRtL);
+    addBorders(radius);
+
+    lastRadius = radius;
+    lastRtL = isRtL;
+  }
+
+  /**
+   * Ensures that the wrapper view is invalidated when child views are invalidated. This method only
+   * exists in Android O+.
+   */
+  @Override
+  public void onDescendantInvalidated(View child, View target) {
+    super.onDescendantInvalidated(child, target);
+    if (hasRoundedCorners()) {
+      Rect targetRect = new Rect();
+      target.getDrawingRect(targetRect);
+      invalidate(targetRect);
+    }
+  }
+
+  /**
+   * Using as an indicator that the child view was invalidated. By overriding this method, we ensure
+   * that the wrapper view is invalidated when the child view is. This is only used in Android N-
+   * and is deprecated, but we must use it because onDescendantInvalidated only exists in O+.
+   */
+  @Override
+  public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
+    if (hasRoundedCorners()) {
+      invalidate(dirty);
+    }
+    return super.invalidateChildInParent(location, dirty);
+  }
+
+  @Override
+  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+    super.onLayout(changed, left, top, right, bottom);
+    if (!changed || !hasRoundedCorners()) {
+      return;
+    }
+
+    int width = getWidth();
+    int height = getHeight();
+    if (width == 0 || height == 0) {
+      // The view is not visible; no further processing is needed.
+      return;
+    }
+
+    int radius = getRadius(width, height);
+
+    // Set up the corner masks and borders, both of which require knowing the radius.
+    // This should no-op if radius and isLtR have not changed.
+    setupCornerMasksAndBorders(radius);
+  }
+
+  @Override
+  public void draw(Canvas canvas) {
+    if (!hasRoundedCorners()) {
+      super.draw(canvas);
+      return;
+    }
+    int width = getWidth();
+    int height = getHeight();
+    if (width == 0 || height == 0) {
+      // The view is not visible, and offscreenBitmap creation will fail. Stop here.
+      return;
+    }
+
+    int radius = getRadius(width, height);
+
+    // Draw the view without rounded corners on the offscreen canvas.
+    Bitmap localOffscreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+    offscreenCanvas.setBitmap(localOffscreenBitmap);
+    super.draw(offscreenCanvas);
+
+    // Crop the corners off using masks
+    maskCorners(offscreenCanvas, width, height, radius, maskPaint);
+
+    // Draw the offscreen bitmap (view with rounded corners) to the target canvas.
+    canvas.drawBitmap(localOffscreenBitmap, 0f, 0f, paint);
+  }
+
+  /** Draws a mask on each corner that is rounded. */
+  private void maskCorners(Canvas canvas, int width, int height, int radius, Paint paint) {
+    if (cornerTL != null) {
+      canvas.drawBitmap(cornerTL, 0, 0, paint);
+    }
+    if (cornerTR != null) {
+      canvas.drawBitmap(cornerTR, width - radius, 0, paint);
+    }
+    if (cornerBL != null) {
+      canvas.drawBitmap(cornerBL, 0, height - radius, paint);
+    }
+    if (cornerBR != null) {
+      canvas.drawBitmap(cornerBR, width - radius, height - radius, paint);
+    }
+  }
+}
diff --git a/src/main/java/com/google/android/libraries/feed/piet/ui/BorderDrawable.java b/src/main/java/com/google/android/libraries/feed/piet/ui/BorderDrawable.java
index 9bfbdc7..c337b26 100644
--- a/src/main/java/com/google/android/libraries/feed/piet/ui/BorderDrawable.java
+++ b/src/main/java/com/google/android/libraries/feed/piet/ui/BorderDrawable.java
@@ -15,16 +15,10 @@
 package com.google.android.libraries.feed.piet.ui;
 
 import android.content.Context;
-import android.graphics.Canvas;
 import android.graphics.Paint;
-import android.graphics.Path;
 import android.graphics.Rect;
-import android.graphics.RectF;
 import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.shapes.RoundRectShape;
-import android.graphics.drawable.shapes.Shape;
-import android.os.Build;
-import android.os.Build.VERSION_CODES;
 import com.google.android.libraries.feed.common.ui.LayoutUtils;
 import com.google.search.now.ui.piet.StylesProto.Borders;
 import com.google.search.now.ui.piet.StylesProto.Borders.Edges;
@@ -34,9 +28,6 @@
  * not specified.
  */
 public class BorderDrawable extends ShapeDrawable {
-  private final float[] cornerRadii;
-  private final int initialWidth;
-  private final int initialHeight;
   private final boolean hasLeftBorder;
   private final boolean hasRightBorder;
   private final boolean hasTopBorder;
@@ -56,9 +47,6 @@
   public BorderDrawable(
       Context context, Borders borders, float[] cornerRadii, boolean isRtL, int width, int height) {
     super(new RoundRectShape(cornerRadii, null, null));
-    this.cornerRadii = cornerRadii;
-    this.initialWidth = width;
-    this.initialHeight = height;
 
     borderWidth = (int) LayoutUtils.dpToPx(borders.getWidth(), context);
 
@@ -109,147 +97,4 @@
   public void setBounds(Rect bounds) {
     setBounds(bounds.left, bounds.top, bounds.right, bounds.bottom);
   }
-
-  @Override
-  protected void onDraw(Shape shape, Canvas canvas, Paint paint) {
-    if (Build.VERSION.SDK_INT < VERSION_CODES.KITKAT && hasLargeRadius()) {
-      drawBorderWithPath(canvas, paint);
-    } else {
-      super.onDraw(shape, canvas, paint);
-    }
-  }
-
-  /**
-   * Checks if the radius is larger than half the width or height. Only applies to elements with
-   * rounded corners.
-   */
-  private boolean hasLargeRadius() {
-    if (initialWidth == 0 || initialHeight == 0) {
-      return false;
-    }
-    int radius = 0;
-    for (float cornerRadius : cornerRadii) {
-      if (cornerRadius != 0) {
-        radius = (int) cornerRadius;
-        break;
-      }
-    }
-    if (radius == 0) {
-      return false;
-    }
-    return radius > (initialWidth / 2) || radius > (initialHeight / 2);
-  }
-
-  /**
-   * Draws the border with a {@link Path} instead of a {@link RoundRectShape}. This is used because
-   * in JellyBean, RoundRectShape clamps the rounded corner radius to half the length of the side of
-   * the shape. When the adjacent corner is not rounded, we want to allow radii that are more than
-   * 50% of that side.
-   */
-  private void drawBorderWithPath(Canvas canvas, Paint paint) {
-    Path path = new Path();
-    int height = initialHeight;
-    int width = initialWidth;
-    // This is necessary because of extra height/width added for borders.
-    if (!hasTopBorder) {
-      height += borderWidth;
-    }
-    if (!hasLeftBorder) {
-      width += borderWidth;
-    }
-    if (hasTopBorder) {
-      if (cornerRadii[0] != 0) {
-        // Add second half of top left corner.
-        float radius = cornerRadii[0];
-        path.addArc(topLeftBoundingBox(radius), 225, 45);
-      }
-      if (cornerRadii[2] != 0) {
-        // If the top right corner is rounded, add the top border and half the top right corner.
-        float radius = cornerRadii[2];
-        path.lineTo(width - radius, 0);
-        path.addArc(topRightBoundingBox(width, radius), 270, 46);
-      } else {
-        // Add border across top.
-        path.lineTo(width, 0);
-      }
-    } else {
-      // If there is no top border, jump to the top right corner.
-      path.moveTo(width, 0);
-    }
-
-    if (hasRightBorder) {
-      if (cornerRadii[2] != 0) {
-        // Add second half of top right corner.
-        float radius = cornerRadii[2];
-        path.addArc(topRightBoundingBox(width, radius), 315, 45);
-      }
-      if (cornerRadii[4] != 0) {
-        // If the bottom right corner is rounded, add right border and half the bottom right corner.
-        float radius = cornerRadii[4];
-        path.lineTo(width, height - radius);
-        path.addArc(bottomRightBoundingBox(width, height, radius), 0, 46);
-      } else {
-        // Add right border, no rounded corner at bottom.
-        path.lineTo(width, height);
-      }
-    } else {
-      // If there is no right border, jump to the bottom right corner.
-      path.moveTo(width, height);
-    }
-
-    if (hasBottomBorder) {
-      if (cornerRadii[4] != 0) {
-        // Add second half of bottom right corner.
-        float radius = cornerRadii[4];
-        path.addArc(bottomRightBoundingBox(width, height, radius), 45, 45);
-      }
-      if (cornerRadii[6] != 0) {
-        // Add bottom border with rounded corner at bottom left.
-        float radius = cornerRadii[6];
-        path.lineTo(radius, height);
-        path.addArc(bottomLeftBoundingBox(height, radius), 90, 46);
-      } else {
-        path.lineTo(0, height);
-      }
-    } else {
-      // If there is no bottom border, jump to the bottom left corner.
-      path.moveTo(0, height);
-    }
-
-    if (hasLeftBorder) {
-      if (cornerRadii[6] != 0) {
-        // Add second half of bottom left corner.
-        float radius = cornerRadii[6];
-        path.addArc(bottomLeftBoundingBox(height, radius), 135, 45);
-      }
-      if (cornerRadii[0] != 0) {
-        // Add left border with rounded corner at top left.
-        float radius = cornerRadii[0];
-        path.lineTo(0, radius);
-        path.addArc(topLeftBoundingBox(radius), 180, 46);
-      } else {
-        // Add left border.
-        path.lineTo(0, 0);
-      }
-    }
-
-    // Actually draw the path that was just built. The paint defines the width/color of the border.
-    canvas.drawPath(path, paint);
-  }
-
-  private RectF topLeftBoundingBox(float radius) {
-    return new RectF(0, 0, radius * 2, radius * 2);
-  }
-
-  private RectF topRightBoundingBox(float width, float radius) {
-    return new RectF(width - (radius * 2), 0, width, radius * 2);
-  }
-
-  private RectF bottomRightBoundingBox(float width, float height, float radius) {
-    return new RectF(width - (radius * 2), height - (radius * 2), width, height);
-  }
-
-  private RectF bottomLeftBoundingBox(float height, float radius) {
-    return new RectF(0, height - (radius * 2), radius * 2, height);
-  }
 }
diff --git a/src/main/java/com/google/android/libraries/feed/piet/ui/LegacyRoundedCornerWrapperView.java b/src/main/java/com/google/android/libraries/feed/piet/ui/LegacyRoundedCornerWrapperView.java
new file mode 100644
index 0000000..f29f6d2
--- /dev/null
+++ b/src/main/java/com/google/android/libraries/feed/piet/ui/LegacyRoundedCornerWrapperView.java
@@ -0,0 +1,98 @@
+// Copyright 2018 The Feed Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.android.libraries.feed.piet.ui;
+
+import static com.google.android.libraries.feed.common.Validators.checkState;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.os.Build;
+import android.os.Build.VERSION;
+import android.os.Build.VERSION_CODES;
+import android.view.View;
+import com.google.android.libraries.feed.common.functional.Supplier;
+import com.google.search.now.ui.piet.RoundedCornersProto.RoundedCorners;
+import com.google.search.now.ui.piet.StylesProto.Borders;
+
+/**
+ * Wrapper for {@link View} instances in Piet that require rounded corners.
+ *
+ * <p>This class does not support antialiasing on rounded corners, but has much better memory and
+ * CPU performance on JB and KK (API 16 and 19)
+ */
+public class LegacyRoundedCornerWrapperView extends RoundedCornerWrapperView {
+
+  /*@Nullable*/ private Path clipPath = null;
+
+  // Doesn't like the call to setLayerType
+  @SuppressWarnings("initialization")
+  public LegacyRoundedCornerWrapperView(
+      Context context,
+      RoundedCorners roundedCorners,
+      Supplier<Boolean> isRtLSupplier,
+      int radiusOverride,
+      Borders borders) {
+    super(context, roundedCorners, isRtLSupplier, radiusOverride, borders);
+
+    checkState(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP);
+
+    if (VERSION.SDK_INT < VERSION_CODES.JELLY_BEAN_MR2) {
+      // clipPath doesn't work with hardware rendering on < 18.
+      setLayerType(LAYER_TYPE_SOFTWARE, null);
+    }
+  }
+
+  @Override
+  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+    super.onSizeChanged(w, h, oldw, oldh);
+
+    int radius = getRadius(w, h);
+    float[] radii =
+        RoundedCornerViewHelper.createRoundedCornerMask(
+            radius, roundedCorners.getBitmask(), isRtLSupplier.get());
+
+    clipPath = new Path();
+    clipPath.addRoundRect(new RectF(0, 0, w, h), radii, Path.Direction.CW);
+  }
+
+  @Override
+  protected void dispatchDraw(Canvas canvas) {
+    if (clipPath != null) {
+      canvas.clipPath(clipPath);
+    }
+    super.dispatchDraw(canvas);
+  }
+
+  @Override
+  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+    super.onLayout(changed, left, top, right, bottom);
+    if (!changed || !hasRoundedCorners()) {
+      return;
+    }
+
+    int width = getWidth();
+    int height = getHeight();
+    if (width == 0 || height == 0) {
+      // The view is not visible; no further processing is needed.
+      return;
+    }
+
+    int radius = getRadius(width, height);
+
+    addBorders(radius);
+  }
+}
diff --git a/src/main/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperView.java b/src/main/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperView.java
index bb994bc..7b6c76e 100644
--- a/src/main/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperView.java
+++ b/src/main/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperView.java
@@ -16,155 +16,43 @@
 
 
 import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Outline;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.drawable.shapes.RoundRectShape;
-import android.os.Build;
 import android.view.View;
-import android.view.ViewOutlineProvider;
-import android.view.ViewParent;
 import android.widget.FrameLayout;
 import com.google.android.libraries.feed.common.functional.Supplier;
 import com.google.android.libraries.feed.common.ui.LayoutUtils;
-import com.google.android.libraries.feed.piet.ui.RoundedCornerMaskCache.Corner;
-import com.google.android.libraries.feed.piet.ui.RoundedCornerMaskCache.RoundedCornerBitmaps;
 import com.google.search.now.ui.piet.RoundedCornersProto.RoundedCorners;
 import com.google.search.now.ui.piet.RoundedCornersProto.RoundedCorners.Corners;
 import com.google.search.now.ui.piet.RoundedCornersProto.RoundedCorners.RadiusOptionsCase;
 import com.google.search.now.ui.piet.StylesProto.Borders;
 
 /** Wrapper for {@link View} instances in Piet that require rounded corners. */
-public class RoundedCornerWrapperView extends FrameLayout {
+public abstract class RoundedCornerWrapperView extends FrameLayout {
+  final int radiusOverride;
+  final RoundedCorners roundedCorners;
+  final Supplier<Boolean> isRtLSupplier;
+  final Context context;
+  final Borders borders;
 
-  private final Paint paint;
-  private final Paint maskPaint;
-  private final int radiusOverride;
-  private final RoundedCorners roundedCorners;
-  private final Supplier<Boolean> isRtLSupplier;
-  private final Context context;
-  private final Borders borders;
-  private final RoundedCornerMaskCache maskCache;
-  private final Canvas offscreenCanvas;
-
-  /*@Nullable*/ private RoundRectShape outlineShape = null;
-  private int roundedCornerRadius;
-
-  // Masks for each of the corners of the view; null if that corner is not rounded.
-  /*@Nullable*/ private Bitmap cornerTL = null;
-  /*@Nullable*/ private Bitmap cornerTR = null;
-  /*@Nullable*/ private Bitmap cornerBL = null;
-  /*@Nullable*/ private Bitmap cornerBR = null;
+  int roundedCornerRadius;
 
   // Keep track of current mask configuration so we can use cached values if nothing has changed.
-  private int lastRadius = -1;
-  private int lastWidth = -1;
-  private int lastHeight = -1;
-  private boolean lastRtL;
+  int lastWidth = -1;
+  int lastHeight = -1;
 
-  // Doesn't like the call to setOutlineProvider
-  @SuppressWarnings("initialization")
-  public RoundedCornerWrapperView(
+  RoundedCornerWrapperView(
       Context context,
       RoundedCorners roundedCorners,
-      RoundedCornerMaskCache maskCache,
       Supplier<Boolean> isRtLSupplier,
       int radiusOverride,
       Borders borders) {
     super(context);
-    this.maskCache = maskCache;
-    this.isRtLSupplier = isRtLSupplier;
-    this.roundedCorners = roundedCorners;
+
     this.radiusOverride = radiusOverride;
+    this.roundedCorners = roundedCorners;
+    this.isRtLSupplier = isRtLSupplier;
+
     this.context = context;
     this.borders = borders;
-    offscreenCanvas = new Canvas();
-    lastRtL = !isRtLSupplier.get(); // Flip this so we must update the layout on the first time.
-
-    this.paint = maskCache.getPaint();
-    this.maskPaint = maskCache.getMaskPaint();
-
-    if (hasRoundedCorners() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
-      super.setOutlineProvider(
-          new ViewOutlineProvider() {
-            @Override
-            public void getOutline(View view, Outline outline) {
-              RoundRectShape localOutlineShape = outlineShape;
-              if (localOutlineShape == null
-                  || localOutlineShape.getHeight() != view.getHeight()
-                  || localOutlineShape.getWidth() != view.getWidth()) {
-                int radius = getRadius(view.getWidth(), view.getHeight());
-                float[] radii =
-                    RoundedCornerViewHelper.createRoundedCornerMask(
-                        radius, roundedCorners.getBitmask(), isRtLSupplier.get());
-                localOutlineShape = new RoundRectShape(radii, null, null);
-                localOutlineShape.resize(view.getWidth(), view.getHeight());
-                outlineShape = localOutlineShape;
-              }
-              localOutlineShape.getOutline(outline);
-            }
-          });
-    }
-
-    setWillNotDraw(false);
-  }
-
-  private void initCornerMasks(int radius, boolean isRtL) {
-    if (radius < 1) {
-      return;
-    }
-    RoundedCornerBitmaps masks = maskCache.getMasks(radius);
-
-    if ((shouldRoundCorner(Corners.TOP_START) && !isRtL)
-        || (shouldRoundCorner(Corners.TOP_END) && isRtL)) {
-      cornerTL = masks.get(Corner.TOP_LEFT);
-    } else {
-      cornerTL = null;
-    }
-
-    if ((shouldRoundCorner(Corners.TOP_END) && !isRtL)
-        || (shouldRoundCorner(Corners.TOP_START) && isRtL)) {
-      cornerTR = masks.get(Corner.TOP_RIGHT);
-    } else {
-      cornerTR = null;
-    }
-
-    if ((shouldRoundCorner(Corners.BOTTOM_START) && !isRtL)
-        || (shouldRoundCorner(Corners.BOTTOM_END) && isRtL)) {
-      cornerBL = masks.get(Corner.BOTTOM_LEFT);
-    } else {
-      cornerBL = null;
-    }
-
-    if ((shouldRoundCorner(Corners.BOTTOM_END) && !isRtL)
-        || (shouldRoundCorner(Corners.BOTTOM_START) && isRtL)) {
-      cornerBR = masks.get(Corner.BOTTOM_RIGHT);
-    } else {
-      cornerBR = null;
-    }
-  }
-
-  /**
-   * Creates corner masks (which cover the parts of the corners that should not be shown) and
-   * borders, as necessary. Both must be created after the radius is known. However, if the size of
-   * the view and the LtR remain the same, it will only create the masks and borders once.
-   */
-  private void setupCornerMasksAndBorders(int radius) {
-    if (!hasRoundedCorners() || radius == 0) {
-      return;
-    }
-    boolean isRtL = isRtLSupplier.get();
-    if (radius == lastRadius && isRtL == lastRtL) {
-      return;
-    }
-
-    initCornerMasks(radius, isRtL);
-    addBorders(radius);
-
-    lastRadius = radius;
-    lastRtL = isRtL;
   }
 
   /**
@@ -175,7 +63,7 @@
    * after layout happens, since we are overriding draw. Draw is guaranteed to happen after layout,
    * so we can make sure that the borders are drawn with the appropriate measurements.
    */
-  private void addBorders(int radius) {
+  void addBorders(int radius) {
     if (borders.getWidth() <= 0) {
       return;
     }
@@ -191,81 +79,6 @@
   }
 
   /**
-   * Ensures that the wrapper view is invalidated when child views are invalidated. This method only
-   * exists in Android O+.
-   */
-  @Override
-  public void onDescendantInvalidated(View child, View target) {
-    super.onDescendantInvalidated(child, target);
-    if (hasRoundedCorners()) {
-      Rect targetRect = new Rect();
-      target.getDrawingRect(targetRect);
-      invalidate(targetRect);
-    }
-  }
-
-  /**
-   * Using as an indicator that the child view was invalidated. By overriding this method, we ensure
-   * that the wrapper view is invalidated when the child view is. This is only used in Android N-
-   * and is deprecated, but we must use it because onDescendantInvalidated only exists in O+.
-   */
-  @Override
-  public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
-    if (hasRoundedCorners()) {
-      invalidate(dirty);
-    }
-    return super.invalidateChildInParent(location, dirty);
-  }
-
-  @Override
-  protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
-    super.onLayout(changed, left, top, right, bottom);
-    if (!changed || !hasRoundedCorners()) {
-      return;
-    }
-
-    int width = getWidth();
-    int height = getHeight();
-    if (width == 0 || height == 0) {
-      // The view is not visible; no further processing is needed.
-      return;
-    }
-
-    int radius = getRadius(width, height);
-
-    // Set up the corner masks and borders, both of which require knowing the radius.
-    // This should no-op if radius and isLtR have not changed.
-    setupCornerMasksAndBorders(radius);
-  }
-
-  @Override
-  public void draw(Canvas canvas) {
-    if (!hasRoundedCorners()) {
-      super.draw(canvas);
-      return;
-    }
-    int width = getWidth();
-    int height = getHeight();
-    if (width == 0 || height == 0) {
-      // The view is not visible, and offscreenBitmap creation will fail. Stop here.
-      return;
-    }
-
-    int radius = getRadius(width, height);
-
-    // Draw the view without rounded corners on the offscreen canvas.
-    Bitmap localOffscreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-    offscreenCanvas.setBitmap(localOffscreenBitmap);
-    super.draw(offscreenCanvas);
-
-    // Crop the corners off using masks
-    maskCorners(offscreenCanvas, width, height, radius, maskPaint);
-
-    // Draw the offscreen bitmap (view with rounded corners) to the target canvas.
-    canvas.drawBitmap(localOffscreenBitmap, 0f, 0f, paint);
-  }
-
-  /**
    * Returns the radius, which is only calculated if necessary. If the width and height are the same
    * as the previous width and height, there's no need to re-calculate, and the previous radius is
    * returned. The width and height are needed for radii calculated as a percentage of width or
@@ -335,22 +148,6 @@
     return currentRadius;
   }
 
-  /** Draws a mask on each corner that is rounded. */
-  private void maskCorners(Canvas canvas, int width, int height, int radius, Paint paint) {
-    if (cornerTL != null) {
-      canvas.drawBitmap(cornerTL, 0, 0, paint);
-    }
-    if (cornerTR != null) {
-      canvas.drawBitmap(cornerTR, width - radius, 0, paint);
-    }
-    if (cornerBL != null) {
-      canvas.drawBitmap(cornerBL, 0, height - radius, paint);
-    }
-    if (cornerBR != null) {
-      canvas.drawBitmap(cornerBR, width - radius, height - radius, paint);
-    }
-  }
-
   /** This should always be true; we should not be using this view when corners are not round. */
   public boolean hasRoundedCorners() {
     return RoundedCornerViewHelper.hasValidRoundedCorners(roundedCorners, radiusOverride);
@@ -366,7 +163,7 @@
         || (shouldRoundCorner(Corners.BOTTOM_START) && shouldRoundCorner(Corners.BOTTOM_END));
   }
 
-  private boolean shouldRoundCorner(Corners corner) {
+  boolean shouldRoundCorner(Corners corner) {
     int bitmask = roundedCorners.getBitmask();
     return (bitmask == 0) || (bitmask & corner.getNumber()) != 0;
   }
diff --git a/src/test/java/com/google/android/libraries/feed/piet/CustomElementAdapterTest.java b/src/test/java/com/google/android/libraries/feed/piet/CustomElementAdapterTest.java
index b4dc32c..d9d022c 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/CustomElementAdapterTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/CustomElementAdapterTest.java
@@ -78,7 +78,7 @@
         .thenAnswer(invocation -> invocation.getArguments()[2]);
 
     adapterParameters =
-        new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock());
+        new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock(), false);
 
     when(frameContext.makeStyleFor(any(StyleIdsStack.class)))
         .thenReturn(adapterParameters.defaultStyleProvider);
diff --git a/src/test/java/com/google/android/libraries/feed/piet/ElementAdapterTest.java b/src/test/java/com/google/android/libraries/feed/piet/ElementAdapterTest.java
index b8653d7..0c1205d 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/ElementAdapterTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/ElementAdapterTest.java
@@ -41,6 +41,7 @@
 import com.google.android.libraries.feed.piet.AdapterFactory.SingletonKeySupplier;
 import com.google.android.libraries.feed.piet.host.ActionHandler;
 import com.google.android.libraries.feed.piet.host.ActionHandler.ActionType;
+import com.google.android.libraries.feed.piet.ui.BitmapMaskingRoundedCornerWrapperView;
 import com.google.android.libraries.feed.piet.ui.RoundedCornerMaskCache;
 import com.google.android.libraries.feed.piet.ui.RoundedCornerWrapperView;
 import com.google.android.libraries.feed.testing.shadows.ExtendedShadowView;
@@ -81,6 +82,7 @@
 public class ElementAdapterTest {
   private static final int WIDTH = 123;
   private static final int HEIGHT = 456;
+  private static final boolean LEGACY_CORNERS_FLAG = false;
 
   @Mock private FrameContext frameContext;
   @Mock private HostProviders hostProviders;
@@ -100,13 +102,18 @@
     context = Robolectric.buildActivity(Activity.class).get();
     parameters =
         new AdapterParameters(
-            context, Suppliers.of((ViewGroup) null), hostProviders, new FakeClock());
+            context,
+            Suppliers.of((ViewGroup) null),
+            hostProviders,
+            new FakeClock(),
+            LEGACY_CORNERS_FLAG);
     maskCache = parameters.roundedCornerMaskCache;
     when(frameContext.makeStyleFor(any(StyleIdsStack.class))).thenReturn(styleProvider);
     when(frameContext.getActionHandler()).thenReturn(actionHandler);
     when(styleProvider.hasRoundedCorners()).thenReturn(false);
     when(styleProvider.getRoundedCorners()).thenReturn(RoundedCorners.getDefaultInstance());
-    when(styleProvider.createWrapperView(context, maskCache)).thenReturn(new FrameLayout(context));
+    when(styleProvider.createWrapperView(context, maskCache, LEGACY_CORNERS_FLAG))
+        .thenReturn(new FrameLayout(context));
 
     view = new View(context);
     adapter = new TestElementAdapter(context, parameters, view);
@@ -308,7 +315,8 @@
             mockFactory,
             mock(TemplateBinder.class),
             new FakeClock());
-    when(styleProvider.createWrapperView(context, parameters.roundedCornerMaskCache))
+    when(styleProvider.createWrapperView(
+            context, parameters.roundedCornerMaskCache, parameters.useLegacyRoundedCornerImpl))
         .thenReturn(new FrameLayout(context));
     adapter = new TestElementAdapter(context, parameters, view);
 
@@ -1274,7 +1282,7 @@
   public void testRoundedCorners() {
     RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(10).build();
     RoundedCornerWrapperView roundedCornerWrapperView =
-        new RoundedCornerWrapperView(
+        new BitmapMaskingRoundedCornerWrapperView(
             context,
             roundedCorners,
             maskCache,
@@ -1282,7 +1290,8 @@
             /*radiusOverride= */ 0,
             Borders.getDefaultInstance());
     when(styleProvider.hasRoundedCorners()).thenReturn(true);
-    when(styleProvider.createWrapperView(context, maskCache)).thenReturn(roundedCornerWrapperView);
+    when(styleProvider.createWrapperView(context, maskCache, LEGACY_CORNERS_FLAG))
+        .thenReturn(roundedCornerWrapperView);
 
     adapter.createAdapter(Element.getDefaultInstance(), frameContext);
 
@@ -1293,7 +1302,7 @@
   public void testRoundedCornersWithVisibility() {
     RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(10).build();
     RoundedCornerWrapperView roundedCornerWrapperView =
-        new RoundedCornerWrapperView(
+        new BitmapMaskingRoundedCornerWrapperView(
             context,
             roundedCorners,
             maskCache,
@@ -1301,7 +1310,8 @@
             /*radiusOverride= */ 0,
             Borders.getDefaultInstance());
     when(styleProvider.hasRoundedCorners()).thenReturn(true);
-    when(styleProvider.createWrapperView(context, maskCache)).thenReturn(roundedCornerWrapperView);
+    when(styleProvider.createWrapperView(context, maskCache, LEGACY_CORNERS_FLAG))
+        .thenReturn(roundedCornerWrapperView);
 
     VisibilityBindingRef visibilityBinding =
         VisibilityBindingRef.newBuilder().setBindingId("visibility").build();
diff --git a/src/test/java/com/google/android/libraries/feed/piet/ElementContainerAdapterTest.java b/src/test/java/com/google/android/libraries/feed/piet/ElementContainerAdapterTest.java
index b00e9bb..cf840e9 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/ElementContainerAdapterTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/ElementContainerAdapterTest.java
@@ -143,7 +143,7 @@
 
     when(hostProviders.getAssetProvider()).thenReturn(assetProvider);
     AdapterParameters adapterParameters =
-        new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock());
+        new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock(), false);
 
     when(frameContext.createTemplateContext(eq(DEFAULT_TEMPLATE), any(BindingContext.class)))
         .thenReturn(frameContext);
diff --git a/src/test/java/com/google/android/libraries/feed/piet/ElementListAdapterTest.java b/src/test/java/com/google/android/libraries/feed/piet/ElementListAdapterTest.java
index 99f2e55..9d176c4 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/ElementListAdapterTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/ElementListAdapterTest.java
@@ -112,7 +112,7 @@
     when(styleProvider.getRoundedCorners()).thenReturn(RoundedCorners.getDefaultInstance());
 
     adapterParameters =
-        new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock());
+        new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock(), false);
 
     when(frameContext.makeStyleFor(StyleIdsStack.getDefaultInstance()))
         .thenReturn(adapterParameters.defaultStyleProvider);
diff --git a/src/test/java/com/google/android/libraries/feed/piet/ElementStackAdapterTest.java b/src/test/java/com/google/android/libraries/feed/piet/ElementStackAdapterTest.java
index 5027c72..e30d8f3 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/ElementStackAdapterTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/ElementStackAdapterTest.java
@@ -88,7 +88,8 @@
     when(mockStyleProvider.getRoundedCorners()).thenReturn(RoundedCorners.getDefaultInstance());
 
     adapterParameters =
-        new AdapterParameters(context, Suppliers.of(null), mockHostProviders, new FakeClock());
+        new AdapterParameters(
+            context, Suppliers.of(null), mockHostProviders, new FakeClock(), false);
 
     adapter = new ElementStackAdapter.KeySupplier().getAdapter(context, adapterParameters);
   }
diff --git a/src/test/java/com/google/android/libraries/feed/piet/FrameAdapterImplTest.java b/src/test/java/com/google/android/libraries/feed/piet/FrameAdapterImplTest.java
index c83f3d8..3b03e32 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/FrameAdapterImplTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/FrameAdapterImplTest.java
@@ -141,7 +141,8 @@
             templateBinder,
             new FakeClock(),
             stylesHelpers,
-            maskCache);
+            maskCache,
+            false);
     when(elementAdapter.getView()).thenReturn(new LinearLayout(context));
     when(templateAdapter.getView()).thenReturn(new LinearLayout(context));
     doReturn(elementAdapter)
@@ -269,7 +270,11 @@
             .build();
     AdapterParameters adapterParameters =
         new AdapterParameters(
-            context, Suppliers.of(new LinearLayout(context)), hostProviders, new FakeClock());
+            context,
+            Suppliers.of(new LinearLayout(context)),
+            hostProviders,
+            new FakeClock(),
+            false);
     PietSharedState pietSharedState =
         PietSharedState.newBuilder()
             .addTemplates(
@@ -332,7 +337,11 @@
   public void testBindAndUnbind_resetsStylesWhenWrapperViewIsAdded() {
     AdapterParameters adapterParameters =
         new AdapterParameters(
-            context, Suppliers.of(new LinearLayout(context)), hostProviders, new FakeClock());
+            context,
+            Suppliers.of(new LinearLayout(context)),
+            hostProviders,
+            new FakeClock(),
+            false);
     PietSharedState pietSharedState =
         PietSharedState.newBuilder()
             .addStylesheets(
@@ -537,7 +546,11 @@
   public void testCreateFrameContext_recyclesPietStylesHelper() {
     AdapterParameters adapterParameters =
         new AdapterParameters(
-            context, Suppliers.of(new LinearLayout(context)), hostProviders, new FakeClock());
+            context,
+            Suppliers.of(new LinearLayout(context)),
+            hostProviders,
+            new FakeClock(),
+            false);
     PietSharedState pietSharedState =
         PietSharedState.newBuilder()
             .addStylesheets(
@@ -607,7 +620,7 @@
     pietSharedStates.add(pietSharedState);
 
     AdapterParameters parameters =
-        new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock());
+        new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock(), false);
     FrameAdapterImpl frameAdapter =
         new FrameAdapterImpl(context, parameters, actionHandler, eventLogger, debugBehavior);
 
diff --git a/src/test/java/com/google/android/libraries/feed/piet/GridRowAdapterTest.java b/src/test/java/com/google/android/libraries/feed/piet/GridRowAdapterTest.java
index da23080..2e7ce98 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/GridRowAdapterTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/GridRowAdapterTest.java
@@ -134,7 +134,7 @@
     when(styleProvider.getRoundedCorners()).thenReturn(RoundedCorners.getDefaultInstance());
 
     adapterParameters =
-        new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock());
+        new AdapterParameters(context, Suppliers.of(null), hostProviders, new FakeClock(), false);
 
     when(frameContext.makeStyleFor(StyleIdsStack.getDefaultInstance()))
         .thenReturn(adapterParameters.defaultStyleProvider);
diff --git a/src/test/java/com/google/android/libraries/feed/piet/ImageElementAdapterTest.java b/src/test/java/com/google/android/libraries/feed/piet/ImageElementAdapterTest.java
index 7560d78..e7cb9db 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/ImageElementAdapterTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/ImageElementAdapterTest.java
@@ -41,8 +41,8 @@
 import com.google.android.libraries.feed.common.ui.LayoutUtils;
 import com.google.android.libraries.feed.piet.PietStylesHelper.PietStylesHelperFactory;
 import com.google.android.libraries.feed.piet.host.AssetProvider;
+import com.google.android.libraries.feed.piet.ui.BitmapMaskingRoundedCornerWrapperView;
 import com.google.android.libraries.feed.piet.ui.RoundedCornerMaskCache;
-import com.google.android.libraries.feed.piet.ui.RoundedCornerWrapperView;
 import com.google.search.now.ui.piet.BindingRefsProto.ImageBindingRef;
 import com.google.search.now.ui.piet.BindingRefsProto.StyleBindingRef;
 import com.google.search.now.ui.piet.ElementsProto.BindingValue;
@@ -79,6 +79,7 @@
       Image.newBuilder().addSources(ImageSource.newBuilder().setUrl("icanhas.chz")).build();
   private static final Element DEFAULT_MODEL =
       asElement(ImageElement.newBuilder().setImage(DEFAULT_IMAGE).build());
+  private static final boolean LEGACY_CORNERS_FLAG = false;
 
   @Mock private ElementAdapterFactory adapterFactory;
   @Mock private TemplateBinder templateBinder;
@@ -114,7 +115,8 @@
             templateBinder,
             clock,
             new PietStylesHelperFactory(),
-            maskCache);
+            maskCache,
+            LEGACY_CORNERS_FLAG);
 
     when(frameContext.makeStyleFor(any(StyleIdsStack.class))).thenReturn(styleProvider);
     when(frameContext.filterImageSourcesByMediaQueryCondition(any(Image.class)))
@@ -124,9 +126,9 @@
     when(styleProvider.hasRoundedCorners()).thenReturn(true);
     when(styleProvider.getRoundedCorners()).thenReturn(CORNERS);
     when(styleProvider.getScaleType()).thenReturn(ScaleType.FIT_CENTER);
-    when(styleProvider.createWrapperView(context, maskCache))
+    when(styleProvider.createWrapperView(context, maskCache, LEGACY_CORNERS_FLAG))
         .thenReturn(
-            new RoundedCornerWrapperView(
+            new BitmapMaskingRoundedCornerWrapperView(
                 context,
                 CORNERS,
                 maskCache,
diff --git a/src/test/java/com/google/android/libraries/feed/piet/ParameterizedTextElementAdapterFactoryTest.java b/src/test/java/com/google/android/libraries/feed/piet/ParameterizedTextElementAdapterFactoryTest.java
index caa22aa..616a25e 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/ParameterizedTextElementAdapterFactoryTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/ParameterizedTextElementAdapterFactoryTest.java
@@ -61,7 +61,11 @@
     context = Robolectric.buildActivity(Activity.class).get();
     adapterParameters =
         new AdapterParameters(
-            null, null, new HostProviders(mock(AssetProvider.class), null, null), new FakeClock());
+            null,
+            null,
+            new HostProviders(mock(AssetProvider.class), null, null),
+            new FakeClock(),
+            false);
     when(keySupplier.getAdapter(context, adapterParameters))
         .thenReturn(adapter)
         .thenReturn(adapter2);
diff --git a/src/test/java/com/google/android/libraries/feed/piet/PietManagerImplTest.java b/src/test/java/com/google/android/libraries/feed/piet/PietManagerImplTest.java
index b517852..d68c9cd 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/PietManagerImplTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/PietManagerImplTest.java
@@ -235,7 +235,8 @@
             mockTemplateBinder,
             new FakeClock(),
             stylesHelpers,
-            maskCache);
+            maskCache,
+            false);
     pietManager.purgeRecyclerPools();
     verify(mockFactory).purgeRecyclerPools();
     verify(stylesHelpers).purge();
diff --git a/src/test/java/com/google/android/libraries/feed/piet/StyleProviderTest.java b/src/test/java/com/google/android/libraries/feed/piet/StyleProviderTest.java
index 5674777..a8c9304 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/StyleProviderTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/StyleProviderTest.java
@@ -26,19 +26,23 @@
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.ShapeDrawable;
+import android.os.Build;
 import android.os.Build.VERSION_CODES;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewGroup.MarginLayoutParams;
+import android.view.ViewOutlineProvider;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
 import com.google.android.libraries.feed.common.functional.Suppliers;
 import com.google.android.libraries.feed.common.ui.LayoutUtils;
 import com.google.android.libraries.feed.piet.host.AssetProvider;
+import com.google.android.libraries.feed.piet.ui.BitmapMaskingRoundedCornerWrapperView;
 import com.google.android.libraries.feed.piet.ui.BorderDrawable;
 import com.google.android.libraries.feed.piet.ui.GradientDrawable;
+import com.google.android.libraries.feed.piet.ui.LegacyRoundedCornerWrapperView;
 import com.google.android.libraries.feed.piet.ui.RoundedCornerMaskCache;
 import com.google.android.libraries.feed.piet.ui.RoundedCornerWrapperView;
 import com.google.search.now.ui.piet.GradientsProto.ColorStop;
@@ -399,8 +403,9 @@
                 .build(),
             mockAssetProvider);
 
-    assertThat(styleProvider.createWrapperView(context, maskCache))
-        .isNotInstanceOf(RoundedCornerWrapperView.class);
+    FrameLayout wrapperView = styleProvider.createWrapperView(context, maskCache, false);
+    assertThat(wrapperView).isNotInstanceOf(RoundedCornerWrapperView.class);
+    assertThat(wrapperView.getOutlineProvider()).isEqualTo(ViewOutlineProvider.BOUNDS);
   }
 
   @Test
@@ -412,8 +417,37 @@
                 .build(),
             mockAssetProvider);
 
-    assertThat(styleProvider.createWrapperView(context, maskCache))
-        .isInstanceOf(RoundedCornerWrapperView.class);
+    FrameLayout wrapperView = styleProvider.createWrapperView(context, maskCache, false);
+    assertThat(wrapperView).isInstanceOf(RoundedCornerWrapperView.class);
+    assertThat(wrapperView.getOutlineProvider()).isNotNull();
+  }
+
+  @Test
+  @Config(sdk = {Build.VERSION_CODES.KITKAT})
+  public void testCreateWrapperView_legacyButFlagOff() {
+    StyleProvider styleProvider =
+        new StyleProvider(
+            Style.newBuilder()
+                .setRoundedCorners(RoundedCorners.newBuilder().setRadius(9).setBitmask(4))
+                .build(),
+            mockAssetProvider);
+
+    assertThat(styleProvider.createWrapperView(context, maskCache, false))
+        .isInstanceOf(BitmapMaskingRoundedCornerWrapperView.class);
+  }
+
+  @Test
+  @Config(sdk = {Build.VERSION_CODES.KITKAT})
+  public void testCreateWrapperView_legacyWithFlagOn() {
+    StyleProvider styleProvider =
+        new StyleProvider(
+            Style.newBuilder()
+                .setRoundedCorners(RoundedCorners.newBuilder().setRadius(9).setBitmask(4))
+                .build(),
+            mockAssetProvider);
+
+    assertThat(styleProvider.createWrapperView(context, maskCache, true))
+        .isInstanceOf(LegacyRoundedCornerWrapperView.class);
   }
 
   @Test
@@ -455,32 +489,32 @@
     StyleProvider styleProvider = new StyleProvider(style, mockAssetProvider);
     styleProvider.applyElementStyles(adapter);
 
-    ColorDrawable colorDrawable = (ColorDrawable) view.getBackground();
+    ColorDrawable colorDrawable = (ColorDrawable) baseView.getBackground();
     assertThat(colorDrawable.getColor()).isEqualTo(color);
   }
 
   @Test
   public void testElementStyles_noBackgroundInStyle() {
-    view.setBackground(new ColorDrawable(0xffff0000));
+    baseView.setBackground(new ColorDrawable(0xffff0000));
 
     new StyleProvider(Style.getDefaultInstance(), mockAssetProvider).applyElementStyles(adapter);
-    assertThat(view.getBackground()).isNull();
+    assertThat(baseView.getBackground()).isNull();
   }
 
   @Test
   public void testElementStyles_noColorInFill() {
-    view.setBackground(new ColorDrawable(0xffff0000));
+    baseView.setBackground(new ColorDrawable(0xffff0000));
 
     Style style = Style.newBuilder().setBackground(Fill.getDefaultInstance()).build();
 
     new StyleProvider(style, mockAssetProvider).applyElementStyles(adapter);
 
-    assertThat(view.getBackground()).isNull();
+    assertThat(baseView.getBackground()).isNull();
   }
 
   @Test
   public void testElementStyles_gradientBackground() {
-    view.setBackground(new ColorDrawable(0xffff0000));
+    baseView.setBackground(new ColorDrawable(0xffff0000));
 
     Style style =
         Style.newBuilder()
@@ -505,13 +539,13 @@
 
     new StyleProvider(style, mockAssetProvider).applyElementStyles(adapter);
 
-    Drawable background = view.getBackground();
+    Drawable background = baseView.getBackground();
     assertThat(background).isInstanceOf(GradientDrawable.class);
   }
 
   @Test
   public void testElementStyles_gradientBackground_badAngle_doesntCrash() {
-    view.setBackground(new ColorDrawable(0xffff0000));
+    baseView.setBackground(new ColorDrawable(0xffff0000));
 
     Style style =
         Style.newBuilder()
@@ -530,13 +564,13 @@
 
     new StyleProvider(style, mockAssetProvider).applyElementStyles(adapter);
 
-    Drawable background = view.getBackground();
+    Drawable background = baseView.getBackground();
     assertThat(background).isInstanceOf(GradientDrawable.class);
   }
 
   @Test
   public void testElementStyles_gradientBackground_badPercent_doesntCrash() {
-    view.setBackground(new ColorDrawable(0xffff0000));
+    baseView.setBackground(new ColorDrawable(0xffff0000));
 
     Style style =
         Style.newBuilder()
@@ -555,7 +589,7 @@
 
     new StyleProvider(style, mockAssetProvider).applyElementStyles(adapter);
 
-    Drawable background = view.getBackground();
+    Drawable background = baseView.getBackground();
     assertThat(background).isInstanceOf(GradientDrawable.class);
   }
 
@@ -677,8 +711,8 @@
     assertThat(baseView.getElevation()).isWithin(0.1f).of(0.0f);
     assertThat(view.getElevation()).isWithin(0.1f).of(5.0f);
 
-    assertThat(baseView.getBackground()).isNull();
-    assertThat(view.getBackground()).isInstanceOf(ColorDrawable.class);
+    assertThat(view.getBackground()).isNull();
+    assertThat(baseView.getBackground()).isInstanceOf(ColorDrawable.class);
   }
 
   @Test
diff --git a/src/test/java/com/google/android/libraries/feed/piet/TemplateBinderTest.java b/src/test/java/com/google/android/libraries/feed/piet/TemplateBinderTest.java
index 5cf87c2..e5f3b7c 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/TemplateBinderTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/TemplateBinderTest.java
@@ -307,7 +307,8 @@
             mockHostProviders,
             new FrameLayout(context));
     AdapterParameters adapterParameters =
-        new AdapterParameters(context, Suppliers.of(null), mockHostProviders, new FakeClock());
+        new AdapterParameters(
+            context, Suppliers.of(null), mockHostProviders, new FakeClock(), false);
     TemplateBinder templateBinder = adapterParameters.templateBinder;
 
     // Create and bind adapter
diff --git a/src/test/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperViewTest.java b/src/test/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperViewTest.java
index bdcc77b..8182526 100644
--- a/src/test/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperViewTest.java
+++ b/src/test/java/com/google/android/libraries/feed/piet/ui/RoundedCornerWrapperViewTest.java
@@ -61,7 +61,7 @@
   @Test
   public void testZeroDimensions_notRounded() {
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new BitmapMaskingRoundedCornerWrapperView(
             context,
             RoundedCorners.getDefaultInstance(),
             maskCache,
@@ -91,7 +91,7 @@
     RoundedCorners roundedCorners =
         RoundedCorners.newBuilder().setBitmask(7).setRadiusDp(10).build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new BitmapMaskingRoundedCornerWrapperView(
             context,
             roundedCorners,
             maskCache,
@@ -124,7 +124,7 @@
 
     RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(10).build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new BitmapMaskingRoundedCornerWrapperView(
             context, roundedCorners, maskCache, Suppliers.of(false), 0, borders);
 
     view.layout(0, 0, 100, 100);
@@ -142,7 +142,7 @@
 
     RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(10).build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new BitmapMaskingRoundedCornerWrapperView(
             context, roundedCorners, maskCache, Suppliers.of(false), 0, borders);
 
     // Set a width and height on the view so that the only thing stopping borders from being created
@@ -162,10 +162,9 @@
     int radiusOverride = 20;
     RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(radiusToSet).build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new CommonRoundedCornerWrapperView(
             context,
             roundedCorners,
-            maskCache,
             Suppliers.of(false),
             radiusOverride,
             Borders.getDefaultInstance());
@@ -183,10 +182,9 @@
     int radiusToSet = 10;
     RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(radiusToSet).build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new CommonRoundedCornerWrapperView(
             context,
             roundedCorners,
-            maskCache,
             Suppliers.of(false),
             /*radiusOverride= */ 0,
             Borders.getDefaultInstance());
@@ -204,10 +202,9 @@
     RoundedCorners roundedCorners =
         RoundedCorners.newBuilder().setRadiusPercentageOfHeight(radiusPercentageOfHeight).build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new CommonRoundedCornerWrapperView(
             context,
             roundedCorners,
-            maskCache,
             Suppliers.of(false),
             /*radiusOverride= */ 0,
             Borders.getDefaultInstance());
@@ -226,10 +223,9 @@
     RoundedCorners roundedCorners =
         RoundedCorners.newBuilder().setRadiusPercentageOfWidth(radiusPercentageOfWidth).build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new CommonRoundedCornerWrapperView(
             context,
             roundedCorners,
-            maskCache,
             Suppliers.of(false),
             /*radiusOverride= */ 0,
             Borders.getDefaultInstance());
@@ -247,10 +243,9 @@
     int radiusToSet = 20;
     RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(radiusToSet).build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new CommonRoundedCornerWrapperView(
             context,
             roundedCorners,
-            maskCache,
             Suppliers.of(false),
             /*radiusOverride= */ 0,
             Borders.getDefaultInstance());
@@ -273,10 +268,9 @@
             .setRadiusDp(radiusToSet)
             .build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new CommonRoundedCornerWrapperView(
             context,
             roundedCorners,
-            maskCache,
             Suppliers.of(false),
             /*radiusOverride= */ 0,
             Borders.getDefaultInstance());
@@ -295,10 +289,9 @@
     int radiusToSet = 20;
     RoundedCorners roundedCorners = RoundedCorners.newBuilder().setRadiusDp(radiusToSet).build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new CommonRoundedCornerWrapperView(
             context,
             roundedCorners,
-            maskCache,
             Suppliers.of(false),
             /*radiusOverride= */ 0,
             Borders.getDefaultInstance());
@@ -321,10 +314,9 @@
             .setRadiusDp(radiusToSet)
             .build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new CommonRoundedCornerWrapperView(
             context,
             roundedCorners,
-            maskCache,
             Suppliers.of(false),
             /*radiusOverride= */ 0,
             Borders.getDefaultInstance());
@@ -344,10 +336,9 @@
     RoundedCorners roundedCorners =
         RoundedCorners.newBuilder().setRadiusPercentageOfHeight(radiusPercentageOfHeight).build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new CommonRoundedCornerWrapperView(
             context,
             roundedCorners,
-            maskCache,
             Suppliers.of(false),
             /*radiusOverride= */ 0,
             Borders.getDefaultInstance());
@@ -369,10 +360,9 @@
             .setRadiusPercentageOfHeight(radiusPercentageOfHeight)
             .build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new CommonRoundedCornerWrapperView(
             context,
             roundedCorners,
-            maskCache,
             Suppliers.of(false),
             /*radiusOverride= */ 0,
             Borders.getDefaultInstance());
@@ -395,10 +385,9 @@
             .setRadiusPercentageOfHeight(radiusPercentageOfHeight)
             .build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new CommonRoundedCornerWrapperView(
             context,
             roundedCorners,
-            maskCache,
             Suppliers.of(false),
             /*radiusOverride= */ 0,
             Borders.getDefaultInstance());
@@ -418,10 +407,9 @@
     RoundedCorners roundedCorners =
         RoundedCorners.newBuilder().setRadiusPercentageOfWidth(radiusPercentageOfWidth).build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new CommonRoundedCornerWrapperView(
             context,
             roundedCorners,
-            maskCache,
             Suppliers.of(false),
             /*radiusOverride= */ 0,
             Borders.getDefaultInstance());
@@ -444,10 +432,9 @@
             .setRadiusPercentageOfWidth(radiusPercentageOfWidth)
             .build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new CommonRoundedCornerWrapperView(
             context,
             roundedCorners,
-            maskCache,
             Suppliers.of(false),
             /*radiusOverride= */ 0,
             Borders.getDefaultInstance());
@@ -470,10 +457,9 @@
             .setRadiusPercentageOfWidth(radiusPercentageOfWidth)
             .build();
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new CommonRoundedCornerWrapperView(
             context,
             roundedCorners,
-            maskCache,
             Suppliers.of(false),
             /*radiusOverride= */ 0,
             Borders.getDefaultInstance());
@@ -497,7 +483,7 @@
     when(mockMaskCache.getMasks(16)).thenReturn(mockBitmaps);
     when(mockBitmaps.get(anyInt())).thenReturn(maskBitmap);
     RoundedCornerWrapperView view =
-        new RoundedCornerWrapperView(
+        new BitmapMaskingRoundedCornerWrapperView(
             context,
             roundedCorners,
             mockMaskCache,
@@ -513,4 +499,15 @@
     verify(mockBitmaps).get(Corner.BOTTOM_LEFT);
     verifyNoMoreInteractions(mockBitmaps);
   }
+
+  static class CommonRoundedCornerWrapperView extends RoundedCornerWrapperView {
+    CommonRoundedCornerWrapperView(
+        Context context,
+        RoundedCorners roundedCorners,
+        Supplier<Boolean> isRtLSupplier,
+        int radiusOverride,
+        Borders borders) {
+      super(context, roundedCorners, isRtLSupplier, radiusOverride, borders);
+    }
+  }
 }