diff --git a/CMakeLists.txt b/CMakeLists.txt index c675f56..64781f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.14) -project(scratchcpp-render VERSION 0.8.0 LANGUAGES CXX) +project(scratchcpp-render VERSION 0.9.0 LANGUAGES CXX) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOUIC ON) diff --git a/libscratchcpp b/libscratchcpp index d9e0e11..8ed5355 160000 --- a/libscratchcpp +++ b/libscratchcpp @@ -1 +1 @@ -Subproject commit d9e0e1124174a23d9f2783e756027da2f57d67b5 +Subproject commit 8ed5355643f3b92cd672cda7673dba1b4105bbb1 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e812296..af50238 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -24,6 +24,8 @@ qt_add_qml_module(scratchcpp-render projectloader.h projectscene.cpp projectscene.h + targetmodel.cpp + targetmodel.h stagemodel.cpp stagemodel.h spritemodel.cpp diff --git a/src/ProjectPlayer.qml b/src/ProjectPlayer.qml index 1653eb2..87f8f7e 100644 --- a/src/ProjectPlayer.qml +++ b/src/ProjectPlayer.qml @@ -27,6 +27,7 @@ ProjectScene { readonly property Rectangle stageRect: contentRect signal loaded() signal failedToLoad() + signal loadingAborted() id: root engine: loader.engine @@ -39,6 +40,11 @@ ProjectScene { loader.fileName = fileName; } + function stopLoading() { + if (priv.loading) + loader.stopLoading(); + } + QtObject { id: priv property bool loading: false @@ -54,10 +60,27 @@ ProjectScene { onLoadingFinished: { priv.loading = false; - if(loadStatus) - loaded(); - else - failedToLoad(); + switch (loadStatus) { + case ProjectLoader.Loaded: + loaded(); + break; + + case ProjectLoader.Failed: + failedToLoad(); + break; + + case ProjectLoader.Aborted: + loadingAborted(); + break; + + default: + break; + } + } + + onLoadStatusChanged: { + if (loadStatus === ProjectLoader.Loading) + priv.loading = true; } onStageChanged: stage.loadCostume(); @@ -150,6 +173,7 @@ ProjectScene { mouseArea: sceneMouseArea stageScale: root.stageScale onStageModelChanged: stageModel.renderedTarget = this + Component.onCompleted: stageModel.penLayer = projectPenLayer } Loader { diff --git a/src/blocks/penblocks.cpp b/src/blocks/penblocks.cpp index 8905786..2d77b79 100644 --- a/src/blocks/penblocks.cpp +++ b/src/blocks/penblocks.cpp @@ -34,6 +34,11 @@ std::string PenBlocks::description() const return "Pen blocks"; } +Rgb PenBlocks::color() const +{ + return rgb(15, 189, 140); +} + void PenBlocks::registerBlocks(IEngine *engine) { // Blocks @@ -215,7 +220,7 @@ unsigned int PenBlocks::stamp(libscratchcpp::VirtualMachine *vm) unsigned int PenBlocks::penDown(VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) model->setPenDown(true); @@ -225,7 +230,7 @@ unsigned int PenBlocks::penDown(VirtualMachine *vm) unsigned int PenBlocks::penUp(libscratchcpp::VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) model->setPenDown(false); @@ -235,7 +240,7 @@ unsigned int PenBlocks::penUp(libscratchcpp::VirtualMachine *vm) unsigned int PenBlocks::changePenSizeBy(libscratchcpp::VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) model->penAttributes().diameter = std::clamp(model->penAttributes().diameter + vm->getInput(0, 1)->toDouble(), PEN_SIZE_MIN, PEN_SIZE_MAX); @@ -245,7 +250,7 @@ unsigned int PenBlocks::changePenSizeBy(libscratchcpp::VirtualMachine *vm) unsigned int PenBlocks::setPenSizeTo(libscratchcpp::VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) model->penAttributes().diameter = std::clamp(vm->getInput(0, 1)->toDouble(), PEN_SIZE_MIN, PEN_SIZE_MAX); @@ -255,7 +260,7 @@ unsigned int PenBlocks::setPenSizeTo(libscratchcpp::VirtualMachine *vm) unsigned int PenBlocks::changePenShadeBy(libscratchcpp::VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) { PenState &penState = model->penState(); @@ -267,7 +272,7 @@ unsigned int PenBlocks::changePenShadeBy(libscratchcpp::VirtualMachine *vm) unsigned int PenBlocks::setPenShadeToNumber(libscratchcpp::VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) setPenShade(vm->getInput(0, 1)->toInt(), model->penState()); @@ -277,7 +282,7 @@ unsigned int PenBlocks::setPenShadeToNumber(libscratchcpp::VirtualMachine *vm) unsigned int PenBlocks::changePenHueBy(libscratchcpp::VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) { PenState &penState = model->penState(); @@ -291,7 +296,7 @@ unsigned int PenBlocks::changePenHueBy(libscratchcpp::VirtualMachine *vm) unsigned int PenBlocks::setPenHueToNumber(libscratchcpp::VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) { PenState &penState = model->penState(); @@ -306,7 +311,7 @@ unsigned int PenBlocks::setPenHueToNumber(libscratchcpp::VirtualMachine *vm) unsigned int PenBlocks::setPenColorToColor(libscratchcpp::VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) { const Value *value = vm->getInput(0, 1); @@ -353,7 +358,7 @@ unsigned int PenBlocks::setPenColorToColor(libscratchcpp::VirtualMachine *vm) unsigned int PenBlocks::changePenColorParamBy(VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) { const auto it = COLOR_PARAM_MAP.find(vm->getInput(0, 2)->toString()); @@ -369,7 +374,7 @@ unsigned int PenBlocks::changePenColorParamBy(VirtualMachine *vm) unsigned int PenBlocks::changePenColorBy(VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) setOrChangeColorParam(ColorParam::COLOR, vm->getInput(0, 1)->toDouble(), model->penState(), true); @@ -379,7 +384,7 @@ unsigned int PenBlocks::changePenColorBy(VirtualMachine *vm) unsigned int PenBlocks::changePenSaturationBy(VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) setOrChangeColorParam(ColorParam::SATURATION, vm->getInput(0, 1)->toDouble(), model->penState(), true); @@ -389,7 +394,7 @@ unsigned int PenBlocks::changePenSaturationBy(VirtualMachine *vm) unsigned int PenBlocks::changePenBrightnessBy(VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) setOrChangeColorParam(ColorParam::BRIGHTNESS, vm->getInput(0, 1)->toDouble(), model->penState(), true); @@ -399,7 +404,7 @@ unsigned int PenBlocks::changePenBrightnessBy(VirtualMachine *vm) unsigned int PenBlocks::changePenTransparencyBy(VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) setOrChangeColorParam(ColorParam::TRANSPARENCY, vm->getInput(0, 1)->toDouble(), model->penState(), true); @@ -409,7 +414,7 @@ unsigned int PenBlocks::changePenTransparencyBy(VirtualMachine *vm) unsigned int PenBlocks::setPenColorParamTo(VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) { const auto it = COLOR_PARAM_MAP.find(vm->getInput(0, 2)->toString()); @@ -425,7 +430,7 @@ unsigned int PenBlocks::setPenColorParamTo(VirtualMachine *vm) unsigned int PenBlocks::setPenColorTo(VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) setOrChangeColorParam(ColorParam::COLOR, vm->getInput(0, 1)->toDouble(), model->penState(), false); @@ -435,7 +440,7 @@ unsigned int PenBlocks::setPenColorTo(VirtualMachine *vm) unsigned int PenBlocks::setPenSaturationTo(VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) setOrChangeColorParam(ColorParam::SATURATION, vm->getInput(0, 1)->toDouble(), model->penState(), false); @@ -445,7 +450,7 @@ unsigned int PenBlocks::setPenSaturationTo(VirtualMachine *vm) unsigned int PenBlocks::setPenBrightnessTo(VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) setOrChangeColorParam(ColorParam::BRIGHTNESS, vm->getInput(0, 1)->toDouble(), model->penState(), false); @@ -455,7 +460,7 @@ unsigned int PenBlocks::setPenBrightnessTo(VirtualMachine *vm) unsigned int PenBlocks::setPenTransparencyTo(VirtualMachine *vm) { - SpriteModel *model = getSpriteModel(vm); + TargetModel *model = getTargetModel(vm); if (model) setOrChangeColorParam(ColorParam::TRANSPARENCY, vm->getInput(0, 1)->toDouble(), model->penState(), false); @@ -463,16 +468,20 @@ unsigned int PenBlocks::setPenTransparencyTo(VirtualMachine *vm) return 1; } -SpriteModel *PenBlocks::getSpriteModel(libscratchcpp::VirtualMachine *vm) +TargetModel *PenBlocks::getTargetModel(libscratchcpp::VirtualMachine *vm) { Target *target = vm->target(); - if (!target || target->isStage()) + if (!target) return nullptr; - Sprite *sprite = static_cast(target); - SpriteModel *model = static_cast(sprite->getInterface()); - return model; + if (target->isStage()) { + Stage *stage = static_cast(target); + return static_cast(stage->getInterface()); + } else { + Sprite *sprite = static_cast(target); + return static_cast(sprite->getInterface()); + } } void PenBlocks::setOrChangeColorParam(ColorParam param, double value, PenState &penState, bool change) diff --git a/src/blocks/penblocks.h b/src/blocks/penblocks.h index 0feb01a..61e147b 100644 --- a/src/blocks/penblocks.h +++ b/src/blocks/penblocks.h @@ -8,7 +8,7 @@ namespace scratchcpprender { -class SpriteModel; +class TargetModel; class PenState; class PenBlocks : public libscratchcpp::IExtension @@ -26,6 +26,7 @@ class PenBlocks : public libscratchcpp::IExtension std::string name() const override; std::string description() const override; + libscratchcpp::Rgb color() const override; void registerBlocks(libscratchcpp::IEngine *engine) override; @@ -77,7 +78,7 @@ class PenBlocks : public libscratchcpp::IExtension TRANSPARENCY }; - static SpriteModel *getSpriteModel(libscratchcpp::VirtualMachine *vm); + static TargetModel *getTargetModel(libscratchcpp::VirtualMachine *vm); static void setOrChangeColorParam(ColorParam param, double value, PenState &penState, bool change); static void setPenShade(int shade, PenState &penState); static void legacyUpdatePenColor(PenState &penState); diff --git a/src/global_functions.cpp b/src/global_functions.cpp index de18977..9338197 100644 --- a/src/global_functions.cpp +++ b/src/global_functions.cpp @@ -10,6 +10,10 @@ void scratchcpprender::init() QSurfaceFormat format = QSurfaceFormat::defaultFormat(); format.setSwapInterval(0); +#ifdef Q_OS_MACOS + format.setProfile(QSurfaceFormat::CoreProfile); + format.setVersion(3, 2); +#endif QSurfaceFormat::setDefaultFormat(format); } diff --git a/src/irenderedtarget.h b/src/irenderedtarget.h index 14cb457..662e95b 100644 --- a/src/irenderedtarget.h +++ b/src/irenderedtarget.h @@ -92,8 +92,8 @@ class IRenderedTarget : public QNanoQuickItem virtual QRgb colorAtScratchPoint(double x, double y) const = 0; virtual bool touchingClones(const std::vector &clones) const = 0; - virtual bool touchingColor(const libscratchcpp::Value &color) const = 0; - virtual bool touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const = 0; + virtual bool touchingColor(libscratchcpp::Rgb color) const = 0; + virtual bool touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const = 0; }; } // namespace scratchcpprender diff --git a/src/penlayer.cpp b/src/penlayer.cpp index 0c95759..7333517 100644 --- a/src/penlayer.cpp +++ b/src/penlayer.cpp @@ -273,6 +273,8 @@ void PenLayer::stamp(IRenderedTarget *target) modelMatrix.scale(scaleX / textureScale, aspectRatio * scaleY / textureScale); m_glF->glDisable(GL_SCISSOR_TEST); m_glF->glDisable(GL_DEPTH_TEST); + m_glF->glEnable(GL_BLEND); + m_glF->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // Create a FBO for the current texture m_glF->glBindFramebuffer(GL_FRAMEBUFFER, m_stampFbo); diff --git a/src/projectloader.cpp b/src/projectloader.cpp index d18f450..3126a5f 100644 --- a/src/projectloader.cpp +++ b/src/projectloader.cpp @@ -45,10 +45,7 @@ ProjectLoader::ProjectLoader(QObject *parent) : ProjectLoader::~ProjectLoader() { - m_stopLoading = true; - - if (m_loadThread.isRunning()) - m_loadThread.waitForFinished(); + stopLoading(); for (SpriteModel *sprite : m_sprites) sprite->deleteLater(); @@ -61,8 +58,11 @@ const QString &ProjectLoader::fileName() const void ProjectLoader::setFileName(const QString &newFileName) { - if (m_loadThread.isRunning()) + if (m_loadThread.isRunning()) { + stopLoading(); m_loadThread.waitForFinished(); + QCoreApplication::processEvents(); + } if (newFileName.isEmpty()) return; @@ -72,7 +72,7 @@ void ProjectLoader::setFileName(const QString &newFileName) clear(); m_project.setFileName(m_fileName.toStdString()); - m_loadStatus = false; + m_loadStatus = LoadStatus::Loading; // TODO: Do not set these to 0 after libscratchcpp starts doing it itself m_downloadedAssets = 0; @@ -87,14 +87,23 @@ void ProjectLoader::setFileName(const QString &newFileName) m_loadThread = QtConcurrent::run(&callLoad, this); } -bool ProjectLoader::loadStatus() const +ProjectLoader::LoadStatus ProjectLoader::loadStatus() const { if (m_loadThread.isRunning()) - return false; + return LoadStatus::Loading; return m_loadStatus; } +void ProjectLoader::stopLoading() +{ + if (m_loadThread.isRunning()) { + m_project.stopLoading(); + m_stopLoading = true; + m_loadThread.waitForFinished(); + } +} + bool ProjectLoader::running() const { return m_running; @@ -122,15 +131,17 @@ void ProjectLoader::setEngine(libscratchcpp::IEngine *engine) StageModel *ProjectLoader::stage() { if (m_loadThread.isRunning()) - m_loadThread.waitForFinished(); + return nullptr; return &m_stage; } QQmlListProperty ProjectLoader::sprites() { - if (m_loadThread.isRunning()) - m_loadThread.waitForFinished(); + if (m_loadThread.isRunning()) { + m_emptySpriteList.clear(); + return QQmlListProperty(this, &m_emptySpriteList); + } return QQmlListProperty(this, &m_sprites); } @@ -168,9 +179,9 @@ const QStringList &ProjectLoader::unsupportedBlocks() const void ProjectLoader::start() { if (m_loadThread.isRunning()) - m_loadThread.waitForFinished(); + return; - if (m_loadStatus) { + if (m_loadStatus == LoadStatus::Loaded) { Q_ASSERT(m_engine); m_engine->start(); } @@ -179,9 +190,9 @@ void ProjectLoader::start() void ProjectLoader::stop() { if (m_loadThread.isRunning()) - m_loadThread.waitForFinished(); + return; - if (m_loadStatus) { + if (m_loadStatus == LoadStatus::Loaded) { Q_ASSERT(m_engine); m_engine->stop(); } @@ -276,12 +287,16 @@ void ProjectLoader::clear() void ProjectLoader::load() { m_unpositionedMonitors.clear(); - m_loadStatus = m_project.load(); + m_loadStatus = m_project.load() ? LoadStatus::Loaded : LoadStatus::Failed; m_engineMutex.lock(); m_engine = m_project.engine().get(); if (!m_engine || m_stopLoading) { m_engineMutex.unlock(); + + if (m_stopLoading) + m_loadStatus = LoadStatus::Aborted; + emit fileNameChanged(); emit loadStatusChanged(); emit loadingFinished(); @@ -330,6 +345,7 @@ void ProjectLoader::load() if (m_stopLoading) { m_engineMutex.unlock(); + m_loadStatus = LoadStatus::Aborted; emit fileNameChanged(); emit loadStatusChanged(); emit loadingFinished(); @@ -364,7 +380,7 @@ void ProjectLoader::initTimer() void ProjectLoader::redraw() { if (m_loadThread.isRunning()) - m_loadThread.waitForFinished(); + return; auto stage = m_stage.renderedTarget(); diff --git a/src/projectloader.h b/src/projectloader.h index 6065277..8ada11c 100644 --- a/src/projectloader.h +++ b/src/projectloader.h @@ -23,7 +23,7 @@ class ProjectLoader : public QObject Q_OBJECT QML_ELEMENT Q_PROPERTY(QString fileName READ fileName WRITE setFileName NOTIFY fileNameChanged) - Q_PROPERTY(bool loadStatus READ loadStatus NOTIFY loadStatusChanged) + Q_PROPERTY(LoadStatus loadStatus READ loadStatus NOTIFY loadStatusChanged) Q_PROPERTY(bool running READ running NOTIFY runningChanged) Q_PROPERTY(int renderFps READ renderFps NOTIFY renderFpsChanged FINAL) Q_PROPERTY(libscratchcpp::IEngine *engine READ engine NOTIFY engineChanged) @@ -43,13 +43,25 @@ class ProjectLoader : public QObject Q_PROPERTY(unsigned int assetCount READ assetCount NOTIFY assetCountChanged) public: + enum class LoadStatus + { + Idle, + Loading, + Loaded, + Failed, + Aborted + }; + + Q_ENUM(LoadStatus) + explicit ProjectLoader(QObject *parent = nullptr); ~ProjectLoader(); const QString &fileName() const; void setFileName(const QString &newFileName); - bool loadStatus() const; + LoadStatus loadStatus() const; + Q_INVOKABLE void stopLoading(); bool running() const; @@ -155,10 +167,11 @@ class ProjectLoader : public QObject libscratchcpp::IEngine *m_engine = nullptr; libscratchcpp::IEngine *m_oldEngine = nullptr; QMutex m_engineMutex; - bool m_loadStatus = false; + LoadStatus m_loadStatus = LoadStatus::Idle; StageModel m_stage; QList m_sprites; QList m_clones; + QList m_emptySpriteList; QList m_monitors; std::vector m_unpositionedMonitors; QStringList m_unsupportedBlocks; diff --git a/src/renderedtarget.cpp b/src/renderedtarget.cpp index 101e236..b2130f6 100644 --- a/src/renderedtarget.cpp +++ b/src/renderedtarget.cpp @@ -668,12 +668,12 @@ bool RenderedTarget::touchingClones(const std::vector & return false; } -bool RenderedTarget::touchingColor(const Value &color) const +bool RenderedTarget::touchingColor(Rgb color) const { - return touchingColor(color, false, Value()); + return touchingColor(color, false, 0); } -bool RenderedTarget::touchingColor(const Value &color, const Value &mask) const +bool RenderedTarget::touchingColor(Rgb color, Rgb mask) const { return touchingColor(color, true, mask); } @@ -873,13 +873,13 @@ CpuTextureManager *RenderedTarget::textureManager() const return m_textureManager.get(); } -bool RenderedTarget::touchingColor(const libscratchcpp::Value &color, bool hasMask, const libscratchcpp::Value &mask) const +bool RenderedTarget::touchingColor(Rgb color, bool hasMask, Rgb mask) const { // https://github.com/scratchfoundation/scratch-render/blob/0a04c2fb165f5c20406ec34ab2ea5682ae45d6e0/src/RenderWebGL.js#L775-L841 if (!m_engine) return false; - QRgb rgb = convertColor(color); + QRgb rgb = qRgb(qRed(color), qGreen(color), qBlue(color)); // ignore alpha QRgb mask3b; double ghostValue = 0; @@ -891,7 +891,7 @@ bool RenderedTarget::touchingColor(const libscratchcpp::Value &color, bool hasMa m_graphicEffectMask &= ~ShaderManager::Effect::Ghost; } - mask3b = convertColor(mask); + mask3b = qRgb(qRed(mask), qGreen(mask), qBlue(mask)); // ignore alpha } std::vector targets; @@ -1065,33 +1065,6 @@ void RenderedTarget::clampRect(Rect &rect, double left, double right, double bot rect.setTop(std::max(rect.top(), bottom)); } -QRgb RenderedTarget::convertColor(const libscratchcpp::Value &color) -{ - // TODO: Remove this after libscratchcpp starts converting colors (it still needs to be converted to RGB here) - std::string stringValue; - - if (color.isString()) - stringValue = color.toString(); - - if (!stringValue.empty() && stringValue[0] == '#') { - bool valid = false; - QColor color; - - if (stringValue.size() <= 7) // #RRGGBB - { - color = QColor::fromString(stringValue); - valid = color.isValid(); - } - - if (!valid) - color = Qt::black; - - return color.rgb(); - - } else - return QColor::fromRgba(static_cast(color.toLong())).rgb(); -} - bool RenderedTarget::colorMatches(QRgb a, QRgb b) { // https://github.com/scratchfoundation/scratch-render/blob/0a04c2fb165f5c20406ec34ab2ea5682ae45d6e0/src/RenderWebGL.js#L77-L81 diff --git a/src/renderedtarget.h b/src/renderedtarget.h index 9550762..5a94bb2 100644 --- a/src/renderedtarget.h +++ b/src/renderedtarget.h @@ -101,8 +101,8 @@ class RenderedTarget : public IRenderedTarget QRgb colorAtScratchPoint(double x, double y) const override; bool touchingClones(const std::vector &) const override; - bool touchingColor(const libscratchcpp::Value &color) const override; - bool touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const override; + bool touchingColor(libscratchcpp::Rgb color) const override; + bool touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const override; signals: void engineChanged(); @@ -132,14 +132,13 @@ class RenderedTarget : public IRenderedTarget QPointF mapFromStageWithOriginPoint(const QPointF &scenePoint) const; QPointF mapFromScratchToLocal(const QPointF &point) const; CpuTextureManager *textureManager() const; - bool touchingColor(const libscratchcpp::Value &color, bool hasMask, const libscratchcpp::Value &mask) const; + bool touchingColor(libscratchcpp::Rgb color, bool hasMask, libscratchcpp::Rgb mask) const; QRectF touchingBounds() const; QRectF candidatesBounds(const QRectF &targetRect, const std::vector &candidates, std::vector &dst) const; QRectF candidatesBounds(const QRectF &targetRect, const std::vector &candidates, std::vector &dst) const; static QRectF candidateIntersection(const QRectF &targetRect, IRenderedTarget *target); static QRectF rectIntersection(const QRectF &targetRect, const libscratchcpp::Rect &candidateRect); static void clampRect(libscratchcpp::Rect &rect, double left, double right, double bottom, double top); - static QRgb convertColor(const libscratchcpp::Value &color); static bool colorMatches(QRgb a, QRgb b); static bool maskMatches(QRgb a, QRgb b); QRgb sampleColor3b(double x, double y, const std::vector &targets) const; diff --git a/src/shadermanager.cpp b/src/shadermanager.cpp index 7d96640..93625ba 100644 --- a/src/shadermanager.cpp +++ b/src/shadermanager.cpp @@ -24,12 +24,10 @@ static float wrapClamp(float n, float min, float max) static const QString VERTEX_SHADER_SRC = ":/qt/qml/ScratchCPP/Render/shaders/sprite.vert"; static const QString FRAGMENT_SHADER_SRC = ":/qt/qml/ScratchCPP/Render/shaders/sprite.frag"; -#if defined(Q_OS_WASM) -static const QString SHADER_PREFIX = ""; // compiles, but doesn't work? -#elif defined(Q_OS_ANDROID) -static const QString SHADER_PREFIX = "#version 300 es\n"; +#ifdef Q_OS_MACOS +static const QString SHADER_PREFIX = "#version 410\n"; #else -static const QString SHADER_PREFIX = "#version 140\n"; +static const QString SHADER_PREFIX = "#version 300 es\n"; #endif static const char *TEXTURE_UNIT_UNIFORM = "u_skin"; diff --git a/src/shaders/sprite.frag b/src/shaders/sprite.frag index 3209cf8..6399587 100644 --- a/src/shaders/sprite.frag +++ b/src/shaders/sprite.frag @@ -35,7 +35,8 @@ uniform vec2 u_skinSize; uniform float u_mosaic; #endif // ENABLE_mosaic -varying vec2 v_texCoord; +in vec2 v_texCoord; +out vec4 fragColor; uniform sampler2D u_skin; // Add this to divisors to prevent division by 0, which results in NaNs propagating through calculations. @@ -147,16 +148,16 @@ void main() } #endif // ENABLE_fisheye - gl_FragColor = texture2D(u_skin, texcoord0); + fragColor = texture(u_skin, texcoord0); #if defined(ENABLE_color) || defined(ENABLE_brightness) // Divide premultiplied alpha values for proper color processing // Add epsilon to avoid dividing by 0 for fully transparent pixels - gl_FragColor.rgb = clamp(gl_FragColor.rgb / (gl_FragColor.a + epsilon), 0.0, 1.0); + fragColor.rgb = clamp(fragColor.rgb / (fragColor.a + epsilon), 0.0, 1.0); #ifdef ENABLE_color { - vec3 hsv = convertRGB2HSV(gl_FragColor.rgb); + vec3 hsv = convertRGB2HSV(fragColor.rgb); // Force grayscale values to be slightly saturated const float minLightness = 0.11 / 2.0; @@ -167,20 +168,20 @@ void main() hsv.x = mod(hsv.x + u_color, 1.0); if (hsv.x < 0.0) hsv.x += 1.0; - gl_FragColor.rgb = convertHSV2RGB(hsv); + fragColor.rgb = convertHSV2RGB(hsv); } #endif // ENABLE_color #ifdef ENABLE_brightness - gl_FragColor.rgb = clamp(gl_FragColor.rgb + vec3(u_brightness), vec3(0), vec3(1)); + fragColor.rgb = clamp(fragColor.rgb + vec3(u_brightness), vec3(0), vec3(1)); #endif // ENABLE_brightness // Re-multiply color values - gl_FragColor.rgb *= gl_FragColor.a + epsilon; + fragColor.rgb *= fragColor.a + epsilon; #endif // defined(ENABLE_color) || defined(ENABLE_brightness) #ifdef ENABLE_ghost - gl_FragColor *= u_ghost; + fragColor *= u_ghost; #endif // ENABLE_ghost } diff --git a/src/shaders/sprite.vert b/src/shaders/sprite.vert index ef79415..b52cc8f 100644 --- a/src/shaders/sprite.vert +++ b/src/shaders/sprite.vert @@ -1,9 +1,9 @@ uniform mat4 u_projectionMatrix; uniform mat4 u_modelMatrix; -attribute vec2 a_position; -attribute vec2 a_texCoord; +in vec2 a_position; +in vec2 a_texCoord; -varying vec2 v_texCoord; +out vec2 v_texCoord; void main() { gl_Position = u_projectionMatrix * u_modelMatrix * vec4(a_position, 0, 1); diff --git a/src/spritemodel.cpp b/src/spritemodel.cpp index 4383022..569ea96 100644 --- a/src/spritemodel.cpp +++ b/src/spritemodel.cpp @@ -6,15 +6,12 @@ #include #include "spritemodel.h" -#include "renderedtarget.h" -#include "ipenlayer.h" -#include "graphicseffect.h" +#include "penlayer.h" -namespace scratchcpprender -{ +using namespace scratchcpprender; SpriteModel::SpriteModel(QObject *parent) : - QObject(parent) + TargetModel(parent) { } @@ -22,36 +19,8 @@ void SpriteModel::init(libscratchcpp::Sprite *sprite) { m_sprite = sprite; - if (m_sprite) { - libscratchcpp::TextBubble *bubble = m_sprite->bubble(); - - bubble->typeChanged().connect([this](libscratchcpp::TextBubble::Type type) { - if (type == libscratchcpp::TextBubble::Type::Say) { - if (m_bubbleType == TextBubbleShape::Type::Say) - return; - - m_bubbleType = TextBubbleShape::Type::Say; - } else { - if (m_bubbleType == TextBubbleShape::Type::Think) - return; - - m_bubbleType = TextBubbleShape::Type::Think; - } - - emit bubbleTypeChanged(); - }); - - bubble->textChanged().connect([this](const std::string &text) { - QString newText = QString::fromStdString(text); - - if (m_bubbleText != newText) { - m_bubbleText = newText; - emit bubbleTextChanged(); - } - }); - - bubble->layerOrderChanged().connect([this](int) { emit bubbleLayerChanged(); }); - } + if (m_sprite) + setupTextBubble(m_sprite->bubble()); } void SpriteModel::deinitClone() @@ -66,123 +35,109 @@ void SpriteModel::onCloned(libscratchcpp::Sprite *clone) SpriteModel *cloneModel = new SpriteModel(m_cloneRoot); cloneModel->m_cloneRoot = m_cloneRoot; - cloneModel->m_penLayer = m_penLayer; - cloneModel->m_penState = m_penState; + cloneModel->setPenLayer(penLayer()); + cloneModel->penState() = penState(); clone->setInterface(cloneModel); emit cloned(cloneModel); } void SpriteModel::onCostumeChanged(libscratchcpp::Costume *costume) { - if (m_renderedTarget) - m_renderedTarget->updateCostume(costume); + updateCostume(costume); } void SpriteModel::onVisibleChanged(bool visible) { - if (m_renderedTarget) - m_renderedTarget->updateVisibility(visible); + updateVisibility(visible); } void SpriteModel::onXChanged(double x) { - if (m_renderedTarget) - m_renderedTarget->updateX(x); + updateX(x); } void SpriteModel::onYChanged(double y) { - if (m_renderedTarget) - m_renderedTarget->updateY(y); + updateY(y); } void SpriteModel::onMoved(double oldX, double oldY, double newX, double newY) { - if (m_penState.penDown && m_penLayer) { - m_penLayer->drawLine(m_penState.penAttributes, oldX, oldY, newX, newY); - libscratchcpp::IEngine *engine = m_sprite->engine(); - - if (engine) - engine->requestRedraw(); - } + TargetModel::onMoved(oldX, oldY, newX, newY); } void SpriteModel::onSizeChanged(double size) { - if (m_renderedTarget) - m_renderedTarget->updateSize(size); + updateSize(size); } void SpriteModel::onDirectionChanged(double direction) { - if (m_renderedTarget) - m_renderedTarget->updateDirection(direction); + updateDirection(direction); } void SpriteModel::onRotationStyleChanged(libscratchcpp::Sprite::RotationStyle rotationStyle) { - if (m_renderedTarget) - m_renderedTarget->updateRotationStyle(rotationStyle); + updateRotationStyle(rotationStyle); } void SpriteModel::onLayerOrderChanged(int layerOrder) { - if (m_renderedTarget) - m_renderedTarget->updateLayerOrder(layerOrder); + updateLayerOrder(layerOrder); } void SpriteModel::onGraphicsEffectChanged(libscratchcpp::IGraphicsEffect *effect, double value) { - GraphicsEffect *graphicsEffect = dynamic_cast(effect); - - if (graphicsEffect && m_renderedTarget) - m_renderedTarget->setGraphicEffect(graphicsEffect->effect(), value); + setGraphicEffect(effect, value); } void SpriteModel::onGraphicsEffectsCleared() { - if (m_renderedTarget) - m_renderedTarget->clearGraphicEffects(); + clearGraphicEffects(); } int SpriteModel::costumeWidth() const { - return m_renderedTarget->costumeWidth(); + return TargetModel::costumeWidth(); } int SpriteModel::costumeHeight() const { - return m_renderedTarget->costumeHeight(); + return TargetModel::costumeHeight(); } libscratchcpp::Rect SpriteModel::boundingRect() const { - return m_renderedTarget->getBounds(); + libscratchcpp::Rect ret; + getBoundingRect(ret); + return ret; } libscratchcpp::Rect SpriteModel::fastBoundingRect() const { - return m_renderedTarget->getFastBounds(); + libscratchcpp::Rect ret; + getFastBoundingRect(ret); + return ret; } bool SpriteModel::touchingClones(const std::vector &clones) const { - return m_renderedTarget->touchingClones(clones); + return TargetModel::touchingClones(clones); } bool SpriteModel::touchingPoint(double x, double y) const { - return m_renderedTarget->containsScratchPoint(x, y); + return TargetModel::touchingPoint(x, y); } -bool SpriteModel::touchingColor(const libscratchcpp::Value &color) const +bool SpriteModel::touchingColor(libscratchcpp::Rgb color) const { - return m_renderedTarget->touchingColor(color); + return TargetModel::touchingColor(color); } -bool SpriteModel::touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const +bool SpriteModel::touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const { - return m_renderedTarget->touchingColor(color, mask); + return TargetModel::touchingColor(color, mask); } libscratchcpp::Sprite *SpriteModel::sprite() const @@ -190,60 +145,9 @@ libscratchcpp::Sprite *SpriteModel::sprite() const return m_sprite; } -IRenderedTarget *SpriteModel::renderedTarget() const -{ - return m_renderedTarget; -} - -void SpriteModel::setRenderedTarget(IRenderedTarget *newRenderedTarget) -{ - if (m_renderedTarget == newRenderedTarget) - return; - - m_renderedTarget = newRenderedTarget; - emit renderedTargetChanged(); -} - -IPenLayer *SpriteModel::penLayer() const -{ - return m_penLayer; -} - -void SpriteModel::setPenLayer(IPenLayer *newPenLayer) -{ - if (m_penLayer == newPenLayer) - return; - - m_penLayer = newPenLayer; - emit penLayerChanged(); -} - -PenState &SpriteModel::penState() -{ - return m_penState; -} - -PenAttributes &SpriteModel::penAttributes() -{ - return m_penState.penAttributes; -} - -bool SpriteModel::penDown() const -{ - return m_penState.penDown; -} - -void SpriteModel::setPenDown(bool newPenDown) +int SpriteModel::bubbleLayer() const { - m_penState.penDown = newPenDown; - - if (m_penState.penDown && m_penLayer && m_sprite) { - m_penLayer->drawPoint(m_penState.penAttributes, m_sprite->x(), m_sprite->y()); - libscratchcpp::IEngine *engine = m_sprite->engine(); - - if (engine) - engine->requestRedraw(); - } + return m_sprite ? m_sprite->bubble()->layerOrder() : 0; } SpriteModel *SpriteModel::cloneRoot() const @@ -254,19 +158,26 @@ SpriteModel *SpriteModel::cloneRoot() const return m_cloneRoot; } -const TextBubbleShape::Type &SpriteModel::bubbleType() const +void SpriteModel::loadCostume() { - return m_bubbleType; + if (m_sprite) + updateCostume(m_sprite->currentCostume().get()); } -const QString &SpriteModel::bubbleText() const +void SpriteModel::drawPenPoint(IPenLayer *penLayer, const PenAttributes &penAttributes) { - return m_bubbleText; + penLayer->drawPoint(penAttributes, m_sprite->x(), m_sprite->y()); + libscratchcpp::IEngine *engine = m_sprite->engine(); + + if (engine) + engine->requestRedraw(); } -int SpriteModel::bubbleLayer() const +void SpriteModel::drawPenLine(IPenLayer *penLayer, const PenAttributes &penAttributes, double x0, double y0, double x1, double y1) { - return m_sprite ? m_sprite->bubble()->layerOrder() : 0; -} + penLayer->drawLine(penAttributes, x0, y0, x1, y1); + libscratchcpp::IEngine *engine = m_sprite->engine(); -} // namespace scratchcpprender + if (engine) + engine->requestRedraw(); +} diff --git a/src/spritemodel.h b/src/spritemodel.h index e54c47c..6428396 100644 --- a/src/spritemodel.h +++ b/src/spritemodel.h @@ -2,33 +2,19 @@ #pragma once -#include -#include #include -#include "penstate.h" -#include "textbubbleshape.h" - -Q_MOC_INCLUDE("renderedtarget.h"); -Q_MOC_INCLUDE("ipenlayer.h"); +#include "targetmodel.h" namespace scratchcpprender { -class IRenderedTarget; -class IPenLayer; - class SpriteModel - : public QObject + : public TargetModel , public libscratchcpp::ISpriteHandler { Q_OBJECT QML_ELEMENT - Q_PROPERTY(IRenderedTarget *renderedTarget READ renderedTarget WRITE setRenderedTarget NOTIFY renderedTargetChanged) - Q_PROPERTY(IPenLayer *penLayer READ penLayer WRITE setPenLayer NOTIFY penLayerChanged) - Q_PROPERTY(TextBubbleShape::Type bubbleType READ bubbleType NOTIFY bubbleTypeChanged) - Q_PROPERTY(QString bubbleText READ bubbleText NOTIFY bubbleTextChanged) - Q_PROPERTY(int bubbleLayer READ bubbleLayer NOTIFY bubbleLayerChanged) public: SpriteModel(QObject *parent = nullptr); @@ -60,48 +46,28 @@ class SpriteModel bool touchingClones(const std::vector &clones) const override; bool touchingPoint(double x, double y) const override; - bool touchingColor(const libscratchcpp::Value &color) const override; - bool touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const override; + bool touchingColor(libscratchcpp::Rgb color) const override; + bool touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const override; libscratchcpp::Sprite *sprite() const; - IRenderedTarget *renderedTarget() const; - void setRenderedTarget(IRenderedTarget *newRenderedTarget); - - IPenLayer *penLayer() const; - void setPenLayer(IPenLayer *newPenLayer); - - PenState &penState(); - PenAttributes &penAttributes(); - - bool penDown() const; - void setPenDown(bool newPenDown); + int bubbleLayer() const override; SpriteModel *cloneRoot() const; - const TextBubbleShape::Type &bubbleType() const; - - const QString &bubbleText() const; - - int bubbleLayer() const; + Q_INVOKABLE void loadCostume() override; signals: - void renderedTargetChanged(); - void penLayerChanged(); - void bubbleTypeChanged(); - void bubbleTextChanged(); void cloned(SpriteModel *cloneModel); void cloneDeleted(SpriteModel *clone); - void bubbleLayerChanged(); + + protected: + void drawPenPoint(IPenLayer *penLayer, const PenAttributes &penAttributes) override; + void drawPenLine(IPenLayer *penLayer, const PenAttributes &penAttributes, double x0, double y0, double x1, double y1) override; private: libscratchcpp::Sprite *m_sprite = nullptr; - IRenderedTarget *m_renderedTarget = nullptr; - IPenLayer *m_penLayer = nullptr; - PenState m_penState; SpriteModel *m_cloneRoot = nullptr; - TextBubbleShape::Type m_bubbleType = TextBubbleShape::Type::Say; - QString m_bubbleText; }; } // namespace scratchcpprender diff --git a/src/stagemodel.cpp b/src/stagemodel.cpp index 26c7dff..4aa3f5b 100644 --- a/src/stagemodel.cpp +++ b/src/stagemodel.cpp @@ -1,16 +1,16 @@ // SPDX-License-Identifier: LGPL-3.0-or-later #include +#include #include #include "stagemodel.h" -#include "renderedtarget.h" -#include "graphicseffect.h" +#include "penlayer.h" using namespace scratchcpprender; StageModel::StageModel(QObject *parent) : - QObject(parent) + TargetModel(parent) { } @@ -18,42 +18,13 @@ void StageModel::init(libscratchcpp::Stage *stage) { m_stage = stage; - if (m_stage) { - libscratchcpp::TextBubble *bubble = m_stage->bubble(); - - bubble->typeChanged().connect([this](libscratchcpp::TextBubble::Type type) { - if (type == libscratchcpp::TextBubble::Type::Say) { - if (m_bubbleType == TextBubbleShape::Type::Say) - return; - - m_bubbleType = TextBubbleShape::Type::Say; - } else { - if (m_bubbleType == TextBubbleShape::Type::Think) - return; - - m_bubbleType = TextBubbleShape::Type::Think; - } - - emit bubbleTypeChanged(); - }); - - bubble->textChanged().connect([this](const std::string &text) { - QString newText = QString::fromStdString(text); - - if (m_bubbleText != newText) { - m_bubbleText = newText; - emit bubbleTextChanged(); - } - }); - - bubble->layerOrderChanged().connect([this](int) { emit bubbleLayerChanged(); }); - } + if (m_stage) + setupTextBubble(m_stage->bubble()); } void StageModel::onCostumeChanged(libscratchcpp::Costume *costume) { - if (m_renderedTarget) - m_renderedTarget->updateCostume(costume); + updateCostume(costume); } void StageModel::onTempoChanged(int tempo) @@ -70,62 +41,56 @@ void StageModel::onVideoTransparencyChanged(int videoTransparency) void StageModel::onGraphicsEffectChanged(libscratchcpp::IGraphicsEffect *effect, double value) { - GraphicsEffect *graphicsEffect = dynamic_cast(effect); - - if (graphicsEffect && m_renderedTarget) - m_renderedTarget->setGraphicEffect(graphicsEffect->effect(), value); + setGraphicEffect(effect, value); } void StageModel::onGraphicsEffectsCleared() { - if (m_renderedTarget) - m_renderedTarget->clearGraphicEffects(); + clearGraphicEffects(); } int StageModel::costumeWidth() const { - return m_renderedTarget->costumeWidth(); + return TargetModel::costumeWidth(); } int StageModel::costumeHeight() const { - return m_renderedTarget->costumeHeight(); + return TargetModel::costumeHeight(); } libscratchcpp::Rect StageModel::boundingRect() const { - return libscratchcpp::Rect(); + libscratchcpp::Rect ret; + getBoundingRect(ret); + return ret; } libscratchcpp::Rect StageModel::fastBoundingRect() const { - return libscratchcpp::Rect(); + libscratchcpp::Rect ret; + getFastBoundingRect(ret); + return ret; } bool StageModel::touchingClones(const std::vector &clones) const { - return m_renderedTarget->touchingClones(clones); + return TargetModel::touchingClones(clones); } bool StageModel::touchingPoint(double x, double y) const { - return m_renderedTarget->containsScratchPoint(x, y); -} - -bool StageModel::touchingColor(const libscratchcpp::Value &color) const -{ - return m_renderedTarget->touchingColor(color); + return TargetModel::touchingPoint(x, y); } -bool StageModel::touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const +bool StageModel::touchingColor(libscratchcpp::Rgb color) const { - return m_renderedTarget->touchingColor(color, mask); + return TargetModel::touchingColor(color); } -void StageModel::loadCostume() +bool StageModel::touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const { - if (m_renderedTarget && m_stage) - m_renderedTarget->updateCostume(m_stage->currentCostume().get()); + return TargetModel::touchingColor(color, mask); } libscratchcpp::Stage *StageModel::stage() const @@ -133,32 +98,22 @@ libscratchcpp::Stage *StageModel::stage() const return m_stage; } -IRenderedTarget *StageModel::renderedTarget() const -{ - return m_renderedTarget; -} - -void StageModel::setRenderedTarget(IRenderedTarget *newRenderedTarget) +int StageModel::bubbleLayer() const { - if (m_renderedTarget == newRenderedTarget) - return; - - m_renderedTarget = newRenderedTarget; - - emit renderedTargetChanged(); + return m_stage ? m_stage->bubble()->layerOrder() : 0; } -const TextBubbleShape::Type &StageModel::bubbleType() const +void StageModel::loadCostume() { - return m_bubbleType; + if (m_stage) + updateCostume(m_stage->currentCostume().get()); } -const QString &StageModel::bubbleText() const +void StageModel::drawPenPoint(IPenLayer *penLayer, const PenAttributes &penAttributes) { - return m_bubbleText; -} + penLayer->drawLine(penAttributes, 0, 0, 0, 0); + libscratchcpp::IEngine *engine = m_stage->engine(); -int StageModel::bubbleLayer() const -{ - return m_stage ? m_stage->bubble()->layerOrder() : 0; + if (engine) + engine->requestRedraw(); } diff --git a/src/stagemodel.h b/src/stagemodel.h index 269e11c..e7512de 100644 --- a/src/stagemodel.h +++ b/src/stagemodel.h @@ -2,27 +2,18 @@ #pragma once -#include #include -#include "textbubbleshape.h" - -Q_MOC_INCLUDE("renderedtarget.h"); +#include "targetmodel.h" namespace scratchcpprender { -class IRenderedTarget; - class StageModel - : public QObject + : public TargetModel , public libscratchcpp::IStageHandler { Q_OBJECT - Q_PROPERTY(IRenderedTarget *renderedTarget READ renderedTarget WRITE setRenderedTarget NOTIFY renderedTargetChanged) - Q_PROPERTY(TextBubbleShape::Type bubbleType READ bubbleType NOTIFY bubbleTypeChanged) - Q_PROPERTY(QString bubbleText READ bubbleText NOTIFY bubbleTextChanged) - Q_PROPERTY(int bubbleLayer READ bubbleLayer NOTIFY bubbleLayerChanged) public: explicit StageModel(QObject *parent = nullptr); @@ -46,33 +37,20 @@ class StageModel bool touchingClones(const std::vector &clones) const override; bool touchingPoint(double x, double y) const override; - bool touchingColor(const libscratchcpp::Value &color) const override; - bool touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const override; - - Q_INVOKABLE void loadCostume(); + bool touchingColor(libscratchcpp::Rgb color) const override; + bool touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const override; libscratchcpp::Stage *stage() const; - IRenderedTarget *renderedTarget() const; - void setRenderedTarget(IRenderedTarget *newRenderedTarget); - - const TextBubbleShape::Type &bubbleType() const; - - const QString &bubbleText() const; + int bubbleLayer() const override; - int bubbleLayer() const; + Q_INVOKABLE void loadCostume() override; - signals: - void renderedTargetChanged(); - void bubbleTypeChanged(); - void bubbleTextChanged(); - void bubbleLayerChanged(); + protected: + void drawPenPoint(IPenLayer *penLayer, const PenAttributes &penAttributes) override; private: libscratchcpp::Stage *m_stage = nullptr; - IRenderedTarget *m_renderedTarget = nullptr; - TextBubbleShape::Type m_bubbleType = TextBubbleShape::Type::Say; - QString m_bubbleText; }; } // namespace scratchcpprender diff --git a/src/targetmodel.cpp b/src/targetmodel.cpp new file mode 100644 index 0000000..82e28db --- /dev/null +++ b/src/targetmodel.cpp @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include + +#include "targetmodel.h" +#include "renderedtarget.h" +#include "penlayer.h" +#include "graphicseffect.h" + +using namespace scratchcpprender; + +TargetModel::TargetModel(QObject *parent) : + QObject(parent) +{ +} + +IRenderedTarget *TargetModel::renderedTarget() const +{ + return m_renderedTarget; +} + +void TargetModel::setRenderedTarget(IRenderedTarget *newRenderedTarget) +{ + if (m_renderedTarget == newRenderedTarget) + return; + + m_renderedTarget = newRenderedTarget; + + emit renderedTargetChanged(); +} + +IPenLayer *TargetModel::penLayer() const +{ + return m_penLayer; +} + +void TargetModel::setPenLayer(IPenLayer *newPenLayer) +{ + if (m_penLayer == newPenLayer) + return; + + m_penLayer = newPenLayer; + emit penLayerChanged(); +} + +PenState &TargetModel::penState() +{ + return m_penState; +} + +PenAttributes &TargetModel::penAttributes() +{ + return m_penState.penAttributes; +} + +bool TargetModel::penDown() const +{ + return m_penState.penDown; +} + +void TargetModel::setPenDown(bool newPenDown) +{ + m_penState.penDown = newPenDown; + + if (m_penState.penDown && m_penLayer) + drawPenPoint(m_penLayer, m_penState.penAttributes); +} + +const TextBubbleShape::Type &TargetModel::bubbleType() const +{ + return m_bubbleType; +} + +const QString &TargetModel::bubbleText() const +{ + return m_bubbleText; +} + +void TargetModel::setupTextBubble(libscratchcpp::TextBubble *bubble) +{ + bubble->typeChanged().connect([this](libscratchcpp::TextBubble::Type type) { + if (type == libscratchcpp::TextBubble::Type::Say) { + if (m_bubbleType == TextBubbleShape::Type::Say) + return; + + m_bubbleType = TextBubbleShape::Type::Say; + } else { + if (m_bubbleType == TextBubbleShape::Type::Think) + return; + + m_bubbleType = TextBubbleShape::Type::Think; + } + + emit bubbleTypeChanged(); + }); + + bubble->textChanged().connect([this](const std::string &text) { + QString newText = QString::fromStdString(text); + + if (m_bubbleText != newText) { + m_bubbleText = newText; + emit bubbleTextChanged(); + } + }); + + bubble->layerOrderChanged().connect([this](int) { emit bubbleLayerChanged(); }); +} + +void TargetModel::updateVisibility(bool visible) +{ + if (m_renderedTarget) + m_renderedTarget->updateVisibility(visible); +} + +void TargetModel::updateX(double x) +{ + if (m_renderedTarget) + m_renderedTarget->updateX(x); +} + +void TargetModel::updateY(double y) +{ + if (m_renderedTarget) + m_renderedTarget->updateY(y); +} + +void TargetModel::updateSize(double size) +{ + if (m_renderedTarget) + m_renderedTarget->updateSize(size); +} + +void TargetModel::updateDirection(double direction) +{ + if (m_renderedTarget) + m_renderedTarget->updateDirection(direction); +} + +void TargetModel::updateRotationStyle(libscratchcpp::Sprite::RotationStyle style) +{ + if (m_renderedTarget) + m_renderedTarget->updateRotationStyle(style); +} + +void TargetModel::updateLayerOrder(int layerOrder) +{ + if (m_renderedTarget) + m_renderedTarget->updateLayerOrder(layerOrder); +} + +void TargetModel::updateCostume(libscratchcpp::Costume *costume) +{ + if (m_renderedTarget) + m_renderedTarget->updateCostume(costume); +} + +void TargetModel::onMoved(double oldX, double oldY, double newX, double newY) +{ + if (m_penState.penDown && m_penLayer) + drawPenLine(m_penLayer, m_penState.penAttributes, oldX, oldY, newX, newY); +} + +void TargetModel::setGraphicEffect(libscratchcpp::IGraphicsEffect *effect, double value) +{ + GraphicsEffect *graphicsEffect = dynamic_cast(effect); + + if (graphicsEffect && m_renderedTarget) + m_renderedTarget->setGraphicEffect(graphicsEffect->effect(), value); +} + +void TargetModel::clearGraphicEffects() +{ + if (m_renderedTarget) + m_renderedTarget->clearGraphicEffects(); +} + +int TargetModel::costumeWidth() const +{ + return m_renderedTarget->costumeWidth(); +} + +int TargetModel::costumeHeight() const +{ + return m_renderedTarget->costumeHeight(); +} + +void TargetModel::getBoundingRect(libscratchcpp::Rect &dst) const +{ + dst = m_renderedTarget->getBounds(); +} + +void TargetModel::getFastBoundingRect(libscratchcpp::Rect &dst) const +{ + dst = m_renderedTarget->getFastBounds(); +} + +bool TargetModel::touchingClones(const std::vector &clones) const +{ + return m_renderedTarget->touchingClones(clones); +} + +bool TargetModel::touchingPoint(double x, double y) const +{ + return m_renderedTarget->containsScratchPoint(x, y); +} + +bool TargetModel::touchingColor(libscratchcpp::Rgb color) const +{ + return m_renderedTarget->touchingColor(color); +} + +bool TargetModel::touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const +{ + return m_renderedTarget->touchingColor(color, mask); +} diff --git a/src/targetmodel.h b/src/targetmodel.h new file mode 100644 index 0000000..2be3e1f --- /dev/null +++ b/src/targetmodel.h @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#include "penstate.h" +#include "textbubbleshape.h" + +Q_MOC_INCLUDE("renderedtarget.h"); +Q_MOC_INCLUDE("ipenlayer.h"); + +namespace scratchcpprender +{ + +class IRenderedTarget; +class IPenLayer; + +class TargetModel : public QObject +{ + Q_OBJECT + Q_PROPERTY(IRenderedTarget *renderedTarget READ renderedTarget WRITE setRenderedTarget NOTIFY renderedTargetChanged) + Q_PROPERTY(IPenLayer *penLayer READ penLayer WRITE setPenLayer NOTIFY penLayerChanged) + Q_PROPERTY(TextBubbleShape::Type bubbleType READ bubbleType NOTIFY bubbleTypeChanged) + Q_PROPERTY(QString bubbleText READ bubbleText NOTIFY bubbleTextChanged) + Q_PROPERTY(int bubbleLayer READ bubbleLayer NOTIFY bubbleLayerChanged) + + public: + explicit TargetModel(QObject *parent = nullptr); + + IRenderedTarget *renderedTarget() const; + void setRenderedTarget(IRenderedTarget *newRenderedTarget); + + IPenLayer *penLayer() const; + void setPenLayer(IPenLayer *newPenLayer); + + PenState &penState(); + PenAttributes &penAttributes(); + + bool penDown() const; + void setPenDown(bool newPenDown); + + const TextBubbleShape::Type &bubbleType() const; + + const QString &bubbleText() const; + + virtual int bubbleLayer() const { return 0; } + + Q_INVOKABLE virtual void loadCostume() { } + + signals: + void renderedTargetChanged(); + void penLayerChanged(); + void bubbleTypeChanged(); + void bubbleTextChanged(); + void bubbleLayerChanged(); + + protected: + void setupTextBubble(libscratchcpp::TextBubble *bubble); + + void updateVisibility(bool visible); + void updateX(double x); + void updateY(double y); + void updateSize(double size); + void updateDirection(double direction); + void updateRotationStyle(libscratchcpp::Sprite::RotationStyle style); + void updateLayerOrder(int layerOrder); + void updateCostume(libscratchcpp::Costume *costume); + + void onMoved(double oldX, double oldY, double newX, double newY); + + void setGraphicEffect(libscratchcpp::IGraphicsEffect *effect, double value); + void clearGraphicEffects(); + + int costumeWidth() const; + int costumeHeight() const; + + void getBoundingRect(libscratchcpp::Rect &dst) const; + void getFastBoundingRect(libscratchcpp::Rect &dst) const; + + bool touchingClones(const std::vector &clones) const; + bool touchingPoint(double x, double y) const; + bool touchingColor(libscratchcpp::Rgb color) const; + bool touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const; + + virtual void drawPenPoint(IPenLayer *penLayer, const PenAttributes &penAttributes) { } // stage and sprites can draw points + virtual void drawPenLine(IPenLayer *penLayer, const PenAttributes &penAttributes, double x0, double y0, double x1, double y1) { } // only sprites can draw lines + + private: + IRenderedTarget *m_renderedTarget = nullptr; + IPenLayer *m_penLayer = nullptr; + PenState m_penState; + TextBubbleShape::Type m_bubbleType = TextBubbleShape::Type::Say; + QString m_bubbleText; +}; + +} // namespace scratchcpprender diff --git a/test/blocks/pen_blocks_test.cpp b/test/blocks/pen_blocks_test.cpp index 8428c82..900ede2 100644 --- a/test/blocks/pen_blocks_test.cpp +++ b/test/blocks/pen_blocks_test.cpp @@ -234,22 +234,34 @@ TEST_F(PenBlocksTest, PenDownImpl) static unsigned int bytecode[] = { vm::OP_START, vm::OP_EXEC, 0, vm::OP_HALT }; static BlockFunc functions[] = { &PenBlocks::penDown }; - SpriteModel model; - Sprite sprite; - sprite.setInterface(&model); - - VirtualMachine vm(&sprite, &m_engineMock, nullptr); - vm.setBytecode(bytecode); - vm.setFunctions(functions); - - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_TRUE(model.penDown()); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_TRUE(model.penDown()); + std::vector> models; + std::vector> targets; + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + for (int i = 0; i < targets.size(); i++) { + auto target = targets[i]; + auto model = models[i]; + + VirtualMachine vm(target.get(), &m_engineMock, nullptr); + vm.setBytecode(bytecode); + vm.setFunctions(functions); + + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_TRUE(model->penDown()); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_TRUE(model->penDown()); + } } TEST_F(PenBlocksTest, PenUp) @@ -275,23 +287,35 @@ TEST_F(PenBlocksTest, PenUpImpl) static unsigned int bytecode[] = { vm::OP_START, vm::OP_EXEC, 0, vm::OP_HALT }; static BlockFunc functions[] = { &PenBlocks::penUp }; - SpriteModel model; - model.setPenDown(true); - Sprite sprite; - sprite.setInterface(&model); + std::vector> models; + std::vector> targets; - VirtualMachine vm(&sprite, &m_engineMock, nullptr); - vm.setBytecode(bytecode); - vm.setFunctions(functions); + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_FALSE(model.penDown()); + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_FALSE(model.penDown()); + for (int i = 0; i < targets.size(); i++) { + auto target = targets[i]; + auto model = models[i]; + model->setPenDown(true); + + VirtualMachine vm(target.get(), &m_engineMock, nullptr); + vm.setBytecode(bytecode); + vm.setFunctions(functions); + + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_FALSE(model->penDown()); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_FALSE(model->penDown()); + } } TEST_F(PenBlocksTest, SetPenColorToColor) @@ -337,54 +361,66 @@ TEST_F(PenBlocksTest, SetPenColorToColorImpl) static BlockFunc functions[] = { &PenBlocks::setPenColorToColor }; static Value constValues[] = { "#AABbCC", "#03F", "#FFGFFF", "#AABBCCDD", "FFFFFF", 1228097602, 255 }; - SpriteModel model; - Sprite sprite; - sprite.setInterface(&model); - - VirtualMachine vm(&sprite, &m_engineMock, nullptr); - vm.setBytecode(bytecode1); - vm.setFunctions(functions); - vm.setConstValues(constValues); - - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(210, 42, 204))); - - vm.reset(); - vm.setBytecode(bytecode2); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(228, 255, 255))); - - vm.reset(); - vm.setBytecode(bytecode3); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(359, 0, 0))); - - vm.reset(); - vm.setBytecode(bytecode4); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(359, 0, 0))); - - vm.reset(); - vm.setBytecode(bytecode5); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(359, 0, 0))); - - vm.reset(); - vm.setBytecode(bytecode6); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(162, 74, 72, 73))); - - vm.reset(); - vm.setBytecode(bytecode7); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(239, 255, 255))); + std::vector> models; + std::vector> targets; + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + for (int i = 0; i < targets.size(); i++) { + auto target = targets[i]; + auto model = models[i]; + + VirtualMachine vm(target.get(), &m_engineMock, nullptr); + vm.setBytecode(bytecode1); + vm.setFunctions(functions); + vm.setConstValues(constValues); + + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(210, 42, 204))); + + vm.reset(); + vm.setBytecode(bytecode2); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(228, 255, 255))); + + vm.reset(); + vm.setBytecode(bytecode3); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(359, 0, 0))); + + vm.reset(); + vm.setBytecode(bytecode4); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(359, 0, 0))); + + vm.reset(); + vm.setBytecode(bytecode5); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(359, 0, 0))); + + vm.reset(); + vm.setBytecode(bytecode6); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(162, 74, 72, 73))); + + vm.reset(); + vm.setBytecode(bytecode7); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(239, 255, 255))); + } } TEST_F(PenBlocksTest, ChangePenColorParamBy) @@ -481,172 +517,184 @@ TEST_F(PenBlocksTest, ChangePenColorParamByImpl) functions[] = { &PenBlocks::changePenColorParamBy, &PenBlocks::changePenColorBy, &PenBlocks::changePenSaturationBy, &PenBlocks::changePenBrightnessBy, &PenBlocks::changePenTransparencyBy }; static Value constValues[] = { "color", "saturation", "brightness", "transparency", "invalid", 53.2, -120.8 }; - SpriteModel model; - model.penState().transparency = 100 * (1 - 150 / 255.0); - Sprite sprite; - sprite.setInterface(&model); - - VirtualMachine vm(&sprite, &m_engineMock, nullptr); - vm.setBytecode(bytecode1); - vm.setFunctions(functions); - vm.setConstValues(constValues); - - // color - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(71, 255, 255, 150))); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(263, 255, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode2); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 255, 255, 150))); - - // saturation - model.penState().saturation = 32.4; - vm.reset(); - vm.setBytecode(bytecode3); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 218, 255, 150))); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 255, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode4); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 255, 150))); - - // brightness - model.penState().brightness = 12.5; - vm.reset(); - vm.setBytecode(bytecode5); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 167, 150))); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode6); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 150))); - - // transparency - model.penState().transparency = 6.28; - vm.reset(); - vm.setBytecode(bytecode7); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 103))); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 0))); - - vm.reset(); - vm.setBytecode(bytecode8); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 255))); - - // invalid parameter - vm.reset(); - vm.setBytecode(bytecode9); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 255))); - - // color (optimized) - model.penState() = PenState(); - model.penState().transparency = 100 * (1 - 150 / 255.0); - vm.reset(); - vm.setBytecode(bytecode10); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(71, 255, 255, 150))); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(263, 255, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode11); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 255, 255, 150))); - - // saturation (optimized) - model.penState().saturation = 32.4; - vm.reset(); - vm.setBytecode(bytecode12); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 218, 255, 150))); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 255, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode13); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 255, 150))); - - // brightness (optimized) - model.penState().brightness = 12.5; - vm.reset(); - vm.setBytecode(bytecode14); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 167, 150))); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode15); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 150))); - - // transparency (optimized) - model.penState().transparency = 6.28; - vm.reset(); - vm.setBytecode(bytecode16); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 103))); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 0))); - - vm.reset(); - vm.setBytecode(bytecode17); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 255))); + std::vector> models; + std::vector> targets; + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + for (int i = 0; i < targets.size(); i++) { + auto target = targets[i]; + auto model = models[i]; + model->penState().transparency = 100 * (1 - 150 / 255.0); + + VirtualMachine vm(target.get(), &m_engineMock, nullptr); + vm.setBytecode(bytecode1); + vm.setFunctions(functions); + vm.setConstValues(constValues); + + // color + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(71, 255, 255, 150))); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(263, 255, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode2); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 255, 255, 150))); + + // saturation + model->penState().saturation = 32.4; + vm.reset(); + vm.setBytecode(bytecode3); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 218, 255, 150))); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 255, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode4); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 255, 150))); + + // brightness + model->penState().brightness = 12.5; + vm.reset(); + vm.setBytecode(bytecode5); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 167, 150))); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode6); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 150))); + + // transparency + model->penState().transparency = 6.28; + vm.reset(); + vm.setBytecode(bytecode7); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 103))); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 0))); + + vm.reset(); + vm.setBytecode(bytecode8); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 255))); + + // invalid parameter + vm.reset(); + vm.setBytecode(bytecode9); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 255))); + + // color (optimized) + model->penState() = PenState(); + model->penState().transparency = 100 * (1 - 150 / 255.0); + vm.reset(); + vm.setBytecode(bytecode10); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(71, 255, 255, 150))); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(263, 255, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode11); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 255, 255, 150))); + + // saturation (optimized) + model->penState().saturation = 32.4; + vm.reset(); + vm.setBytecode(bytecode12); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 218, 255, 150))); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 255, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode13); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 255, 150))); + + // brightness (optimized) + model->penState().brightness = 12.5; + vm.reset(); + vm.setBytecode(bytecode14); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 167, 150))); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode15); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 150))); + + // transparency (optimized) + model->penState().transparency = 6.28; + vm.reset(); + vm.setBytecode(bytecode16); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 103))); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 0))); + + vm.reset(); + vm.setBytecode(bytecode17); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(188, 0, 0, 255))); + } } TEST_F(PenBlocksTest, SetPenColorParamTo) @@ -750,182 +798,194 @@ TEST_F(PenBlocksTest, SetPenColorParamToImpl) static BlockFunc functions[] = { &PenBlocks::setPenColorParamTo, &PenBlocks::setPenColorTo, &PenBlocks::setPenSaturationTo, &PenBlocks::setPenBrightnessTo, &PenBlocks::setPenTransparencyTo }; static Value constValues[] = { "color", "saturation", "brightness", "transparency", "invalid", 53.2, -234.9, 287.1 }; - SpriteModel model; - model.penState().color = 78.6; - model.penState().transparency = 100 * (1 - 150 / 255.0); - Sprite sprite; - sprite.setInterface(&model); - - VirtualMachine vm(&sprite, &m_engineMock, nullptr); - vm.setBytecode(bytecode1); - vm.setFunctions(functions); - vm.setConstValues(constValues); - - // color - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(191, 255, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode2); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(234, 255, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode3); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 150))); - - // saturation - model.penState().saturation = 32.4; - vm.reset(); - vm.setBytecode(bytecode4); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 135, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode5); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 0, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode6); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 150))); - - // brightness - model.penState().brightness = 12.5; - vm.reset(); - vm.setBytecode(bytecode7); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 135, 150))); - - vm.reset(); - vm.setBytecode(bytecode8); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 0, 150))); - - vm.reset(); - vm.setBytecode(bytecode9); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 150))); - - // transparency - model.penState().transparency = 12.5; - vm.reset(); - vm.setBytecode(bytecode10); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 119))); - - vm.reset(); - vm.setBytecode(bytecode11); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 255))); - - vm.reset(); - vm.setBytecode(bytecode12); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 0))); - - // invalid parameter - vm.reset(); - vm.setBytecode(bytecode13); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 0))); - - // color (optimized) - model.penState() = PenState(); - model.penState().color = 78.6; - model.penState().transparency = 100 * (1 - 150 / 255.0); - vm.reset(); - vm.setBytecode(bytecode14); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(191, 255, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode15); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(234, 255, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode16); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 150))); - - // saturation (optimized) - model.penState().saturation = 32.4; - vm.reset(); - vm.setBytecode(bytecode17); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 135, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode18); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 0, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode19); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 150))); - - // brightness (optimized) - model.penState().brightness = 12.5; - vm.reset(); - vm.setBytecode(bytecode20); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 135, 150))); - - vm.reset(); - vm.setBytecode(bytecode21); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 0, 150))); - - vm.reset(); - vm.setBytecode(bytecode22); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 150))); - - // transparency (optimized) - model.penState().transparency = 12.5; - vm.reset(); - vm.setBytecode(bytecode23); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 119))); - - vm.reset(); - vm.setBytecode(bytecode24); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 255))); - - vm.reset(); - vm.setBytecode(bytecode25); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 0))); + std::vector> models; + std::vector> targets; + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + for (int i = 0; i < targets.size(); i++) { + auto target = targets[i]; + auto model = models[i]; + model->penState().color = 78.6; + model->penState().transparency = 100 * (1 - 150 / 255.0); + + VirtualMachine vm(target.get(), &m_engineMock, nullptr); + vm.setBytecode(bytecode1); + vm.setFunctions(functions); + vm.setConstValues(constValues); + + // color + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(191, 255, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode2); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(234, 255, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode3); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 150))); + + // saturation + model->penState().saturation = 32.4; + vm.reset(); + vm.setBytecode(bytecode4); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 135, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode5); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 0, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode6); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 150))); + + // brightness + model->penState().brightness = 12.5; + vm.reset(); + vm.setBytecode(bytecode7); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 135, 150))); + + vm.reset(); + vm.setBytecode(bytecode8); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 0, 150))); + + vm.reset(); + vm.setBytecode(bytecode9); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 150))); + + // transparency + model->penState().transparency = 12.5; + vm.reset(); + vm.setBytecode(bytecode10); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 119))); + + vm.reset(); + vm.setBytecode(bytecode11); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 255))); + + vm.reset(); + vm.setBytecode(bytecode12); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 0))); + + // invalid parameter + vm.reset(); + vm.setBytecode(bytecode13); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 0))); + + // color (optimized) + model->penState() = PenState(); + model->penState().color = 78.6; + model->penState().transparency = 100 * (1 - 150 / 255.0); + vm.reset(); + vm.setBytecode(bytecode14); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(191, 255, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode15); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(234, 255, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode16); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 150))); + + // saturation (optimized) + model->penState().saturation = 32.4; + vm.reset(); + vm.setBytecode(bytecode17); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 135, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode18); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 0, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode19); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 150))); + + // brightness (optimized) + model->penState().brightness = 12.5; + vm.reset(); + vm.setBytecode(bytecode20); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 135, 150))); + + vm.reset(); + vm.setBytecode(bytecode21); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 0, 150))); + + vm.reset(); + vm.setBytecode(bytecode22); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 150))); + + // transparency (optimized) + model->penState().transparency = 12.5; + vm.reset(); + vm.setBytecode(bytecode23); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 119))); + + vm.reset(); + vm.setBytecode(bytecode24); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 255))); + + vm.reset(); + vm.setBytecode(bytecode25); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(313, 255, 255, 0))); + } } TEST_F(PenBlocksTest, ChangePenSizeBy) @@ -966,39 +1026,51 @@ TEST_F(PenBlocksTest, ChangePenSizeByImpl) static BlockFunc functions[] = { &PenBlocks::changePenSizeBy }; static Value constValues[] = { 511.5, -650.08 }; - SpriteModel model; - Sprite sprite; - sprite.setInterface(&model); - - VirtualMachine vm(&sprite, &m_engineMock, nullptr); - vm.setBytecode(bytecode1); - vm.setFunctions(functions); - vm.setConstValues(constValues); - - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().diameter, 512.5); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().diameter, 1024); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().diameter, 1200); - - vm.reset(); - vm.setBytecode(bytecode2); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().diameter, 549.92); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().diameter, 1); + std::vector> models; + std::vector> targets; + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + for (int i = 0; i < targets.size(); i++) { + auto target = targets[i]; + auto model = models[i]; + + VirtualMachine vm(target.get(), &m_engineMock, nullptr); + vm.setBytecode(bytecode1); + vm.setFunctions(functions); + vm.setConstValues(constValues); + + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().diameter, 512.5); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().diameter, 1024); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().diameter, 1200); + + vm.reset(); + vm.setBytecode(bytecode2); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().diameter, 549.92); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().diameter, 1); + } } TEST_F(PenBlocksTest, SetPenSizeTo) @@ -1040,30 +1112,42 @@ TEST_F(PenBlocksTest, SetPenSizeToImpl) static BlockFunc functions[] = { &PenBlocks::setPenSizeTo }; static Value constValues[] = { 511.5, -650.08, 1500 }; - SpriteModel model; - Sprite sprite; - sprite.setInterface(&model); - - VirtualMachine vm(&sprite, &m_engineMock, nullptr); - vm.setBytecode(bytecode1); - vm.setFunctions(functions); - vm.setConstValues(constValues); - - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().diameter, 511.5); - - vm.reset(); - vm.setBytecode(bytecode2); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().diameter, 1); - - vm.reset(); - vm.setBytecode(bytecode3); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().diameter, 1200); + std::vector> models; + std::vector> targets; + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + for (int i = 0; i < targets.size(); i++) { + auto target = targets[i]; + auto model = models[i]; + + VirtualMachine vm(target.get(), &m_engineMock, nullptr); + vm.setBytecode(bytecode1); + vm.setFunctions(functions); + vm.setConstValues(constValues); + + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().diameter, 511.5); + + vm.reset(); + vm.setBytecode(bytecode2); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().diameter, 1); + + vm.reset(); + vm.setBytecode(bytecode3); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().diameter, 1200); + } } TEST_F(PenBlocksTest, ChangePenShadeBy) @@ -1104,40 +1188,52 @@ TEST_F(PenBlocksTest, ChangePenShadeByImpl) static BlockFunc functions[] = { &PenBlocks::changePenShadeBy }; static Value constValues[] = { 134.09, -124.45 }; - SpriteModel model; - model.penState().transparency = 100 * (1 - 150 / 255.0); - Sprite sprite; - sprite.setInterface(&model); - - VirtualMachine vm(&sprite, &m_engineMock, nullptr); - vm.setBytecode(bytecode1); - vm.setFunctions(functions); - vm.setConstValues(constValues); - - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(240, 255, 110, 150))); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(240, 119, 255, 150))); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(240, 247, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode2); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(240, 162, 255, 150))); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(240, 255, 55, 150))); + std::vector> models; + std::vector> targets; + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + for (int i = 0; i < targets.size(); i++) { + auto target = targets[i]; + auto model = models[i]; + model->penState().transparency = 100 * (1 - 150 / 255.0); + + VirtualMachine vm(target.get(), &m_engineMock, nullptr); + vm.setBytecode(bytecode1); + vm.setFunctions(functions); + vm.setConstValues(constValues); + + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(240, 255, 110, 150))); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(240, 119, 255, 150))); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(240, 247, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode2); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(240, 162, 255, 150))); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(240, 255, 55, 150))); + } } TEST_F(PenBlocksTest, SetPenShadeToNumber) @@ -1179,31 +1275,43 @@ TEST_F(PenBlocksTest, SetPenShadeToNumberImpl) static BlockFunc functions[] = { &PenBlocks::setPenShadeToNumber }; static Value constValues[] = { 125.7, -114.09, 489.4 }; - SpriteModel model; - model.penState().transparency = 100 * (1 - 150 / 255.0); - Sprite sprite; - sprite.setInterface(&model); - - VirtualMachine vm(&sprite, &m_engineMock, nullptr); - vm.setBytecode(bytecode1); - vm.setFunctions(functions); - vm.setConstValues(constValues); - - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(240, 148, 253, 150))); - - vm.reset(); - vm.setBytecode(bytecode2); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(240, 102, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode3); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(240, 89, 255, 150))); + std::vector> models; + std::vector> targets; + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + for (int i = 0; i < targets.size(); i++) { + auto target = targets[i]; + auto model = models[i]; + model->penState().transparency = 100 * (1 - 150 / 255.0); + + VirtualMachine vm(target.get(), &m_engineMock, nullptr); + vm.setBytecode(bytecode1); + vm.setFunctions(functions); + vm.setConstValues(constValues); + + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(240, 148, 253, 150))); + + vm.reset(); + vm.setBytecode(bytecode2); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(240, 102, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode3); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(240, 89, 255, 150))); + } } TEST_F(PenBlocksTest, ChangePenHueBy) @@ -1244,40 +1352,52 @@ TEST_F(PenBlocksTest, ChangePenHueByImpl) static BlockFunc functions[] = { &PenBlocks::changePenHueBy }; static Value constValues[] = { 125.7, -114.09 }; - SpriteModel model; - model.penState().transparency = 100 * (1 - 150 / 255.0); - Sprite sprite; - sprite.setInterface(&model); - - VirtualMachine vm(&sprite, &m_engineMock, nullptr); - vm.setBytecode(bytecode1); - vm.setFunctions(functions); - vm.setConstValues(constValues); - - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(106, 255, 255, 150))); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(332, 255, 255, 150))); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(199, 255, 255, 150))); - - vm.reset(); - vm.setBytecode(bytecode2); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(353, 255, 255, 150))); - - vm.reset(); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(148, 255, 255, 150))); + std::vector> models; + std::vector> targets; + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + for (int i = 0; i < targets.size(); i++) { + auto target = targets[i]; + auto model = models[i]; + model->penState().transparency = 100 * (1 - 150 / 255.0); + + VirtualMachine vm(target.get(), &m_engineMock, nullptr); + vm.setBytecode(bytecode1); + vm.setFunctions(functions); + vm.setConstValues(constValues); + + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(106, 255, 255, 150))); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(332, 255, 255, 150))); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(199, 255, 255, 150))); + + vm.reset(); + vm.setBytecode(bytecode2); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(353, 255, 255, 150))); + + vm.reset(); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(148, 255, 255, 150))); + } } TEST_F(PenBlocksTest, SetPenHueToNumber) @@ -1319,29 +1439,41 @@ TEST_F(PenBlocksTest, SetPenHueToNumberImpl) static BlockFunc functions[] = { &PenBlocks::setPenHueToNumber }; static Value constValues[] = { 125.7, -114.09, 489.4 }; - SpriteModel model; - model.penState().transparency = 100 * (1 - 150 / 255.0); - Sprite sprite; - sprite.setInterface(&model); - - VirtualMachine vm(&sprite, &m_engineMock, nullptr); - vm.setBytecode(bytecode1); - vm.setFunctions(functions); - vm.setConstValues(constValues); - - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(226, 255, 255, 255))); - - vm.reset(); - vm.setBytecode(bytecode2); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(154, 255, 255, 255))); - - vm.reset(); - vm.setBytecode(bytecode3); - vm.run(); - ASSERT_EQ(vm.registerCount(), 0); - ASSERT_EQ(model.penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(160, 255, 255, 255))); + std::vector> models; + std::vector> targets; + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + models.push_back(std::make_shared()); + targets.push_back(std::make_shared()); + static_cast(targets.back().get())->setInterface(static_cast(models.back().get())); + + for (int i = 0; i < targets.size(); i++) { + auto target = targets[i]; + auto model = models[i]; + model->penState().transparency = 100 * (1 - 150 / 255.0); + + VirtualMachine vm(target.get(), &m_engineMock, nullptr); + vm.setBytecode(bytecode1); + vm.setFunctions(functions); + vm.setConstValues(constValues); + + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(226, 255, 255, 255))); + + vm.reset(); + vm.setBytecode(bytecode2); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(154, 255, 255, 255))); + + vm.reset(); + vm.setBytecode(bytecode3); + vm.run(); + ASSERT_EQ(vm.registerCount(), 0); + ASSERT_EQ(model->penAttributes().color, QNanoColor::fromQColor(QColor::fromHsv(160, 255, 255, 255))); + } } diff --git a/test/mocks/extensionmock.h b/test/mocks/extensionmock.h index 9c5e9a2..46ae014 100644 --- a/test/mocks/extensionmock.h +++ b/test/mocks/extensionmock.h @@ -13,6 +13,7 @@ class ExtensionMock : public IExtension public: MOCK_METHOD(std::string, name, (), (const, override)); MOCK_METHOD(std::string, description, (), (const, override)); + MOCK_METHOD(Rgb, color, (), (const, override)); MOCK_METHOD(void, registerBlocks, (IEngine * engine), (override)); MOCK_METHOD(void, onInit, (IEngine * engine), (override)); diff --git a/test/mocks/renderedtargetmock.h b/test/mocks/renderedtargetmock.h index bdb610a..04f512c 100644 --- a/test/mocks/renderedtargetmock.h +++ b/test/mocks/renderedtargetmock.h @@ -77,8 +77,8 @@ class RenderedTargetMock : public IRenderedTarget MOCK_METHOD(QRgb, colorAtScratchPoint, (double, double), (const, override)); MOCK_METHOD(bool, touchingClones, (const std::vector &), (const, override)); - MOCK_METHOD(bool, touchingColor, (const libscratchcpp::Value &), (const, override)); - MOCK_METHOD(bool, touchingColor, (const libscratchcpp::Value &, const libscratchcpp::Value &), (const, override)); + MOCK_METHOD(bool, touchingColor, (libscratchcpp::Rgb), (const, override)); + MOCK_METHOD(bool, touchingColor, (libscratchcpp::Rgb, libscratchcpp::Rgb), (const, override)); MOCK_METHOD(QNanoQuickItemPainter *, createItemPainter, (), (const, override)); MOCK_METHOD(void, hoverEnterEvent, (QHoverEvent *), (override)); diff --git a/test/penlayer/penlayer_test.cpp b/test/penlayer/penlayer_test.cpp index 007ef81..0142035 100644 --- a/test/penlayer/penlayer_test.cpp +++ b/test/penlayer/penlayer_test.cpp @@ -30,11 +30,6 @@ class PenLayerTest : public testing::Test m_surface.create(); Q_ASSERT(m_surface.isValid()); m_context.makeCurrent(&m_surface); - - QOpenGLFunctions glF(&m_context); - glF.initializeOpenGLFunctions(); - glF.glEnable(GL_BLEND); - glF.glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } void TearDown() override @@ -367,6 +362,9 @@ TEST_F(PenLayerTest, DrawLine) TEST_F(PenLayerTest, Stamp) { + static const std::chrono::milliseconds timeout(5000); + auto startTime = std::chrono::steady_clock::now(); + PenLayer penLayer; penLayer.setWidth(480); penLayer.setHeight(360); @@ -376,7 +374,9 @@ TEST_F(PenLayerTest, Stamp) ProjectLoader loader; loader.setFileName("stamp_env.sb3"); - loader.start(); // wait until it loads + + while (loader.loadStatus() != ProjectLoader::LoadStatus::Loaded) + ASSERT_LE(std::chrono::steady_clock::now(), startTime + timeout); EXPECT_CALL(engine, stageWidth()).WillRepeatedly(Return(480)); EXPECT_CALL(engine, stageHeight()).WillRepeatedly(Return(360)); diff --git a/test/projectloader/projectloader_test.cpp b/test/projectloader/projectloader_test.cpp index db430a3..a7a7919 100644 --- a/test/projectloader/projectloader_test.cpp +++ b/test/projectloader/projectloader_test.cpp @@ -46,9 +46,9 @@ class ProjectLoaderTest : public testing::Test ASSERT_EQ(monitorsSpy.count(), 1); ASSERT_TRUE(monitorAddedSpy.empty()); ASSERT_EQ(loader->fileName(), fileName); - ASSERT_FALSE(loader->loadStatus()); + ASSERT_EQ(loader->loadStatus(), ProjectLoader::LoadStatus::Loading); - while (!loader->loadStatus()) + while (loader->loadStatus() != ProjectLoader::LoadStatus::Loaded) ASSERT_LE(std::chrono::steady_clock::now(), startTime + timeout); ASSERT_EQ(loader->fileName(), fileName); @@ -84,7 +84,7 @@ TEST_F(ProjectLoaderTest, Load) { ProjectLoader loader; ASSERT_TRUE(loader.fileName().isEmpty()); - ASSERT_FALSE(loader.loadStatus()); + ASSERT_EQ(loader.loadStatus(), ProjectLoader::LoadStatus::Idle); ASSERT_TRUE(loader.stage()); load(&loader, "load_test.sb3"); @@ -111,13 +111,18 @@ TEST_F(ProjectLoaderTest, Load) TEST_F(ProjectLoaderTest, UnsupportedBlocks) { + static const std::chrono::milliseconds timeout(5000); + auto startTime = std::chrono::steady_clock::now(); + ProjectLoader loader; ASSERT_TRUE(loader.fileName().isEmpty()); - ASSERT_FALSE(loader.loadStatus()); + ASSERT_EQ(loader.loadStatus(), ProjectLoader::LoadStatus::Idle); ASSERT_TRUE(loader.stage()); loader.setFileName("unsupported_blocks.sb3"); - loader.start(); // wait until it loads + + while (loader.loadStatus() != ProjectLoader::LoadStatus::Loaded) + ASSERT_LE(std::chrono::steady_clock::now(), startTime + timeout); auto engine = loader.engine(); const auto &blocks = loader.unsupportedBlocks(); diff --git a/test/renderedtarget/renderedtarget_test.cpp b/test/renderedtarget/renderedtarget_test.cpp index 4d67461..4b2ad13 100644 --- a/test/renderedtarget/renderedtarget_test.cpp +++ b/test/renderedtarget/renderedtarget_test.cpp @@ -1152,24 +1152,23 @@ TEST_F(RenderedTargetTest, TouchingColor) EXPECT_CALL(target2, stageModel()).WillRepeatedly(Return(nullptr)); EXPECT_CALL(penLayer, getBounds()).WillRepeatedly(ReturnRef(penBounds)); - static const QRgb color1 = 4286611711; // "purple" - static const QRgb color2 = 596083443; // close to color1 and transparent - static const Value color3 = "#808000"; // "olive" (4286611456) - static const QRgb color3Int = 4286611456; + static const Rgb color1 = 4286611711; // "purple" + static const Rgb color2 = 596083443; // close to color1 and transparent + static const Rgb color3 = 4286611456; // "olive" static const QRgb color4 = 2505545047; // transparent "hippie green" static const QRgb color5 = 4287417025; // color1 + color4 EXPECT_CALL(stageTarget, getFastBounds()).WillOnce(Return(Rect(2, 1, 6, -5))); EXPECT_CALL(target1, getFastBounds()).WillOnce(Return(Rect(2, 1, 6, -5))); EXPECT_CALL(target2, getFastBounds()).WillOnce(Return(Rect(-5, -1, 1, -8))); - EXPECT_CALL(target2, colorAtScratchPoint(1, -3)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(2, -3)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(3, -3)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(1, -2)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(3, -2)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(1, -1)).WillOnce(Return(color3Int)); + EXPECT_CALL(target2, colorAtScratchPoint(1, -3)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(2, -3)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(3, -3)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(1, -2)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(3, -2)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(1, -1)).WillOnce(Return(color3)); EXPECT_CALL(target2, colorAtScratchPoint(2, -1)).WillOnce(Return(color4)); - EXPECT_CALL(target1, colorAtScratchPoint(2, -1)).WillOnce(Return(color3Int)); + EXPECT_CALL(target1, colorAtScratchPoint(2, -1)).WillOnce(Return(color3)); EXPECT_CALL(target2, colorAtScratchPoint(3, -1)).WillOnce(Return(color4)); EXPECT_CALL(target1, colorAtScratchPoint(3, -1)).WillOnce(Return(color4)); EXPECT_CALL(penLayer, colorAtScratchPoint(3, -1)).WillOnce(Return(color4)); @@ -1179,47 +1178,47 @@ TEST_F(RenderedTargetTest, TouchingColor) EXPECT_CALL(stageTarget, getFastBounds()).WillOnce(Return(Rect(2, 1, 6, -5))); EXPECT_CALL(target1, getFastBounds()).WillOnce(Return(Rect(2, 1, 6, -5))); EXPECT_CALL(target2, getFastBounds()).WillOnce(Return(Rect(-5, -1, 1, -8))); - EXPECT_CALL(target2, colorAtScratchPoint(1, -3)).WillOnce(Return(color3Int)); + EXPECT_CALL(target2, colorAtScratchPoint(1, -3)).WillOnce(Return(color3)); EXPECT_CALL(target2, colorAtScratchPoint(2, -3)).WillOnce(Return(color1)); ASSERT_TRUE(target.touchingColor(color1)); EXPECT_CALL(stageTarget, getFastBounds()).WillOnce(Return(Rect(5, 1, 6, -5))); EXPECT_CALL(target1, getFastBounds()).WillOnce(Return(Rect(5, 1, 6, -5))); EXPECT_CALL(target2, getFastBounds()).WillOnce(Return(Rect(-5, -1, 2, -8))); - EXPECT_CALL(target2, colorAtScratchPoint(1, -3)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(2, -3)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(3, -3)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(1, -2)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(3, -2)).WillOnce(Return(color3Int)); + EXPECT_CALL(target2, colorAtScratchPoint(1, -3)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(2, -3)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(3, -3)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(1, -2)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(3, -2)).WillOnce(Return(color3)); EXPECT_CALL(target2, colorAtScratchPoint(1, -1)).WillOnce(Return(color4)); - EXPECT_CALL(target1, colorAtScratchPoint(1, -1)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(2, -1)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(3, -1)).WillOnce(Return(color3Int)); + EXPECT_CALL(target1, colorAtScratchPoint(1, -1)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(2, -1)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(3, -1)).WillOnce(Return(color3)); ASSERT_FALSE(target.touchingColor(color1)); EXPECT_CALL(stageTarget, getFastBounds()).WillOnce(Return(Rect(2, 1, 6, -5))); EXPECT_CALL(target1, getFastBounds()).WillOnce(Return(Rect(2, 1, 6, -5))); EXPECT_CALL(target2, getFastBounds()).WillOnce(Return(Rect(-5, -6, 2, -8))); - EXPECT_CALL(target2, colorAtScratchPoint(1, -3)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(2, -3)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(3, -3)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(1, -2)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(3, -2)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(1, -1)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(2, -1)).WillOnce(Return(color3Int)); + EXPECT_CALL(target2, colorAtScratchPoint(1, -3)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(2, -3)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(3, -3)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(1, -2)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(3, -2)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(1, -1)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(2, -1)).WillOnce(Return(color3)); EXPECT_CALL(target2, colorAtScratchPoint(3, -1)).WillOnce(Return(color1)); ASSERT_TRUE(target.touchingColor(color2)); EXPECT_CALL(stageTarget, getFastBounds()).WillOnce(Return(Rect(2, 1, 6, -5))); EXPECT_CALL(target1, getFastBounds()).WillOnce(Return(Rect(2, 1, 6, -5))); EXPECT_CALL(target2, getFastBounds()).WillOnce(Return(Rect(-5, -6.5, 1.8, -8))); - EXPECT_CALL(target2, colorAtScratchPoint(1, -3)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(2, -3)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(3, -3)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(1, -2)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(3, -2)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(1, -1)).WillOnce(Return(color3Int)); - EXPECT_CALL(target2, colorAtScratchPoint(2, -1)).WillOnce(Return(color3Int)); + EXPECT_CALL(target2, colorAtScratchPoint(1, -3)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(2, -3)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(3, -3)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(1, -2)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(3, -2)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(1, -1)).WillOnce(Return(color3)); + EXPECT_CALL(target2, colorAtScratchPoint(2, -1)).WillOnce(Return(color3)); EXPECT_CALL(target2, colorAtScratchPoint(3, -1)).WillOnce(Return(color4)); EXPECT_CALL(target1, colorAtScratchPoint(3, -1)).WillOnce(Return(color1)); ASSERT_FALSE(target.touchingColor(color1)); diff --git a/test/target_models/CMakeLists.txt b/test/target_models/CMakeLists.txt index bf565a2..0ff7efe 100644 --- a/test/target_models/CMakeLists.txt +++ b/test/target_models/CMakeLists.txt @@ -1,3 +1,23 @@ +# targetmodel_test +add_executable( + targetmodel_test + targetmodel_test.cpp +) + +target_link_libraries( + targetmodel_test + GTest::gtest_main + GTest::gmock_main + scratchcpp-render + scratchcpprender_mocks + qnanopainter + ${QT_LIBS} + Qt6::Test +) + +add_test(targetmodel_test) +gtest_discover_tests(targetmodel_test) + # stagemodel_test add_executable( stagemodel_test diff --git a/test/target_models/spritemodel_test.cpp b/test/target_models/spritemodel_test.cpp index 55a7d5f..501213e 100644 --- a/test/target_models/spritemodel_test.cpp +++ b/test/target_models/spritemodel_test.cpp @@ -393,7 +393,7 @@ TEST(SpriteModelTest, TouchingColor) RenderedTargetMock renderedTarget; model.setRenderedTarget(&renderedTarget); - Value color1 = 123, color2 = 456; + Rgb color1 = 123, color2 = 456; EXPECT_CALL(renderedTarget, touchingColor(color1)).WillOnce(Return(false)); ASSERT_FALSE(model.touchingColor(color1)); @@ -407,30 +407,6 @@ TEST(SpriteModelTest, TouchingColor) ASSERT_TRUE(model.touchingColor(color1, color2)); } -TEST(SpriteModelTest, RenderedTarget) -{ - SpriteModel model; - ASSERT_EQ(model.renderedTarget(), nullptr); - - RenderedTargetMock renderedTarget; - QSignalSpy spy(&model, &SpriteModel::renderedTargetChanged); - model.setRenderedTarget(&renderedTarget); - ASSERT_EQ(spy.count(), 1); - ASSERT_EQ(model.renderedTarget(), &renderedTarget); -} - -TEST(SpriteModelTest, PenLayer) -{ - SpriteModel model; - ASSERT_EQ(model.penLayer(), nullptr); - - PenLayerMock penLayer; - QSignalSpy spy(&model, &SpriteModel::penLayerChanged); - model.setPenLayer(&penLayer); - ASSERT_EQ(spy.count(), 1); - ASSERT_EQ(model.penLayer(), &penLayer); -} - TEST(SpriteModelTest, PenDown) { SpriteModel model; @@ -478,3 +454,28 @@ TEST(SpriteModelTest, BubbleLayer) ASSERT_EQ(model.bubbleLayer(), 5); ASSERT_EQ(spy.count(), 1); } + +TEST(SpriteModelTest, LoadCostume) +{ + SpriteModel model; + Sprite sprite; + model.init(&sprite); + + auto c1 = std::make_shared("", "", ""); + auto c2 = std::make_shared("", "", ""); + auto c3 = std::make_shared("", "", ""); + sprite.addCostume(c1); + sprite.addCostume(c2); + sprite.addCostume(c3); + sprite.setCostumeIndex(1); + + RenderedTargetMock renderedTarget; + QSignalSpy spy(&model, &TargetModel::renderedTargetChanged); + model.setRenderedTarget(&renderedTarget); + ASSERT_EQ(spy.count(), 1); + ASSERT_EQ(model.renderedTarget(), &renderedTarget); + + EXPECT_CALL(renderedTarget, updateCostume(c3.get())); + sprite.setCostumeIndex(2); + model.loadCostume(); +} diff --git a/test/target_models/stagemodel_test.cpp b/test/target_models/stagemodel_test.cpp index dc1214f..b949476 100644 --- a/test/target_models/stagemodel_test.cpp +++ b/test/target_models/stagemodel_test.cpp @@ -186,7 +186,7 @@ TEST(StageModelTest, TouchingColor) RenderedTargetMock renderedTarget; model.setRenderedTarget(&renderedTarget); - Value color1 = 123, color2 = 456; + Rgb color1 = 123, color2 = 456; EXPECT_CALL(renderedTarget, touchingColor(color1)).WillOnce(Return(false)); ASSERT_FALSE(model.touchingColor(color1)); @@ -200,10 +200,21 @@ TEST(StageModelTest, TouchingColor) ASSERT_TRUE(model.touchingColor(color1, color2)); } -TEST(StageModelTest, RenderedTarget) +TEST(StageModelTest, BubbleLayer) +{ + StageModel model; + Stage stage; + model.init(&stage); + QSignalSpy spy(&model, &StageModel::bubbleLayerChanged); + + stage.bubble()->setLayerOrder(5); + ASSERT_EQ(model.bubbleLayer(), 5); + ASSERT_EQ(spy.count(), 1); +} + +TEST(StageModelTest, LoadCostume) { StageModel model; - ASSERT_EQ(model.renderedTarget(), nullptr); Stage stage; model.init(&stage); @@ -216,7 +227,7 @@ TEST(StageModelTest, RenderedTarget) stage.setCostumeIndex(1); RenderedTargetMock renderedTarget; - QSignalSpy spy(&model, &StageModel::renderedTargetChanged); + QSignalSpy spy(&model, &TargetModel::renderedTargetChanged); model.setRenderedTarget(&renderedTarget); ASSERT_EQ(spy.count(), 1); ASSERT_EQ(model.renderedTarget(), &renderedTarget); @@ -225,15 +236,3 @@ TEST(StageModelTest, RenderedTarget) stage.setCostumeIndex(2); model.loadCostume(); } - -TEST(StageModelTest, BubbleLayer) -{ - StageModel model; - Stage stage; - model.init(&stage); - QSignalSpy spy(&model, &StageModel::bubbleLayerChanged); - - stage.bubble()->setLayerOrder(5); - ASSERT_EQ(model.bubbleLayer(), 5); - ASSERT_EQ(spy.count(), 1); -} diff --git a/test/target_models/targetmodel_test.cpp b/test/target_models/targetmodel_test.cpp new file mode 100644 index 0000000..1be0a14 --- /dev/null +++ b/test/target_models/targetmodel_test.cpp @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../common.h" + +using namespace scratchcpprender; +using namespace libscratchcpp; + +using ::testing::Return; +using ::testing::WithArgs; +using ::testing::Invoke; +using ::testing::_; + +TEST(TargetModelTest, Constructors) +{ + QObject parent; + TargetModel model(&parent); + ASSERT_EQ(model.parent(), &parent); +} + +TEST(TargetModelTest, RenderedTarget) +{ + TargetModel model; + ASSERT_EQ(model.renderedTarget(), nullptr); + + RenderedTargetMock renderedTarget; + QSignalSpy spy(&model, &TargetModel::renderedTargetChanged); + model.setRenderedTarget(&renderedTarget); + ASSERT_EQ(spy.count(), 1); + ASSERT_EQ(model.renderedTarget(), &renderedTarget); +} + +TEST(TargetModelTest, PenLayer) +{ + TargetModel model; + ASSERT_EQ(model.penLayer(), nullptr); + + PenLayerMock penLayer; + QSignalSpy spy(&model, &TargetModel::penLayerChanged); + model.setPenLayer(&penLayer); + ASSERT_EQ(spy.count(), 1); + ASSERT_EQ(model.penLayer(), &penLayer); +}