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
This commit is contained in:
robertphillips 2016-02-04 10:52:42 -08:00 committed by Commit bot
parent 7471c780d4
commit b6c65e9995
8 changed files with 848 additions and 0 deletions

View File

@ -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',

View File

@ -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.

224
src/core/SkSpecialImage.cpp Normal file
View File

@ -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

84
src/core/SkSpecialImage.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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

150
tests/SpecialImageTest.cpp Normal file
View File

@ -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

View File

@ -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