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