Chromium Code Reviews| Index: src/image/SkImage_Gpu.cpp |
| diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp |
| index 6b7416b26bf31cc4e19eeba9d1615f5f67e05a79..5a76cd832d9d1f2ae5698a138f8b5b90a7419bbf 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,60 @@ 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; |
| +} |
| + |
| 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 +485,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 +529,117 @@ 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; |
| + // offsetof, which we use below, requires the type have standard layout |
| + SkASSERT(std::is_standard_layout<DeferredTextureImage>::value); |
|
bsalomon
2016/08/29 15:49:31
Can this be a static assert?
cblume
2016/08/29 21:52:58
Done.
|
| + memcpy(reinterpret_cast<void*>(bufferAsInt + offsetof(DeferredTextureImage, fGammaTreatment)), |
|
bsalomon
2016/08/29 15:49:31
Is there some way to write this as a template base
cblume
2016/08/29 21:52:58
I simplified it where I could.
To do this, I intr
|
| + &gammaTreatment, sizeof(gammaTreatment)); |
| + memcpy(reinterpret_cast<void*>(bufferAsInt + offsetof(DeferredTextureImage, fContextUniqueID)), |
| + &proxy.fContextUniqueID, sizeof(proxy.fContextUniqueID)); |
| + int width = info.width(); |
| + memcpy(reinterpret_cast<void*>(bufferAsInt + offsetof(DeferredTextureImage, fWidth)), |
| + &width, sizeof(width)); |
| + int height = info.height(); |
| + memcpy(reinterpret_cast<void*>(bufferAsInt + offsetof(DeferredTextureImage, fHeight)), |
| + &height, sizeof(height)); |
| + SkColorType colorType = info.colorType(); |
| + memcpy(reinterpret_cast<void*>(bufferAsInt + offsetof(DeferredTextureImage, fColorType)), |
| + &colorType, sizeof(colorType)); |
| + SkAlphaType alphaType = info.alphaType(); |
| + memcpy(reinterpret_cast<void*>(bufferAsInt + offsetof(DeferredTextureImage, fAlphaType)), |
| + &alphaType, sizeof(alphaType)); |
| + memcpy(reinterpret_cast<void*>(bufferAsInt + offsetof(DeferredTextureImage, fColorTableCnt)), |
| + &ctCount, sizeof(ctCount)); |
| + memcpy(reinterpret_cast<void*>(bufferAsInt + offsetof(DeferredTextureImage, fColorTableData)), |
| + &ct, sizeof(ct)); |
| + memcpy(reinterpret_cast<void*>(bufferAsInt + offsetof(DeferredTextureImage, fMipMapLevelCount)), |
| + &mipMapLevelCount, sizeof(mipMapLevelCount)); |
| + 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); |
| + memcpy(reinterpret_cast<void*>(bufferAsInt + |
| + offsetof(DeferredTextureImage, fColorSpace)), |
| + &colorSpace, sizeof(colorSpace)); |
| + memcpy(reinterpret_cast<void*>(bufferAsInt + |
| + offsetof(DeferredTextureImage, fColorSpaceSize)), |
| + &colorSpaceSize, sizeof(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,17 +659,30 @@ 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); |
| + } |
| } |
| /////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -558,7 +705,8 @@ GrTexture* GrDeepCopyTexture(GrTexture* src, SkBudgeted budgeted) { |
| 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; |
| } |
| @@ -566,6 +714,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); |