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