分类:
2009-03-23 14:47:01
Canvas : SkCanvas.h 这个文件比较重要。
/* This is the record we keep for each SkDevice that the user installs.
The clip/matrix/proc are fields that reflect the top of the save/restore
stack. Whenever the canvas changes, it marks a dirty flag, and then before
these are used (assuming we're not on a layer) we rebuild these cache
values: they reflect the top of the save stack, but translated and clipped
by the device's XY offset and bitmap-bounds.
*/
//这个结构体是用来保存用户安装的SkDevice的。SkRegion,SkMatrix保存了当前SkDevice的状况。
//一旦SkDevice有变化,将会有个dirty标志位。这个时候将会重新构建每个被保存的SkDevice结构体--DeviceCM。
//这些被cache的值只反映顶点坐标,而坐标的转换和裁剪由SkDevice获得。
//注意这个结构体是一个链表结构DeviceCM* fNext.
struct DeviceCM {
DeviceCM* fNext;
SkDevice* fDevice;
SkRegion fClip;
const SkMatrix* fMatrix;
SkPaint* fPaint; // may be null (in the future)
int16_t fX, fY; // relative to base matrix/clip
DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
: fNext(NULL) {
if (NULL != device) {
device->ref();
device->lockPixels();
}
fDevice = device;
fX = SkToS16(x);
fY = SkToS16(y);
fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
}
~DeviceCM() {
if (NULL != fDevice) {
fDevice->unlockPixels();
fDevice->unref();
}
SkDELETE(fPaint);
}
void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
SkRegion* updateClip) {
int x = fX;
int y = fY;
int width = fDevice->width();
int height = fDevice->height();
if ((x | y) == 0) { //x==0 && y==0
fMatrix = &totalMatrix;
fClip = totalClip;
} else {
fMatrixStorage = totalMatrix;
fMatrixStorage.postTranslate(SkIntToScalar(-x),
SkIntToScalar(-y));
fMatrix = &fMatrixStorage;
totalClip.translate(-x, -y, &fClip);
}
fClip.op(0, 0, width, height, SkRegion::kIntersect_Op);
// intersect clip, but don't translate it (yet)
if (updateClip) {
updateClip->op(x, y, x + width, y + height,
SkRegion::kDifference_Op);
}
fDevice->setMatrixClip(*fMatrix, fClip);
#ifdef SK_DEBUG
if (!fClip.isEmpty()) {
SkIRect deviceR;
deviceR.set(0, 0, width, height);
SkASSERT(deviceR.contains(fClip.getBounds()));
}
#endif
}
void translateClip() {
if (fX | fY) {
fClip.translate(fX, fY);
}
}
private:
SkMatrix fMatrixStorage;
};
/* This is the record we keep for each save/restore level in the stack.
Since a level optionally copies the matrix and/or stack, we have pointers
for these fields. If the value is copied for this level, the copy is
stored in the ...Storage field, and the pointer points to that. If the
value is not copied for this level, we ignore ...Storage, and just point
at the corresponding value in the previous level in the stack.
*/
//这个结构体是用来保存stack中的SkDevice的。SkCanvas可以由多个SkDevice构成,每个SkDevice都会有一个对应的在stack中的结构体保存。
//存取这些结构体使用SkDeque。
//注意这个结构体是一个链表结构MCRec* fNext.
class SkCanvas::MCRec {
public:
MCRec* fNext;
SkMatrix* fMatrix; // points to either fMatrixStorage or prev MCRec
SkRegion* fRegion; // points to either fRegionStorage or prev MCRec
SkDrawFilter* fFilter; // the current filter (or null)
DeviceCM* fLayer;
/* If there are any layers in the stack, this points to the top-most
one that is at or below this level in the stack (so we know what
bitmap/device to draw into from this level. This value is NOT
reference counted, since the real owner is either our fLayer field,
or a previous one in a lower level.)
*/
DeviceCM* fTopLayer;
MCRec(const MCRec* prev, int flags) {
if (NULL != prev) {
if (flags & SkCanvas::kMatrix_SaveFlag) {
fMatrixStorage = *prev->fMatrix;
fMatrix = &fMatrixStorage;
} else {
fMatrix = prev->fMatrix;
}
if (flags & SkCanvas::kClip_SaveFlag) {
fRegionStorage = *prev->fRegion;
fRegion = &fRegionStorage;
} else {
fRegion = prev->fRegion;
}
fFilter = prev->fFilter;
fFilter->safeRef();
fTopLayer = prev->fTopLayer;
} else { // no prev
fMatrixStorage.reset();
fMatrix = &fMatrixStorage;
fRegion = &fRegionStorage;
fFilter = NULL;
fTopLayer = NULL;
}
fLayer = NULL;
// don't bother initializing fNext
inc_rec();
}
~MCRec() {
fFilter->safeUnref();
SkDELETE(fLayer);
dec_rec();
}
private:
SkMatrix fMatrixStorage;
SkRegion fRegionStorage;
};
class SkDrawIter : public SkDraw {
public:
SkDrawIter(SkCanvas* canvas, bool skipEmptyClips = true) {
fCanvas = canvas;
canvas->updateDeviceCMCache();
fBounder = canvas->getBounder();
fCurrLayer = canvas->fMCRec->fTopLayer;
fSkipEmptyClips = skipEmptyClips;
}
bool next() {
// skip over recs with empty clips
if (fSkipEmptyClips) {
while (fCurrLayer && fCurrLayer->fClip.isEmpty()) {
fCurrLayer = fCurrLayer->fNext;
}
}
if (NULL != fCurrLayer) {
const DeviceCM* rec = fCurrLayer;
fMatrix = rec->fMatrix;
fClip = &rec->fClip;
fDevice = rec->fDevice;
fBitmap = &fDevice->accessBitmap(true);
fLayerX = rec->fX;
fLayerY = rec->fY;
fPaint = rec->fPaint;
SkDEBUGCODE(this->validate();)
fCurrLayer = rec->fNext;
if (fBounder) {
fBounder->setClip(fClip);
}
// fCurrLayer may be NULL now
fCanvas->prepareForDeviceDraw(fDevice);
return true;
}
return false;
}
int getX() const { return fLayerX; }
int getY() const { return fLayerY; }
SkDevice* getDevice() const { return fDevice; }
const SkMatrix& getMatrix() const { return *fMatrix; }
const SkRegion& getClip() const { return *fClip; }
const SkPaint* getPaint() const { return fPaint; }
private:
SkCanvas* fCanvas;
const DeviceCM* fCurrLayer;
const SkPaint* fPaint; // May be null.
int fLayerX;
int fLayerY;
SkBool8 fSkipEmptyClips;
typedef SkDraw INHERITED;
};
/////////////////////////////////////////////////////////////////////////////
class AutoDrawLooper {
public:
AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint, SkDrawFilter::Type t)
: fCanvas(canvas), fPaint((SkPaint*)&paint), fType(t) {
if ((fLooper = paint.getLooper()) != NULL) {
fLooper->init(canvas, (SkPaint*)&paint);
} else {
fOnce = true;
}
fFilter = canvas->getDrawFilter();
fNeedFilterRestore = false;
}
~AutoDrawLooper() {
if (fNeedFilterRestore) {
SkASSERT(fFilter);
fFilter->restore(fCanvas, fPaint, fType);
}
if (NULL != fLooper) {
fLooper->restore();
}
}
bool next() {
SkDrawFilter* filter = fFilter;
// if we drew earlier with a filter, then we need to restore first
if (fNeedFilterRestore) {
SkASSERT(filter);
filter->restore(fCanvas, fPaint, fType);
fNeedFilterRestore = false;
}
bool result;
if (NULL != fLooper) {
result = fLooper->next();
} else {
result = fOnce;
fOnce = false;
}
// if we're gonna draw, give the filter a chance to do its work
if (result && NULL != filter) {
fNeedFilterRestore = result = filter->filter(fCanvas, fPaint,
fType);
}
return result;
}
private:
SkDrawLooper* fLooper;
SkDrawFilter* fFilter;
SkCanvas* fCanvas;
SkPaint* fPaint;
SkDrawFilter::Type fType;
bool fOnce;
bool fNeedFilterRestore;
};
/* Stack helper for managing a SkBounder. In the destructor, if we were
given a bounder, we call its commit() method, signifying that we are
done accumulating bounds for that draw.
*/
class SkAutoBounderCommit {
public:
SkAutoBounderCommit(SkBounder* bounder) : fBounder(bounder) {}
~SkAutoBounderCommit() {
if (NULL != fBounder) {
fBounder->commit();
}
}
private:
SkBounder* fBounder;
};
class AutoValidator {
public:
AutoValidator(SkDevice* device) : fDevice(device) {}
~AutoValidator() {
#ifdef SK_DEBUG
const SkBitmap& bm = fDevice->accessBitmap(false);
if (bm.config() == SkBitmap::kARGB_4444_Config) {
for (int y = 0; y < bm.height(); y++) {
const SkPMColor16* p = bm.getAddr16(0, y);
for (int x = 0; x < bm.width(); x++) {
SkPMColor
SkPMColor16Assert(c);
}
}
}
#endif
}
private:
SkDevice* fDevice;
};
/** \class SkCanvas
A Canvas encapsulates all of the state about drawing into a device (bitmap).
This includes a reference to the device itself, and a stack of matrix/clip
values. For any given draw call (e.g. drawRect), the geometry of the object
being drawn is transformed by the concatenation of all the matrices in the
stack. The transformed geometry is clipped by the intersection of all of
the clips in the stack.
While the Canvas holds the state of the drawing device, the state (style)
of the object being drawn is held by the Paint, which is provided as a
parameter to each of the draw() methods. The Paint holds attributes such as
color, typeface, textSize, strokeWidth, shader (e.g. gradients, patterns),
etc.
*/
//SkCanvas包含了所有绘画到device上需要的状态。包括对device的参考,一堆矩阵和裁剪值。
class SkCanvas : public SkRefCnt {
public:
/** Construct a canvas with the specified bitmap to draw into.
@param bitmap Specifies a bitmap for the canvas to draw into. Its
structure are copied to the canvas.
*/
explicit SkCanvas(const SkBitmap& bitmap);
//通过SkBitmap创建SkCanvas。SkCanvas将拷贝SkBitmap结构
//虽然SkCanvas是通过SkBitmap创建,但SkCanvas内部使用的是SkDevice,由传入参数SkBitmap创建这个SkDevice.
/** Construct a canvas with the specified device to draw into.
@param device Specifies a device for the canvas to draw into. The
device may be null.
*/
explicit SkCanvas(SkDevice* device = NULL);
//通过SkDevice创建SkCanvas。
virtual ~SkCanvas();
///////////////////////////////////////////////////////////////////////////
/** If this subclass of SkCanvas supports GL viewports, return true and set
size (if not null) to the size of the viewport. If it is not supported,
ignore vp and return false.
*/
virtual bool getViewport(SkIPoint* size) const;
//子类是否支持GL视口
/** If this subclass of SkCanvas supports GL viewports, return true and set
the viewport to the specified x and y dimensions. If it is not
supported, ignore x and y and return false.
*/
virtual bool setViewport(int x, int y);
//子类是否支持GL视口
/** Return the canvas' device object, which may be null. The device holds
the bitmap of the pixels that the canvas draws into. The reference count
of the returned device is not changed by this call.
*/
SkDevice* getDevice() const;
//返回SkCanvas的SkDevice,可能为空。
/** Specify a device for this canvas to draw into. If it is not null, its
reference count is incremented. If the canvas was already holding a
device, its reference count is decremented. The new device is returned.
*/
SkDevice* setDevice(SkDevice* device);
//设置SkCanvas的SkDevice。如果原来没有SkDevice,则SkDevice参考计数器加1。如果有,会将原来的SkDevice参考计数器减一,并用现在的SkDevice代替。
/** Specify a bitmap for the canvas to draw into. This is a help method for
setDevice(), and it creates a device for the bitmap by calling
createDevice(). The structure of the bitmap is copied into the device.
*/
virtual SkDevice* setBitmapDevice(const SkBitmap& bitmap);
//通过SkBitmap来设置SkCanvas的SkDevice。
///////////////////////////////////////////////////////////////////////////
enum SaveFlags {
/** save the matrix state, restoring it on restore() */
kMatrix_SaveFlag = 0x01,
/** save the clip state, restoring it on restore() */
kClip_SaveFlag = 0x02,
/** the layer needs to support per-pixel alpha */
kHasAlphaLayer_SaveFlag = 0x04,
/** the layer needs to support 8-bits per color component */
kFullColorLayer_SaveFlag = 0x08,
/** the layer should clip against the bounds argument */
kClipToLayer_SaveFlag = 0x10,
// helper masks for common choices
kMatrixClip_SaveFlag = 0x03,
kARGB_NoClipLayer_SaveFlag = 0x
kARGB_ClipLayer_SaveFlag = 0x
};
/** This call saves the current matrix and clip information, and pushes a
copy onto a private stack. Subsequent calls to translate, scale,
rotate, skew, concat or clipRect, clipPath all operate on this copy.
When the balancing call to restore() is made, this copy is deleted and
the previous matrix/clip state is restored.
@return The value to pass to restoreToCount() to balance this save()
*/
//这个函数保存当前的matrix和clip状态,并把当前的状态做一个备份,压栈。
//save()之后,所有的操作,translate, scale, rotate, skew, concat or clipRect, clipPath都会在调用restore()函数后去除。
//所以在save()和restore()直接可以做一些临时操作。
virtual int save(SaveFlags flags = kMatrixClip_SaveFlag);
/** This behaves the same as save(), but in addition it allocates an
offscreen bitmap. All drawing calls are directed there, and only when
the balancing call to restore() is made is that offscreen transfered to
the canvas (or the previous layer). Subsequent calls to translate,
scale, rotate, skew, concat or clipRect, clipPath all operate on this
copy. When the balancing call to restore() is made, this copy is deleted
and the previous matrix/clip state is restored.
@param bounds (may be null) the maximum size the offscreen bitmap needs
to be (in local coordinates)
@param paint (may be null) This is copied, and is applied to the
offscreen when restore() is called
@param flags LayerFlags
@return The value to pass to restoreToCount() to balance this save()
*/
//这个函数功能和save()一样。但是这个函数会保存到离屏图像上,所有的绘画函数都会直接画到那里,直到restore()调用。
virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
SaveFlags flags = kARGB_ClipLayer_SaveFlag);
/** This behaves the same as save(), but in addition it allocates an
offscreen bitmap. All drawing calls are directed there, and only when
the balancing call to restore() is made is that offscreen transfered to
the canvas (or the previous layer). Subsequent calls to translate,
scale, rotate, skew, concat or clipRect, clipPath all operate on this
copy. When the balancing call to restore() is made, this copy is deleted
and the previous matrix/clip state is restored.
@param bounds (may be null) the maximum size the offscreen bitmap needs
to be (in local coordinates)
@param alpha This is applied to the offscreen when restore() is called.
@param flags LayerFlags
@return The value to pass to restoreToCount() to balance this save()
*/
int saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
SaveFlags flags = kARGB_ClipLayer_SaveFlag);
/** This call balances a previous call to save(), and is used to remove all
modifications to the matrix/clip state since the last save call. It is
an error to call restore() more times than save() was called.
*/
//save()和restore()调用数量必须匹配。
virtual void restore();
/** Returns the number of matrix/clip states on the SkCanvas' private stack.
This will equal # save() calls - # restore() calls.
*/
int getSaveCount() const;
/** Efficient way to pop any calls to save() that happened after the save
count reached saveCount. It is an error for saveCount to be less than
getSaveCount()
@param saveCount The number of save() levels to restore from
*/
void restoreToCount(int saveCount);
/** Preconcat the current matrix with the specified translation
@param dx The distance to translate in X
@param dy The distance to translate in Y
returns true if the operation succeeded (e.g. did not overflow)
*/
//坐标系统位移
virtual bool translate(SkScalar dx, SkScalar dy);
/** Preconcat the current matrix with the specified scale.
@param sx The amount to scale in X
@param sy The amount to scale in Y
returns true if the operation succeeded (e.g. did not overflow)
*/
//坐标系统缩放
virtual bool scale(SkScalar sx, SkScalar sy);
/** Preconcat the current matrix with the specified rotation.
@param degrees The amount to rotate, in degrees
returns true if the operation succeeded (e.g. did not overflow)
*/
//坐标系统旋转
virtual bool rotate(SkScalar degrees);
/** Preconcat the current matrix with the specified skew.
@param sx The amount to skew in X
@param sy The amount to skew in Y
returns true if the operation succeeded (e.g. did not overflow)
*/
//坐标系统倾向
virtual bool skew(SkScalar sx, SkScalar sy);
/** Preconcat the current matrix with the specified matrix.
@param matrix The matrix to preconcatenate with the current matrix
@return true if the operation succeeded (e.g. did not overflow)
*/
//设置坐标系统联系???
virtual bool concat(const SkMatrix& matrix);
/** Replace the current matrix with a copy of the specified matrix.
@param matrix The matrix that will be copied into the current matrix.
*/
//设置新坐标系统
virtual void setMatrix(const SkMatrix& matrix);
/** Helper for setMatrix(identity). Sets the current matrix to identity.
*/
//重置坐标系统
void resetMatrix();
/** Modify the current clip with the specified rectangle.
@param rect The rect to intersect with the current clip
@param op The region op to apply to the current clip
@return true if the canvas' clip is non-empty
*/
//通过给定的矩形来改变当前的clip。
virtual bool clipRect(const SkRect& rect,
SkRegion::Op op = SkRegion::kIntersect_Op);
/** Modify the current clip with the specified path.
@param path The path to apply to the current clip
@param op The region op to apply to the current clip
@return true if the canvas' new clip is non-empty
*/
virtual bool clipPath(const SkPath& path,
SkRegion::Op op = SkRegion::kIntersect_Op);
/** Modify the current clip with the specified region. Note that unlike
clipRect() and clipPath() which transform their arguments by the current
matrix, clipRegion() assumes its argument is already in device
coordinates, and so no transformation is performed.
@param deviceRgn The region to apply to the current clip
@param op The region op to apply to the current clip
@return true if the canvas' new clip is non-empty
*/
virtual bool clipRegion(const SkRegion& deviceRgn,
SkRegion::Op op = SkRegion::kIntersect_Op);
/** Helper for clipRegion(rgn, kReplace_Op). Sets the current clip to the
specified region. This does not intersect or in any other way account
for the existing clip region.
@param deviceRgn The region to copy into the current clip.
@return true if the new clip region is non-empty
*/
bool setClipRegion(const SkRegion& deviceRgn) {
return this->clipRegion(deviceRgn, SkRegion::kReplace_Op);
}