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