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