1 /*
2  * Copyright 2011 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/SkBitmap.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColorFilter.h"
11 #include "include/core/SkColorPriv.h"
12 #include "include/core/SkGraphics.h"
13 #include "include/core/SkPath.h"
14 #include "include/core/SkRegion.h"
15 #include "include/core/SkShader.h"
16 #include "include/core/SkStream.h"
17 #include "include/core/SkTime.h"
18 #include "include/core/SkTypeface.h"
19 #include "include/effects/SkCornerPathEffect.h"
20 #include "include/effects/SkGradientShader.h"
21 #include "include/private/SkTo.h"
22 #include "include/utils/SkRandom.h"
23 #include "samplecode/Sample.h"
24 #include "src/utils/SkUTF.h"
25 
26 static SkRandom gRand;
27 
generate_pts(SkPoint pts[],int count,int w,int h)28 static void generate_pts(SkPoint pts[], int count, int w, int h) {
29     for (int i = 0; i < count; i++) {
30         pts[i].set(gRand.nextUScalar1() * 3 * w - SkIntToScalar(w),
31                    gRand.nextUScalar1() * 3 * h - SkIntToScalar(h));
32     }
33 }
34 
check_zeros(const SkPMColor pixels[],int count,int skip)35 static bool check_zeros(const SkPMColor pixels[], int count, int skip) {
36     for (int i = 0; i < count; i++) {
37         if (*pixels) {
38             return false;
39         }
40         pixels += skip;
41     }
42     return true;
43 }
44 
check_bitmap_margin(const SkBitmap & bm,int margin)45 static bool check_bitmap_margin(const SkBitmap& bm, int margin) {
46     size_t rb = bm.rowBytes();
47     for (int i = 0; i < margin; i++) {
48         if (!check_zeros(bm.getAddr32(0, i), bm.width(), 1)) {
49             return false;
50         }
51         int bottom = bm.height() - i - 1;
52         if (!check_zeros(bm.getAddr32(0, bottom), bm.width(), 1)) {
53             return false;
54         }
55         // left column
56         if (!check_zeros(bm.getAddr32(i, 0), bm.height(), SkToInt(rb >> 2))) {
57             return false;
58         }
59         int right = bm.width() - margin + i;
60         if (!check_zeros(bm.getAddr32(right, 0), bm.height(),
61                          SkToInt(rb >> 2))) {
62             return false;
63         }
64     }
65     return true;
66 }
67 
68 #define WIDTH   620
69 #define HEIGHT  460
70 #define MARGIN  10
71 
line_proc(SkCanvas * canvas,const SkPaint & paint,const SkBitmap & bm)72 static void line_proc(SkCanvas* canvas, const SkPaint& paint,
73                       const SkBitmap& bm) {
74     const int N = 2;
75     SkPoint pts[N];
76     for (int i = 0; i < 400; i++) {
77         generate_pts(pts, N, WIDTH, HEIGHT);
78 
79         canvas->drawLine(pts[0], pts[1], paint);
80         if (!check_bitmap_margin(bm, MARGIN)) {
81             SkDebugf("---- hairline failure (%g %g) (%g %g)\n",
82                      pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
83             break;
84         }
85     }
86 }
87 
poly_proc(SkCanvas * canvas,const SkPaint & paint,const SkBitmap & bm)88 static void poly_proc(SkCanvas* canvas, const SkPaint& paint,
89                       const SkBitmap& bm) {
90     const int N = 8;
91     SkPoint pts[N];
92     for (int i = 0; i < 50; i++) {
93         generate_pts(pts, N, WIDTH, HEIGHT);
94 
95         SkPath path;
96         path.moveTo(pts[0]);
97         for (int j = 1; j < N; j++) {
98             path.lineTo(pts[j]);
99         }
100         canvas->drawPath(path, paint);
101     }
102 }
103 
ave(const SkPoint & a,const SkPoint & b)104 static SkPoint ave(const SkPoint& a, const SkPoint& b) {
105     SkPoint c = a + b;
106     c.fX = SkScalarHalf(c.fX);
107     c.fY = SkScalarHalf(c.fY);
108     return c;
109 }
110 
quad_proc(SkCanvas * canvas,const SkPaint & paint,const SkBitmap & bm)111 static void quad_proc(SkCanvas* canvas, const SkPaint& paint,
112                       const SkBitmap& bm) {
113     const int N = 30;
114     SkPoint pts[N];
115     for (int i = 0; i < 10; i++) {
116         generate_pts(pts, N, WIDTH, HEIGHT);
117 
118         SkPath path;
119         path.moveTo(pts[0]);
120         for (int j = 1; j < N - 2; j++) {
121             path.quadTo(pts[j], ave(pts[j], pts[j+1]));
122         }
123         path.quadTo(pts[N - 2], pts[N - 1]);
124 
125         canvas->drawPath(path, paint);
126     }
127 }
128 
add_cubic(SkPath * path,const SkPoint & mid,const SkPoint & end)129 static void add_cubic(SkPath* path, const SkPoint& mid, const SkPoint& end) {
130     SkPoint start;
131     path->getLastPt(&start);
132     path->cubicTo(ave(start, mid), ave(mid, end), end);
133 }
134 
cube_proc(SkCanvas * canvas,const SkPaint & paint,const SkBitmap & bm)135 static void cube_proc(SkCanvas* canvas, const SkPaint& paint,
136                       const SkBitmap& bm) {
137     const int N = 30;
138     SkPoint pts[N];
139     for (int i = 0; i < 10; i++) {
140         generate_pts(pts, N, WIDTH, HEIGHT);
141 
142         SkPath path;
143         path.moveTo(pts[0]);
144         for (int j = 1; j < N - 2; j++) {
145             add_cubic(&path, pts[j], ave(pts[j], pts[j+1]));
146         }
147         add_cubic(&path, pts[N - 2], pts[N - 1]);
148 
149         canvas->drawPath(path, paint);
150     }
151 }
152 
153 typedef void (*HairProc)(SkCanvas*, const SkPaint&, const SkBitmap&);
154 
155 static const struct {
156     const char* fName;
157     HairProc    fProc;
158 } gProcs[] = {
159     { "line",   line_proc },
160     { "poly",   poly_proc },
161     { "quad",   quad_proc },
162     { "cube",   cube_proc },
163 };
164 
cycle_hairproc_index(int index)165 static int cycle_hairproc_index(int index) {
166     return (index + 1) % SK_ARRAY_COUNT(gProcs);
167 }
168 
169 class HairlineView : public Sample {
170     SkMSec fNow;
171     int fProcIndex;
172     bool fDoAA;
173 public:
HairlineView()174     HairlineView() {
175         fProcIndex = 0;
176         fDoAA = true;
177         fNow = 0;
178     }
179 
180 protected:
name()181     SkString name() override { return SkStringPrintf("Hair-%s", gProcs[fProcIndex].fName); }
182 
show_bitmaps(SkCanvas * canvas,const SkBitmap & b0,const SkBitmap & b1,const SkIRect & inset)183     void show_bitmaps(SkCanvas* canvas, const SkBitmap& b0, const SkBitmap& b1,
184                       const SkIRect& inset) {
185         canvas->drawBitmap(b0, 0, 0, nullptr);
186         canvas->drawBitmap(b1, SkIntToScalar(b0.width()), 0, nullptr);
187     }
188 
onDrawContent(SkCanvas * canvas)189     void onDrawContent(SkCanvas* canvas) override {
190         gRand.setSeed(fNow);
191 
192         SkBitmap bm, bm2;
193         bm.allocN32Pixels(WIDTH + MARGIN*2, HEIGHT + MARGIN*2);
194         // this will erase our margin, which we want to always stay 0
195         bm.eraseColor(SK_ColorTRANSPARENT);
196 
197         bm2.installPixels(SkImageInfo::MakeN32Premul(WIDTH, HEIGHT),
198                           bm.getAddr32(MARGIN, MARGIN), bm.rowBytes());
199 
200         SkCanvas c2(bm2);
201         SkPaint paint;
202         paint.setAntiAlias(fDoAA);
203         paint.setStyle(SkPaint::kStroke_Style);
204 
205         bm2.eraseColor(SK_ColorTRANSPARENT);
206         gProcs[fProcIndex].fProc(&c2, paint, bm);
207         canvas->drawBitmap(bm2, SkIntToScalar(10), SkIntToScalar(10), nullptr);
208     }
209 
onAnimate(double)210     bool onAnimate(double /*nanos*/) override {
211         if (fDoAA) {
212             fProcIndex = cycle_hairproc_index(fProcIndex);
213             // todo: signal that we want to rebuild our TITLE
214         }
215         fDoAA = !fDoAA;
216         return true;
217     }
218 
onFindClickHandler(SkScalar x,SkScalar y,skui::ModifierKey modi)219     Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey modi) override {
220         fDoAA = !fDoAA;
221         return this->INHERITED::onFindClickHandler(x, y, modi);
222     }
223 
224 
225 private:
226     typedef Sample INHERITED;
227 };
228 
229 //////////////////////////////////////////////////////////////////////////////
230 
231 DEF_SAMPLE( return new HairlineView(); )
232