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