diff --git a/CMakeLists.txt b/CMakeLists.txt index c675f56..e200f12 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.10.0 LANGUAGES CXX) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOUIC ON) diff --git a/libscratchcpp b/libscratchcpp index d9e0e11..6c5fc94 160000 --- a/libscratchcpp +++ b/libscratchcpp @@ -1 +1 @@ -Subproject commit d9e0e1124174a23d9f2783e756027da2f57d67b5 +Subproject commit 6c5fc94c34ccc9b10cbfdd7a088201643c010518 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e812296..aa7735d 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 @@ -76,13 +78,6 @@ qt_add_qml_module(scratchcpp-render effecttransform.h ) -if (NOT LIBSCRATCHCPP_USE_LLVM) - target_sources(scratchcpp-render - PRIVATE - blocks/penblocks.cpp - blocks/penblocks.h) -endif() - list(APPEND QML_IMPORT_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) list(REMOVE_DUPLICATES QML_IMPORT_PATH) set(QML_IMPORT_PATH ${QML_IMPORT_PATH} CACHE STRING "" FORCE) diff --git a/src/ProjectPlayer.qml b/src/ProjectPlayer.qml index 1653eb2..b458ed4 100644 --- a/src/ProjectPlayer.qml +++ b/src/ProjectPlayer.qml @@ -27,6 +27,7 @@ ProjectScene { readonly property Rectangle stageRect: contentRect signal loaded() signal failedToLoad() + signal loadingAborted() id: root engine: loader.engine @@ -39,6 +40,11 @@ ProjectScene { loader.fileName = fileName; } + function stopLoading() { + if (priv.loading) + loader.stopLoading(); + } + QtObject { id: priv property bool loading: false @@ -54,10 +60,27 @@ ProjectScene { onLoadingFinished: { priv.loading = false; - if(loadStatus) - loaded(); - else - failedToLoad(); + switch (loadStatus) { + case ProjectLoader.Loaded: + loaded(); + break; + + case ProjectLoader.Failed: + failedToLoad(); + break; + + case ProjectLoader.Aborted: + loadingAborted(); + break; + + default: + break; + } + } + + onLoadStatusChanged: { + if (loadStatus === ProjectLoader.Loading) + priv.loading = true; } onStageChanged: stage.loadCostume(); @@ -150,6 +173,7 @@ ProjectScene { mouseArea: sceneMouseArea stageScale: root.stageScale onStageModelChanged: stageModel.renderedTarget = this + Component.onCompleted: stageModel.penLayer = projectPenLayer } Loader { @@ -168,6 +192,16 @@ ProjectScene { scale: hqPen ? 1 : stageScale transformOrigin: Item.TopLeft visible: !priv.loading + + onWidthChanged: { + if (!hqPen) + refresh(); + } + + onHeightChanged: { + if (!hqPen) + refresh(); + } } Component { @@ -239,22 +273,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 { diff --git a/src/blocks/penblocks.cpp b/src/blocks/penblocks.cpp deleted file mode 100644 index 8905786..0000000 --- a/src/blocks/penblocks.cpp +++ /dev/null @@ -1,563 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later - -#include -#include -#include -#include - -#include "penblocks.h" -#include "penlayer.h" -#include "penstate.h" -#include "spritemodel.h" -#include "stagemodel.h" - -using namespace scratchcpprender; -using namespace libscratchcpp; - -// Pen size range: https://github.com/scratchfoundation/scratch-vm/blob/8dbcc1fc8f8d8c4f1e40629fe8a388149d6dfd1c/src/extensions/scratch3_pen/index.js#L100-L102 -static const double PEN_SIZE_MIN = 1; -static const double PEN_SIZE_MAX = 1200; - -static const double COLOR_PARAM_MIN = 0; -static const double COLOR_PARAM_MAX = 100; - -const std::unordered_map - PenBlocks::COLOR_PARAM_MAP = { { "color", ColorParam::COLOR }, { "saturation", ColorParam::SATURATION }, { "brightness", ColorParam::BRIGHTNESS }, { "transparency", ColorParam::TRANSPARENCY } }; - -std::string PenBlocks::name() const -{ - return "pen"; -} - -std::string PenBlocks::description() const -{ - return "Pen blocks"; -} - -void PenBlocks::registerBlocks(IEngine *engine) -{ - // Blocks - engine->addCompileFunction(this, "pen_clear", &compileClear); - engine->addCompileFunction(this, "pen_stamp", &compileStamp); - engine->addCompileFunction(this, "pen_penDown", &compilePenDown); - engine->addCompileFunction(this, "pen_penUp", &compilePenUp); - engine->addCompileFunction(this, "pen_setPenColorToColor", &compileSetPenColorToColor); - engine->addCompileFunction(this, "pen_changePenColorParamBy", &compileChangePenColorParamBy); - engine->addCompileFunction(this, "pen_setPenColorParamTo", &compileSetPenColorParamTo); - engine->addCompileFunction(this, "pen_changePenSizeBy", &compileChangePenSizeBy); - engine->addCompileFunction(this, "pen_setPenSizeTo", &compileSetPenSizeTo); - engine->addCompileFunction(this, "pen_changePenShadeBy", &compileChangePenShadeBy); - engine->addCompileFunction(this, "pen_setPenShadeToNumber", &compileSetPenShadeToNumber); - engine->addCompileFunction(this, "pen_changePenHueBy", &compileChangePenHueBy); - engine->addCompileFunction(this, "pen_setPenHueToNumber", &compileSetPenHueToNumber); - - // Inputs - engine->addInput(this, "COLOR", COLOR); - engine->addInput(this, "COLOR_PARAM", COLOR_PARAM); - engine->addInput(this, "VALUE", VALUE); - engine->addInput(this, "SIZE", SIZE); - engine->addInput(this, "SHADE", SHADE); - engine->addInput(this, "HUE", HUE); -} - -void PenBlocks::compileClear(Compiler *compiler) -{ - compiler->addFunctionCall(&clear); -} - -void PenBlocks::compileStamp(Compiler *compiler) -{ - compiler->addFunctionCall(&stamp); -} - -void PenBlocks::compilePenDown(Compiler *compiler) -{ - compiler->addFunctionCall(&penDown); -} - -void PenBlocks::compilePenUp(Compiler *compiler) -{ - compiler->addFunctionCall(&penUp); -} - -void PenBlocks::compileSetPenColorToColor(libscratchcpp::Compiler *compiler) -{ - compiler->addInput(COLOR); - compiler->addFunctionCall(&setPenColorToColor); -} - -void PenBlocks::compileChangePenColorParamBy(libscratchcpp::Compiler *compiler) -{ - Input *input = compiler->input(COLOR_PARAM); - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - BlockFunc f = nullptr; - - if (value == "color") - f = &changePenColorBy; - else if (value == "saturation") - f = &changePenSaturationBy; - else if (value == "brightness") - f = &changePenBrightnessBy; - else if (value == "transparency") - f = &changePenTransparencyBy; - - if (f) { - compiler->addInput(VALUE); - compiler->addFunctionCall(f); - } - } else { - compiler->addInput(input); - compiler->addInput(VALUE); - compiler->addFunctionCall(&changePenColorParamBy); - } -} - -void PenBlocks::compileSetPenColorParamTo(Compiler *compiler) -{ - Input *input = compiler->input(COLOR_PARAM); - - if (input->pointsToDropdownMenu()) { - std::string value = input->selectedMenuItem(); - BlockFunc f = nullptr; - - if (value == "color") - f = &setPenColorTo; - else if (value == "saturation") - f = &setPenSaturationTo; - else if (value == "brightness") - f = &setPenBrightnessTo; - else if (value == "transparency") - f = &setPenTransparencyTo; - - if (f) { - compiler->addInput(VALUE); - compiler->addFunctionCall(f); - } - } else { - compiler->addInput(input); - compiler->addInput(VALUE); - compiler->addFunctionCall(&setPenColorParamTo); - } -} - -void PenBlocks::compileChangePenSizeBy(libscratchcpp::Compiler *compiler) -{ - compiler->addInput(SIZE); - compiler->addFunctionCall(&changePenSizeBy); -} - -void PenBlocks::compileSetPenSizeTo(libscratchcpp::Compiler *compiler) -{ - compiler->addInput(SIZE); - compiler->addFunctionCall(&setPenSizeTo); -} - -void PenBlocks::compileChangePenShadeBy(Compiler *compiler) -{ - compiler->addInput(SHADE); - compiler->addFunctionCall(&changePenShadeBy); -} - -void PenBlocks::compileSetPenShadeToNumber(libscratchcpp::Compiler *compiler) -{ - compiler->addInput(SHADE); - compiler->addFunctionCall(&setPenShadeToNumber); -} - -void PenBlocks::compileChangePenHueBy(libscratchcpp::Compiler *compiler) -{ - compiler->addInput(HUE); - compiler->addFunctionCall(&changePenHueBy); -} - -void PenBlocks::compileSetPenHueToNumber(libscratchcpp::Compiler *compiler) -{ - compiler->addInput(HUE); - compiler->addFunctionCall(&setPenHueToNumber); -} - -unsigned int PenBlocks::clear(VirtualMachine *vm) -{ - IPenLayer *penLayer = PenLayer::getProjectPenLayer(vm->engine()); - - if (penLayer) { - penLayer->clear(); - vm->engine()->requestRedraw(); - } - - return 0; -} - -unsigned int PenBlocks::stamp(libscratchcpp::VirtualMachine *vm) -{ - IPenLayer *penLayer = PenLayer::getProjectPenLayer(vm->engine()); - - if (penLayer) { - Target *target = vm->target(); - IRenderedTarget *renderedTarget = nullptr; - - if (target->isStage()) { - IStageHandler *iface = static_cast(target)->getInterface(); - renderedTarget = static_cast(iface)->renderedTarget(); - } else { - ISpriteHandler *iface = static_cast(target)->getInterface(); - renderedTarget = static_cast(iface)->renderedTarget(); - } - - penLayer->stamp(renderedTarget); - vm->engine()->requestRedraw(); - } - - return 0; -} - -unsigned int PenBlocks::penDown(VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) - model->setPenDown(true); - - return 0; -} - -unsigned int PenBlocks::penUp(libscratchcpp::VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) - model->setPenDown(false); - - return 0; -} - -unsigned int PenBlocks::changePenSizeBy(libscratchcpp::VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) - model->penAttributes().diameter = std::clamp(model->penAttributes().diameter + vm->getInput(0, 1)->toDouble(), PEN_SIZE_MIN, PEN_SIZE_MAX); - - return 1; -} - -unsigned int PenBlocks::setPenSizeTo(libscratchcpp::VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) - model->penAttributes().diameter = std::clamp(vm->getInput(0, 1)->toDouble(), PEN_SIZE_MIN, PEN_SIZE_MAX); - - return 1; -} - -unsigned int PenBlocks::changePenShadeBy(libscratchcpp::VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) { - PenState &penState = model->penState(); - setPenShade(penState.shade + vm->getInput(0, 1)->toDouble(), penState); - } - - return 1; -} - -unsigned int PenBlocks::setPenShadeToNumber(libscratchcpp::VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) - setPenShade(vm->getInput(0, 1)->toInt(), model->penState()); - - return 1; -} - -unsigned int PenBlocks::changePenHueBy(libscratchcpp::VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) { - PenState &penState = model->penState(); - const double colorChange = vm->getInput(0, 1)->toDouble() / 2; - setOrChangeColorParam(ColorParam::COLOR, colorChange, penState, true); - legacyUpdatePenColor(penState); - } - - return 1; -} - -unsigned int PenBlocks::setPenHueToNumber(libscratchcpp::VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) { - PenState &penState = model->penState(); - const double colorValue = vm->getInput(0, 1)->toDouble() / 2; - setOrChangeColorParam(ColorParam::COLOR, colorValue, penState, false); - penState.transparency = 0; - legacyUpdatePenColor(penState); - } - - return 1; -} - -unsigned int PenBlocks::setPenColorToColor(libscratchcpp::VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) { - const Value *value = vm->getInput(0, 1); - std::string stringValue; - PenState &penState = model->penState(); - QColor newColor; - - if (value->isString()) - stringValue = value->toString(); - - if (!stringValue.empty() && stringValue[0] == '#') { - bool valid = false; - - if (stringValue.size() <= 7) // #RRGGBB - { - newColor = QColor::fromString(stringValue); - valid = newColor.isValid(); - } - - if (!valid) - newColor = Qt::black; - - } else - newColor = QColor::fromRgba(static_cast(value->toLong())); - - QColor hsv = newColor.toHsv(); - penState.color = (hsv.hue() / 360.0) * 100; - penState.saturation = hsv.saturationF() * 100; - penState.brightness = hsv.valueF() * 100; - - if (newColor.alpha() > 0) - penState.transparency = 100 * (1 - newColor.alpha() / 255.0); - else - penState.transparency = 0; - - penState.updateColor(); - - // Set the legacy "shade" value the same way Scratch 2 did. - penState.shade = penState.brightness / 2; - } - - return 1; -} - -unsigned int PenBlocks::changePenColorParamBy(VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) { - const auto it = COLOR_PARAM_MAP.find(vm->getInput(0, 2)->toString()); - - if (it == COLOR_PARAM_MAP.cend()) - return 2; - - setOrChangeColorParam(it->second, vm->getInput(1, 2)->toDouble(), model->penState(), true); - } - - return 2; -} - -unsigned int PenBlocks::changePenColorBy(VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) - setOrChangeColorParam(ColorParam::COLOR, vm->getInput(0, 1)->toDouble(), model->penState(), true); - - return 1; -} - -unsigned int PenBlocks::changePenSaturationBy(VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) - setOrChangeColorParam(ColorParam::SATURATION, vm->getInput(0, 1)->toDouble(), model->penState(), true); - - return 1; -} - -unsigned int PenBlocks::changePenBrightnessBy(VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) - setOrChangeColorParam(ColorParam::BRIGHTNESS, vm->getInput(0, 1)->toDouble(), model->penState(), true); - - return 1; -} - -unsigned int PenBlocks::changePenTransparencyBy(VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) - setOrChangeColorParam(ColorParam::TRANSPARENCY, vm->getInput(0, 1)->toDouble(), model->penState(), true); - - return 1; -} - -unsigned int PenBlocks::setPenColorParamTo(VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) { - const auto it = COLOR_PARAM_MAP.find(vm->getInput(0, 2)->toString()); - - if (it == COLOR_PARAM_MAP.cend()) - return 2; - - setOrChangeColorParam(it->second, vm->getInput(1, 2)->toDouble(), model->penState(), false); - } - - return 2; -} - -unsigned int PenBlocks::setPenColorTo(VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) - setOrChangeColorParam(ColorParam::COLOR, vm->getInput(0, 1)->toDouble(), model->penState(), false); - - return 1; -} - -unsigned int PenBlocks::setPenSaturationTo(VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) - setOrChangeColorParam(ColorParam::SATURATION, vm->getInput(0, 1)->toDouble(), model->penState(), false); - - return 1; -} - -unsigned int PenBlocks::setPenBrightnessTo(VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) - setOrChangeColorParam(ColorParam::BRIGHTNESS, vm->getInput(0, 1)->toDouble(), model->penState(), false); - - return 1; -} - -unsigned int PenBlocks::setPenTransparencyTo(VirtualMachine *vm) -{ - SpriteModel *model = getSpriteModel(vm); - - if (model) - setOrChangeColorParam(ColorParam::TRANSPARENCY, vm->getInput(0, 1)->toDouble(), model->penState(), false); - - return 1; -} - -SpriteModel *PenBlocks::getSpriteModel(libscratchcpp::VirtualMachine *vm) -{ - Target *target = vm->target(); - - if (!target || target->isStage()) - return nullptr; - - Sprite *sprite = static_cast(target); - SpriteModel *model = static_cast(sprite->getInterface()); - return model; -} - -void PenBlocks::setOrChangeColorParam(ColorParam param, double value, PenState &penState, bool change) -{ - switch (param) { - case ColorParam::COLOR: - penState.color = wrapClamp(value + (change ? penState.color : 0), 0, 100); - break; - - case ColorParam::SATURATION: - penState.saturation = std::clamp(value + (change ? penState.saturation : 0), COLOR_PARAM_MIN, COLOR_PARAM_MAX); - break; - - case ColorParam::BRIGHTNESS: - penState.brightness = std::clamp(value + (change ? penState.brightness : 0), COLOR_PARAM_MIN, COLOR_PARAM_MAX); - break; - - case ColorParam::TRANSPARENCY: - penState.transparency = std::clamp(value + (change ? penState.transparency : 0), COLOR_PARAM_MIN, COLOR_PARAM_MAX); - break; - - default: - assert(false); - return; - } - - penState.updateColor(); -} - -void PenBlocks::setPenShade(int shade, PenState &penState) -{ - // https://github.com/scratchfoundation/scratch-vm/blob/8dbcc1fc8f8d8c4f1e40629fe8a388149d6dfd1c/src/extensions/scratch3_pen/index.js#L718-L730 - // Wrap clamp the new shade value the way Scratch 2 did - shade = shade % 200; - - if (shade < 0) - shade += 200; - - // And store the shade that was used to compute this new color for later use - penState.shade = shade; - - legacyUpdatePenColor(penState); -} - -void PenBlocks::legacyUpdatePenColor(PenState &penState) -{ - // https://github.com/scratchfoundation/scratch-vm/blob/8dbcc1fc8f8d8c4f1e40629fe8a388149d6dfd1c/src/extensions/scratch3_pen/index.js#L750-L767 - // Create the new color in RGB using the scratch 2 "shade" model - QRgb rgb = QColor::fromHsvF(penState.color / 100, 1, 1).rgb(); - const double shade = (penState.shade > 100) ? 200 - penState.shade : penState.shade; - - if (shade < 50) - rgb = mixRgb(0, rgb, (10 + shade) / 60); - else - rgb = mixRgb(rgb, 0xFFFFFF, (shade - 50) / 60); - - // Update the pen state according to new color - QColor hsv = QColor::fromRgb(rgb).toHsv(); - penState.color = 100 * hsv.hueF(); - penState.saturation = 100 * hsv.saturationF(); - penState.brightness = 100 * hsv.valueF(); - - penState.updateColor(); -} - -double PenBlocks::wrapClamp(double n, double min, double max) -{ - // TODO: Move this to a separate class - const double range = max - min /*+ 1*/; - return n - (std::floor((n - min) / range) * range); -} - -QRgb PenBlocks::mixRgb(QRgb rgb0, QRgb rgb1, double fraction1) -{ - // https://github.com/scratchfoundation/scratch-vm/blob/a4f095db5e03e072ba222fe721eeeb543c9b9c15/src/util/color.js#L192-L201 - // https://github.com/scratchfoundation/scratch-flash/blob/2e4a402ceb205a042887f54b26eebe1c2e6da6c0/src/util/Color.as#L75-L89 - if (fraction1 <= 0) - return rgb0; - - if (fraction1 >= 1) - return rgb1; - - const double fraction0 = 1 - fraction1; - const int r = static_cast(((fraction0 * qRed(rgb0)) + (fraction1 * qRed(rgb1)))) & 255; - const int g = static_cast(((fraction0 * qGreen(rgb0)) + (fraction1 * qGreen(rgb1)))) & 255; - const int b = static_cast(((fraction0 * qBlue(rgb0)) + (fraction1 * qBlue(rgb1)))) & 255; - return qRgb(r, g, b); -} diff --git a/src/blocks/penblocks.h b/src/blocks/penblocks.h deleted file mode 100644 index 0feb01a..0000000 --- a/src/blocks/penblocks.h +++ /dev/null @@ -1,90 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later - -#pragma once - -#include -#include - -namespace scratchcpprender -{ - -class SpriteModel; -class PenState; - -class PenBlocks : public libscratchcpp::IExtension -{ - public: - enum Inputs - { - COLOR, - COLOR_PARAM, - VALUE, - SIZE, - SHADE, - HUE - }; - - std::string name() const override; - std::string description() const override; - - void registerBlocks(libscratchcpp::IEngine *engine) override; - - static void compileClear(libscratchcpp::Compiler *compiler); - static void compileStamp(libscratchcpp::Compiler *compiler); - static void compilePenDown(libscratchcpp::Compiler *compiler); - static void compilePenUp(libscratchcpp::Compiler *compiler); - static void compileSetPenColorToColor(libscratchcpp::Compiler *compiler); - static void compileChangePenColorParamBy(libscratchcpp::Compiler *compiler); - static void compileSetPenColorParamTo(libscratchcpp::Compiler *compiler); - static void compileChangePenSizeBy(libscratchcpp::Compiler *compiler); - static void compileSetPenSizeTo(libscratchcpp::Compiler *compiler); - static void compileChangePenShadeBy(libscratchcpp::Compiler *compiler); - static void compileSetPenShadeToNumber(libscratchcpp::Compiler *compiler); - static void compileChangePenHueBy(libscratchcpp::Compiler *compiler); - static void compileSetPenHueToNumber(libscratchcpp::Compiler *compiler); - - static unsigned int clear(libscratchcpp::VirtualMachine *vm); - static unsigned int stamp(libscratchcpp::VirtualMachine *vm); - static unsigned int penDown(libscratchcpp::VirtualMachine *vm); - static unsigned int penUp(libscratchcpp::VirtualMachine *vm); - static unsigned int setPenColorToColor(libscratchcpp::VirtualMachine *vm); - - static unsigned int changePenColorParamBy(libscratchcpp::VirtualMachine *vm); - static unsigned int changePenColorBy(libscratchcpp::VirtualMachine *vm); - static unsigned int changePenSaturationBy(libscratchcpp::VirtualMachine *vm); - static unsigned int changePenBrightnessBy(libscratchcpp::VirtualMachine *vm); - static unsigned int changePenTransparencyBy(libscratchcpp::VirtualMachine *vm); - - static unsigned int setPenColorParamTo(libscratchcpp::VirtualMachine *vm); - static unsigned int setPenColorTo(libscratchcpp::VirtualMachine *vm); - static unsigned int setPenSaturationTo(libscratchcpp::VirtualMachine *vm); - static unsigned int setPenBrightnessTo(libscratchcpp::VirtualMachine *vm); - static unsigned int setPenTransparencyTo(libscratchcpp::VirtualMachine *vm); - - static unsigned int changePenSizeBy(libscratchcpp::VirtualMachine *vm); - static unsigned int setPenSizeTo(libscratchcpp::VirtualMachine *vm); - static unsigned int changePenShadeBy(libscratchcpp::VirtualMachine *vm); - static unsigned int setPenShadeToNumber(libscratchcpp::VirtualMachine *vm); - static unsigned int changePenHueBy(libscratchcpp::VirtualMachine *vm); - static unsigned int setPenHueToNumber(libscratchcpp::VirtualMachine *vm); - - private: - enum class ColorParam - { - COLOR, - SATURATION, - BRIGHTNESS, - TRANSPARENCY - }; - - static SpriteModel *getSpriteModel(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); - static double wrapClamp(double n, double min, double max); - static QRgb mixRgb(QRgb rgb0, QRgb rgb1, double fraction1); - - static const std::unordered_map COLOR_PARAM_MAP; -}; - -} // namespace scratchcpprender diff --git a/src/global_functions.cpp b/src/global_functions.cpp index de18977..9338197 100644 --- a/src/global_functions.cpp +++ b/src/global_functions.cpp @@ -10,6 +10,10 @@ void scratchcpprender::init() QSurfaceFormat format = QSurfaceFormat::defaultFormat(); format.setSwapInterval(0); +#ifdef Q_OS_MACOS + format.setProfile(QSurfaceFormat::CoreProfile); + format.setVersion(3, 2); +#endif QSurfaceFormat::setDefaultFormat(format); } diff --git a/src/irenderedtarget.h b/src/irenderedtarget.h index 14cb457..662e95b 100644 --- a/src/irenderedtarget.h +++ b/src/irenderedtarget.h @@ -92,8 +92,8 @@ class IRenderedTarget : public QNanoQuickItem virtual QRgb colorAtScratchPoint(double x, double y) const = 0; virtual bool touchingClones(const std::vector &clones) const = 0; - virtual bool touchingColor(const libscratchcpp::Value &color) const = 0; - virtual bool touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const = 0; + virtual bool touchingColor(libscratchcpp::Rgb color) const = 0; + virtual bool touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const = 0; }; } // namespace scratchcpprender diff --git a/src/listmonitormodel.cpp b/src/listmonitormodel.cpp index b88b3e9..5c09847 100644 --- a/src/listmonitormodel.cpp +++ b/src/listmonitormodel.cpp @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-or-later -#include +#include #include "listmonitormodel.h" #include "listmonitorlistmodel.h" @@ -18,13 +18,10 @@ ListMonitorModel::ListMonitorModel(libscratchcpp::IExtension *extension, QObject m_listModel = new ListMonitorListModel(this); } -void ListMonitorModel::onValueChanged(const libscratchcpp::VirtualMachine *vm) +void ListMonitorModel::onValueChanged(const libscratchcpp::Value &value) { - if (vm->registerCount() == 1) { - long index = vm->getInput(0, 1)->toLong(); - libscratchcpp::List *list = vm->lists()[index]; - m_listModel->setList(list, m_minIndex, m_maxIndex); - } + libscratchcpp::List *list = (libscratchcpp::List *)value.toPointer(); + m_listModel->setList(list, m_minIndex, m_maxIndex); } MonitorModel::Type ListMonitorModel::type() const diff --git a/src/listmonitormodel.h b/src/listmonitormodel.h index acda897..3452b7e 100644 --- a/src/listmonitormodel.h +++ b/src/listmonitormodel.h @@ -25,7 +25,7 @@ class ListMonitorModel : public MonitorModel ListMonitorModel(QObject *parent = nullptr); ListMonitorModel(libscratchcpp::IExtension *extension, QObject *parent = nullptr); - void onValueChanged(const libscratchcpp::VirtualMachine *vm) override; + void onValueChanged(const libscratchcpp::Value &value) override; Type type() const override; diff --git a/src/monitormodel.h b/src/monitormodel.h index 8ad7f73..bf1120b 100644 --- a/src/monitormodel.h +++ b/src/monitormodel.h @@ -47,7 +47,7 @@ class MonitorModel void init(libscratchcpp::Monitor *monitor) override final; - virtual void onValueChanged(const libscratchcpp::VirtualMachine *vm) override { } + virtual void onValueChanged(const libscratchcpp::Value &value) override { } void onVisibleChanged(bool visible) override final; void onXChanged(int x) override final; void onYChanged(int y) override final; diff --git a/src/penlayer.cpp b/src/penlayer.cpp index 0c95759..b93cb30 100644 --- a/src/penlayer.cpp +++ b/src/penlayer.cpp @@ -68,9 +68,15 @@ 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; - createFbo(); if (!m_painter) m_painter = std::make_unique(); @@ -80,6 +86,8 @@ void PenLayer::setEngine(libscratchcpp::IEngine *newEngine) m_glF->initializeOpenGLFunctions(); } + refresh(); + 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 +132,8 @@ void PenLayer::setHqPen(bool newHqPen) return; m_hqPen = newHqPen; - createFbo(); emit hqPenChanged(); + refresh(); } void scratchcpprender::PenLayer::clear() @@ -273,6 +281,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); @@ -322,6 +332,41 @@ 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) { + 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()); + m_scale = width() / m_engine->stageWidth(); + + if (oldCtx != m_glCtx) { + m_glCtx->doneCurrent(); + oldCtx->makeCurrent(oldSurface); + } +} + QOpenGLFramebufferObject *PenLayer::framebufferObject() const { return m_fbo.get(); @@ -425,36 +470,21 @@ 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; } void PenLayer::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) { if (m_hqPen && newGeometry != oldGeometry) - createFbo(); + refresh(); QNanoQuickItem::geometryChange(newGeometry, oldGeometry); } -void PenLayer::createFbo() -{ - if (!QOpenGLContext::currentContext() || !m_engine) - return; - - 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(); -} - void PenLayer::updateTexture() { if (!m_fbo) diff --git a/src/penlayer.h b/src/penlayer.h index d17eadc..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; @@ -65,6 +66,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; diff --git a/src/projectloader.cpp b/src/projectloader.cpp index d18f450..1453620 100644 --- a/src/projectloader.cpp +++ b/src/projectloader.cpp @@ -12,7 +12,7 @@ #include "valuemonitormodel.h" #include "listmonitormodel.h" #include "renderedtarget.h" -#include "blocks/penblocks.h" +// #include "blocks/penblocks.h" using namespace scratchcpprender; using namespace libscratchcpp; @@ -37,18 +37,13 @@ ProjectLoader::ProjectLoader(QObject *parent) : initTimer(); m_renderTimer.start(); -#ifndef USE_LLVM - // Register pen blocks - ScratchConfiguration::registerExtension(std::make_shared()); -#endif + // TODO: Register pen blocks + // ScratchConfiguration::registerExtension(std::make_shared()); } ProjectLoader::~ProjectLoader() { - m_stopLoading = true; - - if (m_loadThread.isRunning()) - m_loadThread.waitForFinished(); + stopLoading(); for (SpriteModel *sprite : m_sprites) sprite->deleteLater(); @@ -61,8 +56,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 +70,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 +85,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 +129,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 +177,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 +188,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(); } @@ -199,6 +208,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()); @@ -219,6 +246,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(); @@ -276,12 +310,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 +368,7 @@ void ProjectLoader::load() if (m_stopLoading) { m_engineMutex.unlock(); + m_loadStatus = LoadStatus::Aborted; emit fileNameChanged(); emit loadStatusChanged(); emit loadingFinished(); @@ -364,7 +403,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..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 { @@ -23,7 +26,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 +46,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 +170,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; @@ -172,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 diff --git a/src/renderedtarget.cpp b/src/renderedtarget.cpp index 101e236..b2130f6 100644 --- a/src/renderedtarget.cpp +++ b/src/renderedtarget.cpp @@ -668,12 +668,12 @@ bool RenderedTarget::touchingClones(const std::vector & return false; } -bool RenderedTarget::touchingColor(const Value &color) const +bool RenderedTarget::touchingColor(Rgb color) const { - return touchingColor(color, false, Value()); + return touchingColor(color, false, 0); } -bool RenderedTarget::touchingColor(const Value &color, const Value &mask) const +bool RenderedTarget::touchingColor(Rgb color, Rgb mask) const { return touchingColor(color, true, mask); } @@ -873,13 +873,13 @@ CpuTextureManager *RenderedTarget::textureManager() const return m_textureManager.get(); } -bool RenderedTarget::touchingColor(const libscratchcpp::Value &color, bool hasMask, const libscratchcpp::Value &mask) const +bool RenderedTarget::touchingColor(Rgb color, bool hasMask, Rgb mask) const { // https://github.com/scratchfoundation/scratch-render/blob/0a04c2fb165f5c20406ec34ab2ea5682ae45d6e0/src/RenderWebGL.js#L775-L841 if (!m_engine) return false; - QRgb rgb = convertColor(color); + QRgb rgb = qRgb(qRed(color), qGreen(color), qBlue(color)); // ignore alpha QRgb mask3b; double ghostValue = 0; @@ -891,7 +891,7 @@ bool RenderedTarget::touchingColor(const libscratchcpp::Value &color, bool hasMa m_graphicEffectMask &= ~ShaderManager::Effect::Ghost; } - mask3b = convertColor(mask); + mask3b = qRgb(qRed(mask), qGreen(mask), qBlue(mask)); // ignore alpha } std::vector targets; @@ -1065,33 +1065,6 @@ void RenderedTarget::clampRect(Rect &rect, double left, double right, double bot rect.setTop(std::max(rect.top(), bottom)); } -QRgb RenderedTarget::convertColor(const libscratchcpp::Value &color) -{ - // TODO: Remove this after libscratchcpp starts converting colors (it still needs to be converted to RGB here) - std::string stringValue; - - if (color.isString()) - stringValue = color.toString(); - - if (!stringValue.empty() && stringValue[0] == '#') { - bool valid = false; - QColor color; - - if (stringValue.size() <= 7) // #RRGGBB - { - color = QColor::fromString(stringValue); - valid = color.isValid(); - } - - if (!valid) - color = Qt::black; - - return color.rgb(); - - } else - return QColor::fromRgba(static_cast(color.toLong())).rgb(); -} - bool RenderedTarget::colorMatches(QRgb a, QRgb b) { // https://github.com/scratchfoundation/scratch-render/blob/0a04c2fb165f5c20406ec34ab2ea5682ae45d6e0/src/RenderWebGL.js#L77-L81 diff --git a/src/renderedtarget.h b/src/renderedtarget.h index 9550762..5a94bb2 100644 --- a/src/renderedtarget.h +++ b/src/renderedtarget.h @@ -101,8 +101,8 @@ class RenderedTarget : public IRenderedTarget QRgb colorAtScratchPoint(double x, double y) const override; bool touchingClones(const std::vector &) const override; - bool touchingColor(const libscratchcpp::Value &color) const override; - bool touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const override; + bool touchingColor(libscratchcpp::Rgb color) const override; + bool touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const override; signals: void engineChanged(); @@ -132,14 +132,13 @@ class RenderedTarget : public IRenderedTarget QPointF mapFromStageWithOriginPoint(const QPointF &scenePoint) const; QPointF mapFromScratchToLocal(const QPointF &point) const; CpuTextureManager *textureManager() const; - bool touchingColor(const libscratchcpp::Value &color, bool hasMask, const libscratchcpp::Value &mask) const; + bool touchingColor(libscratchcpp::Rgb color, bool hasMask, libscratchcpp::Rgb mask) const; QRectF touchingBounds() const; QRectF candidatesBounds(const QRectF &targetRect, const std::vector &candidates, std::vector &dst) const; QRectF candidatesBounds(const QRectF &targetRect, const std::vector &candidates, std::vector &dst) const; static QRectF candidateIntersection(const QRectF &targetRect, IRenderedTarget *target); static QRectF rectIntersection(const QRectF &targetRect, const libscratchcpp::Rect &candidateRect); static void clampRect(libscratchcpp::Rect &rect, double left, double right, double bottom, double top); - static QRgb convertColor(const libscratchcpp::Value &color); static bool colorMatches(QRgb a, QRgb b); static bool maskMatches(QRgb a, QRgb b); QRgb sampleColor3b(double x, double y, const std::vector &targets) const; diff --git a/src/shadermanager.cpp b/src/shadermanager.cpp index 7d96640..93625ba 100644 --- a/src/shadermanager.cpp +++ b/src/shadermanager.cpp @@ -24,12 +24,10 @@ static float wrapClamp(float n, float min, float max) static const QString VERTEX_SHADER_SRC = ":/qt/qml/ScratchCPP/Render/shaders/sprite.vert"; static const QString FRAGMENT_SHADER_SRC = ":/qt/qml/ScratchCPP/Render/shaders/sprite.frag"; -#if defined(Q_OS_WASM) -static const QString SHADER_PREFIX = ""; // compiles, but doesn't work? -#elif defined(Q_OS_ANDROID) -static const QString SHADER_PREFIX = "#version 300 es\n"; +#ifdef Q_OS_MACOS +static const QString SHADER_PREFIX = "#version 410\n"; #else -static const QString SHADER_PREFIX = "#version 140\n"; +static const QString SHADER_PREFIX = "#version 300 es\n"; #endif static const char *TEXTURE_UNIT_UNIFORM = "u_skin"; diff --git a/src/shaders/sprite.frag b/src/shaders/sprite.frag index 3209cf8..6399587 100644 --- a/src/shaders/sprite.frag +++ b/src/shaders/sprite.frag @@ -35,7 +35,8 @@ uniform vec2 u_skinSize; uniform float u_mosaic; #endif // ENABLE_mosaic -varying vec2 v_texCoord; +in vec2 v_texCoord; +out vec4 fragColor; uniform sampler2D u_skin; // Add this to divisors to prevent division by 0, which results in NaNs propagating through calculations. @@ -147,16 +148,16 @@ void main() } #endif // ENABLE_fisheye - gl_FragColor = texture2D(u_skin, texcoord0); + fragColor = texture(u_skin, texcoord0); #if defined(ENABLE_color) || defined(ENABLE_brightness) // Divide premultiplied alpha values for proper color processing // Add epsilon to avoid dividing by 0 for fully transparent pixels - gl_FragColor.rgb = clamp(gl_FragColor.rgb / (gl_FragColor.a + epsilon), 0.0, 1.0); + fragColor.rgb = clamp(fragColor.rgb / (fragColor.a + epsilon), 0.0, 1.0); #ifdef ENABLE_color { - vec3 hsv = convertRGB2HSV(gl_FragColor.rgb); + vec3 hsv = convertRGB2HSV(fragColor.rgb); // Force grayscale values to be slightly saturated const float minLightness = 0.11 / 2.0; @@ -167,20 +168,20 @@ void main() hsv.x = mod(hsv.x + u_color, 1.0); if (hsv.x < 0.0) hsv.x += 1.0; - gl_FragColor.rgb = convertHSV2RGB(hsv); + fragColor.rgb = convertHSV2RGB(hsv); } #endif // ENABLE_color #ifdef ENABLE_brightness - gl_FragColor.rgb = clamp(gl_FragColor.rgb + vec3(u_brightness), vec3(0), vec3(1)); + fragColor.rgb = clamp(fragColor.rgb + vec3(u_brightness), vec3(0), vec3(1)); #endif // ENABLE_brightness // Re-multiply color values - gl_FragColor.rgb *= gl_FragColor.a + epsilon; + fragColor.rgb *= fragColor.a + epsilon; #endif // defined(ENABLE_color) || defined(ENABLE_brightness) #ifdef ENABLE_ghost - gl_FragColor *= u_ghost; + fragColor *= u_ghost; #endif // ENABLE_ghost } diff --git a/src/shaders/sprite.vert b/src/shaders/sprite.vert index ef79415..b52cc8f 100644 --- a/src/shaders/sprite.vert +++ b/src/shaders/sprite.vert @@ -1,9 +1,9 @@ uniform mat4 u_projectionMatrix; uniform mat4 u_modelMatrix; -attribute vec2 a_position; -attribute vec2 a_texCoord; +in vec2 a_position; +in vec2 a_texCoord; -varying vec2 v_texCoord; +out vec2 v_texCoord; void main() { gl_Position = u_projectionMatrix * u_modelMatrix * vec4(a_position, 0, 1); diff --git a/src/spritemodel.cpp b/src/spritemodel.cpp index 4383022..569ea96 100644 --- a/src/spritemodel.cpp +++ b/src/spritemodel.cpp @@ -6,15 +6,12 @@ #include #include "spritemodel.h" -#include "renderedtarget.h" -#include "ipenlayer.h" -#include "graphicseffect.h" +#include "penlayer.h" -namespace scratchcpprender -{ +using namespace scratchcpprender; SpriteModel::SpriteModel(QObject *parent) : - QObject(parent) + TargetModel(parent) { } @@ -22,36 +19,8 @@ void SpriteModel::init(libscratchcpp::Sprite *sprite) { m_sprite = sprite; - if (m_sprite) { - libscratchcpp::TextBubble *bubble = m_sprite->bubble(); - - bubble->typeChanged().connect([this](libscratchcpp::TextBubble::Type type) { - if (type == libscratchcpp::TextBubble::Type::Say) { - if (m_bubbleType == TextBubbleShape::Type::Say) - return; - - m_bubbleType = TextBubbleShape::Type::Say; - } else { - if (m_bubbleType == TextBubbleShape::Type::Think) - return; - - m_bubbleType = TextBubbleShape::Type::Think; - } - - emit bubbleTypeChanged(); - }); - - bubble->textChanged().connect([this](const std::string &text) { - QString newText = QString::fromStdString(text); - - if (m_bubbleText != newText) { - m_bubbleText = newText; - emit bubbleTextChanged(); - } - }); - - bubble->layerOrderChanged().connect([this](int) { emit bubbleLayerChanged(); }); - } + if (m_sprite) + setupTextBubble(m_sprite->bubble()); } void SpriteModel::deinitClone() @@ -66,123 +35,109 @@ void SpriteModel::onCloned(libscratchcpp::Sprite *clone) SpriteModel *cloneModel = new SpriteModel(m_cloneRoot); cloneModel->m_cloneRoot = m_cloneRoot; - cloneModel->m_penLayer = m_penLayer; - cloneModel->m_penState = m_penState; + cloneModel->setPenLayer(penLayer()); + cloneModel->penState() = penState(); clone->setInterface(cloneModel); emit cloned(cloneModel); } void SpriteModel::onCostumeChanged(libscratchcpp::Costume *costume) { - if (m_renderedTarget) - m_renderedTarget->updateCostume(costume); + updateCostume(costume); } void SpriteModel::onVisibleChanged(bool visible) { - if (m_renderedTarget) - m_renderedTarget->updateVisibility(visible); + updateVisibility(visible); } void SpriteModel::onXChanged(double x) { - if (m_renderedTarget) - m_renderedTarget->updateX(x); + updateX(x); } void SpriteModel::onYChanged(double y) { - if (m_renderedTarget) - m_renderedTarget->updateY(y); + updateY(y); } void SpriteModel::onMoved(double oldX, double oldY, double newX, double newY) { - if (m_penState.penDown && m_penLayer) { - m_penLayer->drawLine(m_penState.penAttributes, oldX, oldY, newX, newY); - libscratchcpp::IEngine *engine = m_sprite->engine(); - - if (engine) - engine->requestRedraw(); - } + TargetModel::onMoved(oldX, oldY, newX, newY); } void SpriteModel::onSizeChanged(double size) { - if (m_renderedTarget) - m_renderedTarget->updateSize(size); + updateSize(size); } void SpriteModel::onDirectionChanged(double direction) { - if (m_renderedTarget) - m_renderedTarget->updateDirection(direction); + updateDirection(direction); } void SpriteModel::onRotationStyleChanged(libscratchcpp::Sprite::RotationStyle rotationStyle) { - if (m_renderedTarget) - m_renderedTarget->updateRotationStyle(rotationStyle); + updateRotationStyle(rotationStyle); } void SpriteModel::onLayerOrderChanged(int layerOrder) { - if (m_renderedTarget) - m_renderedTarget->updateLayerOrder(layerOrder); + updateLayerOrder(layerOrder); } void SpriteModel::onGraphicsEffectChanged(libscratchcpp::IGraphicsEffect *effect, double value) { - GraphicsEffect *graphicsEffect = dynamic_cast(effect); - - if (graphicsEffect && m_renderedTarget) - m_renderedTarget->setGraphicEffect(graphicsEffect->effect(), value); + setGraphicEffect(effect, value); } void SpriteModel::onGraphicsEffectsCleared() { - if (m_renderedTarget) - m_renderedTarget->clearGraphicEffects(); + clearGraphicEffects(); } int SpriteModel::costumeWidth() const { - return m_renderedTarget->costumeWidth(); + return TargetModel::costumeWidth(); } int SpriteModel::costumeHeight() const { - return m_renderedTarget->costumeHeight(); + return TargetModel::costumeHeight(); } libscratchcpp::Rect SpriteModel::boundingRect() const { - return m_renderedTarget->getBounds(); + libscratchcpp::Rect ret; + getBoundingRect(ret); + return ret; } libscratchcpp::Rect SpriteModel::fastBoundingRect() const { - return m_renderedTarget->getFastBounds(); + libscratchcpp::Rect ret; + getFastBoundingRect(ret); + return ret; } bool SpriteModel::touchingClones(const std::vector &clones) const { - return m_renderedTarget->touchingClones(clones); + return TargetModel::touchingClones(clones); } bool SpriteModel::touchingPoint(double x, double y) const { - return m_renderedTarget->containsScratchPoint(x, y); + return TargetModel::touchingPoint(x, y); } -bool SpriteModel::touchingColor(const libscratchcpp::Value &color) const +bool SpriteModel::touchingColor(libscratchcpp::Rgb color) const { - return m_renderedTarget->touchingColor(color); + return TargetModel::touchingColor(color); } -bool SpriteModel::touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const +bool SpriteModel::touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const { - return m_renderedTarget->touchingColor(color, mask); + return TargetModel::touchingColor(color, mask); } libscratchcpp::Sprite *SpriteModel::sprite() const @@ -190,60 +145,9 @@ libscratchcpp::Sprite *SpriteModel::sprite() const return m_sprite; } -IRenderedTarget *SpriteModel::renderedTarget() const -{ - return m_renderedTarget; -} - -void SpriteModel::setRenderedTarget(IRenderedTarget *newRenderedTarget) -{ - if (m_renderedTarget == newRenderedTarget) - return; - - m_renderedTarget = newRenderedTarget; - emit renderedTargetChanged(); -} - -IPenLayer *SpriteModel::penLayer() const -{ - return m_penLayer; -} - -void SpriteModel::setPenLayer(IPenLayer *newPenLayer) -{ - if (m_penLayer == newPenLayer) - return; - - m_penLayer = newPenLayer; - emit penLayerChanged(); -} - -PenState &SpriteModel::penState() -{ - return m_penState; -} - -PenAttributes &SpriteModel::penAttributes() -{ - return m_penState.penAttributes; -} - -bool SpriteModel::penDown() const -{ - return m_penState.penDown; -} - -void SpriteModel::setPenDown(bool newPenDown) +int SpriteModel::bubbleLayer() const { - m_penState.penDown = newPenDown; - - if (m_penState.penDown && m_penLayer && m_sprite) { - m_penLayer->drawPoint(m_penState.penAttributes, m_sprite->x(), m_sprite->y()); - libscratchcpp::IEngine *engine = m_sprite->engine(); - - if (engine) - engine->requestRedraw(); - } + return m_sprite ? m_sprite->bubble()->layerOrder() : 0; } SpriteModel *SpriteModel::cloneRoot() const @@ -254,19 +158,26 @@ SpriteModel *SpriteModel::cloneRoot() const return m_cloneRoot; } -const TextBubbleShape::Type &SpriteModel::bubbleType() const +void SpriteModel::loadCostume() { - return m_bubbleType; + if (m_sprite) + updateCostume(m_sprite->currentCostume().get()); } -const QString &SpriteModel::bubbleText() const +void SpriteModel::drawPenPoint(IPenLayer *penLayer, const PenAttributes &penAttributes) { - return m_bubbleText; + penLayer->drawPoint(penAttributes, m_sprite->x(), m_sprite->y()); + libscratchcpp::IEngine *engine = m_sprite->engine(); + + if (engine) + engine->requestRedraw(); } -int SpriteModel::bubbleLayer() const +void SpriteModel::drawPenLine(IPenLayer *penLayer, const PenAttributes &penAttributes, double x0, double y0, double x1, double y1) { - return m_sprite ? m_sprite->bubble()->layerOrder() : 0; -} + penLayer->drawLine(penAttributes, x0, y0, x1, y1); + libscratchcpp::IEngine *engine = m_sprite->engine(); -} // namespace scratchcpprender + if (engine) + engine->requestRedraw(); +} diff --git a/src/spritemodel.h b/src/spritemodel.h index e54c47c..6428396 100644 --- a/src/spritemodel.h +++ b/src/spritemodel.h @@ -2,33 +2,19 @@ #pragma once -#include -#include #include -#include "penstate.h" -#include "textbubbleshape.h" - -Q_MOC_INCLUDE("renderedtarget.h"); -Q_MOC_INCLUDE("ipenlayer.h"); +#include "targetmodel.h" namespace scratchcpprender { -class IRenderedTarget; -class IPenLayer; - class SpriteModel - : public QObject + : public TargetModel , public libscratchcpp::ISpriteHandler { Q_OBJECT QML_ELEMENT - Q_PROPERTY(IRenderedTarget *renderedTarget READ renderedTarget WRITE setRenderedTarget NOTIFY renderedTargetChanged) - Q_PROPERTY(IPenLayer *penLayer READ penLayer WRITE setPenLayer NOTIFY penLayerChanged) - Q_PROPERTY(TextBubbleShape::Type bubbleType READ bubbleType NOTIFY bubbleTypeChanged) - Q_PROPERTY(QString bubbleText READ bubbleText NOTIFY bubbleTextChanged) - Q_PROPERTY(int bubbleLayer READ bubbleLayer NOTIFY bubbleLayerChanged) public: SpriteModel(QObject *parent = nullptr); @@ -60,48 +46,28 @@ class SpriteModel bool touchingClones(const std::vector &clones) const override; bool touchingPoint(double x, double y) const override; - bool touchingColor(const libscratchcpp::Value &color) const override; - bool touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const override; + bool touchingColor(libscratchcpp::Rgb color) const override; + bool touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const override; libscratchcpp::Sprite *sprite() const; - IRenderedTarget *renderedTarget() const; - void setRenderedTarget(IRenderedTarget *newRenderedTarget); - - IPenLayer *penLayer() const; - void setPenLayer(IPenLayer *newPenLayer); - - PenState &penState(); - PenAttributes &penAttributes(); - - bool penDown() const; - void setPenDown(bool newPenDown); + int bubbleLayer() const override; SpriteModel *cloneRoot() const; - const TextBubbleShape::Type &bubbleType() const; - - const QString &bubbleText() const; - - int bubbleLayer() const; + Q_INVOKABLE void loadCostume() override; signals: - void renderedTargetChanged(); - void penLayerChanged(); - void bubbleTypeChanged(); - void bubbleTextChanged(); void cloned(SpriteModel *cloneModel); void cloneDeleted(SpriteModel *clone); - void bubbleLayerChanged(); + + protected: + void drawPenPoint(IPenLayer *penLayer, const PenAttributes &penAttributes) override; + void drawPenLine(IPenLayer *penLayer, const PenAttributes &penAttributes, double x0, double y0, double x1, double y1) override; private: libscratchcpp::Sprite *m_sprite = nullptr; - IRenderedTarget *m_renderedTarget = nullptr; - IPenLayer *m_penLayer = nullptr; - PenState m_penState; SpriteModel *m_cloneRoot = nullptr; - TextBubbleShape::Type m_bubbleType = TextBubbleShape::Type::Say; - QString m_bubbleText; }; } // namespace scratchcpprender diff --git a/src/stagemodel.cpp b/src/stagemodel.cpp index 26c7dff..4aa3f5b 100644 --- a/src/stagemodel.cpp +++ b/src/stagemodel.cpp @@ -1,16 +1,16 @@ // SPDX-License-Identifier: LGPL-3.0-or-later #include +#include #include #include "stagemodel.h" -#include "renderedtarget.h" -#include "graphicseffect.h" +#include "penlayer.h" using namespace scratchcpprender; StageModel::StageModel(QObject *parent) : - QObject(parent) + TargetModel(parent) { } @@ -18,42 +18,13 @@ void StageModel::init(libscratchcpp::Stage *stage) { m_stage = stage; - if (m_stage) { - libscratchcpp::TextBubble *bubble = m_stage->bubble(); - - bubble->typeChanged().connect([this](libscratchcpp::TextBubble::Type type) { - if (type == libscratchcpp::TextBubble::Type::Say) { - if (m_bubbleType == TextBubbleShape::Type::Say) - return; - - m_bubbleType = TextBubbleShape::Type::Say; - } else { - if (m_bubbleType == TextBubbleShape::Type::Think) - return; - - m_bubbleType = TextBubbleShape::Type::Think; - } - - emit bubbleTypeChanged(); - }); - - bubble->textChanged().connect([this](const std::string &text) { - QString newText = QString::fromStdString(text); - - if (m_bubbleText != newText) { - m_bubbleText = newText; - emit bubbleTextChanged(); - } - }); - - bubble->layerOrderChanged().connect([this](int) { emit bubbleLayerChanged(); }); - } + if (m_stage) + setupTextBubble(m_stage->bubble()); } void StageModel::onCostumeChanged(libscratchcpp::Costume *costume) { - if (m_renderedTarget) - m_renderedTarget->updateCostume(costume); + updateCostume(costume); } void StageModel::onTempoChanged(int tempo) @@ -70,62 +41,56 @@ void StageModel::onVideoTransparencyChanged(int videoTransparency) void StageModel::onGraphicsEffectChanged(libscratchcpp::IGraphicsEffect *effect, double value) { - GraphicsEffect *graphicsEffect = dynamic_cast(effect); - - if (graphicsEffect && m_renderedTarget) - m_renderedTarget->setGraphicEffect(graphicsEffect->effect(), value); + setGraphicEffect(effect, value); } void StageModel::onGraphicsEffectsCleared() { - if (m_renderedTarget) - m_renderedTarget->clearGraphicEffects(); + clearGraphicEffects(); } int StageModel::costumeWidth() const { - return m_renderedTarget->costumeWidth(); + return TargetModel::costumeWidth(); } int StageModel::costumeHeight() const { - return m_renderedTarget->costumeHeight(); + return TargetModel::costumeHeight(); } libscratchcpp::Rect StageModel::boundingRect() const { - return libscratchcpp::Rect(); + libscratchcpp::Rect ret; + getBoundingRect(ret); + return ret; } libscratchcpp::Rect StageModel::fastBoundingRect() const { - return libscratchcpp::Rect(); + libscratchcpp::Rect ret; + getFastBoundingRect(ret); + return ret; } bool StageModel::touchingClones(const std::vector &clones) const { - return m_renderedTarget->touchingClones(clones); + return TargetModel::touchingClones(clones); } bool StageModel::touchingPoint(double x, double y) const { - return m_renderedTarget->containsScratchPoint(x, y); -} - -bool StageModel::touchingColor(const libscratchcpp::Value &color) const -{ - return m_renderedTarget->touchingColor(color); + return TargetModel::touchingPoint(x, y); } -bool StageModel::touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const +bool StageModel::touchingColor(libscratchcpp::Rgb color) const { - return m_renderedTarget->touchingColor(color, mask); + return TargetModel::touchingColor(color); } -void StageModel::loadCostume() +bool StageModel::touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const { - if (m_renderedTarget && m_stage) - m_renderedTarget->updateCostume(m_stage->currentCostume().get()); + return TargetModel::touchingColor(color, mask); } libscratchcpp::Stage *StageModel::stage() const @@ -133,32 +98,22 @@ libscratchcpp::Stage *StageModel::stage() const return m_stage; } -IRenderedTarget *StageModel::renderedTarget() const -{ - return m_renderedTarget; -} - -void StageModel::setRenderedTarget(IRenderedTarget *newRenderedTarget) +int StageModel::bubbleLayer() const { - if (m_renderedTarget == newRenderedTarget) - return; - - m_renderedTarget = newRenderedTarget; - - emit renderedTargetChanged(); + return m_stage ? m_stage->bubble()->layerOrder() : 0; } -const TextBubbleShape::Type &StageModel::bubbleType() const +void StageModel::loadCostume() { - return m_bubbleType; + if (m_stage) + updateCostume(m_stage->currentCostume().get()); } -const QString &StageModel::bubbleText() const +void StageModel::drawPenPoint(IPenLayer *penLayer, const PenAttributes &penAttributes) { - return m_bubbleText; -} + penLayer->drawLine(penAttributes, 0, 0, 0, 0); + libscratchcpp::IEngine *engine = m_stage->engine(); -int StageModel::bubbleLayer() const -{ - return m_stage ? m_stage->bubble()->layerOrder() : 0; + if (engine) + engine->requestRedraw(); } diff --git a/src/stagemodel.h b/src/stagemodel.h index 269e11c..e7512de 100644 --- a/src/stagemodel.h +++ b/src/stagemodel.h @@ -2,27 +2,18 @@ #pragma once -#include #include -#include "textbubbleshape.h" - -Q_MOC_INCLUDE("renderedtarget.h"); +#include "targetmodel.h" namespace scratchcpprender { -class IRenderedTarget; - class StageModel - : public QObject + : public TargetModel , public libscratchcpp::IStageHandler { Q_OBJECT - Q_PROPERTY(IRenderedTarget *renderedTarget READ renderedTarget WRITE setRenderedTarget NOTIFY renderedTargetChanged) - Q_PROPERTY(TextBubbleShape::Type bubbleType READ bubbleType NOTIFY bubbleTypeChanged) - Q_PROPERTY(QString bubbleText READ bubbleText NOTIFY bubbleTextChanged) - Q_PROPERTY(int bubbleLayer READ bubbleLayer NOTIFY bubbleLayerChanged) public: explicit StageModel(QObject *parent = nullptr); @@ -46,33 +37,20 @@ class StageModel bool touchingClones(const std::vector &clones) const override; bool touchingPoint(double x, double y) const override; - bool touchingColor(const libscratchcpp::Value &color) const override; - bool touchingColor(const libscratchcpp::Value &color, const libscratchcpp::Value &mask) const override; - - Q_INVOKABLE void loadCostume(); + bool touchingColor(libscratchcpp::Rgb color) const override; + bool touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const override; libscratchcpp::Stage *stage() const; - IRenderedTarget *renderedTarget() const; - void setRenderedTarget(IRenderedTarget *newRenderedTarget); - - const TextBubbleShape::Type &bubbleType() const; - - const QString &bubbleText() const; + int bubbleLayer() const override; - int bubbleLayer() const; + Q_INVOKABLE void loadCostume() override; - signals: - void renderedTargetChanged(); - void bubbleTypeChanged(); - void bubbleTextChanged(); - void bubbleLayerChanged(); + protected: + void drawPenPoint(IPenLayer *penLayer, const PenAttributes &penAttributes) override; private: libscratchcpp::Stage *m_stage = nullptr; - IRenderedTarget *m_renderedTarget = nullptr; - TextBubbleShape::Type m_bubbleType = TextBubbleShape::Type::Say; - QString m_bubbleText; }; } // namespace scratchcpprender diff --git a/src/targetmodel.cpp b/src/targetmodel.cpp new file mode 100644 index 0000000..82e28db --- /dev/null +++ b/src/targetmodel.cpp @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include + +#include "targetmodel.h" +#include "renderedtarget.h" +#include "penlayer.h" +#include "graphicseffect.h" + +using namespace scratchcpprender; + +TargetModel::TargetModel(QObject *parent) : + QObject(parent) +{ +} + +IRenderedTarget *TargetModel::renderedTarget() const +{ + return m_renderedTarget; +} + +void TargetModel::setRenderedTarget(IRenderedTarget *newRenderedTarget) +{ + if (m_renderedTarget == newRenderedTarget) + return; + + m_renderedTarget = newRenderedTarget; + + emit renderedTargetChanged(); +} + +IPenLayer *TargetModel::penLayer() const +{ + return m_penLayer; +} + +void TargetModel::setPenLayer(IPenLayer *newPenLayer) +{ + if (m_penLayer == newPenLayer) + return; + + m_penLayer = newPenLayer; + emit penLayerChanged(); +} + +PenState &TargetModel::penState() +{ + return m_penState; +} + +PenAttributes &TargetModel::penAttributes() +{ + return m_penState.penAttributes; +} + +bool TargetModel::penDown() const +{ + return m_penState.penDown; +} + +void TargetModel::setPenDown(bool newPenDown) +{ + m_penState.penDown = newPenDown; + + if (m_penState.penDown && m_penLayer) + drawPenPoint(m_penLayer, m_penState.penAttributes); +} + +const TextBubbleShape::Type &TargetModel::bubbleType() const +{ + return m_bubbleType; +} + +const QString &TargetModel::bubbleText() const +{ + return m_bubbleText; +} + +void TargetModel::setupTextBubble(libscratchcpp::TextBubble *bubble) +{ + bubble->typeChanged().connect([this](libscratchcpp::TextBubble::Type type) { + if (type == libscratchcpp::TextBubble::Type::Say) { + if (m_bubbleType == TextBubbleShape::Type::Say) + return; + + m_bubbleType = TextBubbleShape::Type::Say; + } else { + if (m_bubbleType == TextBubbleShape::Type::Think) + return; + + m_bubbleType = TextBubbleShape::Type::Think; + } + + emit bubbleTypeChanged(); + }); + + bubble->textChanged().connect([this](const std::string &text) { + QString newText = QString::fromStdString(text); + + if (m_bubbleText != newText) { + m_bubbleText = newText; + emit bubbleTextChanged(); + } + }); + + bubble->layerOrderChanged().connect([this](int) { emit bubbleLayerChanged(); }); +} + +void TargetModel::updateVisibility(bool visible) +{ + if (m_renderedTarget) + m_renderedTarget->updateVisibility(visible); +} + +void TargetModel::updateX(double x) +{ + if (m_renderedTarget) + m_renderedTarget->updateX(x); +} + +void TargetModel::updateY(double y) +{ + if (m_renderedTarget) + m_renderedTarget->updateY(y); +} + +void TargetModel::updateSize(double size) +{ + if (m_renderedTarget) + m_renderedTarget->updateSize(size); +} + +void TargetModel::updateDirection(double direction) +{ + if (m_renderedTarget) + m_renderedTarget->updateDirection(direction); +} + +void TargetModel::updateRotationStyle(libscratchcpp::Sprite::RotationStyle style) +{ + if (m_renderedTarget) + m_renderedTarget->updateRotationStyle(style); +} + +void TargetModel::updateLayerOrder(int layerOrder) +{ + if (m_renderedTarget) + m_renderedTarget->updateLayerOrder(layerOrder); +} + +void TargetModel::updateCostume(libscratchcpp::Costume *costume) +{ + if (m_renderedTarget) + m_renderedTarget->updateCostume(costume); +} + +void TargetModel::onMoved(double oldX, double oldY, double newX, double newY) +{ + if (m_penState.penDown && m_penLayer) + drawPenLine(m_penLayer, m_penState.penAttributes, oldX, oldY, newX, newY); +} + +void TargetModel::setGraphicEffect(libscratchcpp::IGraphicsEffect *effect, double value) +{ + GraphicsEffect *graphicsEffect = dynamic_cast(effect); + + if (graphicsEffect && m_renderedTarget) + m_renderedTarget->setGraphicEffect(graphicsEffect->effect(), value); +} + +void TargetModel::clearGraphicEffects() +{ + if (m_renderedTarget) + m_renderedTarget->clearGraphicEffects(); +} + +int TargetModel::costumeWidth() const +{ + return m_renderedTarget->costumeWidth(); +} + +int TargetModel::costumeHeight() const +{ + return m_renderedTarget->costumeHeight(); +} + +void TargetModel::getBoundingRect(libscratchcpp::Rect &dst) const +{ + dst = m_renderedTarget->getBounds(); +} + +void TargetModel::getFastBoundingRect(libscratchcpp::Rect &dst) const +{ + dst = m_renderedTarget->getFastBounds(); +} + +bool TargetModel::touchingClones(const std::vector &clones) const +{ + return m_renderedTarget->touchingClones(clones); +} + +bool TargetModel::touchingPoint(double x, double y) const +{ + return m_renderedTarget->containsScratchPoint(x, y); +} + +bool TargetModel::touchingColor(libscratchcpp::Rgb color) const +{ + return m_renderedTarget->touchingColor(color); +} + +bool TargetModel::touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const +{ + return m_renderedTarget->touchingColor(color, mask); +} diff --git a/src/targetmodel.h b/src/targetmodel.h new file mode 100644 index 0000000..2be3e1f --- /dev/null +++ b/src/targetmodel.h @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include +#include + +#include "penstate.h" +#include "textbubbleshape.h" + +Q_MOC_INCLUDE("renderedtarget.h"); +Q_MOC_INCLUDE("ipenlayer.h"); + +namespace scratchcpprender +{ + +class IRenderedTarget; +class IPenLayer; + +class TargetModel : public QObject +{ + Q_OBJECT + Q_PROPERTY(IRenderedTarget *renderedTarget READ renderedTarget WRITE setRenderedTarget NOTIFY renderedTargetChanged) + Q_PROPERTY(IPenLayer *penLayer READ penLayer WRITE setPenLayer NOTIFY penLayerChanged) + Q_PROPERTY(TextBubbleShape::Type bubbleType READ bubbleType NOTIFY bubbleTypeChanged) + Q_PROPERTY(QString bubbleText READ bubbleText NOTIFY bubbleTextChanged) + Q_PROPERTY(int bubbleLayer READ bubbleLayer NOTIFY bubbleLayerChanged) + + public: + explicit TargetModel(QObject *parent = nullptr); + + IRenderedTarget *renderedTarget() const; + void setRenderedTarget(IRenderedTarget *newRenderedTarget); + + IPenLayer *penLayer() const; + void setPenLayer(IPenLayer *newPenLayer); + + PenState &penState(); + PenAttributes &penAttributes(); + + bool penDown() const; + void setPenDown(bool newPenDown); + + const TextBubbleShape::Type &bubbleType() const; + + const QString &bubbleText() const; + + virtual int bubbleLayer() const { return 0; } + + Q_INVOKABLE virtual void loadCostume() { } + + signals: + void renderedTargetChanged(); + void penLayerChanged(); + void bubbleTypeChanged(); + void bubbleTextChanged(); + void bubbleLayerChanged(); + + protected: + void setupTextBubble(libscratchcpp::TextBubble *bubble); + + void updateVisibility(bool visible); + void updateX(double x); + void updateY(double y); + void updateSize(double size); + void updateDirection(double direction); + void updateRotationStyle(libscratchcpp::Sprite::RotationStyle style); + void updateLayerOrder(int layerOrder); + void updateCostume(libscratchcpp::Costume *costume); + + void onMoved(double oldX, double oldY, double newX, double newY); + + void setGraphicEffect(libscratchcpp::IGraphicsEffect *effect, double value); + void clearGraphicEffects(); + + int costumeWidth() const; + int costumeHeight() const; + + void getBoundingRect(libscratchcpp::Rect &dst) const; + void getFastBoundingRect(libscratchcpp::Rect &dst) const; + + bool touchingClones(const std::vector &clones) const; + bool touchingPoint(double x, double y) const; + bool touchingColor(libscratchcpp::Rgb color) const; + bool touchingColor(libscratchcpp::Rgb color, libscratchcpp::Rgb mask) const; + + virtual void drawPenPoint(IPenLayer *penLayer, const PenAttributes &penAttributes) { } // stage and sprites can draw points + virtual void drawPenLine(IPenLayer *penLayer, const PenAttributes &penAttributes, double x0, double y0, double x1, double y1) { } // only sprites can draw lines + + private: + IRenderedTarget *m_renderedTarget = nullptr; + IPenLayer *m_penLayer = nullptr; + PenState m_penState; + TextBubbleShape::Type m_bubbleType = TextBubbleShape::Type::Say; + QString m_bubbleText; +}; + +} // namespace scratchcpprender diff --git a/src/valuemonitormodel.cpp b/src/valuemonitormodel.cpp index 78d8c90..0af3911 100644 --- a/src/valuemonitormodel.cpp +++ b/src/valuemonitormodel.cpp @@ -1,7 +1,7 @@ // SPDX-License-Identifier: LGPL-3.0-or-later -#include #include +#include #include "valuemonitormodel.h" @@ -21,12 +21,10 @@ ValueMonitorModel::ValueMonitorModel(IExtension *extension, QObject *parent) : { } -void ValueMonitorModel::onValueChanged(const VirtualMachine *vm) +void ValueMonitorModel::onValueChanged(const libscratchcpp::Value &value) { - if (vm->registerCount() == 1) { - m_value = QString::fromStdString(vm->getInput(0, 1)->toString()); - emit valueChanged(); - } + m_value = QString::fromStdString(value.toString()); + emit valueChanged(); } MonitorModel::Type ValueMonitorModel::type() const diff --git a/src/valuemonitormodel.h b/src/valuemonitormodel.h index c256c74..79401b4 100644 --- a/src/valuemonitormodel.h +++ b/src/valuemonitormodel.h @@ -30,7 +30,7 @@ class ValueMonitorModel : public MonitorModel ValueMonitorModel(QObject *parent = nullptr); ValueMonitorModel(libscratchcpp::IExtension *extension, QObject *parent = nullptr); - void onValueChanged(const libscratchcpp::VirtualMachine *vm) override; + void onValueChanged(const libscratchcpp::Value &value) override; Type type() const override; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0159df6..f697644 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -36,7 +36,6 @@ add_subdirectory(penattributes) add_subdirectory(penstate) add_subdirectory(penlayer) add_subdirectory(penlayerpainter) -add_subdirectory(blocks) add_subdirectory(graphicseffect) add_subdirectory(shadermanager) add_subdirectory(textbubbleshape) diff --git a/test/blocks/CMakeLists.txt b/test/blocks/CMakeLists.txt deleted file mode 100644 index b3b8888..0000000 --- a/test/blocks/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -# pen_blocks_test -add_executable( - pen_blocks_test - pen_blocks_test.cpp -) - -target_link_libraries( - pen_blocks_test - GTest::gtest_main - GTest::gmock_main - scratchcpp-render - scratchcpprender_mocks - ${QT_LIBS} -) - -add_test(pen_blocks_test) -gtest_discover_tests(pen_blocks_test) diff --git a/test/blocks/pen_blocks_test.cpp b/test/blocks/pen_blocks_test.cpp deleted file mode 100644 index 8428c82..0000000 --- a/test/blocks/pen_blocks_test.cpp +++ /dev/null @@ -1,1347 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../common.h" - -using namespace scratchcpprender; -using namespace libscratchcpp; - -using ::testing::Return; - -class PenBlocksTest : public testing::Test -{ - public: - void SetUp() override { m_extension = std::make_unique(); } - - void addValueInput(std::shared_ptr block, const std::string &name, PenBlocks::Inputs id, const Value &value) const - { - auto input = std::make_shared(name, Input::Type::Shadow); - input->setPrimaryValue(value); - input->setInputId(id); - block->addInput(input); - } - - void addObscuredInput(std::shared_ptr block, const std::string &name, PenBlocks::Inputs id, std::shared_ptr valueBlock) const - { - auto input = std::make_shared(name, Input::Type::ObscuredShadow); - input->setValueBlock(valueBlock); - input->setInputId(id); - block->addInput(input); - } - - std::shared_ptr addNullInput(std::shared_ptr block, const std::string &name, PenBlocks::Inputs id) const - { - auto input = std::make_shared(name, Input::Type::Shadow); - input->setInputId(id); - block->addInput(input); - - return input; - } - - void addDropdownInput(std::shared_ptr block, const std::string &name, PenBlocks::Inputs id, const std::string &selectedValue, std::shared_ptr valueBlock = nullptr) const - { - if (valueBlock) - addObscuredInput(block, name, id, valueBlock); - else { - auto input = addNullInput(block, name, id); - auto menu = std::make_shared(block->id() + "_menu", block->opcode() + "_menu"); - menu->setShadow(true); - input->setValueBlock(menu); - addDropdownField(menu, name, -1, selectedValue, -1); - } - } - - void addDropdownField(std::shared_ptr block, const std::string &name, int id, const std::string &value, int valueId) const - { - auto field = std::make_shared(name, value); - field->setFieldId(id); - field->setSpecialValueId(valueId); - block->addField(field); - } - - std::shared_ptr createNullBlock(const std::string &id) - { - std::shared_ptr block = std::make_shared(id, ""); - BlockComp func = [](Compiler *compiler) { compiler->addInstruction(vm::OP_NULL); }; - block->setCompileFunction(func); - - return block; - } - - std::unique_ptr m_extension; - EngineMock m_engineMock; -}; - -TEST_F(PenBlocksTest, Name) -{ - ASSERT_EQ(m_extension->name(), "pen"); -} - -TEST_F(PenBlocksTest, RegisterBlocks) -{ - // Blocks - EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_clear", &PenBlocks::compileClear)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_stamp", &PenBlocks::compileStamp)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_penDown", &PenBlocks::compilePenDown)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_penUp", &PenBlocks::compilePenUp)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_setPenColorToColor", &PenBlocks::compileSetPenColorToColor)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_changePenColorParamBy", &PenBlocks::compileChangePenColorParamBy)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_setPenColorParamTo", &PenBlocks::compileSetPenColorParamTo)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_changePenSizeBy", &PenBlocks::compileChangePenSizeBy)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_setPenSizeTo", &PenBlocks::compileSetPenSizeTo)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_changePenShadeBy", &PenBlocks::compileChangePenShadeBy)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_setPenShadeToNumber", &PenBlocks::compileSetPenShadeToNumber)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_changePenHueBy", &PenBlocks::compileChangePenHueBy)); - EXPECT_CALL(m_engineMock, addCompileFunction(m_extension.get(), "pen_setPenHueToNumber", &PenBlocks::compileSetPenHueToNumber)); - - // Inputs - EXPECT_CALL(m_engineMock, addInput(m_extension.get(), "COLOR", PenBlocks::COLOR)); - EXPECT_CALL(m_engineMock, addInput(m_extension.get(), "COLOR_PARAM", PenBlocks::COLOR_PARAM)); - EXPECT_CALL(m_engineMock, addInput(m_extension.get(), "VALUE", PenBlocks::VALUE)); - EXPECT_CALL(m_engineMock, addInput(m_extension.get(), "SIZE", PenBlocks::SIZE)); - EXPECT_CALL(m_engineMock, addInput(m_extension.get(), "SHADE", PenBlocks::SHADE)); - EXPECT_CALL(m_engineMock, addInput(m_extension.get(), "HUE", PenBlocks::HUE)); - - m_extension->registerBlocks(&m_engineMock); -} - -TEST_F(PenBlocksTest, Clear) -{ - Compiler compiler(&m_engineMock); - - auto block = std::make_shared("a", "pen_clear"); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::clear)).WillOnce(Return(2)); - compiler.init(); - compiler.setBlock(block); - PenBlocks::compileClear(&compiler); - compiler.end(); - - ASSERT_EQ(compiler.bytecode(), std::vector({ vm::OP_START, vm::OP_EXEC, 2, vm::OP_HALT })); - ASSERT_TRUE(compiler.constValues().empty()); - ASSERT_TRUE(compiler.variables().empty()); - ASSERT_TRUE(compiler.lists().empty()); -} - -TEST_F(PenBlocksTest, Stamp) -{ - Compiler compiler(&m_engineMock); - - auto block = std::make_shared("a", "pen_stamp"); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::stamp)).WillOnce(Return(2)); - compiler.init(); - compiler.setBlock(block); - PenBlocks::compileStamp(&compiler); - compiler.end(); - - ASSERT_EQ(compiler.bytecode(), std::vector({ vm::OP_START, vm::OP_EXEC, 2, vm::OP_HALT })); - ASSERT_TRUE(compiler.constValues().empty()); - ASSERT_TRUE(compiler.variables().empty()); - ASSERT_TRUE(compiler.lists().empty()); -} - -TEST_F(PenBlocksTest, StampImpl) -{ - static unsigned int bytecode[] = { vm::OP_START, vm::OP_EXEC, 0, vm::OP_HALT }; - static BlockFunc functions[] = { &PenBlocks::stamp }; - - PenLayerMock penLayer; - PenLayer::addPenLayer(&m_engineMock, &penLayer); - RenderedTargetMock renderedTarget; - - // Test sprite - libscratchcpp::Sprite sprite; - SpriteModel spriteModel; - sprite.setInterface(&spriteModel); - spriteModel.setRenderedTarget(&renderedTarget); - - VirtualMachine vm1(&sprite, &m_engineMock, nullptr); - vm1.setBytecode(bytecode); - vm1.setFunctions(functions); - - EXPECT_CALL(penLayer, stamp(&renderedTarget)); - EXPECT_CALL(m_engineMock, requestRedraw()); - vm1.run(); - - ASSERT_EQ(vm1.registerCount(), 0); - - // Test stage - libscratchcpp::Stage stage; - StageModel stageModel; - stage.setInterface(&stageModel); - stageModel.setRenderedTarget(&renderedTarget); - - VirtualMachine vm2(&stage, &m_engineMock, nullptr); - vm2.setBytecode(bytecode); - vm2.setFunctions(functions); - - EXPECT_CALL(penLayer, stamp(&renderedTarget)); - EXPECT_CALL(m_engineMock, requestRedraw()); - vm2.run(); - - ASSERT_EQ(vm2.registerCount(), 0); -} - -TEST_F(PenBlocksTest, ClearImpl) -{ - static unsigned int bytecode[] = { vm::OP_START, vm::OP_EXEC, 0, vm::OP_HALT }; - static BlockFunc functions[] = { &PenBlocks::clear }; - - PenLayerMock penLayer; - PenLayer::addPenLayer(&m_engineMock, &penLayer); - - VirtualMachine vm(nullptr, &m_engineMock, nullptr); - vm.setBytecode(bytecode); - vm.setFunctions(functions); - - EXPECT_CALL(penLayer, clear()); - EXPECT_CALL(m_engineMock, requestRedraw()); - vm.run(); - - ASSERT_EQ(vm.registerCount(), 0); -} - -TEST_F(PenBlocksTest, PenDown) -{ - Compiler compiler(&m_engineMock); - - auto block = std::make_shared("a", "pen_penDown"); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::penDown)).WillOnce(Return(2)); - compiler.init(); - compiler.setBlock(block); - PenBlocks::compilePenDown(&compiler); - compiler.end(); - - ASSERT_EQ(compiler.bytecode(), std::vector({ vm::OP_START, vm::OP_EXEC, 2, vm::OP_HALT })); - ASSERT_TRUE(compiler.constValues().empty()); - ASSERT_TRUE(compiler.variables().empty()); - ASSERT_TRUE(compiler.lists().empty()); -} - -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()); -} - -TEST_F(PenBlocksTest, PenUp) -{ - Compiler compiler(&m_engineMock); - - auto block = std::make_shared("a", "pen_penUp"); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::penUp)).WillOnce(Return(2)); - compiler.init(); - compiler.setBlock(block); - PenBlocks::compilePenUp(&compiler); - compiler.end(); - - ASSERT_EQ(compiler.bytecode(), std::vector({ vm::OP_START, vm::OP_EXEC, 2, vm::OP_HALT })); - ASSERT_TRUE(compiler.constValues().empty()); - ASSERT_TRUE(compiler.variables().empty()); - ASSERT_TRUE(compiler.lists().empty()); -} - -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); - - VirtualMachine vm(&sprite, &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) -{ - Compiler compiler(&m_engineMock); - - // set pen color to ("#AABBCC") - auto block1 = std::make_shared("a", "pen_setPenColorToColor"); - addValueInput(block1, "COLOR", PenBlocks::COLOR, "#AABBCC"); - - // set pen color to (null block) - auto block2 = std::make_shared("b", "pen_setPenColorToColor"); - addObscuredInput(block2, "COLOR", PenBlocks::COLOR, createNullBlock("c")); - - compiler.init(); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::setPenColorToColor)).WillOnce(Return(2)); - compiler.setBlock(block1); - PenBlocks::compileSetPenColorToColor(&compiler); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::setPenColorToColor)).WillOnce(Return(2)); - compiler.setBlock(block2); - PenBlocks::compileSetPenColorToColor(&compiler); - - compiler.end(); - - ASSERT_EQ(compiler.bytecode(), std::vector({ vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 2, vm::OP_NULL, vm::OP_EXEC, 2, vm::OP_HALT })); - ASSERT_EQ(compiler.constValues().size(), 1); - ASSERT_EQ(compiler.constValues()[0].toString(), "#AABBCC"); - ASSERT_TRUE(compiler.variables().empty()); - ASSERT_TRUE(compiler.lists().empty()); -} - -TEST_F(PenBlocksTest, SetPenColorToColorImpl) -{ - static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode4[] = { vm::OP_START, vm::OP_CONST, 3, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode5[] = { vm::OP_START, vm::OP_CONST, 4, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode6[] = { vm::OP_START, vm::OP_CONST, 5, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode7[] = { vm::OP_START, vm::OP_CONST, 6, vm::OP_EXEC, 0, vm::OP_HALT }; - 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))); -} - -TEST_F(PenBlocksTest, ChangePenColorParamBy) -{ - Compiler compiler(&m_engineMock); - - // change pen (color) by (34.6) - auto block1 = std::make_shared("a", "pen_changePenColorParamBy"); - addDropdownInput(block1, "COLOR_PARAM", PenBlocks::COLOR_PARAM, "color"); - addValueInput(block1, "VALUE", PenBlocks::VALUE, 34.6); - - // change pen (saturation) by (46.8) - auto block2 = std::make_shared("b", "pen_changePenColorParamBy"); - addDropdownInput(block2, "COLOR_PARAM", PenBlocks::COLOR_PARAM, "saturation"); - addValueInput(block2, "VALUE", PenBlocks::VALUE, 46.8); - - // change pen (brightness) by (0.45) - auto block3 = std::make_shared("c", "pen_changePenColorParamBy"); - addDropdownInput(block3, "COLOR_PARAM", PenBlocks::COLOR_PARAM, "brightness"); - addValueInput(block3, "VALUE", PenBlocks::VALUE, 0.45); - - // change pen (transparency) by (89.06) - auto block4 = std::make_shared("d", "pen_changePenColorParamBy"); - addDropdownInput(block4, "COLOR_PARAM", PenBlocks::COLOR_PARAM, "transparency"); - addValueInput(block4, "VALUE", PenBlocks::VALUE, 89.06); - - // change pen (invalid param) by (52.7) - auto block5 = std::make_shared("e", "pen_changePenColorParamBy"); - addDropdownInput(block5, "COLOR_PARAM", PenBlocks::COLOR_PARAM, "invalid param"); - addValueInput(block5, "VALUE", PenBlocks::VALUE, 52.7); - - // change pen (null block) by (35.2) - auto block6 = std::make_shared("f", "pen_changePenColorParamBy"); - addDropdownInput(block6, "COLOR_PARAM", PenBlocks::COLOR_PARAM, "", createNullBlock("g")); - addValueInput(block6, "VALUE", PenBlocks::VALUE, 35.2); - - compiler.init(); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::changePenColorBy)).WillOnce(Return(0)); - compiler.setBlock(block1); - PenBlocks::compileChangePenColorParamBy(&compiler); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::changePenSaturationBy)).WillOnce(Return(1)); - compiler.setBlock(block2); - PenBlocks::compileChangePenColorParamBy(&compiler); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::changePenBrightnessBy)).WillOnce(Return(2)); - compiler.setBlock(block3); - PenBlocks::compileChangePenColorParamBy(&compiler); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::changePenTransparencyBy)).WillOnce(Return(3)); - compiler.setBlock(block4); - PenBlocks::compileChangePenColorParamBy(&compiler); - - compiler.setBlock(block5); - PenBlocks::compileChangePenColorParamBy(&compiler); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::changePenColorParamBy)).WillOnce(Return(4)); - compiler.setBlock(block6); - PenBlocks::compileChangePenColorParamBy(&compiler); - - compiler.end(); - - ASSERT_EQ( - compiler.bytecode(), - std::vector( - { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_CONST, 1, vm::OP_EXEC, 1, vm::OP_CONST, 2, vm::OP_EXEC, 2, vm::OP_CONST, 3, vm::OP_EXEC, 3, - vm::OP_NULL, vm::OP_CONST, 4, vm::OP_EXEC, 4, vm::OP_HALT })); - ASSERT_EQ(compiler.constValues(), std::vector({ 34.6, 46.8, 0.45, 89.06, 35.2 })); - ASSERT_TRUE(compiler.variables().empty()); - ASSERT_TRUE(compiler.lists().empty()); -} - -TEST_F(PenBlocksTest, ChangePenColorParamByImpl) -{ - static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_CONST, 5, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_CONST, 6, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_CONST, 5, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode4[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_CONST, 6, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode5[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_CONST, 5, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode6[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_CONST, 6, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode7[] = { vm::OP_START, vm::OP_CONST, 3, vm::OP_CONST, 5, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode8[] = { vm::OP_START, vm::OP_CONST, 3, vm::OP_CONST, 6, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode9[] = { vm::OP_START, vm::OP_CONST, 4, vm::OP_CONST, 5, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode10[] = { vm::OP_START, vm::OP_CONST, 5, vm::OP_EXEC, 1, vm::OP_HALT }; - static unsigned int bytecode11[] = { vm::OP_START, vm::OP_CONST, 6, vm::OP_EXEC, 1, vm::OP_HALT }; - static unsigned int bytecode12[] = { vm::OP_START, vm::OP_CONST, 5, vm::OP_EXEC, 2, vm::OP_HALT }; - static unsigned int bytecode13[] = { vm::OP_START, vm::OP_CONST, 6, vm::OP_EXEC, 2, vm::OP_HALT }; - static unsigned int bytecode14[] = { vm::OP_START, vm::OP_CONST, 5, vm::OP_EXEC, 3, vm::OP_HALT }; - static unsigned int bytecode15[] = { vm::OP_START, vm::OP_CONST, 6, vm::OP_EXEC, 3, vm::OP_HALT }; - static unsigned int bytecode16[] = { vm::OP_START, vm::OP_CONST, 5, vm::OP_EXEC, 4, vm::OP_HALT }; - static unsigned int bytecode17[] = { vm::OP_START, vm::OP_CONST, 6, vm::OP_EXEC, 4, vm::OP_HALT }; - static BlockFunc - 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))); -} - -TEST_F(PenBlocksTest, SetPenColorParamTo) -{ - Compiler compiler(&m_engineMock); - - // set pen (color) to (34.6) - auto block1 = std::make_shared("a", "pen_setPenColorParamTo"); - addDropdownInput(block1, "COLOR_PARAM", PenBlocks::COLOR_PARAM, "color"); - addValueInput(block1, "VALUE", PenBlocks::VALUE, 34.6); - - // set pen (saturation) to (46.8) - auto block2 = std::make_shared("b", "pen_setPenColorParamTo"); - addDropdownInput(block2, "COLOR_PARAM", PenBlocks::COLOR_PARAM, "saturation"); - addValueInput(block2, "VALUE", PenBlocks::VALUE, 46.8); - - // set pen (brightness) to (0.45) - auto block3 = std::make_shared("c", "pen_setPenColorParamTo"); - addDropdownInput(block3, "COLOR_PARAM", PenBlocks::COLOR_PARAM, "brightness"); - addValueInput(block3, "VALUE", PenBlocks::VALUE, 0.45); - - // set pen (transparency) to (89.06) - auto block4 = std::make_shared("d", "pen_setPenColorParamTo"); - addDropdownInput(block4, "COLOR_PARAM", PenBlocks::COLOR_PARAM, "transparency"); - addValueInput(block4, "VALUE", PenBlocks::VALUE, 89.06); - - // set pen (invalid param) to (52.7) - auto block5 = std::make_shared("e", "pen_setPenColorParamTo"); - addDropdownInput(block5, "COLOR_PARAM", PenBlocks::COLOR_PARAM, "invalid param"); - addValueInput(block5, "VALUE", PenBlocks::VALUE, 52.7); - - // set pen (null block) to (35.2) - auto block6 = std::make_shared("f", "pen_setPenColorParamTo"); - addDropdownInput(block6, "COLOR_PARAM", PenBlocks::COLOR_PARAM, "", createNullBlock("g")); - addValueInput(block6, "VALUE", PenBlocks::VALUE, 35.2); - - compiler.init(); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::setPenColorTo)).WillOnce(Return(0)); - compiler.setBlock(block1); - PenBlocks::compileSetPenColorParamTo(&compiler); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::setPenSaturationTo)).WillOnce(Return(1)); - compiler.setBlock(block2); - PenBlocks::compileSetPenColorParamTo(&compiler); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::setPenBrightnessTo)).WillOnce(Return(2)); - compiler.setBlock(block3); - PenBlocks::compileSetPenColorParamTo(&compiler); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::setPenTransparencyTo)).WillOnce(Return(3)); - compiler.setBlock(block4); - PenBlocks::compileSetPenColorParamTo(&compiler); - - compiler.setBlock(block5); - PenBlocks::compileSetPenColorParamTo(&compiler); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::setPenColorParamTo)).WillOnce(Return(4)); - compiler.setBlock(block6); - PenBlocks::compileSetPenColorParamTo(&compiler); - - compiler.end(); - - ASSERT_EQ( - compiler.bytecode(), - std::vector( - { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_CONST, 1, vm::OP_EXEC, 1, vm::OP_CONST, 2, vm::OP_EXEC, 2, vm::OP_CONST, 3, vm::OP_EXEC, 3, - vm::OP_NULL, vm::OP_CONST, 4, vm::OP_EXEC, 4, vm::OP_HALT })); - ASSERT_EQ(compiler.constValues(), std::vector({ 34.6, 46.8, 0.45, 89.06, 35.2 })); - ASSERT_TRUE(compiler.variables().empty()); - ASSERT_TRUE(compiler.lists().empty()); -} - -TEST_F(PenBlocksTest, SetPenColorParamToImpl) -{ - static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_CONST, 5, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_CONST, 6, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_CONST, 7, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode4[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_CONST, 5, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode5[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_CONST, 6, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode6[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_CONST, 7, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode7[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_CONST, 5, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode8[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_CONST, 6, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode9[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_CONST, 7, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode10[] = { vm::OP_START, vm::OP_CONST, 3, vm::OP_CONST, 5, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode11[] = { vm::OP_START, vm::OP_CONST, 3, vm::OP_CONST, 6, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode12[] = { vm::OP_START, vm::OP_CONST, 3, vm::OP_CONST, 7, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode13[] = { vm::OP_START, vm::OP_CONST, 4, vm::OP_CONST, 5, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode14[] = { vm::OP_START, vm::OP_CONST, 5, vm::OP_EXEC, 1, vm::OP_HALT }; - static unsigned int bytecode15[] = { vm::OP_START, vm::OP_CONST, 6, vm::OP_EXEC, 1, vm::OP_HALT }; - static unsigned int bytecode16[] = { vm::OP_START, vm::OP_CONST, 7, vm::OP_EXEC, 1, vm::OP_HALT }; - static unsigned int bytecode17[] = { vm::OP_START, vm::OP_CONST, 5, vm::OP_EXEC, 2, vm::OP_HALT }; - static unsigned int bytecode18[] = { vm::OP_START, vm::OP_CONST, 6, vm::OP_EXEC, 2, vm::OP_HALT }; - static unsigned int bytecode19[] = { vm::OP_START, vm::OP_CONST, 7, vm::OP_EXEC, 2, vm::OP_HALT }; - static unsigned int bytecode20[] = { vm::OP_START, vm::OP_CONST, 5, vm::OP_EXEC, 3, vm::OP_HALT }; - static unsigned int bytecode21[] = { vm::OP_START, vm::OP_CONST, 6, vm::OP_EXEC, 3, vm::OP_HALT }; - static unsigned int bytecode22[] = { vm::OP_START, vm::OP_CONST, 7, vm::OP_EXEC, 3, vm::OP_HALT }; - static unsigned int bytecode23[] = { vm::OP_START, vm::OP_CONST, 5, vm::OP_EXEC, 4, vm::OP_HALT }; - static unsigned int bytecode24[] = { vm::OP_START, vm::OP_CONST, 6, vm::OP_EXEC, 4, vm::OP_HALT }; - static unsigned int bytecode25[] = { vm::OP_START, vm::OP_CONST, 7, vm::OP_EXEC, 4, vm::OP_HALT }; - 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))); -} - -TEST_F(PenBlocksTest, ChangePenSizeBy) -{ - Compiler compiler(&m_engineMock); - - // change pen size by (4.5) - auto block1 = std::make_shared("a", "pen_changePenSizeBy"); - addValueInput(block1, "SIZE", PenBlocks::SIZE, 4.5); - - // change pen size by (null block) - auto block2 = std::make_shared("b", "pen_changePenSizeBy"); - addObscuredInput(block2, "SIZE", PenBlocks::SIZE, createNullBlock("c")); - - compiler.init(); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::changePenSizeBy)).WillOnce(Return(2)); - compiler.setBlock(block1); - PenBlocks::compileChangePenSizeBy(&compiler); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::changePenSizeBy)).WillOnce(Return(2)); - compiler.setBlock(block2); - PenBlocks::compileChangePenSizeBy(&compiler); - - compiler.end(); - - ASSERT_EQ(compiler.bytecode(), std::vector({ vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 2, vm::OP_NULL, vm::OP_EXEC, 2, vm::OP_HALT })); - ASSERT_EQ(compiler.constValues().size(), 1); - ASSERT_EQ(compiler.constValues()[0].toDouble(), 4.5); - ASSERT_TRUE(compiler.variables().empty()); - ASSERT_TRUE(compiler.lists().empty()); -} - -TEST_F(PenBlocksTest, ChangePenSizeByImpl) -{ - static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_HALT }; - 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); -} - -TEST_F(PenBlocksTest, SetPenSizeTo) -{ - Compiler compiler(&m_engineMock); - - // set pen size to (51.46) - auto block1 = std::make_shared("a", "pen_setPenSizeTo"); - addValueInput(block1, "SIZE", PenBlocks::SIZE, 51.46); - - // set pen size to (null block) - auto block2 = std::make_shared("b", "pen_setPenSizeTo"); - addObscuredInput(block2, "SIZE", PenBlocks::SIZE, createNullBlock("c")); - - compiler.init(); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::setPenSizeTo)).WillOnce(Return(2)); - compiler.setBlock(block1); - PenBlocks::compileSetPenSizeTo(&compiler); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::setPenSizeTo)).WillOnce(Return(2)); - compiler.setBlock(block2); - PenBlocks::compileSetPenSizeTo(&compiler); - - compiler.end(); - - ASSERT_EQ(compiler.bytecode(), std::vector({ vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 2, vm::OP_NULL, vm::OP_EXEC, 2, vm::OP_HALT })); - ASSERT_EQ(compiler.constValues().size(), 1); - ASSERT_EQ(compiler.constValues()[0].toDouble(), 51.46); - ASSERT_TRUE(compiler.variables().empty()); - ASSERT_TRUE(compiler.lists().empty()); -} - -TEST_F(PenBlocksTest, SetPenSizeToImpl) -{ - static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_EXEC, 0, vm::OP_HALT }; - 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); -} - -TEST_F(PenBlocksTest, ChangePenShadeBy) -{ - Compiler compiler(&m_engineMock); - - // change pen shade by (4.5) - auto block1 = std::make_shared("a", "pen_changePenShadeBy"); - addValueInput(block1, "SHADE", PenBlocks::SHADE, 4.5); - - // change pen shade by (null block) - auto block2 = std::make_shared("b", "pen_changePenShadeBy"); - addObscuredInput(block2, "SHADE", PenBlocks::SHADE, createNullBlock("c")); - - compiler.init(); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::changePenShadeBy)).WillOnce(Return(2)); - compiler.setBlock(block1); - PenBlocks::compileChangePenShadeBy(&compiler); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::changePenShadeBy)).WillOnce(Return(2)); - compiler.setBlock(block2); - PenBlocks::compileChangePenShadeBy(&compiler); - - compiler.end(); - - ASSERT_EQ(compiler.bytecode(), std::vector({ vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 2, vm::OP_NULL, vm::OP_EXEC, 2, vm::OP_HALT })); - ASSERT_EQ(compiler.constValues().size(), 1); - ASSERT_EQ(compiler.constValues()[0].toDouble(), 4.5); - ASSERT_TRUE(compiler.variables().empty()); - ASSERT_TRUE(compiler.lists().empty()); -} - -TEST_F(PenBlocksTest, ChangePenShadeByImpl) -{ - static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_HALT }; - 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))); -} - -TEST_F(PenBlocksTest, SetPenShadeToNumber) -{ - Compiler compiler(&m_engineMock); - - // set pen shade to (4.5) - auto block1 = std::make_shared("a", "pen_setPenShadeToNumber"); - addValueInput(block1, "SHADE", PenBlocks::SHADE, 4.5); - - // set pen shade to (null block) - auto block2 = std::make_shared("b", "pen_setPenShadeToNumber"); - addObscuredInput(block2, "SHADE", PenBlocks::SHADE, createNullBlock("c")); - - compiler.init(); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::setPenShadeToNumber)).WillOnce(Return(2)); - compiler.setBlock(block1); - PenBlocks::compileSetPenShadeToNumber(&compiler); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::setPenShadeToNumber)).WillOnce(Return(2)); - compiler.setBlock(block2); - PenBlocks::compileSetPenShadeToNumber(&compiler); - - compiler.end(); - - ASSERT_EQ(compiler.bytecode(), std::vector({ vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 2, vm::OP_NULL, vm::OP_EXEC, 2, vm::OP_HALT })); - ASSERT_EQ(compiler.constValues().size(), 1); - ASSERT_EQ(compiler.constValues()[0].toDouble(), 4.5); - ASSERT_TRUE(compiler.variables().empty()); - ASSERT_TRUE(compiler.lists().empty()); -} - -TEST_F(PenBlocksTest, SetPenShadeToNumberImpl) -{ - static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_EXEC, 0, vm::OP_HALT }; - 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))); -} - -TEST_F(PenBlocksTest, ChangePenHueBy) -{ - Compiler compiler(&m_engineMock); - - // change pen hue by (4.5) - auto block1 = std::make_shared("a", "pen_changePenHueBy"); - addValueInput(block1, "HUE", PenBlocks::HUE, 4.5); - - // change pen hue by (null block) - auto block2 = std::make_shared("b", "pen_changePenHueBy"); - addObscuredInput(block2, "HUE", PenBlocks::HUE, createNullBlock("c")); - - compiler.init(); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::changePenHueBy)).WillOnce(Return(2)); - compiler.setBlock(block1); - PenBlocks::compileChangePenHueBy(&compiler); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::changePenHueBy)).WillOnce(Return(2)); - compiler.setBlock(block2); - PenBlocks::compileChangePenHueBy(&compiler); - - compiler.end(); - - ASSERT_EQ(compiler.bytecode(), std::vector({ vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 2, vm::OP_NULL, vm::OP_EXEC, 2, vm::OP_HALT })); - ASSERT_EQ(compiler.constValues().size(), 1); - ASSERT_EQ(compiler.constValues()[0].toDouble(), 4.5); - ASSERT_TRUE(compiler.variables().empty()); - ASSERT_TRUE(compiler.lists().empty()); -} - -TEST_F(PenBlocksTest, ChangePenHueByImpl) -{ - static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_HALT }; - 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))); -} - -TEST_F(PenBlocksTest, SetPenHueToNumber) -{ - Compiler compiler(&m_engineMock); - - // set pen hue to (54.09) - auto block1 = std::make_shared("a", "pen_setPenHueToNumber"); - addValueInput(block1, "HUE", PenBlocks::HUE, 54.09); - - // set pen hue to (null block) - auto block2 = std::make_shared("b", "pen_setPenHueToNumber"); - addObscuredInput(block2, "HUE", PenBlocks::HUE, createNullBlock("c")); - - compiler.init(); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::setPenHueToNumber)).WillOnce(Return(2)); - compiler.setBlock(block1); - PenBlocks::compileSetPenHueToNumber(&compiler); - - EXPECT_CALL(m_engineMock, functionIndex(&PenBlocks::setPenHueToNumber)).WillOnce(Return(2)); - compiler.setBlock(block2); - PenBlocks::compileSetPenHueToNumber(&compiler); - - compiler.end(); - - ASSERT_EQ(compiler.bytecode(), std::vector({ vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 2, vm::OP_NULL, vm::OP_EXEC, 2, vm::OP_HALT })); - ASSERT_EQ(compiler.constValues().size(), 1); - ASSERT_EQ(compiler.constValues()[0].toDouble(), 54.09); - ASSERT_TRUE(compiler.variables().empty()); - ASSERT_TRUE(compiler.lists().empty()); -} - -TEST_F(PenBlocksTest, SetPenHueToNumberImpl) -{ - static unsigned int bytecode1[] = { vm::OP_START, vm::OP_CONST, 0, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode2[] = { vm::OP_START, vm::OP_CONST, 1, vm::OP_EXEC, 0, vm::OP_HALT }; - static unsigned int bytecode3[] = { vm::OP_START, vm::OP_CONST, 2, vm::OP_EXEC, 0, vm::OP_HALT }; - 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))); -} diff --git a/test/lines.png b/test/lines.png index 245885e..5792818 100644 Binary files a/test/lines.png and b/test/lines.png differ diff --git a/test/lines_hq.png b/test/lines_hq.png index c78d1d3..2cdc7e7 100644 Binary files a/test/lines_hq.png and b/test/lines_hq.png differ 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