Files
Renderive/Renderive/architecture/Frame_Raster_Context.cpp
T
2026-07-31 20:11:39 +08:00

397 lines
13 KiB
C++

#include "Frame_Raster_Context.h"
#include <algorithm>
#include <cmath>
namespace renderive {
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());
}
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;
case Qt::RoundCap: return BL_STROKE_CAP_ROUND;
default: return BL_STROKE_CAP_BUTT;
}
}
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;
case Qt::RoundJoin: return BL_STROKE_JOIN_ROUND;
case Qt::SvgMiterJoin: return BL_STROKE_JOIN_MITER_BEVEL;
default: return BL_STROKE_JOIN_MITER_CLIP;
}
}
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) {
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(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()));
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 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))
return false;
BLPath path;
bool active = false;
for (const QPointF& point : points) {
if (!std::isfinite(point.x()) || !std::isfinite(point.y())) {
active = false;
continue;
}
if (active)
path.line_to(point.x(), point.y());
else {
path.move_to(point.x(), point.y());
active = true;
}
}
if (!path.is_empty())
bl_context_.stroke_path(path);
return true;
}
bool Frame_Raster_Context::fill_polygon(std::span<const QPointF> points, const QBrush& brush) {
if (points.size() < 3 || brush.style() == Qt::NoBrush)
return true;
BLContext* context = blend();
if (!context)
return false;
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