b775e91e87
When the parallel stroke to the curve can't be computed from the intersection of the tangent lines, as straight line connects the two points instead. Allow the intersection to succeed unless the ratio isn't finite or the contribution of (1 - ratio) isn't significant. R=reed@google.com,fmalita@chromium.org BUG=skia:4603 Review URL: https://codereview.chromium.org/1484873003
474 lines
14 KiB
C++
474 lines
14 KiB
C++
/*
|
|
* Copyright 2011 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 "SkPath.h"
|
|
#include "SkRandom.h"
|
|
#include "SkDashPathEffect.h"
|
|
#include "SkParsePath.h"
|
|
|
|
#define W 400
|
|
#define H 400
|
|
#define N 50
|
|
|
|
static const SkScalar SW = SkIntToScalar(W);
|
|
static const SkScalar SH = SkIntToScalar(H);
|
|
|
|
static void rnd_rect(SkRect* r, SkPaint* paint, SkRandom& rand) {
|
|
SkScalar x = rand.nextUScalar1() * W;
|
|
SkScalar y = rand.nextUScalar1() * H;
|
|
SkScalar w = rand.nextUScalar1() * (W >> 2);
|
|
SkScalar h = rand.nextUScalar1() * (H >> 2);
|
|
SkScalar hoffset = rand.nextSScalar1();
|
|
SkScalar woffset = rand.nextSScalar1();
|
|
|
|
r->set(x, y, x + w, y + h);
|
|
r->offset(-w/2 + woffset, -h/2 + hoffset);
|
|
|
|
paint->setColor(rand.nextU());
|
|
paint->setAlpha(0xFF);
|
|
}
|
|
|
|
|
|
class StrokesGM : public skiagm::GM {
|
|
public:
|
|
StrokesGM() {}
|
|
|
|
protected:
|
|
|
|
SkString onShortName() override {
|
|
return SkString("strokes_round");
|
|
}
|
|
|
|
SkISize onISize() override {
|
|
return SkISize::Make(W, H*2);
|
|
}
|
|
|
|
void onDraw(SkCanvas* canvas) override {
|
|
SkPaint paint;
|
|
paint.setStyle(SkPaint::kStroke_Style);
|
|
paint.setStrokeWidth(SkIntToScalar(9)/2);
|
|
|
|
for (int y = 0; y < 2; y++) {
|
|
paint.setAntiAlias(!!y);
|
|
SkAutoCanvasRestore acr(canvas, true);
|
|
canvas->translate(0, SH * y);
|
|
canvas->clipRect(SkRect::MakeLTRB(
|
|
SkIntToScalar(2), SkIntToScalar(2)
|
|
, SW - SkIntToScalar(2), SH - SkIntToScalar(2)
|
|
));
|
|
|
|
SkRandom rand;
|
|
for (int i = 0; i < N; i++) {
|
|
SkRect r;
|
|
rnd_rect(&r, &paint, rand);
|
|
canvas->drawOval(r, paint);
|
|
rnd_rect(&r, &paint, rand);
|
|
canvas->drawRoundRect(r, r.width()/4, r.height()/4, paint);
|
|
rnd_rect(&r, &paint, rand);
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
typedef skiagm::GM INHERITED;
|
|
};
|
|
|
|
/* See
|
|
https://code.google.com/p/chromium/issues/detail?id=422974 and
|
|
http://jsfiddle.net/1xnku3sg/2/
|
|
*/
|
|
class ZeroLenStrokesGM : public skiagm::GM {
|
|
SkPath fMoveHfPath, fMoveZfPath, fDashedfPath, fRefPath[4];
|
|
SkPath fCubicPath, fQuadPath, fLinePath;
|
|
protected:
|
|
void onOnceBeforeDraw() override {
|
|
|
|
SkAssertResult(SkParsePath::FromSVGString("M0,0h0M10,0h0M20,0h0", &fMoveHfPath));
|
|
SkAssertResult(SkParsePath::FromSVGString("M0,0zM10,0zM20,0z", &fMoveZfPath));
|
|
SkAssertResult(SkParsePath::FromSVGString("M0,0h25", &fDashedfPath));
|
|
SkAssertResult(SkParsePath::FromSVGString("M 0 0 C 0 0 0 0 0 0", &fCubicPath));
|
|
SkAssertResult(SkParsePath::FromSVGString("M 0 0 Q 0 0 0 0", &fQuadPath));
|
|
SkAssertResult(SkParsePath::FromSVGString("M 0 0 L 0 0", &fLinePath));
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
fRefPath[0].addCircle(i * 10.f, 0, 5);
|
|
fRefPath[1].addCircle(i * 10.f, 0, 10);
|
|
fRefPath[2].addRect(i * 10.f - 4, -2, i * 10.f + 4, 6);
|
|
fRefPath[3].addRect(i * 10.f - 10, -10, i * 10.f + 10, 10);
|
|
}
|
|
}
|
|
|
|
SkString onShortName() override {
|
|
return SkString("zeroPath");
|
|
}
|
|
|
|
SkISize onISize() override {
|
|
return SkISize::Make(W, H*2);
|
|
}
|
|
|
|
void onDraw(SkCanvas* canvas) override {
|
|
SkPaint fillPaint, strokePaint, dashPaint;
|
|
fillPaint.setAntiAlias(true);
|
|
strokePaint = fillPaint;
|
|
strokePaint.setStyle(SkPaint::kStroke_Style);
|
|
for (int i = 0; i < 2; ++i) {
|
|
fillPaint.setAlpha(255);
|
|
strokePaint.setAlpha(255);
|
|
strokePaint.setStrokeWidth(i ? 8.f : 10.f);
|
|
strokePaint.setStrokeCap(i ? SkPaint::kSquare_Cap : SkPaint::kRound_Cap);
|
|
canvas->save();
|
|
canvas->translate(10 + i * 100.f, 10);
|
|
canvas->drawPath(fMoveHfPath, strokePaint);
|
|
canvas->translate(0, 20);
|
|
canvas->drawPath(fMoveZfPath, strokePaint);
|
|
dashPaint = strokePaint;
|
|
const SkScalar intervals[] = { 0, 10 };
|
|
dashPaint.setPathEffect(SkDashPathEffect::Create(intervals, 2, 0))->unref();
|
|
SkPath fillPath;
|
|
dashPaint.getFillPath(fDashedfPath, &fillPath);
|
|
canvas->translate(0, 20);
|
|
canvas->drawPath(fDashedfPath, dashPaint);
|
|
canvas->translate(0, 20);
|
|
canvas->drawPath(fRefPath[i * 2], fillPaint);
|
|
strokePaint.setStrokeWidth(20);
|
|
strokePaint.setAlpha(127);
|
|
canvas->translate(0, 50);
|
|
canvas->drawPath(fMoveHfPath, strokePaint);
|
|
canvas->translate(0, 30);
|
|
canvas->drawPath(fMoveZfPath, strokePaint);
|
|
canvas->translate(0, 30);
|
|
fillPaint.setAlpha(127);
|
|
canvas->drawPath(fRefPath[1 + i * 2], fillPaint);
|
|
canvas->translate(0, 30);
|
|
canvas->drawPath(fCubicPath, strokePaint);
|
|
canvas->translate(0, 30);
|
|
canvas->drawPath(fQuadPath, strokePaint);
|
|
canvas->translate(0, 30);
|
|
canvas->drawPath(fLinePath, strokePaint);
|
|
canvas->restore();
|
|
}
|
|
}
|
|
|
|
private:
|
|
typedef skiagm::GM INHERITED;
|
|
};
|
|
|
|
class TeenyStrokesGM : public skiagm::GM {
|
|
|
|
SkString onShortName() override {
|
|
return SkString("teenyStrokes");
|
|
}
|
|
|
|
SkISize onISize() override {
|
|
return SkISize::Make(W, H*2);
|
|
}
|
|
|
|
static void line(SkScalar scale, SkCanvas* canvas, SkColor color) {
|
|
SkPaint p;
|
|
p.setAntiAlias(true);
|
|
p.setStyle(SkPaint::kStroke_Style);
|
|
p.setColor(color);
|
|
canvas->translate(50, 0);
|
|
canvas->save();
|
|
p.setStrokeWidth(scale * 5);
|
|
canvas->scale(1 / scale, 1 / scale);
|
|
canvas->drawLine(20 * scale, 20 * scale, 20 * scale, 100 * scale, p);
|
|
canvas->drawLine(20 * scale, 20 * scale, 100 * scale, 100 * scale, p);
|
|
canvas->restore();
|
|
}
|
|
|
|
void onDraw(SkCanvas* canvas) override {
|
|
line(0.00005f, canvas, SK_ColorBLACK);
|
|
line(0.000045f, canvas, SK_ColorRED);
|
|
line(0.0000035f, canvas, SK_ColorGREEN);
|
|
line(0.000003f, canvas, SK_ColorBLUE);
|
|
line(0.000002f, canvas, SK_ColorBLACK);
|
|
}
|
|
private:
|
|
typedef skiagm::GM INHERITED;
|
|
};
|
|
|
|
DEF_SIMPLE_GM(CubicStroke, canvas, 384, 384) {
|
|
SkPaint p;
|
|
p.setAntiAlias(true);
|
|
p.setStyle(SkPaint::kStroke_Style);
|
|
p.setStrokeWidth(1.0720f);
|
|
SkPath path;
|
|
path.moveTo(-6000,-6000);
|
|
path.cubicTo(-3500,5500,-500,5500,2500,-6500);
|
|
canvas->drawPath(path, p);
|
|
p.setStrokeWidth(1.0721f);
|
|
canvas->translate(10, 10);
|
|
canvas->drawPath(path, p);
|
|
p.setStrokeWidth(1.0722f);
|
|
canvas->translate(10, 10);
|
|
canvas->drawPath(path, p);
|
|
}
|
|
|
|
class Strokes2GM : public skiagm::GM {
|
|
SkPath fPath;
|
|
protected:
|
|
void onOnceBeforeDraw() override {
|
|
SkRandom rand;
|
|
fPath.moveTo(0, 0);
|
|
for (int i = 0; i < 13; i++) {
|
|
SkScalar x = rand.nextUScalar1() * (W >> 1);
|
|
SkScalar y = rand.nextUScalar1() * (H >> 1);
|
|
fPath.lineTo(x, y);
|
|
}
|
|
}
|
|
|
|
|
|
SkString onShortName() override {
|
|
return SkString("strokes_poly");
|
|
}
|
|
|
|
SkISize onISize() override {
|
|
return SkISize::Make(W, H*2);
|
|
}
|
|
|
|
static void rotate(SkScalar angle, SkScalar px, SkScalar py, SkCanvas* canvas) {
|
|
SkMatrix matrix;
|
|
matrix.setRotate(angle, px, py);
|
|
canvas->concat(matrix);
|
|
}
|
|
|
|
void onDraw(SkCanvas* canvas) override {
|
|
canvas->drawColor(SK_ColorWHITE);
|
|
|
|
SkPaint paint;
|
|
paint.setStyle(SkPaint::kStroke_Style);
|
|
paint.setStrokeWidth(SkIntToScalar(9)/2);
|
|
|
|
for (int y = 0; y < 2; y++) {
|
|
paint.setAntiAlias(!!y);
|
|
SkAutoCanvasRestore acr(canvas, true);
|
|
canvas->translate(0, SH * y);
|
|
canvas->clipRect(SkRect::MakeLTRB(SkIntToScalar(2),
|
|
SkIntToScalar(2),
|
|
SW - SkIntToScalar(2),
|
|
SH - SkIntToScalar(2)));
|
|
|
|
SkRandom rand;
|
|
for (int i = 0; i < N/2; i++) {
|
|
SkRect r;
|
|
rnd_rect(&r, &paint, rand);
|
|
rotate(SkIntToScalar(15), SW/2, SH/2, canvas);
|
|
canvas->drawPath(fPath, paint);
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
typedef skiagm::GM INHERITED;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
static SkRect inset(const SkRect& r) {
|
|
SkRect rr(r);
|
|
rr.inset(r.width()/10, r.height()/10);
|
|
return rr;
|
|
}
|
|
|
|
class Strokes3GM : public skiagm::GM {
|
|
static void make0(SkPath* path, const SkRect& bounds, SkString* title) {
|
|
path->addRect(bounds, SkPath::kCW_Direction);
|
|
path->addRect(inset(bounds), SkPath::kCW_Direction);
|
|
title->set("CW CW");
|
|
}
|
|
|
|
static void make1(SkPath* path, const SkRect& bounds, SkString* title) {
|
|
path->addRect(bounds, SkPath::kCW_Direction);
|
|
path->addRect(inset(bounds), SkPath::kCCW_Direction);
|
|
title->set("CW CCW");
|
|
}
|
|
|
|
static void make2(SkPath* path, const SkRect& bounds, SkString* title) {
|
|
path->addOval(bounds, SkPath::kCW_Direction);
|
|
path->addOval(inset(bounds), SkPath::kCW_Direction);
|
|
title->set("CW CW");
|
|
}
|
|
|
|
static void make3(SkPath* path, const SkRect& bounds, SkString* title) {
|
|
path->addOval(bounds, SkPath::kCW_Direction);
|
|
path->addOval(inset(bounds), SkPath::kCCW_Direction);
|
|
title->set("CW CCW");
|
|
}
|
|
|
|
static void make4(SkPath* path, const SkRect& bounds, SkString* title) {
|
|
path->addRect(bounds, SkPath::kCW_Direction);
|
|
SkRect r = bounds;
|
|
r.inset(bounds.width() / 10, -bounds.height() / 10);
|
|
path->addOval(r, SkPath::kCW_Direction);
|
|
title->set("CW CW");
|
|
}
|
|
|
|
static void make5(SkPath* path, const SkRect& bounds, SkString* title) {
|
|
path->addRect(bounds, SkPath::kCW_Direction);
|
|
SkRect r = bounds;
|
|
r.inset(bounds.width() / 10, -bounds.height() / 10);
|
|
path->addOval(r, SkPath::kCCW_Direction);
|
|
title->set("CW CCW");
|
|
}
|
|
|
|
public:
|
|
Strokes3GM() {}
|
|
|
|
protected:
|
|
|
|
SkString onShortName() override {
|
|
return SkString("strokes3");
|
|
}
|
|
|
|
SkISize onISize() override {
|
|
return SkISize::Make(1500, 1500);
|
|
}
|
|
|
|
void onDraw(SkCanvas* canvas) override {
|
|
SkPaint origPaint;
|
|
origPaint.setAntiAlias(true);
|
|
origPaint.setStyle(SkPaint::kStroke_Style);
|
|
SkPaint fillPaint(origPaint);
|
|
fillPaint.setColor(SK_ColorRED);
|
|
SkPaint strokePaint(origPaint);
|
|
strokePaint.setColor(sk_tool_utils::color_to_565(0xFF4444FF));
|
|
|
|
void (*procs[])(SkPath*, const SkRect&, SkString*) = {
|
|
make0, make1, make2, make3, make4, make5
|
|
};
|
|
|
|
canvas->translate(SkIntToScalar(20), SkIntToScalar(80));
|
|
|
|
SkRect bounds = SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(50));
|
|
SkScalar dx = bounds.width() * 4/3;
|
|
SkScalar dy = bounds.height() * 5;
|
|
|
|
for (size_t i = 0; i < SK_ARRAY_COUNT(procs); ++i) {
|
|
SkPath orig;
|
|
SkString str;
|
|
procs[i](&orig, bounds, &str);
|
|
|
|
canvas->save();
|
|
for (int j = 0; j < 13; ++j) {
|
|
strokePaint.setStrokeWidth(SK_Scalar1 * j * j);
|
|
canvas->drawPath(orig, strokePaint);
|
|
canvas->drawPath(orig, origPaint);
|
|
SkPath fill;
|
|
strokePaint.getFillPath(orig, &fill);
|
|
canvas->drawPath(fill, fillPaint);
|
|
canvas->translate(dx + strokePaint.getStrokeWidth(), 0);
|
|
}
|
|
canvas->restore();
|
|
canvas->translate(0, dy);
|
|
}
|
|
}
|
|
|
|
private:
|
|
typedef skiagm::GM INHERITED;
|
|
};
|
|
|
|
class Strokes4GM : public skiagm::GM {
|
|
public:
|
|
Strokes4GM() {}
|
|
|
|
protected:
|
|
|
|
SkString onShortName() override {
|
|
return SkString("strokes_zoomed");
|
|
}
|
|
|
|
SkISize onISize() override {
|
|
return SkISize::Make(W, H*2);
|
|
}
|
|
|
|
void onDraw(SkCanvas* canvas) override {
|
|
SkPaint paint;
|
|
paint.setStyle(SkPaint::kStroke_Style);
|
|
paint.setStrokeWidth(0.055f);
|
|
|
|
canvas->scale(1000, 1000);
|
|
canvas->drawCircle(0, 2, 1.97f, paint);
|
|
}
|
|
|
|
private:
|
|
typedef skiagm::GM INHERITED;
|
|
};
|
|
|
|
// Test stroking for curves that produce degenerate tangents when t is 0 or 1 (see bug 4191)
|
|
class Strokes5GM : public skiagm::GM {
|
|
public:
|
|
Strokes5GM() {}
|
|
|
|
protected:
|
|
|
|
SkString onShortName() override {
|
|
return SkString("zero_control_stroke");
|
|
}
|
|
|
|
SkISize onISize() override {
|
|
return SkISize::Make(W, H*2);
|
|
}
|
|
|
|
void onDraw(SkCanvas* canvas) override {
|
|
SkPaint p;
|
|
p.setColor(SK_ColorRED);
|
|
p.setAntiAlias(true);
|
|
p.setStyle(SkPaint::kStroke_Style);
|
|
p.setStrokeWidth(40);
|
|
p.setStrokeCap(SkPaint::kButt_Cap);
|
|
|
|
SkPath path;
|
|
path.moveTo(157.474f,111.753f);
|
|
path.cubicTo(128.5f,111.5f,35.5f,29.5f,35.5f,29.5f);
|
|
canvas->drawPath(path, p);
|
|
path.reset();
|
|
path.moveTo(250, 50);
|
|
path.quadTo(280, 80, 280, 80);
|
|
canvas->drawPath(path, p);
|
|
path.reset();
|
|
path.moveTo(150, 50);
|
|
path.conicTo(180, 80, 180, 80, 0.707f);
|
|
canvas->drawPath(path, p);
|
|
|
|
path.reset();
|
|
path.moveTo(157.474f,311.753f);
|
|
path.cubicTo(157.474f,311.753f,85.5f,229.5f,35.5f,229.5f);
|
|
canvas->drawPath(path, p);
|
|
path.reset();
|
|
path.moveTo(280, 250);
|
|
path.quadTo(280, 250, 310, 280);
|
|
canvas->drawPath(path, p);
|
|
path.reset();
|
|
path.moveTo(180, 250);
|
|
path.conicTo(180, 250, 210, 280, 0.707f);
|
|
canvas->drawPath(path, p);
|
|
}
|
|
|
|
private:
|
|
typedef skiagm::GM INHERITED;
|
|
};
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
static skiagm::GM* F0(void*) { return new StrokesGM; }
|
|
static skiagm::GM* F1(void*) { return new Strokes2GM; }
|
|
static skiagm::GM* F2(void*) { return new Strokes3GM; }
|
|
static skiagm::GM* F3(void*) { return new Strokes4GM; }
|
|
static skiagm::GM* F4(void*) { return new Strokes5GM; }
|
|
|
|
static skiagm::GMRegistry R0(F0);
|
|
static skiagm::GMRegistry R1(F1);
|
|
static skiagm::GMRegistry R2(F2);
|
|
static skiagm::GMRegistry R3(F3);
|
|
static skiagm::GMRegistry R4(F4);
|
|
|
|
DEF_GM( return new ZeroLenStrokesGM; )
|
|
DEF_GM( return new TeenyStrokesGM; )
|