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.set(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.set(0, 0, SkIntToScalar(width * zoom), SkIntToScalar(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.set(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.set(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,ModifierKey modi)471 Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, 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