Add prescale option to deferred params

Currently, Skia always uploads GPU textures at full resolution. This
change allows us to pass a pre-scale mip level to the
deferred texture image logic, which causes us to pre-scale the image
to the given mip level, and upload that mip level instead of the full
image.
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2007113008

Review-Url: https://codereview.chromium.org/2007113008
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
index 9ad33ea..7f45937 100644
--- a/src/image/SkImage_Gpu.cpp
+++ b/src/image/SkImage_Gpu.cpp
@@ -16,6 +16,7 @@
 #include "SkGrPixelRef.h"
 #include "SkGrPriv.h"
 #include "SkImage_Gpu.h"
+#include "SkMipMap.h"
 #include "SkPixelRef.h"
 
 SkImage_Gpu::SkImage_Gpu(int w, int h, uint32_t uniqueID, SkAlphaType at, GrTexture* tex,
@@ -374,15 +375,44 @@
 };
 
 size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& proxy,
-                                            const DeferredTextureImageUsageParams[],
+                                            const DeferredTextureImageUsageParams params[],
                                             int paramCnt, void* buffer) const {
+    // Extract relevant min/max values from the params array.
+    int lowestPreScaleMipLevel = params[0].fPreScaleMipLevel;
+    SkFilterQuality highestFilterQuality = params[0].fQuality;
+    for (int i = 1; i < paramCnt; ++i) {
+        if (lowestPreScaleMipLevel > params[i].fPreScaleMipLevel)
+            lowestPreScaleMipLevel = params[i].fPreScaleMipLevel;
+        if (highestFilterQuality < params[i].fQuality)
+            highestFilterQuality = params[i].fQuality;
+    }
+
     const bool fillMode = SkToBool(buffer);
     if (fillMode && !SkIsAlign8(reinterpret_cast<intptr_t>(buffer))) {
         return 0;
     }
 
+    // Calculate scaling parameters.
+    bool isScaled = lowestPreScaleMipLevel != 0;
+
+    SkISize scaledSize;
+    if (isScaled) {
+        // SkMipMap::ComputeLevelSize takes an index into an SkMipMap. SkMipMaps don't contain the
+        // base level, so to get an SkMipMap index we must subtract one from the GL MipMap level.
+        scaledSize = SkMipMap::ComputeLevelSize(this->width(), this->height(),
+                                                lowestPreScaleMipLevel - 1);
+    } else {
+        scaledSize = SkISize::Make(this->width(), this->height());
+    }
+
+    // We never want to scale at higher than SW medium quality, as SW medium matches GPU high.
+    SkFilterQuality scaleFilterQuality = highestFilterQuality;
+    if (scaleFilterQuality > kMedium_SkFilterQuality) {
+        scaleFilterQuality = kMedium_SkFilterQuality;
+    }
+
     const int maxTextureSize = proxy.fCaps->maxTextureSize();
-    if (width() > maxTextureSize || height() > maxTextureSize) {
+    if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) {
         return 0;
     }
 
@@ -391,7 +421,7 @@
     size_t pixelSize = 0;
     size_t ctSize = 0;
     int ctCount = 0;
-    if (this->peekPixels(&pixmap)) {
+    if (!isScaled && this->peekPixels(&pixmap)) {
         info = pixmap.info();
         pixelSize = SkAlign8(pixmap.getSafeSize());
         if (pixmap.ctable()) {
@@ -403,16 +433,23 @@
         // In the future we will access the cacherator and get the exact data that we want to (e.g.
         // yuv planes) upload.
         SkAutoTUnref<SkData> data(this->refEncoded());
-        if (!data) {
+        if (!data && !this->peekPixels(nullptr)) {
             return 0;
         }
         SkAlphaType at = this->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
-        info = SkImageInfo::MakeN32(this->width(), this->height(), at);
+        info = SkImageInfo::MakeN32(scaledSize.width(), scaledSize.height(), at);
         pixelSize = SkAlign8(SkAutoPixmapStorage::AllocSize(info, nullptr));
         if (fillMode) {
             pixmap.alloc(info);
-            if (!this->readPixels(pixmap, 0, 0, SkImage::kDisallow_CachingHint)) {
-                return 0;
+            if (isScaled) {
+                if (!this->scalePixels(pixmap, scaleFilterQuality,
+                                       SkImage::kDisallow_CachingHint)) {
+                    return 0;
+                }
+            } else {
+                if (!this->readPixels(pixmap, 0, 0, SkImage::kDisallow_CachingHint)) {
+                    return 0;
+                }
             }
             SkASSERT(!pixmap.ctable());
         }