diff options
author | robertphillips <robertphillips@google.com> | 2016-02-04 10:52:42 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-02-04 10:52:42 -0800 |
commit | b6c65e99956b867e24bd5bd68ae37673a9fd4b27 (patch) | |
tree | cb320967a3e8253a1100881942b540594f33315d | |
parent | 7471c780d48afd4dc02ed45c60a2fd2efa9e5a84 (diff) |
Add SkSpecialImage & SkSpecialSurface classes
Initial classes.
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1579323002
Review URL: https://codereview.chromium.org/1579323002
-rw-r--r-- | gyp/core.gypi | 4 | ||||
-rw-r--r-- | include/core/SkPixelRef.h | 1 | ||||
-rw-r--r-- | src/core/SkSpecialImage.cpp | 224 | ||||
-rw-r--r-- | src/core/SkSpecialImage.h | 84 | ||||
-rw-r--r-- | src/core/SkSpecialSurface.cpp | 177 | ||||
-rw-r--r-- | src/core/SkSpecialSurface.h | 93 | ||||
-rw-r--r-- | tests/SpecialImageTest.cpp | 150 | ||||
-rw-r--r-- | tests/SpecialSurfaceTest.cpp | 115 |
8 files changed, 848 insertions, 0 deletions
diff --git a/gyp/core.gypi b/gyp/core.gypi index 80f0eac956..db54377723 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -249,6 +249,10 @@ '<(skia_src_path)/core/SkSharedMutex.cpp', '<(skia_src_path)/core/SkSharedMutex.h', '<(skia_src_path)/core/SkSmallAllocator.h', + '<(skia_src_path)/core/SkSpecialImage.cpp', + '<(skia_src_path)/core/SkSpecialImage.h', + '<(skia_src_path)/core/SkSpecialSurface.cpp', + '<(skia_src_path)/core/SkSpecialSurface.h', '<(skia_src_path)/core/SkSpinlock.cpp', '<(skia_src_path)/core/SkSpriteBlitter_ARGB32.cpp', '<(skia_src_path)/core/SkSpriteBlitter_RGB16.cpp', diff --git a/include/core/SkPixelRef.h b/include/core/SkPixelRef.h index 9cdcd85a79..eccbe4b8e6 100644 --- a/include/core/SkPixelRef.h +++ b/include/core/SkPixelRef.h @@ -381,6 +381,7 @@ private: bool isPreLocked() const { return fPreLocked; } friend class SkImage_Raster; + friend class SkSpecialImage_Raster; // When copying a bitmap to another with the same shape and config, we can safely // clone the pixelref generation ID too, which makes them equivalent under caching. diff --git a/src/core/SkSpecialImage.cpp b/src/core/SkSpecialImage.cpp new file mode 100644 index 0000000000..50447fc1e9 --- /dev/null +++ b/src/core/SkSpecialImage.cpp @@ -0,0 +1,224 @@ +/* + * 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 "SkCanvas.h" +#include "SkSpecialImage.h" +#include "SkSpecialSurface.h" + +/////////////////////////////////////////////////////////////////////////////// +class SkSpecialImage_Base : public SkSpecialImage { +public: + SkSpecialImage_Base(const SkIRect& subset) : INHERITED(subset) { } + virtual ~SkSpecialImage_Base() { } + + virtual void onDraw(SkCanvas*, int x, int y, const SkPaint*) const = 0; + + virtual bool onPeekPixels(SkPixmap*) const { return false; } + + virtual GrTexture* onPeekTexture() const { return nullptr; } + + virtual SkSpecialSurface* onNewSurface(const SkImageInfo& info) const { return nullptr; } + +private: + typedef SkSpecialImage INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// +static inline const SkSpecialImage_Base* as_IB(const SkSpecialImage* image) { + return static_cast<const SkSpecialImage_Base*>(image); +} + +void SkSpecialImage::draw(SkCanvas* canvas, int x, int y, const SkPaint* paint) const { + return as_IB(this)->onDraw(canvas, x, y, paint); +} + +bool SkSpecialImage::peekPixels(SkPixmap* pixmap) const { + return as_IB(this)->onPeekPixels(pixmap); +} + +GrTexture* SkSpecialImage::peekTexture() const { + return as_IB(this)->onPeekTexture(); +} + +SkSpecialSurface* SkSpecialImage::newSurface(const SkImageInfo& info) const { + return as_IB(this)->onNewSurface(info); +} + +/////////////////////////////////////////////////////////////////////////////// +#include "SkImage.h" +#if SK_SUPPORT_GPU +#include "SkGr.h" +#include "SkGrPriv.h" +#endif + +class SkSpecialImage_Image : public SkSpecialImage_Base { +public: + SkSpecialImage_Image(const SkIRect& subset, const SkImage* image) + : INHERITED(subset) + , fImage(SkRef(image)) { + } + + ~SkSpecialImage_Image() override { } + + void onDraw(SkCanvas* canvas, int x, int y, const SkPaint* paint) const override { + SkRect dst = SkRect::MakeXYWH(x, y, this->subset().width(), this->subset().height()); + + canvas->drawImageRect(fImage, this->subset(), + dst, paint, SkCanvas::kStrict_SrcRectConstraint); + } + + bool onPeekPixels(SkPixmap* pixmap) const override { + return fImage->peekPixels(pixmap); + } + + GrTexture* onPeekTexture() const override { return fImage->getTexture(); } + + SkSpecialSurface* onNewSurface(const SkImageInfo& info) const override { +#if SK_SUPPORT_GPU + GrTexture* texture = fImage->getTexture(); + if (texture) { + GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(info); + desc.fFlags = kRenderTarget_GrSurfaceFlag; + + return SkSpecialSurface::NewRenderTarget(texture->getContext(), desc); + } +#endif + return SkSpecialSurface::NewRaster(info, nullptr); + } + +private: + SkAutoTUnref<const SkImage> fImage; + + typedef SkSpecialImage_Base INHERITED; +}; + +#ifdef SK_DEBUG +static bool rect_fits(const SkIRect& rect, int width, int height) { + return rect.fLeft >= 0 && rect.fLeft < width && rect.fLeft < rect.fRight && + rect.fRight >= 0 && rect.fRight <= width && + rect.fTop >= 0 && rect.fTop < height && rect.fTop < rect.fBottom && + rect.fBottom >= 0 && rect.fBottom <= height; +} +#endif + +SkSpecialImage* SkSpecialImage::NewFromImage(const SkIRect& subset, const SkImage* image) { + SkASSERT(rect_fits(subset, image->width(), image->height())); + return new SkSpecialImage_Image(subset, image); +} + +/////////////////////////////////////////////////////////////////////////////// +#include "SkBitmap.h" +#include "SkImageInfo.h" +#include "SkPixelRef.h" + +class SkSpecialImage_Raster : public SkSpecialImage_Base { +public: + SkSpecialImage_Raster(const SkIRect& subset, const SkBitmap& bm) + : INHERITED(subset) + , fBitmap(bm) { + if (bm.pixelRef()->isPreLocked()) { + // we only preemptively lock if there is no chance of triggering something expensive + // like a lazy decode or imagegenerator. PreLocked means it is flat pixels already. + fBitmap.lockPixels(); + } + } + + ~SkSpecialImage_Raster() override { } + + void onDraw(SkCanvas* canvas, int x, int y, const SkPaint* paint) const override { + SkRect dst = SkRect::MakeXYWH(x, y, + this->subset().width(), this->subset().height()); + + canvas->drawBitmapRect(fBitmap, this->subset(), + dst, paint, SkCanvas::kStrict_SrcRectConstraint); + } + + bool onPeekPixels(SkPixmap* pixmap) const override { + const SkImageInfo info = fBitmap.info(); + if ((kUnknown_SkColorType == info.colorType()) || !fBitmap.getPixels()) { + return false; + } + const void* pixels = fBitmap.getPixels(); + if (pixels) { + if (pixmap) { + pixmap->reset(info, pixels, fBitmap.rowBytes()); + } + return true; + } + return false; + } + + SkSpecialSurface* onNewSurface(const SkImageInfo& info) const override { + return SkSpecialSurface::NewRaster(info, nullptr); + } + +private: + SkBitmap fBitmap; + + typedef SkSpecialImage_Base INHERITED; +}; + +SkSpecialImage* SkSpecialImage::NewFromRaster(const SkIRect& subset, const SkBitmap& bm) { + SkASSERT(nullptr == bm.getTexture()); + SkASSERT(rect_fits(subset, bm.width(), bm.height())); + return new SkSpecialImage_Raster(subset, bm); +} + +#if SK_SUPPORT_GPU +/////////////////////////////////////////////////////////////////////////////// +#include "GrTexture.h" + +class SkSpecialImage_Gpu : public SkSpecialImage_Base { +public: + SkSpecialImage_Gpu(const SkIRect& subset, GrTexture* tex) + : INHERITED(subset) + , fTexture(SkRef(tex)) { + } + + ~SkSpecialImage_Gpu() override { } + + void onDraw(SkCanvas* canvas, int x, int y, const SkPaint* paint) const override { + SkRect dst = SkRect::MakeXYWH(x, y, + this->subset().width(), this->subset().height()); + + SkBitmap bm; + + static const bool kUnknownOpacity = false; + GrWrapTextureInBitmap(fTexture, + fTexture->width(), fTexture->height(), kUnknownOpacity, &bm); + + canvas->drawBitmapRect(bm, this->subset(), + dst, paint, SkCanvas::kStrict_SrcRectConstraint); + } + + GrTexture* onPeekTexture() const override { return fTexture; } + + SkSpecialSurface* onNewSurface(const SkImageInfo& info) const override { + GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(info); + desc.fFlags = kRenderTarget_GrSurfaceFlag; + + return SkSpecialSurface::NewRenderTarget(fTexture->getContext(), desc); + } + +private: + SkAutoTUnref<GrTexture> fTexture; + + typedef SkSpecialImage_Base INHERITED; +}; + +SkSpecialImage* SkSpecialImage::NewFromGpu(const SkIRect& subset, GrTexture* tex) { + SkASSERT(rect_fits(subset, tex->width(), tex->height())); + return new SkSpecialImage_Gpu(subset, tex); +} + +#else + +SkSpecialImage* SkSpecialImage::NewFromGpu(const SkIRect& subset, GrTexture* tex) { + return nullptr; +} + +#endif diff --git a/src/core/SkSpecialImage.h b/src/core/SkSpecialImage.h new file mode 100644 index 0000000000..23ef5732c9 --- /dev/null +++ b/src/core/SkSpecialImage.h @@ -0,0 +1,84 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file + */ + +#ifndef SkSpecialImage_DEFINED +#define SkSpecialImage_DEFINED + +#include "SkRefCnt.h" + +class GrTexture; +class SkBitmap; +class SkCanvas; +class SkImage; +struct SkImageInfo; +class SkPaint; +class SkSpecialSurface; + +/** + * This is a restricted form of SkImage solely intended for internal use. It + * differs from SkImage in that: + * - it can only be backed by raster or gpu (no generators) + * - it can be backed by a GrTexture larger than its nominal bounds + * - it can't be drawn tiled + * - it can't be drawn with MIPMAPs + * It is similar to SkImage in that it abstracts how the pixels are stored/represented. + * + * Note: the contents of the backing storage outside of the subset rect are undefined. + */ +class SkSpecialImage : public SkRefCnt { +public: + int width() const { return fSubset.width(); } + int height() const { return fSubset.height(); } + + /** + * Draw this SpecialImage into the canvas. + */ + void draw(SkCanvas*, int x, int y, const SkPaint*) const; + + static SkSpecialImage* NewFromImage(const SkIRect& subset, const SkImage*); + static SkSpecialImage* NewFromRaster(const SkIRect& subset, const SkBitmap&); + static SkSpecialImage* NewFromGpu(const SkIRect& subset, GrTexture*); + + /** + * Create a new surface with a backend that is compatible with this image. + */ + SkSpecialSurface* newSurface(const SkImageInfo&) const; + +protected: + SkSpecialImage(const SkIRect& subset) : fSubset(subset) { } + + // The following 3 are for testing and shouldn't be used. + friend class TestingSpecialImageAccess; + friend class TestingSpecialSurfaceAccess; + const SkIRect& subset() const { return fSubset; } + + /** + * If the SpecialImage is backed by cpu pixels, return the const address + * of those pixels and, if not null, return the ImageInfo and rowBytes. + * The returned address is only valid while the image object is in scope. + * + * The returned ImageInfo represents the backing memory. Use 'subset' + * to get the active portion's dimensions. + * + * On failure, return false and ignore the pixmap parameter. + */ + bool peekPixels(SkPixmap*) const; + + /** + * If the SpecialImage is backed by a gpu texture, return that texture. + * The active portion of the texture can be retrieved via 'subset'. + */ + GrTexture* peekTexture() const; + +private: + const SkIRect fSubset; + + typedef SkRefCnt INHERITED; +}; + +#endif + diff --git a/src/core/SkSpecialSurface.cpp b/src/core/SkSpecialSurface.cpp new file mode 100644 index 0000000000..5d57dcfd4f --- /dev/null +++ b/src/core/SkSpecialSurface.cpp @@ -0,0 +1,177 @@ +/* + * 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 "SkCanvas.h" +#include "SkSpecialImage.h" +#include "SkSpecialSurface.h" +#include "SkSurfacePriv.h" + + /////////////////////////////////////////////////////////////////////////////// +class SkSpecialSurface_Base : public SkSpecialSurface { +public: + SkSpecialSurface_Base(const SkIRect& subset, const SkSurfaceProps* props) + : INHERITED(subset, props) + , fCanvas(nullptr) { + } + + virtual ~SkSpecialSurface_Base() { } + + // reset is called after an SkSpecialImage has been snapped + void reset() { fCanvas.reset(); } + + // This can return nullptr if reset has already been called or something when wrong in the ctor + SkCanvas* onGetCanvas() { return fCanvas; } + + virtual SkSpecialImage* onNewImageSnapshot() = 0; + +protected: + SkAutoTUnref<SkCanvas> fCanvas; // initialized by derived classes in ctors + +private: + typedef SkSpecialSurface INHERITED; +}; + +/////////////////////////////////////////////////////////////////////////////// +static SkSpecialSurface_Base* as_SB(SkSpecialSurface* surface) { + return static_cast<SkSpecialSurface_Base*>(surface); +} + +SkSpecialSurface::SkSpecialSurface(const SkIRect& subset, const SkSurfaceProps* props) + : fProps(SkSurfacePropsCopyOrDefault(props)) + , fSubset(subset) { + SkASSERT(fSubset.width() > 0); + SkASSERT(fSubset.height() > 0); +} + +SkCanvas* SkSpecialSurface::getCanvas() { + return as_SB(this)->onGetCanvas(); +} + +SkSpecialImage* SkSpecialSurface::newImageSnapshot() { + SkSpecialImage* image = as_SB(this)->onNewImageSnapshot(); + as_SB(this)->reset(); + return SkSafeRef(image); // the caller will call unref() to balance this +} + +/////////////////////////////////////////////////////////////////////////////// +#include "SkMallocPixelRef.h" + +class SkSpecialSurface_Raster : public SkSpecialSurface_Base { +public: + SkSpecialSurface_Raster(SkPixelRef* pr, const SkIRect& subset, const SkSurfaceProps* props) + : INHERITED(subset, props) { + const SkImageInfo& info = pr->info(); + + fBitmap.setInfo(info, info.minRowBytes()); + fBitmap.setPixelRef(pr); + + fCanvas.reset(new SkCanvas(fBitmap)); + } + + ~SkSpecialSurface_Raster() override { } + + SkSpecialImage* onNewImageSnapshot() override { + return SkSpecialImage::NewFromRaster(this->subset(), fBitmap); + } + +private: + SkBitmap fBitmap; + + typedef SkSpecialSurface_Base INHERITED; +}; + +SkSpecialSurface* SkSpecialSurface::NewFromBitmap(const SkIRect& subset, SkBitmap& bm, + const SkSurfaceProps* props) { + return new SkSpecialSurface_Raster(bm.pixelRef(), subset, props); +} + +SkSpecialSurface* SkSpecialSurface::NewRaster(const SkImageInfo& info, + const SkSurfaceProps* props) { + SkAutoTUnref<SkPixelRef> pr(SkMallocPixelRef::NewZeroed(info, 0, nullptr)); + if (nullptr == pr.get()) { + return nullptr; + } + + const SkIRect subset = SkIRect::MakeWH(pr->info().width(), pr->info().height()); + + return new SkSpecialSurface_Raster(pr, subset, props); +} + +#if SK_SUPPORT_GPU +/////////////////////////////////////////////////////////////////////////////// +#include "GrContext.h" +#include "SkGpuDevice.h" + +class SkSpecialSurface_Gpu : public SkSpecialSurface_Base { +public: + SkSpecialSurface_Gpu(GrTexture* texture, const SkIRect& subset, const SkSurfaceProps* props) + : INHERITED(subset, props) + , fTexture(texture) { + + SkASSERT(fTexture->asRenderTarget()); + + SkAutoTUnref<SkGpuDevice> device(SkGpuDevice::Create(fTexture->asRenderTarget(), props, + SkGpuDevice::kUninit_InitContents)); + if (!device) { + return; + } + + fCanvas.reset(new SkCanvas(device)); + } + + ~SkSpecialSurface_Gpu() override { } + + SkSpecialImage* onNewImageSnapshot() override { + return SkSpecialImage::NewFromGpu(this->subset(), fTexture); + } + +private: + SkAutoTUnref<GrTexture> fTexture; + + typedef SkSpecialSurface_Base INHERITED; +}; + +SkSpecialSurface* SkSpecialSurface::NewFromTexture(const SkIRect& subset, GrTexture* texture, + const SkSurfaceProps* props) { + if (!texture->asRenderTarget()) { + return nullptr; + } + + return new SkSpecialSurface_Gpu(texture, subset, props); +} + +SkSpecialSurface* SkSpecialSurface::NewRenderTarget(GrContext* context, + const GrSurfaceDesc& desc, + const SkSurfaceProps* props) { + if (!context || !SkToBool(desc.fFlags & kRenderTarget_GrSurfaceFlag)) { + return nullptr; + } + + GrTexture* temp = context->textureProvider()->createApproxTexture(desc); + if (!temp) { + return nullptr; + } + + const SkIRect subset = SkIRect::MakeWH(desc.fWidth, desc.fHeight); + + return new SkSpecialSurface_Gpu(temp, subset, props); +} + +#else + +SkSpecialSurface* SkSpecialSurface::NewFromTexture(const SkIRect& subset, GrTexture*, + const SkSurfaceProps*) { + return nullptr; +} + +SkSpecialSurface* SkSpecialSurface::NewRenderTarget(GrContext* context, + const GrSurfaceDesc& desc, + const SkSurfaceProps* props) { + return nullptr; +} + +#endif diff --git a/src/core/SkSpecialSurface.h b/src/core/SkSpecialSurface.h new file mode 100644 index 0000000000..2751c4a794 --- /dev/null +++ b/src/core/SkSpecialSurface.h @@ -0,0 +1,93 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file + */ + +#ifndef SkSpecialSurface_DEFINED +#define SkSpecialSurface_DEFINED + +#include "SkRefCnt.h" +#include "SkSurfaceProps.h" + +class GrContext; +struct GrSurfaceDesc; +class SkCanvas; +struct SkImageInfo; +class SkSpecialImage; + +/** + * SkSpecialSurface is a restricted form of SkSurface solely for internal use. It differs + * from SkSurface in that: + * - it can be backed by GrTextures larger than [ fWidth, fHeight ] + * - it can't be used for tiling + * - it becomes inactive once a snapshot of it is taken (i.e., no copy-on-write) + * - it has no generation ID + */ +class SkSpecialSurface : public SkRefCnt { +public: + const SkSurfaceProps& props() const { return fProps; } + + int width() const { return fSubset.width(); } + int height() const { return fSubset.height(); } + + /** + * Return a canvas that will draw into this surface. This will always + * return the same canvas for a given surface, and is managed/owned by the + * surface. + * + * The canvas will be invalid after 'newImageSnapshot' is called. + */ + SkCanvas* getCanvas(); + + /** + * Returns an image of the current state of the surface pixels up to this + * point. The canvas returned by 'getCanvas' becomes invalidated by this + * call and no more drawing to this surface is allowed. + */ + SkSpecialImage* newImageSnapshot(); + + /** + * Use an existing (renderTarget-capable) GrTexture as the backing store. + */ + static SkSpecialSurface* NewFromTexture(const SkIRect& subset, GrTexture*, + const SkSurfaceProps* = nullptr); + + /** + * Allocate a new GPU-backed SkSpecialSurface. If the requested surface cannot + * be created, nullptr will be returned. + */ + static SkSpecialSurface* NewRenderTarget(GrContext*, const GrSurfaceDesc&, + const SkSurfaceProps* = nullptr); + + /** + * Use and existing SkBitmap as the backing store. + */ + static SkSpecialSurface* NewFromBitmap(const SkIRect& subset, SkBitmap& bm, + const SkSurfaceProps* = nullptr); + + /** + * Return a new CPU-backed surface, with the memory for the pixels automatically + * allocated. + * + * If the requested surface cannot be created, or the request is not a + * supported configuration, nullptr will be returned. + */ + static SkSpecialSurface* NewRaster(const SkImageInfo&, const SkSurfaceProps* = nullptr); + +protected: + SkSpecialSurface(const SkIRect& subset, const SkSurfaceProps*); + + // For testing only + friend class TestingSpecialSurfaceAccess; + const SkIRect& subset() const { return fSubset; } + +private: + const SkSurfaceProps fProps; + const SkIRect fSubset; + + typedef SkRefCnt INHERITED; +}; + +#endif diff --git a/tests/SpecialImageTest.cpp b/tests/SpecialImageTest.cpp new file mode 100644 index 0000000000..3ae8d33911 --- /dev/null +++ b/tests/SpecialImageTest.cpp @@ -0,0 +1,150 @@ +/* + * 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 "SkBitmap.h" +#include "SkCanvas.h" +#include "SkImage.h" +#include "SkSpecialImage.h" +#include "SkSpecialSurface.h" +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrContext.h" +#endif + +class TestingSpecialImageAccess { +public: + static const SkIRect& Subset(const SkSpecialImage* img) { + return img->subset(); + } + + static bool PeekPixels(const SkSpecialImage* img, SkPixmap* pixmap) { + return img->peekPixels(pixmap); + } + + static GrTexture* PeekTexture(const SkSpecialImage* img) { + return img->peekTexture(); + } +}; + +// This test creates backing resources exactly sized to [kFullSize x kFullSize]. +// It then wraps them in an SkSpecialImage with only the center (red) region being active. +// It then draws the SkSpecialImage to a full sized (all blue) canvas and checks that none +// of the inactive (green) region leaked out. + +static const int kSmallerSize = 10; +static const int kPad = 3; +static const int kFullSize = kSmallerSize + 2 * kPad; + +// Create a bitmap with red in the center and green around it +static SkBitmap create_bm() { + SkBitmap bm; + bm.allocN32Pixels(kFullSize, kFullSize, true); + + SkCanvas temp(bm); + + temp.clear(SK_ColorGREEN); + SkPaint p; + p.setColor(SK_ColorRED); + p.setAntiAlias(false); + + temp.drawRect(SkRect::MakeXYWH(SkIntToScalar(kPad), SkIntToScalar(kPad), + SkIntToScalar(kSmallerSize), SkIntToScalar(kSmallerSize)), + p); + + return bm; +} + +// Basic test of the SkSpecialImage public API (e.g., peekTexture, peekPixels & draw) +static void test_image(SkSpecialImage* img, skiatest::Reporter* reporter, + bool peekPixelsSucceeds, bool peekTextureSucceeds) { + const SkIRect subset = TestingSpecialImageAccess::Subset(img); + REPORTER_ASSERT(reporter, kPad == subset.left()); + REPORTER_ASSERT(reporter, kPad == subset.top()); + REPORTER_ASSERT(reporter, kSmallerSize == subset.width()); + REPORTER_ASSERT(reporter, kSmallerSize == subset.height()); + + //-------------- + REPORTER_ASSERT(reporter, peekTextureSucceeds == !!TestingSpecialImageAccess::PeekTexture(img)); + + //-------------- + SkPixmap pixmap; + REPORTER_ASSERT(reporter, peekPixelsSucceeds == + !!TestingSpecialImageAccess::PeekPixels(img, &pixmap)); + if (peekPixelsSucceeds) { + REPORTER_ASSERT(reporter, kFullSize == pixmap.width()); + REPORTER_ASSERT(reporter, kFullSize == pixmap.height()); + } + + //-------------- + SkImageInfo info = SkImageInfo::MakeN32(kFullSize, kFullSize, kOpaque_SkAlphaType); + + SkAutoTUnref<SkSpecialSurface> surf(img->newSurface(info)); + + SkCanvas* canvas = surf->getCanvas(); + + canvas->clear(SK_ColorBLUE); + img->draw(canvas, kPad, kPad, nullptr); + + SkBitmap bm; + bm.allocN32Pixels(kFullSize, kFullSize, true); + + bool result = canvas->readPixels(bm.info(), bm.getPixels(), bm.rowBytes(), 0, 0); + SkASSERT_RELEASE(result); + + // Only the center (red) portion should've been drawn into the canvas + REPORTER_ASSERT(reporter, SK_ColorBLUE == bm.getColor(kPad-1, kPad-1)); + REPORTER_ASSERT(reporter, SK_ColorRED == bm.getColor(kPad, kPad)); + REPORTER_ASSERT(reporter, SK_ColorRED == bm.getColor(kSmallerSize+kPad-1, + kSmallerSize+kPad-1)); + REPORTER_ASSERT(reporter, SK_ColorBLUE == bm.getColor(kSmallerSize+kPad, + kSmallerSize+kPad)); +} + +DEF_TEST(SpecialImage_Raster, reporter) { + SkBitmap bm = create_bm(); + + const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); + + SkAutoTUnref<SkSpecialImage> img(SkSpecialImage::NewFromRaster(subset, bm)); + test_image(img, reporter, true, false); +} + +DEF_TEST(SpecialImage_Image, reporter) { + SkBitmap bm = create_bm(); + + SkAutoTUnref<SkImage> fullImage(SkImage::NewFromBitmap(bm)); + + const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); + + SkAutoTUnref<SkSpecialImage> img(SkSpecialImage::NewFromImage(subset, fullImage)); + test_image(img, reporter, true, false); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialImage_Gpu, reporter, context) { + SkBitmap bm = create_bm(); + + GrSurfaceDesc desc; + desc.fConfig = kSkia8888_GrPixelConfig; + desc.fFlags = kNone_GrSurfaceFlags; + desc.fWidth = kFullSize; + desc.fHeight = kFullSize; + + SkAutoTUnref<GrTexture> texture(context->textureProvider()->createTexture(desc, false, + bm.getPixels(), 0)); + if (!texture) { + return; + } + + const SkIRect& subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); + + SkAutoTUnref<SkSpecialImage> img(SkSpecialImage::NewFromGpu(subset, texture)); + test_image(img, reporter, false, true); +} + +#endif diff --git a/tests/SpecialSurfaceTest.cpp b/tests/SpecialSurfaceTest.cpp new file mode 100644 index 0000000000..b1cbf68082 --- /dev/null +++ b/tests/SpecialSurfaceTest.cpp @@ -0,0 +1,115 @@ +/* +* 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 "SkCanvas.h" +#include "SkSpecialImage.h" +#include "SkSpecialSurface.h" +#include "Test.h" + +#if SK_SUPPORT_GPU +#include "GrContext.h" +#include "SkGr.h" +#endif + +class TestingSpecialSurfaceAccess { +public: + static const SkIRect& Subset(const SkSpecialSurface* surf) { + return surf->subset(); + } + + static const SkIRect& Subset(const SkSpecialImage* img) { + return img->subset(); + } +}; + +// Both 'kSmallerSize' and 'kFullSize' need to be a non-power-of-2 to exercise +// the gpu's loose fit behavior +static const int kSmallerSize = 10; +static const int kPad = 5; +static const int kFullSize = kSmallerSize + 2 * kPad; + +// Exercise the public API of SkSpecialSurface (e.g., getCanvas, newImageSnapshot) +static void test_surface(SkSpecialSurface* surf, skiatest::Reporter* reporter, int offset) { + + const SkIRect surfSubset = TestingSpecialSurfaceAccess::Subset(surf); + REPORTER_ASSERT(reporter, offset == surfSubset.fLeft); + REPORTER_ASSERT(reporter, offset == surfSubset.fTop); + REPORTER_ASSERT(reporter, kSmallerSize == surfSubset.width()); + REPORTER_ASSERT(reporter, kSmallerSize == surfSubset.height()); + + SkCanvas* canvas = surf->getCanvas(); + SkASSERT_RELEASE(canvas); + + canvas->clear(SK_ColorRED); + + SkAutoTUnref<SkSpecialImage> img(surf->newImageSnapshot()); + REPORTER_ASSERT(reporter, img); + + const SkIRect imgSubset = TestingSpecialSurfaceAccess::Subset(img); + REPORTER_ASSERT(reporter, surfSubset == imgSubset); + + // the canvas was invalidated by the newImageSnapshot call + REPORTER_ASSERT(reporter, !surf->getCanvas()); +} + +DEF_TEST(SpecialSurface_Raster, reporter) { + + SkImageInfo info = SkImageInfo::MakeN32(kSmallerSize, kSmallerSize, kOpaque_SkAlphaType); + SkAutoTUnref<SkSpecialSurface> surf(SkSpecialSurface::NewRaster(info)); + + test_surface(surf, reporter, 0); +} + +DEF_TEST(SpecialSurface_Raster2, reporter) { + + SkBitmap bm; + bm.allocN32Pixels(kFullSize, kFullSize, true); + + const SkIRect subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); + + SkAutoTUnref<SkSpecialSurface> surf(SkSpecialSurface::NewFromBitmap(subset, bm)); + + test_surface(surf, reporter, kPad); + + // TODO: check that the clear didn't escape the active region +} + +#if SK_SUPPORT_GPU + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialSurface_Gpu1, reporter, context) { + GrSurfaceDesc desc; + desc.fConfig = kSkia8888_GrPixelConfig; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fWidth = kSmallerSize; + desc.fHeight = kSmallerSize; + + SkAutoTUnref<SkSpecialSurface> surf(SkSpecialSurface::NewRenderTarget(context, desc)); + + test_surface(surf, reporter, 0); +} + +// test the more flexible factory +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SpecialSurface_Gpu2, reporter, context) { + GrSurfaceDesc desc; + desc.fConfig = kSkia8888_GrPixelConfig; + desc.fFlags = kRenderTarget_GrSurfaceFlag; + desc.fWidth = kFullSize; + desc.fHeight = kFullSize; + + SkAutoTUnref<GrTexture> temp(context->textureProvider()->createApproxTexture(desc)); + SkASSERT_RELEASE(temp); + + const SkIRect subset = SkIRect::MakeXYWH(kPad, kPad, kSmallerSize, kSmallerSize); + + SkAutoTUnref<SkSpecialSurface> surf(SkSpecialSurface::NewFromTexture(subset, temp)); + + test_surface(surf, reporter, kPad); + + // TODO: check that the clear didn't escape the active region +} + +#endif |