Home | History | Annotate | Download | only in samplecode
      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.
     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.
     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:
     86     ChartView() {
     87         fShift = 0;
     88         fSize.set(-1, -1);
     89     }
     90 
     91 protected:
     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 
    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 
    184 static SkView* MyFactory() { return new ChartView; }
    185 static SkViewRegister reg(MyFactory);
    186