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