xref: /aosp_15_r20/external/skia/tools/viewer/FatBitsSlide.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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