Home | History | Annotate | Download | only in gm
      1 /*
      2  * Copyright 2015 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "SkPath.h"
      9 #include "SkStream.h"
     10 #include "gm.h"
     11 
     12 
     13 // Test how short paths are stroked with various caps
     14 class StrokeZeroGM : public skiagm::GM {
     15     SkPath fPaths[8];
     16     SkPath fClipL, fClipR, fClipS;
     17 
     18 protected:
     19     void onOnceBeforeDraw() override {
     20         fClipL.moveTo(0, 0);
     21         fClipL.lineTo(3, 0);
     22         fClipL.lineTo(2.5f, 1);
     23         fClipL.lineTo(3.5f, 2.5f);
     24         fClipL.lineTo(2.5f, 4);
     25         fClipL.lineTo(3, 5);
     26         fClipL.lineTo(0, 5);
     27         fClipL.close();
     28 
     29         fClipR.moveTo(34, 0);
     30         fClipR.lineTo(34, 5);
     31         fClipR.lineTo(31, 5);
     32         fClipR.lineTo(30.5, 4);
     33         fClipR.lineTo(31.5, 2.5);
     34         fClipR.lineTo(30.5, 1);
     35         fClipR.lineTo(31, 0);
     36         fClipR.close();
     37 
     38         fClipS.addRect(SkRect::MakeIWH(4, 5));
     39 
     40         fPaths[0].moveTo(30, 0);  // single line segment
     41         fPaths[0].rLineTo(30, 0);
     42 
     43         fPaths[1].moveTo(90, 0);  // single line segment with close (does not draw caps)
     44         fPaths[1].rLineTo(30, 0);
     45         fPaths[1].close();
     46 
     47         fPaths[2].moveTo(150, 0);  // zero-length line
     48         fPaths[2].rLineTo(0, 0);
     49 
     50         fPaths[3].moveTo(180, 0);  // zero-length line with close (expected not to draw)
     51         fPaths[3].rLineTo(0, 0);
     52         fPaths[3].close();
     53 
     54         fPaths[4].moveTo(210, 0);  // close only, no line
     55         fPaths[4].close();
     56 
     57         fPaths[5].moveTo(30, 90);  // all combos below should draw two caps
     58         fPaths[5].rLineTo(0, 0);
     59         fPaths[5].moveTo(60, 90);
     60         fPaths[5].rLineTo(0, 0);
     61 
     62         fPaths[6].moveTo(90, 90);
     63         fPaths[6].close();
     64         fPaths[6].moveTo(120, 90);
     65         fPaths[6].close();
     66 
     67         fPaths[7].moveTo(150, 90);
     68         fPaths[7].rLineTo(0, 0);
     69         fPaths[7].moveTo(180, 90);
     70         fPaths[7].close();
     71     }
     72 
     73 
     74     SkString onShortName() override {
     75         return SkString("path_stroke_with_zero_length");
     76     }
     77 
     78     SkISize onISize() override {
     79         return SkISize::Make(1120, 840);
     80     }
     81 
     82     void onDraw(SkCanvas* canvas) override {
     83         SkPaint bkgrnd;
     84         bkgrnd.setColor(SK_ColorWHITE);
     85         canvas->drawRect(SkRect::MakeIWH(onISize().fWidth, onISize().fHeight), bkgrnd);
     86 
     87          auto drawPaths = [&](SkPaint& paint, int indexMask) {
     88             canvas->translate(0, 30.0f);
     89             int index = 0;
     90             for (const SkPath& path : fPaths) {
     91                 if (indexMask & (1 << index)) {
     92                     canvas->drawPath(path, paint);
     93                 }
     94                 if (this->getMode() == skiagm::GM::kSample_Mode && paint.getStrokeWidth() < 2) {
     95                     drawFat(canvas, path, paint, index);
     96                 }
     97                 ++index;
     98             }
     99         };
    100 
    101         if (false) { // debugging variant that draws a single element
    102             SkScalar width = 0;
    103             bool antialias = true;
    104 
    105             SkPaint butt;
    106             butt.setAntiAlias(antialias);
    107             butt.setStyle(SkPaint::kStroke_Style);
    108             butt.setStrokeWidth(width);
    109 
    110             SkPaint round(butt);
    111             round.setStrokeCap(SkPaint::kRound_Cap);
    112             drawPaths(round, 1 << 7);
    113             return;
    114         }
    115 
    116         SkScalar widths[] = { 0, .999f, 1, 1.001f, 20 };
    117         bool aliases[] = { false, true };
    118         for (bool antialias : aliases) {
    119             canvas->save();
    120             for (SkScalar width : widths) {
    121                 canvas->save();
    122                 SkPaint butt;
    123                 butt.setAntiAlias(antialias);
    124                 butt.setStyle(SkPaint::kStroke_Style);
    125                 butt.setStrokeWidth(width);
    126                 drawPaths(butt, -1);
    127 
    128                 SkPaint round(butt);
    129                 round.setStrokeCap(SkPaint::kRound_Cap);
    130                 drawPaths(round, -1);
    131 
    132                 SkPaint square(butt);
    133                 square.setStrokeCap(SkPaint::kSquare_Cap);
    134                 drawPaths(square, -1);
    135                 canvas->restore();
    136                 canvas->translate(220, 0);
    137             }
    138             canvas->restore();
    139             canvas->translate(0, 210);
    140         }
    141     }
    142 
    143 private:
    144     void drawFat(SkCanvas* canvas, const SkPath& path, const SkPaint& paint, int index) {
    145         const SkScalar scale = 10;
    146         SkRect bounds = path.getBounds();
    147         SkBitmap offscreen;
    148         offscreen.allocN32Pixels(SkScalarRoundToInt(bounds.width() + 4),
    149                 SkScalarRoundToInt(bounds.height() + 4));
    150         offscreen.eraseColor(SK_ColorWHITE);
    151         SkScalar pathX = bounds.fLeft - 2;
    152         SkScalar pathY = bounds.fTop - 2;
    153         SkMatrix cMatrix = canvas->getTotalMatrix();
    154         if (!canvas->readPixels(&offscreen, SkScalarRoundToInt(pathX + cMatrix.getTranslateX()),
    155                 SkScalarRoundToInt(pathY + cMatrix.getTranslateY()))) {
    156             return;
    157         }
    158 
    159         canvas->save();
    160         SkMatrix clipM;
    161         clipM.reset();
    162         clipM.preScale(scale, scale);
    163         clipM.postTranslate(bounds.fLeft - 17, bounds.fTop - 24.5f + 420);
    164         SkPath clip;
    165         if (index < 2) {
    166             fClipL.transform(clipM, &clip);
    167         } else {
    168             fClipS.transform(clipM, &clip);
    169         }
    170         canvas->clipPath(clip, true);
    171         canvas->scale(scale, scale);
    172         canvas->drawBitmap(offscreen, (bounds.fLeft - 17) / scale,
    173                     (bounds.fTop - 20 + 420) / scale);
    174         canvas->restore();
    175 
    176         if (bounds.width() > 20) {
    177             canvas->save();
    178             clipM.reset();
    179             clipM.preScale(scale, scale);
    180             clipM.postTranslate(bounds.fLeft - 17 - 275, bounds.fTop - 24.5f + 420);
    181             SkPath clip;
    182             fClipR.transform(clipM, &clip);
    183             canvas->clipPath(clip, true);
    184             canvas->scale(10.f, 10.f);
    185             canvas->drawBitmap(offscreen, (bounds.fLeft - 17 - 275
    186                     + (index >= 5 ? 5 : 0)) / scale, (bounds.fTop - 20 + 420) / scale);
    187             canvas->restore();
    188         }
    189     }
    190 
    191 };
    192 
    193 ///////////////////////////////////////////////////////////////////////////////
    194 
    195 DEF_GM( return new StrokeZeroGM(); )
    196