改瀑布图合成bug
This commit is contained in:
@@ -268,19 +268,24 @@ static QWidget* create_image_interpolation_combo(renderive::Waterfall* waterfall
|
||||
layout->addWidget(combo);
|
||||
return container;
|
||||
}
|
||||
static QWidget* create_control(renderive::Abs_Plot* plot, const std::vector<QWidget*>& widgets = {}) {
|
||||
static QWidget* create_control(renderive::Abs_Plot* plot, const std::vector<QWidget*>& widgets = {}, std::shared_ptr<renderive::Performance_Shower>* performance_shower_out = nullptr) {
|
||||
auto control = new QWidget;
|
||||
auto l = new QVBoxLayout(control);
|
||||
l->setAlignment(Qt::AlignTop);
|
||||
l->setContentsMargins({0, 0, 0, 0});
|
||||
l->setSpacing(0);
|
||||
auto performance_shower = renderive::Performance_Shower::Builder(*plot).build();
|
||||
if (performance_shower_out)
|
||||
*performance_shower_out = performance_shower;
|
||||
std::vector<QWidget*> ws = {
|
||||
renderive::create_toggle_button("暂停渲染", "开始渲染", [plot]() {
|
||||
plot->is_rendering() ? plot->pause_render() : plot->start_render();
|
||||
}, plot->is_rendering()),
|
||||
renderive::create_toggle_button("停止性能测试", "启动性能测试", [plot]() {
|
||||
renderive::set_performance_shower_enabled(*plot, !renderive::performance_shower_enabled(*plot));
|
||||
}, renderive::performance_shower_enabled(*plot)),
|
||||
auto performance_shower = std::dynamic_pointer_cast<renderive::Performance_Shower>(plot->frame_lifecycle_observer());
|
||||
if (performance_shower)
|
||||
performance_shower->setEnabled(!performance_shower->enabled());
|
||||
}, performance_shower && performance_shower->enabled()),
|
||||
renderive::create_select_color("更改背景色", [plot](const QColor& color) {
|
||||
plot->set_background_color(color);
|
||||
}, plot->background_color()),
|
||||
|
||||
@@ -46,7 +46,7 @@ struct Abs_Axis_Private : Typed_Render_Data<Abs_Axis, Abs_Axis_Render_State, Abs
|
||||
sync_state_pipeline();
|
||||
}
|
||||
void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot&) override {
|
||||
QPainter* painter = &context.qt();
|
||||
Raster_Canvas* painter = &context.canvas();
|
||||
Abs_Axis_Render_State* s = render_state();
|
||||
const int pixel_start = pixel_start_of(s);
|
||||
Range range = {s->coord_start, s->coord_start + s->coord_length};
|
||||
|
||||
@@ -1,76 +1,49 @@
|
||||
#include "Frame_Raster_Context.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace renderive {
|
||||
Frame_Raster_Context::Frame_Raster_Context(QImage& target, QPointF base_translation)
|
||||
: target_(target), base_translation_(base_translation) {
|
||||
if (target_.format() == QImage::Format_ARGB32_Premultiplied && !target_.isNull()) {
|
||||
blend_image_ready_ = bl_image_.create_from_data(
|
||||
target_.width(), target_.height(), BL_FORMAT_PRGB32, target_.bits(),
|
||||
static_cast<intptr_t>(target_.bytesPerLine()), BL_DATA_ACCESS_RW) == BL_SUCCESS;
|
||||
|
||||
namespace {
|
||||
template <typename Path>
|
||||
void fill_glyph_path(BLContext& context, const Path& path, double dx, double dy, const QColor& color) {
|
||||
if (path.isEmpty())
|
||||
return;
|
||||
BLPath converted;
|
||||
bool open = false;
|
||||
for (int i = 0; i < path.elementCount(); ++i) {
|
||||
const auto element = path.elementAt(i);
|
||||
const int type = static_cast<int>(element.type);
|
||||
if (type == 0) {
|
||||
if (open)
|
||||
converted.close();
|
||||
converted.move_to(element.x + dx, element.y + dy);
|
||||
open = true;
|
||||
}
|
||||
else if (type == 1) {
|
||||
converted.line_to(element.x + dx, element.y + dy);
|
||||
}
|
||||
else if (type == 2 && i + 2 < path.elementCount()) {
|
||||
const auto c1 = path.elementAt(i);
|
||||
const auto c2 = path.elementAt(i + 1);
|
||||
const auto end = path.elementAt(i + 2);
|
||||
converted.cubic_to(c1.x + dx, c1.y + dy, c2.x + dx, c2.y + dy, end.x + dx, end.y + dy);
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
if (open)
|
||||
converted.close();
|
||||
context.set_fill_style(Raster_Canvas::color_of(color));
|
||||
context.fill_path(converted);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
BLRgba32 Raster_Canvas::color_of(const QColor& color) {
|
||||
return BLRgba32(color.red(), color.green(), color.blue(), color.alpha());
|
||||
}
|
||||
|
||||
Frame_Raster_Context::~Frame_Raster_Context() {
|
||||
finish();
|
||||
}
|
||||
|
||||
QPainter& Frame_Raster_Context::qt() {
|
||||
if (backend_ == Backend::Blend2D)
|
||||
end_blend();
|
||||
if (backend_ != Backend::Qt) {
|
||||
qt_painter_.begin(&target_);
|
||||
qt_painter_.translate(base_translation_);
|
||||
backend_ = Backend::Qt;
|
||||
}
|
||||
return qt_painter_;
|
||||
}
|
||||
|
||||
BLContext* Frame_Raster_Context::blend() {
|
||||
if (!blend_image_ready_)
|
||||
return nullptr;
|
||||
if (backend_ == Backend::Qt)
|
||||
end_qt();
|
||||
if (backend_ != Backend::Blend2D) {
|
||||
BLContextCreateInfo create_info{};
|
||||
create_info.thread_count = 0;
|
||||
if (bl_context_.begin(bl_image_, create_info) != BL_SUCCESS)
|
||||
return nullptr;
|
||||
bl_context_.translate(base_translation_.x(), base_translation_.y());
|
||||
backend_ = Backend::Blend2D;
|
||||
}
|
||||
return &bl_context_;
|
||||
}
|
||||
|
||||
void Frame_Raster_Context::end_qt() {
|
||||
if (backend_ == Backend::Qt)
|
||||
qt_painter_.end();
|
||||
backend_ = Backend::None;
|
||||
}
|
||||
|
||||
void Frame_Raster_Context::end_blend() {
|
||||
if (backend_ == Backend::Blend2D)
|
||||
bl_context_.end();
|
||||
backend_ = Backend::None;
|
||||
}
|
||||
|
||||
void Frame_Raster_Context::finish() {
|
||||
if (backend_ == Backend::Qt)
|
||||
end_qt();
|
||||
else if (backend_ == Backend::Blend2D)
|
||||
end_blend();
|
||||
}
|
||||
|
||||
bool Frame_Raster_Context::solid_color(const QBrush& brush, BLRgba32& color) {
|
||||
if (brush.style() != Qt::SolidPattern)
|
||||
return false;
|
||||
const QColor value = brush.color();
|
||||
color = BLRgba32(value.red(), value.green(), value.blue(), value.alpha());
|
||||
return true;
|
||||
}
|
||||
|
||||
BLStrokeCap Frame_Raster_Context::stroke_cap(Qt::PenCapStyle style) {
|
||||
BLStrokeCap Raster_Canvas::stroke_cap(Qt::PenCapStyle style) {
|
||||
switch (style) {
|
||||
case Qt::FlatCap: return BL_STROKE_CAP_BUTT;
|
||||
case Qt::SquareCap: return BL_STROKE_CAP_SQUARE;
|
||||
@@ -79,7 +52,7 @@ BLStrokeCap Frame_Raster_Context::stroke_cap(Qt::PenCapStyle style) {
|
||||
}
|
||||
}
|
||||
|
||||
BLStrokeJoin Frame_Raster_Context::stroke_join(Qt::PenJoinStyle style) {
|
||||
BLStrokeJoin Raster_Canvas::stroke_join(Qt::PenJoinStyle style) {
|
||||
switch (style) {
|
||||
case Qt::MiterJoin: return BL_STROKE_JOIN_MITER_CLIP;
|
||||
case Qt::BevelJoin: return BL_STROKE_JOIN_BEVEL;
|
||||
@@ -89,15 +62,286 @@ BLStrokeJoin Frame_Raster_Context::stroke_join(Qt::PenJoinStyle style) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Raster_Canvas::configure_stroke() {
|
||||
if (!active() || pen_.style() == Qt::NoPen)
|
||||
return false;
|
||||
const double width = pen_.widthF() == 0.0 ? 1.0 : pen_.widthF();
|
||||
context_->set_stroke_style(color_of(pen_.color()));
|
||||
context_->set_stroke_width(width);
|
||||
context_->set_stroke_caps(stroke_cap(pen_.capStyle()));
|
||||
context_->set_stroke_join(stroke_join(pen_.joinStyle()));
|
||||
context_->set_stroke_miter_limit(pen_.miterLimit());
|
||||
context_->set_stroke_dash_offset(pen_.dashOffset() * width);
|
||||
BLArray<double> dashes;
|
||||
const QVector<qreal> pattern = pen_.dashPattern();
|
||||
for (qreal dash : pattern)
|
||||
dashes.append(static_cast<double>(dash) * width);
|
||||
context_->set_stroke_dash_array(dashes);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Raster_Canvas::configure_fill() {
|
||||
if (!active() || brush_.style() == Qt::NoBrush)
|
||||
return false;
|
||||
context_->set_fill_style(color_of(brush_.color()));
|
||||
return true;
|
||||
}
|
||||
|
||||
void Raster_Canvas::save() {
|
||||
if (!active())
|
||||
return;
|
||||
context_->save();
|
||||
states_.push_back({pen_, brush_, font_});
|
||||
}
|
||||
|
||||
void Raster_Canvas::restore() {
|
||||
if (!active())
|
||||
return;
|
||||
context_->restore();
|
||||
if (states_.empty())
|
||||
return;
|
||||
pen_ = states_.back().pen;
|
||||
brush_ = states_.back().brush;
|
||||
font_ = states_.back().font;
|
||||
states_.pop_back();
|
||||
}
|
||||
|
||||
void Raster_Canvas::setPen(const QPen& pen) {
|
||||
pen_ = pen;
|
||||
}
|
||||
|
||||
void Raster_Canvas::setBrush(const QBrush& brush) {
|
||||
brush_ = brush;
|
||||
}
|
||||
|
||||
void Raster_Canvas::drawLine(const QPointF& p0, const QPointF& p1) {
|
||||
if (configure_stroke())
|
||||
context_->stroke_line(p0.x(), p0.y(), p1.x(), p1.y());
|
||||
}
|
||||
|
||||
void Raster_Canvas::drawRect(const QRectF& rect) {
|
||||
if (!active())
|
||||
return;
|
||||
if (configure_fill())
|
||||
context_->fill_rect(rect.left(), rect.top(), rect.width(), rect.height());
|
||||
if (configure_stroke())
|
||||
context_->stroke_rect(rect.left(), rect.top(), rect.width(), rect.height());
|
||||
}
|
||||
|
||||
void Raster_Canvas::fillRect(const QRectF& rect, const QBrush& brush) {
|
||||
if (!active() || brush.style() == Qt::NoBrush)
|
||||
return;
|
||||
context_->set_fill_style(color_of(brush.color()));
|
||||
context_->fill_rect(rect.left(), rect.top(), rect.width(), rect.height());
|
||||
}
|
||||
|
||||
void Raster_Canvas::drawEllipse(const QPointF& center, double rx, double ry) {
|
||||
if (!active())
|
||||
return;
|
||||
if (configure_fill())
|
||||
context_->fill_ellipse(center.x(), center.y(), rx, ry);
|
||||
if (configure_stroke())
|
||||
context_->stroke_ellipse(center.x(), center.y(), rx, ry);
|
||||
}
|
||||
|
||||
void Raster_Canvas::drawPolygon(const QPointF* points, int count) {
|
||||
if (!active() || !points || count < 3)
|
||||
return;
|
||||
std::vector<BLPoint> polygon;
|
||||
polygon.reserve(static_cast<std::size_t>(count));
|
||||
for (int i = 0; i < count; ++i)
|
||||
polygon.emplace_back(points[i].x(), points[i].y());
|
||||
if (configure_fill())
|
||||
context_->fill_polygon(polygon.data(), polygon.size());
|
||||
if (configure_stroke())
|
||||
context_->stroke_polygon(polygon.data(), polygon.size());
|
||||
}
|
||||
|
||||
void Raster_Canvas::drawPoint(const QPointF& point) {
|
||||
if (!active() || pen_.style() == Qt::NoPen)
|
||||
return;
|
||||
const double radius = std::max(0.5, (pen_.widthF() == 0.0 ? 1.0 : pen_.widthF()) * 0.5);
|
||||
context_->set_fill_style(color_of(pen_.color()));
|
||||
context_->fill_ellipse(point.x(), point.y(), radius, radius);
|
||||
}
|
||||
|
||||
void Raster_Canvas::drawPoints(const QPointF* points, int count) {
|
||||
if (!points)
|
||||
return;
|
||||
for (int i = 0; i < count; ++i)
|
||||
drawPoint(points[i]);
|
||||
}
|
||||
|
||||
bool Raster_Canvas::bind_image(const QImage& source, BLImage& image, QImage& converted) {
|
||||
if (source.isNull())
|
||||
return false;
|
||||
converted = source.format() == QImage::Format_ARGB32_Premultiplied
|
||||
? source
|
||||
: source.convertToFormat(QImage::Format_ARGB32_Premultiplied);
|
||||
return image.create_from_data(converted.width(), converted.height(), BL_FORMAT_PRGB32, converted.bits(),
|
||||
static_cast<intptr_t>(converted.bytesPerLine()), BL_DATA_ACCESS_RW) == BL_SUCCESS;
|
||||
}
|
||||
|
||||
void Raster_Canvas::drawImage(const QPoint& top_left, const QImage& image) {
|
||||
drawImage(QPointF(top_left), image);
|
||||
}
|
||||
|
||||
void Raster_Canvas::drawImage(const QPointF& top_left, const QImage& image) {
|
||||
if (!active() || image.isNull())
|
||||
return;
|
||||
BLImage source_image;
|
||||
QImage converted;
|
||||
if (!bind_image(image, source_image, converted))
|
||||
return;
|
||||
context_->blit_image(BLPoint(top_left.x(), top_left.y()), source_image);
|
||||
}
|
||||
|
||||
void Raster_Canvas::drawImage(const QRectF& target, const QImage& image) {
|
||||
drawImage(target, image, QRectF(0.0, 0.0, image.width(), image.height()));
|
||||
}
|
||||
|
||||
void Raster_Canvas::drawImage(const QRectF& target, const QImage& image, const QRectF& source) {
|
||||
if (!active() || image.isNull() || target.isEmpty() || source.isEmpty())
|
||||
return;
|
||||
BLImage source_image;
|
||||
QImage converted;
|
||||
if (!bind_image(image, source_image, converted))
|
||||
return;
|
||||
const BLRect source_rect(source.left(), source.top(), source.width(), source.height());
|
||||
const BLRectI source_area(static_cast<int>(std::floor(source_rect.x)), static_cast<int>(std::floor(source_rect.y)),
|
||||
static_cast<int>(std::ceil(source_rect.w)), static_cast<int>(std::ceil(source_rect.h)));
|
||||
context_->blit_image(BLRect(target.left(), target.top(), target.width(), target.height()), source_image, source_area);
|
||||
}
|
||||
|
||||
void Raster_Canvas::drawText(const QPointF& baseline, const QString& text) {
|
||||
if (!active() || text.isEmpty() || pen_.style() == Qt::NoPen)
|
||||
return;
|
||||
const QRawFont raw_font = QRawFont::fromFont(font_);
|
||||
if (!raw_font.isValid())
|
||||
return;
|
||||
const QVector<quint32> glyphs = raw_font.glyphIndexesForString(text);
|
||||
const QVector<QPointF> advances = raw_font.advancesForGlyphIndexes(glyphs);
|
||||
double x = baseline.x();
|
||||
for (int i = 0; i < glyphs.size(); ++i) {
|
||||
fill_glyph_path(*context_, raw_font.pathForGlyph(glyphs.at(i)), x, baseline.y(), pen_.color());
|
||||
if (i < advances.size())
|
||||
x += advances.at(i).x();
|
||||
}
|
||||
}
|
||||
|
||||
void Raster_Canvas::drawText(const QRectF& rect, Qt::Alignment alignment, const QString& text) {
|
||||
if (text.isEmpty())
|
||||
return;
|
||||
const QFontMetricsF metrics(font_);
|
||||
const double width = metrics.horizontalAdvance(text);
|
||||
const double height = metrics.height();
|
||||
double x = rect.left();
|
||||
if (alignment & Qt::AlignHCenter)
|
||||
x = rect.left() + (rect.width() - width) * 0.5;
|
||||
else if (alignment & Qt::AlignRight)
|
||||
x = rect.right() - width;
|
||||
double baseline = rect.top() + metrics.ascent();
|
||||
if (alignment & Qt::AlignVCenter)
|
||||
baseline = rect.top() + (rect.height() - height) * 0.5 + metrics.ascent();
|
||||
else if (alignment & Qt::AlignBottom)
|
||||
baseline = rect.bottom() - metrics.descent();
|
||||
drawText(QPointF(x, baseline), text);
|
||||
}
|
||||
|
||||
void Raster_Canvas::translate(const QPoint& point) {
|
||||
if (active())
|
||||
context_->translate(point.x(), point.y());
|
||||
}
|
||||
|
||||
void Raster_Canvas::translate(const QPointF& point) {
|
||||
if (active())
|
||||
context_->translate(point.x(), point.y());
|
||||
}
|
||||
|
||||
void Raster_Canvas::rotate(double degrees) {
|
||||
if (active())
|
||||
context_->rotate(degrees * 3.14159265358979323846 / 180.0);
|
||||
}
|
||||
|
||||
void Raster_Canvas::scale(double x, double y) {
|
||||
if (active())
|
||||
context_->scale(x, y);
|
||||
}
|
||||
|
||||
void Raster_Canvas::resetTransform() {
|
||||
if (active())
|
||||
context_->reset_transform();
|
||||
}
|
||||
|
||||
void Raster_Canvas::setClipRect(const QRectF& rect) {
|
||||
if (active())
|
||||
context_->clip_to_rect(rect.left(), rect.top(), rect.width(), rect.height());
|
||||
}
|
||||
|
||||
void Raster_Canvas::setClipping(bool enabled) {
|
||||
if (active() && !enabled)
|
||||
context_->restore_clipping();
|
||||
}
|
||||
|
||||
Frame_Raster_Context::Frame_Raster_Context(QImage& target, QPointF base_translation)
|
||||
: target_(target), base_translation_(base_translation), canvas_(&bl_context_) {
|
||||
if (target_.format() != QImage::Format_ARGB32_Premultiplied || target_.isNull())
|
||||
return;
|
||||
blend_image_ready_ = bl_image_.create_from_data(
|
||||
target_.width(), target_.height(), BL_FORMAT_PRGB32, target_.bits(),
|
||||
static_cast<intptr_t>(target_.bytesPerLine()), BL_DATA_ACCESS_RW) == BL_SUCCESS;
|
||||
}
|
||||
|
||||
Frame_Raster_Context::~Frame_Raster_Context() {
|
||||
finish();
|
||||
}
|
||||
|
||||
BLContext* Frame_Raster_Context::blend() {
|
||||
if (!blend_image_ready_)
|
||||
return nullptr;
|
||||
if (!active_) {
|
||||
BLContextCreateInfo create_info{};
|
||||
create_info.thread_count = 0;
|
||||
if (bl_context_.begin(bl_image_, create_info) != BL_SUCCESS)
|
||||
return nullptr;
|
||||
bl_context_.translate(base_translation_.x(), base_translation_.y());
|
||||
active_ = true;
|
||||
}
|
||||
return &bl_context_;
|
||||
}
|
||||
|
||||
Raster_Canvas& Frame_Raster_Context::canvas() {
|
||||
canvas_.bind(blend());
|
||||
return canvas_;
|
||||
}
|
||||
|
||||
void Frame_Raster_Context::end_blend() {
|
||||
if (!active_)
|
||||
return;
|
||||
bl_context_.end();
|
||||
active_ = false;
|
||||
}
|
||||
|
||||
void Frame_Raster_Context::finish() {
|
||||
end_blend();
|
||||
}
|
||||
|
||||
BLStrokeCap Frame_Raster_Context::stroke_cap(Qt::PenCapStyle style) {
|
||||
return Raster_Canvas::stroke_cap(style);
|
||||
}
|
||||
|
||||
BLStrokeJoin Frame_Raster_Context::stroke_join(Qt::PenJoinStyle style) {
|
||||
return Raster_Canvas::stroke_join(style);
|
||||
}
|
||||
|
||||
bool Frame_Raster_Context::configure_stroke(const QPen& pen) {
|
||||
BLRgba32 color;
|
||||
if (pen.style() == Qt::NoPen || !solid_color(pen.brush(), color))
|
||||
if (pen.style() == Qt::NoPen)
|
||||
return false;
|
||||
BLContext* context = blend();
|
||||
if (!context)
|
||||
return false;
|
||||
const double width = pen.widthF() == 0.0 ? 1.0 : pen.widthF();
|
||||
context->set_stroke_style(color);
|
||||
context->set_stroke_style(Raster_Canvas::color_of(pen.color()));
|
||||
context->set_stroke_width(width);
|
||||
context->set_stroke_caps(stroke_cap(pen.capStyle()));
|
||||
context->set_stroke_join(stroke_join(pen.joinStyle()));
|
||||
@@ -114,12 +358,8 @@ bool Frame_Raster_Context::configure_stroke(const QPen& pen) {
|
||||
bool Frame_Raster_Context::stroke_polyline(std::span<const QPointF> points, const QPen& pen) {
|
||||
if (points.size() < 2 || pen.style() == Qt::NoPen)
|
||||
return true;
|
||||
if (!configure_stroke(pen)) {
|
||||
QPainter& painter = qt();
|
||||
painter.setPen(pen);
|
||||
painter.drawPolyline(points.data(), static_cast<int>(points.size()));
|
||||
if (!configure_stroke(pen))
|
||||
return false;
|
||||
}
|
||||
BLPath path;
|
||||
bool active = false;
|
||||
for (const QPointF& point : points) {
|
||||
@@ -142,21 +382,15 @@ bool Frame_Raster_Context::stroke_polyline(std::span<const QPointF> points, cons
|
||||
bool Frame_Raster_Context::fill_polygon(std::span<const QPointF> points, const QBrush& brush) {
|
||||
if (points.size() < 3 || brush.style() == Qt::NoBrush)
|
||||
return true;
|
||||
BLRgba32 color;
|
||||
BLContext* context = solid_color(brush, color) ? blend() : nullptr;
|
||||
if (!context) {
|
||||
QPainter& painter = qt();
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(brush);
|
||||
painter.drawPolygon(points.data(), static_cast<int>(points.size()));
|
||||
BLContext* context = blend();
|
||||
if (!context)
|
||||
return false;
|
||||
}
|
||||
BLPath path;
|
||||
path.move_to(points.front().x(), points.front().y());
|
||||
for (std::size_t i = 1; i < points.size(); ++i)
|
||||
path.line_to(points[i].x(), points[i].y());
|
||||
path.close();
|
||||
context->fill_path(path, color);
|
||||
std::vector<BLPoint> polygon;
|
||||
polygon.reserve(points.size());
|
||||
for (const QPointF& point : points)
|
||||
polygon.emplace_back(point.x(), point.y());
|
||||
context->set_fill_style(Raster_Canvas::color_of(brush.color()));
|
||||
context->fill_polygon(polygon.data(), polygon.size());
|
||||
return true;
|
||||
}
|
||||
} // namespace renderive
|
||||
|
||||
@@ -1,14 +1,98 @@
|
||||
#pragma once
|
||||
|
||||
#include <QBrush>
|
||||
#include <QFont>
|
||||
#include <QFontMetrics>
|
||||
#include <QImage>
|
||||
#include <QPainter>
|
||||
#include <QPen>
|
||||
#include <QPoint>
|
||||
#include <QPointF>
|
||||
#include <QPolygonF>
|
||||
#include <QRawFont>
|
||||
#include <QRect>
|
||||
#include <QRectF>
|
||||
#include <blend2d/blend2d.h>
|
||||
#include <span>
|
||||
#include <vector>
|
||||
|
||||
namespace renderive {
|
||||
/// @brief Owns mutually exclusive Qt and Blend2D sessions for one pixel target.
|
||||
|
||||
/// @brief Blend2D drawing facade used by the Qt-facing render data.
|
||||
///
|
||||
/// Qt value types remain the public style/data representation, but all pixels
|
||||
/// are produced by Blend2D. In particular, this class deliberately has no
|
||||
/// Qt painter backend.
|
||||
class Raster_Canvas {
|
||||
public:
|
||||
explicit Raster_Canvas(BLContext* context = nullptr) : context_(context) {}
|
||||
|
||||
void bind(BLContext* context) noexcept { context_ = context; }
|
||||
void save();
|
||||
void restore();
|
||||
void setPen(const QPen& pen);
|
||||
void setBrush(const QBrush& brush);
|
||||
void setFont(const QFont& font) { font_ = font; }
|
||||
[[nodiscard]] const QFont& font() const noexcept { return font_; }
|
||||
|
||||
void drawLine(const QPointF& p0, const QPointF& p1);
|
||||
void drawLine(int x0, int y0, int x1, int y1) { drawLine(QPointF(x0, y0), QPointF(x1, y1)); }
|
||||
void drawRect(const QRectF& rect);
|
||||
void drawRect(const QRect& rect) { drawRect(QRectF(rect)); }
|
||||
void fillRect(const QRectF& rect, const QBrush& brush);
|
||||
void fillRect(const QRect& rect, const QBrush& brush) { fillRect(QRectF(rect), brush); }
|
||||
void fillRect(const QRectF& rect, const QColor& color) { fillRect(rect, QBrush(color)); }
|
||||
void fillRect(const QRect& rect, const QColor& color) { fillRect(QRectF(rect), QBrush(color)); }
|
||||
void fillRect(const QRectF& rect, Qt::GlobalColor color) { fillRect(rect, QColor(color)); }
|
||||
void fillRect(const QRect& rect, Qt::GlobalColor color) { fillRect(rect, QColor(color)); }
|
||||
void drawEllipse(const QPointF& center, double rx, double ry);
|
||||
void drawPolygon(const QPointF* points, int count);
|
||||
void drawPolygon(const QPolygonF& polygon) { drawPolygon(polygon.constData(), polygon.size()); }
|
||||
void drawPoint(const QPointF& point);
|
||||
void drawPoints(const QPointF* points, int count);
|
||||
|
||||
void drawImage(const QPoint& top_left, const QImage& image);
|
||||
void drawImage(const QPointF& top_left, const QImage& image);
|
||||
void drawImage(const QRectF& target, const QImage& image);
|
||||
void drawImage(const QRectF& target, const QImage& image, const QRectF& source);
|
||||
|
||||
void drawText(const QPointF& baseline, const QString& text);
|
||||
void drawText(int x, int y, const QString& text) { drawText(QPointF(x, y), text); }
|
||||
void drawText(const QRectF& rect, Qt::Alignment alignment, const QString& text);
|
||||
void drawText(const QRect& rect, Qt::Alignment alignment, const QString& text) { drawText(QRectF(rect), alignment, text); }
|
||||
|
||||
void translate(const QPoint& point);
|
||||
void translate(const QPointF& point);
|
||||
void rotate(double degrees);
|
||||
void scale(double x, double y);
|
||||
void resetTransform();
|
||||
void setClipRect(const QRectF& rect);
|
||||
void setClipRect(const QRect& rect) { setClipRect(QRectF(rect)); }
|
||||
void setClipping(bool enabled);
|
||||
|
||||
static BLRgba32 color_of(const QColor& color);
|
||||
static BLStrokeCap stroke_cap(Qt::PenCapStyle style);
|
||||
static BLStrokeJoin stroke_join(Qt::PenJoinStyle style);
|
||||
|
||||
private:
|
||||
struct State {
|
||||
QPen pen;
|
||||
QBrush brush;
|
||||
QFont font;
|
||||
};
|
||||
|
||||
[[nodiscard]] bool active() const noexcept { return context_ != nullptr; }
|
||||
bool configure_stroke();
|
||||
bool configure_fill();
|
||||
static bool bind_image(const QImage& source, BLImage& image, QImage& converted);
|
||||
|
||||
BLContext* context_{};
|
||||
QPen pen_;
|
||||
QBrush brush_;
|
||||
QFont font_;
|
||||
std::vector<State> states_;
|
||||
};
|
||||
|
||||
/// @brief Owns one Blend2D session for one pixel target.
|
||||
class Frame_Raster_Context {
|
||||
public:
|
||||
explicit Frame_Raster_Context(QImage& target, QPointF base_translation = {});
|
||||
@@ -17,28 +101,24 @@ public:
|
||||
Frame_Raster_Context(const Frame_Raster_Context&) = delete;
|
||||
Frame_Raster_Context& operator=(const Frame_Raster_Context&) = delete;
|
||||
|
||||
QPainter& qt();
|
||||
Raster_Canvas& canvas();
|
||||
bool stroke_polyline(std::span<const QPointF> points, const QPen& pen);
|
||||
bool fill_polygon(std::span<const QPointF> points, const QBrush& brush);
|
||||
void finish();
|
||||
|
||||
private:
|
||||
enum class Backend : std::uint8_t { None, Qt, Blend2D };
|
||||
|
||||
BLContext* blend();
|
||||
void end_qt();
|
||||
void end_blend();
|
||||
static bool solid_color(const QBrush& brush, BLRgba32& color);
|
||||
static BLStrokeCap stroke_cap(Qt::PenCapStyle style);
|
||||
static BLStrokeJoin stroke_join(Qt::PenJoinStyle style);
|
||||
bool configure_stroke(const QPen& pen);
|
||||
|
||||
QImage& target_;
|
||||
QPointF base_translation_;
|
||||
QPainter qt_painter_;
|
||||
BLImage bl_image_;
|
||||
BLContext bl_context_;
|
||||
Backend backend_ = Backend::None;
|
||||
Raster_Canvas canvas_;
|
||||
bool blend_image_ready_{};
|
||||
bool active_{};
|
||||
};
|
||||
} // namespace renderive
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "../base/Memory.h"
|
||||
#include "../base/Object_Semantics.h"
|
||||
#include "Render_Config.h"
|
||||
#include "Frame_Raster_Context.h"
|
||||
#include "Plot_Render_Context.h"
|
||||
#include "Triple_Buffer.h"
|
||||
#include "Update_Completion.h"
|
||||
@@ -19,7 +20,6 @@
|
||||
class QEvent;
|
||||
class QKeyEvent;
|
||||
class QMouseEvent;
|
||||
class QPainter;
|
||||
class QPointF;
|
||||
class QResizeEvent;
|
||||
class QWheelEvent;
|
||||
|
||||
@@ -202,7 +202,7 @@ void Renderable::render_cached(Frame_Raster_Context& context, const Plot_Render_
|
||||
if (snapshot.frame_metadata)
|
||||
snapshot.frame_metadata->record_renderable_cache_hit(object_name, parent_object_name, tree_depth);
|
||||
std::uint64_t begin_time = steady_now_ns();
|
||||
context.qt().drawImage(bounds.topLeft(), cache_image.image());
|
||||
context.canvas().drawImage(bounds.topLeft(), cache_image.image());
|
||||
if (snapshot.frame_metadata)
|
||||
snapshot.frame_metadata->record_renderable_cache_compose(object_name, parent_object_name, tree_depth, steady_now_ns() - begin_time, static_cast<std::uint64_t>(cache_image.sizeInBytes()), static_cast<std::uint64_t>(bounds.width()) * static_cast<std::uint64_t>(bounds.height()), edit_version, cache_ready_version.load(std::memory_order_acquire));
|
||||
return;
|
||||
@@ -215,7 +215,7 @@ void Renderable::render_cached(Frame_Raster_Context& context, const Plot_Render_
|
||||
snapshot.frame_metadata->record_renderable_cache_rebuild(object_name, parent_object_name, tree_depth, steady_now_ns() - rebuild_begin_time, cache_image.isNull() ? 0 : static_cast<std::uint64_t>(cache_image.sizeInBytes()), static_cast<std::uint64_t>(bounds.width()) * static_cast<std::uint64_t>(bounds.height()), edit_version, cache_ready_version.load(std::memory_order_acquire));
|
||||
if (!cache_image.isNull()) {
|
||||
std::uint64_t begin_time = steady_now_ns();
|
||||
context.qt().drawImage(bounds.topLeft(), cache_image.image());
|
||||
context.canvas().drawImage(bounds.topLeft(), cache_image.image());
|
||||
if (snapshot.frame_metadata)
|
||||
snapshot.frame_metadata->record_renderable_cache_compose(object_name, parent_object_name, tree_depth, steady_now_ns() - begin_time, static_cast<std::uint64_t>(cache_image.sizeInBytes()), static_cast<std::uint64_t>(bounds.width()) * static_cast<std::uint64_t>(bounds.height()), edit_version, cache_ready_version.load(std::memory_order_acquire));
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "../base/Object_Semantics.h"
|
||||
#include "../base/Pmr_QImage_Buffer.h"
|
||||
#include "Frame_Scheduler.h"
|
||||
#include "Frame_Raster_Context.h"
|
||||
#include "Render_Data.h"
|
||||
namespace renderive {
|
||||
class Frame_Raster_Context;
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <QDebug>
|
||||
#include <map>
|
||||
#include <QPainter>
|
||||
#include <queue>
|
||||
#include <QResizeEvent>
|
||||
#include <QString>
|
||||
#include <QTime>
|
||||
@@ -10,24 +8,29 @@
|
||||
#include <QWidget>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <utility>
|
||||
// Q_DECL_IMPORT
|
||||
#include <vector>
|
||||
#include "../Core/Geometry/Types.h"
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define ASSERT(condition, message) ((void)0) // 在发布模式下什么都不做
|
||||
#define ASSERT(condition, message) ((void)0)
|
||||
#else
|
||||
#define ASSERT(condition, message) \
|
||||
do { \
|
||||
if (!(condition)) { \
|
||||
do { \
|
||||
if (!(condition)) { \
|
||||
std::cerr << "Assertion failed: " << message \
|
||||
<< "\nFile: " << __FILE__ \
|
||||
<< "\nLine: " << __LINE__ \
|
||||
<< std::endl; \
|
||||
throw std::runtime_error(message); \
|
||||
} \
|
||||
} \
|
||||
<< "\nFile: " << __FILE__ \
|
||||
<< "\nLine: " << __LINE__ \
|
||||
<< std::endl; \
|
||||
throw std::runtime_error(message); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
#endif
|
||||
|
||||
#if defined(build_lib)
|
||||
#define LIB_DECL
|
||||
#elif defined(build_dll)
|
||||
@@ -37,82 +40,64 @@
|
||||
#else
|
||||
#define LIB_DECL
|
||||
#endif
|
||||
|
||||
namespace renderive {
|
||||
std::vector<QRgb> get_turbo_color_gradient();
|
||||
class Abs_Axis;
|
||||
class Raster_Canvas;
|
||||
class Axis;
|
||||
struct Render_Data;
|
||||
struct Renderable;
|
||||
class Abs_Plot;
|
||||
class Plot;
|
||||
// 注意这仅仅只是一个范围 这是一个向量,大小相反就反向画
|
||||
struct LIB_DECL Range {
|
||||
double origin;
|
||||
double target;
|
||||
[[nodiscard]] double size() const {
|
||||
return qAbs(target - origin);
|
||||
}
|
||||
[[nodiscard]] double length() const {
|
||||
return target - origin;
|
||||
}
|
||||
[[nodiscard]] double middle() const {
|
||||
return origin + (target - origin) / 2;
|
||||
}
|
||||
bool operator==(const Range& other) const {
|
||||
return (origin == other.origin && target == other.target);
|
||||
}
|
||||
bool operator!=(const Range& other) const {
|
||||
return !(*this == other);
|
||||
}
|
||||
[[nodiscard]] bool contain(double value) const {
|
||||
if (origin < target) {
|
||||
return value > origin - 0.0001 && value < target + 0.0001;
|
||||
}
|
||||
else {
|
||||
return value > target - 0.0001 && value < origin + 0.0001;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
QDebug operator<<(QDebug debug, const Range& range);
|
||||
} // namespace renderive
|
||||
|
||||
#define PROP(Type, Name) \
|
||||
Type Name(); \
|
||||
void set_##Name(Type);
|
||||
|
||||
#define RENDERIVE_DEFINE_STATE_PROP(Owner, State, Type, Field) \
|
||||
Type Owner::Field() { \
|
||||
return d()->edit_state_value(&State::Field); \
|
||||
} \
|
||||
void Owner::set_##Field(Type value) { \
|
||||
Type Owner::Field() { \
|
||||
return d()->edit_state_value(&State::Field); \
|
||||
} \
|
||||
void Owner::set_##Field(Type value) { \
|
||||
d()->set_state_value(&State::Field, std::move(value)); \
|
||||
}
|
||||
|
||||
#define RENDERIVE_DEFINE_WEAK_STATE_PROP(Owner, State, Pointee, Field) \
|
||||
std::shared_ptr<Pointee> Owner::Field() { \
|
||||
return d()->edit_state_value(&State::Field).lock(); \
|
||||
} \
|
||||
void Owner::set_##Field(std::shared_ptr<Pointee> value) { \
|
||||
std::shared_ptr<Pointee> Owner::Field() { \
|
||||
return d()->edit_state_value(&State::Field).lock(); \
|
||||
} \
|
||||
void Owner::set_##Field(std::shared_ptr<Pointee> value) { \
|
||||
d()->set_state_value(&State::Field, std::weak_ptr<Pointee>(value)); \
|
||||
}
|
||||
|
||||
#define PROP_B(TYPE, FIELD) \
|
||||
public: \
|
||||
Builder& set_##FIELD(TYPE value) { \
|
||||
this->FIELD = std::move(value); \
|
||||
return *this; \
|
||||
}
|
||||
|
||||
#define PROP_BT(TYPE, FIELD) \
|
||||
public: \
|
||||
That& set_##FIELD(TYPE value) { \
|
||||
static_cast<That*>(this)->FIELD = std::move(value); \
|
||||
return *static_cast<That*>(this); \
|
||||
}
|
||||
|
||||
#define PROP_R(TYPE, FIELD) sc->FIELD = this->FIELD;
|
||||
#define PROP_RT(TYPE, FIELD) sc->FIELD = static_cast<That*>(this)->FIELD;
|
||||
|
||||
#define SETTER(TYPE, FIELD, DEFAULT) \
|
||||
\
|
||||
TYPE FIELD = DEFAULT; \
|
||||
public: \
|
||||
Builder& set_##FIELD(TYPE value) { \
|
||||
this->FIELD = std::move(value); \
|
||||
return *this; \
|
||||
}
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
|
||||
+12
-5
@@ -1,6 +1,5 @@
|
||||
#include <QKeyEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QPainter>
|
||||
#include <QResizeEvent>
|
||||
#include <QWheelEvent>
|
||||
#include <algorithm>
|
||||
@@ -19,6 +18,7 @@
|
||||
#include "Plot_p.h"
|
||||
#include "../architecture/Frame_Raster_Context.h"
|
||||
#include "../architecture/Update_Completion.h"
|
||||
#include "../qt/Widget_Presentation.h"
|
||||
namespace renderive {
|
||||
namespace {
|
||||
int renderable_prepare_priority(const std::shared_ptr<Renderable>& able) {
|
||||
@@ -121,6 +121,14 @@ std::shared_ptr<Plot_Render_Context> Abs_Plot::get_render_context() const {
|
||||
std::shared_ptr<Renderable> Abs_Plot::get_root_renderable() const {
|
||||
return d->root_renderable;
|
||||
}
|
||||
void Abs_Plot::set_frame_lifecycle_observer(const std::shared_ptr<Frame_Lifecycle_Observer>& observer) {
|
||||
if (d->destroying.load(std::memory_order_acquire))
|
||||
return;
|
||||
std::atomic_store_explicit(&d->render_context->frame_lifecycle_observer, observer, std::memory_order_release);
|
||||
}
|
||||
std::shared_ptr<Frame_Lifecycle_Observer> Abs_Plot::frame_lifecycle_observer() const {
|
||||
return std::atomic_load_explicit(&d->render_context->frame_lifecycle_observer, std::memory_order_acquire);
|
||||
}
|
||||
std::shared_ptr<Renderable> Abs_Plot::create_renderable_node(const std::shared_ptr<Renderable>& parent, const QString& object_name) const {
|
||||
if (!parent || !parent->is_attached() || !d->root_renderable->shares_plot_with(*parent)) {
|
||||
ASSERT(parent && parent->is_attached() && d->root_renderable->shares_plot_with(*parent), "Abs_Plot create_renderable_node error! parent invalid");
|
||||
@@ -196,18 +204,17 @@ void Abs_Plot_Private::paint_event(QPaintEvent* event) {
|
||||
std::uint64_t paint_begin_time = steady_now_ns();
|
||||
auto consumed = pipeline.consume_ready_color();
|
||||
std::pmr::vector<std::shared_ptr<Update_State>> presented_update_states(memory_resource(Memory_Domain::Update_Completion));
|
||||
QPainter painter(q);
|
||||
if (consumed.lease && !consumed.lease.frame->image.isNull()) {
|
||||
if (consumed.new_frame)
|
||||
consumed.lease.frame->metadata.paint_begin_time = paint_begin_time;
|
||||
painter.drawImage(QPoint(0, 0), consumed.lease.frame->image.image());
|
||||
qt::present_frame(q, consumed.lease.frame->image.image(),
|
||||
QColor::fromRgba(render_context->background_rgba.load(std::memory_order_acquire)));
|
||||
if (consumed.new_frame)
|
||||
presented_update_states = std::move(consumed.lease.frame->presented_update_states);
|
||||
}
|
||||
else {
|
||||
painter.fillRect(q->rect(), QColor::fromRgba(render_context->background_rgba.load(std::memory_order_acquire)));
|
||||
qt::present_frame(q, {}, QColor::fromRgba(render_context->background_rgba.load(std::memory_order_acquire)));
|
||||
}
|
||||
painter.end();
|
||||
complete_update_states(presented_update_states, Update_Stage::Presented);
|
||||
if (consumed.new_frame && consumed.lease) {
|
||||
consumed.lease.frame->metadata.paint_end_time = steady_now_ns();
|
||||
|
||||
@@ -17,6 +17,8 @@ public:
|
||||
QColor background_color();
|
||||
[[nodiscard]] std::shared_ptr<Renderable> get_root_renderable() const;
|
||||
[[nodiscard]] std::shared_ptr<Renderable> create_renderable_node(const std::shared_ptr<Renderable>& parent, const QString& object_name = {}) const;
|
||||
void set_frame_lifecycle_observer(const std::shared_ptr<Frame_Lifecycle_Observer>& observer);
|
||||
[[nodiscard]] std::shared_ptr<Frame_Lifecycle_Observer> frame_lifecycle_observer() const;
|
||||
QString object_name;
|
||||
void set_background_color(const QColor& color);
|
||||
void mark_render_state_dirty();
|
||||
|
||||
@@ -45,7 +45,7 @@ struct Afterglow_Private : Typed_Render_Data<Afterglow, Afterglow_Render_State,
|
||||
cur_index = 0;
|
||||
}
|
||||
void push_data(std::span<const double> power_range_data, const Afterglow_Render_State* s);
|
||||
void draw_image(QPainter* painter, const Afterglow_Render_State* s) {
|
||||
void draw_image(Raster_Canvas* painter, const Afterglow_Render_State* s) {
|
||||
auto frequency_axis = s->frequency_axis.lock();
|
||||
auto power_axis = s->power_axis.lock();
|
||||
if (!frequency_axis || !power_axis || image.is_null())
|
||||
@@ -68,7 +68,7 @@ struct Afterglow_Private : Typed_Render_Data<Afterglow, Afterglow_Render_State,
|
||||
painter->restore();
|
||||
}
|
||||
void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot&) override {
|
||||
QPainter* painter = &context.qt();
|
||||
Raster_Canvas* painter = &context.canvas();
|
||||
Afterglow_Render_State* s = render_state();
|
||||
draw_image(painter, s);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include <QPainter>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include "Hover_Info.h"
|
||||
#include "../Axis/Abs_Axis_p.h"
|
||||
namespace renderive {
|
||||
void Hover_Info_Render_State::draw_hover(QPainter* painter, Abs_Axis* h, Abs_Axis* v, Hover_Info_Renderable_Interface* renderable) {
|
||||
void Hover_Info_Render_State::draw_hover(Raster_Canvas* painter, Abs_Axis* h, Abs_Axis* v, Hover_Info_Renderable_Interface* renderable) {
|
||||
QPoint pos = hover_info_pos;
|
||||
std::vector<QString> lines = renderable->create_hover_string(h, v, pos);
|
||||
QFontMetrics fm(hover_info_font);
|
||||
|
||||
@@ -20,7 +20,7 @@ struct LIB_DECL Hover_Info_Render_State {
|
||||
bool use_hover_info = true;
|
||||
bool hover_info_active = false;
|
||||
QPoint hover_info_pos;
|
||||
void draw_hover(QPainter* painter, Abs_Axis* h, Abs_Axis* v, Hover_Info_Renderable_Interface* renderable);
|
||||
void draw_hover(Raster_Canvas* painter, Abs_Axis* h, Abs_Axis* v, Hover_Info_Renderable_Interface* renderable);
|
||||
bool hover_ok(Renderable* able) const;
|
||||
};
|
||||
struct LIB_DECL Hover_Info_Renderable_Interface {
|
||||
@@ -65,7 +65,7 @@ struct LIB_DECL Hover_Info_Renderable_Interface {
|
||||
};
|
||||
virtual std::vector<QString> create_hover_string(Abs_Axis* h, Abs_Axis* v, QPoint pos);
|
||||
virtual ~Hover_Info_Renderable_Interface() = default;
|
||||
virtual void draw_hover(QPainter* painter, Abs_Axis* h, Abs_Axis* v) {
|
||||
virtual void draw_hover(Raster_Canvas* painter, Abs_Axis* h, Abs_Axis* v) {
|
||||
return hover_render_state()->draw_hover(painter, h, v, this);
|
||||
}
|
||||
virtual bool hover_ok(Renderable* able) {
|
||||
|
||||
@@ -98,7 +98,7 @@ struct Multi_Select_Rect_Private : renderive::Typed_Render_Data<Multi_Select_Rec
|
||||
rect_list.pop_back();
|
||||
}
|
||||
void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot&) override {
|
||||
QPainter* painter = &context.qt();
|
||||
Raster_Canvas* painter = &context.canvas();
|
||||
Multi_Select_Rect_Render_State* s = render_state();
|
||||
auto horizontal_axis = s->horizontal_axis.lock();
|
||||
auto vertical_axis = s->vertical_axis.lock();
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include "Core/spdlog/export.h"
|
||||
#include "../plot/Plot_p.h"
|
||||
#include "../plot/Plot.h"
|
||||
namespace renderive {
|
||||
namespace {
|
||||
double duration_ms(std::uint64_t begin_ns, std::uint64_t end_ns) {
|
||||
@@ -197,39 +197,39 @@ Performance_Frame_Log& frame_log() {
|
||||
static Performance_Frame_Log value;
|
||||
return value;
|
||||
}
|
||||
std::shared_ptr<Performance_Shower> performance_shower_owner(const Abs_Plot_Private* data) {
|
||||
if (!data || !data->render_context)
|
||||
return {};
|
||||
auto observer = std::atomic_load_explicit(&data->render_context->frame_lifecycle_observer, std::memory_order_acquire);
|
||||
return std::dynamic_pointer_cast<Performance_Shower>(observer);
|
||||
}
|
||||
}
|
||||
RENDERIVE_DEFINE_RENDERABLE_BINDING(Performance_Shower, Performance_Shower_Private, Performance_Shower_Render_State, Performance_Shower_Input_Data, "Performance_Shower")
|
||||
std::shared_ptr<Performance_Shower> attach_performance_shower(Abs_Plot& plot) {
|
||||
auto* data = Abs_Plot_Private_Access::data(plot);
|
||||
if (!data)
|
||||
Performance_Shower::Builder::Builder(Abs_Plot& plot) noexcept
|
||||
: plot_(&plot), parent_(plot.get_root_renderable()) {}
|
||||
std::shared_ptr<Performance_Shower> Performance_Shower::Builder::build() const {
|
||||
if (!parent_ || !parent_->is_attached())
|
||||
return {};
|
||||
auto shower = performance_shower_owner(data);
|
||||
if (shower)
|
||||
return shower;
|
||||
auto created_shower = renderive::make_shared<Performance_Shower>();
|
||||
created_shower->init(data->root_renderable);
|
||||
std::atomic_store_explicit(&data->render_context->frame_lifecycle_observer, std::static_pointer_cast<Frame_Lifecycle_Observer>(created_shower), std::memory_order_release);
|
||||
return created_shower;
|
||||
auto result = renderive::make_shared<Performance_Shower>();
|
||||
result->init(parent_);
|
||||
if (plot_)
|
||||
plot_->set_frame_lifecycle_observer(std::static_pointer_cast<Frame_Lifecycle_Observer>(result));
|
||||
return result;
|
||||
}
|
||||
namespace {
|
||||
std::shared_ptr<Performance_Shower> performance_shower_owner(const Abs_Plot& plot) {
|
||||
return std::dynamic_pointer_cast<Performance_Shower>(plot.frame_lifecycle_observer());
|
||||
}
|
||||
}
|
||||
std::shared_ptr<Performance_Shower> attach_performance_shower(Abs_Plot& plot) {
|
||||
if (auto current = performance_shower_owner(plot))
|
||||
return current;
|
||||
return Performance_Shower::Builder(plot).build();
|
||||
}
|
||||
void detach_performance_shower(Abs_Plot& plot) {
|
||||
auto* data = Abs_Plot_Private_Access::data(plot);
|
||||
if (!data)
|
||||
auto shower = performance_shower_owner(plot);
|
||||
plot.set_frame_lifecycle_observer({});
|
||||
if (!shower)
|
||||
return;
|
||||
auto shower = performance_shower_owner(data);
|
||||
std::atomic_store_explicit(&data->render_context->frame_lifecycle_observer, std::shared_ptr<Frame_Lifecycle_Observer>{}, std::memory_order_release);
|
||||
if (shower) {
|
||||
shower->setEnabled(false);
|
||||
plot.remove_renderable(shower);
|
||||
}
|
||||
shower->setEnabled(false);
|
||||
plot.remove_renderable(shower);
|
||||
}
|
||||
bool performance_shower_enabled(const Abs_Plot& plot) {
|
||||
auto shower = performance_shower_owner(Abs_Plot_Private_Access::data(plot));
|
||||
auto shower = performance_shower_owner(plot);
|
||||
return shower && shower->enabled();
|
||||
}
|
||||
void set_performance_shower_enabled(Abs_Plot& plot, bool enabled) {
|
||||
@@ -237,8 +237,7 @@ void set_performance_shower_enabled(Abs_Plot& plot, bool enabled) {
|
||||
detach_performance_shower(plot);
|
||||
return;
|
||||
}
|
||||
auto shower = attach_performance_shower(plot);
|
||||
if (shower)
|
||||
if (auto shower = attach_performance_shower(plot))
|
||||
shower->setEnabled(true);
|
||||
}
|
||||
void Performance_Shower::setEnabled(bool enabled) {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <QWheelEvent>
|
||||
#include "../architecture/Frame_Scheduler.h"
|
||||
#include "../architecture/Renderable.h"
|
||||
#include "../Diagnostics/Performance_Shower.h"
|
||||
#include "Core/Statistics/Statistics.h"
|
||||
#include "Renderive/architecture/Render_Lease.h"
|
||||
namespace renderive {
|
||||
@@ -257,27 +258,6 @@ private:
|
||||
statistics.update(static_cast<double>(end_ns - begin_ns) / 1000000.0);
|
||||
}
|
||||
};
|
||||
struct Performance_Shower_Private;
|
||||
/// @brief 后台渲染性能信息显示图元。
|
||||
class Performance_Shower : public Renderable, public Frame_Lifecycle_Observer {
|
||||
public:
|
||||
/// @brief 创建性能显示图元。
|
||||
Performance_Shower();
|
||||
/// @brief 设置性能显示是否启用。
|
||||
void setEnabled(bool enabled);
|
||||
/// @brief 返回性能显示是否启用。
|
||||
bool enabled() const override;
|
||||
/// @brief 收集一帧生命周期记录。
|
||||
void collect_frame(const Frame_Lifecycle_Record& frame) override;
|
||||
Renderable* renderable() const override {
|
||||
return const_cast<Performance_Shower*>(this);
|
||||
}
|
||||
private:
|
||||
Performance_Shower_Private* d();
|
||||
Render_Object_Owner create_render_data() override;
|
||||
Render_Object_Owner create_render_state() override;
|
||||
Render_Object_Owner create_input_data() override;
|
||||
};
|
||||
struct Performance_Shower_Private : Typed_Render_Data<Performance_Shower, Performance_Shower_Render_State, Performance_Shower_Input_Data> {
|
||||
Frame_Performance_Aggregate aggregate;
|
||||
std::atomic_bool reset_requested{false};
|
||||
@@ -307,7 +287,7 @@ struct Performance_Shower_Private : Typed_Render_Data<Performance_Shower, Perfor
|
||||
return q()->enabled() && panel_rect.contains(pos.toPoint());
|
||||
}
|
||||
void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot& snapshot) override {
|
||||
QPainter* painter = &context.qt();
|
||||
Raster_Canvas* painter = &context.canvas();
|
||||
if (!q()->enabled() || lines.empty())
|
||||
return;
|
||||
Performance_Shower_Render_State* state = render_state();
|
||||
|
||||
@@ -102,7 +102,7 @@ struct Planisphere_Private : Typed_Render_Data<Planisphere, Planisphere_Render_S
|
||||
anchor_set->give_points(anchor_points(s), false);
|
||||
}
|
||||
void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot&) override {
|
||||
QPainter* painter = &context.qt();
|
||||
Raster_Canvas* painter = &context.canvas();
|
||||
Q_UNUSED(painter)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -382,9 +382,9 @@ void Spectrum_Private::update_marker_items(const Spectrum_Render_State* s) {
|
||||
}
|
||||
void Spectrum_Private::draw_spectrum(Frame_Raster_Context& context, Spectrum_Render_State* s) {
|
||||
if (s->use_sweep_frequency_rect)
|
||||
draw_axis_rect(&context.qt(), s);
|
||||
draw_axis_rect(&context.canvas(), s);
|
||||
}
|
||||
void Spectrum_Private::draw_axis_rect(QPainter* painter, const Spectrum_Render_State* s) const {
|
||||
void Spectrum_Private::draw_axis_rect(Raster_Canvas* painter, const Spectrum_Render_State* s) const {
|
||||
auto frequency_axis = s->frequency_axis.lock();
|
||||
auto power_axis = s->power_axis.lock();
|
||||
if (!frequency_axis || !power_axis)
|
||||
@@ -417,7 +417,7 @@ void Spectrum_Private::rasterize(Frame_Raster_Context& context, const Plot_Rende
|
||||
auto frequency_axis = s->frequency_axis.lock();
|
||||
auto power_axis = s->power_axis.lock();
|
||||
if (frequency_axis && power_axis)
|
||||
q()->draw_hover(&context.qt(), frequency_axis.get(), power_axis.get());
|
||||
q()->draw_hover(&context.canvas(), frequency_axis.get(), power_axis.get());
|
||||
}
|
||||
}
|
||||
std::size_t Spectrum_Private::estimated_frame_memory(const Plot_Render_Snapshot& snapshot) {
|
||||
|
||||
@@ -64,7 +64,7 @@ struct Spectrum_Private : Typed_Render_Data<Spectrum, Spectrum_Render_State, Spe
|
||||
void update_frequency_point_size(int frequency_point_size, Range frequency_range);
|
||||
void set_frequency_data(std::pmr::vector<double>& line_data, const Spectrum_Render_State* s);
|
||||
[[nodiscard]] double frequency_at(int index) const;
|
||||
void draw_axis_rect(QPainter* painter, const Spectrum_Render_State* s) const;
|
||||
void draw_axis_rect(Raster_Canvas* painter, const Spectrum_Render_State* s) const;
|
||||
void update_marker_items(const Spectrum_Render_State* s);
|
||||
[[nodiscard]] double get_y(double x, bool& ok, Line_Interpolation mode, Interpolation_Value_Domain domain);
|
||||
void draw_power_curve(Frame_Raster_Context& context, Spectrum_Render_State* s, const std::pmr::vector<double>& powers, const QPen& pen, const QBrush& brush);
|
||||
|
||||
@@ -88,7 +88,7 @@ struct Sweep_Frequency_Private : renderive::Typed_Render_Data<Sweep_Frequency, S
|
||||
return frequency_list.at(i).y();
|
||||
});
|
||||
Curve_Utils::draw_polyline(context, data, s->pen);
|
||||
QPainter* painter = &context.qt();
|
||||
Raster_Canvas* painter = &context.canvas();
|
||||
painter->setPen(s->cur_frequency_pen);
|
||||
double f = block_num > 1 ? offset * frequency_range.length() / (block_num - 1.0) : frequency_range.origin;
|
||||
double ff = render_axis_transform(*frequency_axis).to_pixel(f);
|
||||
|
||||
@@ -442,7 +442,7 @@ struct Waterfall_Private : Typed_Render_Data<Waterfall, Waterfall_Render_State,
|
||||
raster_cache.valid = true;
|
||||
raster_cache.draw_visible = true;
|
||||
}
|
||||
void draw_band(QPainter& painter, const Axis_Transform_Snapshot& h_transform, const Axis_Transform_Snapshot& v_transform, double logical_start, double logical_length, int source_row, int count) {
|
||||
void draw_band(Raster_Canvas& painter, const Axis_Transform_Snapshot& h_transform, const Axis_Transform_Snapshot& v_transform, double logical_start, double logical_length, int source_row, int count) {
|
||||
if (count <= 0)
|
||||
return;
|
||||
const double x = h_transform.to_pixel(raster_cache.draw_frequency_range.origin);
|
||||
@@ -459,7 +459,7 @@ struct Waterfall_Private : Typed_Render_Data<Waterfall, Waterfall_Render_State,
|
||||
render_state()->interpolation_mode);
|
||||
painter.restore();
|
||||
}
|
||||
void draw_image(QPainter& painter, const Axis_Transform_Snapshot& h_transform, const Axis_Transform_Snapshot& v_transform) {
|
||||
void draw_image(Raster_Canvas& painter, const Axis_Transform_Snapshot& h_transform, const Axis_Transform_Snapshot& v_transform) {
|
||||
if (!raster_cache.draw_visible || raster_cache.image.is_null() || raster_cache.draw_row_count <= 0)
|
||||
return;
|
||||
const int first_count = std::min(raster_cache.draw_row_count, raster_cache.draw_row_count - raster_cache.first_source_row);
|
||||
@@ -477,7 +477,7 @@ struct Waterfall_Private : Typed_Render_Data<Waterfall, Waterfall_Render_State,
|
||||
const Axis_Transform_Snapshot h_transform = render_axis_transform(*frequency_axis);
|
||||
const Axis_Transform_Snapshot v_transform = render_axis_transform(*time_axis);
|
||||
update_raster_cache(snapshot, *frame_timeline, *state, h_transform, v_transform);
|
||||
QPainter& painter = context.qt();
|
||||
Raster_Canvas& painter = context.canvas();
|
||||
draw_image(painter, h_transform, v_transform);
|
||||
if (q()->hover_ok(q()))
|
||||
q()->draw_hover(&painter, frequency_axis.get(), time_axis.get());
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "../Diagnostics/Performance_Shower.h"
|
||||
#include "Afterglow.h"
|
||||
#include "Audio_Frequency.h"
|
||||
#include "Hover_Info.h"
|
||||
@@ -17,3 +18,4 @@ LIB_DECL void detach_performance_shower(Abs_Plot& plot);
|
||||
LIB_DECL bool performance_shower_enabled(const Abs_Plot& plot);
|
||||
LIB_DECL void set_performance_shower_enabled(Abs_Plot& plot, bool enabled);
|
||||
} // namespace renderive
|
||||
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#include "Band_Region.h"
|
||||
#include "../Axis/Abs_Axis_p.h"
|
||||
#include "../base/Memory.h"
|
||||
#include "../qt/Core_Geometry_Adapter.h"
|
||||
namespace renderive {
|
||||
struct Band_Region_Render_State : Render_State, Band_Region_Prop {};
|
||||
struct Band_Region_Private : Typed_Render_Data<Band_Region, Band_Region_Render_State, Input_Data> {
|
||||
void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot&) override {
|
||||
QPainter* painter = &context.qt();
|
||||
Raster_Canvas* painter = &context.canvas();
|
||||
Band_Region_Render_State* s = render_state();
|
||||
auto x_axis = s->x_axis.lock();
|
||||
auto y_axis = s->y_axis.lock();
|
||||
@@ -13,28 +14,22 @@ struct Band_Region_Private : Typed_Render_Data<Band_Region, Band_Region_Render_S
|
||||
return;
|
||||
const auto x_transform = render_axis_transform(*x_axis);
|
||||
const auto y_transform = render_axis_transform(*y_axis);
|
||||
Range x_range = x_transform.coordinate_range;
|
||||
Range y_range = y_transform.coordinate_range;
|
||||
double x0{};
|
||||
double x1{};
|
||||
double y0{};
|
||||
double y1{};
|
||||
const core::Axis_Pair_Transform axes = qt_bridge::axis_pair(*x_axis, *y_axis);
|
||||
Directed_Range first_range{};
|
||||
Directed_Range second_range{};
|
||||
if (s->orientation == Band_Region_Orientation::X) {
|
||||
x0 = x_transform.to_pixel(s->range.origin);
|
||||
x1 = x_transform.to_pixel(s->range.target);
|
||||
y0 = y_transform.to_pixel(y_range.origin);
|
||||
y1 = y_transform.to_pixel(y_range.target);
|
||||
first_range = {s->range.origin, s->range.target};
|
||||
second_range = {y_transform.coordinate_range.origin, y_transform.coordinate_range.target};
|
||||
}
|
||||
else {
|
||||
x0 = x_transform.to_pixel(x_range.origin);
|
||||
x1 = x_transform.to_pixel(x_range.target);
|
||||
y0 = y_transform.to_pixel(s->range.origin);
|
||||
y1 = y_transform.to_pixel(s->range.target);
|
||||
first_range = {x_transform.coordinate_range.origin, x_transform.coordinate_range.target};
|
||||
second_range = {s->range.origin, s->range.target};
|
||||
}
|
||||
const auto geometry = core::prepare_band_geometry(axes, first_range, second_range);
|
||||
painter->save();
|
||||
painter->setPen(s->pen);
|
||||
painter->setBrush(s->brush);
|
||||
painter->drawRect(QRectF(QPointF(x0, y0), QPointF(x1, y1)).normalized());
|
||||
painter->drawPolygon(qt_bridge::to_qt_polygon(geometry));
|
||||
painter->restore();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include <QPainter>
|
||||
#include <cstdint>
|
||||
#include "../architecture/Renderable.h"
|
||||
#include "../Axis/Abs_Axis.h"
|
||||
|
||||
@@ -37,7 +37,7 @@ struct Color_Bar_Private : Typed_Render_Data<Color_Bar, Color_Bar_Render_State,
|
||||
return ret;
|
||||
}
|
||||
void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot& snapshot) override {
|
||||
QPainter* painter = &context.qt();
|
||||
Raster_Canvas* painter = &context.canvas();
|
||||
Color_Bar_Render_State* s = render_state();
|
||||
if (s->range.length() == 0.0)
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include <QFont>
|
||||
#include <QPainter>
|
||||
#include <QRectF>
|
||||
#include "../architecture/Renderable.h"
|
||||
#include "Color_Map.h"
|
||||
|
||||
@@ -73,7 +73,12 @@ struct Curve_Private : Typed_Render_Data<Curve, Curve_Render_State, Curve_Input_
|
||||
if (mode == Curve_Input_Mode::Samples) {
|
||||
if (samples.size() < 2 || s->data_range.length() == 0.0)
|
||||
return;
|
||||
if (s->sample_mode == Line_Sampling_Mode::Preserve_Extrema) {
|
||||
if (s->visible_range_only && (s->sample_mode == Line_Sampling_Mode::Exact || s->sample_mode == Line_Sampling_Mode::Preserve_Extrema)) {
|
||||
points = Curve_Utils::create_core_reduced_points(x_axis.get(), y_axis.get(), s->data_range, samples.size(), samples.size(), s->sample_mode, [this](int i) {
|
||||
return samples.at(i);
|
||||
});
|
||||
}
|
||||
else if (s->sample_mode == Line_Sampling_Mode::Preserve_Extrema) {
|
||||
points = Curve_Utils::create_peak_bucket_points(x_axis.get(), y_axis.get(), s->data_range, samples.size(), samples.size(), s->visible_range_only, [this](int i) {
|
||||
return samples.at(i);
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include <QPainter>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
@@ -10,6 +9,7 @@
|
||||
#include "../Axis/Abs_Axis_p.h"
|
||||
#include "../base/Frame_Memory.h"
|
||||
#include "../base/Interpolation_p.h"
|
||||
#include "../qt/Core_Geometry_Adapter.h"
|
||||
#include "Curve.h"
|
||||
|
||||
namespace renderive {
|
||||
@@ -107,6 +107,34 @@ public:
|
||||
return points;
|
||||
}
|
||||
|
||||
template <typename Value_At>
|
||||
static std::pmr::vector<QPointF> create_core_reduced_points(Abs_Axis* x_axis,
|
||||
Abs_Axis* y_axis,
|
||||
const Range& data_range,
|
||||
int total_count,
|
||||
int available_count,
|
||||
Line_Sampling_Mode sampling_mode,
|
||||
Value_At value_at) {
|
||||
std::pmr::vector<QPointF> points(frame_memory_resource());
|
||||
if (total_count < 2 || available_count < 1 || data_range.length() == 0.0)
|
||||
return points;
|
||||
const auto x_transform = render_axis_transform(*x_axis);
|
||||
const auto y_transform = render_axis_transform(*y_axis);
|
||||
const core::Axis_Pair_Transform axes = qt_bridge::axis_pair(*x_axis, *y_axis);
|
||||
std::vector<core::Trace_Sample> source;
|
||||
source.reserve(static_cast<std::size_t>(available_count));
|
||||
for (int i = 0; i < available_count; ++i)
|
||||
source.push_back({source_index_to_coord(data_range, total_count, i), value_at(i), static_cast<std::size_t>(i)});
|
||||
const auto reduced = core::Line_Reduction_Engine::reduce(
|
||||
source,
|
||||
qt_bridge::to_core_transform(x_transform),
|
||||
sampling_mode == Line_Sampling_Mode::Preserve_Extrema ? core::Curve_Reduction_Mode::Preserve_Extrema : core::Curve_Reduction_Mode::Exact);
|
||||
points.reserve(reduced.size());
|
||||
for (const core::Indexed_Sample& sample : reduced)
|
||||
points.push_back(qt_bridge::to_qt_point(axes.map(sample.coordinate, sample.value)));
|
||||
return points;
|
||||
}
|
||||
|
||||
static void draw_polyline(Frame_Raster_Context& context, std::span<const QPointF> points, const QPen& pen);
|
||||
static void draw_fill_to_y(Frame_Raster_Context& context, std::span<const QPointF> points, double baseline_y, const QBrush& brush);
|
||||
static std::pmr::vector<QPointF> representative_fill_points(std::span<const QPointF> line_points);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "../Axis/Abs_Axis_p.h"
|
||||
#include "../base/Frame_Memory.h"
|
||||
#include "../base/Memory.h"
|
||||
#include "../qt/Core_Geometry_Adapter.h"
|
||||
namespace renderive {
|
||||
struct Fill_Area_Input_Data : Latest_Input_Data {
|
||||
std::pmr::vector<QPointF> points{memory_resource(Memory_Domain::Curve)};
|
||||
@@ -36,36 +37,25 @@ struct Fill_Area_Private : Typed_Render_Data<Fill_Area, Fill_Area_Render_State,
|
||||
clear_render_input();
|
||||
}
|
||||
void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot&) override {
|
||||
QPainter* painter = &context.qt();
|
||||
Raster_Canvas* painter = &context.canvas();
|
||||
Fill_Area_Render_State* s = render_state();
|
||||
auto x_axis = s->x_axis.lock();
|
||||
auto y_axis = s->y_axis.lock();
|
||||
if (!x_axis || !y_axis || data_points.empty())
|
||||
return;
|
||||
const auto x_transform = render_axis_transform(*x_axis);
|
||||
const auto y_transform = render_axis_transform(*y_axis);
|
||||
const core::Axis_Pair_Transform axes = qt_bridge::axis_pair(*x_axis, *y_axis);
|
||||
const std::vector<Point> source_points = qt_bridge::to_core_points(data_points);
|
||||
std::pmr::vector<QPointF> polygon(frame_memory_resource());
|
||||
std::vector<Point> mapped_polygon;
|
||||
if (s->mode == Fill_Area_Mode::To_Baseline) {
|
||||
polygon.resize(data_points.size() + 2);
|
||||
double baseline_pixel = y_transform.to_pixel(s->baseline);
|
||||
double first_x = x_transform.to_pixel(data_points.front().x());
|
||||
double last_x = x_transform.to_pixel(data_points.back().x());
|
||||
polygon.front() = QPointF(first_x, baseline_pixel);
|
||||
polygon.back() = QPointF(last_x, baseline_pixel);
|
||||
for (int i = 0; i < data_points.size(); ++i) {
|
||||
const QPointF& point = data_points.at(i);
|
||||
polygon[i + 1] = QPointF(x_transform.to_pixel(point.x()), y_transform.to_pixel(point.y()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
polygon.resize(data_points.size());
|
||||
for (int i = 0; i < data_points.size(); ++i) {
|
||||
const QPointF& point = data_points.at(i);
|
||||
polygon[i] = QPointF(x_transform.to_pixel(point.x()), y_transform.to_pixel(point.y()));
|
||||
}
|
||||
mapped_polygon = core::prepare_fill_geometry(axes, source_points, s->baseline);
|
||||
}
|
||||
else
|
||||
mapped_polygon = core::prepare_curve_geometry(axes, source_points);
|
||||
polygon.reserve(mapped_polygon.size());
|
||||
for (const Point point : mapped_polygon)
|
||||
polygon.push_back(qt_bridge::to_qt_point(point));
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing, s->antialias);
|
||||
painter->setPen(s->pen);
|
||||
painter->setBrush(s->brush);
|
||||
painter->drawPolygon(polygon.data(), polygon.size());
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include <QPainter>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory_resource>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "Grid.h"
|
||||
#include "../Axis/Abs_Axis_p.h"
|
||||
#include "../base/Memory.h"
|
||||
#include "../qt/Core_Geometry_Adapter.h"
|
||||
namespace renderive {
|
||||
struct Grid_Render_State : Render_State, Grid_Prop {};
|
||||
struct Grid_Private : Typed_Render_Data<Grid, Grid_Render_State, Input_Data> {
|
||||
@@ -15,7 +16,7 @@ struct Grid_Private : Typed_Render_Data<Grid, Grid_Render_State, Input_Data> {
|
||||
ticks.push_back(range.origin + step * i);
|
||||
return ticks;
|
||||
}
|
||||
void draw_axis_grid(QPainter* painter, Abs_Axis* axis, Abs_Axis* cross_axis, const Range& axis_range, const Range& cross_range, const QPen& pen, const QPen& sub_pen, bool sub_grid) {
|
||||
void draw_axis_grid(Raster_Canvas* painter, Abs_Axis* axis, Abs_Axis* cross_axis, const Range& axis_range, const Range& cross_range, const QPen& pen, const QPen& sub_pen, bool sub_grid) {
|
||||
if (pen.style() == Qt::NoPen && (!sub_grid || sub_pen.style() == Qt::NoPen))
|
||||
return;
|
||||
const auto cross_transform = render_axis_transform(*cross_axis);
|
||||
@@ -33,7 +34,7 @@ struct Grid_Private : Typed_Render_Data<Grid, Grid_Render_State, Input_Data> {
|
||||
draw_ticks(painter, axis, cross0, cross1, ticks);
|
||||
}
|
||||
}
|
||||
void draw_division_grid(QPainter* painter, Abs_Axis* axis, Abs_Axis* cross_axis, const Range& axis_range, const Range& cross_range, const QPen& pen, int divisions) {
|
||||
void draw_division_grid(Raster_Canvas* painter, Abs_Axis* axis, Abs_Axis* cross_axis, const Range& axis_range, const Range& cross_range, const QPen& pen, int divisions) {
|
||||
if (pen.style() == Qt::NoPen)
|
||||
return;
|
||||
const auto cross_transform = render_axis_transform(*cross_axis);
|
||||
@@ -43,7 +44,7 @@ struct Grid_Private : Typed_Render_Data<Grid, Grid_Render_State, Input_Data> {
|
||||
painter->setPen(pen);
|
||||
draw_ticks(painter, axis, cross0, cross1, ticks);
|
||||
}
|
||||
void draw_ticks(QPainter* painter, Abs_Axis* axis, double cross0, double cross1, const std::vector<double>& ticks) {
|
||||
void draw_ticks(Raster_Canvas* painter, Abs_Axis* axis, double cross0, double cross1, const std::vector<double>& ticks) {
|
||||
const auto transform = render_axis_transform(*axis);
|
||||
bool vertical = transform.orientation == Qt::Horizontal;
|
||||
for (double tick : ticks) {
|
||||
@@ -55,7 +56,7 @@ struct Grid_Private : Typed_Render_Data<Grid, Grid_Render_State, Input_Data> {
|
||||
}
|
||||
}
|
||||
void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot&) override {
|
||||
QPainter* painter = &context.qt();
|
||||
Raster_Canvas* painter = &context.canvas();
|
||||
Grid_Render_State* s = render_state();
|
||||
auto x_axis = s->x_axis.lock();
|
||||
auto y_axis = s->y_axis.lock();
|
||||
@@ -69,10 +70,23 @@ struct Grid_Private : Typed_Render_Data<Grid, Grid_Render_State, Input_Data> {
|
||||
y_range = s->y_range;
|
||||
painter->save();
|
||||
if (s->line_mode == Grid_Line_Mode::Range_Division) {
|
||||
if (s->x_grid)
|
||||
draw_division_grid(painter, x_axis.get(), y_axis.get(), x_range, y_range, s->x_pen, s->x_divisions);
|
||||
if (s->y_grid)
|
||||
draw_division_grid(painter, y_axis.get(), x_axis.get(), y_range, x_range, s->y_pen, s->y_divisions);
|
||||
core::Axis_Pair_Transform axes = qt_bridge::axis_pair(*x_axis, *y_axis);
|
||||
if (s->x_range.length() != 0.0)
|
||||
axes.first.coordinate = {s->x_range.origin, s->x_range.target};
|
||||
if (s->y_range.length() != 0.0)
|
||||
axes.second.coordinate = {s->y_range.origin, s->y_range.target};
|
||||
const auto geometry = core::prepare_grid_geometry(axes, static_cast<std::size_t>(qMax(1, s->x_divisions)), static_cast<std::size_t>(qMax(1, s->y_divisions)));
|
||||
const std::size_t x_line_count = static_cast<std::size_t>(qMax(1, s->x_divisions)) + 1;
|
||||
if (s->x_grid && s->x_pen.style() != Qt::NoPen) {
|
||||
painter->setPen(s->x_pen);
|
||||
for (std::size_t i = 0; i < std::min(x_line_count, geometry.size()); ++i)
|
||||
painter->drawLine(qt_bridge::to_qt_point(geometry[i].first), qt_bridge::to_qt_point(geometry[i].second));
|
||||
}
|
||||
if (s->y_grid && s->y_pen.style() != Qt::NoPen) {
|
||||
painter->setPen(s->y_pen);
|
||||
for (std::size_t i = x_line_count; i < geometry.size(); ++i)
|
||||
painter->drawLine(qt_bridge::to_qt_point(geometry[i].first), qt_bridge::to_qt_point(geometry[i].second));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (s->x_grid)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include <QPainter>
|
||||
#include <cstdint>
|
||||
#include "../architecture/Renderable.h"
|
||||
#include "../Axis/Abs_Axis.h"
|
||||
|
||||
@@ -31,7 +31,7 @@ struct Label_Private : Typed_Render_Data<Label, Label_Render_State, Label_Input_
|
||||
use_prepared_items = true;
|
||||
clear_render_input();
|
||||
}
|
||||
void draw_item(QPainter* painter, const Label_Item& item, Abs_Axis* x_axis, Abs_Axis* y_axis) {
|
||||
void draw_item(Raster_Canvas* painter, const Label_Item& item, Abs_Axis* x_axis, Abs_Axis* y_axis) {
|
||||
if (item.text.isEmpty())
|
||||
return;
|
||||
painter->save();
|
||||
@@ -62,7 +62,7 @@ struct Label_Private : Typed_Render_Data<Label, Label_Render_State, Label_Input_
|
||||
painter->restore();
|
||||
}
|
||||
void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot&) override {
|
||||
QPainter* painter = &context.qt();
|
||||
Raster_Canvas* painter = &context.canvas();
|
||||
Label_Render_State* s = render_state();
|
||||
auto x_axis = s->x_axis.lock();
|
||||
auto y_axis = s->y_axis.lock();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include <QPainter>
|
||||
#include <memory_resource>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "Marker.h"
|
||||
#include "../Axis/Abs_Axis_p.h"
|
||||
#include "../base/Memory.h"
|
||||
#include "../qt/Core_Geometry_Adapter.h"
|
||||
namespace renderive {
|
||||
struct Marker_Render_State : Render_State, Marker_Prop {};
|
||||
struct Marker_Input_Data : Latest_Input_Data {
|
||||
@@ -21,31 +22,29 @@ struct Marker_Input_Data : Latest_Input_Data {
|
||||
struct Marker_Private : Typed_Render_Data<Marker, Marker_Render_State, Marker_Input_Data> {
|
||||
std::pmr::vector<Marker_Item> prepared_items{memory_resource(Memory_Domain::Other)};
|
||||
bool use_prepared_items = false;
|
||||
void draw_item(QPainter* painter, const Marker_Item& item, Abs_Axis* x_axis, Abs_Axis* y_axis) {
|
||||
void draw_item(Raster_Canvas* painter, const Marker_Item& item, Abs_Axis* x_axis, Abs_Axis* y_axis) {
|
||||
const auto x_transform = render_axis_transform(*x_axis);
|
||||
const auto y_transform = render_axis_transform(*y_axis);
|
||||
double x = x_transform.to_pixel(item.position.x());
|
||||
double y = y_transform.to_pixel(item.position.y());
|
||||
Range x_range = x_transform.coordinate_range;
|
||||
Range y_range = y_transform.coordinate_range;
|
||||
double x0 = x_transform.to_pixel(x_range.origin);
|
||||
double x1 = x_transform.to_pixel(x_range.target);
|
||||
double y0 = y_transform.to_pixel(y_range.origin);
|
||||
double y1 = y_transform.to_pixel(y_range.target);
|
||||
const core::Axis_Pair_Transform axes = qt_bridge::axis_pair(*x_axis, *y_axis);
|
||||
const auto marker = core::prepare_marker_geometry(axes, {item.position.x(), item.position.y()}, item.radius);
|
||||
const Point vertical_start = axes.map(item.position.x(), y_transform.coordinate_range.origin);
|
||||
const Point vertical_end = axes.map(item.position.x(), y_transform.coordinate_range.target);
|
||||
const Point horizontal_start = axes.map(x_transform.coordinate_range.origin, item.position.y());
|
||||
const Point horizontal_end = axes.map(x_transform.coordinate_range.target, item.position.y());
|
||||
painter->setPen(item.pen);
|
||||
painter->setBrush(item.brush);
|
||||
if (item.mode == Marker_Mode::Point) {
|
||||
painter->drawEllipse(QPointF(x, y), item.radius, item.radius);
|
||||
painter->drawEllipse(qt_bridge::to_qt_point(marker.center), marker.radius, marker.radius);
|
||||
}
|
||||
else if (item.mode == Marker_Mode::Vertical_Line) {
|
||||
painter->drawLine(QPointF(x, y0), QPointF(x, y1));
|
||||
painter->drawLine(qt_bridge::to_qt_point(vertical_start), qt_bridge::to_qt_point(vertical_end));
|
||||
}
|
||||
else if (item.mode == Marker_Mode::Horizontal_Line) {
|
||||
painter->drawLine(QPointF(x0, y), QPointF(x1, y));
|
||||
painter->drawLine(qt_bridge::to_qt_point(horizontal_start), qt_bridge::to_qt_point(horizontal_end));
|
||||
}
|
||||
else {
|
||||
painter->drawLine(QPointF(x, y0), QPointF(x, y1));
|
||||
painter->drawLine(QPointF(x0, y), QPointF(x1, y));
|
||||
painter->drawLine(qt_bridge::to_qt_point(vertical_start), qt_bridge::to_qt_point(vertical_end));
|
||||
painter->drawLine(qt_bridge::to_qt_point(horizontal_start), qt_bridge::to_qt_point(horizontal_end));
|
||||
}
|
||||
}
|
||||
void prepare_data(const Plot_Render_Snapshot&) override {
|
||||
@@ -58,7 +57,7 @@ struct Marker_Private : Typed_Render_Data<Marker, Marker_Render_State, Marker_In
|
||||
clear_render_input();
|
||||
}
|
||||
void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot&) override {
|
||||
QPainter* painter = &context.qt();
|
||||
Raster_Canvas* painter = &context.canvas();
|
||||
Marker_Render_State* s = render_state();
|
||||
auto x_axis = s->x_axis.lock();
|
||||
auto y_axis = s->y_axis.lock();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include <QPainter>
|
||||
#include <cstdint>
|
||||
#include <memory_resource>
|
||||
#include <span>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "../Axis/Abs_Axis_p.h"
|
||||
#include "../base/Frame_Memory.h"
|
||||
#include "../base/Memory.h"
|
||||
#include "../qt/Core_Geometry_Adapter.h"
|
||||
namespace renderive {
|
||||
struct Point_Set_Input_Data : Latest_Input_Data {
|
||||
std::pmr::vector<QPointF> points{memory_resource(Memory_Domain::Point_Set)};
|
||||
@@ -51,7 +52,7 @@ struct Point_Set_Private : Typed_Render_Data<Point_Set, Point_Set_Render_State,
|
||||
});
|
||||
clear_render_input();
|
||||
}
|
||||
void draw_point(QPainter* painter, const QPointF& point, const Point_Set_Render_State* s) {
|
||||
void draw_point(Raster_Canvas* painter, const QPointF& point, const Point_Set_Render_State* s) {
|
||||
double half = s->size / 2.0;
|
||||
if (s->shape == Point_Set_Shape::Circle) {
|
||||
painter->drawEllipse(point, half, half);
|
||||
@@ -65,22 +66,20 @@ struct Point_Set_Private : Typed_Render_Data<Point_Set, Point_Set_Render_State,
|
||||
}
|
||||
}
|
||||
void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot&) override {
|
||||
QPainter* painter = &context.qt();
|
||||
Raster_Canvas* painter = &context.canvas();
|
||||
Point_Set_Render_State* s = render_state();
|
||||
auto x_axis = s->x_axis.lock();
|
||||
auto y_axis = s->y_axis.lock();
|
||||
if (!x_axis || !y_axis || data_points.empty())
|
||||
return;
|
||||
const auto x_transform = render_axis_transform(*x_axis);
|
||||
const auto y_transform = render_axis_transform(*y_axis);
|
||||
const core::Axis_Pair_Transform axes = qt_bridge::axis_pair(*x_axis, *y_axis);
|
||||
const std::vector<Point> source_points = qt_bridge::to_core_points(data_points);
|
||||
const std::vector<Point> mapped_points = core::prepare_point_set_geometry(axes, source_points);
|
||||
std::pmr::vector<QPointF> points(frame_memory_resource());
|
||||
points.reserve(data_points.size());
|
||||
for (int i = 0; i < data_points.size(); ++i) {
|
||||
const QPointF& point = data_points.at(i);
|
||||
points.push_back(QPointF(x_transform.to_pixel(point.x()), y_transform.to_pixel(point.y())));
|
||||
}
|
||||
points.reserve(mapped_points.size());
|
||||
for (const Point point : mapped_points)
|
||||
points.push_back(qt_bridge::to_qt_point(point));
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::Antialiasing, s->antialias);
|
||||
painter->setPen(s->pen);
|
||||
painter->setBrush(s->brush);
|
||||
if (data_colors.size() == points.size()) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include <QPainter>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory_resource>
|
||||
|
||||
@@ -30,7 +30,7 @@ struct Raster_Image_Private : Typed_Render_Data<Raster_Image, Raster_Image_Rende
|
||||
clear_render_input();
|
||||
}
|
||||
void rasterize(Frame_Raster_Context& context, const Plot_Render_Snapshot&) override {
|
||||
QPainter* painter = &context.qt();
|
||||
Raster_Canvas* painter = &context.canvas();
|
||||
Raster_Image_Render_State* s = render_state();
|
||||
auto x_axis = s->x_axis.lock();
|
||||
auto y_axis = s->y_axis.lock();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include <QPainter>
|
||||
#include <QRectF>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@@ -56,14 +55,13 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
void draw(QPainter* painter, const QRectF& target_rect, Image_Interpolation_Mode mode = Image_Interpolation_Mode::Nearest) const {
|
||||
void draw(Raster_Canvas* painter, const QRectF& target_rect, Image_Interpolation_Mode mode = Image_Interpolation_Mode::Nearest) const {
|
||||
draw(painter, target_rect, QRectF(0.0, 0.0, static_cast<double>(image_data.width()), static_cast<double>(image_data.height())), mode);
|
||||
}
|
||||
void draw(QPainter* painter, const QRectF& target_rect, const QRectF& source_rect, Image_Interpolation_Mode mode) const {
|
||||
void draw(Raster_Canvas* painter, const QRectF& target_rect, const QRectF& source_rect, Image_Interpolation_Mode mode) const {
|
||||
if (image_data.isNull())
|
||||
return;
|
||||
painter->save();
|
||||
painter->setRenderHint(QPainter::SmoothPixmapTransform, mode == Image_Interpolation_Mode::Bilinear);
|
||||
painter->drawImage(target_rect, image_data.image(), source_rect);
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <QImage>
|
||||
#include <QWindow>
|
||||
#include <memory>
|
||||
#include "Renderive/architecture/global.h"
|
||||
@@ -21,8 +22,9 @@ public:
|
||||
return;
|
||||
it = !it;
|
||||
QPixmap* cur = it ? buffer1.get() : buffer2.get();
|
||||
QPainter painter(cur);
|
||||
painter.fillRect(cur->rect(), Qt::red);
|
||||
QImage image(cur->size(), QImage::Format_ARGB32_Premultiplied);
|
||||
image.fill(Qt::red);
|
||||
*cur = QPixmap::fromImage(std::move(image));
|
||||
}
|
||||
void update_texture() {
|
||||
QPixmap* cur = it ? buffer1.get() : buffer2.get();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <QColor>
|
||||
#include <QDebug>
|
||||
#include <QScrollBar>
|
||||
#include <QPainter>
|
||||
#include <QString>
|
||||
#include <QTextEdit>
|
||||
#include <QWidget>
|
||||
|
||||
+130
-47
@@ -1,42 +1,41 @@
|
||||
|
||||
w_use3rd(Qt5)
|
||||
|
||||
|
||||
set(renderive_dependencies
|
||||
global::GTest
|
||||
global::blend2d
|
||||
)
|
||||
rcl_add_dependency_action_targets(renderive_env ${renderive_dependencies})
|
||||
library_is_installed_with_rely(renderive_dependencies_installed ${renderive_dependencies})
|
||||
if (NOT renderive_dependencies_installed)
|
||||
message(STATUS "============== Renderive 依赖未安装,请先构建 renderive_env 目标")
|
||||
return()
|
||||
else ()
|
||||
find_package(blend2d CONFIG REQUIRED)
|
||||
if (NOT TARGET blend2d::blend2d)
|
||||
message(FATAL_ERROR "Renderive依赖错误 blend2d::blend2d was not found")
|
||||
return()
|
||||
endif ()
|
||||
find_package(GTest CONFIG REQUIRED)
|
||||
if (NOT TARGET GTest::gtest)
|
||||
message(FATAL_ERROR "Renderive依赖错误 GTest::GTest was not found")
|
||||
return()
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
option(YSGRAPHIC_CORE_BUILD_TESTS "Build Renderive_Core test targets" ON)
|
||||
option(YSGRAPHIC_CORE_ENABLE_ASAN "Build Renderive_Core targets with AddressSanitizer" OFF)
|
||||
option(YSGRAPHIC_CORE_ENABLE_TSAN "Build Renderive_Core targets with ThreadSanitizer" OFF)
|
||||
option(RENDERIVE_BUILD_TESTS "Build Renderive test targets" ON)
|
||||
option(RENDERIVE_ENABLE_ASAN "Build Renderive targets with AddressSanitizer" OFF)
|
||||
option(RENDERIVE_ENABLE_TSAN "Build Renderive targets with ThreadSanitizer" OFF)
|
||||
option(RENDERIVE_MEMORY_DOMAIN_STATS "Enable Renderive memory domain statistics" ON)
|
||||
option(RENDERIVE_MEMORY_GLOBAL_POOL "Enable Renderive small object synchronized pool" ON)
|
||||
option(RENDERIVE_FRAME_ARENA_GROW "Enable Renderive frame arena high-water growth" ON)
|
||||
|
||||
if (YSGRAPHIC_CORE_ENABLE_ASAN AND YSGRAPHIC_CORE_ENABLE_TSAN)
|
||||
set(renderive_dependencies
|
||||
global::blend2d
|
||||
)
|
||||
if (RENDERIVE_BUILD_TESTS)
|
||||
list(APPEND renderive_dependencies global::GTest)
|
||||
endif ()
|
||||
|
||||
rcl_add_dependency_action_targets(renderive_env ${renderive_dependencies})
|
||||
library_is_installed_with_rely(renderive_dependencies_installed ${renderive_dependencies})
|
||||
if (NOT renderive_dependencies_installed)
|
||||
message(STATUS "============== Renderive dependencies are not installed, build renderive_env first")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
find_package(blend2d CONFIG REQUIRED)
|
||||
if (NOT TARGET blend2d::blend2d)
|
||||
message(FATAL_ERROR "Renderive dependency error: blend2d::blend2d was not found")
|
||||
endif ()
|
||||
|
||||
if (RENDERIVE_BUILD_TESTS)
|
||||
find_package(GTest CONFIG REQUIRED)
|
||||
if (NOT TARGET GTest::gtest AND NOT TARGET GTest::GTest)
|
||||
message(FATAL_ERROR "Renderive dependency error: GTest target was not found")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (RENDERIVE_ENABLE_ASAN AND RENDERIVE_ENABLE_TSAN)
|
||||
message(FATAL_ERROR "Renderive cannot enable ASan and TSan simultaneously")
|
||||
endif ()
|
||||
|
||||
|
||||
if (YSGRAPHIC_CORE_ENABLE_ASAN AND MSVC)
|
||||
if (RENDERIVE_ENABLE_ASAN AND MSVC)
|
||||
string(REPLACE "/RTC1" "" __PSC_CMAKE_CXX_FLAGS_DEBUG "${__PSC_CMAKE_CXX_FLAGS_DEBUG}")
|
||||
string(REPLACE "/RTC1" "" __PSC_CMAKE_C_FLAGS_DEBUG "${__PSC_CMAKE_C_FLAGS_DEBUG}")
|
||||
set(__PSC_CMAKE_CXX_FLAGS_DEBUG "${__PSC_CMAKE_CXX_FLAGS_DEBUG}" CACHE STRING "" FORCE)
|
||||
@@ -48,7 +47,7 @@ endif ()
|
||||
|
||||
function(renderive_apply_sanitizer target)
|
||||
get_target_property(target_type ${target} TYPE)
|
||||
if (YSGRAPHIC_CORE_ENABLE_ASAN)
|
||||
if (RENDERIVE_ENABLE_ASAN)
|
||||
if (MSVC)
|
||||
target_link_libraries(${target} PRIVATE Renderive_ASAN_Options)
|
||||
if (target_type STREQUAL "STATIC_LIBRARY")
|
||||
@@ -63,7 +62,7 @@ function(renderive_apply_sanitizer target)
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
||||
if (YSGRAPHIC_CORE_ENABLE_TSAN)
|
||||
if (RENDERIVE_ENABLE_TSAN)
|
||||
if (MSVC)
|
||||
message(FATAL_ERROR "Renderive TSan is not supported by MSVC")
|
||||
endif ()
|
||||
@@ -84,7 +83,72 @@ function(renderive_enable_qt_target target)
|
||||
)
|
||||
endfunction()
|
||||
|
||||
set(renderive_public_libraries
|
||||
function(renderive_assert_core_boundary)
|
||||
foreach(path IN LISTS ARGN)
|
||||
if (IS_DIRECTORY "${path}")
|
||||
continue()
|
||||
endif ()
|
||||
file(READ "${path}" content)
|
||||
if (content MATCHES "#include[ \t]*<Q[A-Za-z0-9_:/]+>" OR
|
||||
content MATCHES "#include[ \t]*<Qt[A-Za-z0-9_:/]+>" OR
|
||||
content MATCHES "QPainter|QImage|QPen|QBrush|QColor|QFont|QString|QPointF|QRectF|QSize|QMouseEvent|QWheelEvent|QKeyEvent|Qt::Orientation|Qt::Alignment|Data_Buffer_Role|_p\\.h")
|
||||
message(FATAL_ERROR "Renderive_Core boundary violation in ${path}")
|
||||
endif ()
|
||||
endforeach ()
|
||||
endfunction()
|
||||
|
||||
set(renderive_root "${CMAKE_CURRENT_LIST_DIR}/Renderive")
|
||||
set(renderive_core_root "${renderive_root}/Core")
|
||||
set(renderive_widget_root "${CMAKE_CURRENT_LIST_DIR}/Widget")
|
||||
set(renderive_demo_root "${CMAKE_CURRENT_LIST_DIR}/Demo_Gallery")
|
||||
|
||||
file(GLOB_RECURSE renderive_core_sources CONFIGURE_DEPENDS
|
||||
"${renderive_core_root}/*.cpp"
|
||||
"${renderive_core_root}/*.h"
|
||||
)
|
||||
renderive_assert_core_boundary(${renderive_core_sources})
|
||||
|
||||
add_library(Renderive_Core STATIC ${renderive_core_sources})
|
||||
target_include_directories(Renderive_Core PUBLIC "${CMAKE_CURRENT_LIST_DIR}")
|
||||
target_link_libraries(Renderive_Core PUBLIC
|
||||
blend2d::blend2d
|
||||
)
|
||||
renderive_apply_sanitizer(Renderive_Core)
|
||||
|
||||
set(ver 5.14.2)
|
||||
|
||||
set(qt_candidates
|
||||
"D:/Qt/Qt${ver}"
|
||||
"C:/Qt/Qt${ver}"
|
||||
"D:/Qt/${ver}"
|
||||
"C:/Qt/${ver}"
|
||||
)
|
||||
set(t msvc2017_64)
|
||||
select_first_existing_directory(qb ${qt_candidates})
|
||||
set(Qt5_BaseDIR "${qb}/${ver}/${t}")
|
||||
set(Qt5_DIR "${Qt5_BaseDIR}/lib/cmake/Qt5")
|
||||
set(Qt5_Deploy "${Qt5_BaseDIR}/bin/windeployqt.exe")
|
||||
|
||||
|
||||
message("Qt5_DIR ${Qt5_DIR}")
|
||||
|
||||
|
||||
find_package(Qt5 REQUIRED COMPONENTS
|
||||
Core Gui Widgets Svg Xml
|
||||
Network Sql
|
||||
3DCore 3DRender 3DInput 3DExtras
|
||||
Quick 3DQuickScene2D QuickWidgets Qml PrintSupport
|
||||
Multimedia
|
||||
SerialPort CONFIG
|
||||
)
|
||||
|
||||
# Qt5.cmake adds a directory-level Qt source include path. Keep it out of the
|
||||
# Core compile command; Core only needs its own public headers and Blend2D.
|
||||
set_target_properties(Renderive_Core PROPERTIES
|
||||
INCLUDE_DIRECTORIES "${CMAKE_CURRENT_LIST_DIR}"
|
||||
)
|
||||
|
||||
set(renderive_qt_public_libraries
|
||||
Qt5::Widgets
|
||||
Qt5::Core
|
||||
Qt5::Svg
|
||||
@@ -93,15 +157,14 @@ set(renderive_public_libraries
|
||||
Qt5::Multimedia
|
||||
Qt5::QuickWidgets
|
||||
Core_Static
|
||||
blend2d::blend2d
|
||||
Renderive_Core
|
||||
)
|
||||
|
||||
find_package(OpenGL QUIET)
|
||||
if (OpenGL_FOUND)
|
||||
list(APPEND renderive_public_libraries OpenGL::GL)
|
||||
list(APPEND renderive_qt_public_libraries OpenGL::GL)
|
||||
endif ()
|
||||
|
||||
set(renderive_root "${CMAKE_CURRENT_LIST_DIR}/Renderive")
|
||||
file(GLOB_RECURSE renderive_sources CONFIGURE_DEPENDS
|
||||
"${renderive_root}/*.c"
|
||||
"${renderive_root}/*.cpp"
|
||||
@@ -110,10 +173,11 @@ file(GLOB_RECURSE renderive_sources CONFIGURE_DEPENDS
|
||||
)
|
||||
list(FILTER renderive_sources EXCLUDE REGEX "main\\.cpp$")
|
||||
list(FILTER renderive_sources EXCLUDE REGEX "Performance_Shower(_p)?\\.(cpp|h)$")
|
||||
list(FILTER renderive_sources EXCLUDE REGEX "[/\\\\]Core[/\\\\]")
|
||||
|
||||
add_library(Renderive STATIC ${renderive_sources})
|
||||
target_include_directories(Renderive PUBLIC "${CMAKE_CURRENT_LIST_DIR}")
|
||||
target_link_libraries(Renderive PUBLIC ${renderive_public_libraries})
|
||||
target_link_libraries(Renderive PUBLIC ${renderive_qt_public_libraries})
|
||||
target_compile_definitions(Renderive PUBLIC
|
||||
RENDERIVE_MEMORY_DOMAIN_STATS=$<BOOL:${RENDERIVE_MEMORY_DOMAIN_STATS}>
|
||||
RENDERIVE_MEMORY_GLOBAL_POOL=$<BOOL:${RENDERIVE_MEMORY_GLOBAL_POOL}>
|
||||
@@ -123,16 +187,17 @@ renderive_enable_qt_target(Renderive)
|
||||
renderive_apply_sanitizer(Renderive)
|
||||
|
||||
set(renderive_diagnostics_sources
|
||||
"${renderive_root}/Diagnostics/Performance_Shower.h"
|
||||
"${renderive_root}/plottable/Performance_Shower.cpp"
|
||||
"${renderive_root}/plottable/Performance_Shower_p.h"
|
||||
"${renderive_root}/Core/Telemetry/Csv_Telemetry_Sink.h"
|
||||
)
|
||||
add_library(Renderive_Diagnostics STATIC ${renderive_diagnostics_sources})
|
||||
target_include_directories(Renderive_Diagnostics PUBLIC "${CMAKE_CURRENT_LIST_DIR}")
|
||||
target_link_libraries(Renderive_Diagnostics PUBLIC Renderive)
|
||||
target_link_libraries(Renderive_Diagnostics PUBLIC Renderive Renderive_Core)
|
||||
renderive_enable_qt_target(Renderive_Diagnostics)
|
||||
renderive_apply_sanitizer(Renderive_Diagnostics)
|
||||
|
||||
set(renderive_widget_root "${CMAKE_CURRENT_LIST_DIR}/Widget")
|
||||
file(GLOB_RECURSE renderive_widget_sources CONFIGURE_DEPENDS
|
||||
"${renderive_widget_root}/*.c"
|
||||
"${renderive_widget_root}/*.cpp"
|
||||
@@ -146,15 +211,13 @@ target_link_libraries(Renderive_Widget PUBLIC Renderive)
|
||||
renderive_enable_qt_target(Renderive_Widget)
|
||||
renderive_apply_sanitizer(Renderive_Widget)
|
||||
|
||||
if (YSGRAPHIC_CORE_BUILD_TESTS)
|
||||
if (RENDERIVE_BUILD_TESTS)
|
||||
enable_testing()
|
||||
|
||||
if (TARGET GTest::gtest)
|
||||
set(renderive_gtest_library GTest::gtest)
|
||||
elseif (TARGET GTest::GTest)
|
||||
set(renderive_gtest_library GTest::GTest)
|
||||
else ()
|
||||
message(FATAL_ERROR "GTest target was not found")
|
||||
set(renderive_gtest_library GTest::GTest)
|
||||
endif ()
|
||||
|
||||
function(renderive_add_gtest target test_name source)
|
||||
@@ -168,6 +231,27 @@ if (YSGRAPHIC_CORE_BUILD_TESTS)
|
||||
add_test(NAME Renderive.${test_name} COMMAND ${target})
|
||||
endfunction()
|
||||
|
||||
function(renderive_add_core_gtest target test_name source)
|
||||
add_executable(${target} EXCLUDE_FROM_ALL "${CMAKE_CURRENT_LIST_DIR}/tests/${source}")
|
||||
target_link_libraries(${target} PRIVATE
|
||||
Renderive_Core
|
||||
${renderive_gtest_library}
|
||||
${ARGN}
|
||||
)
|
||||
renderive_apply_sanitizer(${target})
|
||||
add_test(NAME Renderive.${test_name} COMMAND ${target})
|
||||
endfunction()
|
||||
|
||||
renderive_add_core_gtest(
|
||||
Renderive_Core_Axis_Transform
|
||||
Axis_Transform
|
||||
Axis_Transform.cpp
|
||||
)
|
||||
renderive_add_core_gtest(
|
||||
Renderive_Core_Batches_2_4
|
||||
Core_Batches_2_4
|
||||
Core_Batches_2_4.cpp
|
||||
)
|
||||
renderive_add_gtest(
|
||||
Renderive_ASan_Runtime_Smoke
|
||||
ASan_Runtime_Smoke
|
||||
@@ -260,7 +344,6 @@ if (YSGRAPHIC_CORE_BUILD_TESTS)
|
||||
)
|
||||
endif ()
|
||||
|
||||
set(renderive_demo_root "${CMAKE_CURRENT_LIST_DIR}/Demo_Gallery")
|
||||
file(GLOB_RECURSE renderive_demo_sources CONFIGURE_DEPENDS
|
||||
"${renderive_demo_root}/*.c"
|
||||
"${renderive_demo_root}/*.cpp"
|
||||
@@ -278,4 +361,4 @@ renderive_apply_sanitizer(Demo_Gallery_Lib)
|
||||
add_executable(Demo_Gallery "${CMAKE_CURRENT_LIST_DIR}/demo_gallery_main.cpp")
|
||||
target_link_libraries(Demo_Gallery PRIVATE Demo_Gallery_Lib)
|
||||
renderive_enable_qt_target(Demo_Gallery)
|
||||
renderive_apply_sanitizer(Demo_Gallery)
|
||||
renderive_apply_sanitizer(Demo_Gallery)
|
||||
|
||||
@@ -390,14 +390,14 @@ TEST(Memory_Data_Path_Stress, ExplicitNonMonotonicCurveClipsSegmentsAndBreaksMis
|
||||
}
|
||||
EXPECT_TRUE(saw_break);
|
||||
}
|
||||
TEST(Memory_Data_Path_Stress, FrameRasterContextPreservesBackendOrder) {
|
||||
TEST(Memory_Data_Path_Stress, FrameRasterContextPreservesDrawOrder) {
|
||||
QImage image(32, 32, QImage::Format_ARGB32_Premultiplied);
|
||||
image.fill(Qt::transparent);
|
||||
renderive::Frame_Raster_Context context(image);
|
||||
context.qt().fillRect(QRect(0, 0, 32, 32), Qt::red);
|
||||
context.canvas().fillRect(QRect(0, 0, 32, 32), Qt::red);
|
||||
const std::array<QPointF, 2> line{{QPointF(2.0, 16.0), QPointF(29.0, 16.0)}};
|
||||
context.stroke_polyline(line, QPen(Qt::blue, 3.0, Qt::SolidLine, Qt::FlatCap));
|
||||
context.qt().fillRect(QRect(0, 0, 4, 4), Qt::green);
|
||||
context.canvas().fillRect(QRect(0, 0, 4, 4), Qt::green);
|
||||
context.finish();
|
||||
EXPECT_EQ(image.pixelColor(8, 8), QColor(Qt::red));
|
||||
EXPECT_GT(image.pixelColor(16, 16).blue(), 200);
|
||||
|
||||
@@ -61,7 +61,9 @@ TEST(Performance_Shower_Interaction, RepeatedToggleDoubleClickAndWheelStayRespon
|
||||
plot.init();
|
||||
plot.show();
|
||||
plot.start_render(240);
|
||||
std::unique_ptr<QWidget> control(create_control(&plot));
|
||||
std::shared_ptr<renderive::Performance_Shower> performance_shower;
|
||||
std::unique_ptr<QWidget> control(create_control(&plot, {}, &performance_shower));
|
||||
ASSERT_NE(performance_shower, nullptr);
|
||||
control->show();
|
||||
app.processEvents();
|
||||
QPushButton* performance_button = find_performance_button(control.get());
|
||||
@@ -75,7 +77,7 @@ TEST(Performance_Shower_Interaction, RepeatedToggleDoubleClickAndWheelStayRespon
|
||||
}
|
||||
double_click_button(performance_button);
|
||||
pump(app, plot, 200);
|
||||
renderive::set_performance_shower_enabled(plot, true);
|
||||
performance_shower->setEnabled(true);
|
||||
pump(app, plot, 200);
|
||||
click_plot(&plot, QPoint(8, 8), QEvent::MouseButtonPress);
|
||||
click_plot(&plot, QPoint(8, 8), QEvent::MouseButtonRelease);
|
||||
@@ -90,7 +92,16 @@ TEST(Performance_Shower_Interaction, RepeatedToggleDoubleClickAndWheelStayRespon
|
||||
pump(app, plot, 10);
|
||||
}
|
||||
pump(app, plot, 200);
|
||||
EXPECT_TRUE(performance_shower->enabled());
|
||||
renderive::set_performance_shower_enabled(plot, false);
|
||||
EXPECT_FALSE(renderive::performance_shower_enabled(plot));
|
||||
auto compatibility_shower = renderive::attach_performance_shower(plot);
|
||||
ASSERT_NE(compatibility_shower, nullptr);
|
||||
EXPECT_EQ(renderive::attach_performance_shower(plot), compatibility_shower);
|
||||
renderive::set_performance_shower_enabled(plot, true);
|
||||
EXPECT_TRUE(renderive::performance_shower_enabled(plot));
|
||||
renderive::detach_performance_shower(plot);
|
||||
EXPECT_FALSE(renderive::performance_shower_enabled(plot));
|
||||
plot.pause_render();
|
||||
}
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
Reference in New Issue
Block a user