Sketch splitting SkPicture into an interface and SkBigPicture.
Adds small pictures for drawRect(), drawTextBlob(), and drawPath(). These cover about 89% of draw calls from Blink SKPs, and about 25% of draw calls from our GMs. SkPicture handles: - serialization and deserialization - unique IDs Everything else is left to the subclasses: - playback(), cullRect() - hasBitmap(), hasText(), suitableForGPU(), etc. - LayerInfo / AccelData if applicable. The time to record a 1-op picture improves a good chunk (2 mallocs to 1), and the time to record a 0-op picture greatly improves (2 mallocs to none): picture_overhead_draw: 450ns -> 350ns picture_overhead_nodraw: 300ns -> 90ns BUG=skia: Review URL: https://codereview.chromium.org/1112523006
This commit is contained in:
parent
2fbd4068bd
commit
c92c129ff8
@ -22,6 +22,7 @@
|
||||
'<(skia_src_path)/core/SkAntiRun.h',
|
||||
'<(skia_src_path)/core/SkBBHFactory.cpp',
|
||||
'<(skia_src_path)/core/SkBBoxHierarchy.h',
|
||||
'<(skia_src_path)/core/SkBigPicture.cpp',
|
||||
'<(skia_src_path)/core/SkBitmap.cpp',
|
||||
'<(skia_src_path)/core/SkBitmapCache.cpp',
|
||||
'<(skia_src_path)/core/SkBitmapDevice.cpp',
|
||||
@ -116,7 +117,6 @@
|
||||
'<(skia_src_path)/core/SkImageInfo.cpp',
|
||||
'<(skia_src_path)/core/SkImageGenerator.cpp',
|
||||
'<(skia_src_path)/core/SkLayerInfo.h',
|
||||
'<(skia_src_path)/core/SkLayerInfo.cpp',
|
||||
'<(skia_src_path)/core/SkLocalMatrixShader.cpp',
|
||||
'<(skia_src_path)/core/SkLineClipper.cpp',
|
||||
'<(skia_src_path)/core/SkMallocPixelRef.cpp',
|
||||
@ -132,6 +132,7 @@
|
||||
'<(skia_src_path)/core/SkMessageBus.h',
|
||||
'<(skia_src_path)/core/SkMetaData.cpp',
|
||||
'<(skia_src_path)/core/SkMipMap.cpp',
|
||||
'<(skia_src_path)/core/SkMiniRecorder.cpp',
|
||||
'<(skia_src_path)/core/SkMultiPictureDraw.cpp',
|
||||
'<(skia_src_path)/core/SkPackBits.cpp',
|
||||
'<(skia_src_path)/core/SkPaint.cpp',
|
||||
|
@ -79,7 +79,6 @@
|
||||
'<(skia_src_path)/utils/SkParse.cpp',
|
||||
'<(skia_src_path)/utils/SkParseColor.cpp',
|
||||
'<(skia_src_path)/utils/SkParsePath.cpp',
|
||||
'<(skia_src_path)/utils/SkPictureUtils.cpp',
|
||||
'<(skia_src_path)/utils/SkPatchGrid.cpp',
|
||||
'<(skia_src_path)/utils/SkPatchGrid.h',
|
||||
'<(skia_src_path)/utils/SkPatchUtils.cpp',
|
||||
|
@ -5,61 +5,31 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SkPicture_DEFINED
|
||||
#define SkPicture_DEFINED
|
||||
|
||||
#include "SkImageDecoder.h"
|
||||
#include "SkRefCnt.h"
|
||||
#include "SkTDArray.h"
|
||||
#include "SkTypes.h"
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
class GrContext;
|
||||
#endif
|
||||
|
||||
class SkBigPicture;
|
||||
class SkBitmap;
|
||||
class SkBBoxHierarchy;
|
||||
class SkCanvas;
|
||||
class SkData;
|
||||
class SkPictureData;
|
||||
class SkPixelSerializer;
|
||||
class SkStream;
|
||||
class SkWStream;
|
||||
|
||||
struct SkPictInfo;
|
||||
|
||||
class SkRecord;
|
||||
|
||||
namespace SkRecords {
|
||||
class CollectLayers;
|
||||
};
|
||||
|
||||
/** \class SkPicture
|
||||
|
||||
The SkPicture class records the drawing commands made to a canvas, to
|
||||
be played back at a later time.
|
||||
An SkPicture records drawing commands made to a canvas to be played back at a later time.
|
||||
This base class handles serialization and a few other miscellany.
|
||||
*/
|
||||
class SK_API SkPicture : public SkNVRefCnt<SkPicture> {
|
||||
class SK_API SkPicture : public SkRefCnt {
|
||||
public:
|
||||
// AccelData provides a base class for device-specific acceleration data.
|
||||
class AccelData : public SkRefCnt {
|
||||
public:
|
||||
typedef uint8_t Domain;
|
||||
typedef uint32_t Key;
|
||||
|
||||
AccelData(Key key) : fKey(key) { }
|
||||
|
||||
const Key& getKey() const { return fKey; }
|
||||
|
||||
// This entry point allows user's to get a unique domain prefix
|
||||
// for their keys
|
||||
static Domain GenerateDomain();
|
||||
private:
|
||||
Key fKey;
|
||||
};
|
||||
|
||||
/** PRIVATE / EXPERIMENTAL -- do not call */
|
||||
const AccelData* EXPERIMENTAL_getAccelData(AccelData::Key) const;
|
||||
virtual ~SkPicture();
|
||||
|
||||
/**
|
||||
* Function signature defining a function that sets up an SkBitmap from encoded data. On
|
||||
@ -95,8 +65,6 @@ public:
|
||||
*/
|
||||
static SkPicture* CreateFromBuffer(SkReadBuffer&);
|
||||
|
||||
~SkPicture();
|
||||
|
||||
/**
|
||||
* Subclasses of this can be passed to playback(). During the playback
|
||||
* of the picture, this callback will periodically be invoked. If its
|
||||
@ -111,7 +79,6 @@ public:
|
||||
public:
|
||||
AbortCallback() {}
|
||||
virtual ~AbortCallback() {}
|
||||
|
||||
virtual bool abort() = 0;
|
||||
};
|
||||
|
||||
@ -122,16 +89,14 @@ public:
|
||||
@param canvas the canvas receiving the drawing commands.
|
||||
@param callback a callback that allows interruption of playback
|
||||
*/
|
||||
void playback(SkCanvas* canvas, AbortCallback* = NULL) const;
|
||||
virtual void playback(SkCanvas*, AbortCallback* = NULL) const = 0;
|
||||
|
||||
/** Return the cull rect used when creating this picture: { 0, 0, cullWidth, cullHeight }.
|
||||
It does not necessarily reflect the bounds of what has been recorded into the picture.
|
||||
@return the cull rect used to create this picture
|
||||
*/
|
||||
SkRect cullRect() const { return fCullRect; }
|
||||
|
||||
/** Return a non-zero, unique value representing the picture.
|
||||
/** Return a cull rect for this picture.
|
||||
Ops recorded into this picture that attempt to draw outside the cull might not be drawn.
|
||||
*/
|
||||
virtual SkRect cullRect() const = 0;
|
||||
|
||||
/** Returns a non-zero value unique among all pictures. */
|
||||
uint32_t uniqueID() const;
|
||||
|
||||
/**
|
||||
@ -140,7 +105,7 @@ public:
|
||||
*
|
||||
* TODO: Use serializer to serialize SkImages as well.
|
||||
*/
|
||||
void serialize(SkWStream*, SkPixelSerializer* serializer = NULL) const;
|
||||
void serialize(SkWStream*, SkPixelSerializer* = NULL) const;
|
||||
|
||||
/**
|
||||
* Serialize to a buffer.
|
||||
@ -151,7 +116,21 @@ public:
|
||||
* Returns true if any bitmaps may be produced when this SkPicture
|
||||
* is replayed.
|
||||
*/
|
||||
bool willPlayBackBitmaps() const;
|
||||
virtual bool willPlayBackBitmaps() const = 0;
|
||||
|
||||
/** Return the approximate number of operations in this picture. This
|
||||
* number may be greater or less than the number of SkCanvas calls
|
||||
* recorded: some calls may be recorded as more than one operation, or some
|
||||
* calls may be optimized away.
|
||||
*/
|
||||
virtual int approximateOpCount() const = 0;
|
||||
|
||||
/** Return true if this picture contains text.
|
||||
*/
|
||||
virtual bool hasText() const = 0;
|
||||
|
||||
/** Returns the approximate byte size of this picture, not including large ref'd objects. */
|
||||
virtual size_t approximateBytesUsed() const = 0;
|
||||
|
||||
/** Return true if the SkStream/Buffer represents a serialized picture, and
|
||||
fills out SkPictInfo. After this function returns, the data source is not
|
||||
@ -164,76 +143,24 @@ public:
|
||||
static bool InternalOnly_StreamIsSKP(SkStream*, SkPictInfo*);
|
||||
static bool InternalOnly_BufferIsSKP(SkReadBuffer*, SkPictInfo*);
|
||||
|
||||
/** Return true if the picture is suitable for rendering on the GPU.
|
||||
*/
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
bool suitableForGpuRasterization(GrContext*, const char ** = NULL) const;
|
||||
#endif
|
||||
|
||||
/** Return the approximate number of operations in this picture. This
|
||||
* number may be greater or less than the number of SkCanvas calls
|
||||
* recorded: some calls may be recorded as more than one operation, or some
|
||||
* calls may be optimized away.
|
||||
*/
|
||||
int approximateOpCount() const;
|
||||
|
||||
/** Return true if this picture contains text.
|
||||
*/
|
||||
bool hasText() const;
|
||||
|
||||
// An array of refcounted const SkPicture pointers.
|
||||
class SnapshotArray : ::SkNoncopyable {
|
||||
public:
|
||||
SnapshotArray(const SkPicture* pics[], int count) : fPics(pics), fCount(count) {}
|
||||
~SnapshotArray() { for (int i = 0; i < fCount; i++) { fPics[i]->unref(); } }
|
||||
|
||||
const SkPicture* const* begin() const { return fPics; }
|
||||
int count() const { return fCount; }
|
||||
private:
|
||||
SkAutoTMalloc<const SkPicture*> fPics;
|
||||
int fCount;
|
||||
};
|
||||
/** Return true if the picture is suitable for rendering on the GPU. */
|
||||
virtual bool suitableForGpuRasterization(GrContext*, const char** whyNot = NULL) const = 0;
|
||||
|
||||
// Sent via SkMessageBus from destructor.
|
||||
struct DeletionMessage { int32_t fUniqueID; };
|
||||
struct DeletionMessage { int32_t fUniqueID; }; // TODO: -> uint32_t?
|
||||
|
||||
// Returns NULL if this is not an SkBigPicture.
|
||||
virtual const SkBigPicture* asSkBigPicture() const { return NULL; }
|
||||
|
||||
private:
|
||||
// V2 : adds SkPixelRef's generation ID.
|
||||
// V3 : PictInfo tag at beginning, and EOF tag at the end
|
||||
// V4 : move SkPictInfo to be the header
|
||||
// V5 : don't read/write FunctionPtr on cross-process (we can detect that)
|
||||
// V6 : added serialization of SkPath's bounds (and packed its flags tighter)
|
||||
// V7 : changed drawBitmapRect(IRect) to drawBitmapRectToRect(Rect)
|
||||
// V8 : Add an option for encoding bitmaps
|
||||
// V9 : Allow the reader and writer of an SKP disagree on whether to support
|
||||
// SK_SUPPORT_HINTING_SCALE_FACTOR
|
||||
// V10: add drawRRect, drawOval, clipRRect
|
||||
// V11: modify how readBitmap and writeBitmap store their info.
|
||||
// V12: add conics to SkPath, use new SkPathRef flattening
|
||||
// V13: add flag to drawBitmapRectToRect
|
||||
// parameterize blurs by sigma rather than radius
|
||||
// V14: Add flags word to PathRef serialization
|
||||
// V15: Remove A1 bitmap config (and renumber remaining configs)
|
||||
// V16: Move SkPath's isOval flag to SkPathRef
|
||||
// V17: SkPixelRef now writes SkImageInfo
|
||||
// V18: SkBitmap now records x,y for its pixelref origin, instead of offset.
|
||||
// V19: encode matrices and regions into the ops stream
|
||||
// V20: added bool to SkPictureImageFilter's serialization (to allow SkPicture serialization)
|
||||
// V21: add pushCull, popCull
|
||||
// V22: SK_PICT_FACTORY_TAG's size is now the chunk size in bytes
|
||||
// V23: SkPaint::FilterLevel became a real enum
|
||||
// V24: SkTwoPointConicalGradient now has fFlipped flag for gradient flipping
|
||||
// V25: SkDashPathEffect now only writes phase and interval array when flattening
|
||||
// V26: Removed boolean from SkColorShader for inheriting color from SkPaint.
|
||||
// V27: Remove SkUnitMapper from gradients (and skia).
|
||||
// V28: No longer call bitmap::flatten inside SkWriteBuffer::writeBitmap.
|
||||
// V29: Removed SaveFlags parameter from save().
|
||||
// V30: Remove redundant SkMatrix from SkLocalMatrixShader.
|
||||
// V31: Add a serialized UniqueID to SkImageFilter.
|
||||
// V32: Removed SkPaintOptionsAndroid from SkPaint
|
||||
// V33: Serialize only public API of effects.
|
||||
// V34: Add SkTextBlob serialization.
|
||||
// Subclass whitelist.
|
||||
SkPicture();
|
||||
friend class SkBigPicture;
|
||||
friend class SkEmptyPicture;
|
||||
template <typename> friend class SkMiniPicture;
|
||||
|
||||
virtual int numSlowPaths() const = 0;
|
||||
|
||||
// V35: Store SkRect (rather then width & height) in header
|
||||
// V36: Remove (obsolete) alphatype from SkColorTable
|
||||
// V37: Added shadow only option to SkDropShadowImageFilter (last version to record CLEAR)
|
||||
@ -242,65 +169,20 @@ private:
|
||||
// V40: Remove UniqueID serialization from SkImageFilter.
|
||||
// V41: Added serialization of SkBitmapSource's filterQuality parameter
|
||||
|
||||
// Note: If the picture version needs to be increased then please follow the
|
||||
// steps to generate new SKPs in (only accessible to Googlers): http://goo.gl/qATVcw
|
||||
|
||||
// Only SKPs within the min/current picture version range (inclusive) can be read.
|
||||
static const uint32_t MIN_PICTURE_VERSION = 35; // Produced by Chrome M39.
|
||||
static const uint32_t CURRENT_PICTURE_VERSION = 41;
|
||||
static const uint32_t MIN_PICTURE_VERSION = 35, // Produced by Chrome M39.
|
||||
CURRENT_PICTURE_VERSION = 41;
|
||||
|
||||
static_assert(MIN_PICTURE_VERSION <= 41,
|
||||
"Remove kFontFileName and related code from SkFontDescriptor.cpp.");
|
||||
|
||||
void createHeader(SkPictInfo* info) const;
|
||||
static bool IsValidPictInfo(const SkPictInfo& info);
|
||||
|
||||
// Takes ownership of the (optional) SnapshotArray.
|
||||
// For performance, we take ownership of the caller's refs on the SkRecord, BBH, and AccelData.
|
||||
SkPicture(const SkRect& cullRect,
|
||||
SkRecord*,
|
||||
SnapshotArray*,
|
||||
SkBBoxHierarchy*,
|
||||
AccelData*,
|
||||
size_t approxBytesUsedBySubPictures);
|
||||
|
||||
static SkPicture* Forwardport(const SkPictInfo&, const SkPictureData*);
|
||||
static SkPictureData* Backport(const SkRecord&, const SkPictInfo&,
|
||||
SkPicture const* const drawablePics[], int drawableCount);
|
||||
|
||||
// uint32_t fRefCnt; from SkNVRefCnt<SkPicture>
|
||||
mutable uint32_t fUniqueID;
|
||||
const SkRect fCullRect;
|
||||
SkAutoTUnref<const SkRecord> fRecord;
|
||||
SkAutoTDelete<const SnapshotArray> fDrawablePicts;
|
||||
SkAutoTUnref<const SkBBoxHierarchy> fBBH;
|
||||
SkAutoTUnref<const AccelData> fAccelData;
|
||||
const size_t fApproxBytesUsedBySubPictures;
|
||||
SkPictInfo createHeader() const;
|
||||
SkPictureData* backport() const;
|
||||
|
||||
// helpers for fDrawablePicts
|
||||
int drawableCount() const;
|
||||
// will return NULL if drawableCount() returns 0
|
||||
SkPicture const* const* drawablePicts() const;
|
||||
|
||||
struct PathCounter;
|
||||
|
||||
struct Analysis {
|
||||
Analysis() {} // Only used by SkPictureData codepath.
|
||||
explicit Analysis(const SkRecord&);
|
||||
|
||||
bool suitableForGpuRasterization(const char** reason, int sampleCount) const;
|
||||
|
||||
uint8_t fNumSlowPathsAndDashEffects;
|
||||
bool fWillPlaybackBitmaps : 1;
|
||||
bool fHasText : 1;
|
||||
} fAnalysis;
|
||||
|
||||
friend class SkPictureRecorder; // SkRecord-based constructor.
|
||||
friend class GrLayerHoister; // access to fRecord
|
||||
friend class ReplaceDraw;
|
||||
friend class SkPictureUtils;
|
||||
friend class SkRecordedDrawable;
|
||||
mutable uint32_t fUniqueID;
|
||||
};
|
||||
SK_COMPILE_ASSERT(sizeof(SkPicture) <= 88, SkPictureSize);
|
||||
|
||||
#endif
|
||||
|
@ -8,6 +8,7 @@
|
||||
#ifndef SkPictureRecorder_DEFINED
|
||||
#define SkPictureRecorder_DEFINED
|
||||
|
||||
#include "../../src/core/SkMiniRecorder.h"
|
||||
#include "SkBBHFactory.h"
|
||||
#include "SkPicture.h"
|
||||
#include "SkRefCnt.h"
|
||||
@ -102,6 +103,7 @@ private:
|
||||
SkAutoTUnref<SkBBoxHierarchy> fBBH;
|
||||
SkAutoTUnref<SkRecorder> fRecorder;
|
||||
SkAutoTUnref<SkRecord> fRecord;
|
||||
SkMiniRecorder fMiniRecorder;
|
||||
|
||||
typedef SkNoncopyable INHERITED;
|
||||
};
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#include "SkPicture.h"
|
||||
|
||||
// TODO: remove this file?
|
||||
|
||||
class SK_API SkPictureUtils {
|
||||
public:
|
||||
/**
|
||||
@ -18,7 +20,9 @@ public:
|
||||
* includes nested SkPictures, but does not include large objects that
|
||||
* SkRecord holds a reference to (e.g. paths, or pixels backing bitmaps).
|
||||
*/
|
||||
static size_t ApproximateBytesUsed(const SkPicture* pict);
|
||||
static size_t ApproximateBytesUsed(const SkPicture* pict) {
|
||||
return pict->approximateBytesUsed();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
167
src/core/SkBigPicture.cpp
Normal file
167
src/core/SkBigPicture.cpp
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBBoxHierarchy.h"
|
||||
#include "SkBigPicture.h"
|
||||
#include "SkPathEffect.h"
|
||||
#include "SkPictureCommon.h"
|
||||
#include "SkRecord.h"
|
||||
#include "SkRecordDraw.h"
|
||||
|
||||
SkBigPicture::SkBigPicture(const SkRect& cull,
|
||||
SkRecord* record,
|
||||
SnapshotArray* drawablePicts,
|
||||
SkBBoxHierarchy* bbh,
|
||||
AccelData* accelData,
|
||||
size_t approxBytesUsedBySubPictures)
|
||||
: fCullRect(cull)
|
||||
, fAnalysis(*record)
|
||||
, fApproxBytesUsedBySubPictures(approxBytesUsedBySubPictures)
|
||||
, fRecord(record) // Take ownership of caller's ref.
|
||||
, fDrawablePicts(drawablePicts) // Take ownership.
|
||||
, fBBH(bbh) // Take ownership of caller's ref.
|
||||
, fAccelData(accelData) // Take ownership of caller's ref.
|
||||
{}
|
||||
|
||||
void SkBigPicture::playback(SkCanvas* canvas, AbortCallback* callback) const {
|
||||
SkASSERT(canvas);
|
||||
|
||||
// If the query contains the whole picture, don't bother with the BBH.
|
||||
SkRect clipBounds = { 0, 0, 0, 0 };
|
||||
(void)canvas->getClipBounds(&clipBounds);
|
||||
const bool useBBH = !clipBounds.contains(this->cullRect());
|
||||
|
||||
SkRecordDraw(*fRecord,
|
||||
canvas,
|
||||
this->drawablePicts(),
|
||||
nullptr,
|
||||
this->drawableCount(),
|
||||
useBBH ? fBBH.get() : nullptr,
|
||||
callback);
|
||||
}
|
||||
|
||||
void SkBigPicture::partialPlayback(SkCanvas* canvas,
|
||||
unsigned start,
|
||||
unsigned stop,
|
||||
const SkMatrix& initialCTM) const {
|
||||
SkASSERT(canvas);
|
||||
SkRecordPartialDraw(*fRecord,
|
||||
canvas,
|
||||
this->drawablePicts(),
|
||||
this->drawableCount(),
|
||||
start,
|
||||
stop,
|
||||
initialCTM);
|
||||
}
|
||||
|
||||
SkRect SkBigPicture::cullRect() const { return fCullRect; }
|
||||
bool SkBigPicture::hasText() const { return fAnalysis.fHasText; }
|
||||
bool SkBigPicture::willPlayBackBitmaps() const { return fAnalysis.fWillPlaybackBitmaps; }
|
||||
int SkBigPicture::numSlowPaths() const { return fAnalysis.fNumSlowPathsAndDashEffects; }
|
||||
int SkBigPicture::approximateOpCount() const { return fRecord->count(); }
|
||||
size_t SkBigPicture::approximateBytesUsed() const {
|
||||
size_t bytes = sizeof(*this) + fRecord->bytesUsed() + fApproxBytesUsedBySubPictures;
|
||||
if (fBBH) { bytes += fBBH->bytesUsed(); }
|
||||
return bytes;
|
||||
}
|
||||
bool SkBigPicture::suitableForGpuRasterization(GrContext*, const char** reason) const {
|
||||
return fAnalysis.suitableForGpuRasterization(reason);
|
||||
}
|
||||
|
||||
int SkBigPicture::drawableCount() const {
|
||||
return fDrawablePicts ? fDrawablePicts->count() : 0;
|
||||
}
|
||||
SkPicture const* const* SkBigPicture::drawablePicts() const {
|
||||
return fDrawablePicts ? fDrawablePicts->begin() : nullptr;
|
||||
}
|
||||
|
||||
// Some ops have a paint, some have an optional paint. Either way, get back a pointer.
|
||||
static const SkPaint* as_ptr(const SkPaint& p) { return &p; }
|
||||
static const SkPaint* as_ptr(const SkRecords::Optional<SkPaint>& p) { return p; }
|
||||
|
||||
struct SkBigPicture::PathCounter {
|
||||
SK_CREATE_MEMBER_DETECTOR(paint);
|
||||
|
||||
PathCounter() : fNumSlowPathsAndDashEffects(0) {}
|
||||
|
||||
// Recurse into nested pictures.
|
||||
void operator()(const SkRecords::DrawPicture& op) {
|
||||
fNumSlowPathsAndDashEffects += op.picture->numSlowPaths();
|
||||
}
|
||||
|
||||
void checkPaint(const SkPaint* paint) {
|
||||
if (paint && paint->getPathEffect()) {
|
||||
// Initially assume it's slow.
|
||||
fNumSlowPathsAndDashEffects++;
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(const SkRecords::DrawPoints& op) {
|
||||
this->checkPaint(&op.paint);
|
||||
const SkPathEffect* effect = op.paint.getPathEffect();
|
||||
if (effect) {
|
||||
SkPathEffect::DashInfo info;
|
||||
SkPathEffect::DashType dashType = effect->asADash(&info);
|
||||
if (2 == op.count && SkPaint::kRound_Cap != op.paint.getStrokeCap() &&
|
||||
SkPathEffect::kDash_DashType == dashType && 2 == info.fCount) {
|
||||
fNumSlowPathsAndDashEffects--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(const SkRecords::DrawPath& op) {
|
||||
this->checkPaint(&op.paint);
|
||||
if (op.paint.isAntiAlias() && !op.path.isConvex()) {
|
||||
SkPaint::Style paintStyle = op.paint.getStyle();
|
||||
const SkRect& pathBounds = op.path.getBounds();
|
||||
if (SkPaint::kStroke_Style == paintStyle &&
|
||||
0 == op.paint.getStrokeWidth()) {
|
||||
// AA hairline concave path is not slow.
|
||||
} else if (SkPaint::kFill_Style == paintStyle && pathBounds.width() < 64.f &&
|
||||
pathBounds.height() < 64.f && !op.path.isVolatile()) {
|
||||
// AADF eligible concave path is not slow.
|
||||
} else {
|
||||
fNumSlowPathsAndDashEffects++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SK_WHEN(HasMember_paint<T>, void) operator()(const T& op) {
|
||||
this->checkPaint(as_ptr(op.paint));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SK_WHEN(!HasMember_paint<T>, void) operator()(const T& op) { /* do nothing */ }
|
||||
|
||||
int fNumSlowPathsAndDashEffects;
|
||||
};
|
||||
|
||||
SkBigPicture::Analysis::Analysis(const SkRecord& record) {
|
||||
SkTextHunter text;
|
||||
SkBitmapHunter bitmap;
|
||||
PathCounter path;
|
||||
|
||||
bool hasText = false, hasBitmap = false;
|
||||
for (unsigned i = 0; i < record.count(); i++) {
|
||||
hasText = hasText || record.visit<bool>(i, text);
|
||||
hasBitmap = hasBitmap || record.visit<bool>(i, bitmap);
|
||||
record.visit<void>(i, path);
|
||||
}
|
||||
|
||||
fHasText = hasText;
|
||||
fWillPlaybackBitmaps = hasBitmap;
|
||||
fNumSlowPathsAndDashEffects = SkTMin<int>(path.fNumSlowPathsAndDashEffects, 255);
|
||||
}
|
||||
|
||||
bool SkBigPicture::Analysis::suitableForGpuRasterization(const char** reason) const {
|
||||
if (fNumSlowPathsAndDashEffects > 5) {
|
||||
if (reason) { *reason = "Too many slow paths (either concave or dashed)."; }
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
89
src/core/SkBigPicture.h
Normal file
89
src/core/SkBigPicture.h
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkBigPicture_DEFINED
|
||||
#define SkBigPicture_DEFINED
|
||||
|
||||
#include "SkPicture.h"
|
||||
|
||||
class SkBBoxHierarchy;
|
||||
class SkRecord;
|
||||
|
||||
// An implementation of SkPicture supporting an arbitrary number of drawing commands.
|
||||
class SkBigPicture final : public SkPicture {
|
||||
public:
|
||||
// AccelData provides a base class for device-specific acceleration data.
|
||||
class AccelData : public SkRefCnt { };
|
||||
|
||||
// An array of refcounted const SkPicture pointers.
|
||||
class SnapshotArray : ::SkNoncopyable {
|
||||
public:
|
||||
SnapshotArray(const SkPicture* pics[], int count) : fPics(pics), fCount(count) {}
|
||||
~SnapshotArray() { for (int i = 0; i < fCount; i++) { fPics[i]->unref(); } }
|
||||
|
||||
const SkPicture* const* begin() const { return fPics; }
|
||||
int count() const { return fCount; }
|
||||
private:
|
||||
SkAutoTMalloc<const SkPicture*> fPics;
|
||||
int fCount;
|
||||
};
|
||||
|
||||
SkBigPicture(const SkRect& cull,
|
||||
SkRecord*, // We take ownership of the caller's ref.
|
||||
SnapshotArray*, // We take exclusive ownership.
|
||||
SkBBoxHierarchy*, // We take ownership of the caller's ref.
|
||||
AccelData*, // We take ownership of the caller's ref.
|
||||
size_t approxBytesUsedBySubPictures);
|
||||
|
||||
|
||||
// SkPicture overrides
|
||||
void playback(SkCanvas*, AbortCallback*) const override;
|
||||
SkRect cullRect() const override;
|
||||
bool hasText() const override;
|
||||
bool willPlayBackBitmaps() const override;
|
||||
int approximateOpCount() const override;
|
||||
size_t approximateBytesUsed() const override;
|
||||
bool suitableForGpuRasterization(GrContext*, const char** whyNot) const override;
|
||||
const SkBigPicture* asSkBigPicture() const override { return this; }
|
||||
|
||||
// Used by GrLayerHoister
|
||||
void partialPlayback(SkCanvas*,
|
||||
unsigned start,
|
||||
unsigned stop,
|
||||
const SkMatrix& initialCTM) const;
|
||||
// Used by GrRecordReplaceDraw
|
||||
const SkBBoxHierarchy* bbh() const { return fBBH; }
|
||||
const SkRecord* record() const { return fRecord; }
|
||||
const AccelData* accelData() const { return fAccelData; }
|
||||
|
||||
private:
|
||||
struct Analysis {
|
||||
explicit Analysis(const SkRecord&);
|
||||
|
||||
bool suitableForGpuRasterization(const char** reason) const;
|
||||
|
||||
uint8_t fNumSlowPathsAndDashEffects;
|
||||
bool fWillPlaybackBitmaps : 1;
|
||||
bool fHasText : 1;
|
||||
};
|
||||
struct PathCounter;
|
||||
|
||||
int numSlowPaths() const override;
|
||||
|
||||
int drawableCount() const;
|
||||
SkPicture const* const* drawablePicts() const;
|
||||
|
||||
const SkRect fCullRect;
|
||||
const Analysis fAnalysis;
|
||||
const size_t fApproxBytesUsedBySubPictures;
|
||||
SkAutoTUnref<const SkRecord> fRecord;
|
||||
SkAutoTDelete<const SnapshotArray> fDrawablePicts;
|
||||
SkAutoTUnref<const SkBBoxHierarchy> fBBH;
|
||||
SkAutoTUnref<const AccelData> fAccelData;
|
||||
};
|
||||
|
||||
#endif//SkBigPicture_DEFINED
|
@ -1,15 +0,0 @@
|
||||
/*
|
||||
* Copyright 2014 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkLayerInfo.h"
|
||||
|
||||
SkPicture::AccelData::Key SkLayerInfo::ComputeKey() {
|
||||
static const SkPicture::AccelData::Key gGPUID = SkPicture::AccelData::GenerateDomain();
|
||||
|
||||
return gGPUID;
|
||||
}
|
||||
|
@ -8,12 +8,12 @@
|
||||
#ifndef SkLayerInfo_DEFINED
|
||||
#define SkLayerInfo_DEFINED
|
||||
|
||||
#include "SkPicture.h"
|
||||
#include "SkBigPicture.h"
|
||||
#include "SkTArray.h"
|
||||
|
||||
// This class stores information about the saveLayer/restore pairs found
|
||||
// within an SkPicture. It is used by Ganesh to perform layer hoisting.
|
||||
class SkLayerInfo : public SkPicture::AccelData {
|
||||
class SkLayerInfo : public SkBigPicture::AccelData {
|
||||
public:
|
||||
// Information about a given saveLayer/restore block in an SkPicture
|
||||
class BlockInfo {
|
||||
@ -33,12 +33,12 @@ public:
|
||||
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
|
||||
// part of the initial matrix during replay so that it will be
|
||||
// preserved across setMatrix calls.
|
||||
SkMatrix fPreMat;
|
||||
// The matrix state (in the leaf picture) in which this layer's draws
|
||||
// The matrix state (in the leaf picture) in which this layer's draws
|
||||
// must occur. It will/can be overridden by setMatrix calls in the
|
||||
// layer itself. It does not include the translation needed to map the
|
||||
// layer itself. It does not include the translation needed to map the
|
||||
// layer's top-left point to the origin (which must be part of the
|
||||
// initial matrix).
|
||||
SkMatrix fLocalMat;
|
||||
@ -60,7 +60,7 @@ public:
|
||||
int fKeySize; // # of ints
|
||||
};
|
||||
|
||||
SkLayerInfo(Key key) : INHERITED(key) { }
|
||||
SkLayerInfo() {}
|
||||
|
||||
BlockInfo& addBlock() { return fBlocks.push_back(); }
|
||||
|
||||
@ -72,14 +72,10 @@ public:
|
||||
return fBlocks[index];
|
||||
}
|
||||
|
||||
// We may, in the future, need to pass in the GPUDevice in order to
|
||||
// incorporate the clip and matrix state into the key
|
||||
static SkPicture::AccelData::Key ComputeKey();
|
||||
|
||||
private:
|
||||
SkTArray<BlockInfo, true> fBlocks;
|
||||
|
||||
typedef SkPicture::AccelData INHERITED;
|
||||
typedef SkBigPicture::AccelData INHERITED;
|
||||
};
|
||||
|
||||
#endif // SkLayerInfo_DEFINED
|
||||
|
103
src/core/SkMiniRecorder.cpp
Normal file
103
src/core/SkMiniRecorder.cpp
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2015 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 "SkLazyPtr.h"
|
||||
#include "SkMiniRecorder.h"
|
||||
#include "SkPicture.h"
|
||||
#include "SkPictureCommon.h"
|
||||
#include "SkRecordDraw.h"
|
||||
#include "SkTextBlob.h"
|
||||
|
||||
using namespace SkRecords;
|
||||
|
||||
class SkEmptyPicture final : public SkPicture {
|
||||
public:
|
||||
void playback(SkCanvas*, AbortCallback*) const override { }
|
||||
|
||||
size_t approximateBytesUsed() const override { return sizeof(*this); }
|
||||
int approximateOpCount() const override { return 0; }
|
||||
SkRect cullRect() const override { return SkRect::MakeEmpty(); }
|
||||
bool hasText() const override { return false; }
|
||||
int numSlowPaths() const override { return 0; }
|
||||
bool willPlayBackBitmaps() const override { return false; }
|
||||
bool suitableForGpuRasterization(GrContext*, const char**) const override { return true; }
|
||||
};
|
||||
SK_DECLARE_STATIC_LAZY_PTR(SkEmptyPicture, gEmptyPicture);
|
||||
|
||||
template <typename T>
|
||||
class SkMiniPicture final : public SkPicture {
|
||||
public:
|
||||
SkMiniPicture(SkRect cull, T* op) : fCull(cull) {
|
||||
memcpy(&fOp, op, sizeof(fOp)); // We take ownership of op's guts.
|
||||
}
|
||||
|
||||
void playback(SkCanvas* c, AbortCallback*) const override {
|
||||
SkRecords::Draw(c, nullptr, nullptr, 0, nullptr)(fOp);
|
||||
}
|
||||
|
||||
size_t approximateBytesUsed() const override { return sizeof(*this); }
|
||||
int approximateOpCount() const override { return 1; }
|
||||
SkRect cullRect() const override { return fCull; }
|
||||
bool hasText() const override { return SkTextHunter()(fOp); }
|
||||
bool willPlayBackBitmaps() const override { return SkBitmapHunter()(fOp); }
|
||||
|
||||
// TODO: These trivial implementations are not all correct for all types.
|
||||
// But I suspect these will never be called on SkMiniPictures, so assert for now.
|
||||
int numSlowPaths() const override { SkASSERT(false); return 0; }
|
||||
bool suitableForGpuRasterization(GrContext*, const char**) const override {
|
||||
SkASSERT(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
SkRect fCull;
|
||||
T fOp;
|
||||
};
|
||||
|
||||
|
||||
SkMiniRecorder::SkMiniRecorder() : fState(State::kEmpty) {}
|
||||
SkMiniRecorder::~SkMiniRecorder() {
|
||||
// We've done something wrong if no one's called detachAsPicture().
|
||||
SkASSERT(fState == State::kEmpty);
|
||||
}
|
||||
|
||||
#define TRY_TO_STORE(Type, ...) \
|
||||
if (fState != State::kEmpty) { return false; } \
|
||||
fState = State::k##Type; \
|
||||
new (fBuffer.get()) Type(__VA_ARGS__); \
|
||||
return true
|
||||
|
||||
bool SkMiniRecorder::drawRect(const SkRect& rect, const SkPaint& paint) {
|
||||
TRY_TO_STORE(DrawRect, paint, rect);
|
||||
}
|
||||
|
||||
bool SkMiniRecorder::drawPath(const SkPath& path, const SkPaint& paint) {
|
||||
TRY_TO_STORE(DrawPath, paint, path);
|
||||
}
|
||||
|
||||
bool SkMiniRecorder::drawTextBlob(const SkTextBlob* b, SkScalar x, SkScalar y, const SkPaint& p) {
|
||||
TRY_TO_STORE(DrawTextBlob, p, b, x, y);
|
||||
}
|
||||
#undef TRY_TO_STORE
|
||||
|
||||
#define CASE(Type) \
|
||||
case State::k##Type: \
|
||||
fState = State::kEmpty; \
|
||||
return SkNEW_ARGS(SkMiniPicture<Type>, (cull, reinterpret_cast<Type*>(fBuffer.get())))
|
||||
|
||||
SkPicture* SkMiniRecorder::detachAsPicture(const SkRect& cull) {
|
||||
switch (fState) {
|
||||
case State::kEmpty: return SkRef(gEmptyPicture.get());
|
||||
CASE(DrawPath);
|
||||
CASE(DrawRect);
|
||||
CASE(DrawTextBlob);
|
||||
}
|
||||
SkASSERT(false);
|
||||
return NULL;
|
||||
}
|
||||
#undef CASE
|
44
src/core/SkMiniRecorder.h
Normal file
44
src/core/SkMiniRecorder.h
Normal file
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef SkMiniRecorder_DEFINED
|
||||
#define SkMiniRecorder_DEFINED
|
||||
|
||||
#include "SkRecords.h"
|
||||
#include "SkScalar.h"
|
||||
#include "SkTypes.h"
|
||||
class SkCanvas;
|
||||
|
||||
// Records small pictures, but only a limited subset of the canvas API, and may fail.
|
||||
class SkMiniRecorder : SkNoncopyable {
|
||||
public:
|
||||
SkMiniRecorder();
|
||||
~SkMiniRecorder();
|
||||
|
||||
// Try to record an op. Returns false on failure.
|
||||
bool drawPath(const SkPath&, const SkPaint&);
|
||||
bool drawRect(const SkRect&, const SkPaint&);
|
||||
bool drawTextBlob(const SkTextBlob*, SkScalar x, SkScalar y, const SkPaint&);
|
||||
|
||||
// Detach anything we've recorded as a picture, resetting this SkMiniRecorder.
|
||||
SkPicture* detachAsPicture(const SkRect& cull);
|
||||
|
||||
private:
|
||||
enum class State { kEmpty, kDrawPath, kDrawRect, kDrawTextBlob };
|
||||
|
||||
State fState;
|
||||
|
||||
template <size_t A, size_t B>
|
||||
struct Max { static const size_t val = A > B ? A : B; };
|
||||
|
||||
static const size_t kInlineStorage = Max<sizeof(SkRecords::DrawPath),
|
||||
Max<sizeof(SkRecords::DrawRect),
|
||||
sizeof(SkRecords::DrawTextBlob)>::val>::val;
|
||||
SkAlignedSStorage<kInlineStorage> fBuffer;
|
||||
};
|
||||
|
||||
#endif//SkMiniRecorder_DEFINED
|
@ -5,468 +5,33 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
|
||||
#include "SkPictureFlat.h"
|
||||
#include "SkAtomics.h"
|
||||
#include "SkMessageBus.h"
|
||||
#include "SkPicture.h"
|
||||
#include "SkPictureData.h"
|
||||
#include "SkPicturePlayback.h"
|
||||
#include "SkPictureRecord.h"
|
||||
#include "SkPictureRecorder.h"
|
||||
|
||||
#include "SkAtomics.h"
|
||||
#include "SkBitmapDevice.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkChunkAlloc.h"
|
||||
#include "SkMessageBus.h"
|
||||
#include "SkPaintPriv.h"
|
||||
#include "SkPathEffect.h"
|
||||
#include "SkPicture.h"
|
||||
#include "SkRegion.h"
|
||||
#include "SkShader.h"
|
||||
#include "SkStream.h"
|
||||
#include "SkTDArray.h"
|
||||
#include "SkTLogic.h"
|
||||
#include "SkTSearch.h"
|
||||
#include "SkTime.h"
|
||||
|
||||
#include "SkReader32.h"
|
||||
#include "SkWriter32.h"
|
||||
#include "SkRTree.h"
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
#include "GrContext.h"
|
||||
#endif
|
||||
|
||||
#include "SkRecord.h"
|
||||
#include "SkRecordDraw.h"
|
||||
#include "SkRecordOpts.h"
|
||||
#include "SkRecorder.h"
|
||||
|
||||
DECLARE_SKMESSAGEBUS_MESSAGE(SkPicture::DeletionMessage);
|
||||
|
||||
template <typename T> int SafeCount(const T* obj) {
|
||||
return obj ? obj->count() : 0;
|
||||
}
|
||||
/* SkPicture impl. This handles generic responsibilities like unique IDs and serialization. */
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace {
|
||||
|
||||
// Some commands have a paint, some have an optional paint. Either way, get back a pointer.
|
||||
static const SkPaint* AsPtr(const SkPaint& p) { return &p; }
|
||||
static const SkPaint* AsPtr(const SkRecords::Optional<SkPaint>& p) { return p; }
|
||||
|
||||
/** SkRecords visitor to determine whether an instance may require an
|
||||
"external" bitmap to rasterize. May return false positives.
|
||||
Does not return true for bitmap text.
|
||||
|
||||
Expected use is to determine whether images need to be decoded before
|
||||
rasterizing a particular SkRecord.
|
||||
*/
|
||||
struct BitmapTester {
|
||||
// Helpers. These create HasMember_bitmap and HasMember_paint.
|
||||
SK_CREATE_MEMBER_DETECTOR(bitmap);
|
||||
SK_CREATE_MEMBER_DETECTOR(paint);
|
||||
|
||||
|
||||
// Main entry for visitor:
|
||||
// If the command is a DrawPicture, recurse.
|
||||
// If the command has a bitmap directly, return true.
|
||||
// If the command has a paint and the paint has a bitmap, return true.
|
||||
// Otherwise, return false.
|
||||
bool operator()(const SkRecords::DrawPicture& op) { return op.picture->willPlayBackBitmaps(); }
|
||||
|
||||
template <typename T>
|
||||
bool operator()(const T& r) { return CheckBitmap(r); }
|
||||
|
||||
|
||||
// If the command has a bitmap, of course we're going to play back bitmaps.
|
||||
template <typename T>
|
||||
static SK_WHEN(HasMember_bitmap<T>, bool) CheckBitmap(const T&) { return true; }
|
||||
|
||||
// If not, look for one in its paint (if it has a paint).
|
||||
template <typename T>
|
||||
static SK_WHEN(!HasMember_bitmap<T>, bool) CheckBitmap(const T& r) { return CheckPaint(r); }
|
||||
|
||||
// If we have a paint, dig down into the effects looking for a bitmap.
|
||||
template <typename T>
|
||||
static SK_WHEN(HasMember_paint<T>, bool) CheckPaint(const T& r) {
|
||||
const SkPaint* paint = AsPtr(r.paint);
|
||||
if (paint) {
|
||||
const SkShader* shader = paint->getShader();
|
||||
if (shader &&
|
||||
shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we don't have a paint, that non-paint has no bitmap.
|
||||
template <typename T>
|
||||
static SK_WHEN(!HasMember_paint<T>, bool) CheckPaint(const T&) { return false; }
|
||||
};
|
||||
|
||||
bool WillPlaybackBitmaps(const SkRecord& record) {
|
||||
BitmapTester tester;
|
||||
for (unsigned i = 0; i < record.count(); i++) {
|
||||
if (record.visit<bool>(i, tester)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// SkRecord visitor to find recorded text.
|
||||
struct TextHunter {
|
||||
// All ops with text have that text as a char array member named "text".
|
||||
SK_CREATE_MEMBER_DETECTOR(text);
|
||||
bool operator()(const SkRecords::DrawPicture& op) { return op.picture->hasText(); }
|
||||
template <typename T> SK_WHEN(HasMember_text<T>, bool) operator()(const T&) { return true; }
|
||||
template <typename T> SK_WHEN(!HasMember_text<T>, bool) operator()(const T&) { return false; }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/** SkRecords visitor to determine heuristically whether or not a SkPicture
|
||||
will be performant when rasterized on the GPU.
|
||||
*/
|
||||
struct SkPicture::PathCounter {
|
||||
SK_CREATE_MEMBER_DETECTOR(paint);
|
||||
|
||||
PathCounter() : fNumSlowPathsAndDashEffects(0) {}
|
||||
|
||||
// Recurse into nested pictures.
|
||||
void operator()(const SkRecords::DrawPicture& op) {
|
||||
const SkPicture::Analysis& analysis = op.picture->fAnalysis;
|
||||
fNumSlowPathsAndDashEffects += analysis.fNumSlowPathsAndDashEffects;
|
||||
}
|
||||
|
||||
void checkPaint(const SkPaint* paint) {
|
||||
if (paint && paint->getPathEffect()) {
|
||||
// Initially assume it's slow.
|
||||
fNumSlowPathsAndDashEffects++;
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(const SkRecords::DrawPoints& op) {
|
||||
this->checkPaint(&op.paint);
|
||||
const SkPathEffect* effect = op.paint.getPathEffect();
|
||||
if (effect) {
|
||||
SkPathEffect::DashInfo info;
|
||||
SkPathEffect::DashType dashType = effect->asADash(&info);
|
||||
if (2 == op.count && SkPaint::kRound_Cap != op.paint.getStrokeCap() &&
|
||||
SkPathEffect::kDash_DashType == dashType && 2 == info.fCount) {
|
||||
fNumSlowPathsAndDashEffects--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(const SkRecords::DrawPath& op) {
|
||||
this->checkPaint(&op.paint);
|
||||
if (op.paint.isAntiAlias() && !op.path.isConvex()) {
|
||||
SkPaint::Style paintStyle = op.paint.getStyle();
|
||||
const SkRect& pathBounds = op.path.getBounds();
|
||||
if (SkPaint::kStroke_Style == paintStyle &&
|
||||
0 == op.paint.getStrokeWidth()) {
|
||||
// AA hairline concave path is not slow.
|
||||
} else if (SkPaint::kFill_Style == paintStyle && pathBounds.width() < 64.f &&
|
||||
pathBounds.height() < 64.f && !op.path.isVolatile()) {
|
||||
// AADF eligible concave path is not slow.
|
||||
} else {
|
||||
fNumSlowPathsAndDashEffects++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SK_WHEN(HasMember_paint<T>, void) operator()(const T& op) {
|
||||
this->checkPaint(AsPtr(op.paint));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
SK_WHEN(!HasMember_paint<T>, void) operator()(const T& op) { /* do nothing */ }
|
||||
|
||||
int fNumSlowPathsAndDashEffects;
|
||||
};
|
||||
|
||||
SkPicture::Analysis::Analysis(const SkRecord& record) {
|
||||
fWillPlaybackBitmaps = WillPlaybackBitmaps(record);
|
||||
|
||||
PathCounter counter;
|
||||
for (unsigned i = 0; i < record.count(); i++) {
|
||||
record.visit<void>(i, counter);
|
||||
}
|
||||
fNumSlowPathsAndDashEffects = SkTMin<int>(counter.fNumSlowPathsAndDashEffects, 255);
|
||||
|
||||
fHasText = false;
|
||||
TextHunter text;
|
||||
for (unsigned i = 0; i < record.count(); i++) {
|
||||
if (record.visit<bool>(i, text)) {
|
||||
fHasText = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SkPicture::Analysis::suitableForGpuRasterization(const char** reason,
|
||||
int sampleCount) const {
|
||||
// TODO: the heuristic used here needs to be refined
|
||||
static const int kNumSlowPathsTol = 6;
|
||||
|
||||
bool ret = fNumSlowPathsAndDashEffects < kNumSlowPathsTol;
|
||||
|
||||
if (!ret && reason) {
|
||||
*reason = "Too many slow paths (either concave or dashed).";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int SkPicture::drawableCount() const {
|
||||
return fDrawablePicts.get() ? fDrawablePicts->count() : 0;
|
||||
}
|
||||
|
||||
SkPicture const* const* SkPicture::drawablePicts() const {
|
||||
return fDrawablePicts.get() ? fDrawablePicts->begin() : NULL;
|
||||
}
|
||||
SkPicture::SkPicture() : fUniqueID(0) {}
|
||||
|
||||
SkPicture::~SkPicture() {
|
||||
// TODO: move this to ~SkBigPicture() only?
|
||||
|
||||
// If the ID is still zero, no one has read it, so no need to send this message.
|
||||
uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed);
|
||||
if (id != 0) {
|
||||
SkPicture::DeletionMessage msg;
|
||||
msg.fUniqueID = id;
|
||||
SkPicture::DeletionMessage msg = { (int32_t)id };
|
||||
SkMessageBus<SkPicture::DeletionMessage>::Post(msg);
|
||||
}
|
||||
}
|
||||
|
||||
const SkPicture::AccelData* SkPicture::EXPERIMENTAL_getAccelData(
|
||||
SkPicture::AccelData::Key key) const {
|
||||
if (fAccelData.get() && fAccelData->getKey() == key) {
|
||||
return fAccelData.get();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() {
|
||||
static int32_t gNextID = 0;
|
||||
|
||||
int32_t id = sk_atomic_inc(&gNextID);
|
||||
if (id >= 1 << (8 * sizeof(Domain))) {
|
||||
SK_CRASH();
|
||||
}
|
||||
|
||||
return static_cast<Domain>(id);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void SkPicture::playback(SkCanvas* canvas, AbortCallback* callback) const {
|
||||
SkASSERT(canvas);
|
||||
|
||||
// If the query contains the whole picture, don't bother with the BBH.
|
||||
SkRect clipBounds = { 0, 0, 0, 0 };
|
||||
(void)canvas->getClipBounds(&clipBounds);
|
||||
const bool useBBH = !clipBounds.contains(this->cullRect());
|
||||
|
||||
SkRecordDraw(*fRecord, canvas, this->drawablePicts(), NULL, this->drawableCount(),
|
||||
useBBH ? fBBH.get() : NULL, callback);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "SkStream.h"
|
||||
|
||||
static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
|
||||
|
||||
bool SkPicture::IsValidPictInfo(const SkPictInfo& info) {
|
||||
if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (info.fVersion < MIN_PICTURE_VERSION ||
|
||||
info.fVersion > CURRENT_PICTURE_VERSION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
|
||||
if (NULL == stream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check magic bytes.
|
||||
SkPictInfo info;
|
||||
SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
|
||||
|
||||
if (!stream->read(&info.fMagic, sizeof(kMagic))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
info.fVersion = stream->readU32();
|
||||
info.fCullRect.fLeft = stream->readScalar();
|
||||
info.fCullRect.fTop = stream->readScalar();
|
||||
info.fCullRect.fRight = stream->readScalar();
|
||||
info.fCullRect.fBottom = stream->readScalar();
|
||||
|
||||
info.fFlags = stream->readU32();
|
||||
|
||||
if (!IsValidPictInfo(info)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pInfo != NULL) {
|
||||
*pInfo = info;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) {
|
||||
// Check magic bytes.
|
||||
SkPictInfo info;
|
||||
SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
|
||||
|
||||
if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
info.fVersion = buffer->readUInt();
|
||||
buffer->readRect(&info.fCullRect);
|
||||
info.fFlags = buffer->readUInt();
|
||||
|
||||
if (!IsValidPictInfo(info)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pInfo != NULL) {
|
||||
*pInfo = info;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SkPicture* SkPicture::Forwardport(const SkPictInfo& info, const SkPictureData* data) {
|
||||
if (!data) {
|
||||
return NULL;
|
||||
}
|
||||
SkPicturePlayback playback(data);
|
||||
SkPictureRecorder r;
|
||||
playback.draw(r.beginRecording(SkScalarCeilToInt(info.fCullRect.width()),
|
||||
SkScalarCeilToInt(info.fCullRect.height())),
|
||||
NULL/*no callback*/);
|
||||
return r.endRecording();
|
||||
}
|
||||
|
||||
SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
|
||||
SkPictInfo info;
|
||||
if (!InternalOnly_StreamIsSKP(stream, &info) || !stream->readBool()) {
|
||||
return NULL;
|
||||
}
|
||||
SkAutoTDelete<SkPictureData> data(SkPictureData::CreateFromStream(stream, info, proc));
|
||||
return Forwardport(info, data);
|
||||
}
|
||||
|
||||
SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) {
|
||||
SkPictInfo info;
|
||||
if (!InternalOnly_BufferIsSKP(&buffer, &info) || !buffer.readBool()) {
|
||||
return NULL;
|
||||
}
|
||||
SkAutoTDelete<SkPictureData> data(SkPictureData::CreateFromBuffer(buffer, info));
|
||||
return Forwardport(info, data);
|
||||
}
|
||||
|
||||
void SkPicture::createHeader(SkPictInfo* info) const {
|
||||
// Copy magic bytes at the beginning of the header
|
||||
SkASSERT(sizeof(kMagic) == 8);
|
||||
SkASSERT(sizeof(kMagic) == sizeof(info->fMagic));
|
||||
memcpy(info->fMagic, kMagic, sizeof(kMagic));
|
||||
|
||||
// Set picture info after magic bytes in the header
|
||||
info->fVersion = CURRENT_PICTURE_VERSION;
|
||||
info->fCullRect = this->cullRect();
|
||||
info->fFlags = SkPictInfo::kCrossProcess_Flag;
|
||||
// TODO: remove this flag, since we're always float (now)
|
||||
info->fFlags |= SkPictInfo::kScalarIsFloat_Flag;
|
||||
|
||||
if (8 == sizeof(void*)) {
|
||||
info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
|
||||
}
|
||||
}
|
||||
|
||||
// This for compatibility with serialization code only. This is not cheap.
|
||||
SkPictureData* SkPicture::Backport(const SkRecord& src, const SkPictInfo& info,
|
||||
SkPicture const* const drawablePicts[], int drawableCount) {
|
||||
SkPictureRecord rec(SkISize::Make(info.fCullRect.width(), info.fCullRect.height()), 0/*flags*/);
|
||||
rec.beginRecording();
|
||||
SkRecordDraw(src, &rec, drawablePicts, NULL, drawableCount, NULL/*bbh*/, NULL/*callback*/);
|
||||
rec.endRecording();
|
||||
return SkNEW_ARGS(SkPictureData, (rec, info, false/*deep copy ops?*/));
|
||||
}
|
||||
|
||||
void SkPicture::serialize(SkWStream* stream, SkPixelSerializer* pixelSerializer) const {
|
||||
SkPictInfo info;
|
||||
this->createHeader(&info);
|
||||
SkAutoTDelete<SkPictureData> data(Backport(*fRecord, info, this->drawablePicts(),
|
||||
this->drawableCount()));
|
||||
|
||||
stream->write(&info, sizeof(info));
|
||||
if (data) {
|
||||
stream->writeBool(true);
|
||||
data->serialize(stream, pixelSerializer);
|
||||
} else {
|
||||
stream->writeBool(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SkPicture::flatten(SkWriteBuffer& buffer) const {
|
||||
SkPictInfo info;
|
||||
this->createHeader(&info);
|
||||
SkAutoTDelete<SkPictureData> data(Backport(*fRecord, info, this->drawablePicts(),
|
||||
this->drawableCount()));
|
||||
|
||||
buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic));
|
||||
buffer.writeUInt(info.fVersion);
|
||||
buffer.writeRect(info.fCullRect);
|
||||
buffer.writeUInt(info.fFlags);
|
||||
if (data) {
|
||||
buffer.writeBool(true);
|
||||
data->flatten(buffer);
|
||||
} else {
|
||||
buffer.writeBool(false);
|
||||
}
|
||||
}
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
bool SkPicture::suitableForGpuRasterization(GrContext*, const char **reason) const {
|
||||
return fAnalysis.suitableForGpuRasterization(reason, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool SkPicture::hasText() const { return fAnalysis.fHasText; }
|
||||
bool SkPicture::willPlayBackBitmaps() const { return fAnalysis.fWillPlaybackBitmaps; }
|
||||
int SkPicture::approximateOpCount() const { return fRecord->count(); }
|
||||
|
||||
SkPicture::SkPicture(const SkRect& cullRect,
|
||||
SkRecord* record,
|
||||
SnapshotArray* drawablePicts,
|
||||
SkBBoxHierarchy* bbh,
|
||||
AccelData* accelData,
|
||||
size_t approxBytesUsedBySubPictures)
|
||||
: fUniqueID(0)
|
||||
, fCullRect(cullRect)
|
||||
, fRecord(record) // Take ownership of caller's ref.
|
||||
, fDrawablePicts(drawablePicts) // Take ownership.
|
||||
, fBBH(bbh) // Take ownership of caller's ref.
|
||||
, fAccelData(accelData) // Take ownership of caller's ref.
|
||||
, fApproxBytesUsedBySubPictures(approxBytesUsedBySubPictures)
|
||||
, fAnalysis(*fRecord)
|
||||
{}
|
||||
|
||||
|
||||
static uint32_t gNextID = 1;
|
||||
uint32_t SkPicture::uniqueID() const {
|
||||
static uint32_t gNextID = 1;
|
||||
uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed);
|
||||
while (id == 0) {
|
||||
uint32_t next = sk_atomic_fetch_add(&gNextID, 1u);
|
||||
@ -480,3 +45,146 @@ uint32_t SkPicture::uniqueID() const {
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' };
|
||||
|
||||
SkPictInfo SkPicture::createHeader() const {
|
||||
SkPictInfo info;
|
||||
// Copy magic bytes at the beginning of the header
|
||||
static_assert(sizeof(kMagic) == 8, "");
|
||||
static_assert(sizeof(kMagic) == sizeof(info.fMagic), "");
|
||||
memcpy(info.fMagic, kMagic, sizeof(kMagic));
|
||||
|
||||
// Set picture info after magic bytes in the header
|
||||
info.fVersion = CURRENT_PICTURE_VERSION;
|
||||
info.fCullRect = this->cullRect();
|
||||
info.fFlags = SkPictInfo::kCrossProcess_Flag;
|
||||
// TODO: remove this flag, since we're always float (now)
|
||||
info.fFlags |= SkPictInfo::kScalarIsFloat_Flag;
|
||||
|
||||
if (8 == sizeof(void*)) {
|
||||
info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
bool SkPicture::IsValidPictInfo(const SkPictInfo& info) {
|
||||
if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) {
|
||||
return false;
|
||||
}
|
||||
if (info.fVersion < MIN_PICTURE_VERSION || info.fVersion > CURRENT_PICTURE_VERSION) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) {
|
||||
if (!stream) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SkPictInfo info;
|
||||
SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
|
||||
if (!stream->read(&info.fMagic, sizeof(kMagic))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
info.fVersion = stream->readU32();
|
||||
info.fCullRect.fLeft = stream->readScalar();
|
||||
info.fCullRect.fTop = stream->readScalar();
|
||||
info.fCullRect.fRight = stream->readScalar();
|
||||
info.fCullRect.fBottom = stream->readScalar();
|
||||
info.fFlags = stream->readU32();
|
||||
|
||||
if (IsValidPictInfo(info)) {
|
||||
if (pInfo) { *pInfo = info; }
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) {
|
||||
SkPictInfo info;
|
||||
SkASSERT(sizeof(kMagic) == sizeof(info.fMagic));
|
||||
if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
info.fVersion = buffer->readUInt();
|
||||
buffer->readRect(&info.fCullRect);
|
||||
info.fFlags = buffer->readUInt();
|
||||
|
||||
if (IsValidPictInfo(info)) {
|
||||
if (pInfo) { *pInfo = info; }
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
SkPicture* SkPicture::Forwardport(const SkPictInfo& info, const SkPictureData* data) {
|
||||
if (!data) {
|
||||
return nullptr;
|
||||
}
|
||||
SkPicturePlayback playback(data);
|
||||
SkPictureRecorder r;
|
||||
playback.draw(r.beginRecording(SkScalarCeilToInt(info.fCullRect.width()),
|
||||
SkScalarCeilToInt(info.fCullRect.height())),
|
||||
nullptr/*no callback*/);
|
||||
return r.endRecording();
|
||||
}
|
||||
|
||||
SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) {
|
||||
SkPictInfo info;
|
||||
if (!InternalOnly_StreamIsSKP(stream, &info) || !stream->readBool()) {
|
||||
return nullptr;
|
||||
}
|
||||
SkAutoTDelete<SkPictureData> data(SkPictureData::CreateFromStream(stream, info, proc));
|
||||
return Forwardport(info, data);
|
||||
}
|
||||
|
||||
SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) {
|
||||
SkPictInfo info;
|
||||
if (!InternalOnly_BufferIsSKP(&buffer, &info) || !buffer.readBool()) {
|
||||
return nullptr;
|
||||
}
|
||||
SkAutoTDelete<SkPictureData> data(SkPictureData::CreateFromBuffer(buffer, info));
|
||||
return Forwardport(info, data);
|
||||
}
|
||||
|
||||
SkPictureData* SkPicture::backport() const {
|
||||
SkPictInfo info = this->createHeader();
|
||||
SkPictureRecord rec(SkISize::Make(info.fCullRect.width(), info.fCullRect.height()), 0/*flags*/);
|
||||
rec.beginRecording();
|
||||
this->playback(&rec);
|
||||
rec.endRecording();
|
||||
return SkNEW_ARGS(SkPictureData, (rec, info, false/*deep copy ops?*/));
|
||||
}
|
||||
|
||||
void SkPicture::serialize(SkWStream* stream, SkPixelSerializer* pixelSerializer) const {
|
||||
SkPictInfo info = this->createHeader();
|
||||
SkAutoTDelete<SkPictureData> data(this->backport());
|
||||
|
||||
stream->write(&info, sizeof(info));
|
||||
if (data) {
|
||||
stream->writeBool(true);
|
||||
data->serialize(stream, pixelSerializer);
|
||||
} else {
|
||||
stream->writeBool(false);
|
||||
}
|
||||
}
|
||||
|
||||
void SkPicture::flatten(SkWriteBuffer& buffer) const {
|
||||
SkPictInfo info = this->createHeader();
|
||||
SkAutoTDelete<SkPictureData> data(this->backport());
|
||||
|
||||
buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic));
|
||||
buffer.writeUInt(info.fVersion);
|
||||
buffer.writeRect(info.fCullRect);
|
||||
buffer.writeUInt(info.fFlags);
|
||||
if (data) {
|
||||
buffer.writeBool(true);
|
||||
data->flatten(buffer);
|
||||
} else {
|
||||
buffer.writeBool(false);
|
||||
}
|
||||
}
|
||||
|
70
src/core/SkPictureCommon.h
Normal file
70
src/core/SkPictureCommon.h
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2015 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
// Some shared code used by both SkBigPicture and SkMiniPicture.
|
||||
|
||||
#include "SkRecords.h"
|
||||
#include "SkTLogic.h"
|
||||
|
||||
struct SkTextHunter {
|
||||
// Most ops never have text. Some always do. Subpictures know themeselves.
|
||||
template <typename T> bool operator()(const T&) { return false; }
|
||||
bool operator()(const SkRecords::DrawPosText&) { return true; }
|
||||
bool operator()(const SkRecords::DrawPosTextH&) { return true; }
|
||||
bool operator()(const SkRecords::DrawText&) { return true; }
|
||||
bool operator()(const SkRecords::DrawTextBlob&) { return true; }
|
||||
bool operator()(const SkRecords::DrawTextOnPath&) { return true; }
|
||||
bool operator()(const SkRecords::DrawPicture& op) { return op.picture->hasText(); }
|
||||
};
|
||||
|
||||
|
||||
struct SkBitmapHunter {
|
||||
// Helpers. These create HasMember_bitmap and HasMember_paint.
|
||||
SK_CREATE_MEMBER_DETECTOR(bitmap);
|
||||
SK_CREATE_MEMBER_DETECTOR(paint);
|
||||
|
||||
// Some ops have a paint, some have an optional paint. Either way, get back a pointer.
|
||||
static const SkPaint* AsPtr(const SkPaint& p) { return &p; }
|
||||
static const SkPaint* AsPtr(const SkRecords::Optional<SkPaint>& p) { return p; }
|
||||
|
||||
// Main entry for visitor:
|
||||
// If the op is a DrawPicture, recurse.
|
||||
// If the op has a bitmap directly, return true.
|
||||
// If the op has a paint and the paint has a bitmap, return true.
|
||||
// Otherwise, return false.
|
||||
bool operator()(const SkRecords::DrawPicture& op) { return op.picture->willPlayBackBitmaps(); }
|
||||
|
||||
template <typename T>
|
||||
bool operator()(const T& r) { return CheckBitmap(r); }
|
||||
|
||||
// If the op has a bitmap, of course we're going to play back bitmaps.
|
||||
template <typename T>
|
||||
static SK_WHEN(HasMember_bitmap<T>, bool) CheckBitmap(const T&) { return true; }
|
||||
|
||||
// If not, look for one in its paint (if it has a paint).
|
||||
template <typename T>
|
||||
static SK_WHEN(!HasMember_bitmap<T>, bool) CheckBitmap(const T& r) { return CheckPaint(r); }
|
||||
|
||||
// If we have a paint, dig down into the effects looking for a bitmap.
|
||||
template <typename T>
|
||||
static SK_WHEN(HasMember_paint<T>, bool) CheckPaint(const T& r) {
|
||||
const SkPaint* paint = AsPtr(r.paint);
|
||||
if (paint) {
|
||||
const SkShader* shader = paint->getShader();
|
||||
if (shader &&
|
||||
shader->asABitmap(nullptr, nullptr, nullptr) == SkShader::kDefault_BitmapType) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we don't have a paint, that non-paint has no bitmap.
|
||||
template <typename T>
|
||||
static SK_WHEN(!HasMember_paint<T>, bool) CheckPaint(const T&) { return false; }
|
||||
};
|
||||
|
@ -5,6 +5,7 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBigPicture.h"
|
||||
#include "SkData.h"
|
||||
#include "SkDrawable.h"
|
||||
#include "SkLayerInfo.h"
|
||||
@ -18,7 +19,7 @@
|
||||
|
||||
SkPictureRecorder::SkPictureRecorder() {
|
||||
fActivelyRecording = false;
|
||||
fRecorder.reset(SkNEW_ARGS(SkRecorder, (nullptr, SkRect::MakeWH(0,0))));
|
||||
fRecorder.reset(SkNEW_ARGS(SkRecorder, (nullptr, SkRect::MakeWH(0,0), &fMiniRecorder)));
|
||||
}
|
||||
|
||||
SkPictureRecorder::~SkPictureRecorder() {}
|
||||
@ -34,8 +35,10 @@ SkCanvas* SkPictureRecorder::beginRecording(const SkRect& cullRect,
|
||||
SkASSERT(fBBH.get());
|
||||
}
|
||||
|
||||
fRecord.reset(SkNEW(SkRecord));
|
||||
fRecorder->reset(fRecord.get(), cullRect);
|
||||
if (!fRecord) {
|
||||
fRecord.reset(SkNEW(SkRecord));
|
||||
}
|
||||
fRecorder->reset(fRecord.get(), cullRect, &fMiniRecorder);
|
||||
fActivelyRecording = true;
|
||||
return this->getRecordingCanvas();
|
||||
}
|
||||
@ -47,19 +50,23 @@ SkCanvas* SkPictureRecorder::getRecordingCanvas() {
|
||||
SkPicture* SkPictureRecorder::endRecordingAsPicture() {
|
||||
fActivelyRecording = false;
|
||||
fRecorder->restoreToCount(1); // If we were missing any restores, add them now.
|
||||
|
||||
if (fRecord->count() == 0) {
|
||||
return fMiniRecorder.detachAsPicture(fCullRect);
|
||||
}
|
||||
|
||||
// TODO: delay as much of this work until just before first playback?
|
||||
SkRecordOptimize(fRecord);
|
||||
|
||||
SkAutoTUnref<SkLayerInfo> saveLayerData;
|
||||
|
||||
if (fBBH && (fFlags & kComputeSaveLayerInfo_RecordFlag)) {
|
||||
SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey();
|
||||
|
||||
saveLayerData.reset(SkNEW_ARGS(SkLayerInfo, (key)));
|
||||
saveLayerData.reset(SkNEW(SkLayerInfo));
|
||||
}
|
||||
|
||||
SkDrawableList* drawableList = fRecorder->getDrawableList();
|
||||
SkPicture::SnapshotArray* pictList = drawableList ? drawableList->newDrawableSnapshot() : NULL;
|
||||
SkBigPicture::SnapshotArray* pictList =
|
||||
drawableList ? drawableList->newDrawableSnapshot() : NULL;
|
||||
|
||||
if (fBBH.get()) {
|
||||
if (saveLayerData) {
|
||||
@ -77,12 +84,12 @@ SkPicture* SkPictureRecorder::endRecordingAsPicture() {
|
||||
for (int i = 0; pictList && i < pictList->count(); i++) {
|
||||
subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]);
|
||||
}
|
||||
return SkNEW_ARGS(SkPicture, (fCullRect,
|
||||
fRecord.detach(),
|
||||
pictList,
|
||||
fBBH.detach(),
|
||||
saveLayerData.detach(),
|
||||
subPictureBytes));
|
||||
return SkNEW_ARGS(SkBigPicture, (fCullRect,
|
||||
fRecord.detach(),
|
||||
pictList,
|
||||
fBBH.detach(),
|
||||
saveLayerData.detach(),
|
||||
subPictureBytes));
|
||||
}
|
||||
|
||||
void SkPictureRecorder::partialReplay(SkCanvas* canvas) const {
|
||||
@ -133,7 +140,7 @@ protected:
|
||||
}
|
||||
|
||||
SkPicture* onNewPictureSnapshot() override {
|
||||
SkPicture::SnapshotArray* pictList = NULL;
|
||||
SkBigPicture::SnapshotArray* pictList = NULL;
|
||||
if (fDrawableList) {
|
||||
// TODO: should we plumb-down the BBHFactory and recordFlags from our host
|
||||
// PictureRecorder?
|
||||
@ -143,9 +150,7 @@ protected:
|
||||
SkAutoTUnref<SkLayerInfo> saveLayerData;
|
||||
|
||||
if (fBBH && fDoSaveLayerInfo) {
|
||||
SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey();
|
||||
|
||||
saveLayerData.reset(SkNEW_ARGS(SkLayerInfo, (key)));
|
||||
saveLayerData.reset(SkNEW(SkLayerInfo));
|
||||
|
||||
SkBBoxHierarchy* bbh = NULL; // we've already computed fBBH (received in constructor)
|
||||
// TODO: update saveLayer info computation to reuse the already computed
|
||||
@ -157,20 +162,22 @@ protected:
|
||||
for (int i = 0; pictList && i < pictList->count(); i++) {
|
||||
subPictureBytes += SkPictureUtils::ApproximateBytesUsed(pictList->begin()[i]);
|
||||
}
|
||||
// SkPicture will take ownership of a ref on both fRecord and fBBH.
|
||||
// SkBigPicture will take ownership of a ref on both fRecord and fBBH.
|
||||
// We're not willing to give up our ownership, so we must ref them for SkPicture.
|
||||
return SkNEW_ARGS(SkPicture, (fBounds,
|
||||
SkRef(fRecord.get()),
|
||||
pictList,
|
||||
SkSafeRef(fBBH.get()),
|
||||
saveLayerData.detach(),
|
||||
subPictureBytes));
|
||||
return SkNEW_ARGS(SkBigPicture, (fBounds,
|
||||
SkRef(fRecord.get()),
|
||||
pictList,
|
||||
SkSafeRef(fBBH.get()),
|
||||
saveLayerData.detach(),
|
||||
subPictureBytes));
|
||||
}
|
||||
};
|
||||
|
||||
SkDrawable* SkPictureRecorder::endRecordingAsDrawable() {
|
||||
fActivelyRecording = false;
|
||||
fRecorder->flushMiniRecorder();
|
||||
fRecorder->restoreToCount(1); // If we were missing any restores, add them now.
|
||||
|
||||
// TODO: delay as much of this work until just before first playback?
|
||||
SkRecordOptimize(fRecord);
|
||||
|
||||
|
@ -587,7 +587,7 @@ private:
|
||||
class CollectLayers : SkNoncopyable {
|
||||
public:
|
||||
CollectLayers(const SkRect& cullRect, const SkRecord& record,
|
||||
const SkPicture::SnapshotArray* pictList, SkLayerInfo* accelData)
|
||||
const SkBigPicture::SnapshotArray* pictList, SkLayerInfo* accelData)
|
||||
: fSaveLayersInStack(0)
|
||||
, fAccelData(accelData)
|
||||
, fPictList(pictList)
|
||||
@ -640,10 +640,10 @@ private:
|
||||
void trackSaveLayersForPicture(const SkPicture* picture, const SkPaint* paint) {
|
||||
// For sub-pictures, we wrap their layer information within the parent
|
||||
// picture's rendering hierarchy
|
||||
SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey();
|
||||
|
||||
const SkLayerInfo* childData =
|
||||
static_cast<const SkLayerInfo*>(picture->EXPERIMENTAL_getAccelData(key));
|
||||
const SkLayerInfo* childData = NULL;
|
||||
if (const SkBigPicture* bp = picture->asSkBigPicture()) {
|
||||
childData = static_cast<const SkLayerInfo*>(bp->accelData());
|
||||
}
|
||||
if (!childData) {
|
||||
// If the child layer hasn't been generated with saveLayer data we
|
||||
// assume the worst (i.e., that it does contain layers which nest
|
||||
@ -774,7 +774,7 @@ private:
|
||||
// The op code indices of all the currently active saveLayers
|
||||
SkTDArray<unsigned> fSaveLayerOpStack;
|
||||
SkLayerInfo* fAccelData;
|
||||
const SkPicture::SnapshotArray* fPictList;
|
||||
const SkBigPicture::SnapshotArray* fPictList;
|
||||
|
||||
SkRecords::FillBounds fFillBounds;
|
||||
};
|
||||
@ -793,7 +793,7 @@ void SkRecordFillBounds(const SkRect& cullRect, const SkRecord& record, SkBBoxHi
|
||||
}
|
||||
|
||||
void SkRecordComputeLayers(const SkRect& cullRect, const SkRecord& record,
|
||||
const SkPicture::SnapshotArray* pictList, SkBBoxHierarchy* bbh,
|
||||
const SkBigPicture::SnapshotArray* pictList, SkBBoxHierarchy* bbh,
|
||||
SkLayerInfo* data) {
|
||||
SkRecords::CollectLayers visitor(cullRect, record, pictList, data);
|
||||
|
||||
|
@ -9,6 +9,7 @@
|
||||
#define SkRecordDraw_DEFINED
|
||||
|
||||
#include "SkBBoxHierarchy.h"
|
||||
#include "SkBigPicture.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkMatrix.h"
|
||||
#include "SkRecord.h"
|
||||
@ -20,7 +21,7 @@ class SkLayerInfo;
|
||||
void SkRecordFillBounds(const SkRect& cullRect, const SkRecord&, SkBBoxHierarchy*);
|
||||
|
||||
void SkRecordComputeLayers(const SkRect& cullRect, const SkRecord& record,
|
||||
const SkPicture::SnapshotArray*,
|
||||
const SkBigPicture::SnapshotArray*,
|
||||
SkBBoxHierarchy* bbh, SkLayerInfo* data);
|
||||
|
||||
// Draw an SkRecord into an SkCanvas. A convenience wrapper around SkRecords::Draw.
|
||||
|
@ -5,6 +5,7 @@
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBigPicture.h"
|
||||
#include "SkPatchUtils.h"
|
||||
#include "SkPicture.h"
|
||||
#include "SkPictureUtils.h"
|
||||
@ -14,7 +15,7 @@ SkDrawableList::~SkDrawableList() {
|
||||
fArray.unrefAll();
|
||||
}
|
||||
|
||||
SkPicture::SnapshotArray* SkDrawableList::newDrawableSnapshot() {
|
||||
SkBigPicture::SnapshotArray* SkDrawableList::newDrawableSnapshot() {
|
||||
const int count = fArray.count();
|
||||
if (0 == count) {
|
||||
return NULL;
|
||||
@ -23,7 +24,7 @@ SkPicture::SnapshotArray* SkDrawableList::newDrawableSnapshot() {
|
||||
for (int i = 0; i < count; ++i) {
|
||||
pics[i] = fArray[i]->newPictureSnapshot();
|
||||
}
|
||||
return SkNEW_ARGS(SkPicture::SnapshotArray, (pics.detach(), count));
|
||||
return SkNEW_ARGS(SkBigPicture::SnapshotArray, (pics.detach(), count));
|
||||
}
|
||||
|
||||
void SkDrawableList::append(SkDrawable* drawable) {
|
||||
@ -32,20 +33,23 @@ void SkDrawableList::append(SkDrawable* drawable) {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
SkRecorder::SkRecorder(SkRecord* record, int width, int height)
|
||||
SkRecorder::SkRecorder(SkRecord* record, int width, int height, SkMiniRecorder* mr)
|
||||
: SkCanvas(SkIRect::MakeWH(width, height), SkCanvas::kConservativeRasterClip_InitFlag)
|
||||
, fApproxBytesUsedBySubPictures(0)
|
||||
, fRecord(record) {}
|
||||
, fRecord(record)
|
||||
, fMiniRecorder(mr) {}
|
||||
|
||||
SkRecorder::SkRecorder(SkRecord* record, const SkRect& bounds)
|
||||
SkRecorder::SkRecorder(SkRecord* record, const SkRect& bounds, SkMiniRecorder* mr)
|
||||
: SkCanvas(bounds.roundOut(), SkCanvas::kConservativeRasterClip_InitFlag)
|
||||
, fApproxBytesUsedBySubPictures(0)
|
||||
, fRecord(record) {}
|
||||
, fRecord(record)
|
||||
, fMiniRecorder(mr) {}
|
||||
|
||||
void SkRecorder::reset(SkRecord* record, const SkRect& bounds) {
|
||||
void SkRecorder::reset(SkRecord* record, const SkRect& bounds, SkMiniRecorder* mr) {
|
||||
this->forgetRecord();
|
||||
fRecord = record;
|
||||
this->resetForNextPicture(bounds.roundOut());
|
||||
fMiniRecorder = mr;
|
||||
}
|
||||
|
||||
void SkRecorder::forgetRecord() {
|
||||
@ -55,9 +59,13 @@ void SkRecorder::forgetRecord() {
|
||||
}
|
||||
|
||||
// To make appending to fRecord a little less verbose.
|
||||
#define APPEND(T, ...) \
|
||||
#define APPEND(T, ...) \
|
||||
if (fMiniRecorder) { this->flushMiniRecorder(); } \
|
||||
SkNEW_PLACEMENT_ARGS(fRecord->append<SkRecords::T>(), SkRecords::T, (__VA_ARGS__))
|
||||
|
||||
#define TRY_MINIRECORDER(method, ...) \
|
||||
if (fMiniRecorder && fMiniRecorder->method(__VA_ARGS__)) { return; }
|
||||
|
||||
// For methods which must call back into SkCanvas.
|
||||
#define INHERITED(method, ...) this->SkCanvas::method(__VA_ARGS__)
|
||||
|
||||
@ -125,6 +133,15 @@ char* SkRecorder::copy(const char* src) {
|
||||
return this->copy(src, strlen(src)+1);
|
||||
}
|
||||
|
||||
void SkRecorder::flushMiniRecorder() {
|
||||
if (fMiniRecorder) {
|
||||
SkMiniRecorder* mr = fMiniRecorder;
|
||||
fMiniRecorder = nullptr; // Needs to happen before p->playback(this) or we loop forever.
|
||||
// TODO: this can probably be done more efficiently by SkMiniRecorder if it matters.
|
||||
SkAutoTUnref<SkPicture> p(mr->detachAsPicture(SkRect::MakeEmpty()));
|
||||
p->playback(this);
|
||||
}
|
||||
}
|
||||
|
||||
void SkRecorder::onDrawPaint(const SkPaint& paint) {
|
||||
APPEND(DrawPaint, delay_copy(paint));
|
||||
@ -138,6 +155,7 @@ void SkRecorder::onDrawPoints(PointMode mode,
|
||||
}
|
||||
|
||||
void SkRecorder::onDrawRect(const SkRect& rect, const SkPaint& paint) {
|
||||
TRY_MINIRECORDER(drawRect, rect, paint);
|
||||
APPEND(DrawRect, delay_copy(paint), rect);
|
||||
}
|
||||
|
||||
@ -162,6 +180,7 @@ void SkRecorder::onDrawDrawable(SkDrawable* drawable) {
|
||||
}
|
||||
|
||||
void SkRecorder::onDrawPath(const SkPath& path, const SkPaint& paint) {
|
||||
TRY_MINIRECORDER(drawPath, path, paint);
|
||||
APPEND(DrawPath, delay_copy(paint), delay_copy(path));
|
||||
}
|
||||
|
||||
@ -248,6 +267,7 @@ void SkRecorder::onDrawTextOnPath(const void* text, size_t byteLength, const SkP
|
||||
|
||||
void SkRecorder::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y,
|
||||
const SkPaint& paint) {
|
||||
TRY_MINIRECORDER(drawTextBlob, blob, x, y, paint);
|
||||
APPEND(DrawTextBlob, delay_copy(paint), blob, x, y);
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,9 @@
|
||||
#ifndef SkRecorder_DEFINED
|
||||
#define SkRecorder_DEFINED
|
||||
|
||||
#include "SkBigPicture.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkMiniRecorder.h"
|
||||
#include "SkRecord.h"
|
||||
#include "SkRecords.h"
|
||||
#include "SkTDArray.h"
|
||||
@ -25,7 +27,7 @@ public:
|
||||
void append(SkDrawable* drawable);
|
||||
|
||||
// Return a new or ref'd array of pictures that were snapped from our drawables.
|
||||
SkPicture::SnapshotArray* newDrawableSnapshot();
|
||||
SkBigPicture::SnapshotArray* newDrawableSnapshot();
|
||||
|
||||
private:
|
||||
SkTDArray<SkDrawable*> fArray;
|
||||
@ -36,10 +38,10 @@ private:
|
||||
class SkRecorder : public SkCanvas {
|
||||
public:
|
||||
// Does not take ownership of the SkRecord.
|
||||
SkRecorder(SkRecord*, int width, int height); // legacy version
|
||||
SkRecorder(SkRecord*, const SkRect& bounds);
|
||||
SkRecorder(SkRecord*, int width, int height, SkMiniRecorder* = nullptr); // legacy version
|
||||
SkRecorder(SkRecord*, const SkRect& bounds, SkMiniRecorder* = nullptr);
|
||||
|
||||
void reset(SkRecord*, const SkRect& bounds);
|
||||
void reset(SkRecord*, const SkRect& bounds, SkMiniRecorder* = nullptr);
|
||||
|
||||
size_t approxBytesUsedBySubPictures() const { return fApproxBytesUsedBySubPictures; }
|
||||
|
||||
@ -120,6 +122,8 @@ public:
|
||||
|
||||
SkSurface* onNewSurface(const SkImageInfo&, const SkSurfaceProps&) override { return NULL; }
|
||||
|
||||
void flushMiniRecorder();
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
T* copy(const T*);
|
||||
@ -136,6 +140,8 @@ private:
|
||||
size_t fApproxBytesUsedBySubPictures;
|
||||
SkRecord* fRecord;
|
||||
SkAutoTDelete<SkDrawableList> fDrawableList;
|
||||
|
||||
SkMiniRecorder* fMiniRecorder;
|
||||
};
|
||||
|
||||
#endif//SkRecorder_DEFINED
|
||||
|
@ -81,6 +81,7 @@ struct T { \
|
||||
#define RECORD1(T, A, a) \
|
||||
struct T { \
|
||||
static const Type kType = T##_Type; \
|
||||
T() {} \
|
||||
template <typename Z> \
|
||||
T(Z a) : a(a) {} \
|
||||
A a; \
|
||||
@ -89,6 +90,7 @@ struct T { \
|
||||
#define RECORD2(T, A, a, B, b) \
|
||||
struct T { \
|
||||
static const Type kType = T##_Type; \
|
||||
T() {} \
|
||||
template <typename Z, typename Y> \
|
||||
T(Z a, Y b) : a(a), b(b) {} \
|
||||
A a; B b; \
|
||||
@ -97,6 +99,7 @@ struct T { \
|
||||
#define RECORD3(T, A, a, B, b, C, c) \
|
||||
struct T { \
|
||||
static const Type kType = T##_Type; \
|
||||
T() {} \
|
||||
template <typename Z, typename Y, typename X> \
|
||||
T(Z a, Y b, X c) : a(a), b(b), c(c) {} \
|
||||
A a; B b; C c; \
|
||||
@ -105,6 +108,7 @@ struct T { \
|
||||
#define RECORD4(T, A, a, B, b, C, c, D, d) \
|
||||
struct T { \
|
||||
static const Type kType = T##_Type; \
|
||||
T() {} \
|
||||
template <typename Z, typename Y, typename X, typename W> \
|
||||
T(Z a, Y b, X c, W d) : a(a), b(b), c(c), d(d) {} \
|
||||
A a; B b; C c; D d; \
|
||||
@ -113,6 +117,7 @@ struct T { \
|
||||
#define RECORD5(T, A, a, B, b, C, c, D, d, E, e) \
|
||||
struct T { \
|
||||
static const Type kType = T##_Type; \
|
||||
T() {} \
|
||||
template <typename Z, typename Y, typename X, typename W, typename V> \
|
||||
T(Z a, Y b, X c, W d, V e) : a(a), b(b), c(c), d(d), e(e) {} \
|
||||
A a; B b; C c; D d; E e; \
|
||||
@ -125,6 +130,7 @@ struct T { \
|
||||
template <typename T>
|
||||
class RefBox : SkNoncopyable {
|
||||
public:
|
||||
RefBox() {}
|
||||
RefBox(T* obj) : fObj(SkSafeRef(obj)) {}
|
||||
~RefBox() { SkSafeUnref(fObj); }
|
||||
|
||||
@ -138,6 +144,7 @@ private:
|
||||
template <typename T>
|
||||
class Optional : SkNoncopyable {
|
||||
public:
|
||||
Optional() : fPtr(nullptr) {}
|
||||
Optional(T* ptr) : fPtr(ptr) {}
|
||||
~Optional() { if (fPtr) fPtr->~T(); }
|
||||
|
||||
@ -167,6 +174,7 @@ private:
|
||||
template <typename T>
|
||||
class PODArray {
|
||||
public:
|
||||
PODArray() {}
|
||||
PODArray(T* ptr) : fPtr(ptr) {}
|
||||
// Default copy and assign.
|
||||
|
||||
@ -181,6 +189,7 @@ private:
|
||||
// Using this, we guarantee the immutability of all bitmaps we record.
|
||||
class ImmutableBitmap : SkNoncopyable {
|
||||
public:
|
||||
ImmutableBitmap() {}
|
||||
explicit ImmutableBitmap(const SkBitmap& bitmap) {
|
||||
if (bitmap.isImmutable()) {
|
||||
fBitmap = bitmap;
|
||||
@ -203,6 +212,7 @@ private:
|
||||
// SkPath::cheapComputeDirection() is similar.
|
||||
// Recording is a convenient time to cache these, or we can delay it to between record and playback.
|
||||
struct PreCachedPath : public SkPath {
|
||||
PreCachedPath() {}
|
||||
explicit PreCachedPath(const SkPath& path) : SkPath(path) {
|
||||
this->updateBoundsCache();
|
||||
SkPath::Direction junk;
|
||||
@ -213,6 +223,7 @@ struct PreCachedPath : public SkPath {
|
||||
// Like SkPath::getBounds(), SkMatrix::getType() isn't thread safe unless we precache it.
|
||||
// This may not cover all SkMatrices used by the picture (e.g. some could be hiding in a shader).
|
||||
struct TypedMatrix : public SkMatrix {
|
||||
TypedMatrix() {}
|
||||
explicit TypedMatrix(const SkMatrix& matrix) : SkMatrix(matrix) {
|
||||
(void)this->getType();
|
||||
}
|
||||
@ -227,6 +238,7 @@ RECORD3(SaveLayer, Optional<SkRect>, bounds, Optional<SkPaint>, paint, SkCanvas:
|
||||
RECORD1(SetMatrix, TypedMatrix, matrix);
|
||||
|
||||
struct RegionOpAndAA {
|
||||
RegionOpAndAA() {}
|
||||
RegionOpAndAA(SkRegion::Op op, bool aa) : op(op), aa(aa) {}
|
||||
SkRegion::Op op : 31; // This really only needs to be 3, but there's no win today to do so.
|
||||
unsigned aa : 1; // MSVC won't pack an enum with an bool, so we call this an unsigned.
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "GrLayerHoister.h"
|
||||
#include "GrRecordReplaceDraw.h"
|
||||
|
||||
#include "SkBigPicture.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkDeviceImageFilterProxy.h"
|
||||
#include "SkDeviceProperties.h"
|
||||
@ -21,7 +22,7 @@
|
||||
|
||||
// Create the layer information for the hoisted layer and secure the
|
||||
// required texture/render target resources.
|
||||
static void prepare_for_hoisting(GrLayerCache* layerCache,
|
||||
static void prepare_for_hoisting(GrLayerCache* layerCache,
|
||||
const SkPicture* topLevelPicture,
|
||||
const SkMatrix& initialMat,
|
||||
const SkLayerInfo::BlockInfo& info,
|
||||
@ -74,7 +75,7 @@ static void prepare_for_hoisting(GrLayerCache* layerCache,
|
||||
} else {
|
||||
hl = recycled->append();
|
||||
}
|
||||
|
||||
|
||||
layerCache->addUse(layer);
|
||||
hl->fLayer = layer;
|
||||
hl->fPicture = pict;
|
||||
@ -129,12 +130,12 @@ void GrLayerHoister::FindLayersToAtlas(GrContext* context,
|
||||
}
|
||||
|
||||
GrLayerCache* layerCache = context->getLayerCache();
|
||||
|
||||
layerCache->processDeletedPictures();
|
||||
|
||||
SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey();
|
||||
|
||||
const SkPicture::AccelData* topLevelData = topLevelPicture->EXPERIMENTAL_getAccelData(key);
|
||||
const SkBigPicture::AccelData* topLevelData = NULL;
|
||||
if (const SkBigPicture* bp = topLevelPicture->asSkBigPicture()) {
|
||||
topLevelData = bp->accelData();
|
||||
}
|
||||
if (!topLevelData) {
|
||||
return;
|
||||
}
|
||||
@ -189,9 +190,10 @@ void GrLayerHoister::FindLayersToHoist(GrContext* context,
|
||||
|
||||
layerCache->processDeletedPictures();
|
||||
|
||||
SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey();
|
||||
|
||||
const SkPicture::AccelData* topLevelData = topLevelPicture->EXPERIMENTAL_getAccelData(key);
|
||||
const SkBigPicture::AccelData* topLevelData = NULL;
|
||||
if (const SkBigPicture* bp = topLevelPicture->asSkBigPicture()) {
|
||||
topLevelData = bp->accelData();
|
||||
}
|
||||
if (!topLevelData) {
|
||||
return;
|
||||
}
|
||||
@ -239,7 +241,11 @@ 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 SkBigPicture* pict = atlased[i].fPicture->asSkBigPicture();
|
||||
if (!pict) {
|
||||
// TODO: can we assume / assert this?
|
||||
continue;
|
||||
}
|
||||
const SkIPoint offset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop);
|
||||
SkDEBUGCODE(const SkPaint* layerPaint = layer->paint();)
|
||||
|
||||
@ -265,10 +271,7 @@ void GrLayerHoister::DrawLayersToAtlas(GrContext* context,
|
||||
atlasCanvas->setMatrix(initialCTM);
|
||||
atlasCanvas->concat(atlased[i].fLocalMat);
|
||||
|
||||
SkRecordPartialDraw(*pict->fRecord.get(), atlasCanvas,
|
||||
pict->drawablePicts(), pict->drawableCount(),
|
||||
layer->start() + 1, layer->stop(), initialCTM);
|
||||
|
||||
pict->partialPlayback(atlasCanvas, layer->start() + 1, layer->stop(), initialCTM);
|
||||
atlasCanvas->restore();
|
||||
}
|
||||
|
||||
@ -328,7 +331,11 @@ void GrLayerHoister::FilterLayer(GrContext* context,
|
||||
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 SkBigPicture* pict = layers[i].fPicture->asSkBigPicture();
|
||||
if (!pict) {
|
||||
// TODO: can we assume / assert this?
|
||||
continue;
|
||||
}
|
||||
const SkIPoint offset = SkIPoint::Make(layer->srcIR().fLeft, layer->srcIR().fTop);
|
||||
|
||||
// Each non-atlased layer has its own GrTexture
|
||||
@ -353,10 +360,7 @@ void GrLayerHoister::DrawLayers(GrContext* context, const SkTDArray<GrHoistedLay
|
||||
layerCanvas->setMatrix(initialCTM);
|
||||
layerCanvas->concat(layers[i].fLocalMat);
|
||||
|
||||
SkRecordPartialDraw(*pict->fRecord.get(), layerCanvas,
|
||||
pict->drawablePicts(), pict->drawableCount(),
|
||||
layer->start()+1, layer->stop(), initialCTM);
|
||||
|
||||
pict->partialPlayback(layerCanvas, layer->start()+1, layer->stop(), initialCTM);
|
||||
layerCanvas->flush();
|
||||
|
||||
if (layer->filter()) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "GrContext.h"
|
||||
#include "GrLayerCache.h"
|
||||
#include "GrRecordReplaceDraw.h"
|
||||
#include "SkBigPicture.h"
|
||||
#include "SkCanvasPriv.h"
|
||||
#include "SkGrPixelRef.h"
|
||||
#include "SkImage.h"
|
||||
@ -45,7 +46,7 @@ static inline void draw_replacement_bitmap(GrCachedLayer* layer, SkCanvas* canva
|
||||
canvas->drawBitmapRectToRect(bm, &src, dst, layer->paint());
|
||||
canvas->restore();
|
||||
} else {
|
||||
canvas->drawSprite(bm,
|
||||
canvas->drawSprite(bm,
|
||||
layer->srcIR().fLeft + layer->offset().fX,
|
||||
layer->srcIR().fTop + layer->offset().fY,
|
||||
layer->paint());
|
||||
@ -59,7 +60,7 @@ public:
|
||||
ReplaceDraw(SkCanvas* canvas, GrLayerCache* layerCache,
|
||||
SkPicture const* const drawablePicts[], int drawableCount,
|
||||
const SkPicture* topLevelPicture,
|
||||
const SkPicture* picture,
|
||||
const SkBigPicture* picture,
|
||||
const SkMatrix& initialMatrix,
|
||||
SkPicture::AbortCallback* callback,
|
||||
const unsigned* opIndices, int numIndices)
|
||||
@ -76,8 +77,8 @@ public:
|
||||
}
|
||||
|
||||
int draw() {
|
||||
const SkBBoxHierarchy* bbh = fPicture->fBBH.get();
|
||||
const SkRecord* record = fPicture->fRecord.get();
|
||||
const SkBBoxHierarchy* bbh = fPicture->bbh();
|
||||
const SkRecord* record = fPicture->record();
|
||||
if (NULL == record) {
|
||||
return 0;
|
||||
}
|
||||
@ -135,13 +136,17 @@ public:
|
||||
|
||||
SkAutoCanvasMatrixPaint acmp(fCanvas, &dp.matrix, dp.paint, dp.picture->cullRect());
|
||||
|
||||
// Draw sub-pictures with the same replacement list but a different picture
|
||||
ReplaceDraw draw(fCanvas, fLayerCache,
|
||||
this->drawablePicts(), this->drawableCount(),
|
||||
fTopLevelPicture, dp.picture, fInitialMatrix, fCallback,
|
||||
fOpIndexStack.begin(), fOpIndexStack.count());
|
||||
|
||||
fNumReplaced += draw.draw();
|
||||
if (const SkBigPicture* bp = dp.picture->asSkBigPicture()) {
|
||||
// Draw sub-pictures with the same replacement list but a different picture
|
||||
ReplaceDraw draw(fCanvas, fLayerCache,
|
||||
this->drawablePicts(), this->drawableCount(),
|
||||
fTopLevelPicture, bp, fInitialMatrix, fCallback,
|
||||
fOpIndexStack.begin(), fOpIndexStack.count());
|
||||
fNumReplaced += draw.draw();
|
||||
} else {
|
||||
// TODO: can we assume / assert this doesn't happen?
|
||||
dp.picture->playback(fCanvas, fCallback);
|
||||
}
|
||||
|
||||
fOpIndexStack.pop();
|
||||
}
|
||||
@ -168,7 +173,7 @@ public:
|
||||
|
||||
draw_replacement_bitmap(layer, fCanvas);
|
||||
|
||||
if (fPicture->fBBH.get()) {
|
||||
if (fPicture->bbh()) {
|
||||
while (fOps[fIndex] < layer->stop()) {
|
||||
++fIndex;
|
||||
}
|
||||
@ -190,7 +195,7 @@ private:
|
||||
SkCanvas* fCanvas;
|
||||
GrLayerCache* fLayerCache;
|
||||
const SkPicture* fTopLevelPicture;
|
||||
const SkPicture* fPicture;
|
||||
const SkBigPicture* fPicture;
|
||||
const SkMatrix fInitialMatrix;
|
||||
SkPicture::AbortCallback* fCallback;
|
||||
|
||||
@ -211,9 +216,15 @@ int GrRecordReplaceDraw(const SkPicture* picture,
|
||||
SkPicture::AbortCallback* callback) {
|
||||
SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
|
||||
|
||||
// TODO: drawablePicts?
|
||||
ReplaceDraw draw(canvas, layerCache, NULL, 0,
|
||||
picture, picture,
|
||||
initialMatrix, callback, NULL, 0);
|
||||
return draw.draw();
|
||||
if (const SkBigPicture* bp = picture->asSkBigPicture()) {
|
||||
// TODO: drawablePicts?
|
||||
ReplaceDraw draw(canvas, layerCache, NULL, 0,
|
||||
bp, bp,
|
||||
initialMatrix, callback, NULL, 0);
|
||||
return draw.draw();
|
||||
} else {
|
||||
// TODO: can we assume / assert this doesn't happen?
|
||||
picture->playback(canvas, callback);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -2014,9 +2014,10 @@ bool SkGpuDevice::EXPERIMENTAL_drawPicture(SkCanvas* mainCanvas, const SkPicture
|
||||
return false;
|
||||
}
|
||||
|
||||
SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey();
|
||||
|
||||
const SkPicture::AccelData* data = mainPicture->EXPERIMENTAL_getAccelData(key);
|
||||
const SkBigPicture::AccelData* data = NULL;
|
||||
if (const SkBigPicture* bp = mainPicture->asSkBigPicture()) {
|
||||
data = bp->accelData();
|
||||
}
|
||||
if (!data) {
|
||||
return false;
|
||||
}
|
||||
|
@ -217,8 +217,6 @@ private:
|
||||
|
||||
bool drawDashLine(const SkPoint pts[2], const SkPaint& paint);
|
||||
|
||||
static SkPicture::AccelData::Key ComputeAccelDataKey();
|
||||
|
||||
static GrRenderTarget* CreateRenderTarget(GrContext*, SkSurface::Budgeted, const SkImageInfo&,
|
||||
int sampleCount);
|
||||
|
||||
|
@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright 2012 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "SkBBoxHierarchy.h"
|
||||
#include "SkCanvas.h"
|
||||
#include "SkData.h"
|
||||
#include "SkPictureUtils.h"
|
||||
#include "SkRecord.h"
|
||||
#include "SkShader.h"
|
||||
|
||||
size_t SkPictureUtils::ApproximateBytesUsed(const SkPicture* pict) {
|
||||
size_t byteCount = sizeof(*pict);
|
||||
|
||||
byteCount += pict->fRecord->bytesUsed();
|
||||
if (pict->fBBH.get()) {
|
||||
byteCount += pict->fBBH->bytesUsed();
|
||||
}
|
||||
byteCount += pict->fApproxBytesUsedBySubPictures;
|
||||
|
||||
return byteCount;
|
||||
}
|
@ -111,7 +111,10 @@ DEF_GPUTEST(GpuLayerCache, reporter, factory) {
|
||||
}
|
||||
|
||||
SkPictureRecorder recorder;
|
||||
recorder.beginRecording(1, 1);
|
||||
SkCanvas* c = recorder.beginRecording(1, 1);
|
||||
// Draw something, anything, to prevent an empty-picture optimization,
|
||||
// which is a singleton and never purged.
|
||||
c->drawRect(SkRect::MakeWH(1,1), SkPaint());
|
||||
SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
|
||||
|
||||
GrLayerCache cache(context);
|
||||
|
@ -363,9 +363,10 @@ static void test_savelayer_extraction(skiatest::Reporter* reporter) {
|
||||
|
||||
// Now test out the SaveLayer extraction
|
||||
if (!SkCanvas::Internal_Private_GetIgnoreSaveLayerBounds()) {
|
||||
SkPicture::AccelData::Key key = SkLayerInfo::ComputeKey();
|
||||
const SkBigPicture* bp = pict->asSkBigPicture();
|
||||
REPORTER_ASSERT(reporter, bp);
|
||||
|
||||
const SkPicture::AccelData* data = pict->EXPERIMENTAL_getAccelData(key);
|
||||
const SkBigPicture::AccelData* data = bp->accelData();
|
||||
REPORTER_ASSERT(reporter, data);
|
||||
|
||||
const SkLayerInfo *gpuData = static_cast<const SkLayerInfo*>(data);
|
||||
@ -1107,30 +1108,6 @@ static void test_gen_id(skiatest::Reporter* reporter) {
|
||||
REPORTER_ASSERT(reporter, hasData->uniqueID() != empty->uniqueID());
|
||||
}
|
||||
|
||||
static void test_bytes_used(skiatest::Reporter* reporter) {
|
||||
SkPictureRecorder recorder;
|
||||
|
||||
recorder.beginRecording(0, 0);
|
||||
SkAutoTUnref<SkPicture> empty(recorder.endRecording());
|
||||
|
||||
// Sanity check to make sure we aren't under-measuring.
|
||||
REPORTER_ASSERT(reporter, SkPictureUtils::ApproximateBytesUsed(empty.get()) >=
|
||||
sizeof(SkPicture) + sizeof(SkRecord));
|
||||
|
||||
// Protect against any unintentional bloat.
|
||||
size_t approxUsed = SkPictureUtils::ApproximateBytesUsed(empty.get());
|
||||
REPORTER_ASSERT(reporter, approxUsed <= 432);
|
||||
|
||||
// Sanity check of nested SkPictures.
|
||||
SkPictureRecorder r2;
|
||||
r2.beginRecording(0, 0);
|
||||
r2.getRecordingCanvas()->drawPicture(empty.get());
|
||||
SkAutoTUnref<SkPicture> nested(r2.endRecording());
|
||||
|
||||
REPORTER_ASSERT(reporter, SkPictureUtils::ApproximateBytesUsed(nested.get()) >=
|
||||
SkPictureUtils::ApproximateBytesUsed(empty.get()));
|
||||
}
|
||||
|
||||
DEF_TEST(Picture, reporter) {
|
||||
#ifdef SK_DEBUG
|
||||
test_deleting_empty_picture();
|
||||
@ -1151,7 +1128,6 @@ DEF_TEST(Picture, reporter) {
|
||||
test_hierarchical(reporter);
|
||||
test_gen_id(reporter);
|
||||
test_savelayer_extraction(reporter);
|
||||
test_bytes_used(reporter);
|
||||
}
|
||||
|
||||
static void draw_bitmaps(const SkBitmap bitmap, SkCanvas* canvas) {
|
||||
@ -1267,7 +1243,10 @@ DEF_TEST(Picture_SkipBBH, r) {
|
||||
SpoonFedBBHFactory factory(&bbh);
|
||||
|
||||
SkPictureRecorder recorder;
|
||||
recorder.beginRecording(bound, &factory);
|
||||
SkCanvas* c = recorder.beginRecording(bound, &factory);
|
||||
// Record a few ops so we don't hit a small- or empty- picture optimization.
|
||||
c->drawRect(bound, SkPaint());
|
||||
c->drawRect(bound, SkPaint());
|
||||
SkAutoTUnref<const SkPicture> picture(recorder.endRecording());
|
||||
|
||||
SkCanvas big(640, 480), small(300, 200);
|
||||
|
Loading…
Reference in New Issue
Block a user