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/SkFont.h"
12 #include "include/core/SkImageInfo.h"
13 #include "include/core/SkMatrix.h"
14 #include "include/core/SkPaint.h"
15 #include "include/core/SkPath.h"
16 #include "include/core/SkPathUtils.h"
17 #include "include/core/SkPoint.h"
18 #include "include/core/SkRect.h"
19 #include "include/core/SkRefCnt.h"
20 #include "include/core/SkScalar.h"
21 #include "include/core/SkShader.h"
22 #include "include/core/SkString.h"
23 #include "include/core/SkSurface.h"
24 #include "include/core/SkTypes.h"
25 #include "src/core/SkPointPriv.h"
26 #include "tools/ToolUtils.h"
27 #include "tools/fonts/FontToolUtils.h"
28 #include "tools/viewer/ClickHandlerSlide.h"
29 #include "tools/viewer/Slide.h"
30
31 #define FAT_PIXEL_COLOR SK_ColorBLACK
32 #define PIXEL_CENTER_SIZE 3
33 #define WIRE_FRAME_COLOR 0xFFFF0000 /*0xFF00FFFF*/
34 #define WIRE_FRAME_SIZE 1.5f
35
apply_grid(SkScalar x)36 static SkScalar apply_grid(SkScalar x) {
37 const SkScalar grid = 2;
38 return SkScalarRoundToScalar(x * grid) / grid;
39 }
40
apply_grid(SkPoint pts[],int count)41 static void apply_grid(SkPoint pts[], int count) {
42 for (int i = 0; i < count; ++i) {
43 pts[i].set(apply_grid(pts[i].fX), apply_grid(pts[i].fY));
44 }
45 }
46
erase(SkSurface * surface)47 static void erase(SkSurface* surface) {
48 surface->getCanvas()->clear(SK_ColorTRANSPARENT);
49 }
50
51 class FatBits {
52 public:
FatBits()53 FatBits() {
54 fAA = false;
55 fStyle = kHair_Style;
56 fGrid = false;
57 fShowSkeleton = true;
58 fUseClip = false;
59 fRectAsOval = false;
60 fUseTriangle = false;
61 fStrokeCap = SkPaint::kButt_Cap;
62
63 fClipRect.setLTRB(2, 2, 11, 8);
64 }
65
getZoom() const66 int getZoom() const { return fZoom; }
67
getAA() const68 bool getAA() const { return fAA; }
setAA(bool aa)69 void setAA(bool aa) { fAA = aa; }
70
getGrid() const71 bool getGrid() const { return fGrid; }
setGrid(bool g)72 void setGrid(bool g) { fGrid = g; }
73
getShowSkeleton() const74 bool getShowSkeleton() const { return fShowSkeleton; }
setShowSkeleton(bool ss)75 void setShowSkeleton(bool ss) { fShowSkeleton = ss; }
76
getTriangle() const77 bool getTriangle() const { return fUseTriangle; }
setTriangle(bool ut)78 void setTriangle(bool ut) { fUseTriangle = ut; }
79
toggleRectAsOval()80 void toggleRectAsOval() { fRectAsOval = !fRectAsOval; }
81
togglePixelColors()82 void togglePixelColors() {
83 if (fShader == fShader0) {
84 fShader = fShader1;
85 } else {
86 fShader = fShader0;
87 }
88 }
89
90 float fStrokeWidth = 1;
91
getUseClip() const92 bool getUseClip() const { return fUseClip; }
setUseClip(bool uc)93 void setUseClip(bool uc) { fUseClip = uc; }
94
95 enum Style {
96 kHair_Style,
97 kStroke_Style,
98 };
getStyle() const99 Style getStyle() const { return fStyle; }
setStyle(Style s)100 void setStyle(Style s) { fStyle = s; }
101
setWHZ(int width,int height,int zoom)102 void setWHZ(int width, int height, int zoom) {
103 fW = width;
104 fH = height;
105 fZoom = zoom;
106 fBounds.setIWH(width * zoom, height * zoom);
107 fMatrix.setScale(SkIntToScalar(zoom), SkIntToScalar(zoom));
108 fInverse.setScale(SK_Scalar1 / zoom, SK_Scalar1 / zoom);
109 fShader0 = ToolUtils::create_checkerboard_shader(0xFFDDDDDD, 0xFFFFFFFF, zoom);
110 fShader1 = SkShaders::Color(SK_ColorWHITE);
111 fShader = fShader0;
112
113 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
114 fMinSurface = SkSurfaces::Raster(info);
115 info = info.makeWH(width * zoom, height * zoom);
116 fMaxSurface = SkSurfaces::Raster(info);
117 }
118
119 void drawBG(SkCanvas*);
120 void drawFG(SkCanvas*);
121 void drawLine(SkCanvas*, SkPoint pts[2]);
122 void drawRect(SkCanvas* canvas, SkPoint pts[2]);
123 void drawTriangle(SkCanvas* canvas, SkPoint pts[3]);
124
125 SkPaint::Cap fStrokeCap;
126
127 private:
128 bool fAA, fGrid, fShowSkeleton, fUseClip, fRectAsOval, fUseTriangle;
129 Style fStyle;
130 int fW, fH, fZoom;
131 SkMatrix fMatrix, fInverse;
132 SkRect fBounds, fClipRect;
133 sk_sp<SkShader> fShader0;
134 sk_sp<SkShader> fShader1;
135 sk_sp<SkShader> fShader;
136 sk_sp<SkSurface> fMinSurface;
137 sk_sp<SkSurface> fMaxSurface;
138
setupPaint(SkPaint * paint)139 void setupPaint(SkPaint* paint) {
140 bool aa = this->getAA();
141 paint->setStrokeCap(fStrokeCap);
142 switch (fStyle) {
143 case kHair_Style:
144 paint->setStrokeWidth(0);
145 break;
146 case kStroke_Style:
147 paint->setStrokeWidth(fStrokeWidth);
148 break;
149 }
150 paint->setAntiAlias(aa);
151 }
152
setupSkeletonPaint(SkPaint * paint)153 void setupSkeletonPaint(SkPaint* paint) {
154 paint->setStyle(SkPaint::kStroke_Style);
155 paint->setStrokeWidth(WIRE_FRAME_SIZE);
156 paint->setColor(fShowSkeleton ? WIRE_FRAME_COLOR : 0);
157 paint->setAntiAlias(true);
158 }
159
160 void drawTriangleSkeleton(SkCanvas* max, const SkPoint pts[]);
161 void drawLineSkeleton(SkCanvas* max, const SkPoint pts[]);
drawRectSkeleton(SkCanvas * max,const SkRect & r)162 void drawRectSkeleton(SkCanvas* max, const SkRect& r) {
163 SkPaint paint;
164 this->setupSkeletonPaint(&paint);
165 SkPath path;
166
167 fRectAsOval ? path.addOval(r) : path.addRect(r);
168 max->drawPath(path, paint);
169 }
170
copyMinToMax()171 void copyMinToMax() {
172 erase(fMaxSurface.get());
173 SkCanvas* canvas = fMaxSurface->getCanvas();
174 canvas->save();
175 canvas->concat(fMatrix);
176 fMinSurface->draw(canvas, 0, 0);
177 canvas->restore();
178
179 SkPaint paint;
180 paint.setBlendMode(SkBlendMode::kClear);
181 for (int iy = 1; iy < fH; ++iy) {
182 SkScalar y = SkIntToScalar(iy * fZoom);
183 canvas->drawLine(0, y - SK_ScalarHalf, 999, y - SK_ScalarHalf, paint);
184 }
185 for (int ix = 1; ix < fW; ++ix) {
186 SkScalar x = SkIntToScalar(ix * fZoom);
187 canvas->drawLine(x - SK_ScalarHalf, 0, x - SK_ScalarHalf, 999, paint);
188 }
189 }
190 };
191
drawBG(SkCanvas * canvas)192 void FatBits::drawBG(SkCanvas* canvas) {
193 SkPaint paint;
194
195 paint.setShader(fShader);
196 canvas->drawRect(fBounds, paint);
197 paint.setShader(nullptr);
198 }
199
drawFG(SkCanvas * canvas)200 void FatBits::drawFG(SkCanvas* canvas) {
201 SkPaint inner, outer;
202
203 inner.setAntiAlias(true);
204 inner.setColor(SK_ColorBLACK);
205 inner.setStrokeWidth(PIXEL_CENTER_SIZE);
206
207 outer.setAntiAlias(true);
208 outer.setColor(SK_ColorWHITE);
209 outer.setStrokeWidth(PIXEL_CENTER_SIZE + 2);
210
211 SkScalar half = SkIntToScalar(fZoom) / 2;
212 for (int iy = 0; iy < fH; ++iy) {
213 SkScalar y = SkIntToScalar(iy * fZoom) + half;
214 for (int ix = 0; ix < fW; ++ix) {
215 SkScalar x = SkIntToScalar(ix * fZoom) + half;
216
217 canvas->drawPoint(x, y, outer);
218 canvas->drawPoint(x, y, inner);
219 }
220 }
221
222 if (fUseClip) {
223 SkPaint p;
224 p.setStyle(SkPaint::kStroke_Style);
225 p.setColor(SK_ColorLTGRAY);
226 SkRect r = {
227 fClipRect.fLeft * fZoom,
228 fClipRect.fTop * fZoom,
229 fClipRect.fRight * fZoom,
230 fClipRect.fBottom * fZoom
231 };
232 canvas->drawRect(r, p);
233 }
234 }
235
drawLineSkeleton(SkCanvas * max,const SkPoint pts[])236 void FatBits::drawLineSkeleton(SkCanvas* max, const SkPoint pts[]) {
237 SkPaint paint;
238 this->setupSkeletonPaint(&paint);
239
240 SkPath path;
241 path.moveTo(pts[0]);
242 path.lineTo(pts[1]);
243
244 if (fStyle == kStroke_Style) {
245 SkPaint p;
246 p.setStyle(SkPaint::kStroke_Style);
247 p.setStrokeWidth(fStrokeWidth * fZoom);
248 p.setStrokeCap(fStrokeCap);
249 SkPath dst;
250 skpathutils::FillPathWithPaint(path, p, &dst);
251 path = dst;
252
253 path.moveTo(pts[0]);
254 path.lineTo(pts[1]);
255 }
256 max->drawPath(path, paint);
257 }
258
drawLine(SkCanvas * canvas,SkPoint pts[2])259 void FatBits::drawLine(SkCanvas* canvas, SkPoint pts[2]) {
260 SkPaint paint;
261
262 fInverse.mapPoints(pts, 2);
263
264 if (fGrid) {
265 apply_grid(pts, 2);
266 }
267
268 erase(fMinSurface.get());
269 this->setupPaint(&paint);
270 paint.setColor(FAT_PIXEL_COLOR);
271 if (fUseClip) {
272 fMinSurface->getCanvas()->save();
273 SkRect r = fClipRect;
274 r.inset(SK_Scalar1/3, SK_Scalar1/3);
275 fMinSurface->getCanvas()->clipRect(r, SkClipOp::kIntersect, true);
276 }
277 fMinSurface->getCanvas()->drawLine(pts[0], pts[1], paint);
278 if (fUseClip) {
279 fMinSurface->getCanvas()->restore();
280 }
281 this->copyMinToMax();
282
283 SkCanvas* max = fMaxSurface->getCanvas();
284
285 fMatrix.mapPoints(pts, 2);
286 this->drawLineSkeleton(max, pts);
287
288 fMaxSurface->draw(canvas, 0, 0);
289 }
290
drawRect(SkCanvas * canvas,SkPoint pts[2])291 void FatBits::drawRect(SkCanvas* canvas, SkPoint pts[2]) {
292 SkPaint paint;
293
294 fInverse.mapPoints(pts, 2);
295
296 if (fGrid) {
297 apply_grid(pts, 2);
298 }
299
300 SkRect r;
301 r.setBounds(pts, 2);
302
303 erase(fMinSurface.get());
304 this->setupPaint(&paint);
305 paint.setColor(FAT_PIXEL_COLOR);
306 {
307 SkCanvas* c = fMinSurface->getCanvas();
308 fRectAsOval ? c->drawOval(r, paint) : c->drawRect(r, paint);
309 }
310 this->copyMinToMax();
311
312 SkCanvas* max = fMaxSurface->getCanvas();
313
314 fMatrix.mapPoints(pts, 2);
315 r.setBounds(pts, 2);
316 this->drawRectSkeleton(max, r);
317
318 fMaxSurface->draw(canvas, 0, 0);
319 }
320
drawTriangleSkeleton(SkCanvas * max,const SkPoint pts[])321 void FatBits::drawTriangleSkeleton(SkCanvas* max, const SkPoint pts[]) {
322 SkPaint paint;
323 this->setupSkeletonPaint(&paint);
324
325 SkPath path;
326 path.moveTo(pts[0]);
327 path.lineTo(pts[1]);
328 path.lineTo(pts[2]);
329 path.close();
330
331 max->drawPath(path, paint);
332 }
333
drawTriangle(SkCanvas * canvas,SkPoint pts[3])334 void FatBits::drawTriangle(SkCanvas* canvas, SkPoint pts[3]) {
335 SkPaint paint;
336
337 fInverse.mapPoints(pts, 3);
338
339 if (fGrid) {
340 apply_grid(pts, 3);
341 }
342
343 SkPath path;
344 path.moveTo(pts[0]);
345 path.lineTo(pts[1]);
346 path.lineTo(pts[2]);
347 path.close();
348
349 erase(fMinSurface.get());
350 this->setupPaint(&paint);
351 paint.setColor(FAT_PIXEL_COLOR);
352 fMinSurface->getCanvas()->drawPath(path, paint);
353 this->copyMinToMax();
354
355 SkCanvas* max = fMaxSurface->getCanvas();
356
357 fMatrix.mapPoints(pts, 3);
358 this->drawTriangleSkeleton(max, pts);
359
360 fMaxSurface->draw(canvas, 0, 0);
361 }
362
363 ///////////////////////////////////////////////////////////////////////////////////////////////////
364
365 class IndexClick : public ClickHandlerSlide::Click {
366 int fIndex;
367
368 public:
IndexClick(int index)369 IndexClick(int index) : fIndex(index) {}
370
GetIndex(Click * click)371 static int GetIndex(Click* click) { return ((IndexClick*)click)->fIndex; }
372 };
373
374 class DrawLineSlide : public ClickHandlerSlide {
375 FatBits fFB;
376 SkPoint fPts[3];
377 bool fIsRect;
378 int fZoom = 64;
379
380 public:
DrawLineSlide()381 DrawLineSlide() {
382 fFB.setWHZ(24*2, 16*2, fZoom);
383 fPts[0].set(1, 1);
384 fPts[1].set(5, 4);
385 fPts[2].set(2, 6);
386 SkMatrix::Scale(fZoom, fZoom).mapPoints(fPts, 3);
387 fIsRect = false;
388 fName = "FatBits";
389 }
390
setStyle(FatBits::Style s)391 void setStyle(FatBits::Style s) { fFB.setStyle(s); }
392
onChar(SkUnichar uni)393 bool onChar(SkUnichar uni) override {
394 switch (uni) {
395 case 'c':
396 fFB.setUseClip(!fFB.getUseClip());
397 return true;
398 case 'r':
399 fIsRect = !fIsRect;
400 return true;
401 case 'o':
402 fFB.toggleRectAsOval();
403 return true;
404 case 'x':
405 fFB.setGrid(!fFB.getGrid());
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 return true;
420 }
421 case 'a':
422 fFB.setAA(!fFB.getAA());
423 return true;
424 case 'w':
425 fFB.setShowSkeleton(!fFB.getShowSkeleton());
426 return true;
427 case 'g':
428 fFB.togglePixelColors();
429 return true;
430 case 't':
431 fFB.setTriangle(!fFB.getTriangle());
432 return true;
433 case '-':
434 fFB.fStrokeWidth -= 0.125f;
435 return true;
436 case '=':
437 fFB.fStrokeWidth += 0.125f;
438 return true;
439 }
440 return false;
441 }
442
draw(SkCanvas * canvas)443 void draw(SkCanvas* canvas) override {
444 fFB.drawBG(canvas);
445 if (fFB.getTriangle()) {
446 fFB.drawTriangle(canvas, fPts);
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.setColor(SK_ColorBLUE);
462 SkFont font(ToolUtils::DefaultTypeface(), 16);
463 canvas->drawString(str, 10, 16, font, paint);
464 }
465 }
466
467 protected:
onFindClickHandler(SkScalar x,SkScalar y,skui::ModifierKey modi)468 Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey 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(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
498 //////////////////////////////////////////////////////////////////////////////
499
500 DEF_SLIDE(return new DrawLineSlide();)
501