diff --git a/Telegram/SourceFiles/statistics/chart_widget.cpp b/Telegram/SourceFiles/statistics/chart_widget.cpp index c8271bf48..43cb40f35 100644 --- a/Telegram/SourceFiles/statistics/chart_widget.cpp +++ b/Telegram/SourceFiles/statistics/chart_widget.cpp @@ -950,7 +950,7 @@ void ChartWidget::setupChartArea() { // !_animationController.isFPSSlow() // || !_animationController.animating()); PainterHighQualityEnabler hp(p); - Statistic::PaintLinearChartView( + _linearChartPainter.main->paint( p, _chartData, _animationController.currentXIndices(), @@ -1076,7 +1076,7 @@ void ChartWidget::setupFooter() { // || !_animationController.animating()); PainterHighQualityEnabler hp(p); _animatedChartLines.setCacheFooter(true); - Statistic::PaintLinearChartView( + _linearChartPainter.footer->paint( p, _chartData, { 0., float64(_chartData.x.size() - 1) }, @@ -1254,6 +1254,9 @@ void ChartWidget::setupFilterButtons() { void ChartWidget::setChartData(Data::StatisticalChart chartData) { _chartData = std::move(chartData); + _linearChartPainter.main = std::make_unique(); + _linearChartPainter.footer = std::make_unique(); + setupDetails(); setupFilterButtons(); diff --git a/Telegram/SourceFiles/statistics/chart_widget.h b/Telegram/SourceFiles/statistics/chart_widget.h index 981ea5496..b2312545a 100644 --- a/Telegram/SourceFiles/statistics/chart_widget.h +++ b/Telegram/SourceFiles/statistics/chart_widget.h @@ -20,6 +20,7 @@ namespace Statistic { class RpMouseWidget; class PointDetailsWidget; class ChartLinesFilterWidget; +class LinearChartPainter; class ChartWidget : public Ui::RpWidget { public: @@ -132,6 +133,10 @@ private: Data::StatisticalChart _chartData; ChartLineViewContext _animatedChartLines; + struct { + std::unique_ptr main; + std::unique_ptr footer; + } _linearChartPainter; struct { base::unique_qptr widget; diff --git a/Telegram/SourceFiles/statistics/linear_chart_view.cpp b/Telegram/SourceFiles/statistics/linear_chart_view.cpp index 704842af0..7bfe9b90d 100644 --- a/Telegram/SourceFiles/statistics/linear_chart_view.cpp +++ b/Telegram/SourceFiles/statistics/linear_chart_view.cpp @@ -16,6 +16,43 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_statistics.h" namespace Statistic { +namespace { + +void PaintChartLine( + QPainter &p, + int lineIndex, + const Data::StatisticalChart &chartData, + const Limits &xIndices, + const Limits &xPercentageLimits, + const Limits &heightLimits, + const QSize &size) { + const auto &line = chartData.lines[lineIndex]; + + auto chartPoints = QPolygonF(); + + const auto localStart = std::max(0, int(xIndices.min)); + const auto localEnd = std::min( + int(chartData.xPercentage.size() - 1), + int(xIndices.max)); + + for (auto i = localStart; i <= localEnd; i++) { + if (line.y[i] < 0) { + continue; + } + const auto xPoint = size.width() + * ((chartData.xPercentage[i] - xPercentageLimits.min) + / (xPercentageLimits.max - xPercentageLimits.min)); + const auto yPercentage = (line.y[i] - heightLimits.min) + / float64(heightLimits.max - heightLimits.min); + const auto yPoint = (1. - yPercentage) * size.height(); + chartPoints << QPointF(xPoint, yPoint); + } + p.setPen(QPen(line.color, st::statisticsChartLineWidth)); + p.setBrush(Qt::NoBrush); + p.drawPolyline(chartPoints); +} + +} // namespace void PaintLinearChartView( QPainter &p, @@ -117,4 +154,78 @@ void PaintLinearChartView( p.setOpacity(1.); } +LinearChartPainter::LinearChartPainter() = default; + +void LinearChartPainter::paint( + QPainter &p, + const Data::StatisticalChart &chartData, + const Limits &xIndices, + const Limits &xPercentageLimits, + const Limits &heightLimits, + const QRect &rect, + ChartLineViewContext &lineViewContext, + DetailsPaintContext &detailsPaintContext) { + + const auto cacheToken = LinearChartPainter::CacheToken( + xIndices, + xPercentageLimits, + heightLimits, + rect.size()); + + for (auto i = 0; i < chartData.lines.size(); i++) { + const auto &line = chartData.lines[i]; + p.setOpacity(lineViewContext.alpha(line.id)); + if (!p.opacity()) { + continue; + } + if (p.opacity() < 1.) { + // p.setRenderHint(QPainter::Antialiasing, false); + } + + //// + auto &cache = _caches[line.id]; + + const auto isSameToken = (cache.lastToken == cacheToken); + if (isSameToken && cache.hq) { + p.drawImage(rect.topLeft(), cache.image); + continue; + } + const auto ratio = style::DevicePixelRatio(); + cache.hq = isSameToken; + auto image = QImage(); + image = QImage( + rect.size() * style::DevicePixelRatio() * (isSameToken ? 1. : ratio), + QImage::Format_ARGB32_Premultiplied); + image.setDevicePixelRatio(style::DevicePixelRatio()); + image.fill(Qt::transparent); + // image.fill(Qt::darkRed); + { + auto imagePainter = QPainter(&image); + imagePainter.setRenderHint(QPainter::Antialiasing, true); + if (isSameToken) { + // PainterHighQualityEnabler hp(imagePainter); + } else { + imagePainter.scale(ratio, ratio); + } + //// + + PaintChartLine( + imagePainter, + i, + chartData, + xIndices, + xPercentageLimits, + heightLimits, + rect.size()); + } + + if (!isSameToken) { + image = image.scaled(rect.size() * style::DevicePixelRatio(), Qt::IgnoreAspectRatio, Qt::FastTransformation); + } + p.drawImage(rect.topLeft(), image); + cache.lastToken = cacheToken; + cache.image = std::move(image); + } +} + } // namespace Statistic diff --git a/Telegram/SourceFiles/statistics/linear_chart_view.h b/Telegram/SourceFiles/statistics/linear_chart_view.h index c9e11f28f..3d10b655a 100644 --- a/Telegram/SourceFiles/statistics/linear_chart_view.h +++ b/Telegram/SourceFiles/statistics/linear_chart_view.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include + namespace Data { struct StatisticalChart; } // namespace Data @@ -17,6 +19,64 @@ struct Limits; struct DetailsPaintContext; struct ChartLineViewContext; +class LinearChartPainter { +public: + LinearChartPainter(); + + void paint( + QPainter &p, + const Data::StatisticalChart &chartData, + const Limits &xIndices, + const Limits &xPercentageLimits, + const Limits &heightLimits, + const QRect &rect, + ChartLineViewContext &lineViewContext, + DetailsPaintContext &detailsPaintContext); + +private: + struct CacheToken final { + explicit CacheToken() = default; + explicit CacheToken( + Limits xIndices, + Limits xPercentageLimits, + Limits heightLimits, + QSize rectSize) + : xIndices(std::move(xIndices)) + , xPercentageLimits(std::move(xPercentageLimits)) + , heightLimits(std::move(heightLimits)) + , rectSize(std::move(rectSize)) { + } + + bool operator==(const CacheToken &other) const { + return (rectSize == other.rectSize) + && (xIndices.min == other.xIndices.min) + && (xIndices.max == other.xIndices.max) + && (xPercentageLimits.min == other.xPercentageLimits.min) + && (xPercentageLimits.max == other.xPercentageLimits.max) + && (heightLimits.min == other.heightLimits.min) + && (heightLimits.max == other.heightLimits.max); + } + + bool operator!=(const CacheToken &other) const { + return !(*this == other); + } + + Limits xIndices; + Limits xPercentageLimits; + Limits heightLimits; + QSize rectSize; + }; + + struct Cache final { + QImage image; + CacheToken lastToken; + bool hq = false; + }; + + base::flat_map _caches; + +}; + void PaintLinearChartView( QPainter &p, const Data::StatisticalChart &chartData,