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