Add createBackendFormat and createFBO0 helper methods to SkSurfaceCharacterization

These make it easier for clients to create new surface characterizations that differ only a little from an existing surface characterization.

Change-Id: Iebd0b32ae941d3f91427927108d092cb5864b09f
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/270444
Commit-Queue: Robert Phillips <robertphillips@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt
index 0b923df..7907549 100644
--- a/RELEASE_NOTES.txt
+++ b/RELEASE_NOTES.txt
@@ -7,6 +7,10 @@
 Milestone 82
 
 <Insert new notes here- top is most recent.>
+  * Added two new helper methods to SkSurfaceCharacterization: createBackendFormat and
+    createFBO0. These make it easier for clients to create new surface characterizations that
+    differ only a little from an existing surface characterization.
+
   * Removed SkTMax and SkTMin.
   * Removed SkTClamp and SkClampMax.
   * Removed SkScalarClampMax and SkScalarPin.
diff --git a/include/core/SkSurfaceCharacterization.h b/include/core/SkSurfaceCharacterization.h
index f259fed..c2cb589 100644
--- a/include/core/SkSurfaceCharacterization.h
+++ b/include/core/SkSurfaceCharacterization.h
@@ -11,13 +11,15 @@
 #include "include/gpu/GrTypes.h"
 
 #include "include/core/SkColorSpace.h"
+#include "include/core/SkImageInfo.h"
 #include "include/core/SkRefCnt.h"
 #include "include/core/SkSurfaceProps.h"
 
 class SkColorSpace;
 
-#if SK_SUPPORT_GPU
 #include "include/gpu/GrBackendSurface.h"
+
+#if SK_SUPPORT_GPU
 // TODO: remove the GrContext.h include once Flutter is updated
 #include "include/gpu/GrContext.h"
 #include "include/gpu/GrContextThreadSafeProxy.h"
@@ -70,6 +72,18 @@
      */
     SkSurfaceCharacterization createColorSpace(sk_sp<SkColorSpace>) const;
 
+    /*
+     * Return a new surface characterization with the backend format replaced. A colorType
+     * must also be supplied to indicate the interpretation of the new format.
+     */
+    SkSurfaceCharacterization createBackendFormat(SkColorType colorType,
+                                                  const GrBackendFormat& backendFormat) const;
+
+    /*
+     * Return a new surface characterization with just a different use of FBO0 (in GL)
+     */
+    SkSurfaceCharacterization createFBO0(bool usesGLFBO0) const;
+
     GrContextThreadSafeProxy* contextInfo() const { return fContextInfo.get(); }
     sk_sp<GrContextThreadSafeProxy> refContextInfo() const { return fContextInfo; }
     size_t cacheMaxResourceBytes() const { return fCacheMaxResourceBytes; }
@@ -200,6 +214,14 @@
         return *this;
     }
 
+    SkSurfaceCharacterization createBackendFormat(SkColorType, const GrBackendFormat&) const {
+        return *this;
+    }
+
+    SkSurfaceCharacterization createFBO0(bool usesGLFBO0) const {
+        return *this;
+    }
+
     bool operator==(const SkSurfaceCharacterization& other) const { return false; }
     bool operator!=(const SkSurfaceCharacterization& other) const {
         return !(*this == other);
diff --git a/include/gpu/GrBackendSurface.h b/include/gpu/GrBackendSurface.h
index 6102ac7..f5c56b2 100644
--- a/include/gpu/GrBackendSurface.h
+++ b/include/gpu/GrBackendSurface.h
@@ -36,6 +36,12 @@
 
 #if !SK_SUPPORT_GPU
 
+// SkSurfaceCharacterization always needs a minimal version of this
+class SK_API GrBackendFormat {
+public:
+    bool isValid() const { return false; }
+};
+
 // SkSurface and SkImage rely on a minimal version of these always being available
 class SK_API GrBackendTexture {
 public:
diff --git a/src/core/SkSurfaceCharacterization.cpp b/src/core/SkSurfaceCharacterization.cpp
index 0d9b5e8..6080fed 100644
--- a/src/core/SkSurfaceCharacterization.cpp
+++ b/src/core/SkSurfaceCharacterization.cpp
@@ -74,6 +74,31 @@
                                      fVulkanSecondaryCBCompatible, fIsProtected, fSurfaceProps);
 }
 
+SkSurfaceCharacterization SkSurfaceCharacterization::createBackendFormat(
+                                                    SkColorType colorType,
+                                                    const GrBackendFormat& backendFormat) const {
+    if (!this->isValid()) {
+        return SkSurfaceCharacterization();
+    }
+
+    SkImageInfo newII = fImageInfo.makeColorType(colorType);
+
+    return SkSurfaceCharacterization(fContextInfo, fCacheMaxResourceBytes, newII, backendFormat,
+                                     fOrigin, fSampleCnt, fIsTextureable, fIsMipMapped, fUsesGLFBO0,
+                                     fVulkanSecondaryCBCompatible, fIsProtected, fSurfaceProps);
+}
+
+SkSurfaceCharacterization SkSurfaceCharacterization::createFBO0(bool usesGLFBO0) const {
+    if (!this->isValid()) {
+        return SkSurfaceCharacterization();
+    }
+
+    return SkSurfaceCharacterization(fContextInfo, fCacheMaxResourceBytes,
+                                     fImageInfo, fBackendFormat,
+                                     fOrigin, fSampleCnt, fIsTextureable, fIsMipMapped,
+                                     usesGLFBO0 ? UsesGLFBO0::kYes : UsesGLFBO0::kNo,
+                                     fVulkanSecondaryCBCompatible, fIsProtected, fSurfaceProps);
+}
 
 bool SkSurfaceCharacterization::isCompatible(const GrBackendTexture& backendTex) const {
     if (!this->isValid() || !backendTex.isValid()) {
diff --git a/tests/DeferredDisplayListTest.cpp b/tests/DeferredDisplayListTest.cpp
index d31fcb6..743f1a6 100644
--- a/tests/DeferredDisplayListTest.cpp
+++ b/tests/DeferredDisplayListTest.cpp
@@ -547,6 +547,78 @@
         s = nullptr;
         params.cleanUpBackEnd(context, backend);
     }
+
+    // Exercise the createBackendFormat method
+    {
+        SurfaceParameters params(context);
+        GrBackendTexture backend;
+
+        sk_sp<SkSurface> s = params.make(context, &backend);
+        if (!s) {
+            return;
+        }
+
+        SkSurfaceCharacterization char0;
+        SkAssertResult(s->characterize(&char0));
+
+        // The default params create a renderable RGBA8 surface
+        auto originalBackendFormat = context->defaultBackendFormat(kRGBA_8888_SkColorType,
+                                                                   GrRenderable::kYes);
+        REPORTER_ASSERT(reporter, originalBackendFormat.isValid());
+        REPORTER_ASSERT(reporter, char0.backendFormat() == originalBackendFormat);
+
+        auto newBackendFormat = context->defaultBackendFormat(kRGB_565_SkColorType,
+                                                              GrRenderable::kYes);
+
+        if (newBackendFormat.isValid()) {
+            SkSurfaceCharacterization char1 = char0.createBackendFormat(kRGB_565_SkColorType,
+                                                                        newBackendFormat);
+            REPORTER_ASSERT(reporter, char1.isValid());
+            REPORTER_ASSERT(reporter, char1.backendFormat() == newBackendFormat);
+
+            SkSurfaceCharacterization invalid;
+            REPORTER_ASSERT(reporter, !invalid.isValid());
+            auto stillInvalid = invalid.createBackendFormat(kRGB_565_SkColorType,
+                                                            newBackendFormat);
+            REPORTER_ASSERT(reporter, !stillInvalid.isValid());
+        }
+
+        s = nullptr;
+        params.cleanUpBackEnd(context, backend);
+    }
+
+    // Exercise the createFBO0 method
+    if (context->backend() == GrBackendApi::kOpenGL) {
+        SurfaceParameters params(context);
+        GrBackendTexture backend;
+
+        sk_sp<SkSurface> s = params.make(context, &backend);
+        if (!s) {
+            return;
+        }
+
+        SkSurfaceCharacterization char0;
+        SkAssertResult(s->characterize(&char0));
+
+        // The default params create a non-FBO0 surface
+        REPORTER_ASSERT(reporter, !char0.usesGLFBO0());
+
+        {
+            SkSurfaceCharacterization char1 = char0.createFBO0(true);
+            REPORTER_ASSERT(reporter, char1.isValid());
+            REPORTER_ASSERT(reporter, char1.usesGLFBO0());
+        }
+
+        {
+            SkSurfaceCharacterization invalid;
+            REPORTER_ASSERT(reporter, !invalid.isValid());
+            SkSurfaceCharacterization stillInvalid = invalid.createFBO0(true);
+            REPORTER_ASSERT(reporter, !stillInvalid.isValid());
+        }
+
+        s = nullptr;
+        params.cleanUpBackEnd(context, backend);
+    }
 }
 
 DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DDLSurfaceCharacterizationTest, reporter, ctxInfo) {