c37b386886
Note: in variedtext.cpp:66 changed static_assert to SkASSERT. Change-Id: I853a2e5563c90c9dde5d6ba5443cc73b664b493d Reviewed-on: https://skia-review.googlesource.com/c/skia/+/551876 Commit-Queue: Herb Derby <herb@google.com> Reviewed-by: John Stiles <johnstiles@google.com>
352 lines
12 KiB
C++
352 lines
12 KiB
C++
/*
|
|
* Copyright 2016 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "gm/gm.h"
|
|
#include "include/core/SkCanvas.h"
|
|
#include "include/core/SkColor.h"
|
|
#include "include/core/SkPaint.h"
|
|
#include "include/core/SkPathBuilder.h"
|
|
#include "include/core/SkPathEffect.h"
|
|
#include "include/core/SkRect.h"
|
|
#include "include/core/SkScalar.h"
|
|
#include "include/core/SkTypes.h"
|
|
#include "include/effects/SkDashPathEffect.h"
|
|
#include "include/private/SkFloatBits.h"
|
|
#include "include/private/SkTArray.h"
|
|
|
|
#include <functional>
|
|
|
|
#include "include/effects/SkStrokeAndFillPathEffect.h"
|
|
static void set_strokeandfill(SkPaint* paint) {
|
|
SkASSERT(paint->getPathEffect() == nullptr);
|
|
paint->setPathEffect(SkStrokeAndFillPathEffect::Make());
|
|
paint->setStroke(true);
|
|
}
|
|
|
|
constexpr SkScalar kStarts[] = {0.f, 10.f, 30.f, 45.f, 90.f, 165.f, 180.f, 270.f};
|
|
constexpr SkScalar kSweeps[] = {1.f, 45.f, 90.f, 130.f, 180.f, 184.f, 300.f, 355.f};
|
|
constexpr SkScalar kDiameter = 40.f;
|
|
constexpr SkRect kRect = {0.f, 0.f, kDiameter, kDiameter};
|
|
constexpr int kW = 1000;
|
|
constexpr int kH = 1000;
|
|
constexpr SkScalar kPad = 20.f;
|
|
|
|
void draw_arcs(SkCanvas* canvas, std::function<void(SkPaint*)> configureStyle) {
|
|
// Draws grid of arcs with different start/sweep angles in red and their complement arcs in
|
|
// blue.
|
|
auto drawGrid = [canvas, &configureStyle] (SkScalar x, SkScalar y, bool useCenter, bool aa) {
|
|
SkPaint p0;
|
|
p0.setColor(SK_ColorRED);
|
|
p0.setAntiAlias(aa);
|
|
// Set a reasonable stroke width that configureStyle can override.
|
|
p0.setStrokeWidth(15.f);
|
|
SkPaint p1 = p0;
|
|
p1.setColor(SK_ColorBLUE);
|
|
// Use alpha so we see magenta on overlap between arc and its complement.
|
|
p0.setAlpha(100);
|
|
p1.setAlpha(100);
|
|
configureStyle(&p0);
|
|
configureStyle(&p1);
|
|
|
|
canvas->save();
|
|
canvas->translate(kPad + x, kPad + y);
|
|
for (auto start : kStarts) {
|
|
canvas->save();
|
|
for (auto sweep : kSweeps) {
|
|
canvas->drawArc(kRect, start, sweep, useCenter, p0);
|
|
canvas->drawArc(kRect, start, -(360.f - sweep), useCenter, p1);
|
|
canvas->translate(kRect.width() + kPad, 0.f);
|
|
}
|
|
canvas->restore();
|
|
canvas->translate(0, kRect.height() + kPad);
|
|
}
|
|
canvas->restore();
|
|
};
|
|
// Draw a grids for combo of enabling/disabling aa and using center.
|
|
constexpr SkScalar kGridW = kW / 2.f;
|
|
constexpr SkScalar kGridH = kH / 2.f;
|
|
drawGrid(0.f , 0.f , false, false);
|
|
drawGrid(kGridW, 0.f , true , false);
|
|
drawGrid(0.f , kGridH, false, true );
|
|
drawGrid(kGridW, kGridH, true , true );
|
|
// Draw separators between the grids.
|
|
SkPaint linePaint;
|
|
linePaint.setAntiAlias(true);
|
|
linePaint.setColor(SK_ColorBLACK);
|
|
canvas->drawLine(kGridW, 0.f , kGridW, SkIntToScalar(kH), linePaint);
|
|
canvas->drawLine(0.f , kGridH, SkIntToScalar(kW), kGridH, linePaint);
|
|
}
|
|
|
|
#define DEF_ARC_GM(name) DEF_SIMPLE_GM(circular_arcs_##name, canvas, kW, kH)
|
|
|
|
DEF_ARC_GM(fill) {
|
|
auto setFill = [] (SkPaint*p) { p->setStroke(false); };
|
|
draw_arcs(canvas, setFill);
|
|
}
|
|
|
|
DEF_ARC_GM(hairline) {
|
|
auto setHairline = [] (SkPaint* p) {
|
|
p->setStroke(true);
|
|
p->setStrokeWidth(0.f);
|
|
};
|
|
draw_arcs(canvas, setHairline);
|
|
}
|
|
|
|
DEF_ARC_GM(stroke_butt) {
|
|
auto setStroke = [](SkPaint* p) {
|
|
p->setStroke(true);
|
|
p->setStrokeCap(SkPaint::kButt_Cap);
|
|
};
|
|
draw_arcs(canvas, setStroke);
|
|
}
|
|
|
|
DEF_ARC_GM(stroke_square) {
|
|
auto setStroke = [] (SkPaint* p) {
|
|
p->setStroke(true);
|
|
p->setStrokeCap(SkPaint::kSquare_Cap);
|
|
};
|
|
draw_arcs(canvas, setStroke);
|
|
}
|
|
|
|
DEF_ARC_GM(stroke_round) {
|
|
auto setStroke = [] (SkPaint* p) {
|
|
p->setStroke(true);
|
|
p->setStrokeCap(SkPaint::kRound_Cap);
|
|
};
|
|
draw_arcs(canvas, setStroke);
|
|
}
|
|
|
|
DEF_ARC_GM(stroke_and_fill_butt) {
|
|
auto setStroke = [] (SkPaint* p) {
|
|
set_strokeandfill(p);
|
|
p->setStrokeCap(SkPaint::kButt_Cap);
|
|
};
|
|
draw_arcs(canvas, setStroke);
|
|
}
|
|
|
|
DEF_ARC_GM(stroke_and_fill_square) {
|
|
auto setStroke = [] (SkPaint* p) {
|
|
set_strokeandfill(p);
|
|
p->setStrokeCap(SkPaint::kSquare_Cap);
|
|
};
|
|
draw_arcs(canvas, setStroke);
|
|
}
|
|
|
|
DEF_ARC_GM(stroke_and_fill_round) {
|
|
auto setStroke = [] (SkPaint* p) {
|
|
set_strokeandfill(p);
|
|
p->setStrokeCap(SkPaint::kRound_Cap);
|
|
};
|
|
draw_arcs(canvas, setStroke);
|
|
}
|
|
|
|
DEF_SIMPLE_GM(circular_arcs_weird, canvas, 1000, 400) {
|
|
constexpr SkScalar kS = 50;
|
|
struct Arc {
|
|
SkRect fOval;
|
|
SkScalar fStart;
|
|
SkScalar fSweep;
|
|
};
|
|
const Arc noDrawArcs[] = {
|
|
// no sweep
|
|
{SkRect::MakeWH(kS, kS), 0, 0},
|
|
// empty rect in x
|
|
{SkRect::MakeWH(-kS, kS), 0, 90},
|
|
// empty rect in y
|
|
{SkRect::MakeWH(kS, -kS), 0, 90},
|
|
// empty rect in x and y
|
|
{SkRect::MakeWH( 0, 0), 0, 90},
|
|
};
|
|
const Arc arcs[] = {
|
|
// large start
|
|
{SkRect::MakeWH(kS, kS), 810.f, 90.f},
|
|
// large negative start
|
|
{SkRect::MakeWH(kS, kS), -810.f, 90.f},
|
|
// exactly 360 sweep
|
|
{SkRect::MakeWH(kS, kS), 0.f, 360.f},
|
|
// exactly -360 sweep
|
|
{SkRect::MakeWH(kS, kS), 0.f, -360.f},
|
|
// exactly 540 sweep
|
|
{SkRect::MakeWH(kS, kS), 0.f, 540.f},
|
|
// exactly -540 sweep
|
|
{SkRect::MakeWH(kS, kS), 0.f, -540.f},
|
|
// generic large sweep and large start
|
|
{SkRect::MakeWH(kS, kS), 1125.f, 990.f},
|
|
};
|
|
SkTArray<SkPaint> paints;
|
|
// fill
|
|
paints.push_back();
|
|
// stroke
|
|
paints.push_back().setStroke(true);
|
|
paints.back().setStrokeWidth(kS / 6.f);
|
|
// hairline
|
|
paints.push_back().setStroke(true);
|
|
paints.back().setStrokeWidth(0.f);
|
|
// stroke and fill
|
|
paints.push_back().setStyle(SkPaint::kStrokeAndFill_Style);
|
|
paints.back().setStrokeWidth(kS / 6.f);
|
|
// dash effect
|
|
paints.push_back().setStroke(true);
|
|
paints.back().setStrokeWidth(kS / 6.f);
|
|
constexpr SkScalar kDashIntervals[] = {kS / 15, 2 * kS / 15};
|
|
paints.back().setPathEffect(SkDashPathEffect::Make(kDashIntervals, 2, 0.f));
|
|
|
|
canvas->translate(kPad, kPad);
|
|
// This loop should draw nothing.
|
|
for (auto arc : noDrawArcs) {
|
|
for (auto paint : paints) {
|
|
paint.setAntiAlias(true);
|
|
canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, false, paint);
|
|
canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, true, paint);
|
|
}
|
|
}
|
|
|
|
SkPaint linePaint;
|
|
linePaint.setAntiAlias(true);
|
|
linePaint.setColor(SK_ColorRED);
|
|
SkScalar midX = std::size(arcs) * (kS + kPad) - kPad/2.f;
|
|
SkScalar height = paints.count() * (kS + kPad);
|
|
canvas->drawLine(midX, -kPad, midX, height, linePaint);
|
|
|
|
for (auto paint : paints) {
|
|
paint.setAntiAlias(true);
|
|
canvas->save();
|
|
for (auto arc : arcs) {
|
|
canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, false, paint);
|
|
canvas->translate(kS + kPad, 0.f);
|
|
}
|
|
for (auto arc : arcs) {
|
|
canvas->drawArc(arc.fOval, arc.fStart, arc.fSweep, true, paint);
|
|
canvas->translate(kS + kPad, 0.f);
|
|
}
|
|
canvas->restore();
|
|
canvas->translate(0, kS + kPad);
|
|
}
|
|
}
|
|
|
|
DEF_SIMPLE_GM(onebadarc, canvas, 100, 100) {
|
|
SkPathBuilder path;
|
|
path.moveTo(SkBits2Float(0x41a00000), SkBits2Float(0x41a00000)); // 20, 20
|
|
path.lineTo(SkBits2Float(0x4208918c), SkBits2Float(0x4208918c)); // 34.1421f, 34.1421f
|
|
path.conicTo(SkBits2Float(0x41a00000), SkBits2Float(0x42412318), // 20, 48.2843f
|
|
SkBits2Float(0x40bb73a0), SkBits2Float(0x4208918c), // 5.85786f, 34.1421f
|
|
SkBits2Float(0x3f3504f3)); // 0.707107f
|
|
path.quadTo(SkBits2Float(0x40bb73a0), SkBits2Float(0x4208918c), // 5.85786f, 34.1421f
|
|
SkBits2Float(0x40bb73a2), SkBits2Float(0x4208918c)); // 5.85787f, 34.1421f
|
|
path.lineTo(SkBits2Float(0x41a00000), SkBits2Float(0x41a00000)); // 20, 20
|
|
path.close();
|
|
SkPaint p0;
|
|
p0.setColor(SK_ColorRED);
|
|
p0.setStrokeWidth(15.f);
|
|
p0.setStroke(true);
|
|
p0.setAlpha(100);
|
|
canvas->translate(20, 0);
|
|
canvas->drawPath(path.detach(), p0);
|
|
|
|
canvas->drawArc(SkRect{60, 0, 100, 40}, 45, 90, true, p0);
|
|
}
|
|
|
|
DEF_SIMPLE_GM(crbug_888453, canvas, 480, 150) {
|
|
// Two GPU path renderers were using a too-large tolerance when chopping connics to quads.
|
|
// This manifested as not-very-round circular arcs at certain radii. All the arcs being drawn
|
|
// here should look like circles.
|
|
SkPaint fill;
|
|
fill.setAntiAlias(true);
|
|
SkPaint hairline = fill;
|
|
hairline.setStroke(true);
|
|
SkPaint stroke = hairline;
|
|
stroke.setStrokeWidth(2.0f);
|
|
int x = 4;
|
|
int y0 = 25, y1 = 75, y2 = 125;
|
|
for (int r = 2; r <= 20; ++r) {
|
|
canvas->drawArc(SkRect::MakeXYWH(x - r, y0 - r, 2 * r, 2 * r), 0, 360, false, fill);
|
|
canvas->drawArc(SkRect::MakeXYWH(x - r, y1 - r, 2 * r, 2 * r), 0, 360, false, hairline);
|
|
canvas->drawArc(SkRect::MakeXYWH(x - r, y2 - r, 2 * r, 2 * r), 0, 360, false, stroke);
|
|
x += 2 * r + 4;
|
|
}
|
|
}
|
|
|
|
DEF_SIMPLE_GM(circular_arc_stroke_matrix, canvas, 820, 1090) {
|
|
static constexpr SkScalar kRadius = 40.f;
|
|
static constexpr SkScalar kStrokeWidth = 5.f;
|
|
static constexpr SkScalar kStart = 89.f;
|
|
static constexpr SkScalar kSweep = 180.f/SK_ScalarPI; // one radian
|
|
|
|
SkTArray<SkMatrix> matrices;
|
|
matrices.push_back().setRotate(kRadius, kRadius, 45.f);
|
|
matrices.push_back(SkMatrix::I());
|
|
matrices.push_back().setAll(-1, 0, 2*kRadius,
|
|
0, 1, 0,
|
|
0, 0, 1);
|
|
matrices.push_back().setAll( 1, 0, 0,
|
|
0, -1, 2*kRadius,
|
|
0, 0, 1);
|
|
matrices.push_back().setAll( 1, 0, 0,
|
|
0, -1, 2*kRadius,
|
|
0, 0, 1);
|
|
matrices.push_back().setAll( 0, -1, 2*kRadius,
|
|
-1, 0, 2*kRadius,
|
|
0, 0, 1);
|
|
matrices.push_back().setAll( 0, -1, 2*kRadius,
|
|
1, 0, 0,
|
|
0, 0, 1);
|
|
matrices.push_back().setAll( 0, 1, 0,
|
|
1, 0, 0,
|
|
0, 0, 1);
|
|
matrices.push_back().setAll( 0, 1, 0,
|
|
-1, 0, 2*kRadius,
|
|
0, 0, 1);
|
|
int baseMatrixCnt = matrices.count();
|
|
|
|
|
|
SkMatrix tinyCW;
|
|
tinyCW.setRotate(0.001f, kRadius, kRadius);
|
|
for (int i = 0; i < baseMatrixCnt; ++i) {
|
|
matrices.push_back().setConcat(matrices[i], tinyCW);
|
|
}
|
|
SkMatrix tinyCCW;
|
|
tinyCCW.setRotate(-0.001f, kRadius, kRadius);
|
|
for (int i = 0; i < baseMatrixCnt; ++i) {
|
|
matrices.push_back().setConcat(matrices[i], tinyCCW);
|
|
}
|
|
SkMatrix cw45;
|
|
cw45.setRotate(45.f, kRadius, kRadius);
|
|
for (int i = 0; i < baseMatrixCnt; ++i) {
|
|
matrices.push_back().setConcat(matrices[i], cw45);
|
|
}
|
|
|
|
int x = 0;
|
|
int y = 0;
|
|
static constexpr SkScalar kPad = 2*kStrokeWidth;
|
|
canvas->translate(kPad, kPad);
|
|
auto bounds = SkRect::MakeWH(2*kRadius, 2*kRadius);
|
|
for (auto cap : {SkPaint::kRound_Cap, SkPaint::kButt_Cap, SkPaint::kSquare_Cap}) {
|
|
for (const auto& m : matrices) {
|
|
SkPaint paint;
|
|
paint.setStrokeCap(cap);
|
|
paint.setAntiAlias(true);
|
|
paint.setStroke(true);
|
|
paint.setStrokeWidth(kStrokeWidth);
|
|
canvas->save();
|
|
canvas->translate(x * (2*kRadius + kPad), y * (2*kRadius + kPad));
|
|
canvas->concat(m);
|
|
paint.setColor(SK_ColorRED);
|
|
paint.setAlpha(0x80);
|
|
canvas->drawArc(bounds, kStart, kSweep, false, paint);
|
|
paint.setColor(SK_ColorBLUE);
|
|
paint.setAlpha(0x80);
|
|
canvas->drawArc(bounds, kStart, kSweep - 360.f, false, paint);
|
|
canvas->restore();
|
|
++x;
|
|
if (x == baseMatrixCnt) {
|
|
x = 0;
|
|
++y;
|
|
}
|
|
}
|
|
}
|
|
}
|