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); |