| Index: tests/ImageTest.cpp
|
| diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp
|
| index 4768b8b0d4bf85cc28b538f84d7173e5b33eb216..d139ca7075101e524b9f6979c290f8e432bed110 100644
|
| --- a/tests/ImageTest.cpp
|
| +++ b/tests/ImageTest.cpp
|
| @@ -361,3 +361,433 @@ DEF_TEST(ImageFromIndex8Bitmap, r) {
|
| 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);
|
| +}
|
|
|