SampleChart.cpp 5.3 KB

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