Pass Bitmap instead of SkBitmap for bitmap rect operation
Test: refactoring cl.
bug:32216791

Change-Id: I66d19194c57b3aa2c400aa87acffc774a533776a
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index f35d605..f2ae847 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -631,13 +631,15 @@
 }
 
 void FrameBuilder::deferVectorDrawableOp(const VectorDrawableOp& op) {
-    const SkBitmap& bitmap = op.vectorDrawable->getBitmapUpdateIfDirty();
+    SkBitmap bitmap;
+    op.vectorDrawable->getBitmapUpdateIfDirty().getSkBitmap(&bitmap);
+    SkBitmap* localBitmap = mAllocator.create<SkBitmap>(bitmap);
     SkPaint* paint = op.vectorDrawable->getPaint();
     const BitmapRectOp* resolvedOp = mAllocator.create_trivial<BitmapRectOp>(op.unmappedBounds,
             op.localMatrix,
             op.localClip,
             paint,
-            &bitmap,
+            localBitmap,
             Rect(bitmap.width(), bitmap.height()));
     deferBitmapRectOp(*resolvedOp);
 }
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 2730ed9..d252026 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -478,8 +478,10 @@
     restore();
 }
 
-void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+void RecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, const SkMatrix& matrix,
                             const SkPaint* paint) {
+    SkBitmap bitmap;
+    hwuiBitmap.getSkBitmap(&bitmap);
     if (matrix.isIdentity()) {
         drawBitmap(&bitmap, paint);
     } else if (!(matrix.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask))
@@ -490,7 +492,7 @@
         SkRect dst;
         bitmap.getBounds(&src);
         matrix.mapRect(&dst, src);
-        drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
+        drawBitmap(hwuiBitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
                    dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
     } else {
         save(SaveFlags::Matrix);
@@ -500,9 +502,11 @@
     }
 }
 
-void RecordingCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+void RecordingCanvas::drawBitmap(Bitmap& hwuiBitmap, float srcLeft, float srcTop,
             float srcRight, float srcBottom, float dstLeft, float dstTop,
             float dstRight, float dstBottom, const SkPaint* paint) {
+    SkBitmap bitmap;
+    hwuiBitmap.getSkBitmap(&bitmap);
     if (srcLeft == 0 && srcTop == 0
             && srcRight == bitmap.width()
             && srcBottom == bitmap.height()
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index c5ed61c..cdb8dfc2 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -177,9 +177,8 @@
 
     // Bitmap-based
     virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override;
-    virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
-                            const SkPaint* paint) override;
-    virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+    virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override;
+    virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
             float srcRight, float srcBottom, float dstLeft, float dstTop,
             float dstRight, float dstBottom, const SkPaint* paint) override;
     virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index c99219f..ab899f9 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -497,15 +497,19 @@
     mCanvas->drawBitmap(skBitmap, left, top, paint);
 }
 
-void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) {
+void SkiaCanvas::drawBitmap(Bitmap& hwuiBitmap, const SkMatrix& matrix, const SkPaint* paint) {
+    SkBitmap bitmap;
+    hwuiBitmap.getSkBitmap(&bitmap);
     SkAutoCanvasRestore acr(mCanvas.get(), true);
     mCanvas->concat(matrix);
     mCanvas->drawBitmap(bitmap, 0, 0, paint);
 }
 
-void SkiaCanvas::drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+void SkiaCanvas::drawBitmap(Bitmap& hwuiBitmap, float srcLeft, float srcTop,
                             float srcRight, float srcBottom, float dstLeft, float dstTop,
                             float dstRight, float dstBottom, const SkPaint* paint) {
+    SkBitmap bitmap;
+    hwuiBitmap.getSkBitmap(&bitmap);
     SkRect srcRect = SkRect::MakeLTRB(srcLeft, srcTop, srcRight, srcBottom);
     SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
     mCanvas->drawBitmapRect(bitmap, srcRect, dstRect, paint);
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 9c4d7c4..49aea8e 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -125,9 +125,8 @@
             const uint16_t* indices, int indexCount, const SkPaint& paint) override;
 
     virtual void drawBitmap(Bitmap& bitmap, float left, float top, const SkPaint* paint) override;
-    virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
-            const SkPaint* paint) override;
-    virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+    virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const SkPaint* paint) override;
+    virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
             float srcRight, float srcBottom, float dstLeft, float dstTop,
             float dstRight, float dstBottom, const SkPaint* paint) override;
     virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
diff --git a/libs/hwui/SkiaCanvasProxy.cpp b/libs/hwui/SkiaCanvasProxy.cpp
index 6620458..863146e 100644
--- a/libs/hwui/SkiaCanvasProxy.cpp
+++ b/libs/hwui/SkiaCanvasProxy.cpp
@@ -107,16 +107,12 @@
 
 void SkiaCanvasProxy::onDrawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
         const SkPaint* paint) {
-    SkPixelRef* pxRef = bitmap.pixelRef();
-
+    sk_sp<Bitmap> hwuiBitmap = Bitmap::createFrom(bitmap.info(), *bitmap.pixelRef());
     // HWUI doesn't support extractSubset(), so convert any subsetted bitmap into
     // a drawBitmapRect(); pass through an un-subsetted bitmap.
-    if (pxRef && bitmap.dimensions() != pxRef->info().dimensions()) {
-        SkBitmap fullBitmap;
-        fullBitmap.setInfo(pxRef->info());
-        fullBitmap.setPixelRef(pxRef, 0, 0);
+    if (hwuiBitmap && bitmap.dimensions() != hwuiBitmap->info().dimensions()) {
         SkIPoint origin = bitmap.pixelRefOrigin();
-        mCanvas->drawBitmap(fullBitmap, origin.fX, origin.fY,
+        mCanvas->drawBitmap(*hwuiBitmap, origin.fX, origin.fY,
                             origin.fX + bitmap.dimensions().width(),
                             origin.fY + bitmap.dimensions().height(),
                             left, top,
@@ -124,16 +120,16 @@
                             top + bitmap.dimensions().height(),
                             paint);
     } else {
-        auto hwuiBitmap= Bitmap::createFrom(bitmap.info(), *pxRef);
         mCanvas->drawBitmap(*hwuiBitmap, left, top, paint);
     }
 }
 
-void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& bitmap, const SkRect* srcPtr,
+void SkiaCanvasProxy::onDrawBitmapRect(const SkBitmap& skBitmap, const SkRect* srcPtr,
         const SkRect& dst, const SkPaint* paint, SrcRectConstraint) {
-    SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(bitmap.width(), bitmap.height());
+    SkRect src = (srcPtr) ? *srcPtr : SkRect::MakeWH(skBitmap.width(), skBitmap.height());
     // TODO: if bitmap is a subset, do we need to add pixelRefOrigin to src?
-    mCanvas->drawBitmap(bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
+   Bitmap* bitmap = reinterpret_cast<Bitmap*>(skBitmap.pixelRef());
+   mCanvas->drawBitmap(*bitmap, src.fLeft, src.fTop, src.fRight, src.fBottom,
                         dst.fLeft, dst.fTop, dst.fRight, dst.fBottom, paint);
 }
 
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 715681d..b50647a 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -502,18 +502,18 @@
 }
 
 void Tree::drawStaging(Canvas* outCanvas) {
-    bool redrawNeeded = allocateBitmapIfNeeded(&mStagingCache.bitmap,
+    bool redrawNeeded = allocateBitmapIfNeeded(mStagingCache,
             mStagingProperties.getScaledWidth(), mStagingProperties.getScaledHeight());
     // draw bitmap cache
     if (redrawNeeded || mStagingCache.dirty) {
-        updateBitmapCache(&mStagingCache.bitmap, true);
+        updateBitmapCache(*mStagingCache.bitmap, true);
         mStagingCache.dirty = false;
     }
 
     SkPaint tmpPaint;
     SkPaint* paint = updatePaint(&tmpPaint, &mStagingProperties);
-    outCanvas->drawBitmap(mStagingCache.bitmap, 0, 0,
-            mStagingCache.bitmap.width(), mStagingCache.bitmap.height(),
+    outCanvas->drawBitmap(*mStagingCache.bitmap, 0, 0,
+            mStagingCache.bitmap->width(), mStagingCache.bitmap->height(),
             mStagingProperties.getBounds().left(), mStagingProperties.getBounds().top(),
             mStagingProperties.getBounds().right(), mStagingProperties.getBounds().bottom(), paint);
 }
@@ -535,46 +535,46 @@
     }
 }
 
-const SkBitmap& Tree::getBitmapUpdateIfDirty() {
-    bool redrawNeeded = allocateBitmapIfNeeded(&mCache.bitmap, mProperties.getScaledWidth(),
+Bitmap& Tree::getBitmapUpdateIfDirty() {
+    bool redrawNeeded = allocateBitmapIfNeeded(mCache, mProperties.getScaledWidth(),
             mProperties.getScaledHeight());
     if (redrawNeeded || mCache.dirty) {
-        updateBitmapCache(&mCache.bitmap, false);
+        updateBitmapCache(*mCache.bitmap, false);
         mCache.dirty = false;
     }
-    return mCache.bitmap;
+    return *mCache.bitmap;
 }
 
-void Tree::updateBitmapCache(SkBitmap* outCache, bool useStagingData) {
-    outCache->eraseColor(SK_ColorTRANSPARENT);
-    SkCanvas outCanvas(*outCache);
+void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) {
+    SkBitmap outCache;
+    bitmap.getSkBitmap(&outCache);
+    outCache.eraseColor(SK_ColorTRANSPARENT);
+    SkCanvas outCanvas(outCache);
     float viewportWidth = useStagingData ?
             mStagingProperties.getViewportWidth() : mProperties.getViewportWidth();
     float viewportHeight = useStagingData ?
             mStagingProperties.getViewportHeight() : mProperties.getViewportHeight();
-    float scaleX = outCache->width() / viewportWidth;
-    float scaleY = outCache->height() / viewportHeight;
+    float scaleX = outCache.width() / viewportWidth;
+    float scaleY = outCache.height() / viewportHeight;
     mRootNode->draw(&outCanvas, SkMatrix::I(), scaleX, scaleY, useStagingData);
 }
 
-bool Tree::allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height) {
-    if (!canReuseBitmap(*outCache, width, height)) {
+bool Tree::allocateBitmapIfNeeded(Cache& cache, int width, int height) {
+    if (!canReuseBitmap(cache.bitmap.get(), width, height)) {
 #ifndef ANDROID_ENABLE_LINEAR_BLENDING
         sk_sp<SkColorSpace> colorSpace = nullptr;
 #else
         sk_sp<SkColorSpace> colorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
 #endif
         SkImageInfo info = SkImageInfo::MakeN32(width, height, kPremul_SkAlphaType, colorSpace);
-        outCache->setInfo(info);
-        // TODO: Count the bitmap cache against app's java heap
-        outCache->allocPixels(info);
+        cache.bitmap = Bitmap::allocateHeapBitmap(info);
         return true;
     }
     return false;
 }
 
-bool Tree::canReuseBitmap(const SkBitmap& bitmap, int width, int height) {
-    return width == bitmap.width() && height == bitmap.height();
+bool Tree::canReuseBitmap(Bitmap* bitmap, int width, int height) {
+    return bitmap && width == bitmap->width() && height == bitmap->height();
 }
 
 void Tree::onPropertyChanged(TreeProperties* prop) {
diff --git a/libs/hwui/VectorDrawable.h b/libs/hwui/VectorDrawable.h
index e68fbf4..e9a9c71 100644
--- a/libs/hwui/VectorDrawable.h
+++ b/libs/hwui/VectorDrawable.h
@@ -18,6 +18,7 @@
 #define ANDROID_HWUI_VPATH_H
 
 #include "hwui/Canvas.h"
+#include "hwui/Bitmap.h"
 #include "DisplayList.h"
 
 #include <SkBitmap.h>
@@ -561,7 +562,7 @@
             const SkRect& bounds, bool needsMirroring, bool canReuseCache);
     void drawStaging(Canvas* canvas);
 
-    const SkBitmap& getBitmapUpdateIfDirty();
+    Bitmap& getBitmapUpdateIfDirty();
     void setAllowCaching(bool allowCaching) {
         mAllowCaching = allowCaching;
     }
@@ -691,11 +692,15 @@
     void setPropertyChangeWillBeConsumed(bool willBeConsumed) { mWillBeConsumed = willBeConsumed; }
 
 private:
+    struct Cache {
+        sk_sp<Bitmap> bitmap;
+        bool dirty = true;
+    };
 
     SkPaint* updatePaint(SkPaint* outPaint, TreeProperties* prop);
-    bool allocateBitmapIfNeeded(SkBitmap* outCache, int width, int height);
-    bool canReuseBitmap(const SkBitmap&, int width, int height);
-    void updateBitmapCache(SkBitmap* outCache, bool useStagingData);
+    bool allocateBitmapIfNeeded(Cache& cache, int width, int height);
+    bool canReuseBitmap(Bitmap*, int width, int height);
+    void updateBitmapCache(Bitmap& outCache, bool useStagingData);
     // Cap the bitmap size, such that it won't hurt the performance too much
     // and it won't crash due to a very large scale.
     // The drawable will look blurry above this size.
@@ -708,10 +713,6 @@
     TreeProperties mStagingProperties = TreeProperties(this);
 
     SkPaint mPaint;
-    struct Cache {
-        SkBitmap bitmap;
-        bool dirty = true;
-    };
 
     Cache mStagingCache;
     Cache mCache;
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 341ece3..d9534f2 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -23,9 +23,9 @@
 
 namespace android {
 
-static bool computeAllocationSize(const SkBitmap& bitmap, size_t* size) {
-    int32_t rowBytes32 = SkToS32(bitmap.rowBytes());
-    int64_t bigSize = (int64_t)bitmap.height() * rowBytes32;
+static bool computeAllocationSize(size_t rowBytes, int height, size_t* size) {
+    int32_t rowBytes32 = SkToS32(rowBytes);
+    int64_t bigSize = (int64_t) height * rowBytes32;
     if (rowBytes32 < 0 || !sk_64_isS32(bigSize)) {
         return false; // allocation will be too large
     }
@@ -45,13 +45,14 @@
     }
 
     size_t size;
-    if (!computeAllocationSize(*bitmap, &size)) {
-        return nullptr;
-    }
 
     // we must respect the rowBytes value already set on the bitmap instead of
     // attempting to compute our own.
     const size_t rowBytes = bitmap->rowBytes();
+    if (!computeAllocationSize(rowBytes, bitmap->height(), &size)) {
+        return nullptr;
+    }
+
     auto wrapper = alloc(size, info, rowBytes, ctable);
     if (wrapper) {
         wrapper->getSkBitmap(bitmap);
@@ -62,15 +63,11 @@
     return wrapper;
 }
 
-sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable) {
-   return allocateBitmap(bitmap, ctable, &Bitmap::allocateHeapBitmap);
-}
-
 sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(SkBitmap* bitmap, SkColorTable* ctable) {
    return allocateBitmap(bitmap, ctable, &Bitmap::allocateAshmemBitmap);
 }
 
-sk_sp<Bitmap> Bitmap::allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes,
+static sk_sp<Bitmap> allocateHeapBitmap(size_t size, const SkImageInfo& info, size_t rowBytes,
         SkColorTable* ctable) {
     void* addr = calloc(size, 1);
     if (!addr) {
@@ -79,6 +76,19 @@
     return sk_sp<Bitmap>(new Bitmap(addr, size, info, rowBytes, ctable));
 }
 
+sk_sp<Bitmap> Bitmap::allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable) {
+   return allocateBitmap(bitmap, ctable, &android::allocateHeapBitmap);
+}
+
+sk_sp<Bitmap> Bitmap::allocateHeapBitmap(const SkImageInfo& info) {
+    size_t size;
+    if (!computeAllocationSize(info.minRowBytes(), info.height(), &size)) {
+        LOG_ALWAYS_FATAL("trying to allocate too large bitmap");
+        return nullptr;
+    }
+    return android::allocateHeapBitmap(size, info, info.minRowBytes(), nullptr);
+}
+
 sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(size_t size, const SkImageInfo& info,
         size_t rowBytes, SkColorTable* ctable) {
     // Create new ashmem region with read/write priv
diff --git a/libs/hwui/hwui/Bitmap.h b/libs/hwui/hwui/Bitmap.h
index 2a5d00b..029d80d 100644
--- a/libs/hwui/hwui/Bitmap.h
+++ b/libs/hwui/hwui/Bitmap.h
@@ -34,8 +34,7 @@
 class ANDROID_API Bitmap : public SkPixelRef {
 public:
     static sk_sp<Bitmap> allocateHeapBitmap(SkBitmap* bitmap, SkColorTable* ctable);
-    static sk_sp<Bitmap> allocateHeapBitmap(size_t allocSize, const SkImageInfo& info,
-        size_t rowBytes, SkColorTable* ctable);
+    static sk_sp<Bitmap> allocateHeapBitmap(const SkImageInfo& info);
 
     static sk_sp<Bitmap> allocateAshmemBitmap(SkBitmap* bitmap, SkColorTable* ctable);
     static sk_sp<Bitmap> allocateAshmemBitmap(size_t allocSize, const SkImageInfo& info,
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 8d08608..baee8fd 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -220,9 +220,9 @@
     // Bitmap-based
     virtual void drawBitmap(Bitmap& bitmap, float left, float top,
             const SkPaint* paint) = 0;
-    virtual void drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+    virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix,
             const SkPaint* paint) = 0;
-    virtual void drawBitmap(const SkBitmap& bitmap, float srcLeft, float srcTop,
+    virtual void drawBitmap(Bitmap& bitmap, float srcLeft, float srcTop,
             float srcRight, float srcBottom, float dstLeft, float dstTop,
             float dstRight, float dstBottom, const SkPaint* paint) = 0;
     virtual void drawBitmapMesh(const SkBitmap& bitmap, int meshWidth, int meshHeight,
diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h
index cdfa2f4..b8dfb01 100644
--- a/libs/hwui/tests/common/TestUtils.h
+++ b/libs/hwui/tests/common/TestUtils.h
@@ -125,8 +125,7 @@
     static sk_sp<Bitmap> createBitmap(int width, int height,
             SkColorType colorType = kN32_SkColorType) {
         SkImageInfo info = SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType);
-        size_t size = height * info.minRowBytes();
-        return Bitmap::allocateHeapBitmap(size, info, info.minRowBytes(), nullptr);
+        return Bitmap::allocateHeapBitmap(info);
     }
 
     static sk_sp<Bitmap> createBitmap(int width, int height, SkBitmap* outBitmap) {
diff --git a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp
index 584f684..8256024 100644
--- a/libs/hwui/tests/common/scenes/RecentsAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/RecentsAnimation.cpp
@@ -45,12 +45,14 @@
         int x = dp(32);
         for (int i = 0; i < 4; i++) {
             int y = (height / 4) * i;
-            SkBitmap thumb = TestUtils::createSkBitmap(thumbnailSize, thumbnailSize);
-            thumb.eraseColor(COLORS[i]);
-            sp<RenderNode> card = createCard(x, y, cardsize, cardsize, thumb);
+            SkBitmap bitmap;
+            sk_sp<Bitmap> thumb(TestUtils::createBitmap(thumbnailSize, thumbnailSize, &bitmap));
+
+            bitmap.eraseColor(COLORS[i]);
+            sp<RenderNode> card = createCard(x, y, cardsize, cardsize, *thumb);
             card->mutateStagingProperties().setElevation(i * dp(8));
             renderer.drawRenderNode(card.get());
-            mThumbnail = thumb;
+            mThumbnail = bitmap;
             mCards.push_back(card);
         }
 
@@ -68,8 +70,7 @@
     }
 
 private:
-    sp<RenderNode> createCard(int x, int y, int width, int height,
-            const SkBitmap& thumb) {
+    sp<RenderNode> createCard(int x, int y, int width, int height, Bitmap& thumb) {
         return TestUtils::createNode(x, y, x + width, y + height,
                 [&thumb, width, height](RenderProperties& props, Canvas& canvas) {
             props.setElevation(dp(16));