// Helper function that uploads the given SkImage using MakeFromDeferredTextureImageData and then
// draws the uploaded version at the specified coordinates.
static void DrawDeferredTextureImageData(SkCanvas* canvas,
- SkImage::DeferredTextureImageUsageParams* params) {
+ const char* resourceName,
+ SkImage::DeferredTextureImageUsageParams* params,
+ SkColorType dstColorType) {
GrContext* context = canvas->getGrContext();
if (!context) {
skiagm::GM::DrawGpuOnlyMessage(canvas);
- sk_sp<SkImage> encodedImage = GetResourceAsImage("mandrill_512.png");
+ sk_sp<SkImage> encodedImage = GetResourceAsImage(resourceName);
if (!encodedImage) {
SkDebugf("\nCould not load resource.\n");
return;
}
size_t requiredMemoryInBytes = encodedImage->getDeferredTextureImageData(
- *proxy, params, 1, nullptr, canvas->imageInfo().colorSpace());
+ *proxy, params, 1, nullptr, canvas->imageInfo().colorSpace(), dstColorType);
if (requiredMemoryInBytes == 0) {
SkDebugf("\nCould not create DeferredTextureImageData.\n");
return;
std::vector<uint8_t> memory;
memory.resize(requiredMemoryInBytes);
encodedImage->getDeferredTextureImageData(
- *proxy, params, 1, memory.data(), canvas->imageInfo().colorSpace());
+ *proxy, params, 1, memory.data(), canvas->imageInfo().colorSpace(), dstColorType);
sk_sp<SkImage> uploadedEncodedImage = SkImage::MakeFromDeferredTextureImageData(
context, memory.data(), SkBudgeted::kNo);
SkBitmap bitmap;
- if (!GetResourceAsBitmap("mandrill_512.png", &bitmap)) {
+ if (!GetResourceAsBitmap(resourceName, &bitmap)) {
SkDebugf("\nCould not decode resource.\n");
return;
}
sk_sp<SkImage> decodedImage = SkImage::MakeFromBitmap(bitmap);
requiredMemoryInBytes = decodedImage->getDeferredTextureImageData(
- *proxy, params, 1, nullptr, canvas->imageInfo().colorSpace());
+ *proxy, params, 1, nullptr, canvas->imageInfo().colorSpace(), dstColorType);
if (requiredMemoryInBytes == 0) {
SkDebugf("\nCould not create DeferredTextureImageData.\n");
return;
memory.resize(requiredMemoryInBytes);
decodedImage->getDeferredTextureImageData(
- *proxy, params, 1, memory.data(), canvas->imageInfo().colorSpace());
+ *proxy, params, 1, memory.data(), canvas->imageInfo().colorSpace(), dstColorType);
sk_sp<SkImage> uploadedDecodedImage = SkImage::MakeFromDeferredTextureImageData(
context, memory.data(), SkBudgeted::kNo);
- canvas->drawImage(uploadedDecodedImage, 512 + 20, 10);
+ canvas->drawImage(uploadedDecodedImage, encodedImage->width() + 20, 10);
}
static void DrawDeferredTextureImageMipMapTree(SkCanvas* canvas, SkImage* image,
- SkImage::DeferredTextureImageUsageParams* params) {
+ SkImage::DeferredTextureImageUsageParams* params,
+ SkColorType dstColorType) {
GrContext* context = canvas->getGrContext();
if (!context) {
skiagm::GM::DrawGpuOnlyMessage(canvas);
int mipLevelCount = SkMipMap::ComputeLevelCount(image->width(), image->height());
size_t requiredMemoryInBytes = image->getDeferredTextureImageData(
- *proxy, params, 1, nullptr, canvas->imageInfo().colorSpace());
+ *proxy, params, 1, nullptr, canvas->imageInfo().colorSpace(), dstColorType);
if (requiredMemoryInBytes == 0) {
SkDebugf("\nCould not create DeferredTextureImageData.\n");
return;
std::vector<uint8_t> memory;
memory.resize(requiredMemoryInBytes);
image->getDeferredTextureImageData(
- *proxy, params, 1, memory.data(), canvas->imageInfo().colorSpace());
+ *proxy, params, 1, memory.data(), canvas->imageInfo().colorSpace(), dstColorType);
sk_sp<SkImage> uploadedImage = SkImage::MakeFromDeferredTextureImageData(
context, memory.data(), SkBudgeted::kNo);
DEF_SIMPLE_GM(deferred_texture_image_none, canvas, 512 + 512 + 30, 512 + 20) {
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(1.f, 1.f),
kNone_SkFilterQuality, 0);
- DrawDeferredTextureImageData(canvas, ¶ms);
+ DrawDeferredTextureImageData(canvas, "mandrill_512.png", ¶ms, kN32_SkColorType);
}
DEF_SIMPLE_GM(deferred_texture_image_low, canvas, 512 + 512 + 30, 512 + 20) {
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(1.f, 1.f),
kLow_SkFilterQuality, 0);
- DrawDeferredTextureImageData(canvas, ¶ms);
+ DrawDeferredTextureImageData(canvas, "mandrill_512.png", ¶ms, kN32_SkColorType);
+}
+
+DEF_SIMPLE_GM(deferred_texture_image_low_dithered, canvas, 180 + 180 + 30, 180 + 20) {
+ auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(0.25f, 0.25f),
+ kLow_SkFilterQuality, 0);
+ DrawDeferredTextureImageData(canvas, "dog.jpg", ¶ms, kARGB_4444_SkColorType);
}
DEF_SIMPLE_GM(deferred_texture_image_medium_encoded, canvas, 512 + 512 + 30, 1110) {
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(0.25f, 0.25f),
kMedium_SkFilterQuality, 0);
- DrawDeferredTextureImageMipMapTree(canvas, encodedImage.get(), ¶ms);
+ DrawDeferredTextureImageMipMapTree(canvas, encodedImage.get(), ¶ms, kN32_SkColorType);
}
DEF_SIMPLE_GM(deferred_texture_image_medium_decoded, canvas, 512 + 512 + 30, 1110) {
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(0.25f, 0.25f),
kMedium_SkFilterQuality, 0);
- DrawDeferredTextureImageMipMapTree(canvas, decodedImage.get(), ¶ms);
+ DrawDeferredTextureImageMipMapTree(canvas, decodedImage.get(), ¶ms, kN32_SkColorType);
}
DEF_SIMPLE_GM(deferred_texture_image_high, canvas, 512 + 512 + 30, 512 + 20) {
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(1.f, 1.f),
kHigh_SkFilterQuality, 0);
- DrawDeferredTextureImageData(canvas, ¶ms);
+ DrawDeferredTextureImageData(canvas, "mandrill_512.png", ¶ms, kN32_SkColorType);
}
DEF_SIMPLE_GM(deferred_texture_image_medium_encoded_indexed, canvas, 128 + 128 + 30, 340) {
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(0.25f, 0.25f),
kMedium_SkFilterQuality, 0);
- DrawDeferredTextureImageMipMapTree(canvas, encodedImage.get(), ¶ms);
+ DrawDeferredTextureImageMipMapTree(canvas, encodedImage.get(), ¶ms, kN32_SkColorType);
}
DEF_SIMPLE_GM(deferred_texture_image_medium_decoded_indexed, canvas, 128 + 128 + 30, 340) {
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(0.25f, 0.25f),
kMedium_SkFilterQuality, 0);
- DrawDeferredTextureImageMipMapTree(canvas, decodedImage.get(), ¶ms);
+ DrawDeferredTextureImageMipMapTree(canvas, decodedImage.get(), ¶ms, kN32_SkColorType);
}
#endif
* dstColorSpace is the color space of the surface where this texture will ultimately be used.
* If the method determines that mip-maps are needed, this helps determine the correct strategy
* for building them (gamma-correct or not).
+ *
+ * dstColorType is the color type of the surface where this texture will ultimately be used.
+ * This determines the format with which the image will be uploaded to the GPU. If dstColorType
+ * does not support color spaces (low bit depth types such as ARGB_4444), then dstColorSpace
+ * must be null.
*/
size_t getDeferredTextureImageData(const GrContextThreadSafeProxy&,
const DeferredTextureImageUsageParams[],
int paramCnt,
void* buffer,
- SkColorSpace* dstColorSpace = nullptr) const;
+ SkColorSpace* dstColorSpace = nullptr,
+ SkColorType dstColorType = kN32_SkColorType) const;
/**
* Returns a texture-backed image from data produced in SkImage::getDeferredTextureImageData.
this->freeStorage();
}
+SkAutoPixmapStorage& SkAutoPixmapStorage::operator=(SkAutoPixmapStorage&& other) {
+ this->fStorage = other.fStorage;
+ this->INHERITED::reset(other.info(), this->fStorage, other.rowBytes(), other.ctable());
+
+ other.fStorage = nullptr;
+ other.INHERITED::reset();
+
+ return *this;
+}
+
size_t SkAutoPixmapStorage::AllocSize(const SkImageInfo& info, size_t* rowBytes) {
size_t rb = info.minRowBytes();
if (rowBytes) {
~SkAutoPixmapStorage();
/**
+ * Leave the moved-from object in a free-but-valid state.
+ */
+ SkAutoPixmapStorage& operator=(SkAutoPixmapStorage&& other);
+
+ /**
* Try to allocate memory for the pixels needed to match the specified Info. On success
* return true and fill out the pixmap to point to that memory. The storage will be freed
* when this object is destroyed, or if another call to tryAlloc() or alloc() is made.
size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy&,
const DeferredTextureImageUsageParams[],
int paramCnt, void* buffer,
- SkColorSpace* dstColorSpace) const {
+ SkColorSpace* dstColorSpace,
+ SkColorType dstColorType) const {
return 0;
}
offsetof(DeferredTextureImage, member), \
sizeof(DeferredTextureImage::member));
+static bool SupportsColorSpace(SkColorType colorType) {
+ switch (colorType) {
+ case kRGBA_8888_SkColorType:
+ case kBGRA_8888_SkColorType:
+ case kRGBA_F16_SkColorType:
+ return true;
+ default:
+ return false;
+ }
+}
+
size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& proxy,
const DeferredTextureImageUsageParams params[],
int paramCnt, void* buffer,
- SkColorSpace* dstColorSpace) const {
+ SkColorSpace* dstColorSpace,
+ SkColorType dstColorType) const {
// Some quick-rejects where is makes no sense to return CPU data
// e.g.
// - texture backed
return 0;
}
+ bool supportsColorSpace = SupportsColorSpace(dstColorType);
+ // Quick reject if the caller requests a color space with an unsupported color type.
+ if (SkToBool(dstColorSpace) && !supportsColorSpace) {
+ return 0;
+ }
+
// Extract relevant min/max values from the params array.
int lowestPreScaleMipLevel = params[0].fPreScaleMipLevel;
SkFilterQuality highestFilterQuality = params[0].fQuality;
SkAutoPixmapStorage pixmap;
SkImageInfo info;
size_t pixelSize = 0;
- if (!isScaled && this->peekPixels(&pixmap) && !pixmap.ctable()) {
+ if (!isScaled && this->peekPixels(&pixmap) && !pixmap.ctable() &&
+ pixmap.info().colorType() == dstColorType) {
info = pixmap.info();
pixelSize = SkAlign8(pixmap.getSafeSize());
if (!dstColorSpace) {
info = info.makeColorSpace(nullptr);
}
}
- if (kIndex_8_SkColorType == info.colorType()) {
- // Force Index8 to be N32 instead. Index8 is unsupported in Ganesh.
- info = info.makeColorType(kN32_SkColorType);
- }
+ // Force color type to be the requested type.
+ info = info.makeColorType(dstColorType);
pixelSize = SkAlign8(SkAutoPixmapStorage::AllocSize(info, nullptr));
if (fillMode) {
- pixmap.alloc(info);
+ // Always decode to N32 and convert to the requested type if necessary.
+ SkImageInfo decodeInfo = info.makeColorType(kN32_SkColorType);
+ SkAutoPixmapStorage decodePixmap;
+ decodePixmap.alloc(decodeInfo);
+
if (isScaled) {
- if (!this->scalePixels(pixmap, scaleFilterQuality,
+ if (!this->scalePixels(decodePixmap, scaleFilterQuality,
SkImage::kDisallow_CachingHint)) {
return 0;
}
} else {
- if (!this->readPixels(pixmap, 0, 0, SkImage::kDisallow_CachingHint)) {
+ if (!this->readPixels(decodePixmap, 0, 0, SkImage::kDisallow_CachingHint)) {
return 0;
}
}
- SkASSERT(!pixmap.ctable());
+ SkASSERT(!decodePixmap.ctable());
+
+ if (decodeInfo.colorType() != info.colorType()) {
+ pixmap.alloc(info);
+ // Convert and copy the decoded pixmap to the target pixmap.
+ decodePixmap.readPixels(pixmap.info(), pixmap.writable_addr(), pixmap.rowBytes(), 0,
+ 0);
+ } else {
+ pixmap = std::move(decodePixmap);
+ }
}
}
int mipMapLevelCount = 1;
SkColorSpaceTransferFn fn;
if (info.colorSpace()) {
SkASSERT(dstColorSpace);
+ SkASSERT(supportsColorSpace);
colorSpaceOffset = size;
colorSpaceSize = info.colorSpace()->writeToMemory(nullptr);
size += colorSpaceSize;
- } else if (this->colorSpace() && this->colorSpace()->isNumericalTransferFn(&fn)) {
+ } else if (supportsColorSpace && this->colorSpace() && this->colorSpace()->isNumericalTransferFn(&fn)) {
// In legacy mode, preserve the color space tag on the SkImage. This is only
// supported if the color space has a parametric transfer function.
SkASSERT(!dstColorSpace);
SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy;
if (proxy.fCaps->srgbSupport() && SkToBool(dstColorSpace) &&
info.colorSpace() && info.colorSpace()->gammaCloseToSRGB()) {
+ SkASSERT(supportsColorSpace);
colorMode = SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware;
}
struct {
std::function<sk_sp<SkImage> ()> fImageFactory;
std::vector<SkImage::DeferredTextureImageUsageParams> fParams;
+ sk_sp<SkColorSpace> fColorSpace;
+ SkColorType fColorType;
SkFilterQuality fExpectedQuality;
int fExpectedScaleFactor;
bool fExpectation;
} testCases[] = {
{ create_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
- kNone_SkFilterQuality, 1, true },
+ nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, true },
{ create_codec_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
- kNone_SkFilterQuality, 1, true },
+ nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, true },
{ create_data_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
- kNone_SkFilterQuality, 1, true },
+ nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, true },
{ create_picture_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
- kNone_SkFilterQuality, 1, false },
+ nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false },
{ [context] { return create_gpu_image(context); },
{{SkMatrix::I(), kNone_SkFilterQuality, 0}},
- kNone_SkFilterQuality, 1, false },
+ nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false },
// Create a texture image in a another GrContext.
{ [testContext, otherContextInfo] {
otherContextInfo.testContext()->makeCurrent();
testContext->makeCurrent();
return otherContextImage;
}, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
- kNone_SkFilterQuality, 1, false },
+ nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false },
// Create an image that is too large to upload.
{ createLarge, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
- kNone_SkFilterQuality, 1, false },
+ nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false },
// Create an image that is too large, but is scaled to an acceptable size.
{ createLarge, {{SkMatrix::I(), kMedium_SkFilterQuality, 4}},
- kMedium_SkFilterQuality, 16, true},
+ nullptr, kN32_SkColorType, kMedium_SkFilterQuality, 16, true},
// Create an image with multiple low filter qualities, make sure we round up.
{ createLarge, {{SkMatrix::I(), kNone_SkFilterQuality, 4},
{SkMatrix::I(), kMedium_SkFilterQuality, 4}},
- kMedium_SkFilterQuality, 16, true},
+ nullptr, kN32_SkColorType, kMedium_SkFilterQuality, 16, true},
// Create an image with multiple prescale levels, make sure we chose the minimum scale.
{ createLarge, {{SkMatrix::I(), kMedium_SkFilterQuality, 5},
{SkMatrix::I(), kMedium_SkFilterQuality, 4}},
- kMedium_SkFilterQuality, 16, true},
+ nullptr, kN32_SkColorType, kMedium_SkFilterQuality, 16, true},
+ // Create a images which are decoded to a 4444 backing.
+ { create_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+ nullptr, kARGB_4444_SkColorType, kNone_SkFilterQuality, 1, true },
+ { create_codec_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+ nullptr, kARGB_4444_SkColorType, kNone_SkFilterQuality, 1, true },
+ { create_data_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+ nullptr, kARGB_4444_SkColorType, kNone_SkFilterQuality, 1, true },
+ // Valid SkColorSpace and SkColorType.
+ { create_data_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+ SkColorSpace::MakeSRGB(), kN32_SkColorType, kNone_SkFilterQuality, 1, true },
+ // Invalid SkColorSpace and SkColorType.
+ { create_data_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
+ SkColorSpace::MakeSRGB(), kARGB_4444_SkColorType, kNone_SkFilterQuality, 1, false },
};
size_t size = image->getDeferredTextureImageData(*proxy, testCase.fParams.data(),
static_cast<int>(testCase.fParams.size()),
- nullptr, nullptr);
+ nullptr, testCase.fColorSpace.get(),
+ testCase.fColorType);
static const char *const kFS[] = { "fail", "succeed" };
if (SkToBool(size) != testCase.fExpectation) {
ERRORF(reporter, "This image was expected to %s but did not.",
void* misaligned = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(buffer) + 3);
if (image->getDeferredTextureImageData(*proxy, testCase.fParams.data(),
static_cast<int>(testCase.fParams.size()),
- misaligned, nullptr)) {
+ misaligned, testCase.fColorSpace.get(),
+ testCase.fColorType)) {
ERRORF(reporter, "Should fail when buffer is misaligned.");
}
if (!image->getDeferredTextureImageData(*proxy, testCase.fParams.data(),
static_cast<int>(testCase.fParams.size()),
- buffer, nullptr)) {
+ buffer, testCase.fColorSpace.get(),
+ testCase.fColorType)) {
ERRORF(reporter, "deferred image size succeeded but creation failed.");
} else {
for (auto budgeted : { SkBudgeted::kNo, SkBudgeted::kYes }) {