From 190519e66dcbc666e41df6f632039888ca281b61 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 8 Dec 2024 11:17:02 +0100 Subject: [PATCH 01/33] Set version to 0.9.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) From 08c8ca1ba0eda3e6d2743c19b236d5fa1d53e616 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 8 Dec 2024 11:17:09 +0100 Subject: [PATCH 02/33] PenLayer: Enable blending in stamp() --- src/penlayer.cpp | 2 ++ test/penlayer/penlayer_test.cpp | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) 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/test/penlayer/penlayer_test.cpp b/test/penlayer/penlayer_test.cpp index 007ef81..6400b29 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 From fa6f24018b04bcbac59cd875c507962ad36e0575 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 12 Jan 2025 23:39:20 +0100 Subject: [PATCH 03/33] ProjectLoader: Add API for stopping loading --- src/projectloader.cpp | 50 +++++++++++++++-------- src/projectloader.h | 19 +++++++-- test/projectloader/projectloader_test.cpp | 15 ++++--- 3 files changed, 59 insertions(+), 25 deletions(-) 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/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(); From 71c4e33419e822da2a797bc44cc3ed91f9d3a809 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 12 Jan 2025 23:39:39 +0100 Subject: [PATCH 04/33] ProjectPlayer: Add stopLoading() function --- src/ProjectPlayer.qml | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/ProjectPlayer.qml b/src/ProjectPlayer.qml index 1653eb2..cb4f3d3 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(); From b3a5425fe539d0ef41da945bb415f9ef9d0da54e Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Mon, 13 Jan 2025 16:24:54 +0100 Subject: [PATCH 05/33] Update libscratchcpp to latest master --- libscratchcpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libscratchcpp b/libscratchcpp index d9e0e11..264cc69 160000 --- a/libscratchcpp +++ b/libscratchcpp @@ -1 +1 @@ -Subproject commit d9e0e1124174a23d9f2783e756027da2f57d67b5 +Subproject commit 264cc693302ce4034599cd5ec15fb08de1ebda16 From 1d6028df1d00ddda56a827b5cec5726382e2220a Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 28 Jan 2025 15:33:32 +0100 Subject: [PATCH 06/33] Update libscratchcpp to v0.13.0 --- libscratchcpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libscratchcpp b/libscratchcpp index 264cc69..a5c4dbe 160000 --- a/libscratchcpp +++ b/libscratchcpp @@ -1 +1 @@ -Subproject commit 264cc693302ce4034599cd5ec15fb08de1ebda16 +Subproject commit a5c4dbe9acc177af46c1e70c5a5817f55faed58b From f494cc1927e4626b2f0f802c1e3b91419a6de8bb Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:07:13 +0100 Subject: [PATCH 07/33] Adapt to new color API --- src/irenderedtarget.h | 4 +- src/renderedtarget.cpp | 39 ++---------- src/renderedtarget.h | 7 +-- src/spritemodel.cpp | 4 +- src/spritemodel.h | 4 +- src/stagemodel.cpp | 4 +- src/stagemodel.h | 4 +- test/mocks/renderedtargetmock.h | 4 +- test/renderedtarget/renderedtarget_test.cpp | 67 ++++++++++----------- test/target_models/spritemodel_test.cpp | 2 +- test/target_models/stagemodel_test.cpp | 2 +- 11 files changed, 56 insertions(+), 85 deletions(-) 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/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/spritemodel.cpp b/src/spritemodel.cpp index 4383022..9189d1d 100644 --- a/src/spritemodel.cpp +++ b/src/spritemodel.cpp @@ -175,12 +175,12 @@ bool SpriteModel::touchingPoint(double x, double y) const return m_renderedTarget->containsScratchPoint(x, y); } -bool SpriteModel::touchingColor(const libscratchcpp::Value &color) const +bool SpriteModel::touchingColor(libscratchcpp::Rgb color) const { return m_renderedTarget->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); } diff --git a/src/spritemodel.h b/src/spritemodel.h index e54c47c..4c3e6bb 100644 --- a/src/spritemodel.h +++ b/src/spritemodel.h @@ -60,8 +60,8 @@ 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; diff --git a/src/stagemodel.cpp b/src/stagemodel.cpp index 26c7dff..cc4c724 100644 --- a/src/stagemodel.cpp +++ b/src/stagemodel.cpp @@ -112,12 +112,12 @@ bool StageModel::touchingPoint(double x, double y) const return m_renderedTarget->containsScratchPoint(x, y); } -bool StageModel::touchingColor(const libscratchcpp::Value &color) const +bool StageModel::touchingColor(libscratchcpp::Rgb color) const { return m_renderedTarget->touchingColor(color); } -bool StageModel::touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const +bool StageModel::touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const { return m_renderedTarget->touchingColor(color, mask); } diff --git a/src/stagemodel.h b/src/stagemodel.h index 269e11c..47d3e8b 100644 --- a/src/stagemodel.h +++ b/src/stagemodel.h @@ -46,8 +46,8 @@ 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; + bool touchingColor(libscratchcpp::Rgb color) const override; + bool touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const override; Q_INVOKABLE void loadCostume(); 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/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/spritemodel_test.cpp b/test/target_models/spritemodel_test.cpp index 55a7d5f..035744d 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)); diff --git a/test/target_models/stagemodel_test.cpp b/test/target_models/stagemodel_test.cpp index dc1214f..8e63826 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)); From af12984488e003cdeae7fe696e0963712fb21f25 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:07:30 +0100 Subject: [PATCH 08/33] Override IExtension::color() --- src/blocks/penblocks.cpp | 5 +++++ src/blocks/penblocks.h | 1 + test/mocks/extensionmock.h | 1 + 3 files changed, 7 insertions(+) diff --git a/src/blocks/penblocks.cpp b/src/blocks/penblocks.cpp index 8905786..e368e54 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 diff --git a/src/blocks/penblocks.h b/src/blocks/penblocks.h index 0feb01a..e611310 100644 --- a/src/blocks/penblocks.h +++ b/src/blocks/penblocks.h @@ -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; 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)); From c5ec812a633de9a296c4cf2e5611de7d57a73a9c Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 28 Jan 2025 17:28:52 +0100 Subject: [PATCH 09/33] Update project loading in PenLayer test --- test/penlayer/penlayer_test.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/penlayer/penlayer_test.cpp b/test/penlayer/penlayer_test.cpp index 6400b29..0142035 100644 --- a/test/penlayer/penlayer_test.cpp +++ b/test/penlayer/penlayer_test.cpp @@ -362,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); @@ -371,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)); From 98d49747ee5cff55cddcc3b588a8829d15876dc0 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sat, 1 Feb 2025 23:55:01 +0100 Subject: [PATCH 10/33] Add TargetModel class --- src/CMakeLists.txt | 2 + src/spritemodel.cpp | 183 ++++++-------------- src/spritemodel.h | 50 +----- src/stagemodel.cpp | 105 ++++-------- src/stagemodel.h | 34 +--- src/targetmodel.cpp | 215 ++++++++++++++++++++++++ src/targetmodel.h | 98 +++++++++++ test/target_models/CMakeLists.txt | 20 +++ test/target_models/spritemodel_test.cpp | 49 +++--- test/target_models/stagemodel_test.cpp | 29 ++-- test/target_models/targetmodel_test.cpp | 49 ++++++ 11 files changed, 514 insertions(+), 320 deletions(-) create mode 100644 src/targetmodel.cpp create mode 100644 src/targetmodel.h create mode 100644 test/target_models/targetmodel_test.cpp 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/spritemodel.cpp b/src/spritemodel.cpp index 9189d1d..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(libscratchcpp::Rgb color) const { - return m_renderedTarget->touchingColor(color); + return TargetModel::touchingColor(color); } 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 4c3e6bb..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); @@ -65,43 +51,23 @@ class SpriteModel 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 cc4c724..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); + return TargetModel::touchingPoint(x, y); } bool StageModel::touchingColor(libscratchcpp::Rgb color) const { - return m_renderedTarget->touchingColor(color); + return TargetModel::touchingColor(color); } bool StageModel::touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const { - return m_renderedTarget->touchingColor(color, mask); -} - -void StageModel::loadCostume() -{ - 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 47d3e8b..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); @@ -49,30 +40,17 @@ class StageModel bool touchingColor(libscratchcpp::Rgb color) const override; bool touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const override; - Q_INVOKABLE void loadCostume(); - 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/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 035744d..501213e 100644 --- a/test/target_models/spritemodel_test.cpp +++ b/test/target_models/spritemodel_test.cpp @@ -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 8e63826..b949476 100644 --- a/test/target_models/stagemodel_test.cpp +++ b/test/target_models/stagemodel_test.cpp @@ -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); +} From 43e90f70dec603e58a53dc93adf4df39cfbf31bf Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 2 Feb 2025 11:08:12 +0100 Subject: [PATCH 11/33] ProjectPlayer: Set stage pen layer --- src/ProjectPlayer.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ProjectPlayer.qml b/src/ProjectPlayer.qml index cb4f3d3..87f8f7e 100644 --- a/src/ProjectPlayer.qml +++ b/src/ProjectPlayer.qml @@ -173,6 +173,7 @@ ProjectScene { mouseArea: sceneMouseArea stageScale: root.stageScale onStageModelChanged: stageModel.renderedTarget = this + Component.onCompleted: stageModel.penLayer = projectPenLayer } Loader { From 5e5ec4fc163f48104b1f46c82bac0d296d635353 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 2 Feb 2025 11:13:25 +0100 Subject: [PATCH 12/33] Add support for stage pen rendering --- src/blocks/penblocks.cpp | 52 +- src/blocks/penblocks.h | 4 +- test/blocks/pen_blocks_test.cpp | 1322 +++++++++++++++++-------------- 3 files changed, 757 insertions(+), 621 deletions(-) diff --git a/src/blocks/penblocks.cpp b/src/blocks/penblocks.cpp index e368e54..2d77b79 100644 --- a/src/blocks/penblocks.cpp +++ b/src/blocks/penblocks.cpp @@ -220,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); @@ -230,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); @@ -240,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); @@ -250,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); @@ -260,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(); @@ -272,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()); @@ -282,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(); @@ -296,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(); @@ -311,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); @@ -358,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()); @@ -374,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); @@ -384,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); @@ -394,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); @@ -404,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); @@ -414,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()); @@ -430,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); @@ -440,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); @@ -450,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); @@ -460,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); @@ -468,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 e611310..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 @@ -78,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/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))); + } } From 58a0f226e680e81d19a80a02ee43e543b24c67d1 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Wed, 5 Feb 2025 14:52:36 +0100 Subject: [PATCH 13/33] Fix GLSL version on some platforms --- src/shadermanager.cpp | 8 +++----- src/shaders/sprite.frag | 17 +++++++++-------- src/shaders/sprite.vert | 6 +++--- 3 files changed, 15 insertions(+), 16 deletions(-) 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); From d7219ffd201afc7fbb91de336dcbec9d24fa79d4 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 7 Feb 2025 11:01:40 +0100 Subject: [PATCH 14/33] Use OpenGL core profile on macOS --- src/global_functions.cpp | 4 ++++ 1 file changed, 4 insertions(+) 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); } From e96c3487005c8b086c95dbe14f5d8fb90e9cf50a Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 7 Feb 2025 14:50:36 +0100 Subject: [PATCH 15/33] Update libscratchcpp to v0.13.1 --- libscratchcpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libscratchcpp b/libscratchcpp index a5c4dbe..8ed5355 160000 --- a/libscratchcpp +++ b/libscratchcpp @@ -1 +1 @@ -Subproject commit a5c4dbe9acc177af46c1e70c5a5817f55faed58b +Subproject commit 8ed5355643f3b92cd672cda7673dba1b4105bbb1 From 322dce01c8c466441fe707ad87590659ad8d5f44 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 7 Feb 2025 16:55:32 +0100 Subject: [PATCH 16/33] Set version to 0.10.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 64781f7..e200f12 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.14) -project(scratchcpp-render VERSION 0.9.0 LANGUAGES CXX) +project(scratchcpp-render VERSION 0.10.0 LANGUAGES CXX) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOUIC ON) From 47e7aadca8ece8f520580ae17c15cbd437e04d5e Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 1 Apr 2025 12:39:30 +0200 Subject: [PATCH 17/33] Fix costume data allocation in tests --- test/renderedtarget/renderedtarget_test.cpp | 28 +++++++++++++++------ test/skins/bitmapskin_test.cpp | 8 ++++-- test/skins/svgskin_test.cpp | 4 ++- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/test/renderedtarget/renderedtarget_test.cpp b/test/renderedtarget/renderedtarget_test.cpp index 4b2ad13..dad1560 100644 --- a/test/renderedtarget/renderedtarget_test.cpp +++ b/test/renderedtarget/renderedtarget_test.cpp @@ -87,7 +87,9 @@ TEST_F(RenderedTargetTest, UpdateMethods) target.setStageModel(&stageModel); auto costume = std::make_shared("", "", "png"); std::string costumeData = readFileStr("image.png"); - costume->setData(costumeData.size(), static_cast(costumeData.data())); + char *data = (char *)malloc((costumeData.size() + 1) * sizeof(char)); + memcpy(data, costumeData.c_str(), (costumeData.size() + 1) * sizeof(char)); + costume->setData(costumeData.size(), static_cast(data)); costume->setRotationCenterX(-23); costume->setRotationCenterY(72); costume->setBitmapResolution(2.5); @@ -282,7 +284,9 @@ TEST_F(RenderedTargetTest, UpdateMethods) // SVG costume = std::make_shared("", "", "svg"); std::string svgCostumeData = readFileStr("image.svg"); - costume->setData(svgCostumeData.size(), static_cast(svgCostumeData.data())); + char *svgData = (char *)malloc((svgCostumeData.size() + 1) * sizeof(char)); + memcpy(svgData, svgCostumeData.c_str(), (svgCostumeData.size() + 1) * sizeof(char)); + costume->setData(svgCostumeData.size(), static_cast(svgData)); costume->setRotationCenterX(25); costume->setRotationCenterY(-8); sprite.addCostume(costume); @@ -373,7 +377,9 @@ TEST_F(RenderedTargetTest, CpuRendering) EXPECT_CALL(engine, stageHeight()).WillRepeatedly(Return(360)); auto costume = std::make_shared("", "", "png"); std::string costumeData = readFileStr("image.png"); - costume->setData(costumeData.size(), static_cast(costumeData.data())); + char *data = (char *)malloc((costumeData.size() + 1) * sizeof(char)); + memcpy(data, costumeData.c_str(), (costumeData.size() + 1) * sizeof(char)); + costume->setData(costumeData.size(), static_cast(data)); sprite.addCostume(costume); target.loadCostumes(); target.updateCostume(costume.get()); @@ -759,7 +765,9 @@ TEST_F(RenderedTargetTest, GetBounds) target.setEngine(&engine); auto costume = std::make_shared("", "", "png"); std::string costumeData = readFileStr("image.png"); - costume->setData(costumeData.size(), static_cast(costumeData.data())); + char *data = (char *)malloc((costumeData.size() + 1) * sizeof(char)); + memcpy(data, costumeData.c_str(), (costumeData.size() + 1) * sizeof(char)); + costume->setData(costumeData.size(), static_cast(data)); costume->setRotationCenterX(-15); costume->setRotationCenterY(48); costume->setBitmapResolution(3.25); @@ -850,7 +858,9 @@ TEST_F(RenderedTargetTest, GetFastBounds) target.setEngine(&engine); auto costume = std::make_shared("", "", "png"); std::string costumeData = readFileStr("image.png"); - costume->setData(costumeData.size(), static_cast(costumeData.data())); + char *data = (char *)malloc((costumeData.size() + 1) * sizeof(char)); + memcpy(data, costumeData.c_str(), (costumeData.size() + 1) * sizeof(char)); + costume->setData(costumeData.size(), static_cast(data)); costume->setRotationCenterX(-15); costume->setRotationCenterY(48); costume->setBitmapResolution(3.25); @@ -925,7 +935,9 @@ TEST_F(RenderedTargetTest, TouchingClones) EXPECT_CALL(engine, stageHeight()).WillRepeatedly(Return(360)); auto costume = std::make_shared("", "", "png"); std::string costumeData = readFileStr("image.png"); - costume->setData(costumeData.size(), static_cast(costumeData.data())); + char *data = (char *)malloc((costumeData.size() + 1) * sizeof(char)); + memcpy(data, costumeData.c_str(), (costumeData.size() + 1) * sizeof(char)); + costume->setData(costumeData.size(), static_cast(data)); sprite.addCostume(costume); target.loadCostumes(); target.updateCostume(costume.get()); @@ -1140,7 +1152,9 @@ TEST_F(RenderedTargetTest, TouchingColor) EXPECT_CALL(engine, stageHeight()).WillRepeatedly(Return(360)); auto costume = std::make_shared("", "", "png"); std::string costumeData = readFileStr("image.png"); - costume->setData(costumeData.size(), static_cast(costumeData.data())); + char *data = (char *)malloc((costumeData.size() + 1) * sizeof(char)); + memcpy(data, costumeData.c_str(), (costumeData.size() + 1) * sizeof(char)); + costume->setData(costumeData.size(), static_cast(data)); sprite->addCostume(costume); target.loadCostumes(); target.updateCostume(costume.get()); diff --git a/test/skins/bitmapskin_test.cpp b/test/skins/bitmapskin_test.cpp index 6e5c936..484eeae 100644 --- a/test/skins/bitmapskin_test.cpp +++ b/test/skins/bitmapskin_test.cpp @@ -21,12 +21,16 @@ class BitmapSkinTest : public testing::Test Costume jpegCostume("", "", ""); std::string costumeData = readFileStr("image.jpg"); - jpegCostume.setData(costumeData.size(), costumeData.data()); + char *data = (char *)malloc((costumeData.size() + 1) * sizeof(char)); + memcpy(data, costumeData.c_str(), (costumeData.size() + 1) * sizeof(char)); + jpegCostume.setData(costumeData.size(), data); m_jpegSkin = std::make_unique(&jpegCostume); Costume pngCostume("", "", ""); costumeData = readFileStr("image.png"); - pngCostume.setData(costumeData.size(), costumeData.data()); + data = (char *)malloc((costumeData.size() + 1) * sizeof(char)); + memcpy(data, costumeData.c_str(), (costumeData.size() + 1) * sizeof(char)); + pngCostume.setData(costumeData.size(), data); m_pngSkin = std::make_unique(&pngCostume); } diff --git a/test/skins/svgskin_test.cpp b/test/skins/svgskin_test.cpp index 7f59952..780d3dd 100644 --- a/test/skins/svgskin_test.cpp +++ b/test/skins/svgskin_test.cpp @@ -21,7 +21,9 @@ class SVGSkinTest : public testing::Test Costume costume("", "", ""); std::string costumeData = readFileStr("image.svg"); - costume.setData(costumeData.size(), costumeData.data()); + char *data = (char *)malloc((costumeData.size() + 1) * sizeof(char)); + memcpy(data, costumeData.c_str(), (costumeData.size() + 1) * sizeof(char)); + costume.setData(costumeData.size(), data); m_skin = std::make_unique(&costume); } From 9033865ee7fab6b6b56567c33e84e3a10f9a452e Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Tue, 1 Apr 2025 12:41:42 +0200 Subject: [PATCH 18/33] Remove stroke width limit from nanovg --- test/lines.png | Bin 2115 -> 1907 bytes test/lines_hq.png | Bin 7403 -> 6405 bytes test/penlayer/penlayer_test.cpp | 6 ++++-- thirdparty/libqnanopainter/nanovg/nanovg.c | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/test/lines.png b/test/lines.png index 245885e9061acacb6132edfbc8897e4e917fafe6..5792818cef52bb0b2290e28979a2bcd4c7195511 100644 GIT binary patch delta 1871 zcmaKsdpy&N8^_x$n_>1fr!917P9%4^jLm&>a$3Z?Ak9WAigHUks7;JDghFyFFQR@< zvO`F7thpb*lY5rS7)Sb{Y)4UY`O)wH-}8F@`FuXl^ZfO^-mS=GM3x=vfCtg#1heSF zgj3YtL=CpwcBldwx$9qIh$xbMSep;nHDu4h;-RK=thNPYA%}Yi;S7NyL4(wcQv)## zO^;Totd>I~aa+uz!3%{WW&I%tFlBA#tKr-N!fbWvmn+IEt8XP*yL2b%ORCQH73N@l za~cMukit*4;W4a;tb8FmluT>Q7tz3F1=R8-(pqyv>tX$xNX<%_`KZD--;-BXB)h)2 zG>**_F0xY$wWkk^hg&4_;goBEkaXkZDuqd=LA}2_s@jy0ksQ)irRD_r`QkCUXgIA? zm``e4le^s?=q}@Oi%eTIeJ;XL&s51%{L9Fv(yVBp+JvRu#_R8%IVtANmK623T%T0) z>d>WX!El_?th?*W-_)cO(ev5kVX2?${++&2a&t(^iTBUhNws7gCSa$KTwP> znB)E|MQri7;ZM-Atbt+zuRrU80+o9@nf>&w{*(m+AFq+LKaiB5H@+WRAN+u=K*Go8 zGd*{Dni0K{{TVZu`55hS45f(^o*@;ZRuM|*xc|wAi}we6mT|mp6Lm< z)un00?AAs`<-MqVY!$#)xcZ)u?KV^V=bgI*_jW%y5$Ty2cK0=#6jt(C15a0$)Jq?o zvD^K-gZ;g};5T_g*x`G_Pf+(5bzX4>vMdgsb)V1cxtP}j3nNeMPaE_t?;=vQOeta= zEflkBbf0HU5Ox`dj$S-AC7l0hk4F<~-fMubBV`hKKeCncPF&M z^xfNHVdAWZw#p5~iRI2)@oQAi(_7Vj;SBgUx)Tl10n(1F%H+Dk26DtxK{I5Lm71A0 zdNmu$&53hpTCeIVJp;21kp;!)zY7LKJ7V9rS8@)u#AF*TCQT&yQdB1PP9*prTnRM* zC=FVSuLZ)yX^*1omhK$N;rpVM9sLjf(QlO;4Z*b1+Lxl8Sn$iM&Q3CYEWvY1xKNDB z%#4)AgP~M|$eMrT78I7)bP|P=-7sjMnK}O)sNrsLNw60#;l`z@0<+4bHifgsW#Jbu zfT2&v>5&f?hKCuk#i3?2U=_cZ+m)r}B-vW&oMT-tUYnfryqwd2UWg4R8LRJ$@@x}( zy#%Gzq4`}wU6YRMpQ2y0x-}$TW82rmD0mHes0&ce8+ruH-asq}Ul>u&M;FS21$+y{TiZzbo=e|G^+G(a^?16}C zRtNSxe1Rxr(;)&y3Jy+b{1HqwvY6gv-hk`m*27domzw&li2{tHjj!^EUww?~f2ifF z>sAAW&9*|$<*&RwhWPcNmgh+86&GP{uJ0j~^}Cv6(@-nUDzoV2N%5Y%ercSqaVcGK zu*rlxHK0iGUCw@|?tSTjh)ip2FK4YJDh0GM;Fn5-hIyhl1kQb-@2o6p>$O`O`BF0u zRh6Q6%DSw!`=hCjM_}NMs!=y#uC>jRkNS?sfurwLlet>O_R#=K9Ai|3ol;e;8I!m(F5>ok1K3}CLi75}D`!I|u;9zW4sSHe*jVHf zhXC2~JU%FZW`@U;^}?EA7+VH;VY(+IuXhmESg(Z^)bb8AA|UBO9o~UAJssP9+$p$K z!}6hLd@kF2Y&?7RF{{SU-b{~nB6{YgB88s%cxjuOfu}}|Ps1<;GWAxX;Gxb*c}mDt zi$v+2)#!%`-|1OqpV$8xxx>+56j^Ey!w6l^vLz3FhGAavGV;ZhK!gclGb7f>bGo?m2fK$N za~}!R@JAG89(jcTu1=giY~#A~C;^&-Xe}&FX#V}=9bd=bV2BFFUHHiSI$zM}3ycw0 AaR2}S delta 2081 zcmV++2;TSe4#N`1h_|c#0E@dhOrB66o0$X5McrXrZIsHV+w-_ zjBGlQt`q5MzYfosz{Y8u!oz5He2!)Hto93lAB0;TZ?Xd#rf?rG;g-b&#wJYNx(FCi z2v-N*cqdBjMiZ_s+Ue)n4b`CUYqxRy-sLN9%?Ha3|=#P3A@{O)!CQ{uH{`GeKsBo2*K>9z+? z;6b?Z%};-Go1r~nUu}tan$m5bjrb(o;cpSKE$eod2!DB+vTdIXcqU#E0=8!TdZ)#R zMNEc#6!Aj1Lqu#x+yM(BUZ&tZ=q~PW)`Z(5U_0WEIJO$Gh?!~JiFES)ACJEXjJHR` zHf%p-K*-AqzXwOpp}M$V6>g7!ZAf#@ujPnE)Py{B`J;v>b|8;*C+%C0SfpymbJs2t zuwQAfx_=4z1oj?s;R9VEHyseLUwJN@V;X0l#%u2uB4VHNUH3}Fk1~WDbE_W#`;_;> zbs|1U5c1Z0bcxuXGIyR3@1y`MyU zZ07yg-yOoezD@UWB4A(2-@Hx4=LYY|QMkXz^?%X-m*~AAVn6CVeSwhA>@N>%-LrUy zHoi9m>_^?lrx3A-oE)Uu&xCs__8c++yHWr7M-4|jdvrPT6d52qtEH)xT1?GY@V)wMchZ+iFBLJ=Ozr9fbG*4u!#GJH%qua zCVyhv>aT&vG`cPh#`+jfbWY$Qrtz|kyVXo#yYvSvLd2mWLndImYy>Pq#A~b!nSkxG z6R-#oud(t#7ZKZJE8rd>hYJk3`|JMan7`gaz?)CqDQ0)0hC8Pgcf z`3_iwh$F>L(%8w3IQ|0`A!4}Ya$PWsb@3|J*TSrUyA>tKS3+vWqz<(k{ z3>RFk3sz%YJj?Pr*z;wSA}vqgA*S&xcBAzo;2sgfAOapD;z>+lo!qVsHe+3U#^PF- zHE^q##%Gwq`VjD(3=wx1_c#5I$X5k?;da*|sRX;zp}}f6^|Hj$uC(`EXtx`dMP$fZ zr>(ZA30M!e8Za1u$nUG^2l3qfJkhDkKLcq^zzX0>DnOvQ~v z{NeWPoBrn-BMvnew&p3^!UT4^VO2Pe_*5St0oUbr!>SsDJPf$!DdZ<^tT;<3(uD$Kai2zuS_bO>{6djV@H!XCe#Ne@OExDOpTfE z!zHqQIM*L2^i@mdeVo|sFMdQ zvf)7j1dJ>)KCVYW*lHpsV1MLQ=($^kqbA^jTyLBSl^&j#IcgOV6EMn9@&wF;Q4??> zt~bty0x>$uwRP5OvasSp$!Ay>Vt4iTKn>)C63B>y0zbXv9NL zK}TIDJ^V#%ydu6YqZDzzaV{7Rd0{tuF6_0$5#0SMR%qY?8B(eiS=d*PO`U24tG&9aUOY?0AvjP#hmmKmDFOyNA3z?LzI zo5uw9!oW0U3a7^e_JV2LBqp#o$|o^XI4vfyH%#KDFo8W&I*pma=`evkV;Z|hz}^wD zYwSX|LfP&#yU}S7aDQu*giOF`5O9l(MohftSOd3+b+H=++$seTGf}r=7aG-fsY7<9 ziG3Uav>J44sHL2mwPcB3?wm(2tNO5io2Z;!y+)JBauU z0mGK1h(($TnSX#`6Vq5k2pE+R_|Ry`1PnhKjF^DoOJfleF#KsKVgiO= z4TMa<@XdL|1PuQiM@+zIgVTr!7;SMDG6ADKjv^*tw8=@t1dMh$i1_Q;F94|KPHyE+ zZsk%gCx2f#CNLm!K8+1bUjRdUxe9vX2LJ#70000002%-Q0Ga#`UU$+3c!Jrp00000 LNkvXXu0mjf6iC~L diff --git a/test/lines_hq.png b/test/lines_hq.png index c78d1d30602cc84f961223ddc15a34e1a2c9a953..2cdc7e7557b325b99b1407f1f57aedf93f0cf6ba 100644 GIT binary patch literal 6405 zcmdUTi9gi)_y7An?=i+Q7|UcvcA-l(H6vr~9TR1X7LCeQU8dZkC`-kgm?&9Fo2xM> zDNC~4%8g_~i{*{b=Tpg28R?2kg_7@UT7JLpzwq^V%;V*KUgwPa zs4l37J#-^-$L@WJzRsg9I>9}QPB&Vv8nZKk>a6PK{|z5)E`L4#QQX{<4r5tLrMeKh zEGBJOjY`4$l))DSaScSUpHC#rb3_rM>Oc!IAcDYvpcQ037Eppid*%2TYfJ=3al!Pi zBof~lMIe*}Q&h4v7$OQx3A?Y&hCY+|5cDvNKLEj2uvdN zpO%s8HrY3IZjZR$9L7b21MTC$0>=8NB-XF(%f~= zetJH$J|R+SY}Fy1HYhzRl9F*>-tOGtfj{C&=WfT70$jX<1I#{@!7F_7c5YK^>k01H zvBV*_)g0ssX8_XNly&8Za{Ay*M(1iac6p`^V z9Y|}9Rpy1g%WrXzY9A4-nb1vtc5;C#X2Q#RuGUuHXO2UOZ?+@+`w9y6XG-udmDb1- zz(;oEAa>&hh$p5|vvfdl>Vf43SQIiymz+{lf@+L%;{BorVABV5XLM3%~>1*VvM3ErE?V#@m`A=tYI zET*c-LTHm=PCPZbgHW{VElX)YF|~z2LA}xkeK<%*G!-f07i#?XonlJ^(6SMTYAxeK zXHVUAu<3<@cEt;V?_ORX8vAW#aF}UY%wn>Z?JIBmo%70|(2Dg~en}2|#8BhmEn{JL ztTyrH+wG3F2rH}swmMKSJvCa|K@2)bih81;Q?hx77p-&S$wN*0Iq<$pR4g>_Bn}jZ zK<1MKa!B{>-L%fGk=Uef&HnRQ8(c=_zlBenPYw<^WX~lpnGxyNPJ@Z5CBg;M8G7}h zcU2m;R`2X(*qs{lmRt#o_03zus^7Lxl(%54;6KlgHCip2Pz-xgv!KatetBh-Xv^W; zZPR}_A1JIr|+_^BbQMKC4fG*;$ZUjBg5n$Va$rYDsP zI7En1&QIEw$X-xJsHv1%QU9JeCFCmHBJXZHa#G?kG{fa0EH!*dGHoBV^Kw>N-1Owc zz_LA>Z6*Z9g^Soe_uFBQWGPo8+(r&_<_Fqu#d*mN9P!{Hm9u#tkqdpUv#;G1L#yO{ z+8cxV9!#EY8>9yW(Ii41bmw)+PJX0#rph5wr_Gxm;6$i#Zb))u#{gKG>$1*%K8t9HJ8cA)I$^kHzq z$H)>%qQd#a2-YI;OzQ`;e7I(iWNmGEZToDFy+v)NFv}(wi{^=(SIo>dQ+g!t_LR15 ziAw`1O^YVUjAA{w(i1XO-@eY@7YXpANxa-EzGXf#@a3l5D9fA+-YSA?^hM4qJbQQOki0$m(lN+E{nzTM8WC-9}DsxEg11@vlzSSfK>e+FU+)bf6*0_x$oim zD@XuEVd}j#@2ljQQAk8$_snTvtu`RtjUN>DghadZW^L?;X*~)_Q6~+6wMKc_F8hkhr #+QWxwMm1e1TxcSf}b zQYoIf%G8QWmi}1>Om-E66R3QTssO#%5&t$CDT`uH&1P@1$XO64UM_qBmZFBp{#{X3 zrz&!GX~NZIt0P?}!s=#`9jkI>*r=}u$5gDfT%I#edyyM^XRg)rdULqTKyJHN1KUM{ zY*)pz)NHN!!xuUyY=!NY3}z>FyM}!SXC547Lkd8zoYIOH3d3g{U5>ZkyX;t5DX&kd zm$Ag3QojnaW_uDR2jiaod(aFIw|Shx%{rrosVT4zM(u1$s4kv-`RaA}?5<3bNCVJh zwvHB!daB(erv?;`RFa;~n@<7Kb0z5?!i(?^8Xy1Ri| z-kCSbyc|c@Ft7nK|C7mYg0A=54UbnULGt?>R@-RL4eb?Hs^hH%(T^?%YqxQr$K_~O znq{ct=t|+nJ#jq)ca&AKWwJk+cT}zHFxZ1(b%~fRTfdUVpM6WwvD;JY^yM&&XUSx1ns-c@k4vE+2FDwdLZ>7?Qg3{H zL@AA`?jHzc9bi#xY5W}%g7m&>zk)e!xTuZb?skr!Pp()C%<8E`)y`PPasx9-nrWEv z(m>Rx)RBSh$O%))4Ohw^nfeZFP2&$v8fWmq5&G5!*)|edIp!VQ?jJt4fUmoBqu)-l zQBxYfX=jRYjrWZwH{v2evGv(UL{GO&;s&1*fD5xZD{Xky)>S$jDxSKIsNyTGOcURW zn|^g~ZtTOo(23yT-p##)PGVD{%VvZOKs&dN;+zt*!6ux_M}QjYT>_(qjwxWKbE&M z_CI(`D93+mkWcq|68eC^)Q4M-aM=adRXV{cg%Gl>07g(IsmKLoEu*$dUOEXn-a&RnsE8JlSXQ__?9P=6{Wu;8+xaTip))qqjA zzX@(}Lmic<6sSuL&v4~Bx;&Dnw78;TngIhUTqFuKqf)&2*TJRp$DC%r4oqoR>42Ep zU{vjJYXhuKsH2>eRbP~+4zz4k!97nW2W@EMU>C`JQ+F;>j}zHX_3|%fXfQ4$VS=py zU2z@i=x&hxh?f^nC-aK{lkFSe_s5-BIDzpA@Fr_8G%sO-T41qe0w%Bnl>m7?D(>U5 z)xw(KRJWgX_Yz*-MGUZ>#RT3!ZK9DVZxOY%zXY#OCbFxH^I<)8m|~~Wq1Ynto(2kN zQ26Umaa;k|sZZuhQj!IQfR{mF#2Dt&J+oL!m<3TX zIP|_ro|1~9j_n{oaj*!rY8{Gz#5ED=6Q~rocR!qP1&;R>2|m?@6S5AaqfK0Ua_C z!2yfRB7DIyDAh$uCcxH1!l~2tSK9wMCn;S`eWUxlGF{xOtlPL)B?%){oF!k{cJFu* zNiX4;mW$>C53`7qXFYlChL7d{vGQpyRZ9c8mXMAs0Wzg3YuJ$Cf$8(LXLkS zMI&1gKscHa!~`w|9bvMbF{0FmwvMMCvwj=5LP|DDiO9}aw^n?%TPnDG+8VDnzM4!UMhx~o?PVMpy*VAz%_IXQZ;qUzb%ph&FZP|c zn^+&ACp;Ef2wl$FFuQ%Y8r@V`DfE^c9xLmpT=Q}Hm7zEHhPAHYECse__XaXKgm-B4 zL-QYA6gxE>RlBtV-Lw%6y@k8Rx?QyF$Y;zBBqGygo%CiF*FuX+mSoAt7BOSi+6?G1 z6xg_htA&>0-ww;H=545Uh57`Tk~mbxIH&z8ZpMe}=u2Q|3aYbNU8Ok4j!)l^7*WMy z8%ZbkBin!&;i7TxzDri}A;g_c;|Exu+Y=O+BCrNk=z%SEMc3Ln9$a?Ta3XSh<$Wpm zK~pNh`pOCTXlFRrUl_)DC5og;AIGQzs6HD(6-~&&aGR?OJ<~8!(Zj7TWz5)~Ss3Zr z#iBxxUb6-q6YKZduYHzNv~+2M{G|Q&=!2P`v#O0>I(y=o)Kvo9-zjxY%7;-Kz$gPka8$Zk82Q(L@ zg*9bvxX()Kqe=e%9a9Y+@DU~wUkj!j2zX5m543QW85las;w}QO_*UPK)?S1EnITcZ zqf2mx-o3FDub%{{8!yb1SwEgt^!h3dPt^7J4PAR3aDxp`J+C=D+fht*wNh*cO^x1t zYa(t(RoglO-=H2jjqXAEyZsIDi{U?)EKfOge`d5HJzK}=?>rz^)A1uI{sFcC zf-+X4NPUv&lYO$TR&L--eQ(cv@2@~F`cn()K;ApN>bRerv_+!_l4`t0jg;1v-jgoV z0}}iX1=?VxPh%tqQv~)%yEMbV0_nBzb6y_YZr~C3LvIPVZ#R4wU4tSh$&3y3LVCwA z66WU3zwqZTHPS9pe_i{?#?j-D-GL@}^8cYZ0k$*0*n|N|f*G*l-_7e^2c}N`nv!Y= zxEPvGD7G5HqJQy-RLs~d!0R8L%J}339sx#wM_cJ1kE}dd;eb5<#T>9iiwpqaS91$G3v`AMmem13q6P^P9j zRelL0)rHr=n^U|srLJp#ROzs@B_;p5sUe$?JJ7b=foK9_`qY=;XPZxEXta9@0H<7X zuql8-4W)53M*VA${#X^qCMo=qp#BrVrXRrT3WAO(za09qY}NU%bJ)KiTJX$Zapa9k zl?xX^E6qBbULWGUyF>8{xD9MT`nKDzcoYQMkKK{(8k04w1!cJZT%*bgd!*j@r$67< zInb%i5()rW|GP*_)2t%29`s1Q$HMnzW$mwnq4nTsNl9Y!l6J^5@#n4t1so+|JN|cg zh$T6p5bE_4K=a~9I&=bL>E9+4q0K+{A&}b>3>7*3G^pUFJ%Voj#h(KBum6HF1yJP* zFvU;>jM9CRb_%({0Mhs8=IO5(WkK(MbPHjMmrpJ%0;idp5)AP{WyT()L)kwA&Cv*e z@;y*TbwI578KeR-K)^xkvQ`a@8lYN|j-aynjx+_1xwP)V4T145f9VO8idX~hr=v(%7^uSxgs`3wOj#nGcKzw|;Y#2$9~65=VA2+* z90t0WL@)wfOhuSd4II!sfeEr7upmWe)Nu*WDFEdi2^b<)#XYlu?mRiZ={j4ae!QFA7#y@6z*ktk3BNOq}$5*J83C9+>jfq?NL07!4f8Xvz9 zMuOTv1+4@4wgWv&8t4!&AYR}K%8s zuc)0Z4jcs1+AbU6Vvs!n$N>e<;Y*+x-VRhmH5@>5$-A9EcMv1!$WEnX82<(=R`Z6$ov;PM?K zprox@+QEzQ%cn!+%ek06wR2JhK%#RpQ&!9EbUG9vVs9jX9GM1!%w$dYsE1p#KF|j@ zDo9)tYIGCm&;|6X6y~!DAU{ggz|Rv8MoY;ro=Lew0U4^hXd()wcK1G50oD74%TAr9 zH*7UUpg#;m;atU#JVnSe3tPIk{UNOxG~xfx{ncyFE92L6X#fBK literal 7403 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 diff --git a/test/penlayer/penlayer_test.cpp b/test/penlayer/penlayer_test.cpp index 0142035..95fd9d2 100644 --- a/test/penlayer/penlayer_test.cpp +++ b/test/penlayer/penlayer_test.cpp @@ -315,9 +315,11 @@ TEST_F(PenLayerTest, DrawLine) penLayer.drawLine(attr, 130, 77, 125, -22); attr.color = QNanoColor(0, 128, 0, 128); - attr.diameter = 10; + attr.diameter = 225; + + penLayer.drawLine(attr, -225, 25, -175, -25); - penLayer.drawLine(attr, 152, -158, -228, 145); + attr.diameter = 10; penLayer.drawLine(attr, -100, 139, 20, 72); attr.color = QNanoColor(255, 50, 200, 185); diff --git a/thirdparty/libqnanopainter/nanovg/nanovg.c b/thirdparty/libqnanopainter/nanovg/nanovg.c index d965990..83c1d0f 100644 --- a/thirdparty/libqnanopainter/nanovg/nanovg.c +++ b/thirdparty/libqnanopainter/nanovg/nanovg.c @@ -2345,7 +2345,7 @@ void nvgStroke(NVGcontext* ctx) { NVGstate* state = nvg__getState(ctx); float scale = nvg__getAverageScale(state->xform); - float strokeWidth = nvg__clampf(state->strokeWidth * scale, 0.0f, 200.0f); + float strokeWidth = state->strokeWidth * scale; NVGpaint strokePaint = state->stroke; const NVGpath* path; int i; From d83b35a47a09936b1638469070236b306f4c5a46 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 4 Apr 2025 14:35:02 +0200 Subject: [PATCH 19/33] PenLayer: Fix FBO creation order --- src/penlayer.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/penlayer.cpp b/src/penlayer.cpp index 7333517..bc67581 100644 --- a/src/penlayer.cpp +++ b/src/penlayer.cpp @@ -70,7 +70,6 @@ void PenLayer::setEngine(libscratchcpp::IEngine *newEngine) if (m_engine && QOpenGLContext::currentContext()) { m_projectPenLayers[m_engine] = this; - createFbo(); if (!m_painter) m_painter = std::make_unique(); @@ -80,6 +79,8 @@ void PenLayer::setEngine(libscratchcpp::IEngine *newEngine) m_glF->initializeOpenGLFunctions(); } + createFbo(); + if (m_vao == 0) { // Set up VBO and VAO float vertices[] = { -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f }; @@ -124,8 +125,8 @@ void PenLayer::setHqPen(bool newHqPen) return; m_hqPen = newHqPen; - createFbo(); emit hqPenChanged(); + createFbo(); } void scratchcpprender::PenLayer::clear() From d05b880a8797c8d4f07036c5f03b444e01232041 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 4 Apr 2025 14:35:33 +0200 Subject: [PATCH 20/33] PenLayer: Use the correct context for FBO creation --- src/penlayer.cpp | 28 ++++++++++++++++++++++++++-- src/penlayer.h | 2 ++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/penlayer.cpp b/src/penlayer.cpp index bc67581..fd8c9d6 100644 --- a/src/penlayer.cpp +++ b/src/penlayer.cpp @@ -68,7 +68,14 @@ void PenLayer::setEngine(libscratchcpp::IEngine *newEngine) m_engine = newEngine; - if (m_engine && QOpenGLContext::currentContext()) { + if (!m_glCtx) { + m_glCtx = QOpenGLContext::currentContext(); + + if (m_glCtx) + m_surface = m_glCtx->surface(); + } + + if (m_engine && m_glCtx) { m_projectPenLayers[m_engine] = this; if (!m_painter) @@ -428,6 +435,10 @@ void PenLayer::addPenLayer(libscratchcpp::IEngine *engine, IPenLayer *penLayer) QNanoQuickItemPainter *PenLayer::createItemPainter() const { + m_glCtx = QOpenGLContext::currentContext(); + Q_ASSERT(m_glCtx); + m_surface = m_glCtx->surface(); + Q_ASSERT(m_surface); return new PenLayerPainter; } @@ -441,9 +452,17 @@ void PenLayer::geometryChange(const QRectF &newGeometry, const QRectF &oldGeomet void PenLayer::createFbo() { - if (!QOpenGLContext::currentContext() || !m_engine) + if (!m_glCtx || !m_surface || !m_engine || !m_glF) return; + QOpenGLContext *oldCtx = QOpenGLContext::currentContext(); + QSurface *oldSurface = oldCtx->surface(); + + if (oldCtx != m_glCtx) { + oldCtx->doneCurrent(); + m_glCtx->makeCurrent(m_surface); + } + QOpenGLFramebufferObjectFormat fboFormat; fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); @@ -456,6 +475,11 @@ void PenLayer::createFbo() m_fbo.reset(newFbo); m_texture = Texture(m_fbo->texture(), m_fbo->size()); m_scale = width() / m_engine->stageWidth(); + + if (oldCtx != m_glCtx) { + m_glCtx->doneCurrent(); + oldCtx->makeCurrent(oldSurface); + } } void PenLayer::updateTexture() diff --git a/src/penlayer.h b/src/penlayer.h index d17eadc..ddac04c 100644 --- a/src/penlayer.h +++ b/src/penlayer.h @@ -65,6 +65,8 @@ class PenLayer : public IPenLayer libscratchcpp::IEngine *m_engine = nullptr; bool m_hqPen = false; std::unique_ptr m_fbo; + mutable QOpenGLContext *m_glCtx = nullptr; + mutable QSurface *m_surface = nullptr; double m_scale = 1; std::unique_ptr m_painter; std::unique_ptr m_glF; From a734c959b50f7c70c6891cbdbfbb6c38f5826f80 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 4 Apr 2025 14:35:52 +0200 Subject: [PATCH 21/33] ProjectLoader: Use the correct context for rendering --- src/projectloader.cpp | 25 +++++++++++++++++++++++++ src/projectloader.h | 5 +++++ 2 files changed, 30 insertions(+) diff --git a/src/projectloader.cpp b/src/projectloader.cpp index 3126a5f..729ecf7 100644 --- a/src/projectloader.cpp +++ b/src/projectloader.cpp @@ -210,6 +210,24 @@ void ProjectLoader::timerEvent(QTimerEvent *event) return; if (m_engine) { + QOpenGLContext *oldCtx = QOpenGLContext::currentContext(); + QSurface *oldSurface = nullptr; + + if (!m_glCtx) + m_glCtx = oldCtx; + + if (m_glCtx) { + if (!m_surface) + m_surface = m_glCtx->surface(); + + oldSurface = oldCtx->surface(); + + if (oldCtx != m_glCtx) { + oldCtx->doneCurrent(); + m_glCtx->makeCurrent(m_surface); + } + } + for (Monitor *monitor : m_unpositionedMonitors) monitor->autoPosition(m_engine->monitors()); @@ -230,6 +248,13 @@ void ProjectLoader::timerEvent(QTimerEvent *event) m_renderTimer.restart(); } else m_renderFpsCounter++; + + if (m_glCtx) { + if (oldCtx != m_glCtx) { + m_glCtx->doneCurrent(); + oldCtx->makeCurrent(oldSurface); + } + } } event->accept(); diff --git a/src/projectloader.h b/src/projectloader.h index 8ada11c..249be57 100644 --- a/src/projectloader.h +++ b/src/projectloader.h @@ -12,6 +12,9 @@ Q_MOC_INCLUDE("spritemodel.h"); Q_MOC_INCLUDE("monitormodel.h"); +class QSurface; +class QOpenGLContext; + namespace scratchcpprender { @@ -185,6 +188,8 @@ class ProjectLoader : public QObject std::atomic m_downloadedAssets = 0; std::atomic m_assetCount = 0; std::atomic m_stopLoading = false; + QOpenGLContext *m_glCtx = nullptr; + QSurface *m_surface = nullptr; }; } // namespace scratchcpprender From 84478e4abd818903cdd425665510ee932312b2c6 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 11 Apr 2025 12:56:25 +0200 Subject: [PATCH 22/33] fix #161: Wrap sprites in Item --- src/ProjectPlayer.qml | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/ProjectPlayer.qml b/src/ProjectPlayer.qml index 87f8f7e..8e475e2 100644 --- a/src/ProjectPlayer.qml +++ b/src/ProjectPlayer.qml @@ -263,22 +263,25 @@ ProjectScene { } } - Repeater { - id: sprites - model: loader.sprites - delegate: renderedSprite - } + Item { + // Sprites are wrapped in Item to ensure monitors appear above them + Repeater { + id: sprites + model: loader.sprites + delegate: renderedSprite + } - Repeater { - id: clones - model: ListModel {} - delegate: renderedSprite - } + Repeater { + id: clones + model: ListModel {} + delegate: renderedSprite + } - Repeater { - id: textBubbles - model: loader.sprites - delegate: renderedTextBubble + Repeater { + id: textBubbles + model: loader.sprites + delegate: renderedTextBubble + } } SceneMouseArea { From bac4fc4ec1a3cf8df15db08a575ea07c65d17624 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:05:24 +0200 Subject: [PATCH 23/33] PenLayer: Add refresh() method --- src/penlayer.cpp | 70 ++++++++++++++++----------------- src/penlayer.h | 3 +- test/penlayer/penlayer_test.cpp | 21 ++++++++++ 3 files changed, 58 insertions(+), 36 deletions(-) diff --git a/src/penlayer.cpp b/src/penlayer.cpp index fd8c9d6..5c702c4 100644 --- a/src/penlayer.cpp +++ b/src/penlayer.cpp @@ -86,7 +86,7 @@ void PenLayer::setEngine(libscratchcpp::IEngine *newEngine) m_glF->initializeOpenGLFunctions(); } - createFbo(); + refresh(); if (m_vao == 0) { // Set up VBO and VAO @@ -133,7 +133,7 @@ void PenLayer::setHqPen(bool newHqPen) m_hqPen = newHqPen; emit hqPenChanged(); - createFbo(); + refresh(); } void scratchcpprender::PenLayer::clear() @@ -332,6 +332,38 @@ void PenLayer::stamp(IRenderedTarget *target) update(); } +void PenLayer::refresh() +{ + if (!m_glCtx || !m_surface || !m_engine || !m_glF) + return; + + QOpenGLContext *oldCtx = QOpenGLContext::currentContext(); + QSurface *oldSurface = oldCtx->surface(); + + if (oldCtx != m_glCtx) { + oldCtx->doneCurrent(); + m_glCtx->makeCurrent(m_surface); + } + + QOpenGLFramebufferObjectFormat fboFormat; + fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); + + QOpenGLFramebufferObject *newFbo = new QOpenGLFramebufferObject(width(), height(), fboFormat); + Q_ASSERT(newFbo->isValid()); + + if (m_fbo) + QOpenGLFramebufferObject::blitFramebuffer(newFbo, m_fbo.get()); + + m_fbo.reset(newFbo); + m_texture = Texture(m_fbo->texture(), m_fbo->size()); + m_scale = width() / m_engine->stageWidth(); + + if (oldCtx != m_glCtx) { + m_glCtx->doneCurrent(); + oldCtx->makeCurrent(oldSurface); + } +} + QOpenGLFramebufferObject *PenLayer::framebufferObject() const { return m_fbo.get(); @@ -445,43 +477,11 @@ QNanoQuickItemPainter *PenLayer::createItemPainter() const void PenLayer::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) { if (m_hqPen && newGeometry != oldGeometry) - createFbo(); + refresh(); QNanoQuickItem::geometryChange(newGeometry, oldGeometry); } -void PenLayer::createFbo() -{ - if (!m_glCtx || !m_surface || !m_engine || !m_glF) - return; - - QOpenGLContext *oldCtx = QOpenGLContext::currentContext(); - QSurface *oldSurface = oldCtx->surface(); - - if (oldCtx != m_glCtx) { - oldCtx->doneCurrent(); - m_glCtx->makeCurrent(m_surface); - } - - QOpenGLFramebufferObjectFormat fboFormat; - fboFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); - - QOpenGLFramebufferObject *newFbo = new QOpenGLFramebufferObject(width(), height(), fboFormat); - Q_ASSERT(newFbo->isValid()); - - if (m_fbo) - QOpenGLFramebufferObject::blitFramebuffer(newFbo, m_fbo.get()); - - m_fbo.reset(newFbo); - m_texture = Texture(m_fbo->texture(), m_fbo->size()); - m_scale = width() / m_engine->stageWidth(); - - if (oldCtx != m_glCtx) { - m_glCtx->doneCurrent(); - oldCtx->makeCurrent(oldSurface); - } -} - void PenLayer::updateTexture() { if (!m_fbo) diff --git a/src/penlayer.h b/src/penlayer.h index ddac04c..53c614c 100644 --- a/src/penlayer.h +++ b/src/penlayer.h @@ -39,6 +39,8 @@ class PenLayer : public IPenLayer void drawLine(const PenAttributes &penAttributes, double x0, double y0, double x1, double y1) override; void stamp(IRenderedTarget *target) override; + Q_INVOKABLE void refresh(); + QOpenGLFramebufferObject *framebufferObject() const override; QRgb colorAtScratchPoint(double x, double y) const override; @@ -56,7 +58,6 @@ class PenLayer : public IPenLayer void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override; private: - void createFbo(); void updateTexture(); static std::unordered_map m_projectPenLayers; diff --git a/test/penlayer/penlayer_test.cpp b/test/penlayer/penlayer_test.cpp index 95fd9d2..4ae36e1 100644 --- a/test/penlayer/penlayer_test.cpp +++ b/test/penlayer/penlayer_test.cpp @@ -148,6 +148,27 @@ TEST_F(PenLayerTest, FramebufferObject) ASSERT_EQ(fbo->format().samples(), 0); } +TEST_F(PenLayerTest, Refresh) +{ + PenLayer penLayer; + + EngineMock engine1, engine2, engine3; + penLayer.setWidth(480); + penLayer.setHeight(360); + EXPECT_CALL(engine1, stageWidth()).WillOnce(Return(480)); + penLayer.setEngine(&engine1); + + penLayer.setWidth(500); + penLayer.setHeight(400); + + EXPECT_CALL(engine1, stageWidth()).WillOnce(Return(500)); + penLayer.refresh(); + + QOpenGLFramebufferObject *fbo = penLayer.framebufferObject(); + ASSERT_EQ(fbo->width(), 500); + ASSERT_EQ(fbo->height(), 400); +} + TEST_F(PenLayerTest, GetProjectPenLayer) { PenLayer penLayer; From d09dbf2233c9e181cdd5d8e7b0b1fbba31598ae7 Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:05:49 +0200 Subject: [PATCH 24/33] fix #164: Refresh pen layer when stage size changes --- src/ProjectPlayer.qml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ProjectPlayer.qml b/src/ProjectPlayer.qml index 8e475e2..b458ed4 100644 --- a/src/ProjectPlayer.qml +++ b/src/ProjectPlayer.qml @@ -192,6 +192,16 @@ ProjectScene { scale: hqPen ? 1 : stageScale transformOrigin: Item.TopLeft visible: !priv.loading + + onWidthChanged: { + if (!hqPen) + refresh(); + } + + onHeightChanged: { + if (!hqPen) + refresh(); + } } Component { From 0e06b3f0e6a4a74b8b2329e69e44f59e95491cdd Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 13 Apr 2025 18:46:35 +0200 Subject: [PATCH 25/33] PenLayer: Disable scissor test when recreating FBO --- src/penlayer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/penlayer.cpp b/src/penlayer.cpp index 5c702c4..b93cb30 100644 --- a/src/penlayer.cpp +++ b/src/penlayer.cpp @@ -351,8 +351,11 @@ void PenLayer::refresh() QOpenGLFramebufferObject *newFbo = new QOpenGLFramebufferObject(width(), height(), fboFormat); Q_ASSERT(newFbo->isValid()); - if (m_fbo) + if (m_fbo) { + m_glF->glDisable(GL_SCISSOR_TEST); QOpenGLFramebufferObject::blitFramebuffer(newFbo, m_fbo.get()); + m_glF->glEnable(GL_SCISSOR_TEST); + } m_fbo.reset(newFbo); m_texture = Texture(m_fbo->texture(), m_fbo->size()); From 48a0864158836345c371bd196c178e13fa233c3a Mon Sep 17 00:00:00 2001 From: adazem009 <68537469+adazem009@users.noreply.github.com> Date: Sun, 10 Aug 2025 18:51:11 +0200 Subject: [PATCH 26/33] Update libscratchcpp to latest master --- libscratchcpp | 2 +- test/mocks/enginemock.h | 32 ++++++++++++++------------------ 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/libscratchcpp b/libscratchcpp index 8ed5355..3619dcc 160000 --- a/libscratchcpp +++ b/libscratchcpp @@ -1 +1 @@ -Subproject commit 8ed5355643f3b92cd672cda7673dba1b4105bbb1 +Subproject commit 3619dccdfe3f22c0257a36fade15d8713ca78a79 diff --git a/test/mocks/enginemock.h b/test/mocks/enginemock.h index 5a9714e..8f63633 100644 --- a/test/mocks/enginemock.h +++ b/test/mocks/enginemock.h @@ -8,7 +8,7 @@ using namespace libscratchcpp; namespace scratchcpprender { -using ScriptMap = std::unordered_map, std::shared_ptr