--- /dev/null
+/*
+ * Copyright 2012 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 "SkPath.h"
+
+#define STROKE_WIDTH SkIntToScalar(20)
+
+static void draw_path(SkCanvas* canvas, const SkPath& path, const SkRect& rect, SkPaint::Join join) {
+ SkPaint paint;
+ paint.setAntiAlias(true);
+ paint.setStyle(SkPaint::kStroke_Style);
+
+ paint.setColor(SK_ColorGRAY);
+ paint.setStrokeWidth(STROKE_WIDTH);
+ paint.setStrokeJoin(join);
+ canvas->drawRect(rect, paint);
+
+ paint.setStrokeWidth(0);
+ paint.setColor(SK_ColorRED);
+ canvas->drawPath(path, paint);
+
+ paint.setStrokeWidth(3);
+ paint.setStrokeJoin(SkPaint::kMiter_Join);
+ int n = path.countPoints();
+ SkAutoTArray<SkPoint> points(n);
+ path.getPoints(points.get(), n);
+ canvas->drawPoints(SkCanvas::kPoints_PointMode, n, points.get(), paint);
+}
+
+/*
+ * Test calling SkStroker for rectangles. Cases to cover:
+ *
+ * geometry: normal, small (smaller than stroke-width), empty, inverted
+ * joint-type for the corners
+ */
+class StrokeRectGM : public skiagm::GM {
+public:
+ StrokeRectGM() {}
+
+protected:
+ virtual SkString onShortName() {
+ return SkString("strokerect");
+ }
+
+ virtual SkISize onISize() {
+ return SkISize::Make(1024, 480);
+ }
+
+ virtual void onDraw(SkCanvas* canvas) {
+ canvas->drawColor(SK_ColorWHITE);
+ canvas->translate(STROKE_WIDTH*3/2, STROKE_WIDTH*3/2);
+
+ SkPaint paint;
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(STROKE_WIDTH);
+
+ static const SkPaint::Join gJoins[] = {
+ SkPaint::kMiter_Join, SkPaint::kRound_Join, SkPaint::kBevel_Join
+ };
+
+ static const SkScalar W = 80;
+ static const SkScalar H = 110;
+ static const SkRect gRects[] = {
+ { 0, 0, W, H },
+ { W, 0, 0, H },
+ { 0, H, W, 0 },
+ { 0, 0, STROKE_WIDTH, H },
+ { 0, 0, W, STROKE_WIDTH },
+ { 0, 0, STROKE_WIDTH/2, STROKE_WIDTH/2 },
+ { 0, 0, W, 0 },
+ { 0, 0, 0, H },
+ { 0, 0, 0, 0 },
+ };
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gJoins); ++i) {
+ SkPaint::Join join = gJoins[i];
+ paint.setStrokeJoin(join);
+
+ SkAutoCanvasRestore acr(canvas, true);
+ for (size_t j = 0; j < SK_ARRAY_COUNT(gRects); ++j) {
+ const SkRect& r = gRects[j];
+
+ SkPath path, fillPath;
+ path.addRect(r);
+ paint.getFillPath(path, &fillPath);
+ draw_path(canvas, fillPath, r, join);
+
+ canvas->translate(W + 2 * STROKE_WIDTH, 0);
+ }
+ acr.restore();
+ canvas->translate(0, H + 2 * STROKE_WIDTH);
+ }
+ }
+
+private:
+ typedef GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+DEF_GM(return new StrokeRectGM;)
+
'../gm/simpleaaclip.cpp',
'../gm/srcmode.cpp',
'../gm/strokefill.cpp',
+ '../gm/strokerect.cpp',
'../gm/strokerects.cpp',
'../gm/strokes.cpp',
'../gm/tablecolorfilter.cpp',
'../tests/SrcOverTest.cpp',
'../tests/StreamTest.cpp',
'../tests/StringTest.cpp',
+ '../tests/StrokeTest.cpp',
'../tests/TDLinkedListTest.cpp',
'../tests/Test.cpp',
'../tests/Test.h',
-
/*
* Copyright 2008 The Android Open Source Project
*
* found in the LICENSE file.
*/
-
#include "SkStrokerPriv.h"
#include "SkGeometry.h"
#include "SkPath.h"
return;
}
+ // If src is really a rect, call our specialty strokeRect() method
+ {
+ SkRect rect;
+ if (src.isRect(&rect)) {
+ this->strokeRect(rect, dst);
+ return;
+ }
+ }
+
#ifdef SK_SCALAR_IS_FIXED
void (*proc)(SkPoint pts[], int count) = identity_proc;
if (needs_to_shrink(src)) {
}
}
+static SkPath::Direction reverse_direction(SkPath::Direction dir) {
+ SkASSERT(SkPath::kUnknown_Direction != dir);
+ return SkPath::kCW_Direction == dir ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
+}
+
+static void addBevel(SkPath* path, const SkRect& r, const SkRect& outer, SkPath::Direction dir) {
+ SkPoint pts[8];
+
+ if (SkPath::kCW_Direction == dir) {
+ pts[0].set(r.fLeft, outer.fTop);
+ pts[1].set(r.fRight, outer.fTop);
+ pts[2].set(outer.fRight, r.fTop);
+ pts[3].set(outer.fRight, r.fBottom);
+ pts[4].set(r.fRight, outer.fBottom);
+ pts[5].set(r.fLeft, outer.fBottom);
+ pts[6].set(outer.fLeft, r.fBottom);
+ pts[7].set(outer.fLeft, r.fTop);
+ } else {
+ pts[7].set(r.fLeft, outer.fTop);
+ pts[6].set(r.fRight, outer.fTop);
+ pts[5].set(outer.fRight, r.fTop);
+ pts[4].set(outer.fRight, r.fBottom);
+ pts[3].set(r.fRight, outer.fBottom);
+ pts[2].set(r.fLeft, outer.fBottom);
+ pts[1].set(outer.fLeft, r.fBottom);
+ pts[0].set(outer.fLeft, r.fTop);
+ }
+ path->addPoly(pts, 8, true);
+}
+
+void SkStroke::strokeRect(const SkRect& origRect, SkPath* dst) const {
+ SkASSERT(dst != NULL);
+ dst->reset();
+
+ SkScalar radius = SkScalarHalf(fWidth);
+ if (radius <= 0) {
+ return;
+ }
+
+ SkPath::Direction dir = SkPath::kCW_Direction;
+ SkScalar rw = origRect.width();
+ SkScalar rh = origRect.height();
+ if ((rw < 0) ^ (rh < 0)) {
+ dir = SkPath::kCCW_Direction;
+ }
+ SkRect rect(origRect);
+ rect.sort();
+ // reassign these, now that we know they'll be >= 0
+ rw = rect.width();
+ rh = rect.height();
+
+ SkRect r(rect);
+ r.outset(radius, radius);
+
+ SkPaint::Join join = (SkPaint::Join)fJoin;
+ if (SkPaint::kMiter_Join == join && fMiterLimit < SK_ScalarSqrt2) {
+ join = SkPaint::kBevel_Join;
+ }
+
+ switch (fJoin) {
+ case SkPaint::kMiter_Join:
+ dst->addRect(r, dir);
+ break;
+ case SkPaint::kBevel_Join:
+ addBevel(dst, rect, r, dir);
+ break;
+ case SkPaint::kRound_Join:
+ dst->addRoundRect(r, radius, radius);
+ break;
+ }
+
+ if (fWidth < SkMinScalar(rw, rh)) {
+ r = rect;
+ r.inset(radius, radius);
+ dst->addRect(r, reverse_direction(dir));
+ }
+}
+
bool getDoFill() const { return SkToBool(fDoFill); }
void setDoFill(bool doFill) { fDoFill = SkToU8(doFill); }
+ void strokeRect(const SkRect&, SkPath*) const;
void strokePath(const SkPath& path, SkPath*) const;
////////////////////////////////////////////////////////////////
--- /dev/null
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRect.h"
+#include "SkStroke.h"
+
+static bool equal(const SkRect& a, const SkRect& b) {
+ return SkScalarNearlyEqual(a.left(), b.left()) &&
+ SkScalarNearlyEqual(a.top(), b.top()) &&
+ SkScalarNearlyEqual(a.right(), b.right()) &&
+ SkScalarNearlyEqual(a.bottom(), b.bottom());
+}
+
+static void test_strokerect(skiatest::Reporter* reporter) {
+ const SkScalar width = SkIntToScalar(10);
+ SkPaint paint;
+
+ paint.setStyle(SkPaint::kStroke_Style);
+ paint.setStrokeWidth(width);
+
+ SkRect r = { 0, 0, SkIntToScalar(200), SkIntToScalar(100) };
+
+ SkRect outer(r);
+ outer.outset(width/2, width/2);
+
+ static const SkPaint::Join joins[] = {
+ SkPaint::kMiter_Join, SkPaint::kRound_Join, SkPaint::kBevel_Join
+ };
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(joins); ++i) {
+ paint.setStrokeJoin(joins[i]);
+
+ SkPath path, fillPath;
+ path.addRect(r);
+ paint.getFillPath(path, &fillPath);
+
+ REPORTER_ASSERT(reporter, equal(outer, fillPath.getBounds()));
+
+ bool isMiter = SkPaint::kMiter_Join == joins[i];
+ SkRect nested[2];
+ REPORTER_ASSERT(reporter, fillPath.isNestedRects(nested) == isMiter);
+ if (isMiter) {
+ SkRect inner(r);
+ inner.inset(width/2, width/2);
+ REPORTER_ASSERT(reporter, equal(nested[0], outer));
+ REPORTER_ASSERT(reporter, equal(nested[1], inner));
+ }
+ }
+}
+
+static void TestStroke(skiatest::Reporter* reporter) {
+ test_strokerect(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Stroke", TestStrokeClass, TestStroke)
+