1/*
2 * Copyright 2013 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 "Benchmark.h"
9#include "SkCanvas.h"
10#include "SkPaint.h"
11#include "SkRandom.h"
12
13/**
14 * This is a conversion of samplecode/SampleChart.cpp into a bench. It sure would be nice to be able
15 * to write one subclass that can be a GM, bench, and/or Sample.
16 */
17
18// Generates y values for the chart plots.
19static void gen_data(SkScalar yAvg, SkScalar ySpread, int count,
20                     SkRandom* random, SkTDArray<SkScalar>* dataPts) {
21    dataPts->setCount(count);
22    for (int i = 0; i < count; ++i) {
23        (*dataPts)[i] = random->nextRangeScalar(yAvg - SkScalarHalf(ySpread),
24                                                yAvg + SkScalarHalf(ySpread));
25    }
26}
27
28// Generates a path to stroke along the top of each plot and a fill path for the area below each
29// plot. The fill path is bounded below by the bottomData plot points or a horizontal line at
30// yBase if bottomData == NULL.
31// The plots are animated by rotating the data points by leftShift.
32static void gen_paths(const SkTDArray<SkScalar>& topData,
33                      const SkTDArray<SkScalar>* bottomData,
34                      SkScalar yBase,
35                      SkScalar xLeft, SkScalar xDelta,
36                      int leftShift,
37                      SkPath* plot, SkPath* fill) {
38    plot->rewind();
39    fill->rewind();
40    plot->incReserve(topData.count());
41    if (NULL == bottomData) {
42        fill->incReserve(topData.count() + 2);
43    } else {
44        fill->incReserve(2 * topData.count());
45    }
46
47    leftShift %= topData.count();
48    SkScalar x = xLeft;
49
50    // Account for the leftShift using two loops
51    int shiftToEndCount = topData.count() - leftShift;
52    plot->moveTo(x, topData[leftShift]);
53    fill->moveTo(x, topData[leftShift]);
54
55    for (int i = 1; i < shiftToEndCount; ++i) {
56        plot->lineTo(x, topData[i + leftShift]);
57        fill->lineTo(x, topData[i + leftShift]);
58        x += xDelta;
59    }
60
61    for (int i = 0; i < leftShift; ++i) {
62        plot->lineTo(x, topData[i]);
63        fill->lineTo(x, topData[i]);
64        x += xDelta;
65    }
66
67    if (NULL != bottomData) {
68        SkASSERT(bottomData->count() == topData.count());
69        // iterate backwards over the previous graph's data to generate the bottom of the filled
70        // area (and account for leftShift).
71        for (int i = 0; i < leftShift; ++i) {
72            x -= xDelta;
73            fill->lineTo(x, (*bottomData)[leftShift - 1 - i]);
74        }
75        for (int i = 0; i < shiftToEndCount; ++i) {
76            x -= xDelta;
77            fill->lineTo(x, (*bottomData)[bottomData->count() - 1 - i]);
78        }
79    } else {
80        fill->lineTo(x - xDelta, yBase);
81        fill->lineTo(xLeft, yBase);
82    }
83}
84
85// A set of scrolling line plots with the area between each plot filled. Stresses out GPU path
86// filling
87class ChartBench : public Benchmark {
88public:
89    ChartBench(bool aa) {
90        fShift = 0;
91        fAA = aa;
92        fSize.fWidth = -1;
93        fSize.fHeight = -1;
94    }
95
96protected:
97    virtual const char* onGetName() SK_OVERRIDE {
98        if (fAA) {
99            return "chart_aa";
100        } else {
101            return "chart_bw";
102        }
103    }
104
105    virtual void onDraw(const int loops, SkCanvas* canvas) SK_OVERRIDE {
106        bool sizeChanged = false;
107        if (canvas->getDeviceSize() != fSize) {
108            fSize = canvas->getDeviceSize();
109            sizeChanged = true;
110        }
111
112        SkScalar ySpread = SkIntToScalar(fSize.fHeight / 20);
113
114        SkScalar height = SkIntToScalar(fSize.fHeight);
115        if (sizeChanged) {
116            int dataPointCount = SkMax32(fSize.fWidth / kPixelsPerTick + 1, 2);
117
118            SkRandom random;
119            for (int i = 0; i < kNumGraphs; ++i) {
120                SkScalar y = (kNumGraphs - i) * (height - ySpread) / (kNumGraphs + 1);
121                fData[i].reset();
122                gen_data(y, ySpread, dataPointCount, &random, fData + i);
123            }
124        }
125
126        SkRandom colorRand;
127        SkColor colors[kNumGraphs];
128        for (int i = 0; i < kNumGraphs; ++i) {
129            colors[i] = colorRand.nextU() | 0xff000000;
130        }
131
132        for (int frame = 0; frame < loops; ++frame) {
133
134            canvas->clear(0xFFE0F0E0);
135
136            SkPath plotPath;
137            SkPath fillPath;
138
139            static const SkScalar kStrokeWidth = SkIntToScalar(2);
140            SkPaint plotPaint;
141            SkPaint fillPaint;
142            plotPaint.setAntiAlias(fAA);
143            plotPaint.setStyle(SkPaint::kStroke_Style);
144            plotPaint.setStrokeWidth(kStrokeWidth);
145            plotPaint.setStrokeCap(SkPaint::kRound_Cap);
146            plotPaint.setStrokeJoin(SkPaint::kRound_Join);
147            fillPaint.setAntiAlias(fAA);
148            fillPaint.setStyle(SkPaint::kFill_Style);
149
150            SkTDArray<SkScalar>* prevData = NULL;
151            for (int i = 0; i < kNumGraphs; ++i) {
152                gen_paths(fData[i],
153                          prevData,
154                          height,
155                          0,
156                          SkIntToScalar(kPixelsPerTick),
157                          fShift,
158                          &plotPath,
159                          &fillPath);
160
161                // Make the fills partially transparent
162                fillPaint.setColor((colors[i] & 0x00ffffff) | 0x80000000);
163                canvas->drawPath(fillPath, fillPaint);
164
165                plotPaint.setColor(colors[i]);
166                canvas->drawPath(plotPath, plotPaint);
167
168                prevData = fData + i;
169            }
170
171            fShift += kShiftPerFrame;
172        }
173    }
174
175private:
176    enum {
177        kNumGraphs = 5,
178        kPixelsPerTick = 3,
179        kShiftPerFrame = 1,
180    };
181    int                 fShift;
182    SkISize             fSize;
183    SkTDArray<SkScalar> fData[kNumGraphs];
184    bool                fAA;
185
186    typedef Benchmark INHERITED;
187};
188
189//////////////////////////////////////////////////////////////////////////////
190
191DEF_BENCH( return new ChartBench(true); )
192DEF_BENCH( return new ChartBench(false); )
193