467 lines · 16.7 KB · C++
RawDownload
////  CanvasCommand.cpp//  libraries/script-engine/src////  Created by Ada <ada@thingvellir.net> on 2025-02-27//  Copyright 2025 Overte e.V.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html//  SPDX-License-Identifier: Apache-2.0// #include "CanvasCommand.h" #include <QtCore/QVariant> #include "ScriptEngine.h"#include "ScriptEngineCast.h"#include "ScriptValue.h"#include "ScriptManager.h"#include "ScriptValueUtils.h"#include "v8/FastScriptValueUtils.h" STATIC_SCRIPT_TYPES_INITIALIZER((+[](ScriptManager* manager){    auto scriptEngine = manager->engine().get();     scriptRegisterMetaType<CanvasCommand, canvasCommandToScriptValue, canvasCommandFromScriptValue>(scriptEngine, "CanvasCommand");    scriptRegisterMetaType<CanvasImage, canvasImageToScriptValue, canvasImageFromScriptValue>(scriptEngine, "CanvasImage");    scriptRegisterMetaType<QPainterPath, qPainterPathToScriptValue, qPainterPathFromScriptValue>(scriptEngine, "QPainterPath");    scriptRegisterMetaType<QVector<CanvasCommand>, qVectorCanvasCommandToScriptValue, qVectorCanvasCommandFromScriptValue>(scriptEngine);    scriptRegisterMetaType<CanvasPathElement, canvasPathElementToScriptValue, canvasPathElementFromScriptValue>(scriptEngine, "CanvasPathElement");})); const QString CMD_TYPE_PROP_NAME = "type"; const QString IMG_WIDTH_PROP_NAME = "width";const QString IMG_HEIGHT_PROP_NAME = "height";const QString IMG_DATA_PROP_NAME = "data"; ScriptValue canvasCommandToScriptValue(ScriptEngine* engine, const CanvasCommand& cmd) {    using Variant = CanvasCommand::Variant;     ScriptValue obj = engine->newObject();     switch (cmd.kind) {        case Variant::SetStrokeWidth: {            obj.setProperty(CMD_TYPE_PROP_NAME, "strokeWidth");            obj.setProperty("width", cmd._float[0]);            return obj;        }         case Variant::SetColor: {            obj.setProperty(CMD_TYPE_PROP_NAME, "color");             auto color = engine->newArray(4);            color.setProperty(0, cmd._color.red());            color.setProperty(1, cmd._color.green());            color.setProperty(2, cmd._color.blue());            color.setProperty(3, cmd._color.alpha());            obj.setProperty("color", color);            return obj;        }         case Variant::SetHints: {            obj.setProperty(CMD_TYPE_PROP_NAME, "hints");            obj.setProperty("hints", cmd._int[0]);            return obj;        }         case Variant::SetBlendMode: {            obj.setProperty(CMD_TYPE_PROP_NAME, "blendMode");            obj.setProperty("mode", cmd._int[0]);            return obj;        }         case Variant::SetFont: {            obj.setProperty(CMD_TYPE_PROP_NAME, "font");            obj.setProperty("family", cmd._text);            obj.setProperty("size", cmd._int[0]);            obj.setProperty("weight", cmd._int[1]);            obj.setProperty("italic", cmd._int[2]);            return obj;        }         case Variant::ClearRect: {            obj.setProperty(CMD_TYPE_PROP_NAME, "clearRect");            obj.setProperty("x", cmd._rect.x());            obj.setProperty("y", cmd._rect.y());            obj.setProperty("w", cmd._rect.width());            obj.setProperty("h", cmd._rect.height());            return obj;        }         case Variant::FillPath: {            obj.setProperty(CMD_TYPE_PROP_NAME, "fillPath");            obj.setProperty("path", qPainterPathToScriptValue(engine, cmd._paintPath));            return obj;        }         case Variant::FillRect: {            obj.setProperty(CMD_TYPE_PROP_NAME, "fillRect");            obj.setProperty("x", cmd._rect.x());            obj.setProperty("y", cmd._rect.y());            obj.setProperty("w", cmd._rect.width());            obj.setProperty("h", cmd._rect.height());            return obj;        }         case Variant::FillEllipse: {            obj.setProperty(CMD_TYPE_PROP_NAME, "fillEllipse");            obj.setProperty("x", cmd._rect.x());            obj.setProperty("y", cmd._rect.y());            obj.setProperty("w", cmd._rect.width());            obj.setProperty("h", cmd._rect.height());            return obj;        }         case Variant::FillText: {            obj.setProperty(CMD_TYPE_PROP_NAME, "fillText");            obj.setProperty("x", cmd._rect.x());            obj.setProperty("y", cmd._rect.y());            obj.setProperty("w", cmd._rect.width());            obj.setProperty("h", cmd._rect.height());            obj.setProperty("text", cmd._text);            obj.setProperty("flag", cmd._int[0]);            return obj;        }         case Variant::StrokePath: {            obj.setProperty(CMD_TYPE_PROP_NAME, "strokePath");            obj.setProperty("path", qPainterPathToScriptValue(engine, cmd._paintPath));            return obj;        }         case Variant::StrokeRect: {            obj.setProperty(CMD_TYPE_PROP_NAME, "strokeRect");            obj.setProperty("x", cmd._rect.x());            obj.setProperty("y", cmd._rect.y());            obj.setProperty("w", cmd._rect.width());            obj.setProperty("h", cmd._rect.height());            return obj;        }         case Variant::StrokeArc: {            obj.setProperty(CMD_TYPE_PROP_NAME, "strokeArc");            obj.setProperty("x", cmd._rect.x());            obj.setProperty("y", cmd._rect.y());            obj.setProperty("w", cmd._rect.width());            obj.setProperty("h", cmd._rect.height());            obj.setProperty("startAngle", cmd._float[0]);            obj.setProperty("spanAngle", cmd._float[1]);            return obj;        }         case Variant::StrokeEllipse: {            obj.setProperty(CMD_TYPE_PROP_NAME, "strokeEllipse");            obj.setProperty("x", cmd._rect.x());            obj.setProperty("y", cmd._rect.y());            obj.setProperty("w", cmd._rect.width());            obj.setProperty("h", cmd._rect.height());            return obj;        }         case Variant::Point: {            obj.setProperty(CMD_TYPE_PROP_NAME, "point");            obj.setProperty("x", cmd._point.x());            obj.setProperty("y", cmd._point.y());            return obj;        }         case Variant::Line: {            obj.setProperty(CMD_TYPE_PROP_NAME, "line");            obj.setProperty("x1", cmd._line.x1());            obj.setProperty("y1", cmd._line.y1());            obj.setProperty("x2", cmd._line.x2());            obj.setProperty("y2", cmd._line.y2());            return obj;        }         case Variant::ImageCopy: {            obj.setProperty(CMD_TYPE_PROP_NAME, "imageCopy");            obj.setProperty("srcX", cmd._rect.x());            obj.setProperty("srcY", cmd._rect.y());            obj.setProperty("srcW", cmd._rect.width());            obj.setProperty("srcH", cmd._rect.height());            obj.setProperty("destX", cmd._rect2.x());            obj.setProperty("destY", cmd._rect2.y());            obj.setProperty("destW", cmd._rect2.width());            obj.setProperty("destH", cmd._rect2.height());            obj.setProperty("image", canvasImageToScriptValue(engine, cmd._image));            return obj;        }         case Variant::Invalid: break;    }     return obj;} bool canvasCommandFromScriptValue(const ScriptValue& object, CanvasCommand& cmd) {    QString type = object.property(CMD_TYPE_PROP_NAME).toString();     if (type == "strokeWidth") {        cmd = CanvasCommand::setStrokeWidth(object.property("width").toNumber());    } else if (type == "color") {        QColor c;        if (!qColorFromScriptValue(object.property("color"), c)) { return false; }         cmd = CanvasCommand::setColor(c);    } else if (type == "hints") {        cmd = CanvasCommand::setHints(object.property("hints").toInt32());    } else if (type == "blendMode") {        cmd = CanvasCommand::setBlendMode(object.property("mode").toInt32());    } else if (type == "font") {        cmd = CanvasCommand::setFont(            object.property("family").toString(),            object.property("size").toInt32(),            object.property("weight").toInt32(),            object.property("italic").toBool()        );    } else if (type == "clearRect") {        cmd = CanvasCommand::clearRect(            object.property("x").toInt32(),            object.property("y").toInt32(),            object.property("w").toInt32(),            object.property("h").toInt32()        );    } else if (type == "fillPath") {        cmd = CanvasCommand::fillPath(qPainterPathFromScriptValue(object.property("path")));    } else if (type == "fillRect") {        cmd = CanvasCommand::fillRect(QRectF(            object.property("x").toNumber(),            object.property("y").toNumber(),            object.property("w").toNumber(),            object.property("h").toNumber()        ));    } else if (type == "fillEllipse") {        cmd = CanvasCommand::fillEllipse(QRectF(            object.property("x").toNumber(),            object.property("y").toNumber(),            object.property("w").toNumber(),            object.property("h").toNumber()        ));    } else if (type == "fillText") {        cmd = CanvasCommand::fillText(            object.property("text").toString(),            QRectF(                object.property("x").toNumber(),                object.property("y").toNumber(),                object.property("w").toNumber(),                object.property("h").toNumber()            ),            object.property("flag").toInt32()        );    } else if (type == "strokePath") {        cmd = CanvasCommand::strokePath(qPainterPathFromScriptValue(object.property("path")));    } else if (type == "strokeRect") {        cmd = CanvasCommand::strokeRect(QRectF(            object.property("x").toNumber(),            object.property("y").toNumber(),            object.property("w").toNumber(),            object.property("h").toNumber()        ));    } else if (type == "strokeArc") {        cmd = CanvasCommand::strokeArc(            QRectF(                object.property("x").toNumber(),                object.property("y").toNumber(),                object.property("w").toNumber(),                object.property("h").toNumber()            ),            object.property("startAngle").toNumber(),            object.property("spanAngle").toNumber()        );    } else if (type == "strokeEllipse") {        cmd = CanvasCommand::strokeEllipse(QRectF(            object.property("x").toNumber(),            object.property("y").toNumber(),            object.property("w").toNumber(),            object.property("h").toNumber()        ));    } else if (type == "point") {        cmd = CanvasCommand::point(            object.property("x").toNumber(),            object.property("y").toNumber()        );    } else if (type == "line") {        cmd = CanvasCommand::line(            object.property("x1").toNumber(),            object.property("y1").toNumber(),            object.property("x2").toNumber(),            object.property("y2").toNumber()        );    } else if (type == "imageCopy") {        cmd = CanvasCommand::imageCopy(            canvasImageFromScriptValue(object.property("image")),            QRectF(                object.property("srcX").toNumber(),                object.property("srcY").toNumber(),                object.property("srcW").toNumber(),                object.property("srcH").toNumber()            ),            QRectF(                object.property("destX").toNumber(),                object.property("destY").toNumber(),                object.property("destW").toNumber(),                object.property("destH").toNumber()            )        );    } else {        cmd = CanvasCommand {};    }     return true;} CanvasCommand canvasCommandFromScriptValue(const ScriptValue& object) {    CanvasCommand cmd;    canvasCommandFromScriptValue(object, cmd);    return cmd;} QVector<CanvasCommand> qVectorCanvasCommandFromScriptValue(const ScriptValue& object) {    QVector<CanvasCommand> list;    qVectorCanvasCommandFromScriptValue(object, list);    return list;} bool qVectorCanvasCommandFromScriptValue(const ScriptValue& array, QVector<CanvasCommand>& list) {    int length = array.property("length").toInteger();     for (int i = 0; i < length; i++) {        list << canvasCommandFromScriptValue(array.property(i));    }     return true;} ScriptValue qVectorCanvasCommandToScriptValue(ScriptEngine* engine, const QVector<CanvasCommand>& list) {    auto array = engine->newArray(list.length());     for (int i = 0; i < list.length(); i++) {        array.setProperty(i, canvasCommandToScriptValue(engine, list.at(i)));    }     return array;} ScriptValue canvasImageToScriptValue(ScriptEngine* engine, const CanvasImage& img) {    ScriptValue obj = engine->newObject();    obj.setProperty(IMG_WIDTH_PROP_NAME, img.width);    obj.setProperty(IMG_HEIGHT_PROP_NAME, img.height);    obj.setProperty(IMG_DATA_PROP_NAME, qBytearrayToScriptValue(engine, img.buffer));    return obj;} bool canvasImageFromScriptValue(const ScriptValue& object, CanvasImage& img) {    img.width = object.property(IMG_WIDTH_PROP_NAME).toInt32();    img.height = object.property(IMG_HEIGHT_PROP_NAME).toInt32();    return qBytearrayFromScriptValue(object.property(IMG_DATA_PROP_NAME), img.buffer);} CanvasImage canvasImageFromScriptValue(const ScriptValue& object) {    CanvasImage img = {};    canvasImageFromScriptValue(object, img);    return img;} ScriptValue qPainterPathToScriptValue(ScriptEngine* engine, const QPainterPath& path) {    ScriptValue array = engine->newArray(path.elementCount());     for (int i = 0; i < path.elementCount(); i++) {        ScriptValue obj = engine->newObject();        auto elem = path.elementAt(i);         // curves have another two points        if (elem.type == QPainterPath::CurveToElement) {            obj.setProperty("type", static_cast<uint>(elem.type));            obj.setProperty("c1x", static_cast<float>(elem.x));            obj.setProperty("c1y", static_cast<float>(elem.y));            obj.setProperty("c2x", static_cast<float>(path.elementAt(i + 1).x));            obj.setProperty("c2y", static_cast<float>(path.elementAt(i + 1).y));            obj.setProperty("x", static_cast<float>(path.elementAt(i + 2).x));            obj.setProperty("y", static_cast<float>(path.elementAt(i + 2).y));             i += 2;        } else {            obj.setProperty("type", static_cast<uint>(elem.type));            obj.setProperty("x", static_cast<float>(elem.x));            obj.setProperty("y", static_cast<float>(elem.y));        }    }     return array;} bool qPainterPathFromScriptValue(const ScriptValue& array, QPainterPath& path) {    int length = array.property("length").toInteger();    path.reserve(length);     for (int i = 0; i < length; i++) {        ScriptValue obj = array.property(i);        uint type = obj.property("type").toUInt32();        auto x = obj.property("x").toNumber();        auto y = obj.property("y").toNumber();         if (type == QPainterPath::CurveToElement) {            auto c1x = obj.property("c1x").toNumber();            auto c1y = obj.property("c1y").toNumber();            auto c2x = obj.property("c2x").toNumber();            auto c2y = obj.property("c2y").toNumber();             path.cubicTo(c1x, c1y, c2x, c2y, x, y);        } else if (type == QPainterPath::LineToElement) {            path.lineTo(x, y);        } else {            path.moveTo(x, y);        }    }     return true;} QPainterPath qPainterPathFromScriptValue(const ScriptValue& object) {    QPainterPath p;    qPainterPathFromScriptValue(object, p);    return p;} ScriptValue canvasPathElementToScriptValue(ScriptEngine* engine, const CanvasPathElement& elem) {    auto obj = engine->newObject();    obj.setProperty("type", elem.type);    obj.setProperty("x", elem.x);    obj.setProperty("y", elem.y);     if (elem.type == QPainterPath::CurveToElement) {        obj.setProperty("c1x", elem.c1x);        obj.setProperty("c1y", elem.c1y);        obj.setProperty("c2x", elem.c2x);        obj.setProperty("c2y", elem.c2y);    }     return obj;} bool canvasPathElementFromScriptValue(const ScriptValue& obj, CanvasPathElement& elem) {    int type = obj.property("type").toInt32();     elem.type = type;    elem.x = obj.property("x").toNumber();    elem.y = obj.property("y").toNumber();     if (type == QPainterPath::CurveToElement) {        elem.c1x = obj.property("c1x").toNumber();        elem.c1y = obj.property("c1y").toNumber();        elem.c2x = obj.property("c2x").toNumber();        elem.c2y = obj.property("c2y").toNumber();    }     return true;} CanvasPathElement canvasPathElementFromScriptValue(const ScriptValue& object) {    CanvasPathElement e;    canvasPathElementFromScriptValue(object, e);    return e;}