CanvasCommand better type safety and accessible from script, segfault on usage

This commit is contained in:
Ada 2025-02-28 23:47:25 +10:00
parent 9701dce201
commit cd8e2fa0b7
5 changed files with 448 additions and 327 deletions

View file

@ -108,6 +108,7 @@ void CanvasEntityItem::setImageData(const CanvasImage& image) {
}
void CanvasEntityItem::paintCommands(const QVector<CanvasCommand>& queue) {
using namespace canvas_cmd;
using V = CanvasCommand::Variant;
using Hint = CanvasCommand::RenderHint;
@ -126,14 +127,14 @@ void CanvasEntityItem::paintCommands(const QVector<CanvasCommand>& queue) {
break;
case V::SetStrokeWidth: {
auto props = cmd._setStrokeWidth;
auto props = dynamic_cast<const SetStrokeWidth&>(cmd);
pen.setWidthF(props.width);
p.setPen(pen);
break;
}
case V::SetColor: {
auto props = cmd._setColor;
auto props = dynamic_cast<const SetColor&>(cmd);
auto color = QColor(props.color[0], props.color[1], props.color[2], 255);
pen.setColor(color);
brush.setColor(color);
@ -143,7 +144,7 @@ void CanvasEntityItem::paintCommands(const QVector<CanvasCommand>& queue) {
}
case V::SetHints: {
auto props = cmd._setHints;
auto props = dynamic_cast<const SetHints&>(cmd);
p.setRenderHint(QPainter::Antialiasing, (props.hints & Hint::PrimitiveAntialiasing) != 0);
p.setRenderHint(QPainter::TextAntialiasing, (props.hints & Hint::TextAntialiasing) != 0);
p.setRenderHint(QPainter::SmoothPixmapTransform, (props.hints & Hint::BilinearImageScaling) != 0);
@ -151,37 +152,37 @@ void CanvasEntityItem::paintCommands(const QVector<CanvasCommand>& queue) {
}
case V::SetBlendMode: {
auto props = cmd._setBlendMode;
auto props = dynamic_cast<const SetBlendMode&>(cmd);
p.setCompositionMode(props.mode);
break;
}
case V::SetFont: {
auto props = cmd._setFont;
auto props = dynamic_cast<const SetFont&>(cmd);
p.setFont(QFont(props.family, props.size, props.weight, props.italic));
break;
}
case V::ClearRect: {
auto props = cmd._clearRect;
auto props = dynamic_cast<const ClearRect&>(cmd);
p.eraseRect(props.rect);
break;
}
case V::FillPath: {
auto props = cmd._fillPath;
auto props = dynamic_cast<const FillPath&>(cmd);
p.fillPath(props.path, brush);
break;
}
case V::FillRect: {
auto props = cmd._fillRect;
auto props = dynamic_cast<const FillRect&>(cmd);
p.fillRect(props.rect, brush);
break;
}
case V::FillEllipse: {
auto props = cmd._fillEllipse;
auto props = dynamic_cast<const FillEllipse&>(cmd);
auto pathHack = QPainterPath();
pathHack.addEllipse(props.rect);
p.fillPath(pathHack, brush);
@ -189,7 +190,7 @@ void CanvasEntityItem::paintCommands(const QVector<CanvasCommand>& queue) {
}
case V::FillText: {
auto props = cmd._fillText;
auto props = dynamic_cast<const FillText&>(cmd);
// unbounded text
if (props.rect.width() == 0.0 && props.rect.height() == 0.0) {
@ -201,43 +202,43 @@ void CanvasEntityItem::paintCommands(const QVector<CanvasCommand>& queue) {
}
case V::StrokePath: {
auto props = cmd._strokePath;
auto props = dynamic_cast<const StrokePath&>(cmd);
p.strokePath(props.path, pen);
break;
}
case V::StrokeRect: {
auto props = cmd._strokeRect;
auto props = dynamic_cast<const StrokeRect&>(cmd);
p.drawRect(props.rect);
break;
}
case V::StrokeArc: {
auto props = cmd._strokeArc;
auto props = dynamic_cast<const StrokeArc&>(cmd);
p.drawArc(props.rect, props.startAngle * 16, props.spanAngle * 16);
break;
}
case V::StrokeEllipse: {
auto props = cmd._strokeEllipse;
auto props = dynamic_cast<const StrokeEllipse&>(cmd);
p.drawEllipse(props.rect);
break;
}
case V::Point: {
auto props = cmd._point;
auto props = dynamic_cast<const Point&>(cmd);
p.drawPoint(props.point);
break;
}
case V::Line: {
auto props = cmd._line;
auto props = dynamic_cast<const Line&>(cmd);
p.drawLine(props.line);
break;
}
case V::ImageCopy: {
auto props = cmd._imageCopy;
auto props = dynamic_cast<const ImageCopy&>(cmd);
if (props.image.buffer.length() != static_cast<int>(4 * props.image.width * props.image.height)) {
qCCritical(entities) << "CanvasEntityItem::paintCommands: Source CanvasImage buffer has incorrect size, expected " << (4 * props.image.width * props.image.height) << ", got " << props.image.buffer.length();
break;

View file

@ -38,6 +38,7 @@ const QString IMG_BUFFER_PROP_NAME = "buffer";
ScriptValue canvasCommandToScriptValue(ScriptEngine* engine, const CanvasCommand& cmd) {
using Variant = CanvasCommand::Variant;
using namespace canvas_cmd;
ScriptValue obj = engine->newObject();
@ -45,31 +46,31 @@ ScriptValue canvasCommandToScriptValue(ScriptEngine* engine, const CanvasCommand
switch (cmd.kind()) {
case Variant::SetStrokeWidth: {
auto props = cmd._setStrokeWidth;
auto props = dynamic_cast<const SetStrokeWidth&>(cmd);
obj.setProperty("width", props.width);
return obj;
}
case Variant::SetColor: {
auto props = cmd._setColor;
auto props = dynamic_cast<const SetColor&>(cmd);
obj.setProperty("color", u8vec3ColorToScriptValue(engine, props.color));
return obj;
}
case Variant::SetHints: {
auto props = cmd._setHints;
auto props = dynamic_cast<const SetHints&>(cmd);
obj.setProperty("hints", props.hints);
return obj;
}
case Variant::SetBlendMode: {
auto props = cmd._setBlendMode;
auto props = dynamic_cast<const SetBlendMode&>(cmd);
obj.setProperty("mode", props.mode);
return obj;
}
case Variant::SetFont: {
auto props = cmd._setFont;
auto props = dynamic_cast<const SetFont&>(cmd);
obj.setProperty("family", props.family);
obj.setProperty("size", props.size);
obj.setProperty("weight", props.weight);
@ -77,14 +78,23 @@ ScriptValue canvasCommandToScriptValue(ScriptEngine* engine, const CanvasCommand
return obj;
}
case Variant::ClearRect: {
auto props = dynamic_cast<const ClearRect&>(cmd);
obj.setProperty("x", props.rect.x());
obj.setProperty("y", props.rect.y());
obj.setProperty("w", props.rect.width());
obj.setProperty("h", props.rect.height());
return obj;
}
case Variant::FillPath: {
auto props = cmd._fillPath;
auto props = dynamic_cast<const FillPath&>(cmd);
obj.setProperty("path", qPainterPathToScriptValue(engine, props.path));
return obj;
}
case Variant::FillRect: {
auto props = cmd._fillRect;
auto props = dynamic_cast<const FillRect&>(cmd);
obj.setProperty("x", props.rect.x());
obj.setProperty("y", props.rect.y());
obj.setProperty("w", props.rect.width());
@ -93,7 +103,7 @@ ScriptValue canvasCommandToScriptValue(ScriptEngine* engine, const CanvasCommand
}
case Variant::FillEllipse: {
auto props = cmd._fillEllipse;
auto props = dynamic_cast<const FillEllipse&>(cmd);
obj.setProperty("x", props.rect.x());
obj.setProperty("y", props.rect.y());
obj.setProperty("w", props.rect.width());
@ -102,7 +112,7 @@ ScriptValue canvasCommandToScriptValue(ScriptEngine* engine, const CanvasCommand
}
case Variant::FillText: {
auto props = cmd._fillText;
auto props = dynamic_cast<const FillText&>(cmd);
obj.setProperty("x", props.rect.x());
obj.setProperty("y", props.rect.y());
obj.setProperty("w", props.rect.width());
@ -113,13 +123,13 @@ ScriptValue canvasCommandToScriptValue(ScriptEngine* engine, const CanvasCommand
}
case Variant::StrokePath: {
auto props = cmd._strokePath;
auto props = dynamic_cast<const StrokePath&>(cmd);
obj.setProperty("path", qPainterPathToScriptValue(engine, props.path));
return obj;
}
case Variant::StrokeRect: {
auto props = cmd._strokeRect;
auto props = dynamic_cast<const StrokeRect&>(cmd);
obj.setProperty("x", props.rect.x());
obj.setProperty("y", props.rect.y());
obj.setProperty("w", props.rect.width());
@ -128,7 +138,7 @@ ScriptValue canvasCommandToScriptValue(ScriptEngine* engine, const CanvasCommand
}
case Variant::StrokeArc: {
auto props = cmd._strokeArc;
auto props = dynamic_cast<const StrokeArc&>(cmd);
obj.setProperty("x", props.rect.x());
obj.setProperty("y", props.rect.y());
obj.setProperty("w", props.rect.width());
@ -139,7 +149,7 @@ ScriptValue canvasCommandToScriptValue(ScriptEngine* engine, const CanvasCommand
}
case Variant::StrokeEllipse: {
auto props = cmd._strokeEllipse;
auto props = dynamic_cast<const StrokeEllipse&>(cmd);
obj.setProperty("x", props.rect.x());
obj.setProperty("y", props.rect.y());
obj.setProperty("w", props.rect.width());
@ -148,14 +158,14 @@ ScriptValue canvasCommandToScriptValue(ScriptEngine* engine, const CanvasCommand
}
case Variant::Point: {
auto props = cmd._point;
auto props = dynamic_cast<const Point&>(cmd);
obj.setProperty("x", props.point.x());
obj.setProperty("y", props.point.y());
return obj;
}
case Variant::Line: {
auto props = cmd._line;
auto props = dynamic_cast<const Line&>(cmd);
obj.setProperty("x1", props.line.x1());
obj.setProperty("y1", props.line.y1());
obj.setProperty("x2", props.line.x2());
@ -164,7 +174,7 @@ ScriptValue canvasCommandToScriptValue(ScriptEngine* engine, const CanvasCommand
}
case Variant::ImageCopy: {
auto props = cmd._imageCopy;
auto props = dynamic_cast<const ImageCopy&>(cmd);
obj.setProperty("srcX", props.src.x());
obj.setProperty("srcY", props.src.y());
obj.setProperty("srcW", props.src.width());
@ -185,65 +195,54 @@ ScriptValue canvasCommandToScriptValue(ScriptEngine* engine, const CanvasCommand
bool canvasCommandFromScriptValue(const ScriptValue& object, CanvasCommand& cmd) {
using Variant = CanvasCommand::Variant;
using namespace canvas_cmd;
uint type = object.property(CMD_TYPE_PROP_NAME).toVariant().toUInt();
uint type = object.property(CMD_TYPE_PROP_NAME).toVariant().toInt();
if (type == static_cast<uint>(Variant::SetStrokeWidth)) {
cmd.set(CanvasCommand::SetStrokeWidth { object.property("width").toNumber() });
} else if (type == static_cast<uint>(Variant::SetColor)) {
if (type == Variant::SetStrokeWidth) {
cmd = SetStrokeWidth { object.property("width").toNumber() };
} else if (type == Variant::SetColor) {
glm::u8vec3 c;
if (!u8vec3FromScriptValue(object.property("color"), c)) { return false; }
// FIXME: we have a script RGB color type but not an RGBA one
cmd.set(CanvasCommand::SetColor { c });
} else if (type == static_cast<uint>(Variant::SetHints)) {
cmd.set(CanvasCommand::SetHints {
static_cast<CanvasCommand::RenderHint>(object.property("hints").toVariant().toUInt())
});
} else if (type == static_cast<uint>(Variant::SetBlendMode)) {
cmd.set(CanvasCommand::SetBlendMode {
static_cast<QPainter::CompositionMode>(object.property("mode").toVariant().toUInt())
});
} else if (type == static_cast<uint>(Variant::SetFont)) {
cmd.set(CanvasCommand::SetFont {
cmd = SetColor(c);
} else if (type == Variant::SetHints) {
cmd = SetHints(object.property("hints").toVariant().toUInt());
} else if (type == Variant::SetBlendMode) {
cmd = SetBlendMode(object.property("mode").toVariant().toUInt());
} else if (type == Variant::SetFont) {
cmd = SetFont(
object.property("family").toString(),
object.property("size").toVariant().toInt(),
object.property("weight").toVariant().toInt(),
object.property("italic").toBool(),
});
} else if (type == static_cast<uint>(Variant::ClearRect)) {
cmd.set(CanvasCommand::ClearRect {
QRect(
object.property("x").toVariant().toInt(),
object.property("y").toVariant().toInt(),
object.property("w").toVariant().toInt(),
object.property("h").toVariant().toInt()
)
});
} else if (type == static_cast<uint>(Variant::FillPath)) {
cmd.set(CanvasCommand::FillPath {
qPainterPathFromScriptValue(object.property("path"))
});
} else if (type == static_cast<uint>(Variant::FillRect)) {
cmd.set(CanvasCommand::FillRect {
QRectF(
object.property("x").toNumber(),
object.property("y").toNumber(),
object.property("w").toNumber(),
object.property("h").toNumber()
)
});
} else if (type == static_cast<uint>(Variant::FillEllipse)) {
cmd.set(CanvasCommand::FillEllipse {
QRectF(
object.property("x").toNumber(),
object.property("y").toNumber(),
object.property("w").toNumber(),
object.property("h").toNumber()
)
});
} else if (type == static_cast<uint>(Variant::FillText)) {
cmd.set(CanvasCommand::FillText {
object.property("italic").toBool()
);
} else if (type == Variant::ClearRect) {
cmd = ClearRect(QRect(
object.property("x").toVariant().toInt(),
object.property("y").toVariant().toInt(),
object.property("w").toVariant().toInt(),
object.property("h").toVariant().toInt()
));
} else if (type == Variant::FillPath) {
cmd = FillPath(qPainterPathFromScriptValue(object.property("path")));
} else if (type == Variant::FillRect) {
cmd = FillRect(QRectF(
object.property("x").toNumber(),
object.property("y").toNumber(),
object.property("w").toNumber(),
object.property("h").toNumber()
));
} else if (type == Variant::FillEllipse) {
cmd = FillEllipse(QRectF(
object.property("x").toNumber(),
object.property("y").toNumber(),
object.property("w").toNumber(),
object.property("h").toNumber()
));
} else if (type == Variant::FillText) {
cmd = FillText(
QRectF(
object.property("x").toNumber(),
object.property("y").toNumber(),
@ -251,23 +250,19 @@ bool canvasCommandFromScriptValue(const ScriptValue& object, CanvasCommand& cmd)
object.property("h").toNumber()
),
object.property("text").toString(),
static_cast<Qt::AlignmentFlag>(object.property("flag").toVariant().toUInt()),
});
} else if (type == static_cast<uint>(Variant::StrokePath)) {
cmd.set(CanvasCommand::StrokePath {
qPainterPathFromScriptValue(object.property("path"))
});
} else if (type == static_cast<uint>(Variant::StrokeRect)) {
cmd.set(CanvasCommand::StrokeRect {
QRectF(
object.property("x").toNumber(),
object.property("y").toNumber(),
object.property("w").toNumber(),
object.property("h").toNumber()
)
});
} else if (type == static_cast<uint>(Variant::StrokeArc)) {
cmd.set(CanvasCommand::StrokeArc {
object.property("flag").toVariant().toInt()
);
} else if (type == Variant::StrokePath) {
cmd = StrokePath(qPainterPathFromScriptValue(object.property("path")));
} else if (type == Variant::StrokeRect) {
cmd = StrokeRect(QRectF(
object.property("x").toNumber(),
object.property("y").toNumber(),
object.property("w").toNumber(),
object.property("h").toNumber()
));
} else if (type == Variant::StrokeArc) {
cmd = StrokeArc(
QRectF(
object.property("x").toNumber(),
object.property("y").toNumber(),
@ -275,35 +270,30 @@ bool canvasCommandFromScriptValue(const ScriptValue& object, CanvasCommand& cmd)
object.property("h").toNumber()
),
object.property("startAngle").toNumber(),
object.property("spanAngle").toNumber(),
});
} else if (type == static_cast<uint>(Variant::StrokeEllipse)) {
cmd.set(CanvasCommand::StrokeEllipse {
QRectF(
object.property("x").toNumber(),
object.property("y").toNumber(),
object.property("w").toNumber(),
object.property("h").toNumber()
)
});
} else if (type == static_cast<uint>(Variant::Point)) {
cmd.set(CanvasCommand::Point {
QPointF(
object.property("x").toNumber(),
object.property("y").toNumber()
)
});
} else if (type == static_cast<uint>(Variant::Line)) {
cmd.set(CanvasCommand::Line {
QLineF(
object.property("x1").toNumber(),
object.property("y1").toNumber(),
object.property("x2").toNumber(),
object.property("y2").toNumber()
)
});
} else if (type == static_cast<uint>(Variant::ImageCopy)) {
cmd.set(CanvasCommand::ImageCopy {
object.property("spanAngle").toNumber()
);
} else if (type == Variant::StrokeEllipse) {
cmd = StrokeEllipse(QRectF(
object.property("x").toNumber(),
object.property("y").toNumber(),
object.property("w").toNumber(),
object.property("h").toNumber()
));
} else if (type == Variant::Point) {
cmd = Point(
object.property("x").toNumber(),
object.property("y").toNumber()
);
} else if (type == Variant::Line) {
cmd = Line(
object.property("x1").toNumber(),
object.property("y1").toNumber(),
object.property("x2").toNumber(),
object.property("y2").toNumber()
);
} else if (type == Variant::ImageCopy) {
cmd = ImageCopy(
canvasImageFromScriptValue(object.property("image")),
QRectF(
object.property("srcX").toNumber(),
object.property("srcY").toNumber(),
@ -315,11 +305,10 @@ bool canvasCommandFromScriptValue(const ScriptValue& object, CanvasCommand& cmd)
object.property("destY").toNumber(),
object.property("destW").toNumber(),
object.property("destH").toNumber()
),
canvasImageFromScriptValue(object.property("image")),
});
)
);
} else {
cmd.set(CanvasCommand::Invalid());
cmd = Invalid();
}
return true;
@ -437,3 +426,73 @@ QPainterPath qPainterPathFromScriptValue(const ScriptValue& object) {
qPainterPathFromScriptValue(object, p);
return p;
}
using namespace canvas_cmd;
CanvasCommand CanvasCommandInterface::setStrokeWidth(qreal width) const {
return SetStrokeWidth(width);
}
CanvasCommand CanvasCommandInterface::setColor(const glm::u8vec3& color) const {
return SetColor(color);
}
CanvasCommand CanvasCommandInterface::setHints(int hints) const {
return SetHints(hints);
}
CanvasCommand CanvasCommandInterface::setBlendMode(int mode) const {
return SetBlendMode(mode);
}
CanvasCommand CanvasCommandInterface::setFont(const QString& family, int size, int weight, bool italic) const {
return SetFont(family, size, weight, italic);
}
CanvasCommand CanvasCommandInterface::clearRect(const QRect& rect) const {
return ClearRect(rect);
}
CanvasCommand CanvasCommandInterface::fillPath(const QPainterPath& path) const {
return FillPath(path);
}
CanvasCommand CanvasCommandInterface::fillRect(const QRectF& rect) const {
return FillRect(rect);
}
CanvasCommand CanvasCommandInterface::fillEllipse(const QRectF& rect) const {
return FillEllipse(rect);
}
CanvasCommand CanvasCommandInterface::fillText(const QString& text, const QRectF& rect, int flag) const {
return FillText(rect, text, flag);
}
CanvasCommand CanvasCommandInterface::strokePath(const QPainterPath& path) const {
return StrokePath(path);
}
CanvasCommand CanvasCommandInterface::strokeRect(const QRectF& rect) const {
return StrokeRect(rect);
}
CanvasCommand CanvasCommandInterface::strokeArc(const QRectF& rect, qreal startAngle, qreal spanAngle) const {
return StrokeArc(rect, startAngle, spanAngle);
}
CanvasCommand CanvasCommandInterface::strokeEllipse(const QRectF& rect) const {
return StrokeEllipse(rect);
}
CanvasCommand CanvasCommandInterface::point(qreal x, qreal y) const {
return Point(x, y);
}
CanvasCommand CanvasCommandInterface::line(qreal x1, qreal y1, qreal x2, qreal y2) const {
return Line(x1, y1, x2, y2);
}
CanvasCommand CanvasCommandInterface::imageCopy(const CanvasImage& image, const QRectF& src, const QRectF& dest) const {
return ImageCopy(image, src, dest);
}

View file

@ -18,6 +18,7 @@
#include "ScriptValue.h"
#include "ScriptValueUtils.h"
#include "Scriptable.h"
#include <QColor>
#include <QPainter>
@ -33,9 +34,8 @@ struct CanvasImage {
CanvasImage(QByteArray buffer, int width, int height) : buffer(buffer), width(width), height(height) {}
};
class CanvasCommand {
public:
enum class Variant: uint8_t {
struct CanvasCommand {
enum Variant {
Invalid,
SetStrokeWidth,
SetColor,
@ -56,208 +56,263 @@ public:
ImageCopy,
};
enum RenderHint: uint8_t {
enum RenderHint {
PrimitiveAntialiasing = (1 << 0),
TextAntialiasing = (1 << 1),
BilinearImageScaling = (1 << 2),
};
struct Invalid {};
struct SetStrokeWidth { qreal width; };
struct SetColor { glm::u8vec3 color; };
struct SetHints { RenderHint hints; };
struct SetBlendMode { QPainter::CompositionMode mode; };
struct SetFont { QString family; int size; int weight; bool italic; };
struct ClearRect { QRect rect; };
struct FillPath { QPainterPath path; };
struct FillRect { QRectF rect; };
struct FillEllipse { QRectF rect; };
struct FillText { QRectF rect; QString text; Qt::AlignmentFlag flag; };
struct StrokePath { QPainterPath path; };
struct StrokeRect { QRectF rect; };
struct StrokeArc { QRectF rect; qreal startAngle, spanAngle; };
struct StrokeEllipse { QRectF rect; };
struct Point { QPointF point; };
struct Line { QLineF line; };
struct ImageCopy { QRectF src; QRectF dst; CanvasImage image; };
CanvasCommand() noexcept: _invalid(), _tag(Variant::Invalid) {}
CanvasCommand(Invalid cmd) noexcept: _invalid(), _tag(Variant::Invalid) {}
CanvasCommand(SetStrokeWidth cmd) noexcept: _setStrokeWidth(cmd), _tag(Variant::SetStrokeWidth) {}
CanvasCommand(SetColor cmd) noexcept: _setColor(cmd), _tag(Variant::SetColor) {}
CanvasCommand(SetHints cmd) noexcept: _setHints(cmd), _tag(Variant::SetHints) {}
CanvasCommand(SetBlendMode cmd) noexcept: _setBlendMode(cmd), _tag(Variant::SetBlendMode) {}
CanvasCommand(SetFont cmd) noexcept: _setFont(cmd), _tag(Variant::SetFont) {}
CanvasCommand(ClearRect cmd) noexcept: _clearRect(cmd), _tag(Variant::ClearRect) {}
CanvasCommand(FillPath cmd) noexcept: _fillPath(cmd), _tag(Variant::FillPath) {}
CanvasCommand(FillRect cmd) noexcept: _fillRect(cmd), _tag(Variant::FillRect) {}
CanvasCommand(FillEllipse cmd) noexcept: _fillEllipse(cmd), _tag(Variant::FillEllipse) {}
CanvasCommand(FillText cmd) noexcept: _fillText(cmd), _tag(Variant::FillText) {}
CanvasCommand(StrokePath cmd) noexcept: _strokePath(cmd), _tag(Variant::StrokePath) {}
CanvasCommand(StrokeRect cmd) noexcept: _strokeRect(cmd), _tag(Variant::StrokeRect) {}
CanvasCommand(StrokeArc cmd) noexcept: _strokeArc(cmd), _tag(Variant::StrokeArc) {}
CanvasCommand(StrokeEllipse cmd) noexcept: _strokeEllipse(cmd), _tag(Variant::StrokeArc) {}
CanvasCommand(Point cmd) noexcept: _point(cmd), _tag(Variant::Point) {}
CanvasCommand(Line cmd) noexcept: _line(cmd), _tag(Variant::Line) {}
CanvasCommand(ImageCopy cmd) noexcept: _imageCopy(cmd), _tag(Variant::ImageCopy) {}
~CanvasCommand() noexcept {
switch (_tag) {
case Variant::Invalid: _invalid.~Invalid(); break;
case Variant::SetStrokeWidth: _setStrokeWidth.~SetStrokeWidth(); break;
case Variant::SetColor: _setColor.~SetColor(); break;
case Variant::SetHints: _setHints.~SetHints(); break;
case Variant::SetBlendMode: _setBlendMode.~SetBlendMode(); break;
case Variant::SetFont: _setFont.~SetFont(); break;
case Variant::ClearRect: _clearRect.~ClearRect(); break;
case Variant::FillPath: _fillPath.~FillPath(); break;
case Variant::FillRect: _fillRect.~FillRect(); break;
case Variant::FillEllipse: _fillEllipse.~FillEllipse(); break;
case Variant::FillText: _fillText.~FillText(); break;
case Variant::StrokePath: _strokePath.~StrokePath(); break;
case Variant::StrokeRect: _strokeRect.~StrokeRect(); break;
case Variant::StrokeArc: _strokeArc.~StrokeArc(); break;
case Variant::StrokeEllipse: _strokeEllipse.~StrokeEllipse(); break;
case Variant::Point: _point.~Point(); break;
case Variant::Line: _line.~Line(); break;
case Variant::ImageCopy: _imageCopy.~ImageCopy(); break;
}
}
CanvasCommand(const CanvasCommand& other) noexcept {
_tag = other._tag;
switch (other._tag) {
case Variant::Invalid: _invalid = other._invalid; break;
case Variant::SetStrokeWidth: _setStrokeWidth = _setStrokeWidth; break;
case Variant::SetColor: _setColor = other._setColor; break;
case Variant::SetHints: _setHints = other._setHints; break;
case Variant::SetBlendMode: _setBlendMode = other._setBlendMode; break;
case Variant::SetFont: _setFont = other._setFont; break;
case Variant::ClearRect: _clearRect = other._clearRect; break;
case Variant::FillPath: _fillPath = other._fillPath; break;
case Variant::FillRect: _fillRect = other._fillRect; break;
case Variant::FillEllipse: _fillEllipse = other._fillEllipse; break;
case Variant::FillText: _fillText = other._fillText; break;
case Variant::StrokePath: _strokePath = other._strokePath; break;
case Variant::StrokeRect: _strokeRect = other._strokeRect; break;
case Variant::StrokeArc: _strokeArc = other._strokeArc; break;
case Variant::StrokeEllipse: _strokeEllipse = other._strokeEllipse; break;
case Variant::Point: _point = other._point; break;
case Variant::Line: _line = other._line; break;
case Variant::ImageCopy: _imageCopy = other._imageCopy; break;
}
}
CanvasCommand(CanvasCommand&& other) noexcept {
_tag = other._tag;
switch (other._tag) {
case Variant::Invalid: _invalid = other._invalid; break;
case Variant::SetStrokeWidth: _setStrokeWidth = _setStrokeWidth; break;
case Variant::SetColor: _setColor = other._setColor; break;
case Variant::SetHints: _setHints = other._setHints; break;
case Variant::SetBlendMode: _setBlendMode = other._setBlendMode; break;
case Variant::SetFont: _setFont = other._setFont; break;
case Variant::ClearRect: _clearRect = other._clearRect; break;
case Variant::FillPath: _fillPath = other._fillPath; break;
case Variant::FillRect: _fillRect = other._fillRect; break;
case Variant::FillEllipse: _fillEllipse = other._fillEllipse; break;
case Variant::FillText: _fillText = other._fillText; break;
case Variant::StrokePath: _strokePath = other._strokePath; break;
case Variant::StrokeRect: _strokeRect = other._strokeRect; break;
case Variant::StrokeArc: _strokeArc = other._strokeArc; break;
case Variant::StrokeEllipse: _strokeEllipse = other._strokeEllipse; break;
case Variant::Point: _point = other._point; break;
case Variant::Line: _line = other._line; break;
case Variant::ImageCopy: _imageCopy = other._imageCopy; break;
}
}
CanvasCommand& operator=(const CanvasCommand& other) noexcept {
_tag = other._tag;
switch (other._tag) {
case Variant::Invalid: _invalid = other._invalid; break;
case Variant::SetStrokeWidth: _setStrokeWidth = _setStrokeWidth; break;
case Variant::SetColor: _setColor = other._setColor; break;
case Variant::SetHints: _setHints = other._setHints; break;
case Variant::SetBlendMode: _setBlendMode = other._setBlendMode; break;
case Variant::SetFont: _setFont = other._setFont; break;
case Variant::ClearRect: _clearRect = other._clearRect; break;
case Variant::FillPath: _fillPath = other._fillPath; break;
case Variant::FillRect: _fillRect = other._fillRect; break;
case Variant::FillEllipse: _fillEllipse = other._fillEllipse; break;
case Variant::FillText: _fillText = other._fillText; break;
case Variant::StrokePath: _strokePath = other._strokePath; break;
case Variant::StrokeRect: _strokeRect = other._strokeRect; break;
case Variant::StrokeArc: _strokeArc = other._strokeArc; break;
case Variant::StrokeEllipse: _strokeEllipse = other._strokeEllipse; break;
case Variant::Point: _point = other._point; break;
case Variant::Line: _line = other._line; break;
case Variant::ImageCopy: _imageCopy = other._imageCopy; break;
}
return *this;
}
void set(Invalid&& cmd) { _tag = Variant::Invalid; _invalid = cmd; }
void set(SetStrokeWidth&& cmd) { _tag = Variant::SetStrokeWidth; _setStrokeWidth = cmd; }
void set(SetColor&& cmd) { _tag = Variant::SetColor; _setColor = cmd; }
void set(SetHints&& cmd) { _tag = Variant::SetHints; _setHints = cmd; }
void set(SetBlendMode&& cmd) { _tag = Variant::SetBlendMode; _setBlendMode = cmd; }
void set(SetFont&& cmd) { _tag = Variant::SetFont; _setFont = cmd; }
void set(ClearRect&& cmd) { _tag = Variant::ClearRect; _clearRect = cmd; }
void set(FillPath&& cmd) { _tag = Variant::FillPath; _fillPath = cmd; }
void set(FillRect&& cmd) { _tag = Variant::FillRect; _fillRect = cmd; }
void set(FillEllipse&& cmd) { _tag = Variant::FillEllipse; _fillEllipse = cmd; }
void set(FillText&& cmd) { _tag = Variant::FillText; _fillText = cmd; }
void set(StrokePath&& cmd) { _tag = Variant::StrokePath; _strokePath = cmd; }
void set(StrokeRect&& cmd) { _tag = Variant::StrokeRect; _strokeRect = cmd; }
void set(StrokeArc&& cmd) { _tag = Variant::StrokeArc; _strokeArc = cmd; }
void set(StrokeEllipse&& cmd) { _tag = Variant::StrokeEllipse; _strokeEllipse = cmd; }
void set(Point&& cmd) { _tag = Variant::Point; _point = cmd; }
void set(Line&& cmd) { _tag = Variant::Line; _line = cmd; }
void set(ImageCopy&& cmd) { _tag = Variant::ImageCopy; _imageCopy = cmd; }
Variant kind() const { return _tag; }
union {
Invalid _invalid;
SetStrokeWidth _setStrokeWidth;
SetColor _setColor;
SetHints _setHints;
SetBlendMode _setBlendMode;
SetFont _setFont;
ClearRect _clearRect;
FillPath _fillPath;
FillRect _fillRect;
FillText _fillText;
FillEllipse _fillEllipse;
StrokePath _strokePath;
StrokeRect _strokeRect;
StrokeArc _strokeArc;
StrokeEllipse _strokeEllipse;
Point _point;
Line _line;
ImageCopy _imageCopy;
};
private:
Variant _tag;
virtual Variant kind() const { return Variant::Invalid; }
};
class CanvasCommandFactory : public QObject {
namespace canvas_cmd {
struct Invalid : public CanvasCommand {
virtual Variant kind() const override { return Variant::Invalid; }
};
struct SetStrokeWidth : public CanvasCommand {
virtual Variant kind() const override { return Variant::SetStrokeWidth; }
SetStrokeWidth(qreal width) : width(width) {}
qreal width;
};
struct SetColor : public CanvasCommand {
virtual Variant kind() const override { return Variant::SetColor; }
SetColor(const glm::u8vec3& color) : color(color) {}
glm::u8vec3 color;
};
struct SetHints : public CanvasCommand {
virtual Variant kind() const override { return Variant::SetHints; }
SetHints(int hints) : hints(static_cast<CanvasCommand::RenderHint>(hints)) {}
CanvasCommand::RenderHint hints;
};
struct SetBlendMode : public CanvasCommand {
virtual Variant kind() const override { return Variant::SetBlendMode; }
SetBlendMode(int mode) : mode(static_cast<QPainter::CompositionMode>(mode)) {}
QPainter::CompositionMode mode;
};
struct SetFont : public CanvasCommand {
virtual Variant kind() const override { return Variant::SetFont; }
SetFont(const QString& family, int size = 12, int weight = 400, bool italic = false) : family(family), size(size), weight(weight), italic(italic) {}
QString family;
int size;
int weight;
bool italic;
};
struct ClearRect : public CanvasCommand {
virtual Variant kind() const override { return Variant::ClearRect; }
ClearRect(const QRect& rect) : rect(rect) {}
QRect rect;
};
struct FillPath : public CanvasCommand {
virtual Variant kind() const override { return Variant::FillPath; }
FillPath(const QPainterPath& path) : path(path) {}
QPainterPath path;
};
struct FillRect : public CanvasCommand {
virtual Variant kind() const override { return Variant::FillRect; }
FillRect(const QRectF& rect) : rect(rect) {}
QRectF rect;
};
struct FillEllipse : public CanvasCommand {
virtual Variant kind() const override { return Variant::FillEllipse; }
FillEllipse(const QRectF& rect) : rect(rect) {}
QRectF rect;
};
struct FillText : public CanvasCommand {
virtual Variant kind() const override { return Variant::FillText; }
FillText(const QRectF& rect, const QString& text, int flag = 0) : rect(rect), text(text), flag(static_cast<Qt::AlignmentFlag>(flag)) {}
QRectF rect;
QString text;
Qt::AlignmentFlag flag;
};
struct StrokePath : public CanvasCommand {
virtual Variant kind() const override { return Variant::StrokePath; }
StrokePath(const QPainterPath& path) : path(path) {}
QPainterPath path;
};
struct StrokeRect : public CanvasCommand {
virtual Variant kind() const override { return Variant::StrokeRect; }
StrokeRect(const QRectF& rect) : rect(rect) {}
QRectF rect;
};
struct StrokeArc : public CanvasCommand {
virtual Variant kind() const override { return Variant::StrokeArc; }
StrokeArc(const QRectF& rect, qreal startAngle, qreal spanAngle) : rect(rect), startAngle(startAngle), spanAngle(spanAngle) {}
QRectF rect;
qreal startAngle, spanAngle;
};
struct StrokeEllipse : public CanvasCommand {
virtual Variant kind() const override { return Variant::StrokeEllipse; }
StrokeEllipse(const QRectF& rect) : rect(rect) {}
QRectF rect;
};
struct Point : public CanvasCommand {
virtual Variant kind() const override { return Variant::Point; }
Point(qreal x, qreal y) : point(QPointF(x, y)) {}
QPointF point;
};
struct Line : public CanvasCommand {
virtual Variant kind() const override { return Variant::Line; }
Line(qreal x1, qreal y1, qreal x2, qreal y2) : line(QLineF(x1, y1, x2, y2)) {}
QLineF line;
};
struct ImageCopy : public CanvasCommand {
virtual Variant kind() const override { return Variant::ImageCopy; }
ImageCopy(const CanvasImage& image, const QRectF& src, const QRectF& dst) : src(src), dst(dst), image(image) {}
QRectF src;
QRectF dst;
CanvasImage image;
};
}
class CanvasCommandInterface : public QObject, protected Scriptable {
Q_OBJECT
public:
static CanvasCommand setStrokeWidth(qreal width) {
return CanvasCommand(CanvasCommand::SetStrokeWidth { width });
}
Q_PROPERTY(int TEXT_ALIGN_LEFT READ TEXT_ALIGN_LEFT CONSTANT)
Q_PROPERTY(int TEXT_ALIGN_RIGHT READ TEXT_ALIGN_RIGHT CONSTANT)
Q_PROPERTY(int TEXT_ALIGN_HCENTER READ TEXT_ALIGN_HCENTER CONSTANT)
Q_PROPERTY(int TEXT_ALIGN_JUSTIFY READ TEXT_ALIGN_JUSTIFY CONSTANT)
Q_PROPERTY(int TEXT_ALIGN_TOP READ TEXT_ALIGN_TOP CONSTANT)
Q_PROPERTY(int TEXT_ALIGN_BOTTOM READ TEXT_ALIGN_BOTTOM CONSTANT)
Q_PROPERTY(int TEXT_ALIGN_VCENTER READ TEXT_ALIGN_VCENTER CONSTANT)
Q_PROPERTY(int TEXT_ALIGN_BASELINE READ TEXT_ALIGN_BASELINE CONSTANT)
static CanvasCommand setColor(glm::u8vec3 color) {
return CanvasCommand(CanvasCommand::SetColor { color });
}
Q_PROPERTY(int HINT_ANTIALIASING READ HINT_ANTIALIASING CONSTANT)
Q_PROPERTY(int HINT_TEXT_ANTIALIASING READ HINT_TEXT_ANTIALIASING CONSTANT)
Q_PROPERTY(int HINT_BILINEAR_SCALING READ HINT_BILINEAR_SCALING CONSTANT)
static CanvasCommand setHints(uint hints) {
return CanvasCommand(CanvasCommand::SetHints { static_cast<CanvasCommand::RenderHint>(hints) });
}
Q_PROPERTY(int BLEND_SOURCEOVER READ BLEND_SOURCEOVER CONSTANT)
Q_PROPERTY(int BLEND_DESTINATIONOVER READ BLEND_DESTINATIONOVER CONSTANT)
Q_PROPERTY(int BLEND_CLEAR READ BLEND_CLEAR CONSTANT)
Q_PROPERTY(int BLEND_SOURCE READ BLEND_SOURCE CONSTANT)
Q_PROPERTY(int BLEND_DESTINATION READ BLEND_DESTINATION CONSTANT)
Q_PROPERTY(int BLEND_SOURCEIN READ BLEND_SOURCEIN CONSTANT)
Q_PROPERTY(int BLEND_DESTINATIONIN READ BLEND_DESTINATIONIN CONSTANT)
Q_PROPERTY(int BLEND_SOURCEOUT READ BLEND_SOURCEOUT CONSTANT)
Q_PROPERTY(int BLEND_DESTINATIONOUT READ BLEND_DESTINATIONOUT CONSTANT)
Q_PROPERTY(int BLEND_SOURCEATOP READ BLEND_SOURCEATOP CONSTANT)
Q_PROPERTY(int BLEND_DESTINATIONATOP READ BLEND_DESTINATIONATOP CONSTANT)
Q_PROPERTY(int BLEND_XOR READ BLEND_XOR CONSTANT)
Q_PROPERTY(int BLEND_PLUS READ BLEND_PLUS CONSTANT)
Q_PROPERTY(int BLEND_MULTIPLY READ BLEND_MULTIPLY CONSTANT)
Q_PROPERTY(int BLEND_SCREEN READ BLEND_SCREEN CONSTANT)
Q_PROPERTY(int BLEND_OVERLAY READ BLEND_OVERLAY CONSTANT)
Q_PROPERTY(int BLEND_DARKEN READ BLEND_DARKEN CONSTANT)
Q_PROPERTY(int BLEND_LIGHTEN READ BLEND_LIGHTEN CONSTANT)
Q_PROPERTY(int BLEND_COLORDODGE READ BLEND_COLORDODGE CONSTANT)
Q_PROPERTY(int BLEND_COLORBURN READ BLEND_COLORBURN CONSTANT)
Q_PROPERTY(int BLEND_HARDLIGHT READ BLEND_HARDLIGHT CONSTANT)
Q_PROPERTY(int BLEND_SOFTLIGHT READ BLEND_SOFTLIGHT CONSTANT)
Q_PROPERTY(int BLEND_DIFFERENCE READ BLEND_DIFFERENCE CONSTANT)
Q_PROPERTY(int BLEND_EXCLUSION READ BLEND_EXCLUSION CONSTANT)
public slots:
CanvasCommand setStrokeWidth(qreal width) const;
CanvasCommand setColor(const glm::u8vec3& color) const;
CanvasCommand setHints(int hints) const;
CanvasCommand setBlendMode(int mode) const;
CanvasCommand setFont(const QString& family, int size = 12, int weight = QFont::Normal, bool italic = false) const;
CanvasCommand clearRect(const QRect& rect) const;
CanvasCommand fillPath(const QPainterPath& path) const;
CanvasCommand fillRect(const QRectF& rect) const;
CanvasCommand fillEllipse(const QRectF& rect) const;
CanvasCommand fillText(const QString& text, const QRectF& rect, int flag = 0) const;
CanvasCommand strokePath(const QPainterPath& path) const;
CanvasCommand strokeRect(const QRectF& rect) const;
CanvasCommand strokeArc(const QRectF& rect, qreal startAngle, qreal spanAngle) const;
CanvasCommand strokeEllipse(const QRectF& rect) const;
CanvasCommand point(qreal x, qreal y) const;
CanvasCommand line(qreal x1, qreal y1, qreal x2, qreal y2) const;
CanvasCommand imageCopy(const CanvasImage& image, const QRectF& src, const QRectF& dest) const;
private:
int TEXT_ALIGN_LEFT() const { return Qt::AlignLeft; }
int TEXT_ALIGN_RIGHT() const { return Qt::AlignRight; }
int TEXT_ALIGN_HCENTER() const { return Qt::AlignHCenter; }
int TEXT_ALIGN_JUSTIFY() const { return Qt::AlignJustify; }
int TEXT_ALIGN_TOP() const { return Qt::AlignTop; }
int TEXT_ALIGN_BOTTOM() const { return Qt::AlignBottom; }
int TEXT_ALIGN_VCENTER() const { return Qt::AlignVCenter; }
int TEXT_ALIGN_BASELINE() const { return Qt::AlignBaseline; }
int HINT_ANTIALIASING() const { return CanvasCommand::RenderHint::PrimitiveAntialiasing; }
int HINT_TEXT_ANTIALIASING() const { return CanvasCommand::RenderHint::PrimitiveAntialiasing; }
int HINT_BILINEAR_SCALING() const { return CanvasCommand::RenderHint::BilinearImageScaling; }
int BLEND_SOURCEOVER() const { return QPainter::CompositionMode_SourceOver; }
int BLEND_DESTINATIONOVER() const { return QPainter::CompositionMode_DestinationOver; }
int BLEND_CLEAR() const { return QPainter::CompositionMode_Clear; }
int BLEND_SOURCE() const { return QPainter::CompositionMode_Source; }
int BLEND_DESTINATION() const { return QPainter::CompositionMode_Destination; }
int BLEND_SOURCEIN() const { return QPainter::CompositionMode_SourceIn; }
int BLEND_DESTINATIONIN() const { return QPainter::CompositionMode_DestinationIn; }
int BLEND_SOURCEOUT() const { return QPainter::CompositionMode_SourceOut; }
int BLEND_DESTINATIONOUT() const { return QPainter::CompositionMode_DestinationOut; }
int BLEND_SOURCEATOP() const { return QPainter::CompositionMode_SourceAtop; }
int BLEND_DESTINATIONATOP() const { return QPainter::CompositionMode_DestinationAtop; }
int BLEND_XOR() const { return QPainter::CompositionMode_Xor; }
int BLEND_PLUS() const { return QPainter::CompositionMode_Plus; }
int BLEND_MULTIPLY() const { return QPainter::CompositionMode_Multiply; }
int BLEND_SCREEN() const { return QPainter::CompositionMode_Screen; }
int BLEND_OVERLAY() const { return QPainter::CompositionMode_Overlay; }
int BLEND_DARKEN() const { return QPainter::CompositionMode_Darken; }
int BLEND_LIGHTEN() const { return QPainter::CompositionMode_Lighten; }
int BLEND_COLORDODGE() const { return QPainter::CompositionMode_ColorDodge; }
int BLEND_COLORBURN() const { return QPainter::CompositionMode_ColorBurn; }
int BLEND_HARDLIGHT() const { return QPainter::CompositionMode_HardLight; }
int BLEND_SOFTLIGHT() const { return QPainter::CompositionMode_SoftLight; }
int BLEND_DIFFERENCE() const { return QPainter::CompositionMode_Difference; }
int BLEND_EXCLUSION() const { return QPainter::CompositionMode_Exclusion; }
};
void registerCanvasMetaTypes(ScriptEngine *engine);

View file

@ -847,6 +847,10 @@ void ScriptManager::init() {
scriptEngine->registerGlobalObject("StackTest", new StackTestScriptingInterface(this));
#endif
if (_context == CLIENT_SCRIPT) {
scriptEngine->registerGlobalObject("CanvasCommand", &_canvasCommandLibrary);
}
qCDebug(scriptengine) << "Engine initialized";
}

View file

@ -48,6 +48,7 @@
#include "ScriptValue.h"
#include "ScriptException.h"
#include "Vec3.h"
#include "CanvasCommand.h"
static const QString NO_SCRIPT("");
@ -1636,6 +1637,7 @@ protected:
Mat4 _mat4Library;
ScriptUUID _uuidLibrary;
ConsoleScriptingInterface _consoleScriptingInterface;
CanvasCommandInterface _canvasCommandLibrary;
std::atomic<bool> _isUserLoaded { false };
bool _isReloading { false };