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 = 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 
getZoom() const54     int getZoom() const { return fZoom; }
55 
getAA() const56     bool getAA() const { return fAA; }
setAA(bool aa)57     void setAA(bool aa) { fAA = aa; }
58 
getGrid() const59     bool getGrid() const { return fGrid; }
setGrid(bool g)60     void setGrid(bool g) { fGrid = g; }
61 
getShowSkeleton() const62     bool getShowSkeleton() const { return fShowSkeleton; }
setShowSkeleton(bool ss)63     void setShowSkeleton(bool ss) { fShowSkeleton = ss; }
64 
getUseGPU() const65     bool getUseGPU() const { return fUseGPU; }
setUseGPU(bool ug)66     void setUseGPU(bool ug) { fUseGPU = ug; }
67 
getTriangle() const68     bool getTriangle() const { return fUseTriangle; }
setTriangle(bool ut)69     void setTriangle(bool ut) { fUseTriangle = ut; }
70 
toggleRectAsOval()71     void toggleRectAsOval() { fRectAsOval = !fRectAsOval; }
72 
getUseClip() const73     bool getUseClip() const { return fUseClip; }
setUseClip(bool uc)74     void setUseClip(bool uc) { fUseClip = uc; }
75 
76     enum Style {
77         kHair_Style,
78         kStroke_Style,
79     };
getStyle() const80     Style getStyle() const { return fStyle; }
setStyle(Style s)81     void setStyle(Style s) { fStyle = s; }
82 
setWHZ(int width,int height,int zoom)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 
105 private:
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 
setupPaint(SkPaint * paint)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 
setupSkeletonPaint(SkPaint * paint)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[]);
drawRectSkeleton(SkCanvas * max,const SkRect & r)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 
copyMinToMax()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 
drawBG(SkCanvas * canvas)182 void FatBits::drawBG(SkCanvas* canvas) {
183     SkPaint paint;
184 
185     paint.setShader(fShader);
186     canvas->drawRect(fBounds, paint);
187     paint.setShader(NULL);
188 }
189 
drawFG(SkCanvas * canvas)190 void 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 
drawLineSkeleton(SkCanvas * max,const SkPoint pts[])226 void 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 
drawLine(SkCanvas * canvas,SkPoint pts[])262 void 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 
drawRect(SkCanvas * canvas,SkPoint pts[2])294 void 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 
drawTriangleSkeleton(SkCanvas * max,const SkPoint pts[])324 void 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 
drawTriangle(SkCanvas * canvas,SkPoint pts[3])337 void 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 
368 class IndexClick : public SkView::Click {
369     int fIndex;
370 public:
IndexClick(SkView * v,int index)371     IndexClick(SkView* v, int index) : SkView::Click(v), fIndex(index) {}
372 
GetIndex(SkView::Click * click)373     static int GetIndex(SkView::Click* click) {
374         return ((IndexClick*)click)->fIndex;
375     }
376 };
377 
378 class DrawLineView : public SampleView {
379     FatBits fFB;
380     SkPoint fPts[3];
381     bool    fIsRect;
382 public:
DrawLineView()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 
setStyle(FatBits::Style s)391     void setStyle(FatBits::Style s) {
392         fFB.setStyle(s);
393         this->inval(NULL);
394     }
395 
396 protected:
onQuery(SkEvent * evt)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 
onDrawContent(SkCanvas * canvas)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 
onFindClickHandler(SkScalar x,SkScalar y,unsigned modi)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 
onClick(Click * click)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 
507 private:
508 
509     typedef SampleView INHERITED;
510 };
511 
512 //////////////////////////////////////////////////////////////////////////////
513 
MyFactory()514 static SkView* MyFactory() { return new DrawLineView; }
515 static SkViewRegister reg(MyFactory);
516