--- /dev/null
+/*
+ * 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 "gm.h"
+#include "SkCanvas.h"
+#include "SkRRect.h"
+#include "SkPath.h"
+
+class DRRectGM : public skiagm::GM {
+public:
+ DRRectGM() {}
+
+protected:
+ virtual SkString onShortName() SK_OVERRIDE {
+ return SkString("drrect");
+ }
+
+ virtual SkISize onISize() SK_OVERRIDE {
+ return SkISize::Make(640, 480);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+
+ SkRRect outers[4];
+ // like squares/circles, to exercise fast-cases in GPU
+ SkRect r = { 0, 0, 100, 100 };
+ SkVector radii[4] = {
+ { 0, 0 }, { 30, 1 }, { 10, 40 }, { 40, 40 }
+ };
+
+ const SkScalar dx = r.width() + 16;
+ const SkScalar dy = r.height() + 16;
+
+ outers[0].setRect(r);
+ outers[1].setOval(r);
+ outers[2].setRectXY(r, 20, 20);
+ outers[3].setRectRadii(r, radii);
+
+ SkRRect inners[5];
+ r.inset(25, 25);
+
+ inners[0].setEmpty();
+ inners[1].setRect(r);
+ inners[2].setOval(r);
+ inners[3].setRectXY(r, 20, 20);
+ inners[4].setRectRadii(r, radii);
+
+ canvas->translate(16, 16);
+ for (size_t j = 0; j < SK_ARRAY_COUNT(inners); ++j) {
+ for (size_t i = 0; i < SK_ARRAY_COUNT(outers); ++i) {
+ canvas->save();
+ canvas->translate(dx * j, dy * i);
+ canvas->drawDRRect(outers[i], inners[j], paint);
+ canvas->restore();
+ }
+ }
+ }
+
+private:
+ typedef GM INHERITED;
+};
+
+DEF_GM( return new DRRectGM; )
'../gm/drawbitmaprect.cpp',
'../gm/drawlooper.cpp',
'../gm/dropshadowimagefilter.cpp',
+ '../gm/drrect.cpp',
'../gm/extractbitmap.cpp',
'../gm/emptypath.cpp',
'../gm/fatpathfill.cpp',
*/
virtual void drawRRect(const SkRRect& rrect, const SkPaint& paint);
+ /**
+ * Draw the annulus formed by the outer and inner rrects. The results
+ * are undefined if the outer does not contain the inner.
+ */
+ void drawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint&);
+
/** Draw the specified circle using the specified paint. If radius is <= 0,
then nothing will be drawn. The circle will be filled
or framed based on the Style in the paint.
// default impl defers to its device
virtual const void* onPeekPixels(SkImageInfo*, size_t* rowBytes);
+ virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&);
+
// Returns the canvas to be used by DrawIter. Default implementation
// returns this. Subclasses that encapsulate an indirect canvas may
// need to overload this method. The impl must keep track of this, as it
virtual void drawRRect(const SkDraw&, const SkRRect& rr,
const SkPaint& paint) = 0;
+ // Default impl calls drawPath()
+ virtual void drawDRRect(const SkDraw&, const SkRRect& outer,
+ const SkRRect& inner, const SkPaint&);
+
/**
* If pathIsMutable, then the implementation is allowed to cast path to a
* non-const pointer and modify it in place (as an optimization). Canvas
}
}
+void SkBBoxRecord::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) {
+ if (this->transformBounds(outer.rect(), &paint)) {
+ this->INHERITED::onDrawDRRect(outer, inner, paint);
+ }
+}
+
void SkBBoxRecord::drawPath(const SkPath& path, const SkPaint& paint) {
if (path.isInverseFillType()) {
// If path is inverse filled, use the current clip bounds as the
const SkPaint& paint) SK_OVERRIDE;
virtual void drawPicture(SkPicture& picture) SK_OVERRIDE;
+protected:
+ virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) SK_OVERRIDE;
+
private:
/**
* Takes a bounding box in current canvas view space, accounts for stroking and effects, and
}
+void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) {
+ if (outer.isEmpty()) {
+ return;
+ }
+ if (inner.isEmpty()) {
+ this->drawRRect(outer, paint);
+ return;
+ }
+
+ // We don't have this method (yet), but technically this is what we should
+ // be able to assert...
+ // SkASSERT(outer.contains(inner));
+ //
+ // For now at least check for containment of bounds
+ SkASSERT(outer.getBounds().contains(inner.getBounds()));
+
+ this->onDrawDRRect(outer, inner, paint);
+}
+
//////////////////////////////////////////////////////////////////////////////
// These are the virtual drawing methods
//////////////////////////////////////////////////////////////////////////////
LOOPER_END
}
+void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) {
+ CHECK_SHADER_NOSETCONTEXT(paint);
+
+ SkRect storage;
+ const SkRect* bounds = NULL;
+ if (paint.canComputeFastBounds()) {
+ bounds = &paint.computeFastBounds(outer.getBounds(), &storage);
+ if (this->quickReject(*bounds)) {
+ return;
+ }
+ }
+
+ LOOPER_BEGIN(paint, SkDrawFilter::kRRect_Type, bounds)
+
+ while (iter.next()) {
+ iter.fDevice->drawDRRect(iter, outer, inner, looper.paint());
+ }
+
+ LOOPER_END
+}
void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
CHECK_SHADER_NOSETCONTEXT(paint);
SkSurface* SkBaseDevice::newSurface(const SkImageInfo&) { return NULL; }
const void* SkBaseDevice::peekPixels(SkImageInfo*, size_t*) { return NULL; }
+
+void SkBaseDevice::drawDRRect(const SkDraw& draw, const SkRRect& outer,
+ const SkRRect& inner, const SkPaint& paint) {
+ SkPath path;
+ path.addRRect(outer);
+ path.addRRect(inner);
+ path.setFillType(SkPath::kEvenOdd_FillType);
+
+ const SkMatrix* preMatrix = NULL;
+ const bool pathIsMutable = true;
+ this->drawPath(draw, path, paint, preMatrix, pathIsMutable);
+}
+
COMMENT,
END_COMMENT_GROUP,
- LAST_DRAWTYPE_ENUM = END_COMMENT_GROUP
+ // new ops -- feel free to re-alphabetize on next version bump
+ DRAW_DRRECT,
+
+ LAST_DRAWTYPE_ENUM = DRAW_DRRECT
};
// In the 'match' method, this constant will match any flavor of DRAW_BITMAP*
canvas.drawData(reader.skip(length), length);
// skip handles padding the read out to a multiple of 4
} break;
+ case DRAW_DRRECT: {
+ const SkPaint& paint = *getPaint(reader);
+ SkRRect outer, inner;
+ reader.readRRect(&outer);
+ reader.readRRect(&inner);
+ canvas.drawDRRect(outer, inner, paint);
+ } break;
case BEGIN_COMMENT_GROUP: {
const char* desc = reader.readString();
canvas.beginCommentGroup(desc);
0, // BEGIN_GROUP - no paint
0, // COMMENT - no paint
0, // END_GROUP - no paint
+ 1, // DRAWDRRECT - right after op code
};
- SkASSERT(sizeof(gPaintOffsets) == LAST_DRAWTYPE_ENUM + 1);
+ SK_COMPILE_ASSERT(sizeof(gPaintOffsets) == LAST_DRAWTYPE_ENUM + 1,
+ need_to_be_in_sync);
SkASSERT((unsigned)op <= (unsigned)LAST_DRAWTYPE_ENUM);
int overflow = 0;
result[0], result[3]);
}
+static bool is_drawing_op(DrawType op) {
+ return (op > CONCAT && op < ROTATE) || DRAW_DRRECT == op;
+}
+
/*
* Restore has just been called (but not recorded), so look back at the
* matching save(), and see if we can eliminate the pair of them, due to no
offset += opSize;
while (offset < restoreOffset) {
op = peek_op_and_size(writer, offset, &opSize);
- if ((op > CONCAT && op < ROTATE) || (SAVE_LAYER == op)) {
+ if (is_drawing_op(op) || (SAVE_LAYER == op)) {
// drawing verb, abort
return false;
}
}
}
+void SkPictureRecord::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) {
+
+#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
+ fMCMgr.call(SkMatrixClipStateMgr::kOther_CallType);
+#endif
+
+ // op + paint index + rrects
+ uint32_t initialOffset, size;
+ size = 2 * kUInt32Size + SkRRect::kSizeInMemory * 2;
+ initialOffset = this->addDraw(DRAW_DRRECT, &size);
+ SkASSERT(initialOffset+getPaintOffset(DRAW_DRRECT, size) == fWriter.bytesWritten());
+ this->addPaint(paint);
+ this->addRRect(outer);
+ this->addRRect(inner);
+ this->validate(initialOffset, size);
+}
+
void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) {
#ifdef SK_COLLAPSE_MATRIX_CLIP_STATE
const void* onPeekPixels(SkImageInfo*, size_t*) SK_OVERRIDE {
return NULL;
}
+ virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) SK_OVERRIDE;
// Return fontmetrics.fTop,fBottom in topbot[0,1], after they have been
// tweaked by paint.computeFastBounds().
kDrawBitmapRectToRect_DrawOp,
kDrawClear_DrawOp,
kDrawData_DrawOp,
+ kDrawDRRect_DrawOp,
kDrawOval_DrawOp,
kDrawPaint_DrawOp,
kDrawPath_DrawOp,
}
}
+static void drawDRRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+ SkGPipeState* state) {
+ SkRRect outer, inner;
+ reader->readRRect(&outer);
+ reader->readRRect(&inner);
+ if (state->shouldDraw()) {
+ canvas->drawDRRect(outer, inner, state->paint());
+ }
+}
+
static void drawPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
SkGPipeState* state) {
SkPath path;
drawBitmapRect_rp,
drawClear_rp,
drawData_rp,
+ drawDRRect_rp,
drawOval_rp,
drawPaint_rp,
drawPath_rp,
* according to slot.
*/
bool shuttleBitmap(const SkBitmap&, int32_t slot);
+
+protected:
+ virtual void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) SK_OVERRIDE;
+
private:
enum {
kNoSaveLayer = -1,
}
}
+void SkGPipeCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner,
+ const SkPaint& paint) {
+ NOTIFY_SETUP(this);
+ this->writePaint(paint);
+ if (this->needOpBytes(kSizeOfFlatRRect * 2)) {
+ this->writeOp(kDrawDRRect_DrawOp);
+ fWriter.writeRRect(outer);
+ fWriter.writeRRect(inner);
+ }
+}
+
void SkGPipeCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
NOTIFY_SETUP(this);
this->writePaint(paint);