| 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); | 
| +} | 
|  |