Home | History | Annotate | Download | only in samplecode
      1 /*
      2  * Copyright 2012 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 "Sample.h"
      9 #include "SkBlendMode.h"
     10 #include "SkCanvas.h"
     11 #include "SkClipOpPriv.h"
     12 #include "SkColor.h"
     13 #include "SkImageInfo.h"
     14 #include "SkMatrix.h"
     15 #include "SkPaint.h"
     16 #include "SkPath.h"
     17 #include "SkPoint.h"
     18 #include "SkPointPriv.h"
     19 #include "SkRect.h"
     20 #include "SkRefCnt.h"
     21 #include "SkScalar.h"
     22 #include "SkShader.h"
     23 #include "SkString.h"
     24 #include "SkSurface.h"
     25 #include "SkTypes.h"
     26 #include "sk_tool_utils.h"
     27 
     28 class SkEvent;
     29 
     30 #define FAT_PIXEL_COLOR     SK_ColorBLACK
     31 #define PIXEL_CENTER_SIZE   3
     32 #define WIRE_FRAME_COLOR    0xFFFF0000  /*0xFF00FFFF*/
     33 #define WIRE_FRAME_SIZE     1.5f
     34 
     35 static SkScalar apply_grid(SkScalar x) {
     36     const SkScalar grid = 2;
     37     return SkScalarRoundToScalar(x * grid) / grid;
     38 }
     39 
     40 static void apply_grid(SkPoint pts[], int count) {
     41     for (int i = 0; i < count; ++i) {
     42         pts[i].set(apply_grid(pts[i].fX), apply_grid(pts[i].fY));
     43     }
     44 }
     45 
     46 static void erase(SkSurface* surface) {
     47     surface->getCanvas()->clear(SK_ColorTRANSPARENT);
     48 }
     49 
     50 class FatBits {
     51 public:
     52     FatBits() {
     53         fAA = false;
     54         fStyle = kHair_Style;
     55         fGrid = false;
     56         fShowSkeleton = true;
     57         fUseClip = false;
     58         fRectAsOval = false;
     59         fUseTriangle = false;
     60         fStrokeCap = SkPaint::kButt_Cap;
     61 
     62         fClipRect.set(2, 2, 11, 8 );
     63     }
     64 
     65     int getZoom() const { return fZoom; }
     66 
     67     bool getAA() const { return fAA; }
     68     void setAA(bool aa) { fAA = aa; }
     69 
     70     bool getGrid() const { return fGrid; }
     71     void setGrid(bool g) { fGrid = g; }
     72 
     73     bool getShowSkeleton() const { return fShowSkeleton; }
     74     void setShowSkeleton(bool ss) { fShowSkeleton = ss; }
     75 
     76     bool getTriangle() const { return fUseTriangle; }
     77     void setTriangle(bool ut) { fUseTriangle = ut; }
     78 
     79     void toggleRectAsOval() { fRectAsOval = !fRectAsOval; }
     80 
     81     void togglePixelColors() {
     82         if (fShader == fShader0) {
     83             fShader = fShader1;
     84         } else {
     85             fShader = fShader0;
     86         }
     87     }
     88 
     89     float fStrokeWidth = 1;
     90 
     91     bool getUseClip() const { return fUseClip; }
     92     void setUseClip(bool uc) { fUseClip = uc; }
     93 
     94     enum Style {
     95         kHair_Style,
     96         kStroke_Style,
     97     };
     98     Style getStyle() const { return fStyle; }
     99     void setStyle(Style s) { fStyle = s; }
    100 
    101     void setWHZ(int width, int height, int zoom) {
    102         fW = width;
    103         fH = height;
    104         fZoom = zoom;
    105         fBounds.set(0, 0, SkIntToScalar(width * zoom), SkIntToScalar(height * zoom));
    106         fMatrix.setScale(SkIntToScalar(zoom), SkIntToScalar(zoom));
    107         fInverse.setScale(SK_Scalar1 / zoom, SK_Scalar1 / zoom);
    108         fShader0 = sk_tool_utils::create_checkerboard_shader(0xFFDDDDDD, 0xFFFFFFFF, zoom);
    109         fShader1 = SkShader::MakeColorShader(SK_ColorWHITE);
    110         fShader = fShader0;
    111 
    112         SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
    113         fMinSurface = SkSurface::MakeRaster(info);
    114         info = info.makeWH(width * zoom, height * zoom);
    115         fMaxSurface = SkSurface::MakeRaster(info);
    116     }
    117 
    118     void drawBG(SkCanvas*);
    119     void drawFG(SkCanvas*);
    120     void drawLine(SkCanvas*, SkPoint pts[2]);
    121     void drawRect(SkCanvas* canvas, SkPoint pts[2]);
    122     void drawTriangle(SkCanvas* canvas, SkPoint pts[3]);
    123 
    124     SkPaint::Cap fStrokeCap;
    125 
    126 private:
    127     bool fAA, fGrid, fShowSkeleton, fUseClip, fRectAsOval, fUseTriangle;
    128     Style fStyle;
    129     int fW, fH, fZoom;
    130     SkMatrix            fMatrix, fInverse;
    131     SkRect              fBounds, fClipRect;
    132     sk_sp<SkShader>     fShader0;
    133     sk_sp<SkShader>     fShader1;
    134     sk_sp<SkShader>     fShader;
    135     sk_sp<SkSurface>    fMinSurface;
    136     sk_sp<SkSurface>    fMaxSurface;
    137 
    138     void setupPaint(SkPaint* paint) {
    139         bool aa = this->getAA();
    140         paint->setStrokeCap(fStrokeCap);
    141         switch (fStyle) {
    142             case kHair_Style:
    143                 paint->setStrokeWidth(0);
    144                 break;
    145             case kStroke_Style:
    146                 paint->setStrokeWidth(fStrokeWidth);
    147                 break;
    148         }
    149         paint->setAntiAlias(aa);
    150     }
    151 
    152     void setupSkeletonPaint(SkPaint* paint) {
    153         paint->setStyle(SkPaint::kStroke_Style);
    154         paint->setStrokeWidth(WIRE_FRAME_SIZE);
    155         paint->setColor(fShowSkeleton ? WIRE_FRAME_COLOR : 0);
    156         paint->setAntiAlias(true);
    157     }
    158 
    159     void drawTriangleSkeleton(SkCanvas* max, const SkPoint pts[]);
    160     void drawLineSkeleton(SkCanvas* max, const SkPoint pts[]);
    161     void drawRectSkeleton(SkCanvas* max, const SkRect& r) {
    162         SkPaint paint;
    163         this->setupSkeletonPaint(&paint);
    164         SkPath path;
    165 
    166         fRectAsOval ? path.addOval(r) : path.addRect(r);
    167         max->drawPath(path, paint);
    168     }
    169 
    170     void copyMinToMax() {
    171         erase(fMaxSurface.get());
    172         SkCanvas* canvas = fMaxSurface->getCanvas();
    173         canvas->save();
    174         canvas->concat(fMatrix);
    175         fMinSurface->draw(canvas, 0, 0, nullptr);
    176         canvas->restore();
    177 
    178         SkPaint paint;
    179         paint.setBlendMode(SkBlendMode::kClear);
    180         for (int iy = 1; iy < fH; ++iy) {
    181             SkScalar y = SkIntToScalar(iy * fZoom);
    182             canvas->drawLine(0, y - SK_ScalarHalf, 999, y - SK_ScalarHalf, paint);
    183         }
    184         for (int ix = 1; ix < fW; ++ix) {
    185             SkScalar x = SkIntToScalar(ix * fZoom);
    186             canvas->drawLine(x - SK_ScalarHalf, 0, x - SK_ScalarHalf, 999, paint);
    187         }
    188     }
    189 };
    190 
    191 void FatBits::drawBG(SkCanvas* canvas) {
    192     SkPaint paint;
    193 
    194     paint.setShader(fShader);
    195     canvas->drawRect(fBounds, paint);
    196     paint.setShader(nullptr);
    197 }
    198 
    199 void FatBits::drawFG(SkCanvas* canvas) {
    200     SkPaint inner, outer;
    201 
    202     inner.setAntiAlias(true);
    203     inner.setColor(SK_ColorBLACK);
    204     inner.setStrokeWidth(PIXEL_CENTER_SIZE);
    205 
    206     outer.setAntiAlias(true);
    207     outer.setColor(SK_ColorWHITE);
    208     outer.setStrokeWidth(PIXEL_CENTER_SIZE + 2);
    209 
    210     SkScalar half = SkIntToScalar(fZoom) / 2;
    211     for (int iy = 0; iy < fH; ++iy) {
    212         SkScalar y = SkIntToScalar(iy * fZoom) + half;
    213         for (int ix = 0; ix < fW; ++ix) {
    214             SkScalar x = SkIntToScalar(ix * fZoom) + half;
    215 
    216             canvas->drawPoint(x, y, outer);
    217             canvas->drawPoint(x, y, inner);
    218         }
    219     }
    220 
    221     if (fUseClip) {
    222         SkPaint p;
    223         p.setStyle(SkPaint::kStroke_Style);
    224         p.setColor(SK_ColorLTGRAY);
    225         SkRect r = {
    226             fClipRect.fLeft * fZoom,
    227             fClipRect.fTop * fZoom,
    228             fClipRect.fRight * fZoom,
    229             fClipRect.fBottom * fZoom
    230         };
    231         canvas->drawRect(r, p);
    232     }
    233 }
    234 
    235 void FatBits::drawLineSkeleton(SkCanvas* max, const SkPoint pts[]) {
    236     SkPaint paint;
    237     this->setupSkeletonPaint(&paint);
    238 
    239     SkPath path;
    240     path.moveTo(pts[0]);
    241     path.lineTo(pts[1]);
    242 
    243     if (fStyle == kStroke_Style) {
    244         SkPaint p;
    245         p.setStyle(SkPaint::kStroke_Style);
    246         p.setStrokeWidth(fStrokeWidth * fZoom);
    247         p.setStrokeCap(fStrokeCap);
    248         SkPath dst;
    249         p.getFillPath(path, &dst);
    250         path = dst;
    251 
    252         path.moveTo(pts[0]);
    253         path.lineTo(pts[1]);
    254     }
    255     max->drawPath(path, paint);
    256 }
    257 
    258 void FatBits::drawLine(SkCanvas* canvas, SkPoint pts[]) {
    259     SkPaint paint;
    260 
    261     fInverse.mapPoints(pts, 2);
    262 
    263     if (fGrid) {
    264         apply_grid(pts, 2);
    265     }
    266 
    267     erase(fMinSurface.get());
    268     this->setupPaint(&paint);
    269     paint.setColor(FAT_PIXEL_COLOR);
    270     if (fUseClip) {
    271         fMinSurface->getCanvas()->save();
    272         SkRect r = fClipRect;
    273         r.inset(SK_Scalar1/3, SK_Scalar1/3);
    274         fMinSurface->getCanvas()->clipRect(r, kIntersect_SkClipOp, true);
    275     }
    276     fMinSurface->getCanvas()->drawLine(pts[0], pts[1], paint);
    277     if (fUseClip) {
    278         fMinSurface->getCanvas()->restore();
    279     }
    280     this->copyMinToMax();
    281 
    282     SkCanvas* max = fMaxSurface->getCanvas();
    283 
    284     fMatrix.mapPoints(pts, 2);
    285     this->drawLineSkeleton(max, pts);
    286 
    287     fMaxSurface->draw(canvas, 0, 0, nullptr);
    288 }
    289 
    290 void FatBits::drawRect(SkCanvas* canvas, SkPoint pts[2]) {
    291     SkPaint paint;
    292 
    293     fInverse.mapPoints(pts, 2);
    294 
    295     if (fGrid) {
    296         apply_grid(pts, 2);
    297     }
    298 
    299     SkRect r;
    300     r.set(pts, 2);
    301 
    302     erase(fMinSurface.get());
    303     this->setupPaint(&paint);
    304     paint.setColor(FAT_PIXEL_COLOR);
    305     {
    306         SkCanvas* c = fMinSurface->getCanvas();
    307         fRectAsOval ? c->drawOval(r, paint) : c->drawRect(r, paint);
    308     }
    309     this->copyMinToMax();
    310 
    311     SkCanvas* max = fMaxSurface->getCanvas();
    312 
    313     fMatrix.mapPoints(pts, 2);
    314     r.set(pts, 2);
    315     this->drawRectSkeleton(max, r);
    316 
    317     fMaxSurface->draw(canvas, 0, 0, nullptr);
    318 }
    319 
    320 void FatBits::drawTriangleSkeleton(SkCanvas* max, const SkPoint pts[]) {
    321     SkPaint paint;
    322     this->setupSkeletonPaint(&paint);
    323 
    324     SkPath path;
    325     path.moveTo(pts[0]);
    326     path.lineTo(pts[1]);
    327     path.lineTo(pts[2]);
    328     path.close();
    329 
    330     max->drawPath(path, paint);
    331 }
    332 
    333 void FatBits::drawTriangle(SkCanvas* canvas, SkPoint pts[3]) {
    334     SkPaint paint;
    335 
    336     fInverse.mapPoints(pts, 3);
    337 
    338     if (fGrid) {
    339         apply_grid(pts, 3);
    340     }
    341 
    342     SkPath path;
    343     path.moveTo(pts[0]);
    344     path.lineTo(pts[1]);
    345     path.lineTo(pts[2]);
    346     path.close();
    347 
    348     erase(fMinSurface.get());
    349     this->setupPaint(&paint);
    350     paint.setColor(FAT_PIXEL_COLOR);
    351     fMinSurface->getCanvas()->drawPath(path, paint);
    352     this->copyMinToMax();
    353 
    354     SkCanvas* max = fMaxSurface->getCanvas();
    355 
    356     fMatrix.mapPoints(pts, 3);
    357     this->drawTriangleSkeleton(max, pts);
    358 
    359     fMaxSurface->draw(canvas, 0, 0, nullptr);
    360 }
    361 
    362 ///////////////////////////////////////////////////////////////////////////////////////////////////
    363 
    364 class IndexClick : public Sample::Click {
    365     int fIndex;
    366 public:
    367     IndexClick(Sample* v, int index) : Sample::Click(v), fIndex(index) {}
    368 
    369     static int GetIndex(Sample::Click* click) {
    370         return ((IndexClick*)click)->fIndex;
    371     }
    372 };
    373 
    374 class DrawLineView : public Sample {
    375     FatBits fFB;
    376     SkPoint fPts[3];
    377     bool    fIsRect;
    378     int     fZoom = 64;
    379 public:
    380     DrawLineView() {
    381         fFB.setWHZ(24*2, 16*2, fZoom);
    382         fPts[0].set(1, 1);
    383         fPts[1].set(5, 4);
    384         fPts[2].set(2, 6);
    385         SkMatrix::MakeScale(SkIntToScalar(fZoom)).mapPoints(fPts, 3);
    386         fIsRect = false;
    387     }
    388 
    389     void setStyle(FatBits::Style s) {
    390         fFB.setStyle(s);
    391     }
    392 
    393 protected:
    394     bool onQuery(Sample::Event* evt) override {
    395         if (Sample::TitleQ(*evt)) {
    396             Sample::TitleR(evt, "FatBits");
    397             return true;
    398         }
    399         SkUnichar uni;
    400         if (Sample::CharQ(*evt, &uni)) {
    401             switch (uni) {
    402                 case 'c':
    403                     fFB.setUseClip(!fFB.getUseClip());
    404                     return true;
    405                 case 'r':
    406                     fIsRect = !fIsRect;
    407                     return true;
    408                 case 'o':
    409                     fFB.toggleRectAsOval();
    410                     return true;
    411                 case 'x':
    412                     fFB.setGrid(!fFB.getGrid());
    413                     return true;
    414                 case 's':
    415                     if (FatBits::kStroke_Style == fFB.getStyle()) {
    416                         this->setStyle(FatBits::kHair_Style);
    417                     } else {
    418                         this->setStyle(FatBits::kStroke_Style);
    419                     }
    420                     return true;
    421                 case 'k': {
    422                     const SkPaint::Cap caps[] = {
    423                         SkPaint::kButt_Cap, SkPaint::kRound_Cap, SkPaint::kSquare_Cap,
    424                     };
    425                     fFB.fStrokeCap = caps[(fFB.fStrokeCap + 1) % 3];
    426                     return true;
    427                 } break;
    428                 case 'a':
    429                     fFB.setAA(!fFB.getAA());
    430                     return true;
    431                 case 'w':
    432                     fFB.setShowSkeleton(!fFB.getShowSkeleton());
    433                     return true;
    434                 case 'g':
    435                     fFB.togglePixelColors();
    436                     return true;
    437                 case 't':
    438                     fFB.setTriangle(!fFB.getTriangle());
    439                     return true;
    440                 case '-':
    441                     fFB.fStrokeWidth -= 0.125f;
    442                     return true;
    443                 case '=':
    444                     fFB.fStrokeWidth += 0.125f;
    445                     return true;
    446             }
    447         }
    448         return this->INHERITED::onQuery(evt);
    449     }
    450 
    451     void onDrawContent(SkCanvas* canvas) override {
    452         fFB.drawBG(canvas);
    453         if (fFB.getTriangle()) {
    454             fFB.drawTriangle(canvas, fPts);
    455         }
    456         else if (fIsRect) {
    457             fFB.drawRect(canvas, fPts);
    458         } else {
    459             fFB.drawLine(canvas, fPts);
    460         }
    461         fFB.drawFG(canvas);
    462 
    463         {
    464             SkString str;
    465             str.printf("%s %s %s",
    466                        fFB.getAA() ? "AA" : "BW",
    467                        FatBits::kHair_Style == fFB.getStyle() ? "Hair" : "Stroke",
    468                        fFB.getUseClip() ? "clip" : "noclip");
    469             SkPaint paint;
    470             paint.setColor(SK_ColorBLUE);
    471             SkFont font(nullptr, 16);
    472             canvas->drawString(str, 10, 16, font, paint);
    473         }
    474     }
    475 
    476     Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) override {
    477         SkPoint pt = { x, y };
    478         int index = -1;
    479         int count = fFB.getTriangle() ? 3 : 2;
    480         SkScalar tol = 12;
    481 
    482         for (int i = 0; i < count; ++i) {
    483             if (SkPointPriv::EqualsWithinTolerance(fPts[i], pt, tol)) {
    484                 index = i;
    485                 break;
    486             }
    487         }
    488         return new IndexClick(this, index);
    489     }
    490 
    491     bool onClick(Click* click) override {
    492         int index = IndexClick::GetIndex(click);
    493         if (index >= 0 && index <= 2) {
    494             fPts[index] = click->fCurr;
    495         } else {
    496             SkScalar dx = click->fCurr.fX - click->fPrev.fX;
    497             SkScalar dy = click->fCurr.fY - click->fPrev.fY;
    498             fPts[0].offset(dx, dy);
    499             fPts[1].offset(dx, dy);
    500             fPts[2].offset(dx, dy);
    501         }
    502         return true;
    503     }
    504 
    505 private:
    506 
    507     typedef Sample INHERITED;
    508 };
    509 
    510 //////////////////////////////////////////////////////////////////////////////
    511 
    512 DEF_SAMPLE( return new DrawLineView(); )
    513