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 "include/core/SkCanvas.h" 9#include "include/core/SkPaint.h" 10#include "include/core/SkPathBuilder.h" 11#include "include/utils/SkRandom.h" 12#include "samplecode/Sample.h" 13 14// Generates y values for the chart plots. 15static 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 == nullptr. 27// The plots are animated by rotating the data points by leftShift. 28static void gen_paths(const SkTDArray<SkScalar>& topData, 29 const SkTDArray<SkScalar>* bottomData, 30 SkScalar yBase, 31 SkScalar xLeft, SkScalar xDelta, 32 int leftShift, 33 SkPathBuilder* plot, SkPathBuilder* fill) { 34 plot->incReserve(topData.count()); 35 if (nullptr == bottomData) { 36 fill->incReserve(topData.count() + 2); 37 } else { 38 fill->incReserve(2 * topData.count()); 39 } 40 41 leftShift %= topData.count(); 42 SkScalar x = xLeft; 43 44 // Account for the leftShift using two loops 45 int shiftToEndCount = topData.count() - leftShift; 46 plot->moveTo(x, topData[leftShift]); 47 fill->moveTo(x, topData[leftShift]); 48 49 for (int i = 1; i < shiftToEndCount; ++i) { 50 plot->lineTo(x, topData[i + leftShift]); 51 fill->lineTo(x, topData[i + leftShift]); 52 x += xDelta; 53 } 54 55 for (int i = 0; i < leftShift; ++i) { 56 plot->lineTo(x, topData[i]); 57 fill->lineTo(x, topData[i]); 58 x += xDelta; 59 } 60 61 if (bottomData) { 62 SkASSERT(bottomData->count() == topData.count()); 63 // iterate backwards over the previous graph's data to generate the bottom of the filled 64 // area (and account for leftShift). 65 for (int i = 0; i < leftShift; ++i) { 66 x -= xDelta; 67 fill->lineTo(x, (*bottomData)[leftShift - 1 - i]); 68 } 69 for (int i = 0; i < shiftToEndCount; ++i) { 70 x -= xDelta; 71 fill->lineTo(x, (*bottomData)[bottomData->count() - 1 - i]); 72 } 73 } else { 74 fill->lineTo(x - xDelta, yBase); 75 fill->lineTo(xLeft, yBase); 76 } 77} 78 79// A set of scrolling line plots with the area between each plot filled. Stresses out GPU path 80// filling 81class ChartView : public Sample { 82 inline static constexpr int kNumGraphs = 5; 83 inline static constexpr int kPixelsPerTick = 3; 84 inline static constexpr int kShiftPerFrame = 1; 85 int fShift = 0; 86 SkISize fSize = {-1, -1}; 87 SkTDArray<SkScalar> fData[kNumGraphs]; 88 89 SkString name() override { return SkString("Chart"); } 90 91 void onDrawContent(SkCanvas* canvas) override { 92 bool sizeChanged = false; 93 if (canvas->getBaseLayerSize() != fSize) { 94 fSize = canvas->getBaseLayerSize(); 95 sizeChanged = true; 96 } 97 98 SkScalar ySpread = SkIntToScalar(fSize.fHeight / 20); 99 100 SkScalar height = SkIntToScalar(fSize.fHeight); 101 102 if (sizeChanged) { 103 int dataPointCount = std::max(fSize.fWidth / kPixelsPerTick + 1, 2); 104 105 for (int i = 0; i < kNumGraphs; ++i) { 106 SkScalar y = (kNumGraphs - i) * (height - ySpread) / (kNumGraphs + 1); 107 fData[i].reset(); 108 gen_data(y, ySpread, dataPointCount, fData + i); 109 } 110 } 111 112 canvas->clear(0xFFE0F0E0); 113 114 static SkRandom colorRand; 115 static SkColor gColors[kNumGraphs] = { 0x0 }; 116 if (0 == gColors[0]) { 117 for (int i = 0; i < kNumGraphs; ++i) { 118 gColors[i] = colorRand.nextU() | 0xff000000; 119 } 120 } 121 122 static const SkScalar kStrokeWidth = SkIntToScalar(2); 123 SkPaint plotPaint; 124 SkPaint fillPaint; 125 plotPaint.setAntiAlias(true); 126 plotPaint.setStyle(SkPaint::kStroke_Style); 127 plotPaint.setStrokeWidth(kStrokeWidth); 128 plotPaint.setStrokeCap(SkPaint::kRound_Cap); 129 plotPaint.setStrokeJoin(SkPaint::kRound_Join); 130 fillPaint.setAntiAlias(true); 131 fillPaint.setStyle(SkPaint::kFill_Style); 132 133 SkPathBuilder plotPath, fillPath; 134 SkTDArray<SkScalar>* prevData = nullptr; 135 136 for (int i = 0; i < kNumGraphs; ++i) { 137 gen_paths(fData[i], 138 prevData, 139 height, 140 0, 141 SkIntToScalar(kPixelsPerTick), 142 fShift, 143 &plotPath, 144 &fillPath); 145 146 // Make the fills partially transparent 147 fillPaint.setColor((gColors[i] & 0x00ffffff) | 0x80000000); 148 canvas->drawPath(fillPath.detach(), fillPaint); 149 150 plotPaint.setColor(gColors[i]); 151 canvas->drawPath(plotPath.detach(), plotPaint); 152 153 prevData = fData + i; 154 } 155 156 fShift += kShiftPerFrame; 157 } 158}; 159 160DEF_SAMPLE( return new ChartView(); ) 161