Add SkSurface factory for wrapping an FBO in SkSurface

Review URL: https://codereview.chromium.org/1221853003
diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h
index 5d08ee6..5860cf2 100644
--- a/include/core/SkSurface.h
+++ b/include/core/SkSurface.h
@@ -88,11 +88,27 @@
     }
 
     /**
-     *  Used to wrap a pre-existing backend 3D API texture in a SkSurface. The kRenderTarget flag
-     *  must be set on GrBackendTextureDesc for this to succeed.
+     *  Used to wrap a pre-existing backend 3D API texture as a SkSurface. The kRenderTarget flag
+     *  must be set on GrBackendTextureDesc for this to succeed. Skia will not assume ownership
+     *  of the texture and the client must ensure the texture is valid for the lifetime of the
+     *  SkSurface.
      */
-    static SkSurface* NewWrappedRenderTarget(GrContext*, GrBackendTextureDesc,
-                                             const SkSurfaceProps*);
+    static SkSurface* NewFromBackendTexture(GrContext*, const GrBackendTextureDesc&,
+                                            const SkSurfaceProps*);
+    // Legacy alias
+    static SkSurface* NewWrappedRenderTarget(GrContext* ctx, const GrBackendTextureDesc& desc,
+                                             const SkSurfaceProps* props) {
+        return NewFromBackendTexture(ctx, desc, props);
+    }
+
+
+    /**
+     *  Used to wrap a pre-existing 3D API rendering target as a SkSurface. Skia will not assume
+     *  ownership of the render target and the client must ensure the render target is valid for the
+     *  lifetime of the SkSurface.
+     */
+    static SkSurface* NewFromBackendRenderTarget(GrContext*, const GrBackendRenderTargetDesc&,
+                                                 const SkSurfaceProps*);
 
     /**
      *  Return a new surface whose contents will be drawn to an offscreen
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
index 34035a3..35409be 100644
--- a/src/image/SkSurface.cpp
+++ b/src/image/SkSurface.cpp
@@ -192,9 +192,14 @@
     return NULL;
 }
 
-SkSurface* SkSurface::NewWrappedRenderTarget(GrContext*, GrBackendTextureDesc,
+SkSurface* SkSurface::NewFromBackendTexture(GrContext*, const GrBackendTextureDesc&,
                                              const SkSurfaceProps*) {
     return NULL;
 }
 
+SkSurface* SkSurface::NewFromBackendRenderTarget(GrContext*, const GrBackendRenderTargetDesc&,
+                                                 const SkSurfaceProps*) {
+    return NULL;
+}
+
 #endif
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index d243b65..70866d3 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -116,8 +116,8 @@
     return SkNEW_ARGS(SkSurface_Gpu, (device));
 }
 
-SkSurface* SkSurface::NewWrappedRenderTarget(GrContext* context, GrBackendTextureDesc desc,
-                                             const SkSurfaceProps* props) {
+SkSurface* SkSurface::NewFromBackendTexture(GrContext* context, const GrBackendTextureDesc& desc,
+                                            const SkSurfaceProps* props) {
     if (NULL == context) {
         return NULL;
     }
@@ -137,4 +137,22 @@
     return SkNEW_ARGS(SkSurface_Gpu, (device));
 }
 
+SkSurface* SkSurface::NewFromBackendRenderTarget(GrContext* context,
+                                                 const GrBackendRenderTargetDesc& desc,
+                                                 const SkSurfaceProps* props) {
+    if (NULL == context) {
+        return NULL;
+    }
+    SkAutoTUnref<GrRenderTarget> rt(context->textureProvider()->wrapBackendRenderTarget(desc));
+    if (!rt) {
+        return NULL;
+    }
+    SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(rt, props,
+                                                         SkGpuDevice::kUninit_InitContents));
+    if (!device) {
+        return NULL;
+    }
+    return SkNEW_ARGS(SkSurface_Gpu, (device));
+}
+
 #endif
diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp
index 42f4fcc..43ce203 100644
--- a/tests/SurfaceTest.cpp
+++ b/tests/SurfaceTest.cpp
@@ -102,7 +102,7 @@
 }
 
 #if SK_SUPPORT_GPU
-static void test_wrapped_texture_surface(skiatest::Reporter* reporter, GrContext* ctx) {
+static void test_wrapped_surface(skiatest::Reporter* reporter, GrContext* ctx) {
     if (NULL == ctx) {
         return;
     }
@@ -120,77 +120,107 @@
         return;
     }
 
-    // Test the wrapped factory for SkSurface by creating a texture using GL and then wrap it in
-    // a SkSurface.
-    GrGLuint texID;
-    static const int kW = 100;
-    static const int kH = 100;
-    static const uint32_t kOrigColor = 0xFFAABBCC;
-    SkAutoTArray<uint32_t> pixels(kW * kH);
-    sk_memset32(pixels.get(), kOrigColor, kW * kH);
-    GR_GL_CALL(gl, GenTextures(1, &texID));
-    GR_GL_CALL(gl, ActiveTexture(GR_GL_TEXTURE0));
-    GR_GL_CALL(gl, PixelStorei(GR_GL_UNPACK_ALIGNMENT, 1));
-    GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, texID));
-    GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_MAG_FILTER,
-                                 GR_GL_NEAREST));
-    GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_MIN_FILTER,
-                                 GR_GL_NEAREST));
-    GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_WRAP_S,
-                                 GR_GL_CLAMP_TO_EDGE));
-    GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_WRAP_T,
-                                 GR_GL_CLAMP_TO_EDGE));
-    GR_GL_CALL(gl, TexImage2D(GR_GL_TEXTURE_2D, 0, GR_GL_RGBA, kW, kH, 0, GR_GL_RGBA,
-                              GR_GL_UNSIGNED_BYTE,
-                              pixels.get()));
+    for (int useFBO = 0; useFBO < 2; ++useFBO) {
+        // Test the wrapped factory for SkSurface by creating a texture using GL and then wrap it in
+        // a SkSurface.
+        GrGLuint texID;
+        static const int kW = 100;
+        static const int kH = 100;
+        static const uint32_t kOrigColor = 0xFFAABBCC;
+        SkAutoTArray<uint32_t> pixels(kW * kH);
+        sk_memset32(pixels.get(), kOrigColor, kW * kH);
+        GR_GL_CALL(gl, GenTextures(1, &texID));
+        GR_GL_CALL(gl, ActiveTexture(GR_GL_TEXTURE0));
+        GR_GL_CALL(gl, PixelStorei(GR_GL_UNPACK_ALIGNMENT, 1));
+        GR_GL_CALL(gl, BindTexture(GR_GL_TEXTURE_2D, texID));
+        GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_MAG_FILTER,
+                                     GR_GL_NEAREST));
+        GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_MIN_FILTER,
+                                     GR_GL_NEAREST));
+        GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_WRAP_S,
+                                     GR_GL_CLAMP_TO_EDGE));
+        GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D, GR_GL_TEXTURE_WRAP_T,
+                                     GR_GL_CLAMP_TO_EDGE));
+        GR_GL_CALL(gl, TexImage2D(GR_GL_TEXTURE_2D, 0, GR_GL_RGBA, kW, kH, 0, GR_GL_RGBA,
+                                  GR_GL_UNSIGNED_BYTE,
+                                  pixels.get()));
 
-    GrBackendTextureDesc wrappedDesc;
-    wrappedDesc.fConfig = kRGBA_8888_GrPixelConfig;
-    wrappedDesc.fWidth = kW;
-    wrappedDesc.fHeight = kH;
-    wrappedDesc.fOrigin = kBottomLeft_GrSurfaceOrigin;
-    wrappedDesc.fSampleCnt = 0;
-    wrappedDesc.fFlags = kRenderTarget_GrBackendTextureFlag;
-    wrappedDesc.fTextureHandle = texID;
+        SkAutoTUnref<SkSurface> surface;
+        GrGLuint fboID = 0;
+        if (useFBO) {
+            GR_GL_CALL(gl, GenFramebuffers(1, &fboID));
+            GR_GL_CALL(gl, BindFramebuffer(GR_GL_FRAMEBUFFER, fboID));
+            GR_GL_CALL(gl, FramebufferTexture2D(GR_GL_FRAMEBUFFER, GR_GL_COLOR_ATTACHMENT0,
+                                                GR_GL_TEXTURE_2D, texID, 0));
+            GrBackendRenderTargetDesc wrappedDesc;
+            wrappedDesc.fConfig = kRGBA_8888_GrPixelConfig;
+            wrappedDesc.fWidth = kW;
+            wrappedDesc.fHeight = kH;
+            wrappedDesc.fOrigin = kBottomLeft_GrSurfaceOrigin;
+            wrappedDesc.fSampleCnt = 0;
+            wrappedDesc.fRenderTargetHandle = fboID;
+            wrappedDesc.fStencilBits = 0;
 
-    SkAutoTUnref<SkSurface> surface(SkSurface::NewWrappedRenderTarget(ctx, wrappedDesc, NULL));
-    REPORTER_ASSERT(reporter, surface);
-    if (surface) {
-        // Validate that we can draw to the canvas and that the original texture color is preserved
-        // in pixels that aren't rendered to via the surface.
-        SkPaint paint;
-        static const SkColor kRectColor = ~kOrigColor | 0xFF000000;
-        paint.setColor(kRectColor);
-        surface->getCanvas()->drawRect(SkRect::MakeWH(SkIntToScalar(kW), SkIntToScalar(kH)/2),
-                                       paint);
-        SkImageInfo readInfo = SkImageInfo::MakeN32Premul(kW, kH);
-        surface->readPixels(readInfo, pixels.get(), kW * sizeof(uint32_t), 0, 0);
-        bool stop = false;
-        SkPMColor origColorPM = SkPackARGB32((kOrigColor >> 24 & 0xFF),
-                                             (kOrigColor >>  0 & 0xFF),
-                                             (kOrigColor >>  8 & 0xFF),
-                                             (kOrigColor >> 16 & 0xFF));
-        SkPMColor rectColorPM = SkPackARGB32((kRectColor >> 24 & 0xFF),
-                                             (kRectColor >> 16 & 0xFF),
-                                             (kRectColor >>  8 & 0xFF),
-                                             (kRectColor >>  0 & 0xFF));
-        for (int y = 0; y < kH/2 && !stop; ++y) {
-            for (int x = 0; x < kW && !stop; ++x) {
-                REPORTER_ASSERT(reporter, rectColorPM == pixels[x + y * kW]);
-                if (rectColorPM != pixels[x + y * kW]) {
-                    stop = true;
+            ctx->resetContext();
+            surface.reset(SkSurface::NewFromBackendRenderTarget(ctx, wrappedDesc, NULL));
+        } else {
+            GrBackendTextureDesc wrappedDesc;
+            wrappedDesc.fConfig = kRGBA_8888_GrPixelConfig;
+            wrappedDesc.fWidth = kW;
+            wrappedDesc.fHeight = kH;
+            wrappedDesc.fOrigin = kBottomLeft_GrSurfaceOrigin;
+            wrappedDesc.fSampleCnt = 0;
+            wrappedDesc.fFlags = kRenderTarget_GrBackendTextureFlag;
+            wrappedDesc.fTextureHandle = texID;
+
+            ctx->resetContext();
+            surface.reset(SkSurface::NewFromBackendTexture(ctx, wrappedDesc, NULL));
+        }
+        REPORTER_ASSERT(reporter, surface);
+        if (surface) {
+            // Validate that we can draw to the canvas and that the original texture color is
+            // preserved in pixels that aren't rendered to via the surface.
+            SkPaint paint;
+            static const SkColor kRectColor = ~kOrigColor | 0xFF000000;
+            paint.setColor(kRectColor);
+            surface->getCanvas()->drawRect(SkRect::MakeWH(SkIntToScalar(kW), SkIntToScalar(kH)/2),
+                                           paint);
+            SkImageInfo readInfo = SkImageInfo::MakeN32Premul(kW, kH);
+            surface->readPixels(readInfo, pixels.get(), kW * sizeof(uint32_t), 0, 0);
+            bool stop = false;
+            SkPMColor origColorPM = SkPackARGB32((kOrigColor >> 24 & 0xFF),
+                                                 (kOrigColor >>  0 & 0xFF),
+                                                 (kOrigColor >>  8 & 0xFF),
+                                                 (kOrigColor >> 16 & 0xFF));
+            SkPMColor rectColorPM = SkPackARGB32((kRectColor >> 24 & 0xFF),
+                                                 (kRectColor >> 16 & 0xFF),
+                                                 (kRectColor >>  8 & 0xFF),
+                                                 (kRectColor >>  0 & 0xFF));
+            for (int y = 0; y < kH/2 && !stop; ++y) {
+                for (int x = 0; x < kW && !stop; ++x) {
+                    REPORTER_ASSERT(reporter, rectColorPM == pixels[x + y * kW]);
+                    if (rectColorPM != pixels[x + y * kW]) {
+                        stop = true;
+                    }
+                }
+            }
+            stop = false;
+            for (int y = kH/2; y < kH && !stop; ++y) {
+                for (int x = 0; x < kW && !stop; ++x) {
+                    REPORTER_ASSERT(reporter, origColorPM == pixels[x + y * kW]);
+                    if (origColorPM != pixels[x + y * kW]) {
+                        stop = true;
+                    }
                 }
             }
         }
-        stop = false;
-        for (int y = kH/2; y < kH && !stop; ++y) {
-            for (int x = 0; x < kW && !stop; ++x) {
-                REPORTER_ASSERT(reporter, origColorPM == pixels[x + y * kW]);
-                if (origColorPM != pixels[x + y * kW]) {
-                    stop = true;
-                }
-            }
+        if (texID) {
+            GR_GL_CALL(gl, DeleteTextures(1, &texID));
         }
+        if (fboID) {
+            GR_GL_CALL(gl, DeleteFramebuffers(1, &fboID));
+        }
+
     }
 }
 #endif
@@ -837,7 +867,7 @@
                 TestGetTexture(reporter, kGpuScratch_SurfaceType, context);
                 test_empty_surface(reporter, context);
                 test_surface_budget(reporter, context);
-                test_wrapped_texture_surface(reporter, context);
+                test_wrapped_surface(reporter, context);
             }
         }
     }