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