diff --git a/src/base/color.h b/src/base/color.h new file mode 100644 index 000000000..4b2d7b1f4 --- /dev/null +++ b/src/base/color.h @@ -0,0 +1,167 @@ + +#ifndef BASE_COLOR_H +#define BASE_COLOR_H + +#include "math.h" +#include "vmath.h" + +/* + Title: Color handling +*/ + +/* + Function: HueToRgb + Converts Hue to RGB +*/ +inline float HueToRgb(float v1, float v2, float h) +{ + if(h < 0.0f) h += 1; + if(h > 1.0f) h -= 1; + if((6.0f * h) < 1.0f) return v1 + (v2 - v1) * 6.0f * h; + if((2.0f * h) < 1.0f) return v2; + if((3.0f * h) < 2.0f) return v1 + (v2 - v1) * ((2.0f/3.0f) - h) * 6.0f; + return v1; +} + +inline float RgbToHue(vec3 rgb) +{ + float h_min = min(min(rgb.r, rgb.g), rgb.b); + float h_max = max(max(rgb.r, rgb.g), rgb.b); + + float hue = 0.0f; + + if(h_max == rgb.r) + hue = (rgb.g-rgb.b) / (h_max-h_min); + else if(h_max == rgb.g) + hue = 2.0f + (rgb.b-rgb.r) / (h_max-h_min); + else + hue = 4.0f + (rgb.r-rgb.g) / (h_max-h_min); + + hue /= 6.0f; + + if(hue < 0.0f) + hue += 1.0f; + + return hue; +} + +/* + Function: HslToRgb + Converts HSL to RGB +*/ +inline vec3 HslToRgb(vec3 HSL) +{ + if(HSL.s == 0.0f) + return vec3(HSL.l, HSL.l, HSL.l); + else + { + float v2 = HSL.l < 0.5f ? HSL.l * (1.0f + HSL.s) : (HSL.l+HSL.s) - (HSL.s*HSL.l); + float v1 = 2.0f * HSL.l - v2; + + return vec3(HueToRgb(v1, v2, HSL.h + (1.0f/3.0f)), HueToRgb(v1, v2, HSL.h), HueToRgb(v1, v2, HSL.h - (1.0f/3.0f))); + } +} + +inline vec3 HsvToRgb(vec3 hsv) +{ + int h = int(hsv.x * 6.0f); + float f = hsv.x * 6.0f - h; + float p = hsv.z * (1.0f - hsv.y); + float q = hsv.z * (1.0f - hsv.y * f); + float t = hsv.z * (1.0f - hsv.y * (1.0f - f)); + + vec3 rgb = vec3(0.0f, 0.0f, 0.0f); + + switch(h % 6) + { + case 0: + rgb.r = hsv.z; + rgb.g = t; + rgb.b = p; + break; + + case 1: + rgb.r = q; + rgb.g = hsv.z; + rgb.b = p; + break; + + case 2: + rgb.r = p; + rgb.g = hsv.z; + rgb.b = t; + break; + + case 3: + rgb.r = p; + rgb.g = q; + rgb.b = hsv.z; + break; + + case 4: + rgb.r = t; + rgb.g = p; + rgb.b = hsv.z; + break; + + case 5: + rgb.r = hsv.z; + rgb.g = p; + rgb.b = q; + break; + } + + return rgb; +} + +inline vec3 RgbToHsv(vec3 rgb) +{ + float h_min = min(min(rgb.r, rgb.g), rgb.b); + float h_max = max(max(rgb.r, rgb.g), rgb.b); + + // hue + float hue = 0.0f; + + if(h_max == h_min) + hue = 0.0f; + else if(h_max == rgb.r) + hue = (rgb.g-rgb.b) / (h_max-h_min); + else if(h_max == rgb.g) + hue = 2.0f + (rgb.b-rgb.r) / (h_max-h_min); + else + hue = 4.0f + (rgb.r-rgb.g) / (h_max-h_min); + + hue /= 6.0f; + + if(hue < 0.0f) + hue += 1.0f; + + // saturation + float s = 0.0f; + if(h_max != 0.0f) + s = (h_max - h_min)/h_max; + + // lightness + float l = h_max; + + return vec3(hue, s, l); +} + +/* + Function: HexToRgba + Converts Hex to Rgba + + Remarks: Hex should be RGBA8 +*/ +inline vec4 HexToRgba(int hex) +{ + vec4 c; + c.r = ((hex >> 24) & 0xFF) / 255.0f; + c.g = ((hex >> 16) & 0xFF) / 255.0f; + c.b = ((hex >> 8) & 0xFF) / 255.0f; + c.a = (hex & 0xFF) / 255.0f; + + return c; +} + +#endif diff --git a/src/game/client/ui.cpp b/src/game/client/ui.cpp index 700b3ac1e..db9959f83 100644 --- a/src/game/client/ui.cpp +++ b/src/game/client/ui.cpp @@ -1,6 +1,7 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include +#include #include #include @@ -437,6 +438,35 @@ int CUI::DoButtonLogic(const void *pID, const char *pText, int Checked, const CU return ReturnValue; } + +int CUI::DoPickerLogic(const void *pID, const CUIRect *pRect, float *pX, float *pY) +{ + int Inside = MouseInside(pRect); + + if(ActiveItem() == pID) + { + if(!MouseButton(0)) + SetActiveItem(0); + } + else if(HotItem() == pID) + { + if(MouseButton(0)) + SetActiveItem(pID); + } + else if(Inside) + SetHotItem(pID); + + if(!Inside || !MouseButton(0)) + return 0; + + if(pX) + *pX = clamp(m_MouseX - pRect->x, 0.0f, pRect->w) / Scale(); + if(pY) + *pY = clamp(m_MouseY - pRect->y, 0.0f, pRect->h) / Scale(); + + return 1; +} + /* int CUI::DoButton(const void *id, const char *text, int checked, const CUIRect *r, ui_draw_button_func draw_func, const void *extra) { diff --git a/src/game/client/ui.h b/src/game/client/ui.h index a063a8f85..8720a3519 100644 --- a/src/game/client/ui.h +++ b/src/game/client/ui.h @@ -91,6 +91,7 @@ public: float Scale(); int DoButtonLogic(const void *pID, const char *pText /* TODO: Refactor: Remove */, int Checked, const CUIRect *pRect); + int DoPickerLogic(const void *pID, const CUIRect *pRect, float *pX, float *pY); // TODO: Refactor: Remove this? void DoLabel(const CUIRect *pRect, const char *pText, float Size, int Align, int MaxWidth = -1); diff --git a/src/game/editor/editor.cpp b/src/game/editor/editor.cpp index 4649fa0e3..1ed8dba4c 100644 --- a/src/game/editor/editor.cpp +++ b/src/game/editor/editor.cpp @@ -2,6 +2,7 @@ /* If you are missing that file, acquire a complete release at teeworlds.com. */ #include #include +#include #include #if defined(CONF_FAMILY_UNIX) @@ -48,6 +49,11 @@ int CEditor::ms_SpeedupTexture; int CEditor::ms_SwitchTexture; int CEditor::ms_TuneTexture; +vec3 CEditor::ms_PickerColor; +int CEditor::ms_SVPicker; +int CEditor::ms_HuePicker; + + enum { BUTTON_CONTEXT=1, @@ -633,6 +639,12 @@ int CEditor::DoButton_ButtonDec(const void *pID, const char *pText, int Checked, return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip); } +int CEditor::DoButton_ColorPicker(const void *pID, const CUIRect *pRect, vec4 *pColor, const char *pToolTip) +{ + RenderTools()->DrawUIRect(pRect, *pColor, 0, 0.0f); + return DoButton_Editor_Common(pID, 0x0, 0, pRect, 0, pToolTip); +} + void CEditor::RenderGrid(CLayerGroup *pGroup) { if(!m_GridActive) @@ -2688,7 +2700,15 @@ int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int * { static const char *s_paTexts[4] = {"R", "G", "B", "A"}; static int s_aShift[] = {24, 16, 8, 0}; - int NewColor = 0; + int NewColor = 0, NewPickerColor = 0; + + // extra space + CUIRect ColorBox, ColorSlots; + + pToolBox->HSplitTop(3.0f*13.0f, &Slot, pToolBox); + Slot.VSplitMid(&ColorBox, &ColorSlots); + ColorBox.HMargin(1.0f, &ColorBox); + ColorBox.VMargin(6.0f, &ColorBox); for(int c = 0; c < 4; c++) { @@ -2697,12 +2717,30 @@ int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int * if(c != 3) { - pToolBox->HSplitTop(13.0f, &Slot, pToolBox); - Slot.VSplitMid(0, &Shifter); + ColorSlots.HSplitTop(13.0f, &Shifter, &ColorSlots); Shifter.HMargin(1.0f, &Shifter); } } + vec4 Color = vec4( + ((pProps[i].m_Value >> s_aShift[0])&0xff)/255.0f, + ((pProps[i].m_Value >> s_aShift[1])&0xff)/255.0f, + ((pProps[i].m_Value >> s_aShift[2])&0xff)/255.0f, + 1.0f); + + static int s_ColorPicker, s_ColorPickerID; + if(DoButton_ColorPicker(&s_ColorPicker, &ColorBox, &Color)) + { + ms_PickerColor = RgbToHsv(vec3(Color.r, Color.g, Color.b)); + UiInvokePopupMenu(&s_ColorPickerID, 0, UI()->MouseX(), UI()->MouseY(), 180, 150, PopupColorPicker); + } + + if(UI()->HotItem() == &ms_SVPicker || UI()->HotItem() == &ms_HuePicker) + { + vec3 c = HsvToRgb(ms_PickerColor); + NewColor = ((int)(c.r * 255.0f)&0xff) << 24 | ((int)(c.g * 255.0f)&0xff) << 16 | ((int)(c.b * 255.0f)&0xff) << 8 | (pProps[i].m_Value&0xff); + } + if(NewColor != pProps[i].m_Value) { *pNewVal = NewColor; @@ -5240,6 +5278,8 @@ void CEditor::Init() m_Map.m_Modified = false; m_Map.m_UndoModified = 0; m_LastUndoUpdateTime = time_get(); + + ms_PickerColor = vec3(1.0f, 0.0f, 0.0f); } void CEditor::DoMapBorder() diff --git a/src/game/editor/editor.h b/src/game/editor/editor.h index 827d857d9..1bdab7933 100644 --- a/src/game/editor/editor.h +++ b/src/game/editor/editor.h @@ -886,6 +886,8 @@ public: int DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip); int DoButton_MenuItem(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags=0, const char *pToolTip=0); + int DoButton_ColorPicker(const void *pID, const CUIRect *pRect, vec4 *pColor, const char *pToolTip=0); + int DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *Offset, bool Hidden=false, int Corners=CUI::CORNER_ALL); void RenderBackground(CUIRect View, int Texture, float Size, float Brightness); @@ -912,6 +914,7 @@ public: static int PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View); static int PopupSound(CEditor *pEditor, CUIRect View); static int PopupSource(CEditor *pEditor, CUIRect View); + static int PopupColorPicker(CEditor *pEditor, CUIRect View); static void CallbackOpenMap(const char *pFileName, int StorageType, void *pUser); static void CallbackAppendMap(const char *pFileName, int StorageType, void *pUser); @@ -981,6 +984,10 @@ public: int GetLineDistance(); void ZoomMouseTarget(float ZoomFactor); + static vec3 ms_PickerColor; + static int ms_SVPicker; + static int ms_HuePicker; + // DDRace static int ms_FrontTexture; diff --git a/src/game/editor/popups.cpp b/src/game/editor/popups.cpp index b612840d7..440cefe78 100644 --- a/src/game/editor/popups.cpp +++ b/src/game/editor/popups.cpp @@ -1,6 +1,7 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ +#include #include #include @@ -1514,3 +1515,105 @@ int CEditor::PopupTune(CEditor *pEditor, CUIRect View) return 0; } + +int CEditor::PopupColorPicker(CEditor *pEditor, CUIRect View) +{ + CUIRect SVPicker, HuePicker; + View.VSplitRight(20.0f, &SVPicker, &HuePicker); + HuePicker.VSplitLeft(4.0f, 0x0, &HuePicker); + + pEditor->Graphics()->TextureSet(-1); + pEditor->Graphics()->QuadsBegin(); + + // base: white - hue + vec3 hsv = pEditor->ms_PickerColor; + IGraphics::CColorVertex ColorArray[4]; + + vec3 c = HsvToRgb(vec3(hsv.x, 0.0f, 1.0f)); + ColorArray[0] = IGraphics::CColorVertex(0, c.r, c.g, c.b, 1.0f); + c = HsvToRgb(vec3(hsv.x, 1.0f, 1.0f)); + ColorArray[1] = IGraphics::CColorVertex(1, c.r, c.g, c.b, 1.0f); + c = HsvToRgb(vec3(hsv.x, 1.0f, 1.0f)); + ColorArray[2] = IGraphics::CColorVertex(2, c.r, c.g, c.b, 1.0f); + c = HsvToRgb(vec3(hsv.x, 0.0f, 1.0f)); + ColorArray[3] = IGraphics::CColorVertex(3, c.r, c.g, c.b, 1.0f); + + pEditor->Graphics()->SetColorVertex(ColorArray, 4); + + IGraphics::CQuadItem QuadItem(SVPicker.x, SVPicker.y, SVPicker.w, SVPicker.h); + pEditor->Graphics()->QuadsDrawTL(&QuadItem, 1); + + // base: transparent - black + ColorArray[0] = IGraphics::CColorVertex(0, 0.0f, 0.0f, 0.0f, 0.0f); + ColorArray[1] = IGraphics::CColorVertex(1, 0.0f, 0.0f, 0.0f, 0.0f); + ColorArray[2] = IGraphics::CColorVertex(2, 0.0f, 0.0f, 0.0f, 1.0f); + ColorArray[3] = IGraphics::CColorVertex(3, 0.0f, 0.0f, 0.0f, 1.0f); + + pEditor->Graphics()->SetColorVertex(ColorArray, 4); + + pEditor->Graphics()->QuadsDrawTL(&QuadItem, 1); + + pEditor->Graphics()->QuadsEnd(); + + // marker + vec2 Marker = vec2(hsv.y*pEditor->UI()->Scale(), (1.0f - hsv.z)*pEditor->UI()->Scale()) * vec2(SVPicker.w, SVPicker.h); + pEditor->Graphics()->QuadsBegin(); + pEditor->Graphics()->SetColor(0.5f, 0.5f, 0.5f, 1.0f); + IGraphics::CQuadItem aMarker[2]; + aMarker[0] = IGraphics::CQuadItem(SVPicker.x+Marker.x, SVPicker.y+Marker.y - 5.0f*pEditor->UI()->PixelSize(), pEditor->UI()->PixelSize(), 11.0f*pEditor->UI()->PixelSize()); + aMarker[1] = IGraphics::CQuadItem(SVPicker.x+Marker.x - 5.0f*pEditor->UI()->PixelSize(), SVPicker.y+Marker.y, 11.0f*pEditor->UI()->PixelSize(), pEditor->UI()->PixelSize()); + pEditor->Graphics()->QuadsDrawTL(aMarker, 2); + pEditor->Graphics()->QuadsEnd(); + + // logic + float X, Y; + if(pEditor->UI()->DoPickerLogic(&pEditor->ms_SVPicker, &SVPicker, &X, &Y)) + { + hsv.y = X/SVPicker.w; + hsv.z = 1.0f - Y/SVPicker.h; + } + + // hue slider + static const float s_aColorIndices[7][3] = { + {1.0f, 0.0f, 0.0f}, // red + {1.0f, 0.0f, 1.0f}, // magenta + {0.0f, 0.0f, 1.0f}, // blue + {0.0f, 1.0f, 1.0f}, // cyan + {0.0f, 1.0f, 0.0f}, // green + {1.0f, 1.0f, 0.0f}, // yellow + {1.0f, 0.0f, 0.0f} // red + }; + + pEditor->Graphics()->QuadsBegin(); + vec4 ColorTop, ColorBottom; + float Offset = HuePicker.h/7.0f; + for(int j = 0; j < 7; j++) + { + ColorTop = vec4(s_aColorIndices[j][0], s_aColorIndices[j][1], s_aColorIndices[j][2], 1.0f); + ColorBottom = vec4(s_aColorIndices[j+1][0], s_aColorIndices[j+1][1], s_aColorIndices[j+1][2], 1.0f); + + ColorArray[0] = IGraphics::CColorVertex(0, ColorTop.r, ColorTop.g, ColorTop.b, ColorTop.a); + ColorArray[1] = IGraphics::CColorVertex(1, ColorTop.r, ColorTop.g, ColorTop.b, ColorTop.a); + ColorArray[2] = IGraphics::CColorVertex(2, ColorBottom.r, ColorBottom.g, ColorBottom.b, ColorBottom.a); + ColorArray[3] = IGraphics::CColorVertex(3, ColorBottom.r, ColorBottom.g, ColorBottom.b, ColorBottom.a); + pEditor->Graphics()->SetColorVertex(ColorArray, 4); + IGraphics::CQuadItem QuadItem(HuePicker.x, HuePicker.y+Offset*j, HuePicker.w, Offset); + pEditor->Graphics()->QuadsDrawTL(&QuadItem, 1); + } + + // marker + pEditor->Graphics()->SetColor(0.5f, 0.5f, 0.5f, 1.0f); + IGraphics::CQuadItem QuadItemMarker(HuePicker.x, HuePicker.y + (1.0f - hsv.x) * HuePicker.h * pEditor->UI()->Scale(), HuePicker.w, pEditor->UI()->PixelSize()); + pEditor->Graphics()->QuadsDrawTL(&QuadItemMarker, 1); + + pEditor->Graphics()->QuadsEnd(); + + if(pEditor->UI()->DoPickerLogic(&pEditor->ms_HuePicker, &HuePicker, &X, &Y)) + { + hsv.x = 1.0f - Y/HuePicker.h; + } + + pEditor->ms_PickerColor = hsv; + + return 0; +} \ No newline at end of file