summaryrefslogtreecommitdiffstats
path: root/skia/sgl/SkCanvas.cpp
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-27 00:09:42 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-27 00:09:42 +0000
commitae2c20f398933a9e86c387dcc465ec0f71065ffc (patch)
treede668b1411e2ee0b4e49b6d8f8b68183134ac990 /skia/sgl/SkCanvas.cpp
parent09911bf300f1a419907a9412154760efd0b7abc3 (diff)
downloadchromium_src-ae2c20f398933a9e86c387dcc465ec0f71065ffc.zip
chromium_src-ae2c20f398933a9e86c387dcc465ec0f71065ffc.tar.gz
chromium_src-ae2c20f398933a9e86c387dcc465ec0f71065ffc.tar.bz2
Add skia to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@16 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'skia/sgl/SkCanvas.cpp')
-rw-r--r--skia/sgl/SkCanvas.cpp1323
1 files changed, 1323 insertions, 0 deletions
diff --git a/skia/sgl/SkCanvas.cpp b/skia/sgl/SkCanvas.cpp
new file mode 100644
index 0000000..a657023
--- /dev/null
+++ b/skia/sgl/SkCanvas.cpp
@@ -0,0 +1,1323 @@
+/*
+ * Copyright (C) 2006-2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "SkCanvas.h"
+#include "SkBounder.h"
+#include "SkDevice.h"
+#include "SkDraw.h"
+#include "SkDrawFilter.h"
+#include "SkDrawLooper.h"
+#include "SkPicture.h"
+#include "SkTemplates.h"
+#include "SkUtils.h"
+#include <new>
+
+//#define SK_TRACE_SAVERESTORE
+
+#ifdef SK_TRACE_SAVERESTORE
+ static int gLayerCounter;
+ static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n", gLayerCounter); }
+ static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n", gLayerCounter); }
+
+ static int gRecCounter;
+ static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n", gRecCounter); }
+ static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n", gRecCounter); }
+
+ static int gCanvasCounter;
+ static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n", gCanvasCounter); }
+ static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n", gCanvasCounter); }
+#else
+ #define inc_layer()
+ #define dec_layer()
+ #define inc_rec()
+ #define dec_rec()
+ #define inc_canvas()
+ #define dec_canvas()
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+/* 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.
+*/
+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) {
+ 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.
+*/
+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;
+ 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; }
+
+private:
+ SkCanvas* fCanvas;
+ const DeviceCM* fCurrLayer;
+ 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;
+};
+
+#include "SkColorPriv.h"
+
+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++) {
+ SkPMColor16 c = p[x];
+ SkPMColor16Assert(c);
+ }
+ }
+ }
+#endif
+ }
+private:
+ SkDevice* fDevice;
+};
+
+////////// macros to place around the internal draw calls //////////////////
+
+#define ITER_BEGIN(paint, type) \
+/* AutoValidator validator(fMCRec->fTopLayer->fDevice); */ \
+ AutoDrawLooper looper(this, paint, type); \
+ while (looper.next()) { \
+ SkAutoBounderCommit ac(fBounder); \
+ SkDrawIter iter(this);
+
+#define ITER_END }
+
+////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkCanvas::init(SkDevice* device) {
+ fBounder = NULL;
+
+ fMCRec = (MCRec*)fMCStack.push_back();
+ new (fMCRec) MCRec(NULL, 0);
+
+ fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
+ fMCRec->fTopLayer = fMCRec->fLayer;
+ fMCRec->fNext = NULL;
+
+ return this->setDevice(device);
+}
+
+SkCanvas::SkCanvas(SkDevice* device)
+ : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
+ inc_canvas();
+
+ this->init(device);
+}
+
+SkCanvas::SkCanvas(const SkBitmap& bitmap)
+ : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
+ inc_canvas();
+
+ this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
+}
+
+SkCanvas::~SkCanvas() {
+ // free up the contents of our deque
+ this->restoreToCount(1); // restore everything but the last
+ this->internalRestore(); // restore the last, since we're going away
+
+ fBounder->safeUnref();
+
+ dec_canvas();
+}
+
+SkBounder* SkCanvas::setBounder(SkBounder* bounder) {
+ SkRefCnt_SafeAssign(fBounder, bounder);
+ return bounder;
+}
+
+SkDrawFilter* SkCanvas::getDrawFilter() const {
+ return fMCRec->fFilter;
+}
+
+SkDrawFilter* SkCanvas::setDrawFilter(SkDrawFilter* filter) {
+ SkRefCnt_SafeAssign(fMCRec->fFilter, filter);
+ return filter;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkCanvas::getDevice() const {
+ // return root device
+ SkDeque::Iter iter(fMCStack);
+ MCRec* rec = (MCRec*)iter.next();
+ SkASSERT(rec && rec->fLayer);
+ return rec->fLayer->fDevice;
+}
+
+SkDevice* SkCanvas::setDevice(SkDevice* device) {
+ // return root device
+ SkDeque::Iter iter(fMCStack);
+ MCRec* rec = (MCRec*)iter.next();
+ SkASSERT(rec && rec->fLayer);
+ SkDevice* rootDevice = rec->fLayer->fDevice;
+
+ if (rootDevice == device) {
+ return device;
+ }
+
+ /* Notify the devices that they are going in/out of scope, so they can do
+ things like lock/unlock their pixels, etc.
+ */
+ if (device) {
+ device->lockPixels();
+ }
+ if (rootDevice) {
+ rootDevice->unlockPixels();
+ }
+
+ SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
+ rootDevice = device;
+
+ fDeviceCMDirty = true;
+
+ /* Now we update our initial region to have the bounds of the new device,
+ and then intersect all of the clips in our stack with these bounds,
+ to ensure that we can't draw outside of the device's bounds (and trash
+ memory).
+
+ NOTE: this is only a partial-fix, since if the new device is larger than
+ the previous one, we don't know how to "enlarge" the clips in our stack,
+ so drawing may be artificially restricted. Without keeping a history of
+ all calls to canvas->clipRect() and canvas->clipPath(), we can't exactly
+ reconstruct the correct clips, so this approximation will have to do.
+ The caller really needs to restore() back to the base if they want to
+ accurately take advantage of the new device bounds.
+ */
+
+ if (NULL == device) {
+ rec->fRegion->setEmpty();
+ while ((rec = (MCRec*)iter.next()) != NULL) {
+ (void)rec->fRegion->setEmpty();
+ }
+ } else {
+ // compute our total bounds for all devices
+ SkIRect bounds;
+
+ bounds.set(0, 0, device->width(), device->height());
+
+ // now jam our 1st clip to be bounds, and intersect the rest with that
+ rec->fRegion->setRect(bounds);
+ while ((rec = (MCRec*)iter.next()) != NULL) {
+ (void)rec->fRegion->op(bounds, SkRegion::kIntersect_Op);
+ }
+ }
+ return device;
+}
+
+SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
+ SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
+ device->unref();
+ return device;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool SkCanvas::getViewport(SkIPoint* size) const {
+ return false;
+}
+
+bool SkCanvas::setViewport(int width, int height) {
+ return false;
+}
+
+void SkCanvas::updateDeviceCMCache() {
+ if (fDeviceCMDirty) {
+ const SkMatrix& totalMatrix = this->getTotalMatrix();
+ const SkRegion& totalClip = this->getTotalClip();
+ DeviceCM* layer = fMCRec->fTopLayer;
+
+ if (NULL == layer->fNext) { // only one layer
+ layer->updateMC(totalMatrix, totalClip, NULL);
+ } else {
+ SkRegion clip;
+ clip = totalClip; // make a copy
+ do {
+ layer->updateMC(totalMatrix, clip, &clip);
+ } while ((layer = layer->fNext) != NULL);
+ }
+ fDeviceCMDirty = false;
+ }
+}
+
+void SkCanvas::prepareForDeviceDraw(SkDevice* device) {
+ SkASSERT(device);
+ device->gainFocus(this);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkCanvas::internalSave(SaveFlags flags) {
+ int saveCount = this->getSaveCount(); // record this before the actual save
+
+ MCRec* newTop = (MCRec*)fMCStack.push_back();
+ new (newTop) MCRec(fMCRec, flags); // balanced in restore()
+
+ newTop->fNext = fMCRec;
+ fMCRec = newTop;
+
+ return saveCount;
+}
+
+int SkCanvas::save(SaveFlags flags) {
+ // call shared impl
+ return this->internalSave(flags);
+}
+
+#define C32MASK (1 << SkBitmap::kARGB_8888_Config)
+#define C16MASK (1 << SkBitmap::kRGB_565_Config)
+#define C8MASK (1 << SkBitmap::kA8_Config)
+
+static SkBitmap::Config resolve_config(SkCanvas* canvas,
+ const SkIRect& bounds,
+ SkCanvas::SaveFlags flags,
+ bool* isOpaque) {
+ *isOpaque = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) == 0;
+
+#if 0
+ // loop through and union all the configs we may draw into
+ uint32_t configMask = 0;
+ for (int i = canvas->countLayerDevices() - 1; i >= 0; --i)
+ {
+ SkDevice* device = canvas->getLayerDevice(i);
+ if (device->intersects(bounds))
+ configMask |= 1 << device->config();
+ }
+
+ // if the caller wants alpha or fullcolor, we can't return 565
+ if (flags & (SkCanvas::kFullColorLayer_SaveFlag |
+ SkCanvas::kHasAlphaLayer_SaveFlag))
+ configMask &= ~C16MASK;
+
+ switch (configMask) {
+ case C8MASK: // if we only have A8, return that
+ return SkBitmap::kA8_Config;
+
+ case C16MASK: // if we only have 565, return that
+ return SkBitmap::kRGB_565_Config;
+
+ default:
+ return SkBitmap::kARGB_8888_Config; // default answer
+ }
+#else
+ return SkBitmap::kARGB_8888_Config; // default answer
+#endif
+}
+
+static bool bounds_affects_clip(SkCanvas::SaveFlags flags) {
+ return (flags & SkCanvas::kClipToLayer_SaveFlag) != 0;
+}
+
+int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
+ SaveFlags flags) {
+ // do this before we create the layer. We don't call the public save() since
+ // that would invoke a possibly overridden virtual
+ int count = this->internalSave(flags);
+
+ fDeviceCMDirty = true;
+
+ SkIRect ir;
+ const SkIRect& clipBounds = this->getTotalClip().getBounds();
+
+ if (NULL != bounds) {
+ SkRect r;
+
+ this->getTotalMatrix().mapRect(&r, *bounds);
+ r.roundOut(&ir);
+ // early exit if the layer's bounds are clipped out
+ if (!ir.intersect(clipBounds)) {
+ if (bounds_affects_clip(flags))
+ fMCRec->fRegion->setEmpty();
+ return count;
+ }
+ } else { // no user bounds, so just use the clip
+ ir = clipBounds;
+ }
+
+ // early exit if the clip is now empty
+ if (bounds_affects_clip(flags) &&
+ !fMCRec->fRegion->op(ir, SkRegion::kIntersect_Op)) {
+ return count;
+ }
+
+ bool isOpaque;
+ SkBitmap::Config config = resolve_config(this, ir, flags, &isOpaque);
+
+ SkDevice* device = this->createDevice(config, ir.width(), ir.height(),
+ isOpaque, true);
+ DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
+ device->unref();
+
+ layer->fNext = fMCRec->fTopLayer;
+ fMCRec->fLayer = layer;
+ fMCRec->fTopLayer = layer; // this field is NOT an owner of layer
+
+ return count;
+}
+
+int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha,
+ SaveFlags flags) {
+ if (0xFF == alpha) {
+ return this->saveLayer(bounds, NULL, flags);
+ } else {
+ SkPaint tmpPaint;
+ tmpPaint.setAlpha(alpha);
+ return this->saveLayer(bounds, &tmpPaint, flags);
+ }
+}
+
+void SkCanvas::restore() {
+ // check for underflow
+ if (fMCStack.count() > 1) {
+ this->internalRestore();
+ }
+}
+
+void SkCanvas::internalRestore() {
+ SkASSERT(fMCStack.count() != 0);
+
+ fDeviceCMDirty = true;
+
+ // reserve our layer (if any)
+ DeviceCM* layer = fMCRec->fLayer; // may be null
+ // now detach it from fMCRec so we can pop(). Gets freed after its drawn
+ fMCRec->fLayer = NULL;
+
+ // now do the normal restore()
+ fMCRec->~MCRec(); // balanced in save()
+ fMCStack.pop_back();
+ fMCRec = (MCRec*)fMCStack.back();
+
+ /* Time to draw the layer's offscreen. We can't call the public drawSprite,
+ since if we're being recorded, we don't want to record this (the
+ recorder will have already recorded the restore).
+ */
+ if (NULL != layer) {
+ if (layer->fNext) {
+ this->drawDevice(layer->fDevice, layer->fX, layer->fY,
+ layer->fPaint);
+ // reset this, since drawDevice will have set it to true
+ fDeviceCMDirty = true;
+ }
+ SkDELETE(layer);
+ }
+}
+
+int SkCanvas::getSaveCount() const {
+ return fMCStack.count();
+}
+
+void SkCanvas::restoreToCount(int count) {
+ // sanity check
+ if (count < 1) {
+ count = 1;
+ }
+ while (fMCStack.count() > count) {
+ this->restore();
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+// can't draw it if its empty, or its too big for a fixed-point width or height
+static bool reject_bitmap(const SkBitmap& bitmap) {
+ return bitmap.width() <= 0 || bitmap.height() <= 0 ||
+ bitmap.width() > 32767 || bitmap.height() > 32767;
+}
+
+void SkCanvas::internalDrawBitmap(const SkBitmap& bitmap,
+ const SkMatrix& matrix, const SkPaint* paint) {
+ if (reject_bitmap(bitmap)) {
+ return;
+ }
+
+ if (NULL == paint) {
+ SkPaint tmpPaint;
+ this->commonDrawBitmap(bitmap, matrix, tmpPaint);
+ } else {
+ this->commonDrawBitmap(bitmap, matrix, *paint);
+ }
+}
+
+void SkCanvas::drawDevice(SkDevice* device, int x, int y,
+ const SkPaint* paint) {
+ SkPaint tmp;
+ if (NULL == paint) {
+ tmp.setDither(true);
+ paint = &tmp;
+ }
+
+ ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
+ while (iter.next()) {
+ iter.fDevice->drawDevice(iter, device, x - iter.getX(), y - iter.getY(),
+ *paint);
+ }
+ ITER_END
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
+ fDeviceCMDirty = true;
+ return fMCRec->fMatrix->preTranslate(dx, dy);
+}
+
+bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
+ fDeviceCMDirty = true;
+ return fMCRec->fMatrix->preScale(sx, sy);
+}
+
+bool SkCanvas::rotate(SkScalar degrees) {
+ fDeviceCMDirty = true;
+ return fMCRec->fMatrix->preRotate(degrees);
+}
+
+bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
+ fDeviceCMDirty = true;
+ return fMCRec->fMatrix->preSkew(sx, sy);
+}
+
+bool SkCanvas::concat(const SkMatrix& matrix) {
+ fDeviceCMDirty = true;
+ return fMCRec->fMatrix->preConcat(matrix);
+}
+
+void SkCanvas::setMatrix(const SkMatrix& matrix) {
+ fDeviceCMDirty = true;
+ *fMCRec->fMatrix = matrix;
+}
+
+// this is not virtual, so it must call a virtual method so that subclasses
+// will see its action
+void SkCanvas::resetMatrix() {
+ SkMatrix matrix;
+
+ matrix.reset();
+ this->setMatrix(matrix);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op) {
+ fDeviceCMDirty = true;
+ if (fMCRec->fMatrix->rectStaysRect()) {
+ SkRect r;
+ SkIRect ir;
+
+ fMCRec->fMatrix->mapRect(&r, rect);
+ r.round(&ir);
+ return fMCRec->fRegion->op(ir, op);
+ } else {
+ SkPath path;
+
+ path.addRect(rect);
+ return this->clipPath(path, op);
+ }
+}
+
+bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op) {
+ fDeviceCMDirty = true;
+
+ SkPath devPath;
+ path.transform(*fMCRec->fMatrix, &devPath);
+
+ if (SkRegion::kIntersect_Op == op) {
+ return fMCRec->fRegion->setPath(devPath, *fMCRec->fRegion);
+ } else {
+ SkRegion base;
+ const SkBitmap& bm = this->getDevice()->accessBitmap(false);
+ base.setRect(0, 0, bm.width(), bm.height());
+
+ if (SkRegion::kReplace_Op == op) {
+ return fMCRec->fRegion->setPath(devPath, base);
+ } else {
+ SkRegion rgn;
+ rgn.setPath(devPath, base);
+ return fMCRec->fRegion->op(rgn, op);
+ }
+ }
+}
+
+bool SkCanvas::clipRegion(const SkRegion& rgn, SkRegion::Op op) {
+ fDeviceCMDirty = true;
+ return fMCRec->fRegion->op(rgn, op);
+}
+
+bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
+ if (fMCRec->fRegion->isEmpty() || rect.isEmpty()) {
+ return true;
+ }
+
+ SkRect r;
+ SkIRect ir;
+
+ fMCRec->fMatrix->mapRect(&r, rect);
+ if (kAA_EdgeType == et) {
+ r.roundOut(&ir);
+ } else {
+ r.round(&ir);
+ }
+ return fMCRec->fRegion->quickReject(ir);
+}
+
+bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
+ if (fMCRec->fRegion->isEmpty() || path.isEmpty()) {
+ return true;
+ }
+
+ if (fMCRec->fMatrix->rectStaysRect()) {
+ SkRect r;
+ path.computeBounds(&r, SkPath::kExact_BoundsType);
+ return this->quickReject(r, et);
+ }
+
+ SkPath dstPath;
+ SkRect r;
+ SkIRect ir;
+
+ path.transform(*fMCRec->fMatrix, &dstPath);
+ dstPath.computeBounds(&r, SkPath::kExact_BoundsType);
+ if (kAA_EdgeType == et) {
+ r.roundOut(&ir);
+ } else {
+ r.round(&ir);
+ }
+ return fMCRec->fRegion->quickReject(ir);
+}
+
+bool SkCanvas::quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
+ if (fMCRec->fRegion->isEmpty() || top >= bottom) {
+ return true;
+ }
+
+ const SkMatrix& matrix = *fMCRec->fMatrix;
+
+ // if we're rotated/skewed/perspective, give up (for now)
+ // TODO: cache this attribute of the matrix? or specialized query method?
+ // TODO: if rotate=90 or 270 is common, we can handle those too...
+ if (matrix.getType() & ~(SkMatrix::kTranslate_Mask |
+ SkMatrix::kScale_Mask)) {
+ return false;
+ }
+ // transform top/botttom into device coordinates
+ const SkScalar sy = matrix[SkMatrix::kMScaleY];
+ const SkScalar ty = matrix[SkMatrix::kMTransY];
+ top = SkScalarMulAdd(top, sy, ty);
+ bottom = SkScalarMulAdd(bottom, sy, ty);
+
+ // if the scale flipped us, flip back
+ if (top > bottom) {
+ SkTSwap<SkScalar>(top, bottom);
+ }
+ // now round based on the edge type
+ int ymin, ymax;
+ if (kAA_EdgeType == et) {
+ ymin = SkScalarFloor(top);
+ ymax = SkScalarCeil(bottom);
+ } else {
+ ymin = SkScalarRound(top);
+ ymax = SkScalarRound(bottom);
+ }
+
+ // now compare against the bounds of the clip
+ const SkIRect& bounds = fMCRec->fRegion->getBounds();
+ return ymin >= bounds.fBottom || ymax <= bounds.fTop;
+}
+
+bool SkCanvas::getClipBounds(SkRect* bounds) const {
+ const SkRegion& clip = *fMCRec->fRegion;
+ if (clip.isEmpty()) {
+ if (bounds) {
+ bounds->setEmpty();
+ }
+ return false;
+ }
+
+ if (NULL != bounds) {
+ SkMatrix inverse;
+ SkRect r;
+
+ // TODO: should we cache the inverse (with a dirty bit)?
+ fMCRec->fMatrix->invert(&inverse);
+ r.set(clip.getBounds());
+ inverse.mapRect(bounds, r);
+ }
+ return true;
+}
+
+const SkMatrix& SkCanvas::getTotalMatrix() const {
+ return *fMCRec->fMatrix;
+}
+
+const SkRegion& SkCanvas::getTotalClip() const {
+ return *fMCRec->fRegion;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDevice* SkCanvas::createDevice(SkBitmap::Config config, int width,
+ int height, bool isOpaque, bool isForLayer) {
+ SkBitmap bitmap;
+
+ bitmap.setConfig(config, width, height);
+ bitmap.setIsOpaque(isOpaque);
+
+ // should this happen in the device subclass?
+ bitmap.allocPixels();
+ if (!bitmap.isOpaque()) {
+ bitmap.eraseARGB(0, 0, 0, 0);
+ }
+
+ return SkNEW_ARGS(SkDevice, (bitmap));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// These are the virtual drawing methods
+//////////////////////////////////////////////////////////////////////////////
+
+void SkCanvas::drawPaint(const SkPaint& paint) {
+ ITER_BEGIN(paint, SkDrawFilter::kPaint_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawPaint(iter, paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+ const SkPaint& paint) {
+ if ((long)count <= 0) {
+ return;
+ }
+
+ SkASSERT(pts != NULL);
+
+ ITER_BEGIN(paint, SkDrawFilter::kPoint_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawPoints(iter, mode, count, pts, paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
+ ITER_BEGIN(paint, SkDrawFilter::kRect_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawRect(iter, r, paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+ ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawPath(iter, path, paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
+ const SkPaint* paint) {
+ SkDEBUGCODE(bitmap.validate();)
+
+ SkMatrix matrix;
+ matrix.setTranslate(x, y);
+ this->internalDrawBitmap(bitmap, matrix, paint);
+}
+
+void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+ const SkRect& dst, const SkPaint* paint) {
+ if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
+ return;
+ }
+
+ // do this now, to avoid the cost of calling extract for RLE bitmaps
+ if (this->quickReject(dst, paint != NULL && paint->isAntiAlias() ?
+ kAA_EdgeType : kBW_EdgeType)) {
+ return;
+ }
+
+ SkBitmap tmp; // storage if we need a subset of bitmap
+ const SkBitmap* bitmapPtr = &bitmap;
+
+ if (NULL != src) {
+ if (!bitmap.extractSubset(&tmp, *src)) {
+ return; // extraction failed
+ }
+ bitmapPtr = &tmp;
+ }
+
+ SkScalar width = SkIntToScalar(bitmapPtr->width());
+ SkScalar height = SkIntToScalar(bitmapPtr->height());
+ SkMatrix matrix;
+
+ if (dst.width() == width && dst.height() == height) {
+ matrix.setTranslate(dst.fLeft, dst.fTop);
+ } else {
+ SkRect tmpSrc;
+ tmpSrc.set(0, 0, width, height);
+ matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
+ }
+ this->internalDrawBitmap(*bitmapPtr, matrix, paint);
+}
+
+void SkCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& matrix,
+ const SkPaint* paint) {
+ SkDEBUGCODE(bitmap.validate();)
+ this->internalDrawBitmap(bitmap, matrix, paint);
+}
+
+void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+ const SkPaint& paint) {
+ SkDEBUGCODE(bitmap.validate();)
+
+ ITER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawBitmap(iter, bitmap, matrix, paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
+ const SkPaint* paint) {
+ SkDEBUGCODE(bitmap.validate();)
+
+ if (reject_bitmap(bitmap)) {
+ return;
+ }
+
+ SkPaint tmp;
+ if (NULL == paint) {
+ paint = &tmp;
+ }
+
+ ITER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
+ *paint);
+ }
+ ITER_END
+}
+
+void SkCanvas::drawText(const void* text, size_t byteLength,
+ SkScalar x, SkScalar y, const SkPaint& paint) {
+ ITER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawText(iter, text, byteLength, x, y, paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawPosText(const void* text, size_t byteLength,
+ const SkPoint pos[], const SkPaint& paint) {
+ ITER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawPosText(iter, text, byteLength, &pos->fX, 0, 2,
+ paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
+ const SkScalar xpos[], SkScalar constY,
+ const SkPaint& paint) {
+ ITER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawPosText(iter, text, byteLength, xpos, constY, 1,
+ paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
+ const SkPath& path, const SkMatrix* matrix,
+ const SkPaint& paint) {
+ ITER_BEGIN(paint, SkDrawFilter::kText_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawTextOnPath(iter, text, byteLength, path,
+ matrix, paint);
+ }
+
+ ITER_END
+}
+
+void SkCanvas::drawVertices(VertexMode vmode, int vertexCount,
+ const SkPoint verts[], const SkPoint texs[],
+ const SkColor colors[], SkXfermode* xmode,
+ const uint16_t indices[], int indexCount,
+ const SkPaint& paint) {
+ ITER_BEGIN(paint, SkDrawFilter::kPath_Type)
+
+ while (iter.next()) {
+ iter.fDevice->drawVertices(iter, vmode, vertexCount, verts, texs,
+ colors, xmode, indices, indexCount, paint);
+ }
+
+ ITER_END
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// These methods are NOT virtual, and therefore must call back into virtual
+// methods, rather than actually drawing themselves.
+//////////////////////////////////////////////////////////////////////////////
+
+void SkCanvas::drawARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b,
+ SkPorterDuff::Mode mode) {
+ SkPaint paint;
+
+ paint.setARGB(a, r, g, b);
+ if (SkPorterDuff::kSrcOver_Mode != mode) {
+ paint.setPorterDuffXfermode(mode);
+ }
+ this->drawPaint(paint);
+}
+
+void SkCanvas::drawColor(SkColor c, SkPorterDuff::Mode mode) {
+ SkPaint paint;
+
+ paint.setColor(c);
+ if (SkPorterDuff::kSrcOver_Mode != mode) {
+ paint.setPorterDuffXfermode(mode);
+ }
+ this->drawPaint(paint);
+}
+
+void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) {
+ SkPoint pt;
+
+ pt.set(x, y);
+ this->drawPoints(kPoints_PointMode, 1, &pt, paint);
+}
+
+void SkCanvas::drawPoint(SkScalar x, SkScalar y, SkColor color) {
+ SkPoint pt;
+ SkPaint paint;
+
+ pt.set(x, y);
+ paint.setColor(color);
+ this->drawPoints(kPoints_PointMode, 1, &pt, paint);
+}
+
+void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1,
+ const SkPaint& paint) {
+ SkPoint pts[2];
+
+ pts[0].set(x0, y0);
+ pts[1].set(x1, y1);
+ this->drawPoints(kLines_PointMode, 2, pts, paint);
+}
+
+void SkCanvas::drawRectCoords(SkScalar left, SkScalar top,
+ SkScalar right, SkScalar bottom,
+ const SkPaint& paint) {
+ SkRect r;
+
+ r.set(left, top, right, bottom);
+ this->drawRect(r, paint);
+}
+
+void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,
+ const SkPaint& paint) {
+ if (radius < 0) {
+ radius = 0;
+ }
+
+ SkPath path;
+ SkRect r;
+
+ r.set(cx - radius, cy - radius, cx + radius, cy + radius);
+ path.addOval(r);
+ this->drawPath(path, paint);
+}
+
+void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
+ const SkPaint& paint) {
+ if (rx > 0 && ry > 0) {
+ SkPath path;
+ path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
+ this->drawPath(path, paint);
+ } else {
+ this->drawRect(r, paint);
+ }
+}
+
+void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
+ SkPath path;
+ path.addOval(oval);
+ this->drawPath(path, paint);
+}
+
+void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
+ SkScalar sweepAngle, bool useCenter,
+ const SkPaint& paint) {
+ if (SkScalarAbs(sweepAngle) >= SkIntToScalar(360)) {
+ this->drawOval(oval, paint);
+ } else {
+ SkPath path;
+ if (useCenter) {
+ path.moveTo(oval.centerX(), oval.centerY());
+ }
+ path.arcTo(oval, startAngle, sweepAngle, !useCenter);
+ if (useCenter) {
+ path.close();
+ }
+ this->drawPath(path, paint);
+ }
+}
+
+void SkCanvas::drawTextOnPathHV(const void* text, size_t byteLength,
+ const SkPath& path, SkScalar hOffset,
+ SkScalar vOffset, const SkPaint& paint) {
+ SkMatrix matrix;
+
+ matrix.setTranslate(hOffset, vOffset);
+ this->drawTextOnPath(text, byteLength, path, &matrix, paint);
+}
+
+void SkCanvas::drawPicture(SkPicture& picture) {
+ int saveCount = save();
+ picture.draw(this);
+ restoreToCount(saveCount);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+SkCanvas::LayerIter::LayerIter(SkCanvas* canvas, bool skipEmptyClips) {
+ // need COMPILE_TIME_ASSERT
+ SkASSERT(sizeof(fStorage) >= sizeof(SkDrawIter));
+
+ SkASSERT(canvas);
+
+ fImpl = new (fStorage) SkDrawIter(canvas, skipEmptyClips);
+ fDone = !fImpl->next();
+}
+
+SkCanvas::LayerIter::~LayerIter() {
+ fImpl->~SkDrawIter();
+}
+
+void SkCanvas::LayerIter::next() {
+ fDone = !fImpl->next();
+}
+
+SkDevice* SkCanvas::LayerIter::device() const {
+ return fImpl->getDevice();
+}
+
+const SkMatrix& SkCanvas::LayerIter::matrix() const {
+ return fImpl->getMatrix();
+}
+
+const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
+int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
+int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
+
+