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