Fix layer hoisting image filter corner cases

This CL fixes 5 bugs related to hoisting image filters:

For image filters the src layer (the one prior to filtering) often needs to be
smaller then the final layer. This requires the saveLayer's optional bounds
to be stored (in SkLayerInfo.h and SkRecordDraw.cpp) and then used in
compute_source_rect and carried around in GrCachedLayer.

The image filters can add an extra offset to the final draw operation.
This is now computed in GrLayerHoister::FilterLayer and  carried around in
GrCachedLayer.

Filtered layers must use exact matches. This is now done in GrLayerCache::lock.

The filter cache requires a valid matrix so it can compute the correct offset.
This is now done in GrLayerHoister::FilterLayer.

Filtered layers need to be drawn with drawSprite while unfiltered (and therefore
hopefully atlased) layers can be drawn with drawBitmap. This is now done in
draw_replacement_bitmap.

Committed: https://skia.googlesource.com/skia/+/702eb9622102599d94ab6798e6227cf29f48c2d3

Review URL: https://codereview.chromium.org/803183003
This commit is contained in:
robertphillips 2014-12-16 08:25:55 -08:00 committed by Commit bot
parent 29097f920a
commit 478dd72336
10 changed files with 182 additions and 79 deletions

View File

@ -655,18 +655,21 @@ public:
}
}
static void generate_image_from_picture(GM* gm, const ConfigData& gRec,
static void generate_image_from_picture(GM* gm, const ConfigData& config,
GrSurface* gpuTarget,
SkPicture* pict, SkBitmap* bitmap,
SkScalar scale = SK_Scalar1,
bool tile = false) {
SkISize size = gm->getISize();
setup_bitmap(gRec, size, bitmap);
const SkISize size = gm->getISize();
SkAutoTUnref<SkSurface> surf(SkSurface::NewRasterDirect(bitmap->info(),
bitmap->getPixels(),
bitmap->rowBytes()));
SkAutoTUnref<SkSurface> surf(CreateSurface(config, size, gpuTarget));
DrawPictureToSurface(surf, pict, scale, tile, false);
setup_bitmap(config, size, bitmap);
surf->readPixels(bitmap->info(), bitmap->getPixels(), bitmap->rowBytes(), 0, 0);
complete_bitmap(bitmap);
}
@ -1630,10 +1633,12 @@ template <typename T> void appendUnique(SkTDArray<T>* array, const T& value) {
*
* Returns all errors encountered while doing so.
*/
ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &compareConfig,
ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm,
const ConfigData &compareConfig, GrSurface* gpuTarget,
const SkBitmap &comparisonBitmap,
const SkTDArray<SkScalar> &tileGridReplayScales);
ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &compareConfig,
ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm,
const ConfigData &compareConfig, GrSurface* gpuTarget,
const SkBitmap &comparisonBitmap,
const SkTDArray<SkScalar> &tileGridReplayScales) {
ErrorCombination errorsForAllModes;
@ -1651,7 +1656,8 @@ ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &co
errorsForAllModes.add(kIntentionallySkipped_ErrorType);
} else {
SkBitmap bitmap;
gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap);
gmmain.generate_image_from_picture(gm, compareConfig, gpuTarget, pict, &bitmap);
errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap(
gm->getName(), compareConfig.fName, renderModeDescriptor, bitmap,
&comparisonBitmap));
@ -1668,7 +1674,7 @@ ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &co
SkPicture* repict = gmmain.stream_to_new_picture(*pict);
SkAutoTUnref<SkPicture> aurr(repict);
SkBitmap bitmap;
gmmain.generate_image_from_picture(gm, compareConfig, repict, &bitmap);
gmmain.generate_image_from_picture(gm, compareConfig, gpuTarget, repict, &bitmap);
errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap(
gm->getName(), compareConfig.fName, renderModeDescriptor, bitmap,
&comparisonBitmap));
@ -1701,7 +1707,7 @@ ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &co
SkPicture* pict = gmmain.generate_new_picture(gm, kRTree_BbhType, 0);
SkAutoTUnref<SkPicture> aur(pict);
SkBitmap bitmap;
gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap);
gmmain.generate_image_from_picture(gm, compareConfig, gpuTarget, pict, &bitmap);
errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap(
gm->getName(), compareConfig.fName, renderModeDescriptor, bitmap,
&comparisonBitmap));
@ -1734,7 +1740,7 @@ ErrorCombination run_multiple_modes(GMMain &gmmain, GM *gm, const ConfigData &co
// We cannot yet pass 'true' to generate_image_from_picture to
// perform actual tiled rendering (see Issue 1198 -
// https://code.google.com/p/skia/issues/detail?id=1198)
gmmain.generate_image_from_picture(gm, compareConfig, pict, &bitmap,
gmmain.generate_image_from_picture(gm, compareConfig, gpuTarget, pict, &bitmap,
replayScale /*, true */);
errorsForAllModes.add(gmmain.compare_test_results_to_reference_bitmap(
gm->getName(), compareConfig.fName, renderModeDescriptor.c_str(), bitmap,
@ -1875,7 +1881,7 @@ ErrorCombination run_multiple_configs(GMMain &gmmain, GM *gm,
// TODO: run only if gmmain.test_drawing succeeded.
if (kRaster_Backend == config.fBackend) {
run_multiple_modes(gmmain, gm, config, comparisonBitmap, tileGridReplayScales);
run_multiple_modes(gmmain, gm, config, gpuTarget, comparisonBitmap, tileGridReplayScales);
}
if (FLAGS_deferred && errorsForThisConfig.isEmpty() &&

View File

@ -28,6 +28,9 @@ public:
const SkPicture* fPicture;
// The device space bounds of this layer.
SkRect fBounds;
// If not-empty, the optional bounds parameter passed in to the saveLayer
// call.
SkRect fSrcBounds;
// The pre-matrix begins as the identity and accumulates the transforms
// of the containing SkPictures (if any). This matrix state has to be
// part of the initial matrix during replay so that it will be

View File

@ -608,24 +608,26 @@ public:
private:
struct SaveLayerInfo {
SaveLayerInfo() { }
SaveLayerInfo(int opIndex, bool isSaveLayer, const SkPaint* paint)
SaveLayerInfo(int opIndex, bool isSaveLayer, const SkRect* bounds, const SkPaint* paint)
: fStartIndex(opIndex)
, fIsSaveLayer(isSaveLayer)
, fHasNestedSaveLayer(false)
, fBounds(bounds ? *bounds : SkRect::MakeEmpty())
, fPaint(paint) {
}
int fStartIndex;
bool fIsSaveLayer;
bool fHasNestedSaveLayer;
SkRect fBounds;
const SkPaint* fPaint;
};
template <typename T> void trackSaveLayers(const T& op) {
/* most ops aren't involved in saveLayers */
}
void trackSaveLayers(const Save& s) { this->pushSaveLayerInfo(false, NULL); }
void trackSaveLayers(const SaveLayer& sl) { this->pushSaveLayerInfo(true, sl.paint); }
void trackSaveLayers(const Save& s) { this->pushSaveLayerInfo(false, NULL, NULL); }
void trackSaveLayers(const SaveLayer& sl) { this->pushSaveLayerInfo(true, sl.bounds, sl.paint); }
void trackSaveLayers(const Restore& r) { this->popSaveLayerInfo(); }
void trackSaveLayersForPicture(const SkPicture* picture, const SkPaint* paint) {
@ -662,6 +664,7 @@ private:
dst.fPicture = src.fPicture ? src.fPicture : picture;
dst.fPicture->ref();
dst.fBounds = newBound;
dst.fSrcBounds = src.fSrcBounds;
dst.fLocalMat = src.fLocalMat;
dst.fPreMat = src.fPreMat;
dst.fPreMat.postConcat(fFillBounds.ctm());
@ -707,14 +710,14 @@ private:
}
}
void pushSaveLayerInfo(bool isSaveLayer, const SkPaint* paint) {
void pushSaveLayerInfo(bool isSaveLayer, const SkRect* bounds, const SkPaint* paint) {
if (isSaveLayer) {
this->updateStackForSaveLayer();
++fSaveLayersInStack;
fSaveLayerOpStack.push(fFillBounds.currentOp());
}
fSaveLayerStack.push(SaveLayerInfo(fFillBounds.currentOp(), isSaveLayer, paint));
fSaveLayerStack.push(SaveLayerInfo(fFillBounds.currentOp(), isSaveLayer, bounds, paint));
}
void popSaveLayerInfo() {
@ -744,6 +747,8 @@ private:
if (sli.fPaint) {
block.fPaint = SkNEW_ARGS(SkPaint, (*sli.fPaint));
}
block.fSrcBounds = sli.fBounds;
block.fSaveLayerOpID = sli.fStartIndex;
block.fRestoreOpID = fFillBounds.currentOp();
block.fHasNestedLayers = sli.fHasNestedSaveLayer;

View File

@ -123,14 +123,16 @@ void GrLayerCache::freeAll() {
GrCachedLayer* GrLayerCache::createLayer(uint32_t pictureID,
int start, int stop,
const SkIRect& bounds,
const SkIRect& srcIR,
const SkIRect& dstIR,
const SkMatrix& initialMat,
const unsigned* key,
int keySize,
const SkPaint* paint) {
SkASSERT(pictureID != SK_InvalidGenID && start >= 0 && stop > 0);
GrCachedLayer* layer = SkNEW_ARGS(GrCachedLayer, (pictureID, start, stop, bounds, initialMat,
GrCachedLayer* layer = SkNEW_ARGS(GrCachedLayer, (pictureID, start, stop,
srcIR, dstIR, initialMat,
key, keySize, paint));
fLayerHash.add(layer);
return layer;
@ -144,7 +146,8 @@ GrCachedLayer* GrLayerCache::findLayer(uint32_t pictureID, const SkMatrix& initi
GrCachedLayer* GrLayerCache::findLayerOrCreate(uint32_t pictureID,
int start, int stop,
const SkIRect& bounds,
const SkIRect& srcIR,
const SkIRect& dstIR,
const SkMatrix& initialMat,
const unsigned* key,
int keySize,
@ -152,7 +155,9 @@ GrCachedLayer* GrLayerCache::findLayerOrCreate(uint32_t pictureID,
SkASSERT(pictureID != SK_InvalidGenID && start >= 0 && stop > 0);
GrCachedLayer* layer = fLayerHash.find(GrCachedLayer::Key(pictureID, initialMat, key, keySize));
if (NULL == layer) {
layer = this->createLayer(pictureID, start, stop, bounds, initialMat, key, keySize, paint);
layer = this->createLayer(pictureID, start, stop,
srcIR, dstIR, initialMat,
key, keySize, paint);
}
return layer;
@ -242,8 +247,14 @@ bool GrLayerCache::lock(GrCachedLayer* layer, const GrSurfaceDesc& desc, bool* n
return true;
}
// TODO: make the test for exact match depend on the image filters themselves
GrContext::ScratchTexMatch usage = GrContext::kApprox_ScratchTexMatch;
if (layer->fFilter) {
usage = GrContext::kExact_ScratchTexMatch;
}
SkAutoTUnref<GrTexture> tex(
fContext->refScratchTexture(desc, GrContext::kApprox_ScratchTexMatch));
fContext->refScratchTexture(desc, usage));
if (!tex) {
return false;

View File

@ -144,13 +144,16 @@ public:
// GrCachedLayer proper
GrCachedLayer(uint32_t pictureID, unsigned start, unsigned stop,
const SkIRect& bounds, const SkMatrix& ctm,
const SkIRect& srcIR, const SkIRect& dstIR,
const SkMatrix& ctm,
const unsigned* key, int keySize,
const SkPaint* paint)
: fKey(pictureID, ctm, key, keySize, true)
, fStart(start)
, fStop(stop)
, fBounds(bounds)
, fSrcIR(srcIR)
, fDstIR(dstIR)
, fOffset(SkIPoint::Make(0, 0))
, fPaint(paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL)
, fFilter(NULL)
, fTexture(NULL)
@ -161,8 +164,10 @@ public:
SkASSERT(SK_InvalidGenID != pictureID);
if (fPaint) {
fFilter = SkSafeRef(fPaint->getImageFilter());
fPaint->setImageFilter(NULL);
if (fPaint->getImageFilter() && fPaint->getImageFilter()->canFilterImageGPU()) {
fFilter = SkSafeRef(fPaint->getImageFilter());
fPaint->setImageFilter(NULL);
}
}
}
@ -179,7 +184,8 @@ public:
unsigned start() const { return fStart; }
// TODO: make bound debug only
const SkIRect& bound() const { return fBounds; }
const SkIRect& srcIR() const { return fSrcIR; }
const SkIRect& dstIR() const { return fDstIR; }
unsigned stop() const { return fStop; }
void setTexture(GrTexture* texture, const SkIRect& rect) {
SkRefCnt_SafeAssign(fTexture, texture);
@ -190,6 +196,9 @@ public:
const SkImageFilter* filter() const { return fFilter; }
const SkIRect& rect() const { return fRect; }
void setOffset(const SkIPoint& offset) { fOffset = offset; }
const SkIPoint& offset() const { return fOffset; }
void setPlot(GrPlot* plot) {
SkASSERT(NULL == plot || NULL == fPlot);
fPlot = plot;
@ -212,7 +221,13 @@ private:
// The final "restore" operation index of the cached layer
const unsigned fStop;
const SkIRect fBounds;
// The layer's src rect (i.e., the portion of the source scene required
// for filtering).
const SkIRect fSrcIR;
// The layer's dest rect (i.e., where it will land in device space)
const SkIRect fDstIR;
// Offset sometimes required by image filters
SkIPoint fOffset;
// The paint used when dropping the layer down into the owning canvas.
// Can be NULL. This class makes a copy for itself.
@ -276,7 +291,8 @@ public:
const unsigned* key, int keySize);
GrCachedLayer* findLayerOrCreate(uint32_t pictureID,
int start, int stop,
const SkIRect& bounds,
const SkIRect& srcIR,
const SkIRect& dstIR,
const SkMatrix& initialMat,
const unsigned* key, int keySize,
const SkPaint* paint);
@ -360,7 +376,8 @@ private:
void initAtlas();
GrCachedLayer* createLayer(uint32_t pictureID, int start, int stop,
const SkIRect& bounds, const SkMatrix& initialMat,
const SkIRect& srcIR, const SkIRect& dstIR,
const SkMatrix& initialMat,
const unsigned* key, int keySize,
const SkPaint* paint);

View File

@ -23,7 +23,8 @@ static void prepare_for_hoisting(GrLayerCache* layerCache,
const SkPicture* topLevelPicture,
const SkMatrix& initialMat,
const SkLayerInfo::BlockInfo& info,
const SkIRect& layerRect,
const SkIRect& srcIR,
const SkIRect& dstIR,
SkTDArray<GrHoistedLayer>* needRendering,
SkTDArray<GrHoistedLayer>* recycled,
bool attemptToAtlas,
@ -33,15 +34,16 @@ static void prepare_for_hoisting(GrLayerCache* layerCache,
GrCachedLayer* layer = layerCache->findLayerOrCreate(topLevelPicture->uniqueID(),
SkToInt(info.fSaveLayerOpID),
SkToInt(info.fRestoreOpID),
layerRect,
srcIR,
dstIR,
initialMat,
info.fKey,
info.fKeySize,
info.fPaint);
GrSurfaceDesc desc;
desc.fFlags = kRenderTarget_GrSurfaceFlag;
desc.fWidth = layerRect.width();
desc.fHeight = layerRect.height();
desc.fWidth = srcIR.width();
desc.fHeight = srcIR.height();
desc.fConfig = kSkia8888_GrPixelConfig;
desc.fSampleCnt = numSamples;
@ -80,6 +82,40 @@ static void prepare_for_hoisting(GrLayerCache* layerCache,
hl->fPreMat.preConcat(info.fPreMat);
}
// Compute the source rect if possible and return false if further processing
// on the layer should be abandoned based on its source rect.
static bool compute_source_rect(const SkLayerInfo::BlockInfo& info, const SkMatrix& initialMat,
const SkIRect& dstIR, SkIRect* srcIR) {
SkIRect clipBounds = dstIR;
SkMatrix totMat = initialMat;
totMat.preConcat(info.fPreMat);
totMat.preConcat(info.fLocalMat);
if (info.fPaint && info.fPaint->getImageFilter()) {
info.fPaint->getImageFilter()->filterBounds(clipBounds, totMat, &clipBounds);
}
if (!info.fSrcBounds.isEmpty()) {
SkRect r;
totMat.mapRect(&r, info.fSrcBounds);
r.roundOut(srcIR);
if (!srcIR->intersect(clipBounds)) {
return false;
}
} else {
*srcIR = clipBounds;
}
if (!GrLayerCache::PlausiblyAtlasable(srcIR->width(), srcIR->height())) {
return false;
}
return true;
}
// Atlased layers must be small enough to fit in the atlas, not have a
// paint with an image filter and be neither nested nor nesting.
// TODO: allow leaf nested layers to appear in the atlas.
@ -130,14 +166,16 @@ void GrLayerHoister::FindLayersToAtlas(GrContext* context,
continue;
}
const SkIRect ir = layerRect.roundOut();
const SkIRect dstIR = layerRect.roundOut();
if (!GrLayerCache::PlausiblyAtlasable(ir.width(), ir.height())) {
SkIRect srcIR;
if (!compute_source_rect(info, initialMat, dstIR, &srcIR)) {
continue;
}
prepare_for_hoisting(layerCache, topLevelPicture, initialMat,
info, ir, atlased, recycled, true, 0);
info, srcIR, dstIR, atlased, recycled, true, 0);
}
}
@ -179,9 +217,14 @@ void GrLayerHoister::FindLayersToHoist(GrContext* context,
continue;
}
const SkIRect ir = layerRect.roundOut();
const SkIRect dstIR = layerRect.roundOut();
prepare_for_hoisting(layerCache, topLevelPicture, initialMat, info, ir,
SkIRect srcIR;
if (!compute_source_rect(info, initialMat, dstIR, &srcIR)) {
continue;
}
prepare_for_hoisting(layerCache, topLevelPicture, initialMat, info, srcIR, dstIR,
needRendering, recycled, false, numSamples);
}
}
@ -198,7 +241,7 @@ void GrLayerHoister::DrawLayersToAtlas(GrContext* context,
for (int i = 0; i < atlased.count(); ++i) {
const GrCachedLayer* layer = atlased[i].fLayer;
const SkPicture* pict = atlased[i].fPicture;
const SkIPoint offset = SkIPoint::Make(layer->bound().fLeft, layer->bound().fTop);
const SkIPoint offset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop);
SkDEBUGCODE(const SkPaint* layerPaint = layer->paint();)
SkASSERT(!layerPaint || !layerPaint->getImageFilter());
@ -234,42 +277,51 @@ void GrLayerHoister::DrawLayersToAtlas(GrContext* context,
}
}
void GrLayerHoister::FilterLayer(GrContext* context, SkGpuDevice* device, GrCachedLayer* layer) {
void GrLayerHoister::FilterLayer(GrContext* context,
SkGpuDevice* device,
const GrHoistedLayer& info) {
GrCachedLayer* layer = info.fLayer;
SkASSERT(layer->filter());
SkASSERT(layer->filter()->canFilterImageGPU());
static const int kDefaultCacheSize = 32 * 1024 * 1024;
if (layer->filter()->canFilterImageGPU()) {
SkBitmap filteredBitmap;
SkIPoint offset = SkIPoint::Make(0, 0);
SkBitmap filteredBitmap;
SkIPoint offset = SkIPoint::Make(0, 0);
SkASSERT(0 == layer->rect().fLeft && 0 == layer->rect().fTop);
SkIRect clipBounds = layer->rect();
const SkIPoint filterOffset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop);
// This cache is transient, and is freed (along with all its contained
// textures) when it goes out of scope.
SkAutoTUnref<SkImageFilter::Cache> cache(SkImageFilter::Cache::Create(kDefaultCacheSize));
SkImageFilter::Context filterContext(SkMatrix::I(), clipBounds, cache);
SkMatrix totMat = SkMatrix::I();
totMat.preConcat(info.fPreMat);
totMat.preConcat(info.fLocalMat);
totMat.postTranslate(-SkIntToScalar(filterOffset.fX), -SkIntToScalar(filterOffset.fY));
if (!device->filterTexture(context, layer->texture(), layer->filter(),
filterContext, &filteredBitmap, &offset)) {
// Filtering failed. Press on with the unfiltered version
return;
}
// TODO: need to fix up offset
SkASSERT(0 == offset.fX && 0 == offset.fY);
SkASSERT(0 == layer->rect().fLeft && 0 == layer->rect().fTop);
SkIRect clipBounds = layer->rect();
SkIRect newRect = SkIRect::MakeWH(filteredBitmap.width(), filteredBitmap.height());
layer->setTexture(filteredBitmap.getTexture(), newRect);
// This cache is transient, and is freed (along with all its contained
// textures) when it goes out of scope.
SkAutoTUnref<SkImageFilter::Cache> cache(SkImageFilter::Cache::Create(kDefaultCacheSize));
SkImageFilter::Context filterContext(totMat, clipBounds, cache);
if (!device->filterTexture(context, layer->texture(), layer->filter(),
filterContext, &filteredBitmap, &offset)) {
// Filtering failed. Press on with the unfiltered version
return;
}
SkIRect newRect = SkIRect::MakeWH(filteredBitmap.width(), filteredBitmap.height());
layer->setTexture(filteredBitmap.getTexture(), newRect);
layer->setOffset(offset);
}
void GrLayerHoister::DrawLayers(GrContext* context, const SkTDArray<GrHoistedLayer>& layers) {
for (int i = 0; i < layers.count(); ++i) {
GrCachedLayer* layer = layers[i].fLayer;
const SkPicture* pict = layers[i].fPicture;
const SkIPoint offset = SkIPoint::Make(layer->bound().fLeft, layer->bound().fTop);
const SkIPoint offset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop);
// Each non-atlased layer has its own GrTexture
SkAutoTUnref<SkSurface> surface(SkSurface::NewRenderTargetDirect(
@ -301,7 +353,7 @@ void GrLayerHoister::DrawLayers(GrContext* context, const SkTDArray<GrHoistedLay
if (layer->filter()) {
SkSurface_Gpu* gpuSurf = static_cast<SkSurface_Gpu*>(surface.get());
FilterLayer(context, gpuSurf->getDevice(), layer);
FilterLayer(context, gpuSurf->getDevice(), layers[i]);
}
}
}

View File

@ -108,9 +108,9 @@ private:
/** Update the GrTexture in 'layer' with its filtered version
@param context Owner of the layer cache (and thus the layers)
@param device Required by the filtering code
@param layer A layer needing filtering prior to being composited
@param info Layer info for a layer needing filtering prior to being composited
*/
static void FilterLayer(GrContext* context, SkGpuDevice* device, GrCachedLayer* layer);
static void FilterLayer(GrContext* context, SkGpuDevice* device, const GrHoistedLayer& info);
};

View File

@ -21,8 +21,6 @@ static inline void wrap_texture(GrTexture* texture, int width, int height, SkBit
}
static inline void draw_replacement_bitmap(GrCachedLayer* layer, SkCanvas* canvas) {
const SkRect src = SkRect::Make(layer->rect());
const SkRect dst = SkRect::Make(layer->bound());
SkBitmap bm;
wrap_texture(layer->texture(),
@ -30,10 +28,22 @@ static inline void draw_replacement_bitmap(GrCachedLayer* layer, SkCanvas* canva
!layer->isAtlased() ? layer->rect().height() : layer->texture()->height(),
&bm);
canvas->save();
canvas->setMatrix(SkMatrix::I());
canvas->drawBitmapRectToRect(bm, &src, dst, layer->paint());
canvas->restore();
if (layer->isAtlased()) {
const SkRect src = SkRect::Make(layer->rect());
const SkRect dst = SkRect::Make(layer->srcIR());
SkASSERT(layer->offset().isZero());
canvas->save();
canvas->setMatrix(SkMatrix::I());
canvas->drawBitmapRectToRect(bm, &src, dst, layer->paint());
canvas->restore();
} else {
canvas->drawSprite(bm,
layer->srcIR().fLeft + layer->offset().fX,
layer->srcIR().fTop + layer->offset().fY,
layer->paint());
}
}
// Used by GrRecordReplaceDraw. It intercepts nested drawPicture calls and

View File

@ -43,6 +43,7 @@ static void create_layers(skiatest::Reporter* reporter,
GrCachedLayer* layer = cache->findLayerOrCreate(picture.uniqueID(),
idOffset+i+1, idOffset+i+2,
SkIRect::MakeEmpty(),
SkIRect::MakeEmpty(),
SkMatrix::I(),
indices, 1,
NULL);

View File

@ -113,6 +113,7 @@ void test_replacements(skiatest::Reporter* r, GrContext* context, bool useBBH) {
SkPaint paint;
GrLayerCache* layerCache = context->getLayerCache();
GrCachedLayer* layer = layerCache->findLayerOrCreate(pic->uniqueID(), 0, 2,
SkIRect::MakeWH(kWidth, kHeight),
SkIRect::MakeWH(kWidth, kHeight),
SkMatrix::I(), key, 1, &paint);
@ -133,20 +134,17 @@ void test_replacements(skiatest::Reporter* r, GrContext* context, bool useBBH) {
GrRecordReplaceDraw(pic, &canvas, layerCache, SkMatrix::I(), NULL/*callback*/);
int recount = rerecord.count();
REPORTER_ASSERT(r, 5 == recount || 7 == recount);
REPORTER_ASSERT(r, 2 == recount || 4 == recount);
int index = 0;
if (7 == recount) {
if (4 == recount) {
assert_type<SkRecords::Save>(r, rerecord, 0);
index += 1;
}
assert_type<SkRecords::Save>(r, rerecord, index + 0);
assert_type<SkRecords::SetMatrix>(r, rerecord, index + 1);
assert_type<SkRecords::DrawBitmapRectToRect>(r, rerecord, index + 2);
assert_type<SkRecords::Restore>(r, rerecord, index + 3);
assert_type<SkRecords::DrawRect>(r, rerecord, index + 4);
if (7 == recount) {
assert_type<SkRecords::Restore>(r, rerecord, 6);
assert_type<SkRecords::DrawSprite>(r, rerecord, index + 0);
assert_type<SkRecords::DrawRect>(r, rerecord, index + 1);
if (4 == recount) {
assert_type<SkRecords::Restore>(r, rerecord, 3);
}
}