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