改瀑布图合成bug

This commit is contained in:
2026-07-31 20:11:39 +08:00
parent 5beb2a9b1e
commit 37ba8b450c
46 changed files with 778 additions and 366 deletions
+8 -3
View File
@@ -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()),
+1 -1
View File
@@ -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};
+322 -88
View File
@@ -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
+89 -9
View File
@@ -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
+1 -1
View File
@@ -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;
+2 -2
View File
@@ -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));
}
+1
View File
@@ -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;
+35 -50
View File
@@ -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
View File
@@ -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();
+2
View File
@@ -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();
+2 -2
View File
@@ -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
View File
@@ -1,5 +1,4 @@
#pragma once
#include <QPainter>
#include <algorithm>
#include <array>
#include <cmath>
+1 -1
View File
@@ -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);
+2 -2
View File
@@ -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) {
+1 -1
View File
@@ -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();
+27 -28
View File
@@ -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) {
+2 -22
View File
@@ -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();
+1 -1
View File
@@ -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)
}
};
+3 -3
View File
@@ -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) {
+1 -1
View File
@@ -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);
+1 -1
View File
@@ -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);
+3 -3
View File
@@ -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());
+2
View File
@@ -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
+11 -16
View File
@@ -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
View File
@@ -1,5 +1,4 @@
#pragma once
#include <QPainter>
#include <cstdint>
#include "../architecture/Renderable.h"
#include "../Axis/Abs_Axis.h"
+1 -1
View File
@@ -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
View File
@@ -1,6 +1,5 @@
#pragma once
#include <QFont>
#include <QPainter>
#include <QRectF>
#include "../architecture/Renderable.h"
#include "Color_Map.h"
+6 -1
View File
@@ -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);
});
+29 -1
View File
@@ -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);
+11 -21
View File
@@ -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
View File
@@ -1,5 +1,4 @@
#pragma once
#include <QPainter>
#include <cstddef>
#include <cstdint>
#include <memory_resource>
+22 -8
View File
@@ -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
View File
@@ -1,5 +1,4 @@
#pragma once
#include <QPainter>
#include <cstdint>
#include "../architecture/Renderable.h"
#include "../Axis/Abs_Axis.h"
+2 -2
View File
@@ -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
View File
@@ -1,5 +1,4 @@
#pragma once
#include <QPainter>
#include <memory_resource>
#include <span>
#include <utility>
+14 -15
View File
@@ -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
View File
@@ -1,5 +1,4 @@
#pragma once
#include <QPainter>
#include <cstdint>
#include <memory_resource>
#include <span>
+9 -10
View File
@@ -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
View File
@@ -1,5 +1,4 @@
#pragma once
#include <QPainter>
#include <cstddef>
#include <cstdint>
#include <memory_resource>
+1 -1
View File
@@ -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();
+2 -4
View File
@@ -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();
}
+4 -2
View File
@@ -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();
+1
View File
@@ -3,6 +3,7 @@
#include <QColor>
#include <QDebug>
#include <QScrollBar>
#include <QPainter>
#include <QString>
#include <QTextEdit>
#include <QWidget>
+130 -47
View File
@@ -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)
+3 -3
View File
@@ -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);
+13 -2
View File
@@ -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) {