size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& proxy, const DeferredTextureImageUsageParams params[], int paramCnt, void* buffer, SkColorSpace* dstColorSpace) 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); 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 (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) { return 0; } SkAutoPixmapStorage pixmap; SkImageInfo info; size_t pixelSize = 0; size_t ctSize = 0; int ctCount = 0; if (!isScaled && this->peekPixels(&pixmap)) { info = pixmap.info(); pixelSize = SkAlign8(pixmap.getSafeSize()); if (pixmap.ctable()) { ctCount = pixmap.ctable()->count(); ctSize = SkAlign8(pixmap.ctable()->count() * 4); } } else { // Here we're just using presence of data to know whether there is a codec behind the image. // In the future we will access the cacherator and get the exact data that we want to (e.g. // yuv planes) upload. sk_sp<SkData> data(this->refEncoded()); if (!data && !this->peekPixels(nullptr)) { return 0; } info = as_IB(this)->onImageInfo().makeWH(scaledSize.width(), scaledSize.height()); pixelSize = SkAlign8(SkAutoPixmapStorage::AllocSize(info, nullptr)); if (fillMode) { pixmap.alloc(info); 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()); } } 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 = info.makeWH(mipSize.fWidth, mipSize.fHeight); pixelSize += SkAlign8(SkAutoPixmapStorage::AllocSize(mipInfo, nullptr)); } } size_t size = 0; size_t dtiSize = SkAlign8(sizeof(DeferredTextureImage)); size += dtiSize; 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; size += ctSize; size_t colorSpaceOffset = 0; size_t colorSpaceSize = 0; if (info.colorSpace()) { colorSpaceOffset = size; colorSpaceSize = info.colorSpace()->writeToMemory(nullptr); size += colorSpaceSize; } if (!fillMode) { return size; } char* bufferAsCharPtr = reinterpret_cast<char*>(buffer); char* pixelsAsCharPtr = bufferAsCharPtr + pixelOffset; void* pixels = pixelsAsCharPtr; void* ct = nullptr; if (ctSize) { ct = bufferAsCharPtr + ctOffset; } memcpy(reinterpret_cast<void*>(SkAlign8(reinterpret_cast<uintptr_t>(pixelsAsCharPtr))), pixmap.addr(), pixmap.getSafeSize()); if (ctSize) { memcpy(ct, pixmap.ctable()->readColors(), ctSize); } // If the context has sRGB support, and we're intending to render to a surface with an attached // color space, and the image has an sRGB-like color space attached, then use our gamma (sRGB) // aware mip-mapping. SkSourceGammaTreatment gammaTreatment = SkSourceGammaTreatment::kIgnore; if (proxy.fCaps->srgbSupport() && SkToBool(dstColorSpace) && info.colorSpace() && info.colorSpace()->gammaCloseToSRGB()) { gammaTreatment = SkSourceGammaTreatment::kRespect; } SkASSERT(info == pixmap.info()); size_t rowBytes = pixmap.rowBytes(); static_assert(std::is_standard_layout<DeferredTextureImage>::value, "offsetof, which we use below, requires the type have standard layout"); auto dtiBufferFiller = DTIBufferFiller{bufferAsCharPtr}; 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); memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData[0].fPixelData), &pixels, sizeof(pixels)); memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData[0].fRowBytes), &rowBytes, sizeof(rowBytes)); if (colorSpaceSize) { void* colorSpace = bufferAsCharPtr + colorSpaceOffset; FILL_MEMBER(dtiBufferFiller, fColorSpace, &colorSpace); FILL_MEMBER(dtiBufferFiller, fColorSpaceSize, &colorSpaceSize); info.colorSpace()->writeToMemory(bufferAsCharPtr + colorSpaceOffset); } else { memset(bufferAsCharPtr + offsetof(DeferredTextureImage, fColorSpace), 0, sizeof(DeferredTextureImage::fColorSpace)); memset(bufferAsCharPtr + offsetof(DeferredTextureImage, fColorSpaceSize), 0, sizeof(DeferredTextureImage::fColorSpaceSize)); } // Fill in the mipmap levels if they exist char* mipLevelPtr = pixelsAsCharPtr + SkAlign8(pixmap.getSafeSize()); if (useMipMaps) { static_assert(std::is_standard_layout<MipMapLevelData>::value, "offsetof, which we use below, requires the type have a standard layout"); 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++) { SkMipMap::Level mipLevel; mipmaps->getLevel(generatedMipLevelIndex, &mipLevel); // Make sure the mipmap data is after the start of the buffer SkASSERT(mipLevelPtr > bufferAsCharPtr); // Make sure the mipmap data starts before the end of the buffer SkASSERT(mipLevelPtr < bufferAsCharPtr + pixelOffset + pixelSize); // Make sure the mipmap data ends before the end of the buffer SkASSERT(mipLevelPtr + mipLevel.fPixmap.getSafeSize() <= bufferAsCharPtr + pixelOffset + pixelSize); // getSafeSize includes rowbyte padding except for the last row, // right? memcpy(mipLevelPtr, mipLevel.fPixmap.addr(), mipLevel.fPixmap.getSafeSize()); memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData) + sizeof(MipMapLevelData) * (generatedMipLevelIndex + 1) + offsetof(MipMapLevelData, fPixelData), &mipLevelPtr, sizeof(void*)); size_t rowBytes = mipLevel.fPixmap.rowBytes(); memcpy(bufferAsCharPtr + offsetof(DeferredTextureImage, fMipMapLevelData) + sizeof(MipMapLevelData) * (generatedMipLevelIndex + 1) + offsetof(MipMapLevelData, fRowBytes), &rowBytes, sizeof(rowBytes)); mipLevelPtr += SkAlign8(mipLevel.fPixmap.getSafeSize()); } } return size; }
size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& proxy, const DeferredTextureImageUsageParams[], int paramCnt, void* buffer) const { const bool fillMode = SkToBool(buffer); if (fillMode && !SkIsAlign8(reinterpret_cast<intptr_t>(buffer))) { return 0; } const int maxTextureSize = proxy.fCaps->maxTextureSize(); if (width() > maxTextureSize || height() > maxTextureSize) { return 0; } SkAutoPixmapStorage pixmap; SkImageInfo info; size_t pixelSize = 0; size_t ctSize = 0; int ctCount = 0; if (this->peekPixels(&pixmap)) { info = pixmap.info(); pixelSize = SkAlign8(pixmap.getSafeSize()); if (pixmap.ctable()) { ctCount = pixmap.ctable()->count(); ctSize = SkAlign8(pixmap.ctable()->count() * 4); } } else { // Here we're just using presence of data to know whether there is a codec behind the image. // 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) { return 0; } SkAlphaType at = this->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType; info = SkImageInfo::MakeN32(this->width(), this->height(), at); pixelSize = SkAlign8(SkAutoPixmapStorage::AllocSize(info, nullptr)); if (fillMode) { pixmap.alloc(info); if (!this->readPixels(pixmap, 0, 0, SkImage::kDisallow_CachingHint)) { return 0; } SkASSERT(!pixmap.ctable()); } } size_t size = 0; size_t dtiSize = SkAlign8(sizeof(DeferredTextureImage)); size += dtiSize; size_t pixelOffset = size; size += pixelSize; size_t ctOffset = size; size += ctSize; if (!fillMode) { return size; } intptr_t bufferAsInt = reinterpret_cast<intptr_t>(buffer); void* pixels = reinterpret_cast<void*>(bufferAsInt + pixelOffset); SkPMColor* ct = nullptr; if (ctSize) { ct = reinterpret_cast<SkPMColor*>(bufferAsInt + ctOffset); } memcpy(pixels, 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->fData.fInfo = info; dti->fData.fPixelData = pixels; dti->fData.fRowBytes = rowBytes; dti->fData.fColorTableCnt = ctCount; dti->fData.fColorTableData = ct; return size; }