skia2/tests/ImageTest.cpp
kkinnunen 4e18413b78 Move SkImage tests from SurfaceTest to ImageTest
The general SkImage features seem to be tested in
ImageTest instead of SurfaceTest.

Helps in reviewing further reformatting of SurfaceTest.

BUG=skia:2992

Review URL: https://codereview.chromium.org/1452123002
2015-11-17 22:53:28 -08:00

794 lines
30 KiB
C++

/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkBitmap.h"
#include "SkCanvas.h"
#include "SkData.h"
#include "SkDevice.h"
#include "SkImageEncoder.h"
#include "SkImage_Base.h"
#include "SkPicture.h"
#include "SkPictureRecorder.h"
#include "SkPixelSerializer.h"
#include "SkRRect.h"
#include "SkStream.h"
#include "SkSurface.h"
#include "SkUtils.h"
#include "Test.h"
#if SK_SUPPORT_GPU
#include "GrContextFactory.h"
#include "GrTest.h"
#include "gl/GrGLInterface.h"
#include "gl/GrGLUtil.h"
#else
class GrContextFactory;
class GrContext;
#endif
static void assert_equal(skiatest::Reporter* reporter, SkImage* a, const SkIRect* subsetA,
SkImage* b) {
const int widthA = subsetA ? subsetA->width() : a->width();
const int heightA = subsetA ? subsetA->height() : a->height();
REPORTER_ASSERT(reporter, widthA == b->width());
REPORTER_ASSERT(reporter, heightA == b->height());
#if 0
// see https://bug.skia.org/3965
bool AO = a->isOpaque();
bool BO = b->isOpaque();
REPORTER_ASSERT(reporter, AO == BO);
#endif
SkImageInfo info = SkImageInfo::MakeN32(widthA, heightA,
a->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
SkAutoPixmapStorage pmapA, pmapB;
pmapA.alloc(info);
pmapB.alloc(info);
const int srcX = subsetA ? subsetA->x() : 0;
const int srcY = subsetA ? subsetA->y() : 0;
REPORTER_ASSERT(reporter, a->readPixels(pmapA, srcX, srcY));
REPORTER_ASSERT(reporter, b->readPixels(pmapB, 0, 0));
const size_t widthBytes = widthA * info.bytesPerPixel();
for (int y = 0; y < heightA; ++y) {
REPORTER_ASSERT(reporter, !memcmp(pmapA.addr32(0, y), pmapB.addr32(0, y), widthBytes));
}
}
static SkImage* make_image(GrContext* ctx, int w, int h, const SkIRect& ir) {
const SkImageInfo info = SkImageInfo::MakeN32(w, h, kOpaque_SkAlphaType);
SkAutoTUnref<SkSurface> surface(ctx ?
SkSurface::NewRenderTarget(ctx, SkSurface::kNo_Budgeted, info) :
SkSurface::NewRaster(info));
SkCanvas* canvas = surface->getCanvas();
canvas->clear(SK_ColorWHITE);
SkPaint paint;
paint.setColor(SK_ColorBLACK);
canvas->drawRect(SkRect::Make(ir), paint);
return surface->newImageSnapshot();
}
static void test_encode(skiatest::Reporter* reporter, GrContext* ctx) {
const SkIRect ir = SkIRect::MakeXYWH(5, 5, 10, 10);
SkAutoTUnref<SkImage> orig(make_image(ctx, 20, 20, ir));
SkAutoTUnref<SkData> origEncoded(orig->encode());
REPORTER_ASSERT(reporter, origEncoded);
REPORTER_ASSERT(reporter, origEncoded->size() > 0);
SkAutoTUnref<SkImage> decoded(SkImage::NewFromEncoded(origEncoded));
REPORTER_ASSERT(reporter, decoded);
assert_equal(reporter, orig, nullptr, decoded);
// Now see if we can instantiate an image from a subset of the surface/origEncoded
decoded.reset(SkImage::NewFromEncoded(origEncoded, &ir));
REPORTER_ASSERT(reporter, decoded);
assert_equal(reporter, orig, &ir, decoded);
}
DEF_TEST(Image_Encode_Cpu, reporter) {
test_encode(reporter, nullptr);
}
#if SK_SUPPORT_GPU
DEF_GPUTEST(Image_Encode_Gpu, reporter, factory) {
GrContext* ctx = factory->get(GrContextFactory::kNative_GLContextType);
if (!ctx) {
REPORTER_ASSERT(reporter, false);
return;
}
test_encode(reporter, ctx);
}
#endif
namespace {
const char* kSerializedData = "serialized";
class MockSerializer : public SkPixelSerializer {
public:
MockSerializer(SkData* (*func)()) : fFunc(func), fDidEncode(false) { }
bool didEncode() const { return fDidEncode; }
protected:
bool onUseEncodedData(const void*, size_t) override {
return false;
}
SkData* onEncodePixels(const SkImageInfo&, const void*, size_t) override {
fDidEncode = true;
return fFunc();
}
private:
SkData* (*fFunc)();
bool fDidEncode;
typedef SkPixelSerializer INHERITED;
};
} // anonymous namespace
// Test that SkImage encoding observes custom pixel serializers.
DEF_TEST(Image_Encode_Serializer, reporter) {
MockSerializer serializer([]() -> SkData* { return SkData::NewWithCString(kSerializedData); });
const SkIRect ir = SkIRect::MakeXYWH(5, 5, 10, 10);
SkAutoTUnref<SkImage> image(make_image(nullptr, 20, 20, ir));
SkAutoTUnref<SkData> encoded(image->encode(&serializer));
SkAutoTUnref<SkData> reference(SkData::NewWithCString(kSerializedData));
REPORTER_ASSERT(reporter, serializer.didEncode());
REPORTER_ASSERT(reporter, encoded);
REPORTER_ASSERT(reporter, encoded->size() > 0);
REPORTER_ASSERT(reporter, encoded->equals(reference));
}
// Test that image encoding failures do not break picture serialization/deserialization.
DEF_TEST(Image_Serialize_Encoding_Failure, reporter) {
SkAutoTUnref<SkSurface> surface(SkSurface::NewRasterN32Premul(100, 100));
surface->getCanvas()->clear(SK_ColorGREEN);
SkAutoTUnref<SkImage> image(surface->newImageSnapshot());
REPORTER_ASSERT(reporter, image);
SkPictureRecorder recorder;
SkCanvas* canvas = recorder.beginRecording(100, 100);
canvas->drawImage(image, 0, 0);
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
REPORTER_ASSERT(reporter, picture);
REPORTER_ASSERT(reporter, picture->approximateOpCount() > 0);
MockSerializer emptySerializer([]() -> SkData* { return SkData::NewEmpty(); });
MockSerializer nullSerializer([]() -> SkData* { return nullptr; });
MockSerializer* serializers[] = { &emptySerializer, &nullSerializer };
for (size_t i = 0; i < SK_ARRAY_COUNT(serializers); ++i) {
SkDynamicMemoryWStream wstream;
REPORTER_ASSERT(reporter, !serializers[i]->didEncode());
picture->serialize(&wstream, serializers[i]);
REPORTER_ASSERT(reporter, serializers[i]->didEncode());
SkAutoTDelete<SkStream> rstream(wstream.detachAsStream());
SkAutoTUnref<SkPicture> deserialized(SkPicture::CreateFromStream(rstream));
REPORTER_ASSERT(reporter, deserialized);
REPORTER_ASSERT(reporter, deserialized->approximateOpCount() > 0);
}
}
DEF_TEST(Image_NewRasterCopy, reporter) {
const SkPMColor red = SkPackARGB32(0xFF, 0xFF, 0, 0);
const SkPMColor green = SkPackARGB32(0xFF, 0, 0xFF, 0);
const SkPMColor blue = SkPackARGB32(0xFF, 0, 0, 0xFF);
SkPMColor colors[] = { red, green, blue, 0 };
SkAutoTUnref<SkColorTable> ctable(new SkColorTable(colors, SK_ARRAY_COUNT(colors)));
// The colortable made a copy, so we can trash the original colors
memset(colors, 0xFF, sizeof(colors));
const SkImageInfo srcInfo = SkImageInfo::Make(2, 2, kIndex_8_SkColorType, kPremul_SkAlphaType);
const size_t srcRowBytes = 2 * sizeof(uint8_t);
uint8_t indices[] = { 0, 1, 2, 3 };
SkAutoTUnref<SkImage> image(SkImage::NewRasterCopy(srcInfo, indices, srcRowBytes, ctable));
// The image made a copy, so we can trash the original indices
memset(indices, 0xFF, sizeof(indices));
const SkImageInfo dstInfo = SkImageInfo::MakeN32Premul(2, 2);
const size_t dstRowBytes = 2 * sizeof(SkPMColor);
SkPMColor pixels[4];
memset(pixels, 0xFF, sizeof(pixels)); // init with values we don't expect
image->readPixels(dstInfo, pixels, dstRowBytes, 0, 0);
REPORTER_ASSERT(reporter, red == pixels[0]);
REPORTER_ASSERT(reporter, green == pixels[1]);
REPORTER_ASSERT(reporter, blue == pixels[2]);
REPORTER_ASSERT(reporter, 0 == pixels[3]);
}
// Test that a draw that only partially covers the drawing surface isn't
// interpreted as covering the entire drawing surface (i.e., exercise one of the
// conditions of SkCanvas::wouldOverwriteEntireSurface()).
DEF_TEST(Image_RetainSnapshot, reporter) {
const SkPMColor red = SkPackARGB32(0xFF, 0xFF, 0, 0);
const SkPMColor green = SkPackARGB32(0xFF, 0, 0xFF, 0);
SkImageInfo info = SkImageInfo::MakeN32Premul(2, 2);
SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(info));
surface->getCanvas()->clear(0xFF00FF00);
SkPMColor pixels[4];
memset(pixels, 0xFF, sizeof(pixels)); // init with values we don't expect
const SkImageInfo dstInfo = SkImageInfo::MakeN32Premul(2, 2);
const size_t dstRowBytes = 2 * sizeof(SkPMColor);
SkAutoTUnref<SkImage> image1(surface->newImageSnapshot());
REPORTER_ASSERT(reporter, image1->readPixels(dstInfo, pixels, dstRowBytes, 0, 0));
for (size_t i = 0; i < SK_ARRAY_COUNT(pixels); ++i) {
REPORTER_ASSERT(reporter, pixels[i] == green);
}
SkPaint paint;
paint.setXfermodeMode(SkXfermode::kSrc_Mode);
paint.setColor(SK_ColorRED);
surface->getCanvas()->drawRect(SkRect::MakeXYWH(1, 1, 1, 1), paint);
SkAutoTUnref<SkImage> image2(surface->newImageSnapshot());
REPORTER_ASSERT(reporter, image2->readPixels(dstInfo, pixels, dstRowBytes, 0, 0));
REPORTER_ASSERT(reporter, pixels[0] == green);
REPORTER_ASSERT(reporter, pixels[1] == green);
REPORTER_ASSERT(reporter, pixels[2] == green);
REPORTER_ASSERT(reporter, pixels[3] == red);
}
/////////////////////////////////////////////////////////////////////////////////////////////////
static void make_bitmap_mutable(SkBitmap* bm) {
bm->allocN32Pixels(10, 10);
}
static void make_bitmap_immutable(SkBitmap* bm) {
bm->allocN32Pixels(10, 10);
bm->setImmutable();
}
DEF_TEST(image_newfrombitmap, reporter) {
const struct {
void (*fMakeProc)(SkBitmap*);
bool fExpectPeekSuccess;
bool fExpectSharedID;
bool fExpectLazy;
} rec[] = {
{ make_bitmap_mutable, true, false, false },
{ make_bitmap_immutable, true, true, false },
};
for (size_t i = 0; i < SK_ARRAY_COUNT(rec); ++i) {
SkBitmap bm;
rec[i].fMakeProc(&bm);
SkAutoTUnref<SkImage> image(SkImage::NewFromBitmap(bm));
SkPixmap pmap;
const bool sharedID = (image->uniqueID() == bm.getGenerationID());
REPORTER_ASSERT(reporter, sharedID == rec[i].fExpectSharedID);
const bool peekSuccess = image->peekPixels(&pmap);
REPORTER_ASSERT(reporter, peekSuccess == rec[i].fExpectPeekSuccess);
const bool lazy = image->isLazyGenerated();
REPORTER_ASSERT(reporter, lazy == rec[i].fExpectLazy);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
#if SK_SUPPORT_GPU
static SkImage* make_gpu_image(GrContext* ctx, const SkImageInfo& info, SkColor color) {
const SkSurface::Budgeted budgeted = SkSurface::kNo_Budgeted;
SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTarget(ctx, budgeted, info, 0));
surface->getCanvas()->drawColor(color);
return surface->newImageSnapshot();
}
#include "SkBitmapCache.h"
/*
* This tests the caching (and preemptive purge) of the raster equivalent of a gpu-image.
* We cache it for performance when drawing into a raster surface.
*
* A cleaner test would know if each drawImage call triggered a read-back from the gpu,
* but we don't have that facility (at the moment) so we use a little internal knowledge
* of *how* the raster version is cached, and look for that.
*/
DEF_GPUTEST(SkImage_Gpu2Cpu, reporter, factory) {
GrContext* ctx = factory->get(GrContextFactory::kNative_GLContextType);
if (!ctx) {
REPORTER_ASSERT(reporter, false);
return;
}
const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
SkAutoTUnref<SkImage> image(make_gpu_image(ctx, info, SK_ColorRED));
const uint32_t uniqueID = image->uniqueID();
SkAutoTUnref<SkSurface> surface(SkSurface::NewRaster(info));
// now we can test drawing a gpu-backed image into a cpu-backed surface
{
SkBitmap cachedBitmap;
REPORTER_ASSERT(reporter, !SkBitmapCache::Find(uniqueID, &cachedBitmap));
}
surface->getCanvas()->drawImage(image, 0, 0);
{
SkBitmap cachedBitmap;
if (SkBitmapCache::Find(uniqueID, &cachedBitmap)) {
REPORTER_ASSERT(reporter, cachedBitmap.getGenerationID() == uniqueID);
REPORTER_ASSERT(reporter, cachedBitmap.isImmutable());
REPORTER_ASSERT(reporter, cachedBitmap.getPixels());
} else {
// unexpected, but not really a bug, since the cache is global and this test may be
// run w/ other threads competing for its budget.
SkDebugf("SkImage_Gpu2Cpu : cachedBitmap was already purged\n");
}
}
image.reset(nullptr);
{
SkBitmap cachedBitmap;
REPORTER_ASSERT(reporter, !SkBitmapCache::Find(uniqueID, &cachedBitmap));
}
}
#endif
// https://bug.skia.org/4390
DEF_TEST(ImageFromIndex8Bitmap, r) {
SkPMColor pmColors[1] = {SkPreMultiplyColor(SK_ColorWHITE)};
SkBitmap bm;
SkAutoTUnref<SkColorTable> ctable(
new SkColorTable(pmColors, SK_ARRAY_COUNT(pmColors)));
SkImageInfo info =
SkImageInfo::Make(1, 1, kIndex_8_SkColorType, kPremul_SkAlphaType);
bm.allocPixels(info, nullptr, ctable);
SkAutoLockPixels autoLockPixels(bm);
*bm.getAddr8(0, 0) = 0;
SkAutoTUnref<SkImage> img(SkImage::NewFromBitmap(bm));
REPORTER_ASSERT(r, img.get() != nullptr);
}
// TODO: The tests below were moved from SurfaceTests and should be reformatted.
enum ImageType {
kRasterCopy_ImageType,
kRasterData_ImageType,
kRasterProc_ImageType,
kGpu_ImageType,
kCodec_ImageType,
};
#include "SkImageGenerator.h"
class EmptyGenerator : public SkImageGenerator {
public:
EmptyGenerator() : SkImageGenerator(SkImageInfo::MakeN32Premul(0, 0)) {}
};
static void test_empty_image(skiatest::Reporter* reporter) {
const SkImageInfo info = SkImageInfo::Make(0, 0, kN32_SkColorType, kPremul_SkAlphaType);
REPORTER_ASSERT(reporter, nullptr == SkImage::NewRasterCopy(info, nullptr, 0));
REPORTER_ASSERT(reporter, nullptr == SkImage::NewRasterData(info, nullptr, 0));
REPORTER_ASSERT(reporter, nullptr == SkImage::NewFromRaster(info, nullptr, 0, nullptr, nullptr));
REPORTER_ASSERT(reporter, nullptr == SkImage::NewFromGenerator(new EmptyGenerator));
}
static void test_image(skiatest::Reporter* reporter) {
SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
size_t rowBytes = info.minRowBytes();
size_t size = info.getSafeSize(rowBytes);
SkData* data = SkData::NewUninitialized(size);
REPORTER_ASSERT(reporter, data->unique());
SkImage* image = SkImage::NewRasterData(info, data, rowBytes);
REPORTER_ASSERT(reporter, !data->unique());
image->unref();
REPORTER_ASSERT(reporter, data->unique());
data->unref();
}
// Want to ensure that our Release is called when the owning image is destroyed
struct ReleaseDataContext {
skiatest::Reporter* fReporter;
SkData* fData;
static void Release(const void* pixels, void* context) {
ReleaseDataContext* state = (ReleaseDataContext*)context;
REPORTER_ASSERT(state->fReporter, state->fData);
state->fData->unref();
state->fData = nullptr;
}
};
// May we (soon) eliminate the need to keep testing this, by hiding the bloody device!
#include "SkDevice.h"
static uint32_t get_legacy_gen_id(SkSurface* surf) {
SkBaseDevice* device = surf->getCanvas()->getDevice_just_for_deprecated_compatibility_testing();
return device->accessBitmap(false).getGenerationID();
}
/*
* Test legacy behavor of bumping the surface's device's bitmap's genID when we access its
* texture handle for writing.
*
* Note: this needs to be tested separately from checking newImageSnapshot, as calling that
* can also incidentally bump the genID (when a new backing surface is created).
*/
template <class F>
static void test_texture_handle_genID(skiatest::Reporter* reporter, SkSurface* surf, F f) {
const uint32_t gen0 = get_legacy_gen_id(surf);
f(surf, SkSurface::kFlushRead_BackendHandleAccess);
const uint32_t gen1 = get_legacy_gen_id(surf);
REPORTER_ASSERT(reporter, gen0 == gen1);
f(surf, SkSurface::kFlushWrite_BackendHandleAccess);
const uint32_t gen2 = get_legacy_gen_id(surf);
REPORTER_ASSERT(reporter, gen0 != gen2);
f(surf, SkSurface::kDiscardWrite_BackendHandleAccess);
const uint32_t gen3 = get_legacy_gen_id(surf);
REPORTER_ASSERT(reporter, gen0 != gen3);
REPORTER_ASSERT(reporter, gen2 != gen3);
}
template <class F>
static void test_backend_handle(skiatest::Reporter* reporter, SkSurface* surf, F f) {
SkAutoTUnref<SkImage> image0(surf->newImageSnapshot());
GrBackendObject obj = f(surf, SkSurface::kFlushRead_BackendHandleAccess);
REPORTER_ASSERT(reporter, obj != 0);
SkAutoTUnref<SkImage> image1(surf->newImageSnapshot());
// just read access should not affect the snapshot
REPORTER_ASSERT(reporter, image0->uniqueID() == image1->uniqueID());
obj = f(surf, SkSurface::kFlushWrite_BackendHandleAccess);
REPORTER_ASSERT(reporter, obj != 0);
SkAutoTUnref<SkImage> image2(surf->newImageSnapshot());
// expect a new image, since we claimed we would write
REPORTER_ASSERT(reporter, image0->uniqueID() != image2->uniqueID());
obj = f(surf, SkSurface::kDiscardWrite_BackendHandleAccess);
REPORTER_ASSERT(reporter, obj != 0);
SkAutoTUnref<SkImage> image3(surf->newImageSnapshot());
// expect a new(er) image, since we claimed we would write
REPORTER_ASSERT(reporter, image0->uniqueID() != image3->uniqueID());
REPORTER_ASSERT(reporter, image2->uniqueID() != image3->uniqueID());
}
static SkImage* create_image(skiatest::Reporter* reporter,
ImageType imageType, GrContext* context, SkColor color,
ReleaseDataContext* releaseContext) {
const SkPMColor pmcolor = SkPreMultiplyColor(color);
const SkImageInfo info = SkImageInfo::MakeN32Premul(10, 10);
const size_t rowBytes = info.minRowBytes();
const size_t size = rowBytes * info.height();
SkAutoTUnref<SkData> data(SkData::NewUninitialized(size));
void* addr = data->writable_data();
sk_memset32((SkPMColor*)addr, pmcolor, SkToInt(size >> 2));
switch (imageType) {
case kRasterCopy_ImageType:
return SkImage::NewRasterCopy(info, addr, rowBytes);
case kRasterData_ImageType:
return SkImage::NewRasterData(info, data, rowBytes);
case kRasterProc_ImageType:
SkASSERT(releaseContext);
releaseContext->fData = SkRef(data.get());
return SkImage::NewFromRaster(info, addr, rowBytes,
ReleaseDataContext::Release, releaseContext);
case kGpu_ImageType: {
SkAutoTUnref<SkSurface> surf(
SkSurface::NewRenderTarget(context, SkSurface::kNo_Budgeted, info, 0));
surf->getCanvas()->clear(color);
// test our backing texture / rendertarget while were here...
auto textureAccessorFunc =
[](SkSurface* surf, SkSurface::BackendHandleAccess access) -> GrBackendObject {
return surf->getTextureHandle(access); };
auto renderTargetAccessorFunc =
[](SkSurface* surf, SkSurface::BackendHandleAccess access) -> GrBackendObject {
GrBackendObject obj;
SkAssertResult(surf->getRenderTargetHandle(&obj, access));
return obj; };
test_backend_handle(reporter, surf, textureAccessorFunc);
test_backend_handle(reporter, surf, renderTargetAccessorFunc);
test_texture_handle_genID(reporter, surf, textureAccessorFunc);
test_texture_handle_genID(reporter, surf, renderTargetAccessorFunc);
// redraw so our returned image looks as expected.
surf->getCanvas()->clear(color);
return surf->newImageSnapshot();
}
case kCodec_ImageType: {
SkBitmap bitmap;
bitmap.installPixels(info, addr, rowBytes);
SkAutoTUnref<SkData> src(
SkImageEncoder::EncodeData(bitmap, SkImageEncoder::kPNG_Type, 100));
return SkImage::NewFromEncoded(src);
}
}
SkASSERT(false);
return nullptr;
}
static void set_pixels(SkPMColor pixels[], int count, SkPMColor color) {
sk_memset32(pixels, color, count);
}
static bool has_pixels(const SkPMColor pixels[], int count, SkPMColor expected) {
for (int i = 0; i < count; ++i) {
if (pixels[i] != expected) {
return false;
}
}
return true;
}
static void test_image_readpixels(skiatest::Reporter* reporter, SkImage* image,
SkPMColor expected) {
const SkPMColor notExpected = ~expected;
const int w = 2, h = 2;
const size_t rowBytes = w * sizeof(SkPMColor);
SkPMColor pixels[w*h];
SkImageInfo info;
info = SkImageInfo::MakeUnknown(w, h);
REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, 0));
// out-of-bounds should fail
info = SkImageInfo::MakeN32Premul(w, h);
REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, -w, 0));
REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, -h));
REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, image->width(), 0));
REPORTER_ASSERT(reporter, !image->readPixels(info, pixels, rowBytes, 0, image->height()));
// top-left should succeed
set_pixels(pixels, w*h, notExpected);
REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, 0, 0));
REPORTER_ASSERT(reporter, has_pixels(pixels, w*h, expected));
// bottom-right should succeed
set_pixels(pixels, w*h, notExpected);
REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes,
image->width() - w, image->height() - h));
REPORTER_ASSERT(reporter, has_pixels(pixels, w*h, expected));
// partial top-left should succeed
set_pixels(pixels, w*h, notExpected);
REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes, -1, -1));
REPORTER_ASSERT(reporter, pixels[3] == expected);
REPORTER_ASSERT(reporter, has_pixels(pixels, w*h - 1, notExpected));
// partial bottom-right should succeed
set_pixels(pixels, w*h, notExpected);
REPORTER_ASSERT(reporter, image->readPixels(info, pixels, rowBytes,
image->width() - 1, image->height() - 1));
REPORTER_ASSERT(reporter, pixels[0] == expected);
REPORTER_ASSERT(reporter, has_pixels(&pixels[1], w*h - 1, notExpected));
}
static void check_legacy_bitmap(skiatest::Reporter* reporter, const SkImage* image,
const SkBitmap& bitmap, SkImage::LegacyBitmapMode mode) {
REPORTER_ASSERT(reporter, image->width() == bitmap.width());
REPORTER_ASSERT(reporter, image->height() == bitmap.height());
REPORTER_ASSERT(reporter, image->isOpaque() == bitmap.isOpaque());
if (SkImage::kRO_LegacyBitmapMode == mode) {
REPORTER_ASSERT(reporter, bitmap.isImmutable());
}
SkAutoLockPixels alp(bitmap);
REPORTER_ASSERT(reporter, bitmap.getPixels());
const SkImageInfo info = SkImageInfo::MakeN32(1, 1, bitmap.alphaType());
SkPMColor imageColor;
REPORTER_ASSERT(reporter, image->readPixels(info, &imageColor, sizeof(SkPMColor), 0, 0));
REPORTER_ASSERT(reporter, imageColor == *bitmap.getAddr32(0, 0));
}
static void test_legacy_bitmap(skiatest::Reporter* reporter, const SkImage* image) {
const SkImage::LegacyBitmapMode modes[] = {
SkImage::kRO_LegacyBitmapMode,
SkImage::kRW_LegacyBitmapMode,
};
for (size_t i = 0; i < SK_ARRAY_COUNT(modes); ++i) {
SkBitmap bitmap;
REPORTER_ASSERT(reporter, image->asLegacyBitmap(&bitmap, modes[i]));
check_legacy_bitmap(reporter, image, bitmap, modes[i]);
// Test subsetting to exercise the rowBytes logic.
SkBitmap tmp;
REPORTER_ASSERT(reporter, bitmap.extractSubset(&tmp, SkIRect::MakeWH(image->width() / 2,
image->height() / 2)));
SkAutoTUnref<SkImage> subsetImage(SkImage::NewFromBitmap(tmp));
REPORTER_ASSERT(reporter, subsetImage);
SkBitmap subsetBitmap;
REPORTER_ASSERT(reporter, subsetImage->asLegacyBitmap(&subsetBitmap, modes[i]));
check_legacy_bitmap(reporter, subsetImage, subsetBitmap, modes[i]);
}
}
static void test_imagepeek(skiatest::Reporter* reporter, GrContextFactory* factory) {
static const struct {
ImageType fType;
bool fPeekShouldSucceed;
const char* fName;
} gRec[] = {
{ kRasterCopy_ImageType, true, "RasterCopy" },
{ kRasterData_ImageType, true, "RasterData" },
{ kRasterProc_ImageType, true, "RasterProc" },
{ kGpu_ImageType, false, "Gpu" },
{ kCodec_ImageType, false, "Codec" },
};
const SkColor color = SK_ColorRED;
const SkPMColor pmcolor = SkPreMultiplyColor(color);
GrContext* ctx = nullptr;
#if SK_SUPPORT_GPU
ctx = factory->get(GrContextFactory::kNative_GLContextType);
if (nullptr == ctx) {
return;
}
#endif
ReleaseDataContext releaseCtx;
releaseCtx.fReporter = reporter;
for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
SkImageInfo info;
size_t rowBytes;
releaseCtx.fData = nullptr;
SkAutoTUnref<SkImage> image(create_image(reporter, gRec[i].fType, ctx, color, &releaseCtx));
if (!image.get()) {
SkDebugf("failed to createImage[%d] %s\n", i, gRec[i].fName);
continue; // gpu may not be enabled
}
if (kRasterProc_ImageType == gRec[i].fType) {
REPORTER_ASSERT(reporter, nullptr != releaseCtx.fData); // we are tracking the data
} else {
REPORTER_ASSERT(reporter, nullptr == releaseCtx.fData); // we ignored the context
}
test_legacy_bitmap(reporter, image);
const void* addr = image->peekPixels(&info, &rowBytes);
bool success = SkToBool(addr);
REPORTER_ASSERT(reporter, gRec[i].fPeekShouldSucceed == success);
if (success) {
REPORTER_ASSERT(reporter, 10 == info.width());
REPORTER_ASSERT(reporter, 10 == info.height());
REPORTER_ASSERT(reporter, kN32_SkColorType == info.colorType());
REPORTER_ASSERT(reporter, kPremul_SkAlphaType == info.alphaType() ||
kOpaque_SkAlphaType == info.alphaType());
REPORTER_ASSERT(reporter, info.minRowBytes() <= rowBytes);
REPORTER_ASSERT(reporter, pmcolor == *(const SkPMColor*)addr);
}
test_image_readpixels(reporter, image, pmcolor);
}
REPORTER_ASSERT(reporter, nullptr == releaseCtx.fData); // we released the data
}
#if SK_SUPPORT_GPU
struct ReleaseTextureContext {
ReleaseTextureContext(skiatest::Reporter* reporter) {
fReporter = reporter;
fIsReleased = false;
}
skiatest::Reporter* fReporter;
bool fIsReleased;
void doRelease() {
REPORTER_ASSERT(fReporter, false == fIsReleased);
fIsReleased = true;
}
static void ReleaseProc(void* context) {
((ReleaseTextureContext*)context)->doRelease();
}
};
static SkImage* make_desc_image(GrContext* ctx, int w, int h, GrBackendObject texID,
ReleaseTextureContext* releaseContext) {
GrBackendTextureDesc desc;
desc.fConfig = kSkia8888_GrPixelConfig;
// need to be a rendertarget for now...
desc.fFlags = kRenderTarget_GrBackendTextureFlag;
desc.fWidth = w;
desc.fHeight = h;
desc.fSampleCnt = 0;
desc.fTextureHandle = texID;
return releaseContext
? SkImage::NewFromTexture(ctx, desc, kPremul_SkAlphaType,
ReleaseTextureContext::ReleaseProc, releaseContext)
: SkImage::NewFromTextureCopy(ctx, desc, kPremul_SkAlphaType);
}
static void test_image_color(skiatest::Reporter* reporter, SkImage* image, SkPMColor expected) {
const SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1);
SkPMColor pixel;
REPORTER_ASSERT(reporter, image->readPixels(info, &pixel, sizeof(pixel), 0, 0));
REPORTER_ASSERT(reporter, pixel == expected);
}
DEF_GPUTEST(SkImage_NewFromTexture, reporter, factory) {
GrContext* ctx = factory->get(GrContextFactory::kNative_GLContextType);
if (!ctx) {
REPORTER_ASSERT(reporter, false);
return;
}
GrTextureProvider* provider = ctx->textureProvider();
const int w = 10;
const int h = 10;
SkPMColor storage[w * h];
const SkPMColor expected0 = SkPreMultiplyColor(SK_ColorRED);
sk_memset32(storage, expected0, w * h);
GrSurfaceDesc desc;
desc.fFlags = kRenderTarget_GrSurfaceFlag; // needs to be a rendertarget for readpixels();
desc.fOrigin = kDefault_GrSurfaceOrigin;
desc.fWidth = w;
desc.fHeight = h;
desc.fConfig = kSkia8888_GrPixelConfig;
desc.fSampleCnt = 0;
SkAutoTUnref<GrTexture> tex(provider->createTexture(desc, false, storage, w * 4));
if (!tex) {
REPORTER_ASSERT(reporter, false);
return;
}
GrBackendObject srcTex = tex->getTextureHandle();
ReleaseTextureContext releaseCtx(reporter);
SkAutoTUnref<SkImage> refImg(make_desc_image(ctx, w, h, srcTex, &releaseCtx));
SkAutoTUnref<SkImage> cpyImg(make_desc_image(ctx, w, h, srcTex, nullptr));
test_image_color(reporter, refImg, expected0);
test_image_color(reporter, cpyImg, expected0);
// Now lets jam new colors into our "external" texture, and see if the images notice
const SkPMColor expected1 = SkPreMultiplyColor(SK_ColorBLUE);
sk_memset32(storage, expected1, w * h);
tex->writePixels(0, 0, w, h, kSkia8888_GrPixelConfig, storage, GrContext::kFlushWrites_PixelOp);
// The cpy'd one should still see the old color
#if 0
// There is no guarantee that refImg sees the new color. We are free to have made a copy. Our
// write pixels call violated the contract with refImg and refImg is now undefined.
test_image_color(reporter, refImg, expected1);
#endif
test_image_color(reporter, cpyImg, expected0);
// Now exercise the release proc
REPORTER_ASSERT(reporter, !releaseCtx.fIsReleased);
refImg.reset(nullptr); // force a release of the image
REPORTER_ASSERT(reporter, releaseCtx.fIsReleased);
}
#endif
DEF_GPUTEST(ImageTestsFromSurfaceTestsTODO, reporter, factory) {
test_image(reporter);
test_empty_image(reporter);
test_imagepeek(reporter, factory);
}