static void packA8ToA1(const SkMask& mask, const uint8_t* src, size_t srcRB) { const int height = mask.fBounds.height(); const int width = mask.fBounds.width(); const int octs = width >> 3; const int leftOverBits = width & 7; uint8_t* dst = mask.fImage; const int dstPad = mask.fRowBytes - SkAlign8(width)/8; SkASSERT(dstPad >= 0); SkASSERT(width >= 0); SkASSERT(srcRB >= (size_t)width); const size_t srcPad = srcRB - width; for (int y = 0; y < height; ++y) { for (int i = 0; i < octs; ++i) { *dst++ = pack_8_to_1(src); src += 8; } if (leftOverBits > 0) { unsigned bits = 0; int shift = 7; for (int i = 0; i < leftOverBits; ++i, --shift) { bits |= convert_8_to_1(*src++) << shift; } *dst++ = bits; } src += srcPad; dst += dstPad; } }
static void codec_yuv(skiatest::Reporter* reporter, const char path[], SkISize expectedSizes[3]) { SkAutoTDelete<SkStream> stream(resource(path)); if (!stream) { SkDebugf("Missing resource '%s'\n", path); return; } SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach())); REPORTER_ASSERT(reporter, codec); if (!codec) { return; } // Test queryYUV8() SkCodec::YUVSizeInfo info; bool success = codec->queryYUV8(nullptr, nullptr); REPORTER_ASSERT(reporter, !success); success = codec->queryYUV8(&info, nullptr); REPORTER_ASSERT(reporter, (expectedSizes == nullptr) == !success); if (!success) { return; } REPORTER_ASSERT(reporter, 0 == memcmp((const void*) &info, (const void*) expectedSizes, 3 * sizeof(SkISize))); REPORTER_ASSERT(reporter, info.fYWidthBytes == (uint32_t) SkAlign8(info.fYSize.width())); REPORTER_ASSERT(reporter, info.fUWidthBytes == (uint32_t) SkAlign8(info.fUSize.width())); REPORTER_ASSERT(reporter, info.fVWidthBytes == (uint32_t) SkAlign8(info.fVSize.width())); SkYUVColorSpace colorSpace; success = codec->queryYUV8(&info, &colorSpace); REPORTER_ASSERT(reporter, 0 == memcmp((const void*) &info, (const void*) expectedSizes, 3 * sizeof(SkISize))); REPORTER_ASSERT(reporter, info.fYWidthBytes == (uint32_t) SkAlign8(info.fYSize.width())); REPORTER_ASSERT(reporter, info.fUWidthBytes == (uint32_t) SkAlign8(info.fUSize.width())); REPORTER_ASSERT(reporter, info.fVWidthBytes == (uint32_t) SkAlign8(info.fVSize.width())); REPORTER_ASSERT(reporter, kJPEG_SkYUVColorSpace == colorSpace); // Allocate the memory for the YUV decode size_t totalBytes = info.fYWidthBytes * info.fYSize.height() + info.fUWidthBytes * info.fUSize.height() + info.fVWidthBytes * info.fVSize.height(); SkAutoMalloc storage(totalBytes); void* planes[3]; planes[0] = storage.get(); planes[1] = SkTAddOffset<void>(planes[0], info.fYWidthBytes * info.fYSize.height()); planes[2] = SkTAddOffset<void>(planes[1], info.fUWidthBytes * info.fUSize.height()); // Test getYUV8Planes() REPORTER_ASSERT(reporter, SkCodec::kInvalidInput == codec->getYUV8Planes(info, nullptr)); REPORTER_ASSERT(reporter, SkCodec::kSuccess == codec->getYUV8Planes(info, planes)); }
char* startOfData() const { return reinterpret_cast<char*>(SkAlign8(reinterpret_cast<size_t>(this + 1))); }
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; }