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 "SkPointPriv.h"
14 #include "SkRegion.h"
15 #include "SkShader.h"
16 #include "SkUtils.h"
17 #include "SkImage.h"
18 #include "SkSurface.h"
19 #include "SkClipOpPriv.h"
20
21 #define FAT_PIXEL_COLOR SK_ColorBLACK
22 #define PIXEL_CENTER_SIZE 3
23 #define WIRE_FRAME_COLOR 0xFFFF0000 /*0xFF00FFFF*/
24 #define WIRE_FRAME_SIZE 1.5f
25
apply_grid(SkScalar x)26 static SkScalar apply_grid(SkScalar x) {
27 const SkScalar grid = 2;
28 return SkScalarRoundToScalar(x * grid) / grid;
29 }
30
apply_grid(SkPoint pts[],int count)31 static void apply_grid(SkPoint pts[], int count) {
32 for (int i = 0; i < count; ++i) {
33 pts[i].set(apply_grid(pts[i].fX), apply_grid(pts[i].fY));
34 }
35 }
36
erase(SkSurface * surface)37 static void erase(SkSurface* surface) {
38 surface->getCanvas()->clear(SK_ColorTRANSPARENT);
39 }
40
41 class FatBits {
42 public:
FatBits()43 FatBits() {
44 fAA = false;
45 fStyle = kHair_Style;
46 fGrid = false;
47 fShowSkeleton = true;
48 fUseClip = false;
49 fRectAsOval = false;
50 fUseTriangle = false;
51 fStrokeCap = SkPaint::kButt_Cap;
52
53 fClipRect.set(2, 2, 11, 8 );
54 }
55
getZoom() const56 int getZoom() const { return fZoom; }
57
getAA() const58 bool getAA() const { return fAA; }
setAA(bool aa)59 void setAA(bool aa) { fAA = aa; }
60
getGrid() const61 bool getGrid() const { return fGrid; }
setGrid(bool g)62 void setGrid(bool g) { fGrid = g; }
63
getShowSkeleton() const64 bool getShowSkeleton() const { return fShowSkeleton; }
setShowSkeleton(bool ss)65 void setShowSkeleton(bool ss) { fShowSkeleton = ss; }
66
getTriangle() const67 bool getTriangle() const { return fUseTriangle; }
setTriangle(bool ut)68 void setTriangle(bool ut) { fUseTriangle = ut; }
69
toggleRectAsOval()70 void toggleRectAsOval() { fRectAsOval = !fRectAsOval; }
71
togglePixelColors()72 void togglePixelColors() {
73 if (fShader == fShader0) {
74 fShader = fShader1;
75 } else {
76 fShader = fShader0;
77 }
78 }
79
80 float fStrokeWidth = 1;
81
getUseClip() const82 bool getUseClip() const { return fUseClip; }
setUseClip(bool uc)83 void setUseClip(bool uc) { fUseClip = uc; }
84
85 enum Style {
86 kHair_Style,
87 kStroke_Style,
88 };
getStyle() const89 Style getStyle() const { return fStyle; }
setStyle(Style s)90 void setStyle(Style s) { fStyle = s; }
91
setWHZ(int width,int height,int zoom)92 void setWHZ(int width, int height, int zoom) {
93 fW = width;
94 fH = height;
95 fZoom = zoom;
96 fBounds.set(0, 0, SkIntToScalar(width * zoom), SkIntToScalar(height * zoom));
97 fMatrix.setScale(SkIntToScalar(zoom), SkIntToScalar(zoom));
98 fInverse.setScale(SK_Scalar1 / zoom, SK_Scalar1 / zoom);
99 fShader0 = sk_tool_utils::create_checkerboard_shader(0xFFDDDDDD, 0xFFFFFFFF, zoom);
100 fShader1 = SkShader::MakeColorShader(SK_ColorWHITE);
101 fShader = fShader0;
102
103 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
104 fMinSurface = SkSurface::MakeRaster(info);
105 info = info.makeWH(width * zoom, height * zoom);
106 fMaxSurface = SkSurface::MakeRaster(info);
107 }
108
109 void drawBG(SkCanvas*);
110 void drawFG(SkCanvas*);
111 void drawLine(SkCanvas*, SkPoint pts[2]);
112 void drawRect(SkCanvas* canvas, SkPoint pts[2]);
113 void drawTriangle(SkCanvas* canvas, SkPoint pts[3]);
114
115 SkPaint::Cap fStrokeCap;
116
117 private:
118 bool fAA, fGrid, fShowSkeleton, fUseClip, fRectAsOval, fUseTriangle;
119 Style fStyle;
120 int fW, fH, fZoom;
121 SkMatrix fMatrix, fInverse;
122 SkRect fBounds, fClipRect;
123 sk_sp<SkShader> fShader0;
124 sk_sp<SkShader> fShader1;
125 sk_sp<SkShader> fShader;
126 sk_sp<SkSurface> fMinSurface;
127 sk_sp<SkSurface> fMaxSurface;
128
setupPaint(SkPaint * paint)129 void setupPaint(SkPaint* paint) {
130 bool aa = this->getAA();
131 paint->setStrokeCap(fStrokeCap);
132 switch (fStyle) {
133 case kHair_Style:
134 paint->setStrokeWidth(0);
135 break;
136 case kStroke_Style:
137 paint->setStrokeWidth(fStrokeWidth);
138 break;
139 }
140 paint->setAntiAlias(aa);
141 }
142
setupSkeletonPaint(SkPaint * paint)143 void setupSkeletonPaint(SkPaint* paint) {
144 paint->setStyle(SkPaint::kStroke_Style);
145 paint->setStrokeWidth(WIRE_FRAME_SIZE);
146 paint->setColor(fShowSkeleton ? WIRE_FRAME_COLOR : 0);
147 paint->setAntiAlias(true);
148 }
149
150 void drawTriangleSkeleton(SkCanvas* max, const SkPoint pts[]);
151 void drawLineSkeleton(SkCanvas* max, const SkPoint pts[]);
drawRectSkeleton(SkCanvas * max,const SkRect & r)152 void drawRectSkeleton(SkCanvas* max, const SkRect& r) {
153 SkPaint paint;
154 this->setupSkeletonPaint(&paint);
155 SkPath path;
156
157 fRectAsOval ? path.addOval(r) : path.addRect(r);
158 max->drawPath(path, paint);
159 }
160
copyMinToMax()161 void copyMinToMax() {
162 erase(fMaxSurface.get());
163 SkCanvas* canvas = fMaxSurface->getCanvas();
164 canvas->save();
165 canvas->concat(fMatrix);
166 fMinSurface->draw(canvas, 0, 0, nullptr);
167 canvas->restore();
168
169 SkPaint paint;
170 paint.setBlendMode(SkBlendMode::kClear);
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(nullptr);
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 if (fStyle == kStroke_Style) {
235 SkPaint p;
236 p.setStyle(SkPaint::kStroke_Style);
237 p.setStrokeWidth(fStrokeWidth * fZoom);
238 p.setStrokeCap(fStrokeCap);
239 SkPath dst;
240 p.getFillPath(path, &dst);
241 path = dst;
242
243 path.moveTo(pts[0]);
244 path.lineTo(pts[1]);
245 }
246 max->drawPath(path, paint);
247 }
248
drawLine(SkCanvas * canvas,SkPoint pts[])249 void FatBits::drawLine(SkCanvas* canvas, SkPoint pts[]) {
250 SkPaint paint;
251
252 fInverse.mapPoints(pts, 2);
253
254 if (fGrid) {
255 apply_grid(pts, 2);
256 }
257
258 erase(fMinSurface.get());
259 this->setupPaint(&paint);
260 paint.setColor(FAT_PIXEL_COLOR);
261 if (fUseClip) {
262 fMinSurface->getCanvas()->save();
263 SkRect r = fClipRect;
264 r.inset(SK_Scalar1/3, SK_Scalar1/3);
265 fMinSurface->getCanvas()->clipRect(r, kIntersect_SkClipOp, true);
266 }
267 fMinSurface->getCanvas()->drawLine(pts[0], pts[1], paint);
268 if (fUseClip) {
269 fMinSurface->getCanvas()->restore();
270 }
271 this->copyMinToMax();
272
273 SkCanvas* max = fMaxSurface->getCanvas();
274
275 fMatrix.mapPoints(pts, 2);
276 this->drawLineSkeleton(max, pts);
277
278 fMaxSurface->draw(canvas, 0, 0, nullptr);
279 }
280
drawRect(SkCanvas * canvas,SkPoint pts[2])281 void FatBits::drawRect(SkCanvas* canvas, SkPoint pts[2]) {
282 SkPaint paint;
283
284 fInverse.mapPoints(pts, 2);
285
286 if (fGrid) {
287 apply_grid(pts, 2);
288 }
289
290 SkRect r;
291 r.set(pts, 2);
292
293 erase(fMinSurface.get());
294 this->setupPaint(&paint);
295 paint.setColor(FAT_PIXEL_COLOR);
296 {
297 SkCanvas* c = fMinSurface->getCanvas();
298 fRectAsOval ? c->drawOval(r, paint) : c->drawRect(r, paint);
299 }
300 this->copyMinToMax();
301
302 SkCanvas* max = fMaxSurface->getCanvas();
303
304 fMatrix.mapPoints(pts, 2);
305 r.set(pts, 2);
306 this->drawRectSkeleton(max, r);
307
308 fMaxSurface->draw(canvas, 0, 0, nullptr);
309 }
310
drawTriangleSkeleton(SkCanvas * max,const SkPoint pts[])311 void FatBits::drawTriangleSkeleton(SkCanvas* max, const SkPoint pts[]) {
312 SkPaint paint;
313 this->setupSkeletonPaint(&paint);
314
315 SkPath path;
316 path.moveTo(pts[0]);
317 path.lineTo(pts[1]);
318 path.lineTo(pts[2]);
319 path.close();
320
321 max->drawPath(path, paint);
322 }
323
drawTriangle(SkCanvas * canvas,SkPoint pts[3])324 void FatBits::drawTriangle(SkCanvas* canvas, SkPoint pts[3]) {
325 SkPaint paint;
326
327 fInverse.mapPoints(pts, 3);
328
329 if (fGrid) {
330 apply_grid(pts, 3);
331 }
332
333 SkPath path;
334 path.moveTo(pts[0]);
335 path.lineTo(pts[1]);
336 path.lineTo(pts[2]);
337 path.close();
338
339 erase(fMinSurface.get());
340 this->setupPaint(&paint);
341 paint.setColor(FAT_PIXEL_COLOR);
342 fMinSurface->getCanvas()->drawPath(path, paint);
343 this->copyMinToMax();
344
345 SkCanvas* max = fMaxSurface->getCanvas();
346
347 fMatrix.mapPoints(pts, 3);
348 this->drawTriangleSkeleton(max, pts);
349
350 fMaxSurface->draw(canvas, 0, 0, nullptr);
351 }
352
353 ///////////////////////////////////////////////////////////////////////////////////////////////////
354
355 class IndexClick : public SkView::Click {
356 int fIndex;
357 public:
IndexClick(SkView * v,int index)358 IndexClick(SkView* v, int index) : SkView::Click(v), fIndex(index) {}
359
GetIndex(SkView::Click * click)360 static int GetIndex(SkView::Click* click) {
361 return ((IndexClick*)click)->fIndex;
362 }
363 };
364
365 class DrawLineView : public SampleView {
366 FatBits fFB;
367 SkPoint fPts[3];
368 bool fIsRect;
369 int fZoom = 64;
370 public:
DrawLineView()371 DrawLineView() {
372 fFB.setWHZ(24*2, 16*2, fZoom);
373 fPts[0].set(1, 1);
374 fPts[1].set(5, 4);
375 fPts[2].set(2, 6);
376 SkMatrix::MakeScale(SkIntToScalar(fZoom)).mapPoints(fPts, 3);
377 fIsRect = false;
378 }
379
setStyle(FatBits::Style s)380 void setStyle(FatBits::Style s) {
381 fFB.setStyle(s);
382 }
383
384 protected:
onQuery(SkEvent * evt)385 bool onQuery(SkEvent* evt) override {
386 if (SampleCode::TitleQ(*evt)) {
387 SampleCode::TitleR(evt, "FatBits");
388 return true;
389 }
390 SkUnichar uni;
391 if (SampleCode::CharQ(*evt, &uni)) {
392 switch (uni) {
393 case 'c':
394 fFB.setUseClip(!fFB.getUseClip());
395 return true;
396 case 'r':
397 fIsRect = !fIsRect;
398 return true;
399 case 'o':
400 fFB.toggleRectAsOval();
401 return true;
402 case 'x':
403 fFB.setGrid(!fFB.getGrid());
404 return true;
405 case 's':
406 if (FatBits::kStroke_Style == fFB.getStyle()) {
407 this->setStyle(FatBits::kHair_Style);
408 } else {
409 this->setStyle(FatBits::kStroke_Style);
410 }
411 return true;
412 case 'k': {
413 const SkPaint::Cap caps[] = {
414 SkPaint::kButt_Cap, SkPaint::kRound_Cap, SkPaint::kSquare_Cap,
415 };
416 fFB.fStrokeCap = caps[(fFB.fStrokeCap + 1) % 3];
417 return true;
418 } break;
419 case 'a':
420 fFB.setAA(!fFB.getAA());
421 return true;
422 case 'w':
423 fFB.setShowSkeleton(!fFB.getShowSkeleton());
424 return true;
425 case 'g':
426 fFB.togglePixelColors();
427 return true;
428 case 't':
429 fFB.setTriangle(!fFB.getTriangle());
430 return true;
431 case '-':
432 fFB.fStrokeWidth -= 0.125f;
433 return true;
434 case '=':
435 fFB.fStrokeWidth += 0.125f;
436 return true;
437 }
438 }
439 return this->INHERITED::onQuery(evt);
440 }
441
onDrawContent(SkCanvas * canvas)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->drawString(str, 10, 16, paint);
465 }
466 }
467
onFindClickHandler(SkScalar x,SkScalar y,unsigned modi)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 (SkPointPriv::EqualsWithinTolerance(fPts[i], pt, tol)) {
476 index = i;
477 break;
478 }
479 }
480 return new IndexClick(this, index);
481 }
482
onClick(Click * click)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 return true;
495 }
496
497 private:
498
499 typedef SampleView INHERITED;
500 };
501
502 //////////////////////////////////////////////////////////////////////////////
503
MyFactory()504 static SkView* MyFactory() { return new DrawLineView; }
505 static SkViewRegister reg(MyFactory);
506