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