Revert "Remove SkImage deferred texture image data APIs."
This reverts commit 4f5e1d4ff3
.
Reason for revert: Unfortunately, we need this in Chrome for a bit longer. Working on understanding why the new path led to regressions. Will re-land this once the new path sticks.
Original change's description:
> Remove SkImage deferred texture image data APIs.
>
> These APIs existed for Chrome. Chrome is no longer using them.
>
> Change-Id: I15a5e2f88c7e8d1356188748fc68d4658f6f1849
> Reviewed-on: https://skia-review.googlesource.com/81021
> Reviewed-by: Brian Osman <brianosman@google.com>
> Reviewed-by: Cary Clark <caryclark@google.com>
> Commit-Queue: Brian Salomon <bsalomon@google.com>
TBR=bsalomon@google.com,brianosman@google.com,caryclark@google.com,caryclark@skia.org
# Not skipping CQ checks because original CL landed > 1 day ago.
Change-Id: Ic9f683f262f2e1d0469156360f5ffaee977ca44a
Reviewed-on: https://skia-review.googlesource.com/86280
Reviewed-by: Eric Karl <ericrk@chromium.org>
Commit-Queue: Eric Karl <ericrk@chromium.org>
This commit is contained in:
parent
b2e3a3ada2
commit
aae533e418
222
gm/deferredtextureimage.cpp
Normal file
222
gm/deferredtextureimage.cpp
Normal file
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright 2016 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
#include <vector>
|
||||
|
||||
#include "gm.h"
|
||||
#include "SkImage.h"
|
||||
#include "SkMipMap.h"
|
||||
#include "Resources.h"
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
#include "GrContext.h"
|
||||
|
||||
// Helper function that uploads the given SkImage using MakeFromDeferredTextureImageData and then
|
||||
// draws the uploaded version at the specified coordinates.
|
||||
static void DrawDeferredTextureImageData(SkCanvas* canvas,
|
||||
const char* resourceName,
|
||||
SkImage::DeferredTextureImageUsageParams* params,
|
||||
SkColorType dstColorType) {
|
||||
GrContext* context = canvas->getGrContext();
|
||||
if (!context) {
|
||||
skiagm::GM::DrawGpuOnlyMessage(canvas);
|
||||
return;
|
||||
}
|
||||
sk_sp<GrContextThreadSafeProxy> proxy(context->threadSafeProxy());
|
||||
|
||||
|
||||
|
||||
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(), 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(), dstColorType);
|
||||
sk_sp<SkImage> uploadedEncodedImage = SkImage::MakeFromDeferredTextureImageData(
|
||||
context, memory.data(), SkBudgeted::kNo);
|
||||
|
||||
canvas->drawImage(uploadedEncodedImage, 10, 10);
|
||||
|
||||
|
||||
|
||||
SkBitmap 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(), dstColorType);
|
||||
if (requiredMemoryInBytes == 0) {
|
||||
SkDebugf("\nCould not create DeferredTextureImageData.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
memory.resize(requiredMemoryInBytes);
|
||||
decodedImage->getDeferredTextureImageData(
|
||||
*proxy, params, 1, memory.data(), canvas->imageInfo().colorSpace(), dstColorType);
|
||||
sk_sp<SkImage> uploadedDecodedImage = SkImage::MakeFromDeferredTextureImageData(
|
||||
context, memory.data(), SkBudgeted::kNo);
|
||||
|
||||
canvas->drawImage(uploadedDecodedImage, encodedImage->width() + 20, 10);
|
||||
}
|
||||
|
||||
static void DrawDeferredTextureImageMipMapTree(SkCanvas* canvas, SkImage* image,
|
||||
SkImage::DeferredTextureImageUsageParams* params,
|
||||
SkColorType dstColorType) {
|
||||
GrContext* context = canvas->getGrContext();
|
||||
if (!context) {
|
||||
skiagm::GM::DrawGpuOnlyMessage(canvas);
|
||||
return;
|
||||
}
|
||||
sk_sp<GrContextThreadSafeProxy> proxy(context->threadSafeProxy());
|
||||
|
||||
SkPaint paint;
|
||||
paint.setFilterQuality(params->fQuality);
|
||||
|
||||
int mipLevelCount = SkMipMap::ComputeLevelCount(image->width(), image->height());
|
||||
size_t requiredMemoryInBytes = image->getDeferredTextureImageData(
|
||||
*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(), dstColorType);
|
||||
sk_sp<SkImage> uploadedImage = SkImage::MakeFromDeferredTextureImageData(
|
||||
context, memory.data(), SkBudgeted::kNo);
|
||||
|
||||
// draw a column using deferred texture images
|
||||
SkScalar offsetHeight = 10.f;
|
||||
// handle base mipmap level
|
||||
canvas->save();
|
||||
canvas->translate(10.f, offsetHeight);
|
||||
canvas->drawImage(uploadedImage, 0, 0, &paint);
|
||||
canvas->restore();
|
||||
offsetHeight += image->height() + 10;
|
||||
// handle generated mipmap levels
|
||||
for (int i = 0; i < mipLevelCount; i++) {
|
||||
SkISize mipSize = SkMipMap::ComputeLevelSize(image->width(), image->height(), i);
|
||||
canvas->save();
|
||||
canvas->translate(10.f, offsetHeight);
|
||||
canvas->scale(mipSize.width() / static_cast<float>(image->width()),
|
||||
mipSize.height() / static_cast<float>(image->height()));
|
||||
canvas->drawImage(uploadedImage, 0, 0, &paint);
|
||||
canvas->restore();
|
||||
offsetHeight += mipSize.height() + 10;
|
||||
}
|
||||
|
||||
// draw a column using SkImage
|
||||
offsetHeight = 10;
|
||||
// handle base mipmap level
|
||||
canvas->save();
|
||||
canvas->translate(image->width() + 20.f, offsetHeight);
|
||||
canvas->drawImage(image, 0, 0, &paint);
|
||||
canvas->restore();
|
||||
offsetHeight += image->height() + 10;
|
||||
// handle generated mipmap levels
|
||||
for (int i = 0; i < mipLevelCount; i++) {
|
||||
SkISize mipSize = SkMipMap::ComputeLevelSize(image->width(), image->height(), i);
|
||||
canvas->save();
|
||||
canvas->translate(image->width() + 20.f, offsetHeight);
|
||||
canvas->scale(mipSize.width() / static_cast<float>(image->width()),
|
||||
mipSize.height() / static_cast<float>(image->height()));
|
||||
canvas->drawImage(image, 0, 0, &paint);
|
||||
canvas->restore();
|
||||
offsetHeight += mipSize.height() + 10;
|
||||
}
|
||||
}
|
||||
|
||||
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, "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, "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) {
|
||||
sk_sp<SkImage> encodedImage = GetResourceAsImage("mandrill_512.png");
|
||||
if (!encodedImage) {
|
||||
SkDebugf("\nCould not load resource.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(0.25f, 0.25f),
|
||||
kMedium_SkFilterQuality, 0);
|
||||
DrawDeferredTextureImageMipMapTree(canvas, encodedImage.get(), ¶ms, kN32_SkColorType);
|
||||
}
|
||||
|
||||
DEF_SIMPLE_GM(deferred_texture_image_medium_decoded, canvas, 512 + 512 + 30, 1110) {
|
||||
SkBitmap bitmap;
|
||||
if (!GetResourceAsBitmap("mandrill_512.png", &bitmap)) {
|
||||
SkDebugf("\nCould not decode resource.\n");
|
||||
return;
|
||||
}
|
||||
sk_sp<SkImage> decodedImage = SkImage::MakeFromBitmap(bitmap);
|
||||
|
||||
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(0.25f, 0.25f),
|
||||
kMedium_SkFilterQuality, 0);
|
||||
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, "mandrill_512.png", ¶ms, kN32_SkColorType);
|
||||
}
|
||||
|
||||
DEF_SIMPLE_GM(deferred_texture_image_medium_encoded_indexed, canvas, 128 + 128 + 30, 340) {
|
||||
sk_sp<SkImage> encodedImage = GetResourceAsImage("color_wheel.gif");
|
||||
if (!encodedImage) {
|
||||
SkDebugf("\nCould not load resource.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(0.25f, 0.25f),
|
||||
kMedium_SkFilterQuality, 0);
|
||||
DrawDeferredTextureImageMipMapTree(canvas, encodedImage.get(), ¶ms, kN32_SkColorType);
|
||||
}
|
||||
|
||||
DEF_SIMPLE_GM(deferred_texture_image_medium_decoded_indexed, canvas, 128 + 128 + 30, 340) {
|
||||
SkBitmap bitmap;
|
||||
if (!GetResourceAsBitmap("color_wheel.gif", &bitmap)) {
|
||||
SkDebugf("\nCould not decode resource.\n");
|
||||
return;
|
||||
}
|
||||
sk_sp<SkImage> decodedImage = SkImage::MakeFromBitmap(bitmap);
|
||||
|
||||
auto params = SkImage::DeferredTextureImageUsageParams(SkMatrix::MakeScale(0.25f, 0.25f),
|
||||
kMedium_SkFilterQuality, 0);
|
||||
DrawDeferredTextureImageMipMapTree(canvas, decodedImage.get(), ¶ms, kN32_SkColorType);
|
||||
}
|
||||
|
||||
#endif
|
@ -99,6 +99,7 @@ gm_sources = [
|
||||
"$_gm/dashcircle.cpp",
|
||||
"$_gm/dashcubics.cpp",
|
||||
"$_gm/dashing.cpp",
|
||||
"$_gm/deferredtextureimage.cpp",
|
||||
"$_gm/degeneratesegments.cpp",
|
||||
"$_gm/dftext.cpp",
|
||||
"$_gm/dftext_blob_persp.cpp",
|
||||
|
@ -436,6 +436,60 @@ public:
|
||||
const SkIRect& clipBounds, SkIRect* outSubset,
|
||||
SkIPoint* offset) const;
|
||||
|
||||
/** Drawing params for which a deferred texture image data should be optimized. */
|
||||
struct DeferredTextureImageUsageParams {
|
||||
DeferredTextureImageUsageParams(const SkMatrix matrix, const SkFilterQuality quality,
|
||||
int preScaleMipLevel)
|
||||
: fMatrix(matrix), fQuality(quality), fPreScaleMipLevel(preScaleMipLevel) {}
|
||||
SkMatrix fMatrix;
|
||||
SkFilterQuality fQuality;
|
||||
int fPreScaleMipLevel;
|
||||
};
|
||||
|
||||
/**
|
||||
* This method allows clients to capture the data necessary to turn a SkImage into a texture-
|
||||
* backed image. If the original image is codec-backed this will decode into a format optimized
|
||||
* for the context represented by the proxy. This method is thread safe with respect to the
|
||||
* GrContext whence the proxy came. Clients allocate and manage the storage of the deferred
|
||||
* texture data and control its lifetime. No cleanup is required, thus it is safe to simply free
|
||||
* the memory out from under the data.
|
||||
*
|
||||
* The same method is used both for getting the size necessary for pre-uploaded texture data
|
||||
* and for retrieving the data. The params array represents the set of draws over which to
|
||||
* optimize the pre-upload data.
|
||||
*
|
||||
* When called with a null buffer this returns the size that the client must allocate in order
|
||||
* to create deferred texture data for this image (or zero if this is an inappropriate
|
||||
* candidate). The buffer allocated by the client should be 8 byte aligned.
|
||||
*
|
||||
* When buffer is not null this fills in the deferred texture data for this image in the
|
||||
* provided buffer (assuming this is an appropriate candidate image and the buffer is
|
||||
* appropriately aligned). Upon success the size written is returned, otherwise 0.
|
||||
*
|
||||
* 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& contextThreadSafeProxy,
|
||||
const DeferredTextureImageUsageParams deferredTextureImageUsageParams[],
|
||||
int paramCnt,
|
||||
void* buffer,
|
||||
SkColorSpace* dstColorSpace = nullptr,
|
||||
SkColorType dstColorType = kN32_SkColorType) const;
|
||||
|
||||
/**
|
||||
* Returns a texture-backed image from data produced in SkImage::getDeferredTextureImageData.
|
||||
* The context must be the context that provided the proxy passed to
|
||||
* getDeferredTextureImageData.
|
||||
*/
|
||||
static sk_sp<SkImage> MakeFromDeferredTextureImageData(GrContext* context, const void* data,
|
||||
SkBudgeted budgeted);
|
||||
|
||||
typedef std::function<void(GrBackendTexture)> BackendTextureReleaseProc;
|
||||
|
||||
/**
|
||||
|
@ -358,6 +358,19 @@ sk_sp<SkImage> SkImage::MakeFromTexture(GrContext* ctx,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy&,
|
||||
const DeferredTextureImageUsageParams[],
|
||||
int paramCnt, void* buffer,
|
||||
SkColorSpace* dstColorSpace,
|
||||
SkColorType dstColorType) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
sk_sp<SkImage> SkImage::MakeFromDeferredTextureImageData(GrContext* context, const void*,
|
||||
SkBudgeted) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool SkImage::MakeBackendTextureFromSkImage(GrContext*,
|
||||
sk_sp<SkImage>,
|
||||
GrBackendTexture*,
|
||||
|
@ -617,6 +617,385 @@ sk_sp<SkImage> SkImage::makeNonTextureImage() const {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace {
|
||||
struct MipMapLevelData {
|
||||
void* fPixelData;
|
||||
size_t fRowBytes;
|
||||
};
|
||||
|
||||
struct DeferredTextureImage {
|
||||
uint32_t fContextUniqueID;
|
||||
// Right now, the destination color mode is only considered when generating mipmaps
|
||||
SkDestinationSurfaceColorMode fColorMode;
|
||||
// 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 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];
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
static bool should_use_mip_maps(const SkImage::DeferredTextureImageUsageParams & param) {
|
||||
// There is a bug in the mipmap pre-generation logic in use in getDeferredTextureImageData.
|
||||
// This can cause runaway memory leaks, so we are disabling this path until we can
|
||||
// investigate further. crbug.com/669775
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class DTIBufferFiller
|
||||
{
|
||||
public:
|
||||
explicit DTIBufferFiller(char* bufferAsCharPtr)
|
||||
: bufferAsCharPtr_(bufferAsCharPtr) {}
|
||||
|
||||
void fillMember(const void* source, size_t memberOffset, size_t size) {
|
||||
memcpy(bufferAsCharPtr_ + memberOffset, source, size);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
char* bufferAsCharPtr_;
|
||||
};
|
||||
}
|
||||
|
||||
#define FILL_MEMBER(bufferFiller, member, source) \
|
||||
bufferFiller.fillMember(source, \
|
||||
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,
|
||||
SkColorType dstColorType) const {
|
||||
// Some quick-rejects where is makes no sense to return CPU data
|
||||
// e.g.
|
||||
// - texture backed
|
||||
// - picture backed
|
||||
//
|
||||
if (this->isTextureBacked()) {
|
||||
return 0;
|
||||
}
|
||||
if (as_IB(this)->onCanLazyGenerateOnGPU()) {
|
||||
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;
|
||||
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;
|
||||
if (!isScaled && this->peekPixels(&pixmap) && pixmap.info().colorType() == dstColorType) {
|
||||
info = pixmap.info();
|
||||
pixelSize = SkAlign8(pixmap.computeByteSize());
|
||||
if (!dstColorSpace) {
|
||||
pixmap.setColorSpace(nullptr);
|
||||
info = info.makeColorSpace(nullptr);
|
||||
}
|
||||
} else {
|
||||
if (!this->isLazyGenerated() && !this->peekPixels(nullptr)) {
|
||||
return 0;
|
||||
}
|
||||
if (SkImageCacherator* cacher = as_IB(this)->peekCacherator()) {
|
||||
// Generator backed image. Tweak info to trigger correct kind of decode.
|
||||
SkImageCacherator::CachedFormat cacheFormat = cacher->chooseCacheFormat(
|
||||
dstColorSpace, proxy.fCaps.get());
|
||||
info = cacher->buildCacheInfo(cacheFormat).makeWH(scaledSize.width(),
|
||||
scaledSize.height());
|
||||
} else {
|
||||
info = as_IB(this)->onImageInfo().makeWH(scaledSize.width(), scaledSize.height());
|
||||
if (!dstColorSpace) {
|
||||
info = info.makeColorSpace(nullptr);
|
||||
}
|
||||
}
|
||||
// Force color type to be the requested type.
|
||||
info = info.makeColorType(dstColorType);
|
||||
pixelSize = SkAlign8(SkAutoPixmapStorage::AllocSize(info, nullptr));
|
||||
if (fillMode) {
|
||||
// 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(decodePixmap, scaleFilterQuality,
|
||||
SkImage::kDisallow_CachingHint)) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
if (!this->readPixels(decodePixmap, 0, 0, SkImage::kDisallow_CachingHint)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
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 colorSpaceOffset = 0;
|
||||
size_t colorSpaceSize = 0;
|
||||
SkColorSpaceTransferFn fn;
|
||||
if (info.colorSpace()) {
|
||||
SkASSERT(dstColorSpace);
|
||||
SkASSERT(supportsColorSpace);
|
||||
colorSpaceOffset = size;
|
||||
colorSpaceSize = info.colorSpace()->writeToMemory(nullptr);
|
||||
size += colorSpaceSize;
|
||||
} 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);
|
||||
colorSpaceOffset = size;
|
||||
colorSpaceSize = this->colorSpace()->writeToMemory(nullptr);
|
||||
size += colorSpaceSize;
|
||||
}
|
||||
if (!fillMode) {
|
||||
return size;
|
||||
}
|
||||
char* bufferAsCharPtr = reinterpret_cast<char*>(buffer);
|
||||
char* pixelsAsCharPtr = bufferAsCharPtr + pixelOffset;
|
||||
void* pixels = pixelsAsCharPtr;
|
||||
|
||||
memcpy(reinterpret_cast<void*>(SkAlign8(reinterpret_cast<uintptr_t>(pixelsAsCharPtr))),
|
||||
pixmap.addr(), pixmap.computeByteSize());
|
||||
|
||||
// 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.
|
||||
SkDestinationSurfaceColorMode colorMode = SkDestinationSurfaceColorMode::kLegacy;
|
||||
if (proxy.fCaps->srgbSupport() && SkToBool(dstColorSpace) &&
|
||||
info.colorSpace() && info.colorSpace()->gammaCloseToSRGB()) {
|
||||
SkASSERT(supportsColorSpace);
|
||||
colorMode = SkDestinationSurfaceColorMode::kGammaAndColorSpaceAware;
|
||||
}
|
||||
|
||||
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, fColorMode, &colorMode);
|
||||
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, 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);
|
||||
if (info.colorSpace()) {
|
||||
info.colorSpace()->writeToMemory(bufferAsCharPtr + colorSpaceOffset);
|
||||
} else {
|
||||
SkASSERT(this->colorSpace() && this->colorSpace()->isNumericalTransferFn(&fn));
|
||||
SkASSERT(!dstColorSpace);
|
||||
this->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.computeByteSize());
|
||||
|
||||
if (useMipMaps) {
|
||||
static_assert(std::is_standard_layout<MipMapLevelData>::value,
|
||||
"offsetof, which we use below, requires the type have a standard layout");
|
||||
|
||||
std::unique_ptr<SkMipMap> mipmaps(SkMipMap::Build(pixmap, colorMode, 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.computeByteSize() <=
|
||||
bufferAsCharPtr + pixelOffset + pixelSize);
|
||||
|
||||
memcpy(mipLevelPtr, mipLevel.fPixmap.addr(), mipLevel.fPixmap.computeByteSize());
|
||||
|
||||
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.computeByteSize());
|
||||
}
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
sk_sp<SkImage> SkImage::MakeFromDeferredTextureImageData(GrContext* context, const void* data,
|
||||
SkBudgeted budgeted) {
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
}
|
||||
const DeferredTextureImage* dti = reinterpret_cast<const DeferredTextureImage*>(data);
|
||||
|
||||
if (!context || context->uniqueID() != dti->fContextUniqueID || context->abandoned()) {
|
||||
return nullptr;
|
||||
}
|
||||
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);
|
||||
if (mipLevelCount == 1) {
|
||||
SkPixmap pixmap;
|
||||
pixmap.reset(info, dti->fMipMapLevelData[0].fPixelData, dti->fMipMapLevelData[0].fRowBytes);
|
||||
|
||||
// Pass nullptr for the |dstColorSpace|. This opts in to more lenient color space
|
||||
// verification. This is ok because we've already verified the color space in
|
||||
// getDeferredTextureImageData().
|
||||
sk_sp<GrTextureProxy> proxy(GrUploadPixmapToTextureProxy(
|
||||
context->resourceProvider(), pixmap, budgeted, nullptr));
|
||||
if (!proxy) {
|
||||
return nullptr;
|
||||
}
|
||||
return sk_make_sp<SkImage_Gpu>(context, kNeedNewImageUniqueID, pixmap.alphaType(),
|
||||
std::move(proxy), std::move(colorSpace), budgeted);
|
||||
} else {
|
||||
std::unique_ptr<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->fColorMode);
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool SkImage::MakeBackendTextureFromSkImage(GrContext* ctx,
|
||||
sk_sp<SkImage> image,
|
||||
GrBackendTexture* backendTexture,
|
||||
|
@ -967,6 +967,179 @@ DEF_GPUTEST(SkImage_MakeCrossContextFromPixmapRelease, reporter, options) {
|
||||
});
|
||||
}
|
||||
|
||||
static void check_images_same(skiatest::Reporter* reporter, const SkImage* a, const SkImage* b) {
|
||||
if (a->width() != b->width() || a->height() != b->height()) {
|
||||
ERRORF(reporter, "Images must have the same size");
|
||||
return;
|
||||
}
|
||||
if (a->alphaType() != b->alphaType()) {
|
||||
ERRORF(reporter, "Images must have the same alpha type");
|
||||
return;
|
||||
}
|
||||
|
||||
SkImageInfo info = SkImageInfo::MakeN32Premul(a->width(), a->height());
|
||||
SkAutoPixmapStorage apm;
|
||||
SkAutoPixmapStorage bpm;
|
||||
|
||||
apm.alloc(info);
|
||||
bpm.alloc(info);
|
||||
|
||||
if (!a->readPixels(apm, 0, 0)) {
|
||||
ERRORF(reporter, "Could not read image a's pixels");
|
||||
return;
|
||||
}
|
||||
if (!b->readPixels(bpm, 0, 0)) {
|
||||
ERRORF(reporter, "Could not read image b's pixels");
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto y = 0; y < info.height(); ++y) {
|
||||
for (auto x = 0; x < info.width(); ++x) {
|
||||
uint32_t pixelA = *apm.addr32(x, y);
|
||||
uint32_t pixelB = *bpm.addr32(x, y);
|
||||
if (pixelA != pixelB) {
|
||||
ERRORF(reporter, "Expected image pixels to be the same. At %d,%d 0x%08x != 0x%08x",
|
||||
x, y, pixelA, pixelB);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DeferredTextureImage, reporter, ctxInfo) {
|
||||
GrContext* context = ctxInfo.grContext();
|
||||
sk_gpu_test::TestContext* testContext = ctxInfo.testContext();
|
||||
sk_sp<GrContextThreadSafeProxy> proxy = context->threadSafeProxy();
|
||||
|
||||
GrContextFactory otherFactory;
|
||||
ContextInfo otherContextInfo = otherFactory.getContextInfo(ctxInfo.type());
|
||||
|
||||
testContext->makeCurrent();
|
||||
REPORTER_ASSERT(reporter, proxy);
|
||||
auto createLarge = [context] {
|
||||
return create_image_large(context->caps()->maxTextureSize());
|
||||
};
|
||||
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}},
|
||||
nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, true },
|
||||
{ create_codec_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
|
||||
nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, true },
|
||||
{ create_data_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
|
||||
nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, true },
|
||||
{ create_picture_image, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
|
||||
nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false },
|
||||
{ [context] { return create_gpu_image(context); },
|
||||
{{SkMatrix::I(), kNone_SkFilterQuality, 0}},
|
||||
nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false },
|
||||
// Create a texture image in a another GrContext.
|
||||
{ [testContext, otherContextInfo] {
|
||||
otherContextInfo.testContext()->makeCurrent();
|
||||
sk_sp<SkImage> otherContextImage = create_gpu_image(otherContextInfo.grContext());
|
||||
testContext->makeCurrent();
|
||||
return otherContextImage;
|
||||
}, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
|
||||
nullptr, kN32_SkColorType, kNone_SkFilterQuality, 1, false },
|
||||
// Create an image that is too large to upload.
|
||||
{ createLarge, {{SkMatrix::I(), kNone_SkFilterQuality, 0}},
|
||||
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}},
|
||||
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}},
|
||||
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}},
|
||||
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 },
|
||||
};
|
||||
|
||||
|
||||
for (auto testCase : testCases) {
|
||||
sk_sp<SkImage> image(testCase.fImageFactory());
|
||||
if (!image) {
|
||||
ERRORF(reporter, "Failed to create image!");
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t size = image->getDeferredTextureImageData(*proxy, testCase.fParams.data(),
|
||||
static_cast<int>(testCase.fParams.size()),
|
||||
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.",
|
||||
kFS[testCase.fExpectation]);
|
||||
}
|
||||
if (size) {
|
||||
void* buffer = sk_malloc_throw(size);
|
||||
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, 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, testCase.fColorSpace.get(),
|
||||
testCase.fColorType)) {
|
||||
ERRORF(reporter, "deferred image size succeeded but creation failed.");
|
||||
} else {
|
||||
for (auto budgeted : { SkBudgeted::kNo, SkBudgeted::kYes }) {
|
||||
sk_sp<SkImage> newImage(
|
||||
SkImage::MakeFromDeferredTextureImageData(context, buffer, budgeted));
|
||||
REPORTER_ASSERT(reporter, newImage != nullptr);
|
||||
if (newImage) {
|
||||
// Scale the image in software for comparison.
|
||||
SkImageInfo scaled_info = SkImageInfo::MakeN32(
|
||||
image->width() / testCase.fExpectedScaleFactor,
|
||||
image->height() / testCase.fExpectedScaleFactor,
|
||||
image->alphaType());
|
||||
SkAutoPixmapStorage scaled;
|
||||
scaled.alloc(scaled_info);
|
||||
image->scalePixels(scaled, testCase.fExpectedQuality);
|
||||
sk_sp<SkImage> scaledImage = SkImage::MakeRasterCopy(scaled);
|
||||
check_images_same(reporter, scaledImage.get(), newImage.get());
|
||||
}
|
||||
// The other context should not be able to create images from texture data
|
||||
// created by the original context.
|
||||
sk_sp<SkImage> newImage2(SkImage::MakeFromDeferredTextureImageData(
|
||||
otherContextInfo.grContext(), buffer, budgeted));
|
||||
REPORTER_ASSERT(reporter, !newImage2);
|
||||
testContext->makeCurrent();
|
||||
}
|
||||
}
|
||||
sk_free(buffer);
|
||||
}
|
||||
|
||||
testContext->makeCurrent();
|
||||
context->flush();
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t GetIdForBackendObject(GrContext* ctx, GrBackendObject object) {
|
||||
if (!object) {
|
||||
return 0;
|
||||
|
Loading…
Reference in New Issue
Block a user