From f3ebf9110527e8185eda547072fe00a1a2506810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BB=D0=B0=D0=B4=D0=B8=D1=81=D0=BB=D0=B0=D0=B2=20?= =?UTF-8?q?=D0=A1=D1=83=D1=85=D0=BE=D0=B2?= <22411953+Vladislav4KZ@users.noreply.github.com> Date: Mon, 18 May 2026 20:51:42 +0000 Subject: [PATCH 1/2] Add Color Picker functionality for custom logo (spraypaint) color selection * Introduced a new color picker widget (CMenuColorPicker) for selecting RGB and HSV colors. * Implemented color utility functions for converting between RGB and HSV formats in ColorUtils. * Created a color picker dialog (CMenuColorPickerDialog) to allow users to choose colors for logos. * Removed hardcoded color definitions and replaced them with a dynamic color selection system. --- ColorUtils.h | 101 +++++++++++ controls/ColorPicker.cpp | 309 +++++++++++++++++++++++++++++++++ controls/ColorPicker.h | 66 +++++++ menus/ColorPickerDialog.cpp | 275 +++++++++++++++++++++++++++++ menus/ColorPickerDialog.h | 72 ++++++++ menus/PlayerSetup.cpp | 302 ++++++++++++-------------------- sdk_includes/engine/menu_int.h | 2 + 7 files changed, 937 insertions(+), 190 deletions(-) create mode 100644 ColorUtils.h create mode 100644 controls/ColorPicker.cpp create mode 100644 controls/ColorPicker.h create mode 100644 menus/ColorPickerDialog.cpp create mode 100644 menus/ColorPickerDialog.h diff --git a/ColorUtils.h b/ColorUtils.h new file mode 100644 index 0000000..bd00084 --- /dev/null +++ b/ColorUtils.h @@ -0,0 +1,101 @@ +/* +ColorUtils.h - color utilities +Copyright (C) 2026 $_Vladislav + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#pragma once +#ifndef COLORUTILS_H +#define COLORUTILS_H + +#include +#include "port.h" +#include "utils.h" + +namespace ColorUtils +{ + +inline void HSVtoRGB( float h, float s, float v, byte &r, byte &g, byte &b ) +{ + float R, G, B; + + // Normalize hue to [0, 360) + h = fmodf( h, 360.f ); + if( h < 0.f ) + h += 360.f; + + if( s <= 0.f ) + { + R = G = B = v; + } + else + { + float hh = h / 60.f; + int i = (int)hh % 6; + float ff = hh - (int)hh; + float p = v * ( 1.f - s ); + float q = v * ( 1.f - s * ff ); + float t = v * ( 1.f - s * ( 1.f - ff ) ); + + switch( i ) + { + case 0: R = v; G = t; B = p; break; + case 1: R = q; G = v; B = p; break; + case 2: R = p; G = v; B = t; break; + case 3: R = p; G = q; B = v; break; + case 4: R = t; G = p; B = v; break; + default:R = v; G = p; B = q; break; + } + } + + r = (byte)( R * 255.f + 0.5f ); + g = (byte)( G * 255.f + 0.5f ); + b = (byte)( B * 255.f + 0.5f ); +} + +inline void RGBtoHSV( byte r, byte g, byte b, float &h, float &s, float &v ) +{ + float R = r / 255.f; + float G = g / 255.f; + float B = b / 255.f; + + float cmax = Q_max( R, Q_max( G, B ) ); + float cmin = Q_min( R, Q_min( G, B ) ); + float delta = cmax - cmin; + + v = cmax; + s = ( cmax > 0.f ) ? ( delta / cmax ) : 0.f; + + if( delta <= 0.f ) + { + h = 0.f; + } + else if( cmax == R ) + { + h = 60.f * fmodf( ( G - B ) / delta, 6.f ); + } + else if( cmax == G ) + { + h = 60.f * ( ( B - R ) / delta + 2.f ); + } + else + { + h = 60.f * ( ( R - G ) / delta + 4.f ); + } + + if( h < 0.f ) + h += 360.f; +} + +} // namespace ColorUtils + +#endif // COLORUTILS_H diff --git a/controls/ColorPicker.cpp b/controls/ColorPicker.cpp new file mode 100644 index 0000000..e1322c8 --- /dev/null +++ b/controls/ColorPicker.cpp @@ -0,0 +1,309 @@ +/* +ColorPicker.cpp - color picker widget +Copyright (C) 2026 $_Vladislav + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include + +#include "ColorPicker.h" +#include "BaseMenu.h" +#include "extdll_menu.h" +#include "utlvector.h" + +CMenuColorPicker::CMenuColorPicker() + : m_hue( 0.f ), m_sat( 1.f ), m_val( 1.f ), m_svTex( 0 ), m_hueTex( 0 ), + m_cursorTex( 0 ), m_draggingSV( false ), m_draggingHue( false ) +{ + size.w = 246; + size.h = 220; +} + +CMenuColorPicker::~CMenuColorPicker() +{ + EngFuncs::PIC_Free( "#cp_sv.tga" ); + EngFuncs::PIC_Free( "#cp_hue.tga" ); + EngFuncs::PIC_Free( "#cp_cursor.tga" ); +} + +void CMenuColorPicker::VidInit() +{ + CMenuBaseItem::VidInit(); + + const int hueW = 20; + const int gap = 6; + + int side = Q_min( m_scSize.w - hueW - gap, m_scSize.h ); + + m_svSize = { side, side }; + m_svPos = { m_scPos.x, m_scPos.y }; + + m_hueSize = { hueW, side }; + m_huePos = { m_scPos.x + side + gap, m_scPos.y }; + + BuildHueTexture(); + BuildCursorTexture(); + RebuildSVTexture(); +} + +void CMenuColorPicker::RebuildSVTexture() +{ + if( m_svSize.w <= 0 || m_svSize.h <= 0 ) + { + return; + } + + const int W = Q_max( 1, m_svSize.w ); + const int H = Q_max( 1, m_svSize.h ); + const int bufSize = sizeof( tga_t ) + W * H * 3; + CUtlVector buf; + buf.SetCount( bufSize ); + memset( buf.Base(), 0, bufSize ); + + tga_t *hdr = (tga_t *)buf.Base(); + hdr->image_type = 2; // uncompressed true-color + hdr->width = W; + hdr->height = H; + hdr->pixel_size = 24; + hdr->attributes = 0x20; // descriptor: origin upper left + + byte *pixels = buf.Base() + sizeof( tga_t ); + + for( int y = 0; y < H; y++ ) + { + float val = H > 1 ? 1.f - y / (float)( H - 1 ) : 1.f; + for( int x = 0; x < W; x++ ) + { + float sat = W > 1 ? x / (float)( W - 1 ) : 1.f; + byte r, g, b; + ColorUtils::HSVtoRGB( m_hue, sat, val, r, g, b ); + int i = ( y * W + x ) * 3; + pixels[i + 0] = b; + pixels[i + 1] = g; + pixels[i + 2] = r; + } + } + + EngFuncs::PIC_Free( "#cp_sv.tga" ); + m_svTex = EngFuncs::PIC_Load( "#cp_sv.tga", buf.Base(), bufSize, PIC_NOMIPMAP | PIC_NEAREST ); +} + +void CMenuColorPicker::BuildHueTexture() +{ + if( m_hueSize.h <= 0 ) + { + return; + } + + const int W = 4; + const int H = Q_max( 1, m_hueSize.h ); + const int bufSize = sizeof( tga_t ) + W * H * 3; + CUtlVector buf; + buf.SetCount( bufSize ); + memset( buf.Base(), 0, bufSize ); + + tga_t *hdr = (tga_t *)buf.Base(); + hdr->image_type = 2; // uncompressed true-color + hdr->width = W; + hdr->height = H; + hdr->pixel_size = 24; + hdr->attributes = 0x20; // descriptor: origin upper left + + byte *pixels = buf.Base() + sizeof( tga_t ); + + for( int y = 0; y < H; y++ ) + { + float hue = H > 1 ? ( 1.f - y / (float)( H - 1 ) ) * 360.f : 0.f; + byte r, g, b; + ColorUtils::HSVtoRGB( hue, 1.f, 1.f, r, g, b ); + + for( int x = 0; x < W; x++ ) + { + int i = ( y * W + x ) * 3; + pixels[i + 0] = b; + pixels[i + 1] = g; + pixels[i + 2] = r; + } + } + + EngFuncs::PIC_Free( "#cp_hue.tga" ); + m_hueTex = EngFuncs::PIC_Load( "#cp_hue.tga", buf.Base(), bufSize, PIC_NOMIPMAP | PIC_NEAREST ); +} + +void CMenuColorPicker::BuildCursorTexture() +{ + const int SIZE = 17; + const int bufSize = sizeof( tga_t ) + SIZE * SIZE * 4; + CUtlVector buf; + buf.SetCount( bufSize ); + memset( buf.Base(), 0, bufSize ); + + tga_t *hdr = (tga_t *)buf.Base(); + hdr->image_type = 2; // uncompressed true-color + hdr->width = SIZE; + hdr->height = SIZE; + hdr->pixel_size = 32; + hdr->attributes = 0x28; // 8-bit alpha, descriptor: origin upper left + + byte *pixels = buf.Base() + sizeof( tga_t ); + + const float cx = ( SIZE - 1 ) * 0.5f; + const float cy = ( SIZE - 1 ) * 0.5f; + const float r_mid = 6.0f; + const float w_white = 0.75f; + const float w_black = 0.9f; + const float aa = 0.5f; + + for( int y = 0; y < SIZE; y++ ) + { + for( int x = 0; x < SIZE; x++ ) + { + float dist = sqrtf( ( x - cx ) * ( x - cx ) + ( y - cy ) * ( y - cy ) ); + float d = fabsf( dist - r_mid ); + + float white_mask = Q_max( 0.f, Q_min( 1.f, ( w_white - d ) / aa + 0.5f ) ); + float cursor_mask = Q_max( 0.f, Q_min( 1.f, ( ( w_white + w_black ) - d ) / aa + 0.5f ) ); + + int i = ( y * SIZE + x ) * 4; + pixels[i + 0] = (byte)( white_mask * 255.f + 0.5f ); + pixels[i + 1] = (byte)( white_mask * 255.f + 0.5f ); + pixels[i + 2] = (byte)( white_mask * 255.f + 0.5f ); + pixels[i + 3] = (byte)( cursor_mask * 255.f + 0.5f ); + } + } + + EngFuncs::PIC_Free( "#cp_cursor.tga" ); + m_cursorTex = EngFuncs::PIC_Load( "#cp_cursor.tga", buf.Base(), bufSize, PIC_NOMIPMAP | PIC_HAS_ALPHA ); +} + +void CMenuColorPicker::Draw() +{ + if( m_svTex ) + { + EngFuncs::PIC_EnableScissor( m_svPos.x, m_svPos.y, m_svSize.w, m_svSize.h ); + + EngFuncs::PIC_Set( m_svTex, 255, 255, 255 ); + EngFuncs::PIC_DrawTrans( m_svPos, m_svSize ); + + if( m_cursorTex ) + { + const int CS = 17; + int cx = m_svPos.x + (int)( m_sat * Q_max( 0, m_svSize.w - 1 ) ); + int cy = m_svPos.y + (int)( ( 1.f - m_val ) * Q_max( 0, m_svSize.h - 1 ) ); + EngFuncs::PIC_Set( m_cursorTex, 255, 255, 255 ); + EngFuncs::PIC_DrawTrans( Point( cx - CS / 2, cy - CS / 2 ), Size( CS, CS ) ); + } + + UI_DrawRectangle( m_svPos, m_svSize, PackRGBA( 255, 255, 255, 64 ) ); + EngFuncs::PIC_DisableScissor(); + } + + if( m_hueTex ) + { + EngFuncs::PIC_EnableScissor( m_huePos.x, m_huePos.y, m_hueSize.w, m_hueSize.h ); + + EngFuncs::PIC_Set( m_hueTex, 255, 255, 255 ); + EngFuncs::PIC_DrawTrans( m_huePos, m_hueSize ); + + int hy = m_huePos.y + (int)( ( 1.f - m_hue / 360.f ) * Q_max( 0, m_hueSize.h - 1 ) ); + EngFuncs::FillRGBA( m_huePos.x, hy - 1, m_hueSize.w, 1, 0, 0, 0, 255 ); + EngFuncs::FillRGBA( m_huePos.x, hy, m_hueSize.w, 2, 255, 255, 255, 255 ); + EngFuncs::FillRGBA( m_huePos.x, hy + 2, m_hueSize.w, 1, 0, 0, 0, 255 ); + + UI_DrawRectangle( m_huePos, m_hueSize, PackRGBA( 255, 255, 255, 64 ) ); + EngFuncs::PIC_DisableScissor(); + } +} + +bool CMenuColorPicker::MouseMove( int x, int y ) +{ + if( m_draggingSV || m_draggingHue ) + { + if( !EngFuncs::KEY_IsDown( K_MOUSE1 ) ) + { + m_draggingSV = m_draggingHue = false; + return false; + } + } + + if( m_draggingSV ) + { + const float w = m_svSize.w > 1 ? (float)( m_svSize.w - 1 ) : 1.f; + const float h = m_svSize.h > 1 ? (float)( m_svSize.h - 1 ) : 1.f; + m_sat = Q_max( 0.f, Q_min( 1.f, ( x - m_svPos.x ) / w ) ); + m_val = Q_max( 0.f, Q_min( 1.f, 1.f - ( y - m_svPos.y ) / h ) ); + _Event( QM_CHANGED ); + return true; + } + if( m_draggingHue ) + { + const float h = m_hueSize.h > 1 ? (float)( m_hueSize.h - 1 ) : 1.f; + float t = ( y - m_huePos.y ) / h; + m_hue = ( 1.f - Q_max( 0.f, Q_min( 1.f, t ) ) ) * 360.f; + RebuildSVTexture(); + _Event( QM_CHANGED ); + return true; + } + return false; +} + +bool CMenuColorPicker::KeyDown( int key ) +{ + if( key == K_MOUSE1 ) + { + if( UI_CursorInRect( m_svPos, m_svSize ) ) + { + m_draggingSV = true; + MouseMove( uiStatic.cursorX, uiStatic.cursorY ); + return true; + } + if( UI_CursorInRect( m_huePos, m_hueSize ) ) + { + m_draggingHue = true; + MouseMove( uiStatic.cursorX, uiStatic.cursorY ); + return true; + } + } + return false; +} + +bool CMenuColorPicker::KeyUp( int key ) +{ + if( key == K_MOUSE1 && ( m_draggingSV || m_draggingHue ) ) + { + m_draggingSV = m_draggingHue = false; + return true; + } + return false; +} + +void CMenuColorPicker::GetRGB( byte &r, byte &g, byte &b ) const +{ + ColorUtils::HSVtoRGB( m_hue, m_sat, m_val, r, g, b ); +} + +void CMenuColorPicker::SetRGB( byte r, byte g, byte b, bool rebuild ) +{ + ColorUtils::RGBtoHSV( r, g, b, m_hue, m_sat, m_val ); + if( rebuild ) + RebuildSVTexture(); +} + +void CMenuColorPicker::SetHSV( float h, float s, float v ) +{ + bool hueChanged = ( fabsf( h - m_hue ) > 0.001f ); + m_hue = h; + m_sat = s; + m_val = v; + if( hueChanged ) + RebuildSVTexture(); +} diff --git a/controls/ColorPicker.h b/controls/ColorPicker.h new file mode 100644 index 0000000..dfe906b --- /dev/null +++ b/controls/ColorPicker.h @@ -0,0 +1,66 @@ +/* +ColorPicker.h - color picker widget +Copyright (C) 2026 $_Vladislav + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#pragma once +#ifndef COLORPICKER_H +#define COLORPICKER_H + +#include "BaseItem.h" +#include "ColorUtils.h" + +class CMenuColorPicker : public CMenuBaseItem +{ +public: + CMenuColorPicker(); + virtual ~CMenuColorPicker(); + + void VidInit() override; + void Draw() override; + bool KeyDown( int key ) override; + bool KeyUp( int key ) override; + bool MouseMove( int x, int y ) override; + + void GetRGB( byte &r, byte &g, byte &b ) const; + void SetRGB( byte r, byte g, byte b, bool rebuild = true ); + + float GetHue() const { return m_hue; } + float GetSat() const { return m_sat; } + float GetVal() const { return m_val; } + bool IsDragging() const { return m_draggingSV || m_draggingHue; } + void SetHSV( float h, float s, float v ); + +private: + float m_hue; + float m_sat; + float m_val; + + Point m_svPos; + Size m_svSize; + Point m_huePos; + Size m_hueSize; + + HIMAGE m_svTex; + HIMAGE m_hueTex; + HIMAGE m_cursorTex; + + bool m_draggingSV; + bool m_draggingHue; + + void RebuildSVTexture(); + void BuildHueTexture(); + void BuildCursorTexture(); +}; + +#endif // COLORPICKER_H diff --git a/menus/ColorPickerDialog.cpp b/menus/ColorPickerDialog.cpp new file mode 100644 index 0000000..03de808 --- /dev/null +++ b/menus/ColorPickerDialog.cpp @@ -0,0 +1,275 @@ +/* +ColorPickerDialog.cpp - color picker dialog window +Copyright (C) 2026 $_Vladislav + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "ColorPickerDialog.h" +#include "BaseMenu.h" +#include "extdll_menu.h" +#include "fmtstr.h" + +namespace Layout +{ + static const int DLG_W = 600; + static const int DLG_H = 430; + static const int PAD = 20; + static const int TITLE_H = 28; + static const int PICKER_W = 300; + static const int PICKER_H = 220; + static const int LOGO_W = 180; + static const int LOGO_H = 180; + static const int BTN_W = 120; + static const int BTN_H = 36; + static const int FIELD_W = 56; + static const int FIELD_H = 28; + static const int LABEL_W = 18; + static const int MODE_W = 140; + static const int INPUT_X = 102; + static const int UI_VIRTUAL_HEIGHT = 768; + + static const int CONTENT_GAP = 16; + static const int INPUT_GAP = 24; + static const int INPUT_GROUP_GAP = 14; + static const int FIELD_GAP = 8; + static const int LABEL_GAP = 4; + static const int LABEL_Y_OFFSET = 4; + static const int BTN_GAP = 16; +} + +CMenuColorPickerDialog::CMenuColorPickerDialog() + : CMenuBaseWindow( "CMenuColorPickerDialog" ), + m_modeRGB( true ), m_updatingFields( false ) +{ +} + +void CMenuColorPickerDialog::_Init() +{ + SetRect( ( uiStatic.width - Layout::DLG_W ) / 2, ( Layout::UI_VIRTUAL_HEIGHT - Layout::DLG_H ) / 2, Layout::DLG_W, Layout::DLG_H ); + + int currentY = Layout::PAD + Layout::TITLE_H + Layout::CONTENT_GAP; + + m_picker.SetRect( Layout::PAD, currentY, Layout::PICKER_W, Layout::PICKER_H ); + SET_EVENT_MULTI( m_picker.onChanged, + { + CMenuColorPickerDialog *dlg = (CMenuColorPickerDialog *)pSelf->Parent(); + dlg->UpdateLogoPreview(); + dlg->UpdateFieldsFromPicker(); + }); + + int logoX = Layout::DLG_W - Layout::PAD - Layout::LOGO_W; + int logoY = currentY + ( Layout::PICKER_H - Layout::LOGO_H ) / 2; + m_logoPreviewItem.SetRect( logoX, logoY, Layout::LOGO_W, Layout::LOGO_H ); + + currentY += Layout::PICKER_H + Layout::INPUT_GAP; + + static const char *modeItems[] = { "RGB", "HSV" }; + static CStringArrayModel modeModel( modeItems, 2 ); + + m_modeSwitch.Setup( &modeModel ); + m_modeSwitch.SetCurrentValue( 0.f ); + m_modeSwitch.SetRect( Layout::INPUT_X, currentY, Layout::MODE_W, Layout::FIELD_H ); + SET_EVENT_MULTI( m_modeSwitch.onChanged, + { + CMenuColorPickerDialog *dlg = (CMenuColorPickerDialog *)pSelf->Parent(); + bool toRGB = ( ( (CMenuSpinControl *)pSelf )->GetCurrentValue() < 0.5f ); + dlg->SwitchMode( toRGB ); + }); + + static const char *rgbLabels[3] = { "R", "G", "B" }; + int fieldX = Layout::INPUT_X + Layout::MODE_W + Layout::INPUT_GROUP_GAP; + + for( int i = 0; i < 3; i++ ) + { + int lx = fieldX + i * ( Layout::LABEL_W + Layout::FIELD_W + Layout::FIELD_GAP ); + int fx = lx + Layout::LABEL_W + Layout::LABEL_GAP; + + m_labels[i].pos = { lx, currentY + Layout::LABEL_Y_OFFSET }; + m_labels[i].size = { Layout::LABEL_W, Layout::FIELD_H }; + m_labels[i].text = rgbLabels[i]; + + m_fields[i].iMaxLength = 3; + m_fields[i].bNumbersOnly = true; + m_fields[i].SetRect( fx, currentY, Layout::FIELD_W, Layout::FIELD_H ); + + SET_EVENT_MULTI( m_fields[i].onChanged, + { + CMenuColorPickerDialog *dlg = (CMenuColorPickerDialog *)pSelf->Parent(); + dlg->UpdatePickerFromFields(); + }); + } + + const int buttonY = Layout::DLG_H - Layout::PAD - Layout::BTN_H; + // Center the gap between OK and Cancel buttons exactly on the right edge of the color picker column (PAD + PICKER_W) + const int buttonsX = ( Layout::PAD + Layout::PICKER_W ) - Layout::BTN_W - ( Layout::BTN_GAP / 2 ); + + m_btnOk.SetPicture( PC_OK ); + m_btnOk.szName = L( "OK" ); + m_btnOk.eTextAlignment = QM_CENTER; + m_btnOk.SetRect( buttonsX, buttonY, Layout::BTN_W, Layout::BTN_H ); + SET_EVENT_MULTI( m_btnOk.onReleased, + { + CMenuColorPickerDialog *dlg = (CMenuColorPickerDialog *)pSelf->Parent(); + if( dlg->onOk ) + dlg->onOk( dlg ); + dlg->Hide(); + }); + + m_btnCancel.SetPicture( PC_CANCEL ); + m_btnCancel.szName = L( "Cancel" ); + m_btnCancel.eTextAlignment = QM_CENTER; + m_btnCancel.SetRect( buttonsX + Layout::BTN_W + Layout::BTN_GAP, buttonY, Layout::BTN_W, Layout::BTN_H ); + SET_EVENT_MULTI( m_btnCancel.onReleased, + { + CMenuColorPickerDialog *dlg = (CMenuColorPickerDialog *)pSelf->Parent(); + dlg->Hide(); + }); + + AddItem( m_picker ); + AddItem( m_logoPreviewItem ); + AddItem( m_modeSwitch ); + for( int i = 0; i < 3; i++ ) + AddItem( m_fields[i] ); + AddItem( m_btnOk ); + AddItem( m_btnCancel ); +} + +void CMenuColorPickerDialog::_VidInit() +{ + SetRect( ( uiStatic.width - Layout::DLG_W ) / 2, ( Layout::UI_VIRTUAL_HEIGHT - Layout::DLG_H ) / 2, Layout::DLG_W, Layout::DLG_H ); + pos.x += uiStatic.xOffset; + pos.y += uiStatic.yOffset; +} + +void CMenuColorPickerDialog::Draw() +{ + UI_FillRect( 0, 0, gpGlobals->scrWidth, gpGlobals->scrHeight, 0x40000000 ); + + EngFuncs::FillRGBA( m_scPos.x, m_scPos.y, m_scSize.w, m_scSize.h, 20, 20, 20, 235 ); + + UI_DrawRectangle( m_scPos, m_scSize, uiInputFgColor ); + + UI_DrawString( font, m_scPos.x, m_scPos.y + Layout::PAD * uiStatic.scaleY, m_scSize.w, Layout::TITLE_H * uiStatic.scaleY, L( "Logo Color" ), uiColorHelp, m_scChSize, QM_CENTER, ETF_SHADOW ); + + EngFuncs::FillRGBA( m_scPos.x + Layout::PAD * uiStatic.scaleX, m_scPos.y + ( Layout::PAD + Layout::TITLE_H ) * uiStatic.scaleY, m_scSize.w - Layout::PAD * 2 * uiStatic.scaleX, 1, 255, 255, 255, 40 ); + + for( int i = 0; i < 3; i++ ) + { + int lx = m_scPos.x + m_labels[i].pos.x * uiStatic.scaleX; + int ly = m_scPos.y + m_labels[i].pos.y * uiStatic.scaleY; + int lw = m_labels[i].size.w * uiStatic.scaleX; + int lh = m_labels[i].size.h * uiStatic.scaleY; + + UI_DrawString( font, lx, ly, lw, lh, m_labels[i].text, uiColorHelp, m_scChSize, QM_LEFT, ETF_SHADOW ); + } + + BaseClass::Draw(); +} + +void CMenuColorPickerDialog::CLogoPreview::Draw() +{ + if( !hImage ) + { + UI_FillRect( m_scPos, m_scSize, uiPromptBgColor ); + UI_DrawString( font, m_scPos, m_scSize, L( "No logo" ), colorBase, m_scChSize, QM_CENTER, ETF_SHADOW ); + } + else + { + EngFuncs::PIC_Set( hImage, r, g, b ); + EngFuncs::PIC_DrawTrans( m_scPos, m_scSize ); + } + UI_DrawRectangle( m_scPos, m_scSize, uiInputFgColor ); +} + +void CMenuColorPickerDialog::UpdateLogoPreview() +{ + byte r, g, b; + m_picker.GetRGB( r, g, b ); + m_logoPreviewItem.r = r; + m_logoPreviewItem.g = g; + m_logoPreviewItem.b = b; +} + +void CMenuColorPickerDialog::UpdateFieldsFromPicker() +{ + if( m_updatingFields ) + return; + m_updatingFields = true; + + if( m_modeRGB ) + { + byte r, g, b; + m_picker.GetRGB( r, g, b ); + m_fields[0].SetBuffer( CNumStr( (int)r ) ); + m_fields[1].SetBuffer( CNumStr( (int)g ) ); + m_fields[2].SetBuffer( CNumStr( (int)b ) ); + } + else + { + m_fields[0].SetBuffer( CNumStr( (int)( m_picker.GetHue() + 0.5f ) ) ); + m_fields[1].SetBuffer( CNumStr( (int)( m_picker.GetSat() * 100.f + 0.5f ) ) ); + m_fields[2].SetBuffer( CNumStr( (int)( m_picker.GetVal() * 100.f + 0.5f ) ) ); + } + + m_updatingFields = false; +} + +void CMenuColorPickerDialog::UpdatePickerFromFields() +{ + if( m_updatingFields || m_picker.IsDragging() ) + return; + m_updatingFields = true; + + if( m_modeRGB ) + { + int r = Q_max( 0, Q_min( 255, atoi( m_fields[0].GetBuffer() ) ) ); + int g = Q_max( 0, Q_min( 255, atoi( m_fields[1].GetBuffer() ) ) ); + int b = Q_max( 0, Q_min( 255, atoi( m_fields[2].GetBuffer() ) ) ); + m_picker.SetRGB( (byte)r, (byte)g, (byte)b ); + } + else + { + float h = Q_max( 0.f, Q_min( 360.f, (float)atoi( m_fields[0].GetBuffer() ) ) ); + float s = Q_max( 0.f, Q_min( 1.f, atoi( m_fields[1].GetBuffer() ) / 100.f ) ); + float v = Q_max( 0.f, Q_min( 1.f, atoi( m_fields[2].GetBuffer() ) / 100.f ) ); + m_picker.SetHSV( h, s, v ); + } + + UpdateLogoPreview(); + m_updatingFields = false; +} + +void CMenuColorPickerDialog::SwitchMode( bool toRGB ) +{ + m_modeRGB = toRGB; + static const char *rgbLabels[3] = { "R", "G", "B" }; + static const char *hsvLabels[3] = { "H", "S", "V" }; + const char *const *labels = toRGB ? rgbLabels : hsvLabels; + for( int i = 0; i < 3; i++ ) + m_labels[i].text = labels[i]; + UpdateFieldsFromPicker(); +} + +void CMenuColorPickerDialog::Show( byte r, byte g, byte b, HIMAGE logoImage ) +{ + m_picker.SetRGB( r, g, b, false ); + m_logoPreviewItem.hImage = logoImage; + m_logoPreviewItem.r = r; + m_logoPreviewItem.g = g; + m_logoPreviewItem.b = b; + + m_modeRGB = true; + m_modeSwitch.SetCurrentValue( 0.f ); + SwitchMode( true ); + + BaseClass::Show(); +} diff --git a/menus/ColorPickerDialog.h b/menus/ColorPickerDialog.h new file mode 100644 index 0000000..e1793a6 --- /dev/null +++ b/menus/ColorPickerDialog.h @@ -0,0 +1,72 @@ +/* +ColorPickerDialog.h - color picker dialog window +Copyright (C) 2026 $_Vladislav + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#pragma once +#ifndef COLORPICKERDIALOG_H +#define COLORPICKERDIALOG_H + +#include "Framework.h" +#include "PicButton.h" +#include "Field.h" +#include "SpinControl.h" +#include "ColorPicker.h" +#include "StringArrayModel.h" + +class CMenuPlayerSetup; + +class CMenuColorPickerDialog : public CMenuBaseWindow +{ +public: + typedef CMenuBaseWindow BaseClass; + CMenuColorPickerDialog(); + + void Show( byte r, byte g, byte b, HIMAGE logoImage ); + CEventCallback onOk; + + void GetRGB( byte &r, byte &g, byte &b ) const + { + m_picker.GetRGB( r, g, b ); + } + +private: + void _Init() override; + void _VidInit() override; + void Draw() override; + + void UpdateLogoPreview(); + void UpdateFieldsFromPicker(); + void UpdatePickerFromFields(); + void SwitchMode( bool toRGB ); + + CMenuColorPicker m_picker; + CMenuPicButton m_btnOk; + CMenuPicButton m_btnCancel; + CMenuSpinControl m_modeSwitch; + CMenuField m_fields[3]; + bool m_modeRGB; + bool m_updatingFields; + + struct FieldLabel { Point pos; Size size; const char *text; } m_labels[3]; + + class CLogoPreview : public CMenuBaseItem + { + public: + void Draw() override; + HIMAGE hImage = 0; + byte r = 255, g = 255, b = 255; + } m_logoPreviewItem; +}; + +#endif // COLORPICKERDIALOG_H diff --git a/menus/PlayerSetup.cpp b/menus/PlayerSetup.cpp index 6c88e96..6735108 100644 --- a/menus/PlayerSetup.cpp +++ b/menus/PlayerSetup.cpp @@ -27,105 +27,11 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "SpinControl.h" #include "YesNoMessageBox.h" #include "PlayerModelView.h" -#include "StringArrayModel.h" #include "StringVectorModel.h" +#include "ColorPickerDialog.h" #define ART_BANNER "gfx/shell/head_customize" -static const byte Orange[] = { 255, 120, 24 }; -static const byte Yellow[] = { 225, 180, 24 }; -static const byte Blue[] = { 0, 60, 255 }; -static const byte Ltblue[] = { 0, 167, 255 }; -static const byte Green[] = { 0, 167, 0 }; -static const byte Red[] = { 255, 43, 0 }; -static const byte Brown[] = { 123, 73, 0 }; -static const byte Ltgray[] = { 100, 100, 100 }; -static const byte Dkgray[] = { 36, 36, 36 }; -static const byte Rainbow[] = { - 0xE4, 0x03, 0x03, - 0xFF, 0x8C, 0x00, - 0xFF, 0xED, 0x00, - 0x00, 0x80, 0x26, - 0x24, 0x40, 0x8E, - 0x73, 0x29, 0x82, -}; -static const byte Lesbian[] = { - 0xD5, 0x2D, 0x00, - 0xEF, 0x76, 0x27, - 0xFF, 0x9A, 0x56, - 0xFF, 0xFF, 0xFF, - 0xD1, 0x62, 0xA4, - 0xB5, 0x56, 0x90, - 0xA3, 0x02, 0x62, -}; -static const byte Gay[] = { - 0x07, 0x8D, 0x70, - 0x26, 0xCE, 0xAA, - 0x98, 0xE8, 0xC1, - 0xFF, 0xFF, 0xFF, - 0x7B, 0xAD, 0xE2, - 0x50, 0x49, 0xCC, - 0x3D, 0x1A, 0x78, -}; -static const byte Bi[] = { - 0xD6, 0x02, 0x70, - 0xD6, 0x02, 0x70, - 0x9B, 0x4F, 0x96, - 0x00, 0x38, 0xA8, - 0x00, 0x38, 0xA8, -}; -static const byte Trans[] = { - 0x5B, 0xCE, 0xFA, - 0xF5, 0xA9, 0xB8, - 0xFF, 0xFF, 0xFF, - 0xF5, 0xA9, 0xB8, - 0x5B, 0xCE, 0xFA, -}; -static const byte Pan[] = { - 0xFF, 0x21, 0x8C, - 0xFF, 0xD8, 0x00, - 0x21, 0xB1, 0xFF, -}; -static const byte Enby[] = { - 0xFC, 0xF4, 0x34, - 0xFF, 0xFF, 0xFF, - 0x9C, 0x59, 0xD1, - 0x2C, 0x2C, 0x2C, -}; - -#define FLAG_L( str, val, x ) str, val, x, sizeof( x ) / 3 -#define FLAG( x ) FLAG_L( #x, #x, x ) - -// TODO: Get rid of this hardcoded mess -// allow user to set whatever they want -// through UI or some config lst file -static const struct logo_color_t -{ - const char *name; - const char *value; - const byte *rgb; - int stripes; -} g_LogoColors[] = -{ -{ "FullColor", "FullColor", 0, 0 }, -{ FLAG_L( "#Valve_Orange", "Orange", Orange ) }, -{ FLAG_L( "#Valve_Yellow", "Yellow", Yellow ) }, -{ FLAG_L( "#Valve_Blue", "Blue", Blue ) }, -{ FLAG_L( "#Valve_Ltblue", "Ltblue", Ltblue ) }, -{ FLAG_L( "#Valve_Green", "Green", Green ) }, -{ FLAG_L( "#Valve_Red", "Red", Red ) }, -{ FLAG_L( "#Valve_Brown", "Brown", Brown ) }, -{ FLAG_L( "#Valve_Ltgray", "Ltgray", Ltgray ) }, -{ FLAG_L( "#Valve_Dkgray", "Dkgray", Dkgray ) }, -{ FLAG( Rainbow ) }, -{ FLAG( Lesbian ) }, -{ FLAG( Gay ) }, -{ FLAG( Bi ) }, -{ FLAG( Trans ) }, -{ FLAG( Pan ) }, -{ FLAG( Enby ) }, -}; - class CMenuPlayerSetup : public CMenuFramework { private: @@ -140,12 +46,14 @@ class CMenuPlayerSetup : public CMenuFramework void ApplyColorToImagePreview(); void ApplyColorToLogoPreview(); void WriteNewLogo(); + void ShowColorPicker(); + void OnColorPickerOk(); void SaveAndPopMenu() override; class CModelListModel : public CStringVectorModel { public: - void Update(); + void Update() override; } modelsModel; class CLogosListModel : public CStringVectorModel @@ -192,12 +100,22 @@ class CMenuPlayerSetup : public CMenuFramework { public: virtual void Draw(); - const logo_color_t *color; HIMAGE hImage; + byte previewR; + byte previewG; + byte previewB; } logoImage; CMenuSpinControl logo; - CMenuSpinControl logoColor; + CMenuPicButton btnChooseColor; + CMenuColorPickerDialog colorPickerDlg; + byte m_logoR; + byte m_logoG; + byte m_logoB; + byte m_lastCvarR; + byte m_lastCvarG; + byte m_lastCvarB; + bool m_logoColorable; CMenuYesNoMessageBox msgBox; @@ -213,39 +131,10 @@ void CMenuPlayerSetup::CMenuLogoPreview::Draw() UI_DrawString( font, m_scPos, m_scSize, L( "No logo" ), colorBase, m_scChSize, QM_CENTER, ETF_SHADOW ); } - else if( color->stripes == 0 ) - { - EngFuncs::PIC_Set( hImage, 255, 255, 255 ); - EngFuncs::PIC_DrawTrans( m_scPos, m_scSize ); - } else { - const Size img_sz = EngFuncs::PIC_Size( hImage ); - Size ui_sz = m_scSize; - wrect_t rc = { 0 }; - - rc.right = img_sz.w; - rc.bottom = img_sz.h; - - double texture_pixels_per_stripe = img_sz.h / (double)color->stripes; - double screen_pixels_per_stripe = ui_sz.h / (double)color->stripes; - - ui_sz.h = round( screen_pixels_per_stripe ); - - for( int i = 0; i < color->stripes; i++ ) - { - wrect_t rc2 = rc; - Point ui_pt; - - rc2.top = round( i * texture_pixels_per_stripe ); - rc2.bottom = round(( i + 1 ) * texture_pixels_per_stripe ); - - ui_pt.x = m_scPos.x; - ui_pt.y = m_scPos.y + round( i * screen_pixels_per_stripe ); - - EngFuncs::PIC_Set( hImage, color->rgb[i * 3 + 0], color->rgb[i * 3 + 1], color->rgb[i * 3 + 2] ); - EngFuncs::PIC_DrawTrans( ui_pt, ui_sz, &rc2 ); - } + EngFuncs::PIC_Set( hImage, previewR, previewG, previewB ); + EngFuncs::PIC_DrawTrans( m_scPos, m_scSize ); } int textHeight = m_scPos.y - (m_scChSize * 1.5f); @@ -414,20 +303,36 @@ void CMenuPlayerSetup::UpdateLogo() } } - if( colorable ) + m_logoColorable = colorable; + if( m_logoColorable ) { - ApplyColorToLogoPreview(); - logoColor.SetGrayed( false ); + logoImage.previewR = m_logoR; + logoImage.previewG = m_logoG; + logoImage.previewB = m_logoB; } else { - logoImage.color = &g_LogoColors[0]; - logoColor.SetCurrentValue( L( g_LogoColors[0].name )); - logoColor.SetGrayed( true ); + logoImage.previewR = 255; + logoImage.previewG = 255; + logoImage.previewB = 255; } - EngFuncs::CvarSetString( "cl_logofile", logo.GetCurrentString( )); - logoColor.WriteCvar(); + btnChooseColor.SetGrayed( !m_logoColorable ); + EngFuncs::CvarSetString( "cl_logofile", logo.GetCurrentString() ); +} + +void CMenuPlayerSetup::ShowColorPicker() +{ + if( !m_logoColorable ) + return; + + colorPickerDlg.Show( m_logoR, m_logoG, m_logoB, logoImage.hImage ); +} + +void CMenuPlayerSetup::OnColorPickerOk() +{ + colorPickerDlg.GetRGB( m_logoR, m_logoG, m_logoB ); + ApplyColorToLogoPreview(); } void CMenuPlayerSetup::ApplyColorToImagePreview() @@ -438,19 +343,9 @@ void CMenuPlayerSetup::ApplyColorToImagePreview() void CMenuPlayerSetup::ApplyColorToLogoPreview() { - const char *logoColorStr = logoColor.GetCurrentString(); - - for( size_t i = 0; i < V_ARRAYSIZE( g_LogoColors ) && logoColorStr; i++ ) - { - if( !stricmp( logoColorStr, L( g_LogoColors[i].name ))) - { - logoImage.color = &g_LogoColors[i]; - return; - } - } - - logoColor.SetCurrentValue( L( g_LogoColors[0].name ) ); - logoImage.color = &g_LogoColors[0]; + logoImage.previewR = m_logoColorable ? m_logoR : 255; + logoImage.previewG = m_logoColorable ? m_logoG : 255; + logoImage.previewB = m_logoColorable ? m_logoB : 255; } void CMenuPlayerSetup::WriteNewLogo( void ) @@ -488,8 +383,11 @@ void CMenuPlayerSetup::WriteNewLogo( void ) return; // remap logo if needed - if( logoImage.color->stripes >= 1 ) - bmpFile->RemapLogo( logoImage.color->stripes, logoImage.color->rgb ); + if( m_logoColorable ) + { + byte rgb[3] = { m_logoR, m_logoG, m_logoB }; + bmpFile->RemapLogo( 1, rgb ); + } bmpFile->Save( "logos/remapped.bmp" ); EngFuncs::CvarSetString( "cl_logoext", "bmp" ); @@ -497,8 +395,17 @@ void CMenuPlayerSetup::WriteNewLogo( void ) delete bmpFile; } + if( m_logoColorable ) + { + CUtlString rgbColor; + rgbColor.Format( "%d %d %d", m_logoR, m_logoG, m_logoB ); + EngFuncs::CvarSetString( "cl_logocolor", rgbColor.String() ); + m_lastCvarR = m_logoR; + m_lastCvarG = m_logoG; + m_lastCvarB = m_logoB; + } + logo.WriteCvar(); - logoColor.WriteCvar(); } /* @@ -509,8 +416,28 @@ UI_PlayerSetup_Init void CMenuPlayerSetup::_Init( void ) { int addFlags = 0; + const char *logoColor = EngFuncs::GetCvarString( "cl_logocolor" ); hideModels = hideLogos = false; + m_logoR = 255; + m_logoG = 255; + m_logoB = 255; + + if ( logoColor ) + { + int r, g, b; + if ( sscanf( logoColor, "%d %d %d", &r, &g, &b ) == 3 ) + { + m_logoR = r; + m_logoG = g; + m_logoB = b; + } + } + + m_logoColorable = false; + m_lastCvarR = m_logoR; + m_lastCvarG = m_logoG; + m_lastCvarB = m_logoB; // disable playermodel preview for HLRally to prevent crash if( !stricmp( gMenu.m_gameinfo.gamefolder, "hlrally" )) @@ -622,41 +549,21 @@ void CMenuPlayerSetup::_Init( void ) } else { - static const char *itemlist[V_ARRAYSIZE( g_LogoColors )]; - static CStringArrayModel colors( itemlist, V_ARRAYSIZE( g_LogoColors ) ); - for( size_t i = 0; i < V_ARRAYSIZE( g_LogoColors ); i++ ) - itemlist[i] = L( g_LogoColors[i].name ); - - logoImage.szName = L( "Spraypaint image" ); - logoImage.SetRect( 460, 370, 200, 200 ); - - logo.Setup( &logosModel ); - logo.LinkCvar( "cl_logofile", CMenuEditable::CVAR_STRING ); - logo.onChanged = VoidCb( &CMenuPlayerSetup::UpdateLogo ); - logo.SetRect( 460, logoImage.pos.y + logoImage.size.h + UI_OUTLINE_WIDTH, 200, 32 ); - - logoColor.Setup( &colors ); - logoColor.LinkCvar( "cl_logocolor", CMenuEditable::CVAR_STRING ); - SET_EVENT_MULTI( logoColor.onCvarGet, { - const char *val = EngFuncs::GetCvarString( "cl_logocolor" ); - for( size_t i = 0; i < V_ARRAYSIZE( g_LogoColors ); i++ ) - { - if( !stricmp( val, g_LogoColors[i].value )) - { - ((CMenuEditable *)pSelf)->SetOriginalString( L( g_LogoColors[i].name )); - return; - } - } - ((CMenuEditable *)pSelf)->SetOriginalString( L( g_LogoColors[0].name )); - }); - SET_EVENT_MULTI( logoColor.onCvarWrite, { - int idx = (int)((CMenuSpinControl *)pSelf)->GetCurrentValue(); - if( idx >= 0 && idx < (int)V_ARRAYSIZE( g_LogoColors )) - EngFuncs::CvarSetString( "cl_logocolor", g_LogoColors[idx].value ); - }); - logoColor.onChanged = VoidCb( &CMenuPlayerSetup::ApplyColorToLogoPreview ); - logoColor.SetRect( 460, logo.pos.y + logo.size.h + UI_OUTLINE_WIDTH, 200, 32 ); - } + logoImage.szName = L( "Spraypaint image" ); + logoImage.SetRect( 460, 370, 200, 200 ); + + logo.Setup( &logosModel ); + logo.LinkCvar( "cl_logofile", CMenuEditable::CVAR_STRING ); + logo.onChanged = VoidCb( &CMenuPlayerSetup::UpdateLogo ); + logo.SetRect( 460, logoImage.pos.y + logoImage.size.h + UI_OUTLINE_WIDTH, 200, 32 ); + + btnChooseColor.szName = L( "Choose logo color" ); + btnChooseColor.SetRect( 460, logo.pos.y + logo.size.h + UI_OUTLINE_WIDTH, 200, 32 ); + btnChooseColor.onReleased = VoidCb( &CMenuPlayerSetup::ShowColorPicker ); + + colorPickerDlg.Link( this ); + colorPickerDlg.onOk = VoidCb( &CMenuPlayerSetup::OnColorPickerOk ); + } } AddItem( name ); @@ -669,7 +576,7 @@ void CMenuPlayerSetup::_Init( void ) { UpdateLogo(); AddItem( logo ); - AddItem( logoColor ); + AddItem( btnChooseColor ); AddItem( logoImage ); } @@ -690,8 +597,23 @@ void CMenuPlayerSetup::_Init( void ) void CMenuPlayerSetup::Reload() { - if( !hideLogos ) UpdateLogo(); + if( !hideLogos ) + { + const char *logoColor = EngFuncs::GetCvarString( "cl_logocolor" ); + if( logoColor ) + { + int r, g, b; + if( sscanf( logoColor, "%d %d %d", &r, &g, &b ) == 3 ) + { + m_logoR = r; + m_logoG = g; + m_logoB = b; + } + } + UpdateLogo(); + } if( !hideModels ) UpdateModel(); } + ADD_MENU( menu_playersetup, CMenuPlayerSetup, UI_PlayerSetup_Menu ); diff --git a/sdk_includes/engine/menu_int.h b/sdk_includes/engine/menu_int.h index f86c8b1..c9cbc41 100644 --- a/sdk_includes/engine/menu_int.h +++ b/sdk_includes/engine/menu_int.h @@ -32,6 +32,8 @@ typedef int HIMAGE; // handle to a graphic #define PIC_KEEP_SOURCE (1<<1) // some images keep source #define PIC_NOFLIP_TGA (1<<2) // Steam background completely ignore tga attribute 0x20 #define PIC_EXPAND_SOURCE (1<<3) // don't keep as 8-bit source, expand to RGBA +#define PIC_NOMIPMAP (1<<12) // disable mipmaps +#define PIC_HAS_ALPHA (1<<16) // image has alpha channel // flags for COM_ParseFileSafe #define PFILE_IGNOREBRACKET (1<<0) From 29d37f988f29bdb7efebfe552f764acb3617ccc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=BB=D0=B0=D0=B4=D0=B8=D1=81=D0=BB=D0=B0=D0=B2=20?= =?UTF-8?q?=D0=A1=D1=83=D1=85=D0=BE=D0=B2?= <22411953+Vladislav4KZ@users.noreply.github.com> Date: Tue, 19 May 2026 12:10:12 +0000 Subject: [PATCH 2/2] ColorUtils: Fix Utils.h include case and remove unnecessary port.h include --- ColorUtils.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ColorUtils.h b/ColorUtils.h index bd00084..f2e5a0b 100644 --- a/ColorUtils.h +++ b/ColorUtils.h @@ -18,8 +18,7 @@ GNU General Public License for more details. #define COLORUTILS_H #include -#include "port.h" -#include "utils.h" +#include "Utils.h" namespace ColorUtils {