char fStorage[sizeof(T)];
};
+/**
+ * A helper built on top of SkTLazy to do copy-on-first-write. The object is initialized
+ * with a const pointer but provides a non-const pointer accessor. The first time the
+ * accessor is called (if ever) the object is cloned.
+ *
+ * In the following example at most one copy of constThing is made:
+ *
+ * SkTCopyOnFirstWrite<Thing> thing(&constThing);
+ * ...
+ * function_that_takes_a_const_thing_ptr(thing); // constThing is passed
+ * ...
+ * if (need_to_modify_thing()) {
+ * thing.writable()->modifyMe(); // makes a copy of constThing
+ * }
+ * ...
+ * x = thing->readSomething();
+ * ...
+ * if (need_to_modify_thing_now()) {
+ * thing.writable()->changeMe(); // makes a copy of constThing if we didn't call modifyMe()
+ * }
+ *
+ * consume_a_thing(thing); // could be constThing or a modified copy.
+ */
+template <typename T>
+class SkTCopyOnFirstWrite {
+public:
+ SkTCopyOnFirstWrite(const T& initial) : fObj(&initial) {}
+
+ /**
+ * Returns a writable T*. The first time this is called the initial object is cloned.
+ */
+ T* writable() {
+ if (!fLazy.isValid()) {
+ fLazy.set(*fObj);
+ fObj = fLazy.get();
+ }
+ return const_cast<T*>(fObj);
+ }
+
+ /**
+ * Operators for treating this as though it were a const pointer.
+ */
+
+ const T *operator->() const { return fObj; }
+
+ operator const T*() const { return fObj; }
+
+ const T& operator *() const { return *fObj; }
+
+private:
+ const T* fObj;
+ SkTLazy<T> fLazy;
+};
+
#endif
SkXfermode* mode = origPaint.getXfermode();
Sk3DShader* shader3D = NULL;
- SkTLazy<SkPaint> lazyPaint;
- // we promise not to mutate paint unless we know we've reassigned it from
- // lazyPaint
- SkPaint* paint = const_cast<SkPaint*>(&origPaint);
+ SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
if (origPaint.getMaskFilter() != NULL &&
origPaint.getMaskFilter()->getFormat() == SkMask::k3D_Format) {
shader3D = SkNEW_ARGS(Sk3DShader, (shader));
// we know we haven't initialized lazyPaint yet, so just do it
- paint = lazyPaint.set(origPaint);
- paint->setShader(shader3D)->unref();
+ paint.writable()->setShader(shader3D)->unref();
shader = shader3D;
}
switch (interpret_xfermode(*paint, mode, device.config())) {
case kSrcOver_XferInterp:
mode = NULL;
- if (!lazyPaint.isValid()) {
- paint = lazyPaint.set(origPaint);
- }
- paint->setXfermode(NULL);
+ paint.writable()->setXfermode(NULL);
break;
case kSkipDrawing_XferInterp:
SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
#endif
// xfermodes (and filters) require shaders for our current blitters
shader = SkNEW(SkColorShader);
- if (!lazyPaint.isValid()) {
- paint = lazyPaint.set(origPaint);
- }
- paint->setShader(shader)->unref();
+ paint.writable()->setShader(shader)->unref();
} else if (cf) {
// if no shader && no xfermode, we just apply the colorfilter to
// our color and move on.
- if (!lazyPaint.isValid()) {
- paint = lazyPaint.set(origPaint);
- }
- paint->setColor(cf->filterColor(paint->getColor()));
- paint->setColorFilter(NULL);
+ SkPaint* writablePaint = paint.writable();
+ writablePaint->setColor(cf->filterColor(paint->getColor()));
+ writablePaint->setColorFilter(NULL);
cf = NULL;
}
}
if (cf) {
SkASSERT(shader);
shader = SkNEW_ARGS(SkFilterShader, (shader, cf));
- if (!lazyPaint.isValid()) {
- paint = lazyPaint.set(origPaint);
- }
- paint->setShader(shader)->unref();
+ paint.writable()->setShader(shader)->unref();
// blitters should ignore the presence/absence of a filter, since
// if there is one, the shader will take care of it.
}
// at this point we're done with prePathMatrix
SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
- const SkPaint* paint = &origPaint;
- SkTLazy<SkPaint> lazyPaint;
+ SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
{
SkScalar coverage;
if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) {
if (SK_Scalar1 == coverage) {
- lazyPaint.set(origPaint);
- lazyPaint.get()->setStrokeWidth(0);
- paint = lazyPaint.get();
+ paint.writable()->setStrokeWidth(0);
} else if (xfermodeSupportsCoverageAsAlpha(origPaint.getXfermode())) {
U8CPU newAlpha;
#if 0
int scale = (int)SkScalarMul(coverage, 256);
newAlpha = origPaint.getAlpha() * scale >> 8;
#endif
- lazyPaint.set(origPaint);
- lazyPaint.get()->setStrokeWidth(0);
- lazyPaint.get()->setAlpha(newAlpha);
- paint = lazyPaint.get();
+ SkPaint* writablePaint = paint.writable();
+ writablePaint->setStrokeWidth(0);
+ writablePaint->setAlpha(newAlpha);
}
}
}
SkDEBUGF(("XPS drawVertices not yet implemented."));
}
-void SkXPSDevice::drawPaint(const SkDraw& d, const SkPaint& paint) {
+void SkXPSDevice::drawPaint(const SkDraw& d, const SkPaint& origPaint) {
const SkRect r = SkRect::MakeSize(this->fCurrentCanvasSize);
//If trying to paint with a stroke, ignore that and fill.
- SkPaint* fillPaint = const_cast<SkPaint*>(&paint);
- SkTLazy<SkPaint> modifiedPaint;
- if (paint.getStyle() != SkPaint::kFill_Style) {
- fillPaint = modifiedPaint.set(paint);
- fillPaint->setStyle(SkPaint::kFill_Style);
+ SkPaint* fillPaint = const_cast<SkPaint*>(&origPaint);
+ SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
+ if (paint->getStyle() != SkPaint::kFill_Style) {
+ paint.writable()->setStyle(SkPaint::kFill_Style);
}
this->internalDrawRect(d, r, false, *fillPaint);
void SkXPSDevice::drawPath(const SkDraw& d,
const SkPath& platonicPath,
- const SkPaint& paint,
+ const SkPaint& origPaint,
const SkMatrix* prePathMatrix,
bool pathIsMutable) {
+ SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
+
// nothing to draw
if (d.fClip->isEmpty() ||
- (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+ (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
return;
}
SkPath modifiedPath;
- const bool paintHasPathEffect = paint.getPathEffect()
- || paint.getStyle() != SkPaint::kFill_Style;
+ const bool paintHasPathEffect = paint->getPathEffect()
+ || paint->getStyle() != SkPaint::kFill_Style;
//Apply pre-path matrix [Platonic-path -> Skeletal-path].
SkMatrix matrix = *d.fMatrix;
SkPath* skeletalPath = const_cast<SkPath*>(&platonicPath);
if (prePathMatrix) {
- if (paintHasPathEffect || paint.getRasterizer()) {
+ if (paintHasPathEffect || paint->getRasterizer()) {
if (!pathIsMutable) {
skeletalPath = &modifiedPath;
pathIsMutable = true;
}
}
- SkTLazy<SkPaint> lazyShaderPaint;
- SkPaint* shaderPaint = const_cast<SkPaint*>(&paint);
-
//Apply path effect [Skeletal-path -> Fillable-path].
SkPath* fillablePath = skeletalPath;
if (paintHasPathEffect) {
fillablePath = &modifiedPath;
pathIsMutable = true;
}
- bool fill = paint.getFillPath(*skeletalPath, fillablePath);
+ bool fill = paint->getFillPath(*skeletalPath, fillablePath);
- shaderPaint = lazyShaderPaint.set(*shaderPaint);
- shaderPaint->setPathEffect(NULL);
+ SkPaint* writablePaint = paint.writable();
+ writablePaint->setPathEffect(NULL);
if (fill) {
- shaderPaint->setStyle(SkPaint::kFill_Style);
+ writablePaint->setStyle(SkPaint::kFill_Style);
} else {
- shaderPaint->setStyle(SkPaint::kStroke_Style);
- shaderPaint->setStrokeWidth(0);
+ writablePaint->setStyle(SkPaint::kStroke_Style);
+ writablePaint->setStrokeWidth(0);
}
}
HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()),
"Could not add the shaded geometry to shaded path.");
- SkRasterizer* rasterizer = paint.getRasterizer();
- SkMaskFilter* filter = paint.getMaskFilter();
-
- SkTLazy<SkPaint> lazyRasterizePaint;
- const SkPaint* rasterizePaint = shaderPaint;
+ SkRasterizer* rasterizer = paint->getRasterizer();
+ SkMaskFilter* filter = paint->getMaskFilter();
//Determine if we will draw or shade and mask.
if (rasterizer || filter) {
- if (shaderPaint->getStyle() != SkPaint::kFill_Style) {
- if (lazyShaderPaint.isValid()) {
- rasterizePaint = lazyRasterizePaint.set(*shaderPaint);
- } else {
- rasterizePaint = shaderPaint;
- shaderPaint = lazyShaderPaint.set(*shaderPaint);
- }
- shaderPaint->setStyle(SkPaint::kFill_Style);
+ if (paint->getStyle() != SkPaint::kFill_Style) {
+ paint.writable()->setStyle(SkPaint::kFill_Style);
}
}
BOOL fill;
BOOL stroke;
HRV(this->shadePath(shadedPath.get(),
- *shaderPaint,
+ *paint,
*d.fMatrix,
&fill,
&stroke));
&matrix,
&rasteredMask,
SkMask::kComputeBoundsAndRenderImage_CreateMode,
- rasterizePaint->getStyle())) {
+ paint->getStyle())) {
SkAutoMaskFreeImage rasteredAmi(rasteredMask.fImage);
mask = &rasteredMask;
this->prepareToDraw(NULL, DEFAULT_BUFFERING)->clear(rect, color, target);
}
-void GrContext::drawPaint(const GrPaint& paint) {
+void GrContext::drawPaint(const GrPaint& origPaint) {
// set rect to be big enough to fill the space, but not super-huge, so we
// don't overflow fixed-point implementations
GrRect r;
GrIntToScalar(getRenderTarget()->width()),
GrIntToScalar(getRenderTarget()->height()));
GrMatrix inverse;
- SkTLazy<GrPaint> tmpPaint;
- const GrPaint* p = &paint;
+ SkTCopyOnFirstWrite<GrPaint> paint(origPaint);
AutoMatrix am;
// We attempt to map r by the inverse matrix and draw that. mapRect will
}
inverse.mapRect(&r);
} else {
- if (paint.hasStage()) {
- tmpPaint.set(paint);
- p = tmpPaint.get();
- if (!tmpPaint.get()->preConcatSamplerMatricesWithInverse(fDrawState->getViewMatrix())) {
+ if (paint->hasStage()) {
+ if (!paint.writable()->preConcatSamplerMatricesWithInverse(fDrawState->getViewMatrix())) {
GrPrintf("Could not invert matrix\n");
}
}
am.set(this, GrMatrix::I());
}
// by definition this fills the entire clip, no need for AA
- if (paint.isAntiAlias()) {
- if (!tmpPaint.isValid()) {
- tmpPaint.set(paint);
- p = tmpPaint.get();
- }
- GrAssert(p == tmpPaint.get());
- tmpPaint.get()->setAntiAlias(false);
+ if (paint->isAntiAlias()) {
+ paint.writable()->setAntiAlias(false);
}
- this->drawRect(*p, r);
+ this->drawRect(*paint, r);
}
////////////////////////////////////////////////////////////////////////////////
#include "SkDrawProcs.h"
#include "SkGlyphCache.h"
#include "SkImageFilter.h"
-#include "SkTLazy.h"
#include "SkUtils.h"
#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1