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