diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fdf033a..0b2eb1c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -72,6 +72,8 @@ qt_add_qml_module(scratchcpp-render textbubblepainter.h cputexturemanager.cpp cputexturemanager.h + effecttransform.cpp + effecttransform.h blocks/penextension.cpp blocks/penextension.h blocks/penblocks.cpp diff --git a/src/cputexturemanager.cpp b/src/cputexturemanager.cpp index c97b37d..f8daf67 100644 --- a/src/cputexturemanager.cpp +++ b/src/cputexturemanager.cpp @@ -2,6 +2,7 @@ #include "cputexturemanager.h" #include "texture.h" +#include "effecttransform.h" using namespace scratchcpprender; @@ -51,10 +52,36 @@ const std::vector &CpuTextureManager::getTextureConvexHullPoints(const T return it->second; } -bool CpuTextureManager::textureContainsPoint(const Texture &texture, const QPointF &localPoint) +QRgb CpuTextureManager::getPointColor(const Texture &texture, int x, int y, const std::unordered_map &effects) +{ + const int width = texture.width(); + const int height = texture.height(); + + if (!effects.empty()) { + // Get local position with effect transform + QVector2D transformedCoords; + const QVector2D localCoords(x / static_cast(width), y / static_cast(height)); + EffectTransform::transformPoint(effects, localCoords, transformedCoords); + x = transformedCoords.x() * width; + y = transformedCoords.y() * height; + } + + if ((x < 0 || x >= width) || (y < 0 || y >= height)) + return qRgba(0, 0, 0, 0); + + GLubyte *pixels = getTextureData(texture); + QRgb color = qRgba(pixels[(y * width + x) * 4], pixels[(y * width + x) * 4 + 1], pixels[(y * width + x) * 4 + 2], pixels[(y * width + x) * 4 + 3]); + + if (effects.empty()) + return color; + else + return EffectTransform::transformColor(effects, color); +} + +bool CpuTextureManager::textureContainsPoint(const Texture &texture, const QPointF &localPoint, const std::unordered_map &effects) { // https://github.com/scratchfoundation/scratch-render/blob/7b823985bc6fe92f572cc3276a8915e550f7c5e6/src/Silhouette.js#L219-L226 - return getPointAlpha(texture, localPoint.x(), localPoint.y()) > 0; + return qAlpha(getPointColor(texture, localPoint.x(), localPoint.y(), effects)) > 0; } void CpuTextureManager::removeTexture(const Texture &texture) @@ -137,12 +164,3 @@ bool CpuTextureManager::addTexture(const Texture &texture) return true; } - -int CpuTextureManager::getPointAlpha(const Texture &texture, int x, int y) -{ - if ((x < 0 || x >= texture.width()) || (y < 0 || y >= texture.height())) - return 0; - - GLubyte *pixels = getTextureData(texture); - return pixels[(y * texture.width() + x) * 4 + 3]; -} diff --git a/src/cputexturemanager.h b/src/cputexturemanager.h index df19588..4abdb5a 100644 --- a/src/cputexturemanager.h +++ b/src/cputexturemanager.h @@ -6,6 +6,8 @@ #include #include +#include "shadermanager.h" + namespace scratchcpprender { @@ -20,13 +22,13 @@ class CpuTextureManager GLubyte *getTextureData(const Texture &texture); const std::vector &getTextureConvexHullPoints(const Texture &texture); - bool textureContainsPoint(const Texture &texture, const QPointF &localPoint); + QRgb getPointColor(const Texture &texture, int x, int y, const std::unordered_map &effects); + bool textureContainsPoint(const Texture &texture, const QPointF &localPoint, const std::unordered_map &effects); void removeTexture(const Texture &texture); private: bool addTexture(const Texture &texture); - int getPointAlpha(const Texture &texture, int x, int y); std::unordered_map m_textureData; std::unordered_map> m_convexHullPoints; diff --git a/src/effecttransform.cpp b/src/effecttransform.cpp new file mode 100644 index 0000000..4ea1b70 --- /dev/null +++ b/src/effecttransform.cpp @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include + +#include "effecttransform.h" + +using namespace scratchcpprender; + +QRgb EffectTransform::transformColor(const std::unordered_map &effectValues, QRgb color) +{ + // https://github.com/scratchfoundation/scratch-render/blob/e075e5f5ebc95dec4a2718551624ad587c56f0a6/src/EffectTransform.js#L40-L119 + // If the color is fully transparent, don't bother attempting any transformations. + if (qAlpha(color) == 0) + return color; + + QColor inOutColor = QColor::fromRgba(color); + + std::unordered_map uniforms; + ShaderManager::getUniformValuesForEffects(effectValues, uniforms); + + const bool enableColor = uniforms[ShaderManager::Effect::Color] != 0; + const bool enableBrightness = uniforms[ShaderManager::Effect::Brightness] != 0; + + if (enableColor || enableBrightness) { + // gl_FragColor.rgb /= gl_FragColor.a + epsilon; + // Here, we're dividing by the (previously pre-multiplied) alpha to ensure HSV is properly calculated + // for partially transparent pixels. + const float alpha = inOutColor.alphaF(); + + if (alpha == 0) { + inOutColor.setRed(255); + inOutColor.setGreen(255); + inOutColor.setBlue(255); + } else { + inOutColor.setRedF(inOutColor.redF() / alpha); + inOutColor.setGreenF(inOutColor.greenF() / alpha); + inOutColor.setBlueF(inOutColor.blueF() / alpha); + } + + if (enableColor) { + // vec3 hsv = convertRGB2HSV(gl_FragColor.xyz); + QColor hsv = inOutColor.toHsv(); + + // this code forces grayscale values to be slightly saturated + // so that some slight change of hue will be visible + // const float minLightness = 0.11 / 2.0; + const float minV = 0.11f / 2.0f; + // const float minSaturation = 0.09; + const float minS = 0.09f; + // if (hsv.z < minLightness) hsv = vec3(0.0, 1.0, minLightness); + if (hsv.valueF() < minV) { + hsv.setHsvF(0.0f, 1.0f, minV); + // else if (hsv.y < minSaturation) hsv = vec3(0.0, minSaturation, hsv.z); + } else if (hsv.saturationF() < minS) { + hsv.setHsvF(0.0f, minS, hsv.valueF()); + } + + // hsv.x = mod(hsv.x + u_color, 1.0); + // if (hsv.x < 0.0) hsv.x += 1.0; + float hue = std::fmod(uniforms[ShaderManager::Effect::Color] + hsv.hueF(), 1.0f); + + if (hue < 0.0f) + hue += 1.0f; + + hsv.setHsvF(hue, hsv.saturationF(), hsv.valueF()); + + // gl_FragColor.rgb = convertHSV2RGB(hsl); + inOutColor = hsv.toRgb(); + } + + if (enableBrightness) { + const float brightness = uniforms[ShaderManager::Effect::Brightness] * 255.0f; + // gl_FragColor.rgb = clamp(gl_FragColor.rgb + vec3(u_brightness), vec3(0), vec3(1)); + inOutColor.setRed(std::clamp(inOutColor.red() + brightness, 0.0f, 255.0f)); + inOutColor.setGreen(std::clamp(inOutColor.green() + brightness, 0.0f, 255.0f)); + inOutColor.setBlue(std::clamp(inOutColor.blue() + brightness, 0.0f, 255.0f)); + } + + // gl_FragColor.rgb *= gl_FragColor.a + epsilon; + // Now we're doing the reverse, premultiplying by the alpha once again. + inOutColor.setRedF(inOutColor.redF() * alpha); + inOutColor.setGreenF(inOutColor.greenF() * alpha); + inOutColor.setBlueF(inOutColor.blueF() * alpha); + + // Restore alpha + inOutColor.setAlphaF(alpha); + } + + const float ghost = uniforms[ShaderManager::Effect::Ghost]; + + if (ghost != 1) { + // gl_FragColor *= u_ghost + inOutColor.setRedF(inOutColor.redF() * ghost); + inOutColor.setGreenF(inOutColor.greenF() * ghost); + inOutColor.setBlueF(inOutColor.blueF() * ghost); + inOutColor.setAlphaF(inOutColor.alphaF() * ghost); + } + + return inOutColor.rgba(); +} + +void EffectTransform::transformPoint(const std::unordered_map &effectValues, const QVector2D &vec, QVector2D &dst) +{ + // TODO: Implement remaining effects + dst = vec; +} diff --git a/src/effecttransform.h b/src/effecttransform.h new file mode 100644 index 0000000..6992f25 --- /dev/null +++ b/src/effecttransform.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later + +#pragma once + +#include + +#include "shadermanager.h" + +namespace scratchcpprender +{ + +class EffectTransform +{ + public: + EffectTransform() = delete; + + static QRgb transformColor(const std::unordered_map &effectValues, QRgb color); + static void transformPoint(const std::unordered_map &effectValues, const QVector2D &vec, QVector2D &dst); +}; + +} // namespace scratchcpprender diff --git a/src/renderedtarget.cpp b/src/renderedtarget.cpp index 2550bf4..d73e797 100644 --- a/src/renderedtarget.cpp +++ b/src/renderedtarget.cpp @@ -636,11 +636,7 @@ QRgb RenderedTarget::colorAtScratchPoint(double x, double y) const if ((x < 0 || x >= width) || (y < 0 || y >= height)) return qRgba(0, 0, 0, 0); - GLubyte *data = textureManager()->getTextureData(m_cpuTexture); - const int index = (y * width + x) * 4; // RGBA channels - Q_ASSERT(index >= 0 && index < width * height * 4); - // TODO: Apply graphic effects (#117) - return qRgba(data[index], data[index + 1], data[index + 2], data[index + 3]); + return textureManager()->getPointColor(m_cpuTexture, x, y, m_graphicEffects); } bool RenderedTarget::touchingClones(const std::vector &clones) const @@ -817,7 +813,7 @@ void RenderedTarget::updateHullPoints() bool RenderedTarget::containsLocalPoint(const QPointF &point) const { - return textureManager()->textureContainsPoint(m_cpuTexture, point); + return textureManager()->textureContainsPoint(m_cpuTexture, point, m_graphicEffects); } QPointF RenderedTarget::transformPoint(double scratchX, double scratchY, double originX, double originY, double rot) const diff --git a/src/shadermanager.cpp b/src/shadermanager.cpp index a87f9b7..928e32f 100644 --- a/src/shadermanager.cpp +++ b/src/shadermanager.cpp @@ -101,12 +101,10 @@ QOpenGLShaderProgram *ShaderManager::getShaderProgram(const std::unordered_mapsecond; } -void ShaderManager::setUniforms(QOpenGLShaderProgram *program, int textureUnit, const std::unordered_map &effectValues) +void ShaderManager::getUniformValuesForEffects(const std::unordered_map &effectValues, std::unordered_map &dst) { - // Set the texture unit - program->setUniformValue(TEXTURE_UNIT_UNIFORM, textureUnit); + dst.clear(); - // Set the uniform values for the enabled effects and reset the other effects for (const auto &[effect, name] : EFFECT_TO_NAME) { const auto it = effectValues.find(effect); double value; @@ -117,10 +115,23 @@ void ShaderManager::setUniforms(QOpenGLShaderProgram *program, int textureUnit, value = it->second; auto converter = EFFECT_CONVERTER.at(effect); - program->setUniformValue(EFFECT_UNIFORM_NAME.at(effect), converter(value)); + dst[effect] = converter(value); } } +void ShaderManager::setUniforms(QOpenGLShaderProgram *program, int textureUnit, const std::unordered_map &effectValues) +{ + // Set the texture unit + program->setUniformValue(TEXTURE_UNIT_UNIFORM, textureUnit); + + // Set uniform values + std::unordered_map values; + getUniformValuesForEffects(effectValues, values); + + for (const auto &[effect, value] : values) + program->setUniformValue(EFFECT_UNIFORM_NAME.at(effect), value); +} + void ShaderManager::registerEffects() { // Register graphic effects in libscratchcpp diff --git a/src/shadermanager.h b/src/shadermanager.h index dde8245..edcb614 100644 --- a/src/shadermanager.h +++ b/src/shadermanager.h @@ -26,6 +26,7 @@ class ShaderManager : public QObject static ShaderManager *instance(); QOpenGLShaderProgram *getShaderProgram(const std::unordered_map &effectValues); + static void getUniformValuesForEffects(const std::unordered_map &effectValues, std::unordered_map &dst); void setUniforms(QOpenGLShaderProgram *program, int textureUnit, const std::unordered_map &effectValues); private: diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dcd6bef..0159df6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -41,3 +41,4 @@ add_subdirectory(graphicseffect) add_subdirectory(shadermanager) add_subdirectory(textbubbleshape) add_subdirectory(textbubblepainter) +add_subdirectory(effecttransform) diff --git a/test/effecttransform/CMakeLists.txt b/test/effecttransform/CMakeLists.txt new file mode 100644 index 0000000..89e9206 --- /dev/null +++ b/test/effecttransform/CMakeLists.txt @@ -0,0 +1,14 @@ +add_executable( + effecttransform_test + effecttransform_test.cpp +) + +target_link_libraries( + effecttransform_test + GTest::gtest_main + scratchcpp-render + qnanopainter +) + +add_test(effecttransform_test) +gtest_discover_tests(effecttransform_test) diff --git a/test/effecttransform/effecttransform_test.cpp b/test/effecttransform/effecttransform_test.cpp new file mode 100644 index 0000000..d5f92fa --- /dev/null +++ b/test/effecttransform/effecttransform_test.cpp @@ -0,0 +1,123 @@ +#include +#include + +#include "../common.h" + +using namespace scratchcpprender; + +class EffectTransformTest : public testing::Test +{ + public: + void SetUp() override { } + + std::unordered_map m_effects; +}; + +TEST_F(EffectTransformTest, NoEffect) +{ + QRgb color = qRgba(0, 0, 0, 0); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), color); + + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), color); + + color = qRgba(0, 255, 255, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), color); + + color = qRgba(255, 255, 255, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), color); + + QVector2D dst; + EffectTransform::transformPoint(m_effects, QVector2D(0.5, -0.3), dst); + ASSERT_EQ(dst, QVector2D(0.5, -0.3)); +} + +TEST_F(EffectTransformTest, ColorEffect) +{ + // 100 + m_effects[ShaderManager::Effect::Color] = 100; + QRgb color = qRgba(0, 0, 0, 0); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), color); + + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgb(0, 255, 255)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(128, 100, 100, 128)); + + // 175 + m_effects[ShaderManager::Effect::Color] = 175; + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgb(255, 0, 191)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(100, 128, 107, 128)); +} + +TEST_F(EffectTransformTest, BrightnessEffect) +{ + // -100 + m_effects[ShaderManager::Effect::Brightness] = -100; + QRgb color = qRgba(0, 0, 0, 0); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), color); + + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgb(0, 0, 0)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(0, 0, 0, 128)); + + // -50 + m_effects[ShaderManager::Effect::Brightness] = -50; + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgb(127, 0, 0)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(36, 64, 64, 128)); + + // 50 + m_effects[ShaderManager::Effect::Brightness] = 50; + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgb(255, 127, 127)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(128, 128, 128, 128)); + + // 100 + m_effects[ShaderManager::Effect::Brightness] = 100; + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgb(255, 255, 255)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(128, 128, 128, 128)); +} + +TEST_F(EffectTransformTest, GhostEffect) +{ + // 25 + m_effects[ShaderManager::Effect::Ghost] = 25; + QRgb color = qRgba(0, 0, 0, 0); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), color); + + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(191, 0, 0, 191)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(75, 191, 150, 96)); + + // 50 + m_effects[ShaderManager::Effect::Ghost] = 50; + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(128, 0, 0, 128)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(50, 128, 100, 64)); + + // 100 + m_effects[ShaderManager::Effect::Ghost] = 100; + color = qRgba(255, 0, 0, 255); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(0, 0, 0, 0)); + + color = qRgba(100, 255, 200, 128); + ASSERT_EQ(EffectTransform::transformColor(m_effects, color), qRgba(0, 0, 0, 0)); +} diff --git a/test/renderedtarget/renderedtarget_test.cpp b/test/renderedtarget/renderedtarget_test.cpp index 484bdd7..d8883d5 100644 --- a/test/renderedtarget/renderedtarget_test.cpp +++ b/test/renderedtarget/renderedtarget_test.cpp @@ -459,6 +459,15 @@ TEST_F(RenderedTargetTest, CpuRendering) ASSERT_EQ(target.colorAtScratchPoint(-225, 162), 4286611456); // [3, 3] ASSERT_EQ(target.colorAtScratchPoint(-224.7, 161.5), 4286611456); // [3.3, 3.5] + target.setGraphicEffect(ShaderManager::Effect::Color, 50); + ASSERT_EQ(target.colorAtScratchPoint(-227, 162), 4286595072); // [1, 3] + ASSERT_EQ(target.colorAtScratchPoint(-226, 162), 4294934720); // [2, 3] + ASSERT_EQ(target.colorAtScratchPoint(-225, 162), 4278222912); // [3, 3] + ASSERT_EQ(target.colorAtScratchPoint(-224.7, 161.5), 4278222912); // [3.3, 3.5] + target.setGraphicEffect(ShaderManager::Effect::Color, 0); + + // TODO: Test point transform (graphic effects that change shape) + // Cleanup context.doneCurrent(); } diff --git a/test/shadermanager/shadermanager_test.cpp b/test/shadermanager/shadermanager_test.cpp index ad6a68d..6bbe0a2 100644 --- a/test/shadermanager/shadermanager_test.cpp +++ b/test/shadermanager/shadermanager_test.cpp @@ -179,6 +179,8 @@ TEST_F(ShaderManagerTest, ColorEffectValue) static const QString uniformName = "u_" + effectName; static const ShaderManager::Effect effect = ShaderManager::Effect::Color; + std::unordered_map values; + QOpenGLFunctions glF(&m_context); glF.initializeOpenGLFunctions(); ShaderManager manager; @@ -190,28 +192,34 @@ TEST_F(ShaderManagerTest, ColorEffectValue) std::unordered_map effects = { { effect, 64.9 } }; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); GLfloat value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(value, 0.3245f); + ASSERT_EQ(values.at(effect), value); // Below the minimum effects[effect] = -395.7; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(std::round(value * 100.0f) / 100.0f, 0.02f); + ASSERT_EQ(values.at(effect), value); // Above the maximum effects[effect] = 579.05; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(std::round(value * 100.0f) / 100.0f, 0.9f); + ASSERT_EQ(values.at(effect), value); program.release(); } @@ -222,6 +230,8 @@ TEST_F(ShaderManagerTest, BrightnessEffectValue) static const QString uniformName = "u_" + effectName; static const ShaderManager::Effect effect = ShaderManager::Effect::Brightness; + std::unordered_map values; + QOpenGLFunctions glF(&m_context); glF.initializeOpenGLFunctions(); ShaderManager manager; @@ -233,28 +243,34 @@ TEST_F(ShaderManagerTest, BrightnessEffectValue) std::unordered_map effects = { { effect, 4.6 } }; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); GLfloat value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(value, 0.046f); + ASSERT_EQ(values.at(effect), value); // Below the minimum effects[effect] = -102.9; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(value, -1.0f); + ASSERT_EQ(values.at(effect), value); // Above the maximum effects[effect] = 353.2; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(value, 1.0f); + ASSERT_EQ(values.at(effect), value); program.release(); } @@ -265,6 +281,8 @@ TEST_F(ShaderManagerTest, GhostEffectValue) static const QString uniformName = "u_" + effectName; static const ShaderManager::Effect effect = ShaderManager::Effect::Ghost; + std::unordered_map values; + QOpenGLFunctions glF(&m_context); glF.initializeOpenGLFunctions(); ShaderManager manager; @@ -276,28 +294,34 @@ TEST_F(ShaderManagerTest, GhostEffectValue) std::unordered_map effects = { { effect, 58.5 } }; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); GLfloat value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(std::round(value * 1000.0f) / 1000.0f, 0.415f); + ASSERT_EQ(values.at(effect), value); // Below the minimum effects[effect] = -20.8; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(value, 1.0f); + ASSERT_EQ(values.at(effect), value); // Above the maximum effects[effect] = 248.2; program.bind(); manager.setUniforms(&program, 0, effects); + manager.getUniformValuesForEffects(effects, values); value = 0.0f; glF.glGetUniformfv(program.programId(), program.uniformLocation(uniformName), &value); ASSERT_EQ(value, 0.0f); + ASSERT_EQ(values.at(effect), value); program.release(); } diff --git a/test/texture/cputexturemanager_test.cpp b/test/texture/cputexturemanager_test.cpp index 2d0ec05..436eb32 100644 --- a/test/texture/cputexturemanager_test.cpp +++ b/test/texture/cputexturemanager_test.cpp @@ -129,6 +129,49 @@ TEST_F(CpuTextureManagerTest, TextureDataAndHullPoints) context.doneCurrent(); } +TEST_F(CpuTextureManagerTest, GetPointColor) +{ + // Create OpenGL context + QOpenGLContext context; + QOffscreenSurface surface; + createContextAndSurface(&context, &surface); + + // Paint + QNanoPainter painter; + ImagePainter imgPainter(&painter, "image.png"); + + // Read texture data + Texture texture(imgPainter.fbo()->texture(), imgPainter.fbo()->size()); + + // Test + CpuTextureManager manager; + ASSERT_EQ(manager.getPointColor(texture, 0, 0, {}), qRgba(0, 0, 0, 0)); + ASSERT_EQ(manager.getPointColor(texture, 1, 0, {}), qRgba(0, 0, 0, 0)); + ASSERT_EQ(manager.getPointColor(texture, 2, 0, {}), qRgba(0, 0, 0, 0)); + ASSERT_EQ(manager.getPointColor(texture, 3, 0, {}), qRgba(0, 0, 0, 0)); + + ASSERT_FALSE(manager.textureContainsPoint(texture, { 0, 1 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 1, 1 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 1.4, 1.25 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 2, 1 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 3, 1 }, {})); + + ASSERT_EQ(manager.getPointColor(texture, 0, 1, {}), qRgba(0, 0, 0, 0)); + ASSERT_EQ(manager.getPointColor(texture, 1, 1, {}), qRgb(0, 0, 255)); + ASSERT_EQ(manager.getPointColor(texture, 2, 1, {}), qRgb(255, 0, 255)); + ASSERT_EQ(manager.getPointColor(texture, 3, 1, {}), qRgb(255, 128, 128)); + + std::unordered_map effects = { { ShaderManager::Effect::Color, 50 } }; + ASSERT_EQ(manager.getPointColor(texture, 1, 1, effects), qRgb(255, 0, 128)); + ASSERT_EQ(manager.getPointColor(texture, 2, 1, effects), qRgb(255, 128, 0)); + ASSERT_EQ(manager.getPointColor(texture, 3, 1, effects), qRgb(192, 255, 128)); + + // TODO: Test point transform (graphic effects that change shape) + + // Cleanup + context.doneCurrent(); +} + TEST_F(CpuTextureManagerTest, TextureContainsPoint) { // Create OpenGL context @@ -145,26 +188,34 @@ TEST_F(CpuTextureManagerTest, TextureContainsPoint) // Test CpuTextureManager manager; - ASSERT_FALSE(manager.textureContainsPoint(texture, { 0, 0 })); - ASSERT_FALSE(manager.textureContainsPoint(texture, { 1, 0 })); - ASSERT_FALSE(manager.textureContainsPoint(texture, { 2, 0 })); - ASSERT_FALSE(manager.textureContainsPoint(texture, { 3, 0 })); - - ASSERT_FALSE(manager.textureContainsPoint(texture, { 0, 1 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 1, 1 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 1.4, 1.25 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 2, 1 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 3, 1 })); - - ASSERT_TRUE(manager.textureContainsPoint(texture, { 1, 2 })); - ASSERT_FALSE(manager.textureContainsPoint(texture, { 2, 2 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 3, 2 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 3.5, 2.1 })); - - ASSERT_TRUE(manager.textureContainsPoint(texture, { 1, 3 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 2, 3 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 3, 3 })); - ASSERT_TRUE(manager.textureContainsPoint(texture, { 3.3, 3.5 })); + ASSERT_FALSE(manager.textureContainsPoint(texture, { 0, 0 }, {})); + ASSERT_FALSE(manager.textureContainsPoint(texture, { 1, 0 }, {})); + ASSERT_FALSE(manager.textureContainsPoint(texture, { 2, 0 }, {})); + ASSERT_FALSE(manager.textureContainsPoint(texture, { 3, 0 }, {})); + + ASSERT_FALSE(manager.textureContainsPoint(texture, { 0, 1 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 1, 1 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 1.4, 1.25 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 2, 1 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 3, 1 }, {})); + + ASSERT_TRUE(manager.textureContainsPoint(texture, { 1, 2 }, {})); + ASSERT_FALSE(manager.textureContainsPoint(texture, { 2, 2 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 3, 2 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 3.5, 2.1 }, {})); + + ASSERT_TRUE(manager.textureContainsPoint(texture, { 1, 3 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 2, 3 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 3, 3 }, {})); + ASSERT_TRUE(manager.textureContainsPoint(texture, { 3.3, 3.5 }, {})); + + std::unordered_map effects = { { ShaderManager::Effect::Ghost, 100 } }; + ASSERT_FALSE(manager.textureContainsPoint(texture, { 1, 3 }, effects)); + ASSERT_FALSE(manager.textureContainsPoint(texture, { 2, 3 }, effects)); + ASSERT_FALSE(manager.textureContainsPoint(texture, { 3, 3 }, effects)); + ASSERT_FALSE(manager.textureContainsPoint(texture, { 3.3, 3.5 }, effects)); + + // TODO: Test point transform (graphic effects that change shape) // Cleanup context.doneCurrent();