From cd8e2fa0b7ca51e1198a1a800f285d0591af9973 Mon Sep 17 00:00:00 2001 From: Ada <ada+github@thingvellir.net> Date: Fri, 28 Feb 2025 23:47:25 +1000 Subject: [PATCH] CanvasCommand better type safety and accessible from script, segfault on usage --- .../entities/src/CanvasEntityItem.cpp.in | 35 +- libraries/script-engine/src/CanvasCommand.cpp | 291 +++++++----- libraries/script-engine/src/CanvasCommand.h | 443 ++++++++++-------- libraries/script-engine/src/ScriptManager.cpp | 4 + libraries/script-engine/src/ScriptManager.h | 2 + 5 files changed, 448 insertions(+), 327 deletions(-) diff --git a/libraries/entities/src/CanvasEntityItem.cpp.in b/libraries/entities/src/CanvasEntityItem.cpp.in index 9c8bcbe6c5..e09cfbbf93 100644 --- a/libraries/entities/src/CanvasEntityItem.cpp.in +++ b/libraries/entities/src/CanvasEntityItem.cpp.in @@ -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; diff --git a/libraries/script-engine/src/CanvasCommand.cpp b/libraries/script-engine/src/CanvasCommand.cpp index aece5fd9ae..47db820684 100644 --- a/libraries/script-engine/src/CanvasCommand.cpp +++ b/libraries/script-engine/src/CanvasCommand.cpp @@ -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); +} diff --git a/libraries/script-engine/src/CanvasCommand.h b/libraries/script-engine/src/CanvasCommand.h index 47c01a6a4b..8ffd321ae2 100644 --- a/libraries/script-engine/src/CanvasCommand.h +++ b/libraries/script-engine/src/CanvasCommand.h @@ -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); diff --git a/libraries/script-engine/src/ScriptManager.cpp b/libraries/script-engine/src/ScriptManager.cpp index 58fee1f06d..54c0a59d38 100644 --- a/libraries/script-engine/src/ScriptManager.cpp +++ b/libraries/script-engine/src/ScriptManager.cpp @@ -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"; } diff --git a/libraries/script-engine/src/ScriptManager.h b/libraries/script-engine/src/ScriptManager.h index 7185d3e615..88bbd7c357 100644 --- a/libraries/script-engine/src/ScriptManager.h +++ b/libraries/script-engine/src/ScriptManager.h @@ -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 };