18b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary/*
28b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary * Copyright 2015 Google Inc.
38b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary *
48b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary * Use of this source code is governed by a BSD-style license that can be
58b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary * found in the LICENSE file.
68b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary */
78b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary
88b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary#include "SkPath.h"
98b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary#include "SkStream.h"
108b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary#include "gm.h"
118b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary
121f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
138b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary// Test how short paths are stroked with various caps
141f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclarkclass StrokeZeroGM : public skiagm::GM {
151f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark    SkPath fPaths[8];
161f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark    SkPath fClipL, fClipR, fClipS;
171f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
181f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclarkprotected:
191f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark    void onOnceBeforeDraw() override {
201f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipL.moveTo(0, 0);
211f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipL.lineTo(3, 0);
221f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipL.lineTo(2.5f, 1);
231f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipL.lineTo(3.5f, 2.5f);
241f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipL.lineTo(2.5f, 4);
251f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipL.lineTo(3, 5);
261f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipL.lineTo(0, 5);
271f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipL.close();
281f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
291f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipR.moveTo(34, 0);
301f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipR.lineTo(34, 5);
311f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipR.lineTo(31, 5);
321f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipR.lineTo(30.5, 4);
331f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipR.lineTo(31.5, 2.5);
341f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipR.lineTo(30.5, 1);
351f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipR.lineTo(31, 0);
361f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipR.close();
371f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
381f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fClipS.addRect(SkRect::MakeIWH(4, 5));
391f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
401f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[0].moveTo(30, 0);  // single line segment
411f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[0].rLineTo(30, 0);
421f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
431f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[1].moveTo(90, 0);  // single line segment with close (does not draw caps)
441f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[1].rLineTo(30, 0);
451f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[1].close();
461f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
471f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[2].moveTo(150, 0);  // zero-length line
481f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[2].rLineTo(0, 0);
491f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
501f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[3].moveTo(180, 0);  // zero-length line with close (expected not to draw)
511f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[3].rLineTo(0, 0);
521f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[3].close();
531f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
541f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[4].moveTo(210, 0);  // close only, no line
551f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[4].close();
561f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
571f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[5].moveTo(30, 90);  // all combos below should draw two caps
581f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[5].rLineTo(0, 0);
591f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[5].moveTo(60, 90);
601f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[5].rLineTo(0, 0);
611f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
621f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[6].moveTo(90, 90);
631f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[6].close();
641f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[6].moveTo(120, 90);
651f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[6].close();
661f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
671f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[7].moveTo(150, 90);
681f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[7].rLineTo(0, 0);
691f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[7].moveTo(180, 90);
701f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        fPaths[7].close();
711f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark    }
721f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
731f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
741f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark    SkString onShortName() override {
751f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        return SkString("path_stroke_with_zero_length");
761f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark    }
771f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
781f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark    SkISize onISize() override {
791f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        return SkISize::Make(1120, 840);
801f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark    }
818b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary
821f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark    void onDraw(SkCanvas* canvas) override {
831f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        SkPaint bkgrnd;
841f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        bkgrnd.setColor(SK_ColorWHITE);
851f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        canvas->drawRect(SkRect::MakeIWH(onISize().fWidth, onISize().fHeight), bkgrnd);
868b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary
871f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark         auto drawPaths = [&](SkPaint& paint, int indexMask) {
881f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            canvas->translate(0, 30.0f);
891f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            int index = 0;
901f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            for (const SkPath& path : fPaths) {
911f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                if (indexMask & (1 << index)) {
921f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                    canvas->drawPath(path, paint);
931f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                }
9448156ed412410c9d27b560e8596e3f34d175a277mtklein                if (this->getMode() == skiagm::GM::kSample_Mode && paint.getStrokeWidth() < 2) {
951f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                    drawFat(canvas, path, paint, index);
961f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                }
971f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                ++index;
981f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            }
991f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        };
1008b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary
1011f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        if (false) { // debugging variant that draws a single element
1021f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            SkScalar width = 0;
1031f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            bool antialias = true;
1048b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary
1051f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            SkPaint butt;
1061f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            butt.setAntiAlias(antialias);
1071f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            butt.setStyle(SkPaint::kStroke_Style);
1081f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            butt.setStrokeWidth(width);
1098b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary
1101f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            SkPaint round(butt);
1111f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            round.setStrokeCap(SkPaint::kRound_Cap);
1121f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            drawPaths(round, 1 << 7);
1131f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            return;
1148b2bc252faed7c751cf9248c3833c0631f498b7dhalcanary        }
1151f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
1161f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        SkScalar widths[] = { 0, .999f, 1, 1.001f, 20 };
1171f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        bool aliases[] = { false, true };
1181f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        for (bool antialias : aliases) {
1191f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            canvas->save();
120636270245fb486559b0324e9bb9e794d56bc3101mtklein            for (SkScalar width : widths) {
1211f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                canvas->save();
1221f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                SkPaint butt;
1231f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                butt.setAntiAlias(antialias);
1241f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                butt.setStyle(SkPaint::kStroke_Style);
1251f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                butt.setStrokeWidth(width);
1261f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                drawPaths(butt, -1);
1271f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
1281f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                SkPaint round(butt);
1291f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                round.setStrokeCap(SkPaint::kRound_Cap);
1301f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                drawPaths(round, -1);
1311f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
1321f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                SkPaint square(butt);
1331f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                square.setStrokeCap(SkPaint::kSquare_Cap);
1341f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                drawPaths(square, -1);
1351f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                canvas->restore();
1361f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                canvas->translate(220, 0);
1371f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            }
1381f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            canvas->restore();
1391f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            canvas->translate(0, 210);
1401f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        }
1411f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark    }
1421f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
1431f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclarkprivate:
1441f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark    void drawFat(SkCanvas* canvas, const SkPath& path, const SkPaint& paint, int index) {
1451f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        const SkScalar scale = 10;
1461f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        SkRect bounds = path.getBounds();
1471f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        SkBitmap offscreen;
1481f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        offscreen.allocN32Pixels(SkScalarRoundToInt(bounds.width() + 4),
1491f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                SkScalarRoundToInt(bounds.height() + 4));
150636270245fb486559b0324e9bb9e794d56bc3101mtklein        offscreen.eraseColor(SK_ColorWHITE);
1511f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        SkScalar pathX = bounds.fLeft - 2;
1521f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        SkScalar pathY = bounds.fTop - 2;
1531f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        SkMatrix cMatrix = canvas->getTotalMatrix();
1541f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        if (!canvas->readPixels(&offscreen, SkScalarRoundToInt(pathX + cMatrix.getTranslateX()),
1551f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                SkScalarRoundToInt(pathY + cMatrix.getTranslateY()))) {
1561f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            return;
1571f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        }
1581f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
1591f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        canvas->save();
1601f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        SkMatrix clipM;
1611f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        clipM.reset();
1621f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        clipM.preScale(scale, scale);
1631f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        clipM.postTranslate(bounds.fLeft - 17, bounds.fTop - 24.5f + 420);
1641f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        SkPath clip;
1651f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        if (index < 2) {
1661f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            fClipL.transform(clipM, &clip);
1671f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        } else {
1681f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            fClipS.transform(clipM, &clip);
1691f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        }
170669983856d99b9312be3166b7dd1f8483a90c315reed        canvas->clipPath(clip, true);
1711f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        canvas->scale(scale, scale);
1721f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        canvas->drawBitmap(offscreen, (bounds.fLeft - 17) / scale,
1731f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                    (bounds.fTop - 20 + 420) / scale);
1741f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        canvas->restore();
1751f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
1761f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        if (bounds.width() > 20) {
1771f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            canvas->save();
1781f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            clipM.reset();
1791f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            clipM.preScale(scale, scale);
1801f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            clipM.postTranslate(bounds.fLeft - 17 - 275, bounds.fTop - 24.5f + 420);
1811f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            SkPath clip;
1821f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            fClipR.transform(clipM, &clip);
183669983856d99b9312be3166b7dd1f8483a90c315reed            canvas->clipPath(clip, true);
1841f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            canvas->scale(10.f, 10.f);
1851f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            canvas->drawBitmap(offscreen, (bounds.fLeft - 17 - 275
1861f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark                    + (index >= 5 ? 5 : 0)) / scale, (bounds.fTop - 20 + 420) / scale);
1871f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark            canvas->restore();
1881f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark        }
1891f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark    }
1901f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
1911f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark};
1921f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
1931f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark///////////////////////////////////////////////////////////////////////////////
1941f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclark
1951f17ab59929f5f5eb399e09690ef9dbc21525ccccaryclarkDEF_GM( return new StrokeZeroGM(); )
196