Add MakeCrossContextFromPixmap

This operates just like MakeCrossContextFromEncoded, but starting from
raster data. This version is defensive (always uses copies if a raster
image needs to be made).

Bug: skia:
Change-Id: Ibc2b9a235c89a41fbbfd022d943f15ac212d0677
Reviewed-on: https://skia-review.googlesource.com/68205
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/gm/crosscontextimage.cpp b/gm/crosscontextimage.cpp
index cf973db..ff56a28 100644
--- a/gm/crosscontextimage.cpp
+++ b/gm/crosscontextimage.cpp
@@ -12,7 +12,7 @@
 #include "GrContext.h"
 #include "SkImage.h"
 
-DEF_SIMPLE_GM(cross_context_image, canvas, 512 + 512 + 30, 512 + 128 + 30) {
+DEF_SIMPLE_GM(cross_context_image, canvas, 512 * 3 + 60, 512 + 128 + 30) {
     GrContext* context = canvas->getGrContext();
     if (!context) {
         skiagm::GM::DrawGpuOnlyMessage(canvas);
@@ -25,15 +25,26 @@
     canvas->drawImage(encodedImage, 10, 10);
 
     sk_sp<SkImage> crossContextImage = SkImage::MakeCrossContextFromEncoded(
-        context, encodedData, false, canvas->imageInfo().colorSpace());
+            context, encodedData, false, canvas->imageInfo().colorSpace());
     canvas->drawImage(crossContextImage, 512 + 30, 10);
 
+    SkBitmap bmp;
+    SkPixmap pixmap;
+    SkAssertResult(encodedImage->asLegacyBitmap(&bmp, SkImage::kRO_LegacyBitmapMode) &&
+                   bmp.peekPixels(&pixmap));
+
+    sk_sp<SkImage> crossContextRaster = SkImage::MakeCrossContextFromPixmap(
+            context, pixmap, false, canvas->imageInfo().colorSpace());
+    canvas->drawImage(crossContextRaster, 512 + 512 + 60, 10);
+
     SkIRect subset = SkIRect::MakeXYWH(256 - 64, 256 - 64, 128, 128);
     sk_sp<SkImage> encodedSubset = encodedImage->makeSubset(subset);
     sk_sp<SkImage> crossContextSubset = crossContextImage->makeSubset(subset);
+    sk_sp<SkImage> crossContextRasterSubset = crossContextRaster->makeSubset(subset);
 
     canvas->drawImage(encodedSubset, 10, 512 + 30);
     canvas->drawImage(crossContextSubset, 512 + 30, 512 + 30);
+    canvas->drawImage(crossContextRasterSubset, 512 + 512 + 60, 512 + 30);
 }
 
 #endif
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
index 540f099..1d725c2 100644
--- a/include/core/SkImage.h
+++ b/include/core/SkImage.h
@@ -136,8 +136,23 @@
      *  from the encoded data.
      */
     static sk_sp<SkImage> MakeCrossContextFromEncoded(GrContext* context, sk_sp<SkData> data,
-                                                      bool buildMips,
-                                                      SkColorSpace* dstColorSpace);
+                                                      bool buildMips, SkColorSpace* dstColorSpace);
+
+    /**
+     *  Uploads the pixmap to a GPU backed image using the supplied GrContext.
+     *  That image can be safely used by other GrContexts, across thread boundaries. The GrContext
+     *  used here, and the ones used to draw this image later must be in the same GL share group,
+     *  or otherwise be able to share resources.
+     *
+     *  When the image's ref count reaches zero, the original GrContext will destroy the texture,
+     *  asynchronously.
+     *
+     *  The texture will be processed to be suitable for use with surfaces that have the
+     *  supplied destination color space. The color space of the image itself will be determined
+     *  from the pixmap.
+     */
+    static sk_sp<SkImage> MakeCrossContextFromPixmap(GrContext* context, const SkPixmap& pixmap,
+                                                     bool buildMips, SkColorSpace* dstColorSpace);
 
     /**
      *  Create a new image from the specified descriptor. Note - Skia will delete or recycle the
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index d279188..b829f64 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -508,6 +508,41 @@
     return SkImage::MakeFromGenerator(std::move(gen));
 }
 
+sk_sp<SkImage> SkImage::MakeCrossContextFromPixmap(GrContext* context, const SkPixmap& pixmap,
+                                                   bool buildMips, SkColorSpace* dstColorSpace) {
+    // Some backends or drivers don't support (safely) moving resources between contexts
+    if (!context || !context->caps()->crossContextTextureSupport()) {
+        return SkImage::MakeRasterCopy(pixmap);
+    }
+
+    // Turn the pixmap into a GrTextureProxy
+    sk_sp<GrTextureProxy> proxy;
+    if (buildMips) {
+        SkBitmap bmp;
+        bmp.installPixels(pixmap);
+        proxy = GrGenerateMipMapsAndUploadToTextureProxy(context, bmp, dstColorSpace);
+    } else {
+        proxy = GrUploadPixmapToTextureProxy(context->resourceProvider(), pixmap, SkBudgeted::kYes,
+                                             dstColorSpace);
+    }
+
+    if (!proxy) {
+        return SkImage::MakeRasterCopy(pixmap);
+    }
+
+    sk_sp<GrTexture> texture = sk_ref_sp(proxy->priv().peekTexture());
+
+    // Flush any writes or uploads
+    context->contextPriv().prepareSurfaceForExternalIO(proxy.get());
+
+    sk_sp<GrSemaphore> sema = context->getGpu()->prepareTextureForCrossContextUsage(texture.get());
+
+    auto gen = GrBackendTextureImageGenerator::Make(std::move(texture), proxy->origin(),
+                                                    std::move(sema), pixmap.alphaType(),
+                                                    pixmap.info().refColorSpace());
+    return SkImage::MakeFromGenerator(std::move(gen));
+}
+
 #if defined(SK_BUILD_FOR_ANDROID) && __ANDROID_API__ >= 26
 sk_sp<SkImage> SkImage::MakeFromAHardwareBuffer(AHardwareBuffer* graphicBuffer, SkAlphaType at,
                                                sk_sp<SkColorSpace> cs) {
diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp
index 8eecbbd..49ef868 100644
--- a/tests/ImageTest.cpp
+++ b/tests/ImageTest.cpp
@@ -810,12 +810,10 @@
     ctxInfo.grContext()->getGpu()->deleteTestingOnlyBackendTexture(backendTexHandle);
 }
 
-DEF_GPUTEST(SkImage_MakeCrossContextRelease, reporter, /*factory*/) {
+static void test_cross_context_image(skiatest::Reporter* reporter,
+                                     std::function<sk_sp<SkImage>(GrContext*)> imageMaker) {
     GrContextFactory testFactory;
 
-    sk_sp<SkData> data = GetResourceAsData("mandrill_128.png");
-    SkASSERT(data.get());
-
     for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) {
         GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i);
         ContextInfo ctxInfo = testFactory.getContextInfo(ctxType);
@@ -841,7 +839,7 @@
 
         // Case #1: Create image, free image
         {
-            sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+            sk_sp<SkImage> refImg(imageMaker(ctx));
             refImg.reset(nullptr); // force a release of the image
         }
 
@@ -851,7 +849,7 @@
 
         // Case #2: Create image, draw, flush, free image
         {
-            sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+            sk_sp<SkImage> refImg(imageMaker(ctx));
 
             canvas->drawImage(refImg, 0, 0);
             canvas->flush();
@@ -861,7 +859,7 @@
 
         // Case #3: Create image, draw, free image, flush
         {
-            sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+            sk_sp<SkImage> refImg(imageMaker(ctx));
 
             canvas->drawImage(refImg, 0, 0);
             refImg.reset(nullptr); // force a release of the image
@@ -887,7 +885,7 @@
         // Case #4: Create image, draw*, flush*, free image
         {
             testContext->makeCurrent();
-            sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+            sk_sp<SkImage> refImg(imageMaker(ctx));
 
             otherTestContext->makeCurrent();
             canvas->drawImage(refImg, 0, 0);
@@ -900,7 +898,7 @@
         // Case #5: Create image, draw*, free image, flush*
         {
             testContext->makeCurrent();
-            sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+            sk_sp<SkImage> refImg(imageMaker(ctx));
 
             otherTestContext->makeCurrent();
             canvas->drawImage(refImg, 0, 0);
@@ -923,7 +921,7 @@
         // Case #6: Verify that only one context can be using the image at a time
         {
             testContext->makeCurrent();
-            sk_sp<SkImage> refImg(SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr));
+            sk_sp<SkImage> refImg(imageMaker(ctx));
 
             // Any context should be able to borrow the texture at this point
             sk_sp<SkColorSpace> texColorSpace;
@@ -960,6 +958,25 @@
     }
 }
 
+DEF_GPUTEST(SkImage_MakeCrossContextFromEncodedRelease, reporter, /*factory*/) {
+    sk_sp<SkData> data = GetResourceAsData("mandrill_128.png");
+    SkASSERT(data.get());
+
+    test_cross_context_image(reporter, [&data](GrContext* ctx) {
+        return SkImage::MakeCrossContextFromEncoded(ctx, data, false, nullptr);
+    });
+}
+
+DEF_GPUTEST(SkImage_MakeCrossContextFromPixmapRelease, reporter, /*factory*/) {
+    SkBitmap bitmap;
+    SkPixmap pixmap;
+    SkAssertResult(GetResourceAsBitmap("mandrill_128.png", &bitmap) && bitmap.peekPixels(&pixmap));
+
+    test_cross_context_image(reporter, [&pixmap](GrContext* ctx) {
+        return SkImage::MakeCrossContextFromPixmap(ctx, pixmap, false, nullptr);
+    });
+}
+
 static void check_images_same(skiatest::Reporter* reporter, const SkImage* a, const SkImage* b) {
     if (a->width() != b->width() || a->height() != b->height()) {
         ERRORF(reporter, "Images must have the same size");