blob: 35f8206f76f2bd815a512db8a802428ff6033349 [file] [log] [blame]
bsalomon@google.com9053c2e2013-02-13 21:22:13 +00001
2/*
3 * Copyright 2013 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8#include "SampleCode.h"
9#include "SkCanvas.h"
10#include "SkPaint.h"
11#include "SkRandom.h"
12#include "SkView.h"
13
14namespace {
15
16// Generates y values for the chart plots.
17void gen_data(SkScalar yAvg, SkScalar ySpread, int count, SkTDArray<SkScalar>* dataPts) {
18 dataPts->setCount(count);
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +000019 static SkRandom gRandom;
bsalomon@google.com9053c2e2013-02-13 21:22:13 +000020 for (int i = 0; i < count; ++i) {
21 (*dataPts)[i] = gRandom.nextRangeScalar(yAvg - SkScalarHalf(ySpread),
22 yAvg + SkScalarHalf(ySpread));
23 }
24}
25
26// Generates a path to stroke along the top of each plot and a fill path for the area below each
27// plot. The fill path is bounded below by the bottomData plot points or a horizontal line at
28// yBase if bottomData == NULL.
29// The plots are animated by rotating the data points by leftShift.
30void gen_paths(const SkTDArray<SkScalar>& topData,
31 const SkTDArray<SkScalar>* bottomData,
32 SkScalar yBase,
33 SkScalar xLeft, SkScalar xDelta,
34 int leftShift,
35 SkPath* plot, SkPath* fill) {
36 plot->rewind();
37 fill->rewind();
38 plot->incReserve(topData.count());
39 if (NULL == bottomData) {
40 fill->incReserve(topData.count() + 2);
41 } else {
42 fill->incReserve(2 * topData.count());
43 }
44
45 leftShift %= topData.count();
46 SkScalar x = xLeft;
47
48 // Account for the leftShift using two loops
49 int shiftToEndCount = topData.count() - leftShift;
50 plot->moveTo(x, topData[leftShift]);
51 fill->moveTo(x, topData[leftShift]);
52
53 for (int i = 1; i < shiftToEndCount; ++i) {
54 plot->lineTo(x, topData[i + leftShift]);
55 fill->lineTo(x, topData[i + leftShift]);
56 x += xDelta;
57 }
58
59 for (int i = 0; i < leftShift; ++i) {
60 plot->lineTo(x, topData[i]);
61 fill->lineTo(x, topData[i]);
62 x += xDelta;
63 }
64
65 if (NULL != bottomData) {
66 SkASSERT(bottomData->count() == topData.count());
67 // iterate backwards over the previous graph's data to generate the bottom of the filled
68 // area (and account for leftShift).
69 for (int i = 0; i < leftShift; ++i) {
70 x -= xDelta;
71 fill->lineTo(x, (*bottomData)[leftShift - 1 - i]);
72 }
73 for (int i = 0; i < shiftToEndCount; ++i) {
74 x -= xDelta;
75 fill->lineTo(x, (*bottomData)[bottomData->count() - 1 - i]);
76 }
77 } else {
78 fill->lineTo(x - xDelta, yBase);
79 fill->lineTo(xLeft, yBase);
80 }
81}
82
83}
84
85// A set of scrolling line plots with the area between each plot filled. Stresses out GPU path
86// filling
87class ChartView : public SampleView {
88public:
89 ChartView() {
90 fShift = 0;
91 fSize.set(-1, -1);
92 }
93
94protected:
95 virtual bool onQuery(SkEvent* evt) SK_OVERRIDE {
96 if (SampleCode::TitleQ(*evt)) {
97 SampleCode::TitleR(evt, "Chart");
98 return true;
99 }
100 return this->INHERITED::onQuery(evt);
101 }
102
103 virtual void onDrawContent(SkCanvas* canvas) SK_OVERRIDE {
104 bool sizeChanged = false;
105 if (canvas->getDeviceSize() != fSize) {
106 fSize = canvas->getDeviceSize();
107 sizeChanged = true;
108 }
109
110 SkScalar ySpread = SkIntToScalar(fSize.fHeight / 20);
111
112 SkScalar height = SkIntToScalar(fSize.fHeight);
113
114 if (sizeChanged) {
115 int dataPointCount = SkMax32(fSize.fWidth / kPixelsPerTick + 1, 2);
116
117 for (int i = 0; i < kNumGraphs; ++i) {
118 SkScalar y = (kNumGraphs - i) * (height - ySpread) / (kNumGraphs + 1);
119 fData[i].reset();
120 gen_data(y, ySpread, dataPointCount, fData + i);
121 }
122 }
123
124 canvas->clear(0xFFE0F0E0);
125
commit-bot@chromium.orge0e7cfe2013-09-09 20:09:12 +0000126 static SkRandom colorRand;
bsalomon@google.com9053c2e2013-02-13 21:22:13 +0000127 static SkColor gColors[kNumGraphs] = { 0x0 };
128 if (0 == gColors[0]) {
129 for (int i = 0; i < kNumGraphs; ++i) {
130 gColors[i] = colorRand.nextU() | 0xff000000;
131 }
132 }
133
134 SkPath plotPath;
135 SkPath fillPath;
136
137 static const SkScalar kStrokeWidth = SkIntToScalar(2);
138 SkPaint plotPaint;
139 SkPaint fillPaint;
140 plotPaint.setAntiAlias(true);
141 plotPaint.setStyle(SkPaint::kStroke_Style);
142 plotPaint.setStrokeWidth(kStrokeWidth);
143 plotPaint.setStrokeCap(SkPaint::kRound_Cap);
144 plotPaint.setStrokeJoin(SkPaint::kRound_Join);
145 fillPaint.setAntiAlias(true);
146 fillPaint.setStyle(SkPaint::kFill_Style);
147
148 SkTDArray<SkScalar>* prevData = NULL;
149 for (int i = 0; i < kNumGraphs; ++i) {
150 gen_paths(fData[i],
151 prevData,
152 height,
153 0,
154 SkIntToScalar(kPixelsPerTick),
155 fShift,
156 &plotPath,
157 &fillPath);
158
159 // Make the fills partially transparent
160 fillPaint.setColor((gColors[i] & 0x00ffffff) | 0x80000000);
161 canvas->drawPath(fillPath, fillPaint);
162
163 plotPaint.setColor(gColors[i]);
164 canvas->drawPath(plotPath, plotPaint);
165
166 prevData = fData + i;
167 }
168
169 fShift += kShiftPerFrame;
170 this->inval(NULL);
171 }
172
173private:
174 enum {
175 kNumGraphs = 5,
176 kPixelsPerTick = 3,
177 kShiftPerFrame = 1,
178 };
179 int fShift;
180 SkISize fSize;
181 SkTDArray<SkScalar> fData[kNumGraphs];
182 typedef SampleView INHERITED;
183};
184
185//////////////////////////////////////////////////////////////////////////////
186
187static SkView* MyFactory() { return new ChartView; }
188static SkViewRegister reg(MyFactory);