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