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