2016-06-15 16:13:34 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2016 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
2018-08-08 15:36:17 +00:00
|
|
|
#include "Sample.h"
|
2016-06-15 16:13:34 +00:00
|
|
|
#include "SkCanvas.h"
|
|
|
|
#include "SkPath.h"
|
|
|
|
|
2016-06-30 17:04:21 +00:00
|
|
|
#include <iostream>
|
2016-06-15 16:13:34 +00:00
|
|
|
#include <cmath>
|
|
|
|
|
|
|
|
#define PI SK_ScalarPI
|
|
|
|
|
|
|
|
#define LIN_SEGMENTS 10
|
|
|
|
|
2018-08-08 15:36:17 +00:00
|
|
|
class OverstrokeView : public Sample {
|
2016-06-15 16:13:34 +00:00
|
|
|
public:
|
|
|
|
SkScalar fStroke;
|
|
|
|
int fPathType; // super lazy enum
|
|
|
|
bool fClosePath;
|
|
|
|
bool fDrawFillPath;
|
2016-06-30 17:04:21 +00:00
|
|
|
bool fDumpHex;
|
2016-06-15 16:13:34 +00:00
|
|
|
OverstrokeView() {
|
|
|
|
fStroke = 5;
|
|
|
|
fPathType = 0;
|
|
|
|
fClosePath = false;
|
|
|
|
fDrawFillPath = false;
|
2016-06-30 17:04:21 +00:00
|
|
|
fDumpHex = false;
|
2016-06-15 16:13:34 +00:00
|
|
|
this->setBGColor(0xFFFFFFFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2018-08-08 15:36:17 +00:00
|
|
|
bool onQuery(Sample::Event* evt) override {
|
|
|
|
if (Sample::TitleQ(*evt)) {
|
|
|
|
Sample::TitleR(evt, "PathOverstroke");
|
2016-06-15 16:13:34 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
SkUnichar uni;
|
2018-08-08 15:36:17 +00:00
|
|
|
if (Sample::CharQ(*evt, &uni)) {
|
2016-06-15 16:13:34 +00:00
|
|
|
switch (uni) {
|
|
|
|
case ',':
|
|
|
|
fStroke += 1.0;
|
|
|
|
return true;
|
|
|
|
case '.':
|
|
|
|
fStroke -= 1.0;
|
|
|
|
return true;
|
|
|
|
case 'x':
|
2016-06-30 17:04:21 +00:00
|
|
|
fPathType = (fPathType + 1) % 4;
|
2016-06-15 16:13:34 +00:00
|
|
|
return true;
|
|
|
|
case 'c':
|
|
|
|
fClosePath = !fClosePath;
|
|
|
|
return true;
|
|
|
|
case 'f':
|
|
|
|
fDrawFillPath = !fDrawFillPath;
|
|
|
|
return true;
|
2016-06-30 17:04:21 +00:00
|
|
|
case 'D':
|
|
|
|
fDumpHex = !fDumpHex;
|
|
|
|
return true;
|
2016-06-15 16:13:34 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return this->INHERITED::onQuery(evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
SkPath quadPath(SkPoint p1, SkPoint p2) {
|
|
|
|
SkASSERT(p1.y() == p2.y());
|
|
|
|
|
|
|
|
SkPath path;
|
|
|
|
path.moveTo(p1);
|
|
|
|
path.lineTo(p2);
|
|
|
|
|
|
|
|
SkPoint p3 = SkPoint::Make((p1.x() + p2.x()) / 2.0f, p1.y() * 0.7f);
|
|
|
|
|
|
|
|
path.quadTo(p3, p1);
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2016-06-30 17:04:21 +00:00
|
|
|
SkPath cubicPath(SkPoint p1, SkPoint p2) {
|
|
|
|
SkASSERT(p1.y() == p2.y());
|
|
|
|
|
|
|
|
SkPath path;
|
|
|
|
path.moveTo(p1);
|
|
|
|
|
|
|
|
SkPoint p3 = SkPoint::Make((p1.x() + p2.x()) / 3.0f, p1.y() * 0.7f);
|
|
|
|
SkPoint p4 = SkPoint::Make(2.0f*(p1.x() + p2.x()) / 3.0f, p1.y() * 1.5f);
|
|
|
|
|
|
|
|
path.cubicTo(p3, p4, p2);
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2016-06-15 16:13:34 +00:00
|
|
|
SkPath linSemicirclePath(SkPoint p1, SkPoint p2) {
|
|
|
|
SkASSERT(p1.y() == p2.y());
|
|
|
|
|
|
|
|
SkPath path;
|
|
|
|
path.moveTo(p1);
|
|
|
|
path.lineTo(p2);
|
|
|
|
|
|
|
|
SkPoint pt;
|
|
|
|
|
|
|
|
for (int i = 0; i < LIN_SEGMENTS; i++) {
|
|
|
|
float theta = i * PI / (LIN_SEGMENTS);
|
|
|
|
SkScalar x = 65 + 15 * cos(theta);
|
|
|
|
SkScalar y = 50 - 15 * sin(theta);
|
|
|
|
pt = SkPoint::Make(x, y);
|
|
|
|
path.lineTo(pt);
|
|
|
|
}
|
|
|
|
path.lineTo(p1);
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
SkPath rectPath(SkPoint p1) {
|
|
|
|
SkRect r = SkRect::MakeXYWH(p1.fX, p1.fY, 20, 20);
|
|
|
|
SkPath path;
|
|
|
|
path.addRect(r);
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
|
|
|
void onDrawContent(SkCanvas* canvas) override {
|
|
|
|
const float SCALE = 1;
|
|
|
|
|
|
|
|
canvas->translate(30, 40);
|
|
|
|
canvas->scale(SCALE, SCALE);
|
|
|
|
|
|
|
|
SkPoint p1 = SkPoint::Make(50, 50);
|
|
|
|
SkPoint p2 = SkPoint::Make(80, 50);
|
|
|
|
|
|
|
|
SkPath path;
|
|
|
|
switch (fPathType) {
|
|
|
|
case 0:
|
|
|
|
path = quadPath(p1, p2);
|
|
|
|
break;
|
|
|
|
case 1:
|
2016-06-30 17:04:21 +00:00
|
|
|
path = cubicPath(p1, p2);
|
2016-06-15 16:13:34 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
path = rectPath(p1);
|
|
|
|
break;
|
2016-06-30 17:04:21 +00:00
|
|
|
case 3:
|
|
|
|
path = linSemicirclePath(p1, p2);
|
|
|
|
break;
|
2016-06-15 16:13:34 +00:00
|
|
|
default:
|
|
|
|
path = quadPath(p1, p2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fClosePath) {
|
|
|
|
path.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
SkPaint p;
|
|
|
|
p.setColor(SK_ColorRED);
|
|
|
|
p.setAntiAlias(true);
|
|
|
|
p.setStyle(SkPaint::kStroke_Style);
|
|
|
|
p.setStrokeWidth(fStroke);
|
|
|
|
|
|
|
|
canvas->drawPath(path, p);
|
|
|
|
|
2016-06-30 17:04:21 +00:00
|
|
|
if (fDumpHex) {
|
|
|
|
std::cerr << "path dumpHex" << std::endl;
|
|
|
|
path.dumpHex();
|
|
|
|
}
|
|
|
|
|
|
|
|
SkPaint hairp;
|
|
|
|
hairp.setColor(SK_ColorBLACK);
|
|
|
|
hairp.setAntiAlias(true);
|
|
|
|
hairp.setStyle(SkPaint::kStroke_Style);
|
|
|
|
|
2016-06-15 16:13:34 +00:00
|
|
|
if (fDrawFillPath) {
|
|
|
|
SkPath fillpath;
|
|
|
|
p.getFillPath(path, &fillpath);
|
|
|
|
|
2016-06-30 17:04:21 +00:00
|
|
|
canvas->drawPath(fillpath, hairp);
|
2016-06-15 16:13:34 +00:00
|
|
|
|
2016-06-30 17:04:21 +00:00
|
|
|
if (fDumpHex) {
|
|
|
|
std::cerr << "fillpath dumpHex" << std::endl;
|
|
|
|
fillpath.dumpHex();
|
|
|
|
}
|
2016-06-15 16:13:34 +00:00
|
|
|
}
|
2016-06-30 17:04:21 +00:00
|
|
|
|
|
|
|
if (fDumpHex) {
|
|
|
|
std::cerr << std::endl;
|
|
|
|
|
|
|
|
fDumpHex = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// draw original path with green hairline
|
|
|
|
hairp.setColor(SK_ColorGREEN);
|
|
|
|
canvas->drawPath(path, hairp);
|
2016-06-15 16:13:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2018-08-08 15:36:17 +00:00
|
|
|
typedef Sample INHERITED;
|
2016-06-15 16:13:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2018-08-08 15:36:17 +00:00
|
|
|
DEF_SAMPLE( return new OverstrokeView(); )
|