skia2/gm/dashing.cpp
mtklein dbfd7ab108 Replace a lot of 'static const' with 'constexpr' or 'const'.
'static const' means, there must be at most one of these, and initialize it at
compile time if possible or runtime if necessary.  This leads to unexpected
code execution, and TSAN* will complain about races on the guard variables.

Generally 'constexpr' or 'const' are better choices.  Neither can cause races:
they're either intialized at compile time (constexpr) or intialized each time
independently (const).

This CL prefers constexpr where possible, and uses const where not.  It even
prefers constexpr over const where they don't make a difference... I want to have
lots of examples of constexpr for people to see and mimic.

The scoped-to-class static has nothing to do with any of this, and is not changed.

* Not yet on the bots, which use an older TSAN.

BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2300623005

Review-Url: https://codereview.chromium.org/2300623005
2016-09-01 11:24:54 -07:00

557 lines
18 KiB
C++

/*
* 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 "SkPaint.h"
#include "SkDashPathEffect.h"
static void drawline(SkCanvas* canvas, int on, int off, const SkPaint& paint,
SkScalar finalX = SkIntToScalar(600), SkScalar finalY = SkIntToScalar(0),
SkScalar phase = SkIntToScalar(0),
SkScalar startX = SkIntToScalar(0), SkScalar startY = SkIntToScalar(0)) {
SkPaint p(paint);
const SkScalar intervals[] = {
SkIntToScalar(on),
SkIntToScalar(off),
};
p.setPathEffect(SkDashPathEffect::Make(intervals, 2, phase));
canvas->drawLine(startX, startY, finalX, finalY, p);
}
// earlier bug stopped us from drawing very long single-segment dashes, because
// SkPathMeasure was skipping very small delta-T values (nearlyzero). This is
// now fixes, so this giant dash should appear.
static void show_giant_dash(SkCanvas* canvas) {
SkPaint paint;
drawline(canvas, 1, 1, paint, SkIntToScalar(20 * 1000));
}
static void show_zero_len_dash(SkCanvas* canvas) {
SkPaint paint;
drawline(canvas, 2, 2, paint, SkIntToScalar(0));
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(SkIntToScalar(2));
canvas->translate(0, SkIntToScalar(20));
drawline(canvas, 4, 4, paint, SkIntToScalar(0));
}
class DashingGM : public skiagm::GM {
public:
DashingGM() {}
protected:
SkString onShortName() {
return SkString("dashing");
}
SkISize onISize() { return SkISize::Make(640, 340); }
virtual void onDraw(SkCanvas* canvas) {
constexpr struct {
int fOnInterval;
int fOffInterval;
} gData[] = {
{ 1, 1 },
{ 4, 1 },
};
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
canvas->translate(0, SK_ScalarHalf);
for (int width = 0; width <= 2; ++width) {
for (size_t data = 0; data < SK_ARRAY_COUNT(gData); ++data) {
for (int aa = 0; aa <= 1; ++aa) {
int w = width * width * width;
paint.setAntiAlias(SkToBool(aa));
paint.setStrokeWidth(SkIntToScalar(w));
int scale = w ? w : 1;
drawline(canvas, gData[data].fOnInterval * scale,
gData[data].fOffInterval * scale,
paint);
canvas->translate(0, SkIntToScalar(20));
}
}
}
show_giant_dash(canvas);
canvas->translate(0, SkIntToScalar(20));
show_zero_len_dash(canvas);
canvas->translate(0, SkIntToScalar(20));
// Draw 0 on, 0 off dashed line
paint.setStrokeWidth(SkIntToScalar(8));
drawline(canvas, 0, 0, paint);
}
};
///////////////////////////////////////////////////////////////////////////////
static void make_unit_star(SkPath* path, int n) {
SkScalar rad = -SK_ScalarPI / 2;
const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n;
path->moveTo(0, -SK_Scalar1);
for (int i = 1; i < n; i++) {
rad += drad;
SkScalar cosV, sinV = SkScalarSinCos(rad, &cosV);
path->lineTo(cosV, sinV);
}
path->close();
}
static void make_path_line(SkPath* path, const SkRect& bounds) {
path->moveTo(bounds.left(), bounds.top());
path->lineTo(bounds.right(), bounds.bottom());
}
static void make_path_rect(SkPath* path, const SkRect& bounds) {
path->addRect(bounds);
}
static void make_path_oval(SkPath* path, const SkRect& bounds) {
path->addOval(bounds);
}
static void make_path_star(SkPath* path, const SkRect& bounds) {
make_unit_star(path, 5);
SkMatrix matrix;
matrix.setRectToRect(path->getBounds(), bounds, SkMatrix::kCenter_ScaleToFit);
path->transform(matrix);
}
class Dashing2GM : public skiagm::GM {
public:
Dashing2GM() {}
protected:
SkString onShortName() {
return SkString("dashing2");
}
SkISize onISize() { return SkISize::Make(640, 480); }
virtual void onDraw(SkCanvas* canvas) {
constexpr int gIntervals[] = {
3, // 3 dashes: each count [0] followed by intervals [1..count]
2, 10, 10,
4, 20, 5, 5, 5,
2, 2, 2
};
void (*gProc[])(SkPath*, const SkRect&) = {
make_path_line, make_path_rect, make_path_oval, make_path_star,
};
SkPaint paint;
paint.setAntiAlias(true);
paint.setStyle(SkPaint::kStroke_Style);
paint.setStrokeWidth(SkIntToScalar(6));
SkRect bounds = SkRect::MakeWH(SkIntToScalar(120), SkIntToScalar(120));
bounds.offset(SkIntToScalar(20), SkIntToScalar(20));
SkScalar dx = bounds.width() * 4 / 3;
SkScalar dy = bounds.height() * 4 / 3;
const int* intervals = &gIntervals[1];
for (int y = 0; y < gIntervals[0]; ++y) {
SkScalar vals[SK_ARRAY_COUNT(gIntervals)]; // more than enough
int count = *intervals++;
for (int i = 0; i < count; ++i) {
vals[i] = SkIntToScalar(*intervals++);
}
SkScalar phase = vals[0] / 2;
paint.setPathEffect(SkDashPathEffect::Make(vals, count, phase));
for (size_t x = 0; x < SK_ARRAY_COUNT(gProc); ++x) {
SkPath path;
SkRect r = bounds;
r.offset(x * dx, y * dy);
gProc[x](&path, r);
canvas->drawPath(path, paint);
}
}
}
};
//////////////////////////////////////////////////////////////////////////////
// Test out the on/off line dashing Chrome if fond of
class Dashing3GM : public skiagm::GM {
public:
Dashing3GM() {}
protected:
SkString onShortName() {
return SkString("dashing3");
}
SkISize onISize() { return SkISize::Make(640, 480); }
// Draw a 100x100 block of dashed lines. The horizontal ones are BW
// while the vertical ones are AA.
void drawDashedLines(SkCanvas* canvas,
SkScalar lineLength,
SkScalar phase,
SkScalar dashLength,
int strokeWidth,
bool circles) {
SkPaint p;
p.setColor(SK_ColorBLACK);
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(SkIntToScalar(strokeWidth));
if (circles) {
p.setStrokeCap(SkPaint::kRound_Cap);
}
SkScalar intervals[2] = { dashLength, dashLength };
p.setPathEffect(SkDashPathEffect::Make(intervals, 2, phase));
SkPoint pts[2];
for (int y = 0; y < 100; y += 10*strokeWidth) {
pts[0].set(0, SkIntToScalar(y));
pts[1].set(lineLength, SkIntToScalar(y));
canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p);
}
p.setAntiAlias(true);
for (int x = 0; x < 100; x += 14*strokeWidth) {
pts[0].set(SkIntToScalar(x), 0);
pts[1].set(SkIntToScalar(x), lineLength);
canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p);
}
}
virtual void onDraw(SkCanvas* canvas) {
// 1on/1off 1x1 squares with phase of 0 - points fastpath
canvas->save();
canvas->translate(2, 0);
this->drawDashedLines(canvas, 100, 0, SK_Scalar1, 1, false);
canvas->restore();
// 1on/1off 1x1 squares with phase of .5 - rects fastpath (due to partial squares)
canvas->save();
canvas->translate(112, 0);
this->drawDashedLines(canvas, 100, SK_ScalarHalf, SK_Scalar1, 1, false);
canvas->restore();
// 1on/1off 1x1 squares with phase of 1 - points fastpath
canvas->save();
canvas->translate(222, 0);
this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, false);
canvas->restore();
// 1on/1off 1x1 squares with phase of 1 and non-integer length - rects fastpath
canvas->save();
canvas->translate(332, 0);
this->drawDashedLines(canvas, 99.5f, SK_ScalarHalf, SK_Scalar1, 1, false);
canvas->restore();
// 255on/255off 1x1 squares with phase of 0 - rects fast path
canvas->save();
canvas->translate(446, 0);
this->drawDashedLines(canvas, 100, 0, SkIntToScalar(255), 1, false);
canvas->restore();
// 1on/1off 3x3 squares with phase of 0 - points fast path
canvas->save();
canvas->translate(2, 110);
this->drawDashedLines(canvas, 100, 0, SkIntToScalar(3), 3, false);
canvas->restore();
// 1on/1off 3x3 squares with phase of 1.5 - rects fast path
canvas->save();
canvas->translate(112, 110);
this->drawDashedLines(canvas, 100, 1.5f, SkIntToScalar(3), 3, false);
canvas->restore();
// 1on/1off 1x1 circles with phase of 1 - no fast path yet
canvas->save();
canvas->translate(2, 220);
this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, true);
canvas->restore();
// 1on/1off 3x3 circles with phase of 1 - no fast path yet
canvas->save();
canvas->translate(112, 220);
this->drawDashedLines(canvas, 100, 0, SkIntToScalar(3), 3, true);
canvas->restore();
// 1on/1off 1x1 squares with rotation - should break fast path
canvas->save();
canvas->translate(332+SK_ScalarRoot2Over2*100, 110+SK_ScalarRoot2Over2*100);
canvas->rotate(45);
canvas->translate(-50, -50);
this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, false);
canvas->restore();
// 3on/3off 3x1 rects - should use rect fast path regardless of phase
for (int phase = 0; phase <= 3; ++phase) {
canvas->save();
canvas->translate(SkIntToScalar(phase*110+2),
SkIntToScalar(330));
this->drawDashedLines(canvas, 100, SkIntToScalar(phase), SkIntToScalar(3), 1, false);
canvas->restore();
}
}
};
//////////////////////////////////////////////////////////////////////////////
class Dashing4GM : public skiagm::GM {
public:
Dashing4GM() {}
protected:
SkString onShortName() {
return SkString("dashing4");
}
SkISize onISize() { return SkISize::Make(640, 950); }
virtual void onDraw(SkCanvas* canvas) {
constexpr struct {
int fOnInterval;
int fOffInterval;
} gData[] = {
{ 1, 1 },
{ 4, 2 },
{ 0, 4 }, // test for zero length on interval
};
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
canvas->translate(0, SK_ScalarHalf);
for (int width = 0; width <= 2; ++width) {
for (size_t data = 0; data < SK_ARRAY_COUNT(gData); ++data) {
for (int aa = 0; aa <= 1; ++aa) {
for (int cap = 0; cap <= 1; ++cap) {
int w = width * width * width;
paint.setAntiAlias(SkToBool(aa));
paint.setStrokeWidth(SkIntToScalar(w));
SkToBool(cap) ? paint.setStrokeCap(SkPaint::kSquare_Cap)
: paint.setStrokeCap(SkPaint::kRound_Cap);
int scale = w ? w : 1;
drawline(canvas, gData[data].fOnInterval * scale,
gData[data].fOffInterval * scale,
paint);
canvas->translate(0, SkIntToScalar(20));
}
}
}
}
for (int aa = 0; aa <= 1; ++aa) {
paint.setAntiAlias(SkToBool(aa));
paint.setStrokeWidth(8.f);
paint.setStrokeCap(SkPaint::kSquare_Cap);
// Single dash element that is cut off at start and end
drawline(canvas, 32, 16, paint, 20.f, 0, 5.f);
canvas->translate(0, SkIntToScalar(20));
// Two dash elements where each one is cut off at beginning and end respectively
drawline(canvas, 32, 16, paint, 56.f, 0, 5.f);
canvas->translate(0, SkIntToScalar(20));
// Many dash elements where first and last are cut off at beginning and end respectively
drawline(canvas, 32, 16, paint, 584.f, 0, 5.f);
canvas->translate(0, SkIntToScalar(20));
// Diagonal dash line where src pnts are not axis aligned (as apposed to being diagonal from
// a canvas rotation)
drawline(canvas, 32, 16, paint, 600.f, 30.f);
canvas->translate(0, SkIntToScalar(20));
// Case where only the off interval exists on the line. Thus nothing should be drawn
drawline(canvas, 32, 16, paint, 8.f, 0.f, 40.f);
canvas->translate(0, SkIntToScalar(20));
}
}
};
//////////////////////////////////////////////////////////////////////////////
class Dashing5GM : public skiagm::GM {
public:
Dashing5GM(bool doAA) : fDoAA(doAA) {}
protected:
bool runAsBench() const override { return true; }
SkString onShortName() override {
if (fDoAA) {
return SkString("dashing5_aa");
} else {
return SkString("dashing5_bw");
}
}
SkISize onISize() override { return SkISize::Make(400, 200); }
void onDraw(SkCanvas* canvas) override {
constexpr int kOn = 4;
constexpr int kOff = 4;
constexpr int kIntervalLength = kOn + kOff;
constexpr SkColor gColors[kIntervalLength] = {
SK_ColorRED,
SK_ColorGREEN,
SK_ColorBLUE,
SK_ColorCYAN,
SK_ColorMAGENTA,
SK_ColorYELLOW,
SK_ColorGRAY,
SK_ColorDKGRAY
};
SkPaint paint;
paint.setStyle(SkPaint::kStroke_Style);
paint.setAntiAlias(fDoAA);
SkMatrix rot;
rot.setRotate(90);
SkASSERT(rot.rectStaysRect());
canvas->concat(rot);
int sign; // used to toggle the direction of the lines
int phase = 0;
for (int x = 0; x < 200; x += 10) {
paint.setStrokeWidth(SkIntToScalar(phase+1));
paint.setColor(gColors[phase]);
sign = (x % 20) ? 1 : -1;
drawline(canvas, kOn, kOff, paint,
SkIntToScalar(x), -sign * SkIntToScalar(10003),
SkIntToScalar(phase),
SkIntToScalar(x), sign * SkIntToScalar(10003));
phase = (phase + 1) % kIntervalLength;
}
for (int y = -400; y < 0; y += 10) {
paint.setStrokeWidth(SkIntToScalar(phase+1));
paint.setColor(gColors[phase]);
sign = (y % 20) ? 1 : -1;
drawline(canvas, kOn, kOff, paint,
-sign * SkIntToScalar(10003), SkIntToScalar(y),
SkIntToScalar(phase),
sign * SkIntToScalar(10003), SkIntToScalar(y));
phase = (phase + 1) % kIntervalLength;
}
}
private:
bool fDoAA;
};
DEF_SIMPLE_GM(longpathdash, canvas, 612, 612) {
SkPath lines;
for (int x = 32; x < 256; x += 16) {
for (SkScalar a = 0; a < 3.141592f * 2; a += 0.03141592f) {
SkPoint pts[2] = {
{ 256 + (float) sin(a) * x,
256 + (float) cos(a) * x },
{ 256 + (float) sin(a + 3.141592 / 3) * (x + 64),
256 + (float) cos(a + 3.141592 / 3) * (x + 64) }
};
lines.moveTo(pts[0]);
for (SkScalar i = 0; i < 1; i += 0.05f) {
lines.lineTo(pts[0].fX * (1 - i) + pts[1].fX * i,
pts[0].fY * (1 - i) + pts[1].fY * i);
}
}
}
SkPaint p;
p.setAntiAlias(true);
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(1);
const SkScalar intervals[] = { 1, 1 };
p.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 0));
canvas->translate(50, 50);
canvas->drawPath(lines, p);
}
DEF_SIMPLE_GM(longlinedash, canvas, 512, 512) {
SkPaint p;
p.setAntiAlias(true);
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(80);
const SkScalar intervals[] = { 2, 2 };
p.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 0));
canvas->drawRect(SkRect::MakeXYWH(-10000, 100, 20000, 20), p);
}
DEF_SIMPLE_GM(longwavyline, canvas, 512, 512) {
SkPaint p;
p.setAntiAlias(true);
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(2);
SkPath wavy;
wavy.moveTo(-10000, 100);
for (SkScalar i = -10000; i < 10000; i += 20) {
wavy.quadTo(i + 5, 95, i + 10, 100);
wavy.quadTo(i + 15, 105, i + 20, 100);
}
canvas->drawPath(wavy, p);
}
DEF_SIMPLE_GM(dashtextcaps, canvas, 512, 512) {
SkPaint p;
p.setAntiAlias(true);
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(10);
p.setStrokeCap(SkPaint::kRound_Cap);
p.setStrokeJoin(SkPaint::kRound_Join);
p.setTextSize(100);
p.setARGB(0xff, 0xbb, 0x00, 0x00);
sk_tool_utils::set_portable_typeface(&p);
const SkScalar intervals[] = { 12, 12 };
p.setPathEffect(SkDashPathEffect::Make(intervals, SK_ARRAY_COUNT(intervals), 0));
canvas->drawText("Sausages", 8, 10, 90, p);
canvas->drawLine(8, 120, 456, 120, p);
}
//////////////////////////////////////////////////////////////////////////////
DEF_GM(return new DashingGM;)
DEF_GM(return new Dashing2GM;)
DEF_GM(return new Dashing3GM;)
DEF_GM(return new Dashing4GM;)
DEF_GM(return new Dashing5GM(true);)
DEF_GM(return new Dashing5GM(false);)