| Index: src/image/SkImage_Gpu.cpp
 | 
| diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
 | 
| index 53765b15a2b6ef11087e50ca282d1f2df5d341bb..06ec224a2a9cbc23fa02b83288128a4252329b26 100644
 | 
| --- a/src/image/SkImage_Gpu.cpp
 | 
| +++ b/src/image/SkImage_Gpu.cpp
 | 
| @@ -5,11 +5,16 @@
 | 
|   * found in the LICENSE file.
 | 
|   */
 | 
|  
 | 
| +#include <cstddef>
 | 
| +#include <cstring>
 | 
| +#include <type_traits>
 | 
| +
 | 
|  #include "SkAutoPixmapStorage.h"
 | 
|  #include "GrCaps.h"
 | 
|  #include "GrContext.h"
 | 
|  #include "GrDrawContext.h"
 | 
|  #include "GrImageIDTextureAdjuster.h"
 | 
| +#include "GrTexturePriv.h"
 | 
|  #include "effects/GrYUVEffect.h"
 | 
|  #include "SkCanvas.h"
 | 
|  #include "SkBitmapCache.h"
 | 
| @@ -358,35 +363,84 @@ struct MipMapLevelData {
 | 
|  };
 | 
|  
 | 
|  struct DeferredTextureImage {
 | 
| -    uint32_t    fContextUniqueID;
 | 
| +    uint32_t               fContextUniqueID;
 | 
| +    // Right now, the gamma treatment is only considered when generating mipmaps
 | 
| +    SkSourceGammaTreatment fGammaTreatment;
 | 
|      // We don't store a SkImageInfo because it contains a ref-counted SkColorSpace.
 | 
| -    int         fWidth;
 | 
| -    int         fHeight;
 | 
| -    SkColorType fColorType;
 | 
| -    SkAlphaType fAlphaType;
 | 
| -    void*       fColorSpace;
 | 
| -    size_t      fColorSpaceSize;
 | 
| -    int         fColorTableCnt;
 | 
| -    uint32_t*   fColorTableData;
 | 
| -    int         fMipMapLevelCount;
 | 
| +    int                    fWidth;
 | 
| +    int                    fHeight;
 | 
| +    SkColorType            fColorType;
 | 
| +    SkAlphaType            fAlphaType;
 | 
| +    void*                  fColorSpace;
 | 
| +    size_t                 fColorSpaceSize;
 | 
| +    int                    fColorTableCnt;
 | 
| +    uint32_t*              fColorTableData;
 | 
| +    int                    fMipMapLevelCount;
 | 
|      // The fMipMapLevelData array may contain more than 1 element.
 | 
|      // It contains fMipMapLevelCount elements.
 | 
|      // That means this struct's size is not known at compile-time.
 | 
| -    MipMapLevelData fMipMapLevelData[1];
 | 
| +    MipMapLevelData        fMipMapLevelData[1];
 | 
|  };
 | 
|  }  // anonymous namespace
 | 
|  
 | 
| +static bool should_use_mip_maps(const SkImage::DeferredTextureImageUsageParams & param) {
 | 
| +    bool shouldUseMipMaps = false;
 | 
| +
 | 
| +    // Use mipmaps if either
 | 
| +    // 1.) it is a perspective matrix, or
 | 
| +    // 2.) the quality is med/high and the scale is < 1
 | 
| +    if (param.fMatrix.hasPerspective()) {
 | 
| +        shouldUseMipMaps = true;
 | 
| +    }
 | 
| +    if (param.fQuality == kMedium_SkFilterQuality ||
 | 
| +        param.fQuality == kHigh_SkFilterQuality) {
 | 
| +        SkScalar minAxisScale = param.fMatrix.getMinScale();
 | 
| +        if (minAxisScale != -1.f && minAxisScale < 1.f) {
 | 
| +            shouldUseMipMaps = true;
 | 
| +        }
 | 
| +    }
 | 
| +
 | 
| +
 | 
| +    return shouldUseMipMaps;
 | 
| +}
 | 
| +
 | 
| +namespace {
 | 
| +
 | 
| +class DTIBufferFiller
 | 
| +{
 | 
| +public:
 | 
| +    explicit DTIBufferFiller(intptr_t bufferAsInt)
 | 
| +        : bufferAsInt_(bufferAsInt) {}
 | 
| +
 | 
| +    void fillMember(const void* source, size_t memberOffset, size_t size) {
 | 
| +        memcpy(reinterpret_cast<void*>(bufferAsInt_ + memberOffset), source, size);
 | 
| +    }
 | 
| +
 | 
| +private:
 | 
| +
 | 
| +    intptr_t bufferAsInt_;
 | 
| +};
 | 
| +}
 | 
| +
 | 
| +#define FILL_MEMBER(bufferFiller, member, source) \
 | 
| +    bufferFiller.fillMember(source, \
 | 
| +               offsetof(DeferredTextureImage, member), \
 | 
| +               sizeof(DeferredTextureImage::member));
 | 
| +
 | 
|  size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& proxy,
 | 
|                                              const DeferredTextureImageUsageParams params[],
 | 
| -                                            int paramCnt, void* buffer) const {
 | 
| +                                            int paramCnt, void* buffer,
 | 
| +                                            SkSourceGammaTreatment gammaTreatment) const {
 | 
|      // Extract relevant min/max values from the params array.
 | 
|      int lowestPreScaleMipLevel = params[0].fPreScaleMipLevel;
 | 
|      SkFilterQuality highestFilterQuality = params[0].fQuality;
 | 
| +    bool useMipMaps = should_use_mip_maps(params[0]);
 | 
|      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;
 | 
| +        useMipMaps |= should_use_mip_maps(params[i]);
 | 
|      }
 | 
|  
 | 
|      const bool fillMode = SkToBool(buffer);
 | 
| @@ -455,11 +509,35 @@ size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& prox
 | 
|              SkASSERT(!pixmap.ctable());
 | 
|          }
 | 
|      }
 | 
| +    SkAlphaType at = this->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
 | 
|      int mipMapLevelCount = 1;
 | 
| +    if (useMipMaps) {
 | 
| +        // SkMipMap only deals with the mipmap levels it generates, which does
 | 
| +        // not include the base level.
 | 
| +        // That means it generates and holds levels 1-x instead of 0-x.
 | 
| +        // So the total mipmap level count is 1 more than what
 | 
| +        // SkMipMap::ComputeLevelCount returns.
 | 
| +        mipMapLevelCount = SkMipMap::ComputeLevelCount(scaledSize.width(), scaledSize.height()) + 1;
 | 
| +
 | 
| +        // We already initialized pixelSize to the size of the base level.
 | 
| +        // SkMipMap will generate the extra mipmap levels. Their sizes need to
 | 
| +        // be added to the total.
 | 
| +        // Index 0 here does not refer to the base mipmap level -- it is
 | 
| +        // SkMipMap's first generated mipmap level (level 1).
 | 
| +        for (int currentMipMapLevelIndex = mipMapLevelCount - 2; currentMipMapLevelIndex >= 0;
 | 
| +             currentMipMapLevelIndex--) {
 | 
| +            SkISize mipSize = SkMipMap::ComputeLevelSize(scaledSize.width(), scaledSize.height(),
 | 
| +                                                         currentMipMapLevelIndex);
 | 
| +            SkImageInfo mipInfo = SkImageInfo::MakeN32(mipSize.fWidth, mipSize.fHeight, at);
 | 
| +            pixelSize += SkAlign8(SkAutoPixmapStorage::AllocSize(mipInfo, nullptr));
 | 
| +        }
 | 
| +    }
 | 
|      size_t size = 0;
 | 
|      size_t dtiSize = SkAlign8(sizeof(DeferredTextureImage));
 | 
|      size += dtiSize;
 | 
| -    size += mipMapLevelCount * sizeof(MipMapLevelData);
 | 
| +    size += (mipMapLevelCount - 1) * sizeof(MipMapLevelData);
 | 
| +    // We subtract 1 because DeferredTextureImage already includes the base
 | 
| +    // level in its size
 | 
|      size_t pixelOffset = size;
 | 
|      size += pixelSize;
 | 
|      size_t ctOffset = size;
 | 
| @@ -475,37 +553,106 @@ size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& prox
 | 
|          return size;
 | 
|      }
 | 
|      intptr_t bufferAsInt = reinterpret_cast<intptr_t>(buffer);
 | 
| -    void* pixels = reinterpret_cast<void*>(bufferAsInt + pixelOffset);
 | 
| -    SkPMColor* ct = nullptr;
 | 
| +    intptr_t pixelsAsInt = bufferAsInt + pixelOffset;
 | 
| +    void* pixels = reinterpret_cast<void*>(pixelsAsInt);
 | 
| +    void* ct = nullptr;
 | 
|      if (ctSize) {
 | 
| -        ct = reinterpret_cast<SkPMColor*>(bufferAsInt + ctOffset);
 | 
| +        ct = reinterpret_cast<void*>(bufferAsInt + ctOffset);
 | 
|      }
 | 
|  
 | 
| -    memcpy(pixels, pixmap.addr(), pixmap.getSafeSize());
 | 
| +    memcpy(reinterpret_cast<void*>(SkAlign8(pixelsAsInt)), pixmap.addr(), pixmap.getSafeSize());
 | 
|      if (ctSize) {
 | 
|          memcpy(ct, pixmap.ctable()->readColors(), ctSize);
 | 
|      }
 | 
|  
 | 
|      SkASSERT(info == pixmap.info());
 | 
|      size_t rowBytes = pixmap.rowBytes();
 | 
| -    DeferredTextureImage* dti = new (buffer) DeferredTextureImage();
 | 
| -    dti->fContextUniqueID = proxy.fContextUniqueID;
 | 
| -    dti->fWidth = info.width();
 | 
| -    dti->fHeight = info.height();
 | 
| -    dti->fColorType = info.colorType();
 | 
| -    dti->fAlphaType = info.alphaType();
 | 
| -    dti->fColorTableCnt = ctCount;
 | 
| -    dti->fColorTableData = ct;
 | 
| -    dti->fMipMapLevelCount = mipMapLevelCount;
 | 
| -    dti->fMipMapLevelData[0].fPixelData = pixels;
 | 
| -    dti->fMipMapLevelData[0].fRowBytes = rowBytes;
 | 
| +    static_assert(std::is_standard_layout<DeferredTextureImage>::value,
 | 
| +                  "offsetof, which we use below, requires the type have standard layout");
 | 
| +    auto dtiBufferFiller = DTIBufferFiller{bufferAsInt};
 | 
| +    FILL_MEMBER(dtiBufferFiller, fGammaTreatment, &gammaTreatment);
 | 
| +    FILL_MEMBER(dtiBufferFiller, fContextUniqueID, &proxy.fContextUniqueID);
 | 
| +    int width = info.width();
 | 
| +    FILL_MEMBER(dtiBufferFiller, fWidth, &width);
 | 
| +    int height = info.height();
 | 
| +    FILL_MEMBER(dtiBufferFiller, fHeight, &height);
 | 
| +    SkColorType colorType = info.colorType();
 | 
| +    FILL_MEMBER(dtiBufferFiller, fColorType, &colorType);
 | 
| +    SkAlphaType alphaType = info.alphaType();
 | 
| +    FILL_MEMBER(dtiBufferFiller, fAlphaType, &alphaType);
 | 
| +    FILL_MEMBER(dtiBufferFiller, fColorTableCnt, &ctCount);
 | 
| +    FILL_MEMBER(dtiBufferFiller, fColorTableData, &ct);
 | 
| +    FILL_MEMBER(dtiBufferFiller, fMipMapLevelCount, &mipMapLevelCount);
 | 
| +//    FILL_MEMBER(dtiBufferFiller, fMipMapLevelCount[0].fPixelData, &pixels);
 | 
| +    memcpy(reinterpret_cast<void*>(bufferAsInt +
 | 
| +                                  offsetof(DeferredTextureImage, fMipMapLevelData[0].fPixelData)),
 | 
| +           &pixels, sizeof(pixels));
 | 
| +    memcpy(reinterpret_cast<void*>(bufferAsInt +
 | 
| +                                  offsetof(DeferredTextureImage, fMipMapLevelData[0].fRowBytes)),
 | 
| +           &rowBytes, sizeof(rowBytes));
 | 
|      if (colorSpaceSize) {
 | 
| -        dti->fColorSpace = reinterpret_cast<void*>(bufferAsInt + colorSpaceOffset);
 | 
| -        dti->fColorSpaceSize = colorSpaceSize;
 | 
| -        info.colorSpace()->writeToMemory(dti->fColorSpace);
 | 
| +        void* colorSpace = reinterpret_cast<void*>(bufferAsInt + colorSpaceOffset);
 | 
| +        FILL_MEMBER(dtiBufferFiller, fColorSpace, &colorSpace);
 | 
| +        FILL_MEMBER(dtiBufferFiller, fColorSpaceSize, &colorSpaceSize);
 | 
| +        info.colorSpace()->writeToMemory(reinterpret_cast<void*>(bufferAsInt + colorSpaceOffset));
 | 
|      } else {
 | 
| -        dti->fColorSpace = nullptr;
 | 
| -        dti->fColorSpaceSize = 0;
 | 
| +        memset(reinterpret_cast<void*>(bufferAsInt +
 | 
| +                                       offsetof(DeferredTextureImage, fColorSpace)),
 | 
| +               0, sizeof(DeferredTextureImage::fColorSpace));
 | 
| +        memset(reinterpret_cast<void*>(bufferAsInt +
 | 
| +                                       offsetof(DeferredTextureImage, fColorSpaceSize)),
 | 
| +               0, sizeof(DeferredTextureImage::fColorSpaceSize));
 | 
| +    }
 | 
| +
 | 
| +    // Fill in the mipmap levels if they exist
 | 
| +    intptr_t mipLevelPtr = bufferAsInt + pixelOffset + SkAlign8(pixmap.getSafeSize());
 | 
| +
 | 
| +    if (useMipMaps) {
 | 
| +        // offsetof, which we use below, requires the type have standard layout
 | 
| +        SkASSERT(std::is_standard_layout<MipMapLevelData>::value);
 | 
| +
 | 
| +        SkAutoTDelete<SkMipMap> mipmaps(SkMipMap::Build(pixmap, gammaTreatment, nullptr));
 | 
| +        // SkMipMap holds only the mipmap levels it generates.
 | 
| +        // A programmer can use the data they provided to SkMipMap::Build as level 0.
 | 
| +        // So the SkMipMap provides levels 1-x but it stores them in its own
 | 
| +        // range 0-(x-1).
 | 
| +        for (int generatedMipLevelIndex = 0; generatedMipLevelIndex < mipMapLevelCount - 1;
 | 
| +             generatedMipLevelIndex++) {
 | 
| +            SkISize mipSize = SkMipMap::ComputeLevelSize(scaledSize.width(), scaledSize.height(),
 | 
| +                                                         generatedMipLevelIndex);
 | 
| +
 | 
| +            SkImageInfo mipInfo = SkImageInfo::MakeN32(mipSize.fWidth, mipSize.fHeight, at);
 | 
| +            SkMipMap::Level mipLevel;
 | 
| +            mipmaps->getLevel(generatedMipLevelIndex, &mipLevel);
 | 
| +
 | 
| +            // Make sure the mipmap data is after the start of the buffer
 | 
| +            SkASSERT(mipLevelPtr > bufferAsInt);
 | 
| +            // Make sure the mipmap data starts before the end of the buffer
 | 
| +            SkASSERT(static_cast<size_t>(mipLevelPtr) < bufferAsInt + pixelOffset + pixelSize);
 | 
| +            // Make sure the mipmap data ends before the end of the buffer
 | 
| +            SkASSERT(mipLevelPtr + mipLevel.fPixmap.getSafeSize() <=
 | 
| +                     bufferAsInt + pixelOffset + pixelSize);
 | 
| +
 | 
| +            // getSafeSize includes rowbyte padding except for the last row,
 | 
| +            // right?
 | 
| +
 | 
| +            memcpy(reinterpret_cast<void*>(mipLevelPtr), mipLevel.fPixmap.addr(),
 | 
| +                       mipLevel.fPixmap.getSafeSize());
 | 
| +
 | 
| +            memcpy(reinterpret_cast<void*>(bufferAsInt +
 | 
| +                offsetof(DeferredTextureImage, fMipMapLevelData) +
 | 
| +                sizeof(MipMapLevelData) * (generatedMipLevelIndex + 1) +
 | 
| +                offsetof(MipMapLevelData, fPixelData)),
 | 
| +                   &mipLevelPtr, sizeof(void*));
 | 
| +            size_t rowBytes = mipLevel.fPixmap.rowBytes();
 | 
| +            memcpy(reinterpret_cast<void*>(bufferAsInt +
 | 
| +                offsetof(DeferredTextureImage, fMipMapLevelData) +
 | 
| +                sizeof(MipMapLevelData) * (generatedMipLevelIndex + 1) +
 | 
| +                offsetof(MipMapLevelData, fRowBytes)),
 | 
| +                   &rowBytes, sizeof(rowBytes));
 | 
| +
 | 
| +            mipLevelPtr += SkAlign8(mipLevel.fPixmap.getSafeSize());
 | 
| +        }
 | 
|      }
 | 
|      return size;
 | 
|  }
 | 
| @@ -525,24 +672,38 @@ sk_sp<SkImage> SkImage::MakeFromDeferredTextureImageData(GrContext* context, con
 | 
|          SkASSERT(dti->fColorTableData);
 | 
|          colorTable.reset(new SkColorTable(dti->fColorTableData, dti->fColorTableCnt));
 | 
|      }
 | 
| -    SkASSERT(dti->fMipMapLevelCount == 1);
 | 
| +    int mipLevelCount = dti->fMipMapLevelCount;
 | 
| +    SkASSERT(mipLevelCount >= 1);
 | 
|      sk_sp<SkColorSpace> colorSpace;
 | 
|      if (dti->fColorSpaceSize) {
 | 
|          colorSpace = SkColorSpace::Deserialize(dti->fColorSpace, dti->fColorSpaceSize);
 | 
|      }
 | 
|      SkImageInfo info = SkImageInfo::Make(dti->fWidth, dti->fHeight,
 | 
|                                           dti->fColorType, dti->fAlphaType, colorSpace);
 | 
| -    SkPixmap pixmap;
 | 
| -    pixmap.reset(info, dti->fMipMapLevelData[0].fPixelData,
 | 
| -                 dti->fMipMapLevelData[0].fRowBytes, colorTable.get());
 | 
| -    return SkImage::MakeTextureFromPixmap(context, pixmap, budgeted);
 | 
| +    if (mipLevelCount == 1) {
 | 
| +        SkPixmap pixmap;
 | 
| +        pixmap.reset(info, dti->fMipMapLevelData[0].fPixelData,
 | 
| +                     dti->fMipMapLevelData[0].fRowBytes, colorTable.get());
 | 
| +        return SkImage::MakeTextureFromPixmap(context, pixmap, budgeted);
 | 
| +    } else {
 | 
| +        SkAutoTDeleteArray<GrMipLevel> texels(new GrMipLevel[mipLevelCount]);
 | 
| +        for (int i = 0; i < mipLevelCount; i++) {
 | 
| +            texels[i].fPixels = dti->fMipMapLevelData[i].fPixelData;
 | 
| +            texels[i].fRowBytes = dti->fMipMapLevelData[i].fRowBytes;
 | 
| +        }
 | 
| +
 | 
| +        return SkImage::MakeTextureFromMipMap(context, info, texels.get(),
 | 
| +                                              mipLevelCount, SkBudgeted::kYes,
 | 
| +                                              dti->fGammaTreatment);
 | 
| +    }
 | 
|  }
 | 
|  
 | 
|  ///////////////////////////////////////////////////////////////////////////////////////////////////
 | 
|  
 | 
|  sk_sp<SkImage> SkImage::MakeTextureFromMipMap(GrContext* ctx, const SkImageInfo& info,
 | 
|                                                const GrMipLevel* texels, int mipLevelCount,
 | 
| -                                              SkBudgeted budgeted) {
 | 
| +                                              SkBudgeted budgeted,
 | 
| +                                              SkSourceGammaTreatment gammaTreatment) {
 | 
|      if (!ctx) {
 | 
|          return nullptr;
 | 
|      }
 | 
| @@ -550,6 +711,7 @@ sk_sp<SkImage> SkImage::MakeTextureFromMipMap(GrContext* ctx, const SkImageInfo&
 | 
|      if (!texture) {
 | 
|          return nullptr;
 | 
|      }
 | 
| +    texture->texturePriv().setGammaTreatment(gammaTreatment);
 | 
|      return sk_make_sp<SkImage_Gpu>(texture->width(), texture->height(), kNeedNewImageUniqueID,
 | 
|                                     info.alphaType(), texture, sk_ref_sp(info.colorSpace()),
 | 
|                                     budgeted);
 | 
| 
 |