2ee3c2ce64
My goal here was to add a single location where we could patch up the created PicturePlayback. Unfortunately, the complexity of the recording process (e.g., the BBH) makes this quite complex. I will investigate altering the behavior of SkPicturePlayback to account for a potentially unbalanced set of saves/saveLayers. R=reed@google.com Author: robertphillips@google.com Review URL: https://codereview.chromium.org/283333007 git-svn-id: http://skia.googlecode.com/svn/trunk@14773 2bbb7eff-a529-9590-31e7-b0007b416f81
528 lines
20 KiB
C++
528 lines
20 KiB
C++
|
|
/*
|
|
* Copyright 2007 The Android Open Source Project
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
|
|
#ifndef SkPicture_DEFINED
|
|
#define SkPicture_DEFINED
|
|
|
|
#include "SkBitmap.h"
|
|
#include "SkImageDecoder.h"
|
|
#include "SkRefCnt.h"
|
|
|
|
#if SK_SUPPORT_GPU
|
|
class GrContext;
|
|
#endif
|
|
|
|
class SkBBHFactory;
|
|
class SkBBoxHierarchy;
|
|
class SkCanvas;
|
|
class SkDrawPictureCallback;
|
|
class SkData;
|
|
class SkPathHeap;
|
|
class SkPicturePlayback;
|
|
class SkPictureRecord;
|
|
class SkStream;
|
|
class SkWStream;
|
|
|
|
struct SkPictInfo;
|
|
|
|
/** \class SkPicture
|
|
|
|
The SkPicture class records the drawing commands made to a canvas, to
|
|
be played back at a later time.
|
|
*/
|
|
class SK_API SkPicture : public SkRefCnt {
|
|
public:
|
|
SK_DECLARE_INST_COUNT(SkPicture)
|
|
|
|
// AccelData provides a base class for device-specific acceleration
|
|
// data. It is added to the picture via a call to a device's optimize
|
|
// method.
|
|
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;
|
|
|
|
typedef SkRefCnt INHERITED;
|
|
};
|
|
|
|
SkPicture();
|
|
/** Make a copy of the contents of src. If src records more drawing after
|
|
this call, those elements will not appear in this picture.
|
|
*/
|
|
SkPicture(const SkPicture& src);
|
|
|
|
/** PRIVATE / EXPERIMENTAL -- do not call */
|
|
void EXPERIMENTAL_addAccelData(const AccelData* data) {
|
|
SkRefCnt_SafeAssign(fAccelData, data);
|
|
}
|
|
/** PRIVATE / EXPERIMENTAL -- do not call */
|
|
const AccelData* EXPERIMENTAL_getAccelData(AccelData::Key key) const {
|
|
if (NULL != fAccelData && fAccelData->getKey() == key) {
|
|
return fAccelData;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Function signature defining a function that sets up an SkBitmap from encoded data. On
|
|
* success, the SkBitmap should have its Config, width, height, rowBytes and pixelref set.
|
|
* If the installed pixelref has decoded the data into pixels, then the src buffer need not be
|
|
* copied. If the pixelref defers the actual decode until its lockPixels() is called, then it
|
|
* must make a copy of the src buffer.
|
|
* @param src Encoded data.
|
|
* @param length Size of the encoded data, in bytes.
|
|
* @param dst SkBitmap to install the pixel ref on.
|
|
* @param bool Whether or not a pixel ref was successfully installed.
|
|
*/
|
|
typedef bool (*InstallPixelRefProc)(const void* src, size_t length, SkBitmap* dst);
|
|
|
|
/**
|
|
* Recreate a picture that was serialized into a stream.
|
|
* @param SkStream Serialized picture data.
|
|
* @param proc Function pointer for installing pixelrefs on SkBitmaps representing the
|
|
* encoded bitmap data from the stream.
|
|
* @return A new SkPicture representing the serialized data, or NULL if the stream is
|
|
* invalid.
|
|
*/
|
|
static SkPicture* CreateFromStream(SkStream*,
|
|
InstallPixelRefProc proc = &SkImageDecoder::DecodeMemory);
|
|
|
|
/**
|
|
* Recreate a picture that was serialized into a buffer. If the creation requires bitmap
|
|
* decoding, the decoder must be set on the SkReadBuffer parameter by calling
|
|
* SkReadBuffer::setBitmapDecoder() before calling SkPicture::CreateFromBuffer().
|
|
* @param SkReadBuffer Serialized picture data.
|
|
* @return A new SkPicture representing the serialized data, or NULL if the buffer is
|
|
* invalid.
|
|
*/
|
|
static SkPicture* CreateFromBuffer(SkReadBuffer&);
|
|
|
|
virtual ~SkPicture();
|
|
|
|
/**
|
|
* Swap the contents of the two pictures. Guaranteed to succeed.
|
|
*/
|
|
void swap(SkPicture& other);
|
|
|
|
/**
|
|
* Creates a thread-safe clone of the picture that is ready for playback.
|
|
*/
|
|
SkPicture* clone() const;
|
|
|
|
/**
|
|
* Creates multiple thread-safe clones of this picture that are ready for
|
|
* playback. The resulting clones are stored in the provided array of
|
|
* SkPictures.
|
|
*/
|
|
void clone(SkPicture* pictures, int count) const;
|
|
|
|
enum RecordingFlags {
|
|
/* This flag specifies that when clipPath() is called, the path will
|
|
be faithfully recorded, but the recording canvas' current clip will
|
|
only see the path's bounds. This speeds up the recording process
|
|
without compromising the fidelity of the playback. The only side-
|
|
effect for recording is that calling getTotalClip() or related
|
|
clip-query calls will reflect the path's bounds, not the actual
|
|
path.
|
|
*/
|
|
kUsePathBoundsForClip_RecordingFlag = 0x01
|
|
};
|
|
|
|
#ifndef SK_SUPPORT_DEPRECATED_RECORD_FLAGS
|
|
// TODO: once kOptimizeForClippedPlayback_RecordingFlag is hidden from
|
|
// all external consumers, SkPicture::createBBoxHierarchy can also be
|
|
// cleaned up.
|
|
private:
|
|
#endif
|
|
enum Deprecated_RecordingFlags {
|
|
/* This flag causes the picture to compute bounding boxes and build
|
|
up a spatial hierarchy (currently an R-Tree), plus a tree of Canvas'
|
|
usually stack-based clip/etc state. This requires an increase in
|
|
recording time (often ~2x; likely more for very complex pictures),
|
|
but allows us to perform much faster culling at playback time, and
|
|
completely avoid some unnecessary clips and other operations. This
|
|
is ideal for tiled rendering, or any other situation where you're
|
|
drawing a fraction of a large scene into a smaller viewport.
|
|
|
|
In most cases the record cost is offset by the playback improvement
|
|
after a frame or two of tiled rendering (and complex pictures that
|
|
induce the worst record times will generally get the largest
|
|
speedups at playback time).
|
|
|
|
Note: Currently this is not serializable, the bounding data will be
|
|
discarded if you serialize into a stream and then deserialize.
|
|
*/
|
|
kOptimizeForClippedPlayback_RecordingFlag = 0x02,
|
|
};
|
|
#ifndef SK_SUPPORT_DEPRECATED_RECORD_FLAGS
|
|
public:
|
|
#endif
|
|
|
|
#ifndef SK_SUPPORT_LEGACY_PICTURE_CAN_RECORD
|
|
private:
|
|
#endif
|
|
|
|
#ifdef SK_SUPPORT_LEGACY_DERIVED_PICTURE_CLASSES
|
|
|
|
/** Returns the canvas that records the drawing commands.
|
|
@param width the base width for the picture, as if the recording
|
|
canvas' bitmap had this width.
|
|
@param height the base width for the picture, as if the recording
|
|
canvas' bitmap had this height.
|
|
@param recordFlags optional flags that control recording.
|
|
@return the picture canvas.
|
|
*/
|
|
SkCanvas* beginRecording(int width, int height, uint32_t recordFlags = 0);
|
|
#endif
|
|
|
|
/** Returns the recording canvas if one is active, or NULL if recording is
|
|
not active. This does not alter the refcnt on the canvas (if present).
|
|
*/
|
|
SkCanvas* getRecordingCanvas() const;
|
|
/** Signal that the caller is done recording. This invalidates the canvas
|
|
returned by beginRecording/getRecordingCanvas, and prepares the picture
|
|
for drawing. Note: this happens implicitly the first time the picture
|
|
is drawn.
|
|
*/
|
|
void endRecording();
|
|
|
|
#ifndef SK_SUPPORT_LEGACY_PICTURE_CAN_RECORD
|
|
public:
|
|
#endif
|
|
|
|
/** Replays the drawing commands on the specified canvas. This internally
|
|
calls endRecording() if that has not already been called.
|
|
@param canvas the canvas receiving the drawing commands.
|
|
*/
|
|
void draw(SkCanvas* canvas, SkDrawPictureCallback* = NULL);
|
|
|
|
/** Return the width of the picture's recording canvas. This
|
|
value reflects what was passed to setSize(), and does not necessarily
|
|
reflect the bounds of what has been recorded into the picture.
|
|
@return the width of the picture's recording canvas
|
|
*/
|
|
int width() const { return fWidth; }
|
|
|
|
/** Return the height of the picture's recording canvas. This
|
|
value reflects what was passed to setSize(), and does not necessarily
|
|
reflect the bounds of what has been recorded into the picture.
|
|
@return the height of the picture's recording canvas
|
|
*/
|
|
int height() const { return fHeight; }
|
|
|
|
/** Return a non-zero, unique value representing the picture. This call is
|
|
only valid when not recording. Between a beginRecording/endRecording
|
|
pair it will just return 0 (the invalid ID). Each beginRecording/
|
|
endRecording pair will cause a different generation ID to be returned.
|
|
*/
|
|
uint32_t uniqueID() const;
|
|
|
|
/**
|
|
* Function to encode an SkBitmap to an SkData. A function with this
|
|
* signature can be passed to serialize() and SkWriteBuffer.
|
|
* Returning NULL will tell the SkWriteBuffer to use
|
|
* SkBitmap::flatten() to store the bitmap.
|
|
*
|
|
* @param pixelRefOffset DEPRECATED -- caller assumes it will return 0.
|
|
* @return SkData If non-NULL, holds encoded data representing the passed
|
|
* in bitmap. The caller is responsible for calling unref().
|
|
*/
|
|
typedef SkData* (*EncodeBitmap)(size_t* pixelRefOffset, const SkBitmap& bm);
|
|
|
|
/**
|
|
* Serialize to a stream. If non NULL, encoder will be used to encode
|
|
* any bitmaps in the picture.
|
|
* encoder will never be called with a NULL pixelRefOffset.
|
|
*/
|
|
void serialize(SkWStream*, EncodeBitmap encoder = NULL) const;
|
|
|
|
/**
|
|
* Serialize to a buffer.
|
|
*/
|
|
void flatten(SkWriteBuffer&) const;
|
|
|
|
/**
|
|
* Returns true if any bitmaps may be produced when this SkPicture
|
|
* is replayed.
|
|
* Returns false if called while still recording.
|
|
*/
|
|
bool willPlayBackBitmaps() const;
|
|
|
|
#ifdef SK_BUILD_FOR_ANDROID
|
|
/** Signals that the caller is prematurely done replaying the drawing
|
|
commands. This can be called from a canvas virtual while the picture
|
|
is drawing. Has no effect if the picture is not drawing.
|
|
@deprecated preserving for legacy purposes
|
|
*/
|
|
void abortPlayback();
|
|
#endif
|
|
|
|
/** Return true if the SkStream/Buffer represents a serialized picture, and
|
|
fills out SkPictInfo. After this function returns, the data source is not
|
|
rewound so it will have to be manually reset before passing to
|
|
CreateFromStream or CreateFromBuffer. Note, CreateFromStream and
|
|
CreateFromBuffer perform this check internally so these entry points are
|
|
intended for stand alone tools.
|
|
If false is returned, SkPictInfo is unmodified.
|
|
*/
|
|
static bool InternalOnly_StreamIsSKP(SkStream*, SkPictInfo*);
|
|
static bool InternalOnly_BufferIsSKP(SkReadBuffer&, SkPictInfo*);
|
|
|
|
/** Enable/disable all the picture recording optimizations (i.e.,
|
|
those in SkPictureRecord). It is mainly intended for testing the
|
|
existing optimizations (i.e., to actually have the pattern
|
|
appear in an .skp we have to disable the optimization). Call right
|
|
after 'beginRecording'.
|
|
*/
|
|
void internalOnly_EnableOpts(bool enableOpts);
|
|
|
|
/** Return true if the picture is suitable for rendering on the GPU.
|
|
*/
|
|
|
|
#if SK_SUPPORT_GPU
|
|
bool suitableForGpuRasterization(GrContext*) const;
|
|
#endif
|
|
|
|
protected:
|
|
// 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.
|
|
|
|
// 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 = 19;
|
|
static const uint32_t CURRENT_PICTURE_VERSION = 26;
|
|
|
|
mutable uint32_t fUniqueID;
|
|
|
|
// fPlayback, fRecord, fWidth & fHeight are protected to allow derived classes to
|
|
// install their own SkPicturePlayback-derived players,SkPictureRecord-derived
|
|
// recorders and set the picture size
|
|
SkPicturePlayback* fPlayback;
|
|
SkPictureRecord* fRecord;
|
|
int fWidth, fHeight;
|
|
const AccelData* fAccelData;
|
|
|
|
void needsNewGenID() { fUniqueID = SK_InvalidGenID; }
|
|
|
|
// Create a new SkPicture from an existing SkPicturePlayback. Ref count of
|
|
// playback is unchanged.
|
|
SkPicture(SkPicturePlayback*, int width, int height);
|
|
|
|
#ifdef SK_SUPPORT_LEGACY_DERIVED_PICTURE_CLASSES
|
|
// For testing. Derived classes may instantiate an alternate
|
|
// SkBBoxHierarchy implementation
|
|
virtual SkBBoxHierarchy* createBBoxHierarchy() const;
|
|
#endif
|
|
|
|
SkCanvas* beginRecording(int width, int height, SkBBHFactory* factory, uint32_t recordFlags);
|
|
|
|
private:
|
|
friend class SkPictureRecord;
|
|
friend class SkPictureTester; // for unit testing
|
|
|
|
SkAutoTUnref<SkPathHeap> fPathHeap; // reference counted
|
|
|
|
// ContentInfo is not serialized! It is intended solely for use
|
|
// with suitableForGpuRasterization.
|
|
class ContentInfo {
|
|
public:
|
|
ContentInfo() { this->reset(); }
|
|
|
|
ContentInfo(const ContentInfo& src) { this->set(src); }
|
|
|
|
void set(const ContentInfo& src) {
|
|
fNumPaintWithPathEffectUses = src.fNumPaintWithPathEffectUses;
|
|
fNumAAConcavePaths = src.fNumAAConcavePaths;
|
|
fNumAAHairlineConcavePaths = src.fNumAAHairlineConcavePaths;
|
|
}
|
|
|
|
void reset() {
|
|
fNumPaintWithPathEffectUses = 0;
|
|
fNumAAConcavePaths = 0;
|
|
fNumAAHairlineConcavePaths = 0;
|
|
}
|
|
|
|
void swap(ContentInfo* other) {
|
|
SkTSwap(fNumPaintWithPathEffectUses, other->fNumPaintWithPathEffectUses);
|
|
SkTSwap(fNumAAConcavePaths, other->fNumAAConcavePaths);
|
|
SkTSwap(fNumAAHairlineConcavePaths, other->fNumAAHairlineConcavePaths);
|
|
}
|
|
|
|
// This field is incremented every time a paint with a path effect is
|
|
// used (i.e., it is not a de-duplicated count)
|
|
int fNumPaintWithPathEffectUses;
|
|
// This field is incremented every time an anti-aliased drawPath call is
|
|
// issued with a concave path
|
|
int fNumAAConcavePaths;
|
|
// This field is incremented every time a drawPath call is
|
|
// issued for a hairline stroked concave path.
|
|
int fNumAAHairlineConcavePaths;
|
|
};
|
|
|
|
ContentInfo fContentInfo;
|
|
|
|
void incPaintWithPathEffectUses() {
|
|
++fContentInfo.fNumPaintWithPathEffectUses;
|
|
}
|
|
int numPaintWithPathEffectUses() const {
|
|
return fContentInfo.fNumPaintWithPathEffectUses;
|
|
}
|
|
|
|
void incAAConcavePaths() {
|
|
++fContentInfo.fNumAAConcavePaths;
|
|
}
|
|
int numAAConcavePaths() const {
|
|
return fContentInfo.fNumAAConcavePaths;
|
|
}
|
|
|
|
void incAAHairlineConcavePaths() {
|
|
++fContentInfo.fNumAAHairlineConcavePaths;
|
|
SkASSERT(fContentInfo.fNumAAHairlineConcavePaths <= fContentInfo.fNumAAConcavePaths);
|
|
}
|
|
int numAAHairlineConcavePaths() const {
|
|
return fContentInfo.fNumAAHairlineConcavePaths;
|
|
}
|
|
|
|
const SkPath& getPath(int index) const;
|
|
int addPathToHeap(const SkPath& path);
|
|
|
|
void flattenToBuffer(SkWriteBuffer& buffer) const;
|
|
bool parseBufferTag(SkReadBuffer& buffer, uint32_t tag, uint32_t size);
|
|
|
|
static void WriteTagSize(SkWriteBuffer& buffer, uint32_t tag, size_t size);
|
|
static void WriteTagSize(SkWStream* stream, uint32_t tag, size_t size);
|
|
|
|
void initForPlayback() const;
|
|
void dumpSize() const;
|
|
|
|
// An OperationList encapsulates a set of operation offsets into the picture byte
|
|
// stream along with the CTMs needed for those operation.
|
|
class OperationList : ::SkNoncopyable {
|
|
public:
|
|
virtual ~OperationList() {}
|
|
|
|
// If valid returns false then there is no optimization data
|
|
// present. All the draw operations need to be issued.
|
|
virtual bool valid() const { return false; }
|
|
|
|
// The following three entry points should only be accessed if
|
|
// 'valid' returns true.
|
|
virtual int numOps() const { SkASSERT(false); return 0; };
|
|
// The offset in the picture of the operation to execute.
|
|
virtual uint32_t offset(int index) const { SkASSERT(false); return 0; };
|
|
// The CTM that must be installed for the operation to behave correctly
|
|
virtual const SkMatrix& matrix(int index) const { SkASSERT(false); return SkMatrix::I(); }
|
|
|
|
static const OperationList& InvalidList();
|
|
};
|
|
|
|
/** PRIVATE / EXPERIMENTAL -- do not call
|
|
Return the operations required to render the content inside 'queryRect'.
|
|
*/
|
|
const OperationList& EXPERIMENTAL_getActiveOps(const SkIRect& queryRect);
|
|
|
|
/** PRIVATE / EXPERIMENTAL -- do not call
|
|
Return the ID of the operation currently being executed when playing
|
|
back. 0 indicates no call is active.
|
|
*/
|
|
size_t EXPERIMENTAL_curOpID() const;
|
|
|
|
void createHeader(SkPictInfo* info) const;
|
|
static bool IsValidPictInfo(const SkPictInfo& info);
|
|
static SkPicturePlayback* FakeEndRecording(const SkPicture* resourceSrc,
|
|
const SkPictureRecord& record,
|
|
bool deepCopy);
|
|
|
|
friend class SkFlatPicture;
|
|
friend class SkPicturePlayback;
|
|
friend class SkPictureRecorder;
|
|
friend class SkGpuDevice;
|
|
friend class GrGatherCanvas;
|
|
friend class GrGatherDevice;
|
|
friend class SkDebugCanvas;
|
|
|
|
typedef SkRefCnt INHERITED;
|
|
};
|
|
|
|
/**
|
|
* Subclasses of this can be passed to canvas.drawPicture. During the drawing
|
|
* of the picture, this callback will periodically be invoked. If its
|
|
* abortDrawing() returns true, then picture playback will be interrupted.
|
|
*
|
|
* The resulting drawing is undefined, as there is no guarantee how often the
|
|
* callback will be invoked. If the abort happens inside some level of nested
|
|
* calls to save(), restore will automatically be called to return the state
|
|
* to the same level it was before the drawPicture call was made.
|
|
*/
|
|
class SK_API SkDrawPictureCallback {
|
|
public:
|
|
SkDrawPictureCallback() {}
|
|
virtual ~SkDrawPictureCallback() {}
|
|
|
|
virtual bool abortDrawing() = 0;
|
|
};
|
|
|
|
#ifdef SK_SUPPORT_LEGACY_DERIVED_PICTURE_CLASSES
|
|
|
|
class SkPictureFactory : public SkRefCnt {
|
|
public:
|
|
/**
|
|
* Allocate a new SkPicture. Return NULL on failure.
|
|
*/
|
|
virtual SkPicture* create(int width, int height) = 0;
|
|
|
|
private:
|
|
typedef SkRefCnt INHERITED;
|
|
};
|
|
|
|
#endif
|
|
|
|
#ifdef SK_SUPPORT_LEGACY_PICTURE_HEADERS
|
|
#include "SkPictureRecorder.h"
|
|
#endif
|
|
|
|
#endif
|