From 0948b078e1789605c42746a024df6039bb566b45 Mon Sep 17 00:00:00 2001 From: vaxerski <43317083+vaxerski@users.noreply.github.com> Date: Sat, 26 Nov 2022 17:56:43 +0000 Subject: [PATCH] added border gradients --- src/Compositor.cpp | 23 +++++++++---- src/Window.cpp | 8 ++--- src/Window.hpp | 5 ++- src/config/ConfigDataValues.hpp | 48 ++++++++++++++++++++++++++ src/config/ConfigManager.cpp | 61 +++++++++++++++++++++++++++++++-- src/config/ConfigManager.hpp | 2 ++ src/debug/HyprCtl.cpp | 7 ++-- src/defines.hpp | 2 ++ src/render/OpenGL.cpp | 44 ++++++++++++++---------- src/render/OpenGL.hpp | 4 ++- src/render/Renderer.cpp | 12 +++++-- src/render/Shader.hpp | 4 +++ src/render/shaders/Border.hpp | 20 +++++++++-- 13 files changed, 200 insertions(+), 40 deletions(-) create mode 100644 src/config/ConfigDataValues.hpp diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 686354237..a45d48a0f 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -1407,8 +1407,8 @@ void CCompositor::updateAllWindowsAnimatedDecorationValues() { void CCompositor::updateWindowAnimatedDecorationValues(CWindow* pWindow) { // optimization - static int64_t* ACTIVECOL = &g_pConfigManager->getConfigValuePtr("general:col.active_border")->intValue; - static int64_t* INACTIVECOL = &g_pConfigManager->getConfigValuePtr("general:col.inactive_border")->intValue; + static auto *const ACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.active_border")->data.get(); + static auto *const INACTIVECOL = (CGradientValueData*)g_pConfigManager->getConfigValuePtr("general:col.inactive_border")->data.get(); static auto *const PINACTIVEALPHA = &g_pConfigManager->getConfigValuePtr("decoration:inactive_opacity")->floatValue; static auto *const PACTIVEALPHA = &g_pConfigManager->getConfigValuePtr("decoration:active_opacity")->floatValue; static auto *const PFULLSCREENALPHA = &g_pConfigManager->getConfigValuePtr("decoration:fullscreen_opacity")->floatValue; @@ -1416,14 +1416,25 @@ void CCompositor::updateWindowAnimatedDecorationValues(CWindow* pWindow) { static auto *const PSHADOWCOLINACTIVE = &g_pConfigManager->getConfigValuePtr("decoration:col.shadow_inactive")->intValue; static auto *const PDIMSTRENGTH = &g_pConfigManager->getConfigValuePtr("decoration:dim_strength")->floatValue; + auto setBorderColor = [&] (CGradientValueData grad) -> void { + + if (grad == pWindow->m_cRealBorderColor) + return; + + pWindow->m_cRealBorderColorPrevious = pWindow->m_cRealBorderColor; + pWindow->m_cRealBorderColor = grad; + pWindow->m_fBorderAnimationProgress.setValueAndWarp(0.f); + pWindow->m_fBorderAnimationProgress = 1.f; + }; + // border const auto RENDERDATA = g_pLayoutManager->getCurrentLayout()->requestRenderHints(pWindow); if (RENDERDATA.isBorderColor) - pWindow->m_cRealBorderColor = RENDERDATA.borderColor; + setBorderColor(RENDERDATA.borderColor); else - pWindow->m_cRealBorderColor = CColor(pWindow == m_pLastWindow ? - (pWindow->m_sSpecialRenderData.activeBorderColor >= 0 ? pWindow->m_sSpecialRenderData.activeBorderColor : *ACTIVECOL) : - (pWindow->m_sSpecialRenderData.inactiveBorderColor >= 0 ? pWindow->m_sSpecialRenderData.inactiveBorderColor : *INACTIVECOL)); + setBorderColor(pWindow == m_pLastWindow ? + (pWindow->m_sSpecialRenderData.activeBorderColor >= 0 ? CGradientValueData(pWindow->m_sSpecialRenderData.activeBorderColor) : *ACTIVECOL) : + (pWindow->m_sSpecialRenderData.inactiveBorderColor >= 0 ? CGradientValueData(pWindow->m_sSpecialRenderData.inactiveBorderColor) : *INACTIVECOL)); // opacity diff --git a/src/Window.cpp b/src/Window.cpp index f13cb6d1b..274c3d57a 100644 --- a/src/Window.cpp +++ b/src/Window.cpp @@ -5,7 +5,7 @@ CWindow::CWindow() { m_vRealPosition.create(AVARTYPE_VECTOR, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), (void*)this, AVARDAMAGE_ENTIRE); m_vRealSize.create(AVARTYPE_VECTOR, g_pConfigManager->getAnimationPropertyConfig("windowsIn"), (void*)this, AVARDAMAGE_ENTIRE); - m_cRealBorderColor.create(AVARTYPE_COLOR, g_pConfigManager->getAnimationPropertyConfig("border"), (void*)this, AVARDAMAGE_BORDER); + m_fBorderAnimationProgress.create(AVARTYPE_FLOAT, g_pConfigManager->getAnimationPropertyConfig("border"), (void*)this, AVARDAMAGE_BORDER); m_fAlpha.create(AVARTYPE_FLOAT, g_pConfigManager->getAnimationPropertyConfig("fadeIn"), (void*)this, AVARDAMAGE_ENTIRE); m_fActiveInactiveAlpha.create(AVARTYPE_FLOAT, g_pConfigManager->getAnimationPropertyConfig("fadeSwitch"), (void*)this, AVARDAMAGE_ENTIRE); m_cRealShadowColor.create(AVARTYPE_COLOR, g_pConfigManager->getAnimationPropertyConfig("fadeShadow"), (void*)this, AVARDAMAGE_SHADOW); @@ -262,7 +262,7 @@ void CWindow::onUnmap() { m_vRealPosition.setCallbackOnEnd(unregisterVar); m_vRealSize.setCallbackOnEnd(unregisterVar); - m_cRealBorderColor.setCallbackOnEnd(unregisterVar); + m_fBorderAnimationProgress.setCallbackOnEnd(unregisterVar); m_fActiveInactiveAlpha.setCallbackOnEnd(unregisterVar); m_fAlpha.setCallbackOnEnd(unregisterVar); m_cRealShadowColor.setCallbackOnEnd(unregisterVar); @@ -276,7 +276,7 @@ void CWindow::onMap() { // JIC, reset the callbacks. If any are set, we'll make sure they are cleared so we don't accidentally unset them. (In case a window got remapped) m_vRealPosition.resetAllCallbacks(); m_vRealSize.resetAllCallbacks(); - m_cRealBorderColor.resetAllCallbacks(); + m_fBorderAnimationProgress.resetAllCallbacks(); m_fActiveInactiveAlpha.resetAllCallbacks(); m_fAlpha.resetAllCallbacks(); m_cRealShadowColor.resetAllCallbacks(); @@ -284,7 +284,7 @@ void CWindow::onMap() { m_vRealPosition.registerVar(); m_vRealSize.registerVar(); - m_cRealBorderColor.registerVar(); + m_fBorderAnimationProgress.registerVar(); m_fActiveInactiveAlpha.registerVar(); m_fAlpha.registerVar(); m_cRealShadowColor.registerVar(); diff --git a/src/Window.hpp b/src/Window.hpp index 09ad5f489..5aab6db88 100644 --- a/src/Window.hpp +++ b/src/Window.hpp @@ -6,6 +6,7 @@ #include "helpers/AnimatedVariable.hpp" #include "render/decorations/IHyprWindowDecoration.hpp" #include +#include "config/ConfigDataValues.hpp" enum eIdleInhibitMode { IDLEINHIBIT_NONE = 0, @@ -136,7 +137,9 @@ public: SSurfaceTreeNode* m_pSurfaceTree = nullptr; // Animated border - CAnimatedVariable m_cRealBorderColor; + CGradientValueData m_cRealBorderColor = {0}; + CGradientValueData m_cRealBorderColorPrevious = {0}; + CAnimatedVariable m_fBorderAnimationProgress; // Fade in-out CAnimatedVariable m_fAlpha; diff --git a/src/config/ConfigDataValues.hpp b/src/config/ConfigDataValues.hpp new file mode 100644 index 000000000..f2ea79a0c --- /dev/null +++ b/src/config/ConfigDataValues.hpp @@ -0,0 +1,48 @@ +#pragma once +#include "../defines.hpp" + +enum eConfigValueDataTypes { + CVD_TYPE_INVALID = -1, + CVD_TYPE_GRADIENT = 0 +}; + +interface ICustomConfigValueData { +public: + virtual ~ICustomConfigValueData() = 0; + + virtual eConfigValueDataTypes getDataType() = 0; +}; + +class CGradientValueData : public ICustomConfigValueData { +public: + CGradientValueData(CColor col) { + m_vColors.push_back(col); + }; + virtual ~CGradientValueData() { }; + + virtual eConfigValueDataTypes getDataType() { + return CVD_TYPE_GRADIENT; + } + + void reset(CColor col) { + m_vColors.clear(); + m_vColors.emplace_back(col); + m_fAngle = 0; + } + + /* Vector containing the colors */ + std::vector m_vColors; + + /* Float corresponding to the angle (rad) */ + float m_fAngle = 0; + + bool operator==(const CGradientValueData& other) { + if (other.m_vColors.size() != m_vColors.size() || m_fAngle != other.m_fAngle) + return false; + + for (size_t i = 0; i < m_vColors.size(); ++i) + if (m_vColors[i] != other.m_vColors[i]) return false; + + return true; + } +}; \ No newline at end of file diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 0f046c1c1..795cb473e 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -11,6 +11,9 @@ #include CConfigManager::CConfigManager() { + configValues["general:col.active_border"].data = std::make_shared(0xffffffff); + configValues["general:col.inactive_border"].data = std::make_shared(0xff444444); + setDefaultVars(); setDefaultAnimationVars(); @@ -39,8 +42,8 @@ void CConfigManager::setDefaultVars() { configValues["general:no_border_on_floating"].intValue = 0; configValues["general:gaps_in"].intValue = 5; configValues["general:gaps_out"].intValue = 20; - configValues["general:col.active_border"].intValue = 0xffffffff; - configValues["general:col.inactive_border"].intValue = 0xff444444; + ((CGradientValueData*)configValues["general:col.active_border"].data.get())->reset(0xffffffff); + ((CGradientValueData*)configValues["general:col.inactive_border"].data.get())->reset(0xff444444); configValues["general:cursor_inactive_timeout"].intValue = 0; configValues["general:no_cursor_warps"].intValue = 0; @@ -349,6 +352,56 @@ void CConfigManager::configSetValueSafe(const std::string& COMMAND, const std::s Debug::log(WARN, "Error reading value of %s", COMMAND.c_str()); parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">."; } + } else if (CONFIGENTRY->data.get() != nullptr) { + + switch (CONFIGENTRY->data->getDataType()) { + case CVD_TYPE_GRADIENT: { + + CVarList varlist(VALUE, 0, ' '); + + CGradientValueData* data = (CGradientValueData*)CONFIGENTRY->data.get(); + data->m_vColors.clear(); + + for (auto& var : varlist) { + if (var.find("deg") != std::string::npos) { + // last arg + try { + data->m_fAngle = std::stoi(var.substr(0, var.find("deg"))) * (PI / 180.0); // radians + } catch (...) { + Debug::log(WARN, "Error reading value of %s", COMMAND.c_str()); + parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">."; + } + + break; + } + + if (data->m_vColors.size() >= 10) { + Debug::log(WARN, "Error reading value of %s", COMMAND.c_str()); + parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">. Max colors in a gradient is 10."; + break; + } + + try { + data->m_vColors.push_back(configStringToInt(var)); + } catch (std::exception& e) { + Debug::log(WARN, "Error reading value of %s", COMMAND.c_str()); + parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">. " + e.what(); + } + } + + if (data->m_vColors.size() == 0) { + Debug::log(WARN, "Error reading value of %s", COMMAND.c_str()); + parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">. No colors provided."; + + data->m_vColors.push_back(0); // transparent + } + + break; + } + default: { + break; + } + } } } @@ -1607,3 +1660,7 @@ CMonitor* CConfigManager::getBoundMonitorForWS(std::string wsname) { void CConfigManager::addExecRule(SExecRequestedRule rule) { execRequestedRules.push_back(rule); } + +ICustomConfigValueData::~ICustomConfigValueData() { + ; // empty +} diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp index 58f020ba5..188f14e47 100644 --- a/src/config/ConfigManager.hpp +++ b/src/config/ConfigManager.hpp @@ -13,6 +13,7 @@ #include "../Window.hpp" #include "defaultConfig.hpp" +#include "ConfigDataValues.hpp" #define STRVAL_EMPTY "[[EMPTY]]" @@ -24,6 +25,7 @@ struct SConfigValue { float floatValue = -__FLT_MAX__; std::string strValue = ""; Vector2D vecValue = Vector2D(-__FLT_MAX__, -__FLT_MAX__); + std::shared_ptr data; bool set = false; // used for device configs }; diff --git a/src/debug/HyprCtl.cpp b/src/debug/HyprCtl.cpp index 265d1aed9..70fb3bd55 100644 --- a/src/debug/HyprCtl.cpp +++ b/src/debug/HyprCtl.cpp @@ -709,7 +709,7 @@ std::string dispatchGetOption(std::string request, HyprCtl::eHyprCtlOutputFormat return "no such option"; if (format == HyprCtl::eHyprCtlOutputFormat::FORMAT_NORMAL) - return getFormat("option %s\n\tint: %lld\n\tfloat: %f\n\tstr: \"%s\"", curitem.c_str(), PCFGOPT->intValue, PCFGOPT->floatValue, PCFGOPT->strValue.c_str()); + return getFormat("option %s\n\tint: %lld\n\tfloat: %f\n\tstr: \"%s\"\n\tdata: %x", curitem.c_str(), PCFGOPT->intValue, PCFGOPT->floatValue, PCFGOPT->strValue.c_str(), PCFGOPT->data.get()); else { return getFormat( R"#( @@ -717,9 +717,10 @@ R"#( "option": "%s", "int": %lld, "float": %f, - "str": "%s" + "str": "%s", + "data": "0x%x" } -)#", curitem.c_str(), PCFGOPT->intValue, PCFGOPT->floatValue, PCFGOPT->strValue.c_str() +)#", curitem.c_str(), PCFGOPT->intValue, PCFGOPT->floatValue, PCFGOPT->strValue.c_str(), PCFGOPT->data.get() ); } } diff --git a/src/defines.hpp b/src/defines.hpp index c6bd7d6e7..4e517754b 100644 --- a/src/defines.hpp +++ b/src/defines.hpp @@ -79,3 +79,5 @@ #endif #define SPECIAL_WORKSPACE_ID -99 + +#define PI 3.14159265358979 diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 8010f9cac..676a49a99 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -239,7 +239,9 @@ void CHyprOpenGLImpl::initShaders() { m_RenderData.pCurrentMonData->m_shBORDER1.fullSize = glGetUniformLocation(prog, "fullSize"); m_RenderData.pCurrentMonData->m_shBORDER1.radius = glGetUniformLocation(prog, "radius"); m_RenderData.pCurrentMonData->m_shBORDER1.primitiveMultisample = glGetUniformLocation(prog, "primitiveMultisample"); - m_RenderData.pCurrentMonData->m_shBORDER1.color = glGetUniformLocation(prog, "color"); + m_RenderData.pCurrentMonData->m_shBORDER1.gradient = glGetUniformLocation(prog, "gradient"); + m_RenderData.pCurrentMonData->m_shBORDER1.gradientLength = glGetUniformLocation(prog, "gradientLength"); + m_RenderData.pCurrentMonData->m_shBORDER1.angle = glGetUniformLocation(prog, "angle"); m_RenderData.pCurrentMonData->m_bShadersInitialized = true; @@ -804,7 +806,7 @@ void pushVert2D(float x, float y, float* arr, int& counter, wlr_box* box) { counter++; } -void CHyprOpenGLImpl::renderBorder(wlr_box* box, const CColor& col, int round) { +void CHyprOpenGLImpl::renderBorder(wlr_box* box, const CGradientValueData& grad, int round, float a) { RASSERT((box->width > 0 && box->height > 0), "Tried to render rect with width/height < 0!"); RASSERT(m_RenderData.pMonitor, "Tried to render rect without begin()!"); @@ -819,27 +821,13 @@ void CHyprOpenGLImpl::renderBorder(wlr_box* box, const CColor& col, int round) { int scaledBorderSize = *PBORDERSIZE * m_RenderData.pMonitor->scale; - if (round < 1) { - // zero rounding, just lines - wlr_box borderbox = {box->x - scaledBorderSize, box->y - scaledBorderSize, scaledBorderSize, box->height + 2 * scaledBorderSize}; - renderRect(&borderbox, col, 0); // left - borderbox = {box->x, box->y - (int)scaledBorderSize, box->width + (int)scaledBorderSize, (int)scaledBorderSize}; - renderRect(&borderbox, col, 0); // top - borderbox = {box->x + box->width, box->y, (int)scaledBorderSize, box->height + (int)scaledBorderSize}; - renderRect(&borderbox, col, 0); // right - borderbox = {box->x, box->y + box->height, box->width, (int)scaledBorderSize}; - renderRect(&borderbox, col, 0); // bottom - - return; - } - // adjust box box->x -= scaledBorderSize; box->y -= scaledBorderSize; box->width += 2 * scaledBorderSize; box->height += 2 * scaledBorderSize; - round += scaledBorderSize; + round += round == 0 ? 0 : scaledBorderSize; float matrix[9]; wlr_matrix_project_box(matrix, box, wlr_output_transform_invert(!m_bEndFrame ? WL_OUTPUT_TRANSFORM_NORMAL : m_RenderData.pMonitor->transform), 0, m_RenderData.pMonitor->output->transform_matrix); // TODO: write own, don't use WLR here @@ -858,7 +846,19 @@ void CHyprOpenGLImpl::renderBorder(wlr_box* box, const CColor& col, int round) { wlr_matrix_transpose(glMatrix, glMatrix); glUniformMatrix3fv(m_RenderData.pCurrentMonData->m_shBORDER1.proj, 1, GL_FALSE, glMatrix); #endif - glUniform4f(m_RenderData.pCurrentMonData->m_shBORDER1.color, col.r / 255.f, col.g / 255.f, col.b / 255.f, col.a / 255.f); + + // TODO: make gradients already in 0 ... 1 and just pass vec.data(), alpha in shader + float* gradientValues = (float*)malloc(sizeof(float) * grad.m_vColors.size() * 4); + for (size_t i = 0; i < grad.m_vColors.size(); ++i) { + gradientValues[i * 4] = grad.m_vColors[i].r / 255.f; + gradientValues[i * 4 + 1] = grad.m_vColors[i].g / 255.f; + gradientValues[i * 4 + 2] = grad.m_vColors[i].b / 255.f; + gradientValues[i * 4 + 3] = grad.m_vColors[i].a / 255.f * a; + } + + glUniform4fv(m_RenderData.pCurrentMonData->m_shBORDER1.gradient, grad.m_vColors.size(), gradientValues); + glUniform1i(m_RenderData.pCurrentMonData->m_shBORDER1.gradientLength, grad.m_vColors.size()); + glUniform1f(m_RenderData.pCurrentMonData->m_shBORDER1.angle, grad.m_fAngle); wlr_box transformedBox; wlr_box_transform(&transformedBox, box, wlr_output_transform_invert(m_RenderData.pMonitor->transform), @@ -906,6 +906,14 @@ void CHyprOpenGLImpl::renderBorder(wlr_box* box, const CColor& col, int round) { glDisableVertexAttribArray(m_RenderData.pCurrentMonData->m_shBORDER1.texAttrib); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + free(gradientValues); + + // fix back box + box->x += scaledBorderSize; + box->y += scaledBorderSize; + box->width -= 2 * scaledBorderSize; + box->height -= 2 * scaledBorderSize; } void CHyprOpenGLImpl::makeRawWindowSnapshot(CWindow* pWindow, CFramebuffer* pFramebuffer) { diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 70e6b258a..e54b76aa0 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -68,6 +68,8 @@ struct SCurrentRenderData { wlr_box clipBox = {}; }; +class CGradientValueData; + class CHyprOpenGLImpl { public: @@ -82,7 +84,7 @@ public: void renderTexture(const CTexture&, wlr_box*, float a, int round = 0, bool discardOpaque = false, bool allowCustomUV = false); void renderTextureWithBlur(const CTexture&, wlr_box*, float a, wlr_surface* pSurface, int round = 0); void renderRoundedShadow(wlr_box*, int round, int range, float a = 1.0); - void renderBorder(wlr_box*, const CColor&, int round); + void renderBorder(wlr_box*, const CGradientValueData&, int round, float a = 1.0); void makeWindowSnapshot(CWindow*); void makeRawWindowSnapshot(CWindow*, CFramebuffer*); diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index eabd03e06..a85ba1189 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -300,14 +300,20 @@ void CHyprRenderer::renderWindow(CWindow* pWindow, CMonitor* pMonitor, timespec* float rounding = renderdata.dontRound ? 0 : renderdata.rounding == -1 ? *PROUNDING : renderdata.rounding; rounding *= pMonitor->scale; - auto col = g_pHyprOpenGL->m_pCurrentWindow->m_cRealBorderColor.col(); - col.a *= renderdata.fadeAlpha * renderdata.alpha / 255.f; + auto grad = g_pHyprOpenGL->m_pCurrentWindow->m_cRealBorderColor; + const bool ANIMATED = g_pHyprOpenGL->m_pCurrentWindow->m_fBorderAnimationProgress.isBeingAnimated(); + float a1 = renderdata.fadeAlpha * renderdata.alpha / 255.f * (ANIMATED ? g_pHyprOpenGL->m_pCurrentWindow->m_fBorderAnimationProgress.fl() : 1.f); wlr_box windowBox = {renderdata.x - pMonitor->vecPosition.x, renderdata.y - pMonitor->vecPosition.y, renderdata.w, renderdata.h}; scaleBox(&windowBox, pMonitor->scale); - g_pHyprOpenGL->renderBorder(&windowBox, col, rounding); + g_pHyprOpenGL->renderBorder(&windowBox, grad, rounding, a1); + + if (ANIMATED) { + float a2 = 1.f - a1; + g_pHyprOpenGL->renderBorder(&windowBox, g_pHyprOpenGL->m_pCurrentWindow->m_cRealBorderColorPrevious, rounding, a2); + } } } diff --git a/src/render/Shader.hpp b/src/render/Shader.hpp index cf31ed9cd..db98b1f2c 100644 --- a/src/render/Shader.hpp +++ b/src/render/Shader.hpp @@ -32,6 +32,10 @@ public: GLint applyTint; GLint tint; + GLint gradient; + GLint gradientLength; + GLint angle; + GLint getUniformLocation(const std::string&); private: diff --git a/src/render/shaders/Border.hpp b/src/render/shaders/Border.hpp index 70ed8baa7..a2d610668 100644 --- a/src/render/shaders/Border.hpp +++ b/src/render/shaders/Border.hpp @@ -14,12 +14,28 @@ uniform float radius; uniform float thick; uniform int primitiveMultisample; +uniform vec4 gradient[10]; +uniform int gradientLength; +uniform float angle; + +vec4 getColorForCoord(vec2 normalizedCoord) { + if (gradientLength < 2) + return gradient[0]; + + float sine = sin(angle); + float progress = (normalizedCoord[1] * sine + normalizedCoord[0] * (1.0 - sine)) * float(gradientLength - 1); + int bottom = int(floor(progress)); + int top = bottom + 1; + + return gradient[top] * (progress - float(bottom)) + gradient[bottom] * (float(top) - progress); +} + void main() { highp vec2 pixCoord = vec2(gl_FragCoord); vec2 originalPixCoord = fullSize * v_texcoord; - vec4 pixColor = v_color; + vec4 pixColor = getColorForCoord(v_texcoord); bool done = false; @@ -27,7 +43,7 @@ void main() { pixCoord *= vec2(lessThan(pixCoord, vec2(0.0))) * -2.0 + 1.0; pixCoord -= fullSize * 0.5 - radius; - if (min(pixCoord.x, pixCoord.y) > 0.0) { + if (min(pixCoord.x, pixCoord.y) > 0.0 && radius > 0.0) { float dist = length(pixCoord);