hl2_src-leak-2017/src/engine/colorcorrectionpanel.cpp

5413 lines
148 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "client_pch.h"
#include <vgui_controls/Frame.h>
#include <vgui/ISurface.h>
#include <vgui/IVGui.h>
#include <vgui_controls/BuildGroup.h>
#include "KeyValues.h"
#include <vgui_controls/Label.h>
#include <vgui_controls/Slider.h>
#include <vgui_controls/ComboBox.h>
#include <vgui_controls/Controls.h>
#include <vgui_controls/Button.h>
#include <vgui_controls/FileOpenDialog.h>
#include <vgui_controls/RadioButton.h>
#include <vgui_controls/CheckButton.h>
#include <vgui_controls/PanelListPanel.h>
#include <vgui_controls/ImageList.h>
#include <vgui/IInput.h>
#include "icolorcorrectiontools.h"
#include "vgui_baseui_interface.h"
#include "ivideomode.h"
#include "materialsystem/MaterialSystemUtil.h"
#include "matsys_controls/curveeditorpanel.h"
#include "matsys_controls/proceduraltexturepanel.h"
#include "VGuiMatSurface/IMatSystemSurface.h"
#include "materialsystem/itexture.h"
#include "vtf/vtf.h"
#include "pixelwriter.h"
#include "UtlSortVector.h"
#include "filesystem_engine.h"
#include "gl_matsysiface.h"
#include "materialsystem/IColorCorrection.h"
#include "tier2/tier2.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
using namespace vgui;
const int g_nPreviewImageWidth = 128;
const int g_nPreviewImageHeight = 96;
ConVar mat_colorcorrection( "mat_colorcorrection", "0", FCVAR_ARCHIVE );
ConVar mat_colcorrection_disableentities( "mat_colcorrection_disableentities", "0" );
//-----------------------------------------------------------------------------
// CPrecisionSlider
// A drop-in replacement for the slider class that contains a text entry that
// can be used to read and set the current value.
// Also provides mousewheel support.
//-----------------------------------------------------------------------------
class CPrecisionSlider : public vgui::Slider
{
DECLARE_CLASS_SIMPLE( CPrecisionSlider, vgui::Slider );
public:
CPrecisionSlider( Panel *parent, const char *panelName );
~CPrecisionSlider( );
virtual void SetValue( int value, bool bTriggerChangeMessage = true );
virtual void OnSizeChanged( int wide, int tall );
virtual void GetTrackRect( int &x, int &y, int &w, int &h );
virtual void SetEnabled( bool state );
protected:
MESSAGE_FUNC_PARAMS( OnTextNewLine, "TextNewLine", data );
virtual void OnMouseWheeled( int delta );
private:
vgui::TextEntry *m_pTextEntry;
int m_nTextEntryWidth;
int m_nSpacing;
};
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CPrecisionSlider::CPrecisionSlider( Panel *parent, const char *panelName ) : BaseClass( parent, panelName )
{
m_pTextEntry = new vgui::TextEntry( this, "PrecisionEditPanel" );
m_pTextEntry->SendNewLine( true );
m_pTextEntry->SetCatchEnterKey( true );
m_pTextEntry->AddActionSignalTarget( this );
m_nTextEntryWidth = 32;
m_nSpacing = 8;
}
//-----------------------------------------------------------------------------
// Destructor
//-----------------------------------------------------------------------------
CPrecisionSlider::~CPrecisionSlider( )
{
delete m_pTextEntry;
}
//-----------------------------------------------------------------------------
// Override OnSizeChanged to update text entry size as well
//-----------------------------------------------------------------------------
void CPrecisionSlider::OnSizeChanged( int wide, int tall )
{
int nSliderWidth, nSliderHeight;
int nEditWidth, nEditHeight;
nEditWidth = m_nTextEntryWidth;
nSliderHeight = tall;
nEditHeight = tall - 12;
nSliderWidth = wide - (m_nSpacing + nEditWidth);
m_pTextEntry->SetBounds( nSliderWidth + m_nSpacing, 0, nEditWidth, nEditHeight );
BaseClass::OnSizeChanged( wide, tall );
}
//-----------------------------------------------------------------------------
// Override GetTrackRect in order to adjust for the text entry
//-----------------------------------------------------------------------------
void CPrecisionSlider::GetTrackRect( int &x, int &y, int &w, int &h )
{
int wide, tall;
GetPaintSize( wide, tall );
x = 0;
y = 8;
w = wide - ( _nobSize + m_nTextEntryWidth + m_nSpacing );
h = 4;
}
//-----------------------------------------------------------------------------
// Override SetValue to update the text entry data
//-----------------------------------------------------------------------------
void CPrecisionSlider::SetValue( int value, bool bTriggerChangeMessage )
{
BaseClass::SetValue( value, bTriggerChangeMessage );
char szValueString[256];
sprintf( szValueString, "%d", _value );
m_pTextEntry->SetText( szValueString );
}
//-----------------------------------------------------------------------------
// Override SetEnabled to also effect the text entry field
//-----------------------------------------------------------------------------
void CPrecisionSlider::SetEnabled( bool state )
{
BaseClass::SetEnabled( state );
m_pTextEntry->SetEnabled( state );
}
//-----------------------------------------------------------------------------
// Handle updates from the text entry field
//-----------------------------------------------------------------------------
void CPrecisionSlider::OnTextNewLine( KeyValues *data )
{
char buf[256];
m_pTextEntry->GetText( buf, 256 );
int value;
sscanf( buf, "%d", &value );
SetValue( value );
}
//-----------------------------------------------------------------------------
// Handle mousewheel updates
//-----------------------------------------------------------------------------
void CPrecisionSlider::OnMouseWheeled( int delta )
{
BaseClass::OnMouseWheeled( delta );
if( IsEnabled() )
{
int value = GetValue();
if( input()->IsKeyDown( KEY_LCONTROL ) || input()->IsKeyDown( KEY_RCONTROL ) )
SetValue( value + delta*4 );
else
SetValue( value + delta );
}
}
enum
{
IMAGE_BUFFER_MAX_DIM = 128
};
class CColorCorrectionUIPanel;
// If you add a tool, add it to the string list and instance the panel in the constructor below
enum ColorCorrectionTool_t
{
CC_TOOL_NONE = 0,
CC_TOOL_CURVES,
CC_TOOL_LEVELS,
CC_TOOL_SELECTED_HSV,
CC_TOOL_LOOKUP,
CC_TOOL_BALANCE,
CC_TOOL_COUNT,
DEFAULT_CC_TOOL = CC_TOOL_NONE,
};
static const char *s_pColorCorrectionToolNames[CC_TOOL_COUNT] =
{
"No Tool Active",
"Curves Tool",
"Levels Tool",
"Selected HSV Tool",
"Lookup Tool",
"Color Balance Tool",
};
//-----------------------------------------------------------------------------
// Converts RGB to normalized
//-----------------------------------------------------------------------------
static void Color24ToVector( color24 inColor, Vector *pOutVector )
{
pOutVector->Init( inColor.r / 255.0f, inColor.g / 255.0f, inColor.b / 255.0f );
}
static void VectorToColor24( const Vector &inVector, color24 &outColor )
{
int r = (int)((inVector.x * 255.0f) + 0.5f);
int g = (int)((inVector.y * 255.0f) + 0.5f);
int b = (int)((inVector.z * 255.0f) + 0.5f);
outColor.r = clamp( r, 0, 255 );
outColor.g = clamp( g, 0, 255 );
outColor.b = clamp( b, 0, 255 );
}
//-----------------------------------------------------------------------------
// Convert HSV to RGB
//-----------------------------------------------------------------------------
float HueToRGB( float v1, float v2, float vH )
{
float fResult = v1;
vH = vH / 360.0f;
vH = fmod (vH + 1.0f, 1.0f);
if ( ( vH * 6.0f ) < 1.0f )
{
fResult = ( v1 + ( v2 - v1 ) * 6.0f * vH );
}
else if ( ( vH * 2.0f ) < 1.0f )
{
fResult = ( v2 );
}
else if ( ( vH * 3.0f ) < 2.0f )
{
fResult = ( v1 + ( v2 - v1 ) * ( ( 2.0f / 3.0f ) - vH ) * 6.0f );
}
return fResult;
}
//-----------------------------------------------------------------------------
// Computes the point on the spline whose x value == flInColor
//-----------------------------------------------------------------------------
static void ComputeSplinePoint( float flInColor, Vector *pControlPoints[4], Vector &vecOut )
{
if ( pControlPoints[2]->x == pControlPoints[1]->x )
{
VectorAdd( *pControlPoints[1], *pControlPoints[2], vecOut );
vecOut *= 0.5f;
return;
}
int nIterCount = 0;
float flStart = 0.0f;
float flEnd = 1.0f;
float flMid = ( flInColor - pControlPoints[1]->x ) / ( pControlPoints[2]->x - pControlPoints[1]->x );
while( true )
{
Catmull_Rom_Spline( *pControlPoints[0], *pControlPoints[1], *pControlPoints[2], *pControlPoints[3], flMid, vecOut );
if ( fabs( vecOut.x - flInColor ) < 1e-5 )
return;
if ( flInColor < vecOut.x )
{
flEnd = flMid;
}
else
{
flStart = flMid;
}
flMid = (flStart + flEnd) * 0.5f;
++nIterCount;
}
}
//-----------------------------------------------------------------------------
// A color operation
//-----------------------------------------------------------------------------
abstract_class IColorOperation
{
public:
// RGB are in 0-1 space here
virtual void Apply( const Vector &inRGB, Vector &outRGB ) = 0;
// Causes the operation to be deleted
virtual void Release() = 0;
virtual const char *GetName() = 0;
virtual void SetName( const char *pName ) = 0;
virtual IColorOperation *Clone() = 0;
virtual ColorCorrectionTool_t ToolID() = 0;
virtual bool IsEnabled() = 0;
virtual void SetEnabled( bool bEnable ) = 0;
virtual void SetBlendFactor( float flBlendFactor ) = 0;
virtual float GetBlendFactor( ) = 0;
};
//-----------------------------------------------------------------------------
// List of color operations
//-----------------------------------------------------------------------------
class CColorOperationList
{
public:
CColorOperationList();
// Clears the list
void Clear();
// Adds an operation
void AddOperation( IColorOperation *pOp );
// Deletes the operation at the specified index
void DeleteOperation( int opIndex );
// Applys all operations in the list to the color
void Apply( color24 in, color24 &out, IColorOperation *pFinalOp=NULL );
// Queries for the number of operations in the list
int GetNumOperations( );
// Returns the operation at the specified index in the list
IColorOperation *GetOperation( int opIndex );
// Move the item at the specified index in the list towards the front
void BringForward( int opIndex );
// Move the item at the specified index in the list towards the back
void PushBack( int opIndex );
private:
CUtlVector< IColorOperation* > m_OpList;
};
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CColorOperationList::CColorOperationList()
{
}
//-----------------------------------------------------------------------------
// Clears the list
//-----------------------------------------------------------------------------
void CColorOperationList::Clear()
{
for ( int i = m_OpList.Count(); --i >= 0; )
{
m_OpList[i]->Release();
}
m_OpList.RemoveAll();
}
//-----------------------------------------------------------------------------
// Adds an operation
//-----------------------------------------------------------------------------
void CColorOperationList::AddOperation( IColorOperation *pOp )
{
m_OpList.AddToTail( pOp );
}
//-----------------------------------------------------------------------------
// Deletes an operation
//-----------------------------------------------------------------------------
void CColorOperationList::DeleteOperation( int opIndex )
{
if( !m_OpList.IsValidIndex( opIndex ) )
return;
m_OpList.Remove( opIndex );
}
//-----------------------------------------------------------------------------
// Applys all operations in the list to the color
//-----------------------------------------------------------------------------
void CColorOperationList::Apply( color24 in, color24 &out, IColorOperation *pFinalOp )
{
int nCount = m_OpList.Count();
if ( nCount == 0 )
{
out = in;
return;
}
Vector rgb;
Color24ToVector( in, &rgb );
for ( int i = 0; i < nCount && m_OpList[i] != pFinalOp ; ++i )
{
Vector temp;
m_OpList[i]->Apply( rgb, temp );
rgb = temp;
}
VectorToColor24( rgb, out );
}
//-----------------------------------------------------------------------------
// Queries for the number of operations in the list
//-----------------------------------------------------------------------------
int CColorOperationList::GetNumOperations( )
{
return m_OpList.Count();
}
//-----------------------------------------------------------------------------
// Returns the operation at the specified index in the list
//-----------------------------------------------------------------------------
IColorOperation *CColorOperationList::GetOperation( int opIndex )
{
if( !m_OpList.IsValidIndex( opIndex ) )
return NULL;
return m_OpList.Element( opIndex );
}
void CColorOperationList::BringForward( int opIndex )
{
if( !m_OpList.IsValidIndex( opIndex ) || opIndex==0 )
return;
IColorOperation *pOp = m_OpList[ opIndex ];
m_OpList.Remove( opIndex );
m_OpList.InsertBefore( opIndex-1, pOp );
}
void CColorOperationList::PushBack( int opIndex )
{
if( !m_OpList.IsValidIndex( opIndex ) || opIndex==m_OpList.Count()-1 )
return;
IColorOperation *pOp = m_OpList[ opIndex ];
m_OpList.Remove( opIndex );
m_OpList.InsertAfter( opIndex, pOp );
}
//-----------------------------------------------------------------------------
// Base class for all color correction tool panels
//-----------------------------------------------------------------------------
class CColorCorrectionUIChildPanel : public vgui::Frame
{
DECLARE_CLASS_SIMPLE( CColorCorrectionUIChildPanel, vgui::Frame );
public:
CColorCorrectionUIChildPanel( vgui::Panel *parent, const char *name ) : BaseClass( parent, name )
{
}
~CColorCorrectionUIChildPanel()
{
}
virtual void OnClose()
{
KeyValues *msg = new KeyValues( "OpPanelClose" );
msg->SetPtr( "panel", this );
PostMessage( GetParent(), msg );
}
virtual void Init() {}
virtual void Shutdown() {}
virtual IColorOperation *GetOperation() { return 0; }
virtual void OnKeyCodeTyped( KeyCode code )
{
if( code==KEY_ESCAPE )
{
void ShowHideColorCorrectionUI();
ShowHideColorCorrectionUI();
}
}
virtual void ReadUncorrectedImage( Rect_t *pSrcRect, unsigned char *pPreviewImage ) {}
};
//-----------------------------------------------------------------------------
// Sort function for ControlPoints
//-----------------------------------------------------------------------------
class CCurvesLessFunc
{
public:
bool Less( const Vector& src1, const Vector& src2, void *pCtx )
{
return src1.x < src2.x;
}
};
//-----------------------------------------------------------------------------
// Similar to the 'curves...' operation from Photoshop
//-----------------------------------------------------------------------------
class CCurvesColorOperation : public IColorOperation
{
public:
enum Channel_t
{
RED_CHANNEL = 0x1,
GREEN_CHANNEL = 0x2,
BLUE_CHANNEL = 0x4,
ALL_CHANNELS = RED_CHANNEL | GREEN_CHANNEL | BLUE_CHANNEL,
};
CCurvesColorOperation();
// Methods of IColorOperation
virtual void Apply( const Vector &inRGB, Vector &outRGB );
virtual void Release() { delete this; }
virtual const char *GetName() { return m_pName; }
virtual void SetName( const char *pName ) { V_strcpy_safe( m_pName, pName ); }
virtual IColorOperation *Clone();
virtual ColorCorrectionTool_t ToolID() { return CC_TOOL_CURVES; }
virtual bool IsEnabled( ) { return m_bEnable; }
virtual void SetEnabled( bool bEnable ) { m_bEnable = bEnable; }
// Controls which channels to modify (see Channel_t)
void SetChannelMask( int nMask );
// Controls how much this op should take effect (1 = use 100% converted color, 0 = use 100% input color)
virtual void SetBlendFactor( float flBlend );
virtual float GetBlendFactor( ) { return m_flBlendFactor; }
// Compute corrected color
float ComputeCorrectedColor( float flInColor );
// Finds or adds a control point
int FindControlPoint( float flInValue, float flTolerance );
// Finds or adds a control point
int FindOrAddControlPoint( float flInValue, float flTolerance, float flOutValue );
// Modifies a control point
int ModifyControlPoint( int nPoint, float flInValue, float flOutValue );
// Removes a control point. Points 0 and Last can't be removed
void RemoveControlPoint( int nPoint );
// Iterates the control points
int ControlPointCount() const;
void GetControlPoint( int nPoint, float *pInValue, float *pOutValue );
private:
// Computes actual corrected color (expensive!!)
float ComputeActualCorrectedColor( float flInColor );
// Update the outvalue array
void UpdateOutColorArray();
// This is an optimization to avoid a costly lookup
float m_pOutValue[256];
// Note: The x component of the control points is the in color
// and the y component of the control points is the out color
// z is unused; we use 3d vectors because the mathlib catmull rom
// spline stuff uses them.
int m_nChannelMask;
//-----------------------------------------------------------------------------
// Sort function for ControlPoints
//-----------------------------------------------------------------------------
class CurvesLessFunc
{
public:
bool Less( const Vector& src1, const Vector& src2, void *pCtx )
{
return src1.x < src2.x;
}
};
CUtlSortVector< Vector, CurvesLessFunc > m_ControlPoints;
float m_flBlendFactor;
char m_pName[256];
bool m_bEnable;
};
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CCurvesColorOperation::CCurvesColorOperation() : m_ControlPoints()
{
Vector startpt, endpt;
startpt.Init( 0, 0, 0 );
endpt.Init( 1, 1, 0 );
m_ControlPoints.Insert( startpt );
m_ControlPoints.Insert( endpt );
m_flBlendFactor = 1.0f;
m_nChannelMask = ALL_CHANNELS;
m_bEnable = true;
UpdateOutColorArray();
V_strcpy_safe( m_pName, "Curves" );
}
//-----------------------------------------------------------------------------
// Controls which channels to modify (see Channel_t)
//-----------------------------------------------------------------------------
void CCurvesColorOperation::SetChannelMask( int nMask )
{
m_nChannelMask = nMask;
colorcorrectiontools->UpdateColorCorrection();
}
//-----------------------------------------------------------------------------
// Controls how much this op should take effect (1 = use 100% converted color, 0 = use 100% input color)
//-----------------------------------------------------------------------------
void CCurvesColorOperation::SetBlendFactor( float flBlend )
{
m_flBlendFactor = flBlend;
colorcorrectiontools->UpdateColorCorrection();
}
//-----------------------------------------------------------------------------
// Iterates the control points
//-----------------------------------------------------------------------------
int CCurvesColorOperation::ControlPointCount() const
{
return m_ControlPoints.Count();
}
void CCurvesColorOperation::GetControlPoint( int nPoint, float *pInValue, float *pOutValue )
{
*pInValue = m_ControlPoints[nPoint].x;
*pOutValue = m_ControlPoints[nPoint].y;
}
//-----------------------------------------------------------------------------
// Finds or adds a control point
//-----------------------------------------------------------------------------
int CCurvesColorOperation::FindControlPoint( float flInValue, float flTolerance )
{
for ( int i = m_ControlPoints.Count(); --i >= 0; )
{
if ( fabs( m_ControlPoints[i].x - flInValue ) < flTolerance )
return i;
}
return -1;
}
//-----------------------------------------------------------------------------
// Finds or adds a control point
//-----------------------------------------------------------------------------
int CCurvesColorOperation::FindOrAddControlPoint( float flInValue, float flTolerance, float flOutValue )
{
int nPoint = FindControlPoint( flInValue, flTolerance );
if ( nPoint != -1 )
return nPoint;
Vector insert( flInValue, flOutValue, 0.0f );
m_ControlPoints.Insert( insert );
int n = m_ControlPoints.Find( insert );
UpdateOutColorArray();
colorcorrectiontools->UpdateColorCorrection();
return n;
}
//-----------------------------------------------------------------------------
// Modifies a control point
//-----------------------------------------------------------------------------
int CCurvesColorOperation::ModifyControlPoint( int nPoint, float flInValue, float flOutValue )
{
Assert( ( nPoint >= 0 ) && ( nPoint < m_ControlPoints.Count() ) );
Vector temp = m_ControlPoints[nPoint];
m_ControlPoints.Remove( nPoint );
temp.x = flInValue;
temp.y = flOutValue;
m_ControlPoints.Insert( temp );
int nIndex = m_ControlPoints.Find( temp );
UpdateOutColorArray();
colorcorrectiontools->UpdateColorCorrection();
return nIndex;
}
//-----------------------------------------------------------------------------
// Removes a control point. Points 0 and Last can't be removed
//-----------------------------------------------------------------------------
void CCurvesColorOperation::RemoveControlPoint( int nPoint )
{
Assert( ( nPoint >= 0 ) && ( nPoint < m_ControlPoints.Count() ) );
if ( ( nPoint == 0 ) || ( nPoint == m_ControlPoints.Count() - 1 ) )
return;
m_ControlPoints.Remove( nPoint );
UpdateOutColorArray();
colorcorrectiontools->UpdateColorCorrection();
}
//-----------------------------------------------------------------------------
// Computes actual corrected color (expensive!!)
//-----------------------------------------------------------------------------
float CCurvesColorOperation::ComputeActualCorrectedColor( float flInColor )
{
flInColor = clamp( flInColor, 0.0f, 1.0f );
// First find the control points we are between
Vector find( flInColor, 0, 0 );
int i = m_ControlPoints.FindLessOrEqual( find );
if ( i < 0 )
return m_ControlPoints[0].y;
int nCount = m_ControlPoints.Count();
if ( i == (nCount - 1) )
return m_ControlPoints[nCount - 1].y;
Vector *pControlPoints[4];
pControlPoints[0] = (i >= 1) ? &m_ControlPoints[i-1] : &m_ControlPoints[0];
pControlPoints[1] = &m_ControlPoints[i];
pControlPoints[2] = &m_ControlPoints[i+1];
pControlPoints[3] = (i + 2 < nCount) ? &m_ControlPoints[i+2] : &m_ControlPoints[nCount-1];
Vector vecOut;
ComputeSplinePoint( flInColor, pControlPoints, vecOut );
AssertFloatEquals( vecOut.x, flInColor, 1e-3 );
return vecOut.y;
}
//-----------------------------------------------------------------------------
// Update the outvalue array
//-----------------------------------------------------------------------------
void CCurvesColorOperation::UpdateOutColorArray()
{
for ( int i = 0; i < 256; ++i )
{
m_pOutValue[i] = ComputeActualCorrectedColor( (float)i / 255.0f );
}
}
//-----------------------------------------------------------------------------
// Compute corrected color
//-----------------------------------------------------------------------------
float CCurvesColorOperation::ComputeCorrectedColor( float flInColor )
{
flInColor *= 255.0f;
int i = (int)flInColor;
i = clamp( i, 0, 255 );
if ( i == 255 )
return m_pOutValue[i];
float f = flInColor - i;
return Lerp( f, m_pOutValue[i], m_pOutValue[i+1] );
}
//-----------------------------------------------------------------------------
// Apply curves
//-----------------------------------------------------------------------------
void CCurvesColorOperation::Apply( const Vector &inRGB, Vector &outRGB )
{
if( !m_bEnable )
{
outRGB = inRGB;
return;
}
if ( m_nChannelMask & RED_CHANNEL )
{
outRGB.x = ComputeCorrectedColor( inRGB.x );
}
else
{
outRGB.x = inRGB.x;
}
if ( m_nChannelMask & GREEN_CHANNEL )
{
outRGB.y = ComputeCorrectedColor( inRGB.y );
}
else
{
outRGB.y = inRGB.y;
}
if ( m_nChannelMask & BLUE_CHANNEL )
{
outRGB.z = ComputeCorrectedColor( inRGB.z );
}
else
{
outRGB.z = inRGB.z;
}
VectorLerp( inRGB, outRGB, m_flBlendFactor, outRGB );
}
IColorOperation *CCurvesColorOperation::Clone( )
{
CCurvesColorOperation *pClone = new CCurvesColorOperation();
Q_memcpy( pClone->m_pOutValue, m_pOutValue, sizeof(float)*256 );
pClone->m_nChannelMask = m_nChannelMask;
pClone->m_ControlPoints = m_ControlPoints;
pClone->m_flBlendFactor = m_flBlendFactor;
Q_memcpy( pClone->m_pName, m_pName, sizeof(char)*256 );
pClone->m_bEnable = m_bEnable;
return pClone;
}
//-----------------------------------------------------------------------------
// Panel that displays + edits color correction spline curves
//-----------------------------------------------------------------------------
class CColorCurvesEditPanel : public CCurveEditorPanel
{
DECLARE_CLASS_SIMPLE( CColorCurvesEditPanel, CCurveEditorPanel );
public:
// constructor
CColorCurvesEditPanel( vgui::Panel *pParent, const char *pName );
~CColorCurvesEditPanel();
// Sets the color curves operation to edit
void SetCurvesOp( CCurvesColorOperation *pCurvesOp );
protected:
// Control points + values...
virtual int FindOrAddControlPoint( float flIn, float flTolerance, float flOut );
virtual int FindControlPoint( float flIn, float flTolerance );
virtual int ModifyControlPoint( int nPoint, float flIn, float flOut );
virtual void RemoveControlPoint( int nPoint );
virtual float GetValue( float flIn );
virtual int ControlPointCount();
virtual void GetControlPoint( int nPoint, float *pIn, float *pOut );
private:
CCurvesColorOperation *m_pCurvesOp;
};
//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
CColorCurvesEditPanel::CColorCurvesEditPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName )
{
m_pCurvesOp = NULL;
SetVisible( false );
}
CColorCurvesEditPanel::~CColorCurvesEditPanel()
{
}
//-----------------------------------------------------------------------------
// Control points + values...
//-----------------------------------------------------------------------------
int CColorCurvesEditPanel::FindOrAddControlPoint( float flIn, float flTolerance, float flOut )
{
Assert( m_pCurvesOp );
return m_pCurvesOp->FindOrAddControlPoint( flIn, flTolerance, flOut );
}
int CColorCurvesEditPanel::FindControlPoint( float flIn, float flTolerance )
{
Assert( m_pCurvesOp );
return m_pCurvesOp->FindControlPoint( flIn, flTolerance );
}
int CColorCurvesEditPanel::ModifyControlPoint( int nPoint, float flIn, float flOut )
{
Assert( m_pCurvesOp );
m_pCurvesOp->ModifyControlPoint( nPoint, flIn, flOut );
return nPoint;
}
void CColorCurvesEditPanel::RemoveControlPoint( int nPoint )
{
Assert( m_pCurvesOp );
m_pCurvesOp->RemoveControlPoint( nPoint );
}
float CColorCurvesEditPanel::GetValue( float flIn )
{
Assert( m_pCurvesOp );
return m_pCurvesOp->ComputeCorrectedColor( flIn );
}
int CColorCurvesEditPanel::ControlPointCount()
{
Assert( m_pCurvesOp );
return m_pCurvesOp->ControlPointCount( );
}
void CColorCurvesEditPanel::GetControlPoint( int nPoint, float *pIn, float *pOut )
{
Assert( m_pCurvesOp );
m_pCurvesOp->GetControlPoint( nPoint, pIn, pOut );
}
//-----------------------------------------------------------------------------
// Sets the color curves operation to edit
//-----------------------------------------------------------------------------
void CColorCurvesEditPanel::SetCurvesOp( CCurvesColorOperation *pCurvesOp )
{
m_pCurvesOp = pCurvesOp;
SetVisible( m_pCurvesOp != NULL );
}
//-----------------------------------------------------------------------------
// Root panel for editing color curves
//-----------------------------------------------------------------------------
class CColorCurvesUIPanel : public CColorCorrectionUIChildPanel
{
DECLARE_CLASS_SIMPLE( CColorCurvesUIPanel, CColorCorrectionUIChildPanel );
public:
// constructor
CColorCurvesUIPanel( vgui::Panel *pParent, CCurvesColorOperation *pOp );
~CColorCurvesUIPanel();
virtual void Init() {}
virtual void Shutdown() {}
virtual void ReadUncorrectedImage( Rect_t *pSrcRect, unsigned char *pPreviewImage ) {};
virtual IColorOperation *GetOperation( ) { return (IColorOperation*)m_pColorOp; }
// Command issued
virtual void OnMessage(const KeyValues *params, vgui::VPANEL fromPanel);
virtual void OnCommand( const char *command );
private:
enum
{
COLOR_MASK_RGB = 0,
COLOR_MASK_RED,
COLOR_MASK_GREEN,
COLOR_MASK_BLUE,
COLOR_MASK_TYPE_COUNT
};
// The color mask was changed
void OnColorMaskSelected();
MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", data );
void ResetBlendFactorSlider();
vgui::ComboBox *m_pColorMask;
CPrecisionSlider *m_pBlendFactorSlider;
CColorCurvesEditPanel *m_pCurveEditor;
CCurvesColorOperation *m_pColorOp;
static const char *s_pColorMaskLabel[COLOR_MASK_TYPE_COUNT];
};
const char *CColorCurvesUIPanel::s_pColorMaskLabel[CColorCurvesUIPanel::COLOR_MASK_TYPE_COUNT] =
{
"RGB",
"Red",
"Green",
"Blue"
};
//-----------------------------------------------------------------------------
// constructor
//-----------------------------------------------------------------------------
CColorCurvesUIPanel::CColorCurvesUIPanel( vgui::Panel *pParent, CCurvesColorOperation *pOp ) : BaseClass( pParent, "ColorCurvesUIPanel" )
{
m_pColorMask = new ComboBox(this, "ColorMask", COLOR_MASK_TYPE_COUNT, false);
int i;
for ( i = 0; i < COLOR_MASK_TYPE_COUNT; i++ )
{
m_pColorMask->AddItem( s_pColorMaskLabel[i], NULL );
}
m_pColorMask->AddActionSignalTarget( this );
m_pColorMask->ActivateItem( 0 );
m_pBlendFactorSlider = new CPrecisionSlider( this, "BlendFactorSlider" );
m_pBlendFactorSlider->SetRange( 0, 255 );
m_pBlendFactorSlider->SetValue( 255 );
m_pBlendFactorSlider->AddActionSignalTarget( this );
m_pColorOp = pOp;
m_pCurveEditor = new CColorCurvesEditPanel( this, "CurveEditor" );
m_pCurveEditor->SetCurvesOp( m_pColorOp );
LoadControlSettings("Resource\\ColorCurvesUIPanel.res");
}
CColorCurvesUIPanel::~CColorCurvesUIPanel()
{
if( m_pCurveEditor )
delete m_pCurveEditor;
}
//-----------------------------------------------------------------------------
// Command issued
//-----------------------------------------------------------------------------
void CColorCurvesUIPanel::OnMessage(const KeyValues *params, vgui::VPANEL fromPanel)
{
BaseClass::OnMessage( params, fromPanel );
if ( !Q_stricmp( "SliderMoved", params->GetName() ) )
{
vgui::Panel *pPanel = reinterpret_cast<vgui::Panel *>( const_cast<KeyValues*>(params)->GetPtr("panel") );
CPrecisionSlider *pSlider = dynamic_cast<CPrecisionSlider *>( pPanel );
if ( pSlider == m_pBlendFactorSlider )
{
m_pColorOp->SetBlendFactor( m_pBlendFactorSlider->GetValue() / 255.0f );
}
PostMessage( GetParent(), new KeyValues( "command", "command", "BlendFactorUpdate" ) );
}
}
void CColorCurvesUIPanel::OnCommand( const char *command )
{
BaseClass::OnCommand( command );
if( !Q_stricmp( "BlendFactorUpdate", command ) )
{
ResetBlendFactorSlider( );
}
}
void CColorCurvesUIPanel::ResetBlendFactorSlider()
{
float flBlend;
if( m_pColorOp )
flBlend = m_pColorOp->GetBlendFactor();
else
flBlend = 0.0f;
m_pBlendFactorSlider->SetValue( flBlend*255.0f );
}
//-----------------------------------------------------------------------------
// The color mask was changed
//-----------------------------------------------------------------------------
void CColorCurvesUIPanel::OnColorMaskSelected()
{
int nMask = m_pColorMask->GetActiveItem();
switch( nMask )
{
case COLOR_MASK_RGB:
m_pColorOp->SetChannelMask( CCurvesColorOperation::ALL_CHANNELS );
break;
case COLOR_MASK_RED:
m_pColorOp->SetChannelMask( CCurvesColorOperation::RED_CHANNEL );
break;
case COLOR_MASK_GREEN:
m_pColorOp->SetChannelMask( CCurvesColorOperation::GREEN_CHANNEL );
break;
case COLOR_MASK_BLUE:
m_pColorOp->SetChannelMask( CCurvesColorOperation::BLUE_CHANNEL );
break;
}
}
//-----------------------------------------------------------------------------
// A combo box changed
//-----------------------------------------------------------------------------
void CColorCurvesUIPanel::OnTextChanged( KeyValues *data )
{
Panel *pPanel = reinterpret_cast<vgui::Panel *>( data->GetPtr("panel") );
vgui::ComboBox *pBox = dynamic_cast<vgui::ComboBox *>( pPanel );
if ( pBox == m_pColorMask )
{
OnColorMaskSelected();
return;
}
}
//-----------------------------------------------------------------------------
// Similar to the 'levels...' operation from Photoshop
//-----------------------------------------------------------------------------
class CLevelsColorOperation : public IColorOperation
{
public:
enum Channel_t
{
RED_CHANNEL = 0x1,
GREEN_CHANNEL = 0x2,
BLUE_CHANNEL = 0x4,
ALL_CHANNELS = RED_CHANNEL | GREEN_CHANNEL | BLUE_CHANNEL,
};
CLevelsColorOperation();
// Methods of IColorOperation
virtual void Apply( const Vector &inRGB, Vector &outRGB );
virtual void Release() { delete this; }
virtual const char *GetName() { return m_pName; }
virtual void SetName( const char *pName ) { V_strcpy_safe( m_pName, pName ); }
virtual ColorCorrectionTool_t ToolID() { return CC_TOOL_LEVELS; }
virtual IColorOperation *Clone();
virtual bool IsEnabled( ) { return m_bEnable; }
virtual void SetEnabled( bool bEnable ) { m_bEnable = bEnable; }
// Controls which channels to modify (see Channel_t)
void SetChannelMask( int nMask );
// Controls how much this op should take effect (1 = use 100% converted color, 0 = use 100% input color)
virtual void SetBlendFactor( float flBlend );
virtual float GetBlendFactor( ) { return m_flBlendFactor; }
// Sets input levels
void SetInputLevels( float flMinValue, float flMidValue, float flMaxValue );
// Sets output levels
void SetOutputLevels( float flMinValue, float flMaxValue );
// Used to set/get the list
CColorOperationList *GetColorOpList() { return m_pOpList; }
void SetColorOpList( CColorOperationList *pList ) { m_pOpList = pList; }
private:
// Computes normalized input level (expensive!!)
float ComputeNormalizedInputLevel( float flInLevel );
// Compute corrected level
float ComputeCorrectedLevel( float flInLevel );
// Update the outvalue array
void UpdateOutputLevelArray();
// This is an optimization to avoid a costly lookup
float m_pOutValue[256];
int m_nChannelMask;
float m_flBlendFactor;
float m_flMinInputLevel;
float m_flMidInputLevel;
float m_flMaxInputLevel;
float m_flMinOutputLevel;
float m_flMaxOutputLevel;
bool m_bEnable;
char m_pName[256];
CColorOperationList *m_pOpList;
};
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CLevelsColorOperation::CLevelsColorOperation()
{
m_flMinInputLevel = 0.0f;
m_flMidInputLevel = 0.5f;
m_flMaxInputLevel = 1.0f;
m_flMinOutputLevel = 0.0f;
m_flMaxOutputLevel = 1.0f;
m_flBlendFactor = 1.0f;
m_nChannelMask = ALL_CHANNELS;
m_bEnable = true;
UpdateOutputLevelArray();
V_strcpy_safe( m_pName, "Levels" );
}
//-----------------------------------------------------------------------------
// Controls which channels to modify (see Channel_t)
//-----------------------------------------------------------------------------
void CLevelsColorOperation::SetChannelMask( int nMask )
{
m_nChannelMask = nMask;
colorcorrectiontools->UpdateColorCorrection();
}
//-----------------------------------------------------------------------------
// Controls how much this op should take effect (1 = use 100% converted color, 0 = use 100% input color)
//-----------------------------------------------------------------------------
void CLevelsColorOperation::SetBlendFactor( float flBlend )
{
m_flBlendFactor = flBlend;
colorcorrectiontools->UpdateColorCorrection();
}
//-----------------------------------------------------------------------------
// Sets input levels
//-----------------------------------------------------------------------------
void CLevelsColorOperation::SetInputLevels( float flMinValue, float flMidValue, float flMaxValue )
{
m_flMinInputLevel = clamp( flMinValue, 0.0f, 1.0f );
m_flMidInputLevel = clamp( flMidValue, 0.0f, 1.0f );
m_flMaxInputLevel = clamp( flMaxValue, 0.0f, 1.0f );
UpdateOutputLevelArray();
colorcorrectiontools->UpdateColorCorrection();
}
//-----------------------------------------------------------------------------
// Sets output levels
//-----------------------------------------------------------------------------
void CLevelsColorOperation::SetOutputLevels( float flMinValue, float flMaxValue )
{
m_flMinOutputLevel = clamp( flMinValue, 0.0f, 1.0f );
m_flMaxOutputLevel = clamp( flMaxValue, 0.0f, 1.0f );
UpdateOutputLevelArray();
colorcorrectiontools->UpdateColorCorrection();
}
//-----------------------------------------------------------------------------
// Computes actual corrected level (expensive!!)
//-----------------------------------------------------------------------------
float CLevelsColorOperation::ComputeNormalizedInputLevel( float flInLevel )
{
if ( flInLevel <= m_flMinInputLevel )
return 0.0f;
if ( flInLevel >= m_flMaxInputLevel )
return 1.0f;
// We effectively have 3 control points; 1 at each end, and 1 in the middle
// Duplicate the end which is
Vector controlPoints[4];
controlPoints[0].Init( m_flMinInputLevel, 0.0f, 0.0f );
controlPoints[3].Init( m_flMaxInputLevel, 1.0f, 0.0f );
if ( flInLevel < m_flMidInputLevel )
{
controlPoints[1].Init( m_flMinInputLevel, 0.0f, 0.0f );
controlPoints[2].Init( m_flMidInputLevel, 0.5f, 0.0f );
}
else
{
controlPoints[1].Init( m_flMidInputLevel, 0.5f, 0.0f );
controlPoints[2].Init( m_flMaxInputLevel, 1.0f, 0.0f );
}
Vector *pControlPoints[4];
pControlPoints[0] = &controlPoints[0];
pControlPoints[1] = &controlPoints[1];
pControlPoints[2] = &controlPoints[2];
pControlPoints[3] = &controlPoints[3];
Vector vecOut;
ComputeSplinePoint( flInLevel, pControlPoints, vecOut );
AssertFloatEquals( vecOut.x, flInLevel, 1e-5 );
return vecOut.y;
}
//-----------------------------------------------------------------------------
// Update the outvalue array
//-----------------------------------------------------------------------------
void CLevelsColorOperation::UpdateOutputLevelArray()
{
for ( int i = 0; i < 256; ++i )
{
m_pOutValue[i] = ComputeNormalizedInputLevel( (float)i / 255.0f );
m_pOutValue[i] *= m_flMaxOutputLevel - m_flMinOutputLevel;
m_pOutValue[i] += m_flMinOutputLevel;
}
}
//-----------------------------------------------------------------------------
// Compute corrected level
//-----------------------------------------------------------------------------
float CLevelsColorOperation::ComputeCorrectedLevel( float flInLevel )
{
flInLevel *= 255.0f;
int i = (int)flInLevel;
i = clamp( i, 0, 255 );
if ( i == 255 )
return m_pOutValue[i];
float f = flInLevel - i;
return Lerp( f, m_pOutValue[i], m_pOutValue[i+1] );
}
//-----------------------------------------------------------------------------
// Apply curves
//-----------------------------------------------------------------------------
void CLevelsColorOperation::Apply( const Vector &inRGB, Vector &outRGB )
{
if( !m_bEnable )
{
outRGB = inRGB;
return;
}
if ( m_nChannelMask & RED_CHANNEL )
{
outRGB.x = ComputeCorrectedLevel( inRGB.x );
}
else
{
outRGB.x = inRGB.x;
}
if ( m_nChannelMask & GREEN_CHANNEL )
{
outRGB.y = ComputeCorrectedLevel( inRGB.y );
}
else
{
outRGB.y = inRGB.y;
}
if ( m_nChannelMask & BLUE_CHANNEL )
{
outRGB.z = ComputeCorrectedLevel( inRGB.z );
}
else
{
outRGB.z = inRGB.z;
}
VectorLerp( inRGB, outRGB, m_flBlendFactor, outRGB );
}
IColorOperation *CLevelsColorOperation::Clone( )
{
CLevelsColorOperation *pClone = new CLevelsColorOperation;
Q_memcpy( pClone->m_pOutValue, m_pOutValue, sizeof(float)*256.0f );
pClone->m_nChannelMask = m_nChannelMask;
pClone->m_flBlendFactor = m_flBlendFactor;
pClone->m_flMinInputLevel = m_flMinInputLevel;
pClone->m_flMidInputLevel = m_flMidInputLevel;
pClone->m_flMaxInputLevel = m_flMaxInputLevel;
pClone->m_flMinOutputLevel = m_flMinOutputLevel;
pClone->m_flMaxOutputLevel = m_flMaxOutputLevel;
pClone->m_bEnable = m_bEnable;
pClone->m_pOpList = m_pOpList;
Q_memcpy( pClone->m_pName, m_pName, sizeof(char)*256 );
return pClone;
}
//-----------------------------------------------------------------------------
// Panel that displays a histogram of the color information
//-----------------------------------------------------------------------------
class CColorHistogramPanel : public vgui::Panel
{
DECLARE_CLASS_SIMPLE( CColorHistogramPanel, vgui::Panel );
public:
enum HistogramType_t
{
RED = 0,
GREEN,
BLUE,
RGB,
HISTOGRAM_TYPE_COUNT,
};
// constructor
CColorHistogramPanel( vgui::Panel *pParent, const char *pName, CLevelsColorOperation *pOp );
~CColorHistogramPanel();
virtual void Paint( void );
virtual void PaintBackground( void );
void SetHistogramType( HistogramType_t type );
void ComputeHistogram( Rect_t &srcRect, unsigned char *pBits, ImageFormat format, int nStride );
private:
// Converts screen location to normalized color values and back
void ScreenToColor( int x, int y, float *pIn, float *pOut );
void ColorToScreen( float flIn, float flOut, int *x, int *y );
// The histogram of the screen image
float m_pHistogram[256];
HistogramType_t m_Type;
CLevelsColorOperation *m_pOp;
float m_flMax;
};
//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
CColorHistogramPanel::CColorHistogramPanel( vgui::Panel *pParent, const char *pName, CLevelsColorOperation *pOp ) : BaseClass( pParent, pName )
{
for ( int i = 0; i < 256; ++i )
{
m_pHistogram[i] = 0.0f;
}
m_Type = RGB;
m_pOp = pOp;
}
CColorHistogramPanel::~CColorHistogramPanel()
{
}
//-----------------------------------------------------------------------------
// Computes histogram
//-----------------------------------------------------------------------------
void CColorHistogramPanel::SetHistogramType( HistogramType_t type )
{
m_Type = type;
}
void CColorHistogramPanel::ComputeHistogram( Rect_t &srcRect, unsigned char *pBits, ImageFormat format, int nStride )
{
for ( int i = 0; i < 256; ++i )
{
m_pHistogram[i] = 0.0f;
}
int nPixelCount = srcRect.width * srcRect.height;
int nSizeInBytes = ImageLoader::SizeInBytes( format );
CPixelWriter writer;
writer.SetPixelMemory( format, pBits + srcRect.y * nStride + srcRect.x * nSizeInBytes, nStride );
for ( int y = 0; y < srcRect.height; ++y )
{
writer.Seek( 0, y );
for ( int x = 0; x < srcRect.width; ++x )
{
int r, g, b, a;
writer.ReadPixelNoAdvance( r, g, b, a );
color24 inColor, col;
inColor.r = clamp( r, 0, 255 );
inColor.g = clamp( g, 0, 255 );
inColor.b = clamp( b, 0, 255 );
m_pOp->GetColorOpList()->Apply( inColor, col, m_pOp );
switch( m_Type )
{
case RED:
++m_pHistogram[col.r];
break;
case GREEN:
++m_pHistogram[col.g];
break;
case BLUE:
++m_pHistogram[col.b];
break;
case RGB:
{
float flGreyScale = 0.299f * col.r + 0.587f * col.g + 0.114f * col.b;
g = (int)(flGreyScale + 0.5f);
g = clamp( g, 0, 255 );
++m_pHistogram[g];
}
break;
}
writer.SkipBytes( nSizeInBytes );
}
}
m_flMax = 0.0f;
for ( int i = 0; i < 256; ++i )
{
m_pHistogram[i] /= (float)nPixelCount;
if ( m_flMax < m_pHistogram[i] )
{
m_flMax = m_pHistogram[i];
}
}
}
//-----------------------------------------------------------------------------
// This paints the grid behind the curves
//-----------------------------------------------------------------------------
void CColorHistogramPanel::PaintBackground( void )
{
int w, h;
GetSize( w, h );
vgui::surface()->DrawSetColor( 255, 255, 255, 255 );
vgui::surface()->DrawFilledRect( 0, 0, w, h );
vgui::surface()->DrawSetColor( 128, 128, 128, 255 );
vgui::surface()->DrawLine( 0, h/4, w, h/4 );
vgui::surface()->DrawLine( 0, h/2, w, h/2 );
vgui::surface()->DrawLine( 0, 3*h/4, w, 3*h/4 );
vgui::surface()->DrawLine( w/4, 0, w/4, h );
vgui::surface()->DrawLine( w/2, 0, w/2, h );
vgui::surface()->DrawLine( 3*w/4, 0, 3*w/4, h );
vgui::surface()->DrawSetColor( 0, 0, 0, 255 );
vgui::surface()->DrawLine( 0, 0, w, 0 );
vgui::surface()->DrawLine( w, 0, w, h );
vgui::surface()->DrawLine( w, h, 0, h );
vgui::surface()->DrawLine( 0, h, 0, 0 );
}
//-----------------------------------------------------------------------------
// Sets the color curves operation to edit
//-----------------------------------------------------------------------------
void CColorHistogramPanel::Paint( void )
{
int w, h;
GetSize( w, h );
// FIXME: Add method to draw multiple lines DrawPolyLine connects the 1st and last points... bleah
switch( m_Type )
{
case RED:
vgui::surface()->DrawSetColor( 255, 0, 0, 255 );
break;
case GREEN:
vgui::surface()->DrawSetColor( 0, 255, 0, 255 );
break;
case BLUE:
vgui::surface()->DrawSetColor( 0, 0, 255, 255 );
break;
case RGB:
vgui::surface()->DrawSetColor( 0, 0, 0, 255 );
break;
}
float flOOMax = (m_flMax != 0.0f) ? 1.0f / m_flMax : 1.0f;
for ( int i = 0; i < 256; ++i )
{
int x = (float)i * (w-1) / 255.0f;
int y = (float)m_pHistogram[i] * (h-1) * flOOMax;
vgui::surface()->DrawLine( x, h - 1, x, h - 1 - y );
}
}
//-----------------------------------------------------------------------------
// A color slider panel used to control input + output levels
//-----------------------------------------------------------------------------
class CColorSlider : public vgui::Panel
{
DECLARE_CLASS_SIMPLE( CColorSlider, vgui::Panel );
public:
// constructor
CColorSlider( vgui::Panel *pParent, const char *pName, int nKnobCount );
~CColorSlider();
// Painting
virtual void Paint();
void SetValue( int nKnobIndex, int value );
void SetNormalizedValue( int nKnobIndex, float flValue );
int GetValue( int nKnobIndex );
void SetRange( int min, int max ); // set to max and min range of rows to display
void GetRange( int &min, int &max );
virtual void OnCursorMoved( int x,int y );
virtual void OnMousePressed( vgui::MouseCode code );
virtual void OnMouseReleased( MouseCode code );
private:
// Draws a single knob with a particular color
void PaintKnob( float flPosition, unsigned char r, unsigned char g, unsigned char b );
// Purpose: Send a message to interested parties when the slider moves
void SendSliderMovedMessage( int nKnobIndex );
// Update other sliders
void UpdateOtherSliders( int nKnobChanged );
int m_nKnobCount;
float m_flKnobPosition[3];
int m_nMinValue;
int m_nMaxValue;
int m_nWhiteMaterial;
int m_nSelectedKnob;
};
//-----------------------------------------------------------------------------
// constructor
//-----------------------------------------------------------------------------
CColorSlider::CColorSlider( vgui::Panel *pParent, const char *pName, int nKnobCount ) : BaseClass( pParent, pName )
{
m_nKnobCount = nKnobCount;
Assert( m_nKnobCount == 2 || m_nKnobCount == 3 );
m_flKnobPosition[0] = 0.0f;
m_flKnobPosition[1] = 1.0f;
m_flKnobPosition[2] = 0.5f;
m_nMinValue = 0;
m_nMaxValue = 1;
m_nSelectedKnob = -1;
m_nWhiteMaterial = vgui::surface()->CreateNewTextureID();
vgui::surface()->DrawSetTextureFile( m_nWhiteMaterial, "vgui/white" , true, false );
SetMouseInputEnabled( true );
}
CColorSlider::~CColorSlider()
{
if ( vgui::surface() && m_nWhiteMaterial != -1 )
{
vgui::surface()->DestroyTextureID( m_nWhiteMaterial );
m_nWhiteMaterial = -1;
}
}
//-----------------------------------------------------------------------------
// Purpose: Send a message to interested parties when the slider moves
//-----------------------------------------------------------------------------
void CColorSlider::SendSliderMovedMessage( int nKnobIndex )
{
// send a changed message
PostActionSignal( new KeyValues("SliderMoved", "knob", nKnobIndex) );
}
//-----------------------------------------------------------------------------
// Update other sliders
//-----------------------------------------------------------------------------
void CColorSlider::UpdateOtherSliders( int nKnobChanged )
{
// Remember: 0 == low, 1 == high, 2 == middle!
float flValue = m_flKnobPosition[ nKnobChanged ];
switch (nKnobChanged)
{
case 0:
if ( m_flKnobPosition[1] < flValue )
{
m_flKnobPosition[1] = flValue;
}
if ( m_flKnobPosition[2] < flValue )
{
m_flKnobPosition[2] = flValue;
}
break;
case 1:
if ( m_flKnobPosition[0] > flValue )
{
m_flKnobPosition[0] = flValue;
}
if ( m_flKnobPosition[2] > flValue )
{
m_flKnobPosition[2] = flValue;
}
break;
case 2:
if ( m_flKnobPosition[0] > flValue )
{
m_flKnobPosition[0] = flValue;
}
if ( m_flKnobPosition[1] < flValue )
{
m_flKnobPosition[1] = flValue;
}
break;
}
}
//-----------------------------------------------------------------------------
// Value getting + setting
//-----------------------------------------------------------------------------
void CColorSlider::SetNormalizedValue( int nKnobIndex, float flValue )
{
Assert( m_nKnobCount > nKnobIndex );
m_flKnobPosition[nKnobIndex] = clamp( flValue, 0.0f, 1.0f );
UpdateOtherSliders( nKnobIndex );
SendSliderMovedMessage( nKnobIndex );
}
void CColorSlider::SetValue( int nKnobIndex, int value )
{
Assert( m_nKnobCount > nKnobIndex );
SetNormalizedValue( nKnobIndex, (float)(value - m_nMinValue) / (m_nMaxValue - m_nMinValue) );
}
int CColorSlider::GetValue( int nKnobIndex )
{
Assert( m_nKnobCount > nKnobIndex );
return m_flKnobPosition[nKnobIndex] * (m_nMaxValue - m_nMinValue) + m_nMinValue;
}
void CColorSlider::SetRange( int minValue, int maxValue )
{
Assert( maxValue > minValue );
m_nMinValue = minValue;
m_nMaxValue = maxValue;
}
void CColorSlider::GetRange( int &minValue, int &maxValue )
{
minValue = m_nMinValue;
maxValue = m_nMaxValue;
}
//-----------------------------------------------------------------------------
// Handle input
//-----------------------------------------------------------------------------
void CColorSlider::OnMousePressed( vgui::MouseCode code )
{
BaseClass::OnMousePressed( code );
int x, y;
input()->GetCursorPos( x, y );
ScreenToLocal( x, y );
if ( code == MOUSE_LEFT )
{
input()->SetMouseCapture(GetVPanel());
// Choose the closest knob
int w, h;
GetSize( w, h );
float flNormalizedVal = (float)x / (w-1);
m_nSelectedKnob = 0;
for ( int i = 1; i < m_nKnobCount; ++i )
{
if ( fabs(flNormalizedVal - m_flKnobPosition[i]) < fabs(flNormalizedVal - m_flKnobPosition[m_nSelectedKnob]) )
{
m_nSelectedKnob = i;
}
}
SetNormalizedValue( m_nSelectedKnob, flNormalizedVal );
}
}
void CColorSlider::OnMouseReleased( vgui::MouseCode code )
{
BaseClass::OnMouseReleased( code );
if ( code == MOUSE_LEFT )
{
if ( m_nSelectedKnob >= 0 )
{
input()->SetMouseCapture( NULL );
m_nSelectedKnob = -1;
}
}
}
void CColorSlider::OnCursorMoved( int x, int y )
{
BaseClass::OnCursorMoved( x, y );
if ( m_nSelectedKnob >= 0 )
{
int w, h;
GetSize( w, h );
float flNormalizedVal = (float)x / (w-1);
if( m_nSelectedKnob<2 && m_nKnobCount==3 )
{
// We need to adjust the grey knob, if active
float fOldRelGrey = (m_flKnobPosition[2] - m_flKnobPosition[0]) / (m_flKnobPosition[1] - m_flKnobPosition[0]);
SetNormalizedValue( m_nSelectedKnob, flNormalizedVal );
SetNormalizedValue( 2, fOldRelGrey*(m_flKnobPosition[1]-m_flKnobPosition[0]) + m_flKnobPosition[0] );
}
else
{
SetNormalizedValue( m_nSelectedKnob, flNormalizedVal );
}
}
}
//-----------------------------------------------------------------------------
// Draws a single knob with a particular color
//-----------------------------------------------------------------------------
void CColorSlider::PaintKnob( float flPosition, unsigned char r, unsigned char g, unsigned char b )
{
int w, h;
GetSize( w, h );
Vertex_t triangle[3];
triangle[0].m_Position.x = flPosition * (w-1);
triangle[0].m_Position.y = 0.0f;
triangle[0].m_TexCoord.Init( 0.0f, 0.0f );
triangle[1].m_Position.x = triangle[0].m_Position.x + (h-1);
triangle[1].m_Position.y = (h-1);
triangle[1].m_TexCoord.Init( 0.0f, 0.0f );
triangle[2].m_Position.x = triangle[0].m_Position.x - (h-1);
triangle[2].m_Position.y = (h-1);
triangle[2].m_TexCoord.Init( 0.0f, 0.0f );
vgui::surface()->DrawSetColor( r, g, b, 255 );
vgui::surface()->DrawSetTexture( m_nWhiteMaterial );
vgui::surface()->DrawTexturedPolygon( 3, triangle );
vgui::surface()->DrawSetColor( 0, 0, 0, 255 );
vgui::surface()->DrawTexturedPolyLine( triangle, 3 );
}
//-----------------------------------------------------------------------------
// Painting
//-----------------------------------------------------------------------------
void CColorSlider::Paint()
{
// Knob 0 is black, knob 1 is white, knob 2 is grey (if active)
PaintKnob( m_flKnobPosition[0], 0, 0, 0 );
if ( m_nKnobCount == 3 )
{
PaintKnob( m_flKnobPosition[2], 128, 128, 128 );
}
PaintKnob( m_flKnobPosition[1], 255, 255, 255 );
}
//-----------------------------------------------------------------------------
// Root panel for editing levels
//-----------------------------------------------------------------------------
class CColorLevelsUIPanel : public CColorCorrectionUIChildPanel
{
DECLARE_CLASS_SIMPLE( CColorLevelsUIPanel, CColorCorrectionUIChildPanel );
public:
// constructor
CColorLevelsUIPanel( vgui::Panel *pParent, CLevelsColorOperation *pOp );
~CColorLevelsUIPanel();
// Command issued
virtual void OnMessage(const KeyValues *params, vgui::VPANEL fromPanel);
virtual void OnCommand( const char *command );
// Reads the uncorrected image + generates a hisogram
virtual void ReadUncorrectedImage( Rect_t *pSrcRect, unsigned char *pPreviewImage );
virtual void Init() {}
virtual void Shutdown() {}
virtual IColorOperation *GetOperation( ) { return (IColorOperation*)m_pLevelsOp; }
private:
enum
{
HISTOGRAM_IMAGE_SIZE = 256
};
enum
{
COLOR_MASK_RGB = 0,
COLOR_MASK_RED,
COLOR_MASK_GREEN,
COLOR_MASK_BLUE,
COLOR_MASK_TYPE_COUNT
};
// The color mask was changed
void OnColorMaskSelected();
MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", data );
void ResetBlendFactorSlider();
vgui::ComboBox *m_pColorMask;
CPrecisionSlider *m_pBlendFactorSlider;
CColorHistogramPanel *m_pHistogramPanel;
CLevelsColorOperation *m_pLevelsOp;
CColorSlider *m_pInputLevelSlider;
CColorSlider *m_pOutputLevelSlider;
static const char *s_pColorMaskLabel[COLOR_MASK_TYPE_COUNT];
};
const char *CColorLevelsUIPanel::s_pColorMaskLabel[CColorLevelsUIPanel::COLOR_MASK_TYPE_COUNT] =
{
"RGB",
"Red",
"Green",
"Blue"
};
//-----------------------------------------------------------------------------
// constructor
//-----------------------------------------------------------------------------
CColorLevelsUIPanel::CColorLevelsUIPanel( vgui::Panel *pParent, CLevelsColorOperation *pOp ) : BaseClass( pParent, "LevelsUIPanel" )
{
m_pColorMask = new ComboBox(this, "ColorMask", COLOR_MASK_TYPE_COUNT, false);
int i;
for ( i = 0; i < COLOR_MASK_TYPE_COUNT; i++ )
{
m_pColorMask->AddItem( s_pColorMaskLabel[i], NULL );
}
m_pColorMask->AddActionSignalTarget( this );
m_pColorMask->ActivateItem( 0 );
m_pBlendFactorSlider = new CPrecisionSlider( this, "BlendFactorSlider" );
m_pBlendFactorSlider->SetRange( 0, 255 );
m_pBlendFactorSlider->SetValue( 255 );
m_pBlendFactorSlider->AddActionSignalTarget( this );
m_pInputLevelSlider = new CColorSlider( this, "InputLevelSlider", 3 );
m_pInputLevelSlider->SetRange( 0, 255 );
m_pInputLevelSlider->AddActionSignalTarget( this );
m_pOutputLevelSlider = new CColorSlider( this, "OutputLevelSlider", 2 );
m_pOutputLevelSlider->SetRange( 0, 255 );
m_pOutputLevelSlider->AddActionSignalTarget( this );
m_pLevelsOp = new CLevelsColorOperation;
m_pHistogramPanel = new CColorHistogramPanel( this, "Histogram", pOp );
m_pLevelsOp = pOp;
LoadControlSettings("Resource\\ColorLevelsUIPanel.res");
ResetBlendFactorSlider();
}
CColorLevelsUIPanel::~CColorLevelsUIPanel()
{
}
//-----------------------------------------------------------------------------
// Reads the uncorrected image + generates a hisogram
//-----------------------------------------------------------------------------
void CColorLevelsUIPanel::ReadUncorrectedImage( Rect_t *pSrcRect, unsigned char *pPreviewImage )
{
Rect_t dstRect;
dstRect.x = 0;
dstRect.y = 0;
dstRect.width = g_nPreviewImageWidth;
dstRect.height = g_nPreviewImageHeight;
m_pHistogramPanel->ComputeHistogram( dstRect, pPreviewImage, IMAGE_FORMAT_BGRX8888, dstRect.width * 4 );
}
//-----------------------------------------------------------------------------
// Command issued
//-----------------------------------------------------------------------------
void CColorLevelsUIPanel::OnMessage(const KeyValues *params, vgui::VPANEL fromPanel)
{
BaseClass::OnMessage( params, fromPanel );
if ( !Q_stricmp( "SliderMoved", params->GetName() ) )
{
vgui::Panel *pPanel = reinterpret_cast<vgui::Panel *>( const_cast<KeyValues*>(params)->GetPtr("panel") );
if ( pPanel == m_pBlendFactorSlider )
{
m_pLevelsOp->SetBlendFactor( m_pBlendFactorSlider->GetValue() / 255.0f );
PostMessage( GetParent(), new KeyValues( "command", "command", "BlendFactorUpdate" ) );
return;
}
if ( pPanel == m_pInputLevelSlider )
{
m_pLevelsOp->SetInputLevels(
m_pInputLevelSlider->GetValue( 0 ) / 255.0f,
m_pInputLevelSlider->GetValue( 2 ) / 255.0f,
m_pInputLevelSlider->GetValue( 1 ) / 255.0f );
return;
}
if ( pPanel == m_pOutputLevelSlider )
{
m_pLevelsOp->SetOutputLevels(
m_pOutputLevelSlider->GetValue( 0 ) / 255.0f,
m_pOutputLevelSlider->GetValue( 1 ) / 255.0f );
return;
}
}
}
void CColorLevelsUIPanel::OnCommand( const char *command )
{
BaseClass::OnCommand( command );
if( !Q_stricmp( "BlendFactorUpdate", command ) )
{
ResetBlendFactorSlider( );
}
}
void CColorLevelsUIPanel::ResetBlendFactorSlider()
{
float flBlend;
if( m_pLevelsOp )
flBlend = m_pLevelsOp->GetBlendFactor();
else
flBlend = 0.0f;
m_pBlendFactorSlider->SetValue( flBlend*255.0f );
}
//-----------------------------------------------------------------------------
// The color mask was changed
//-----------------------------------------------------------------------------
void CColorLevelsUIPanel::OnColorMaskSelected()
{
int nMask = m_pColorMask->GetActiveItem();
switch( nMask )
{
case COLOR_MASK_RGB:
m_pLevelsOp->SetChannelMask( CLevelsColorOperation::ALL_CHANNELS );
m_pHistogramPanel->SetHistogramType( CColorHistogramPanel::RGB );
break;
case COLOR_MASK_RED:
m_pLevelsOp->SetChannelMask( CLevelsColorOperation::RED_CHANNEL );
m_pHistogramPanel->SetHistogramType( CColorHistogramPanel::RED );
break;
case COLOR_MASK_GREEN:
m_pLevelsOp->SetChannelMask( CLevelsColorOperation::GREEN_CHANNEL );
m_pHistogramPanel->SetHistogramType( CColorHistogramPanel::GREEN );
break;
case COLOR_MASK_BLUE:
m_pLevelsOp->SetChannelMask( CLevelsColorOperation::BLUE_CHANNEL );
m_pHistogramPanel->SetHistogramType( CColorHistogramPanel::BLUE );
break;
}
}
//-----------------------------------------------------------------------------
// A combo box changed
//-----------------------------------------------------------------------------
void CColorLevelsUIPanel::OnTextChanged( KeyValues *data )
{
Panel *pPanel = reinterpret_cast<vgui::Panel *>( data->GetPtr("panel") );
vgui::ComboBox *pBox = dynamic_cast<vgui::ComboBox *>( pPanel );
if ( pBox == m_pColorMask )
{
OnColorMaskSelected();
return;
}
}
//-----------------------------------------------------------------------------
// HSV modification on selected parts of the image
//-----------------------------------------------------------------------------
class CSelectedHSVOperation : public IColorOperation
{
public:
CSelectedHSVOperation();
// Selection methods
enum SelectionMethod_t
{
SELECT_NONE = 0,
SELECT_ALL,
SELECT_GREATER_RED,
SELECT_LESSER_RED,
SELECT_GREATER_GREEN,
SELECT_LESSER_GREEN,
SELECT_GREATER_BLUE,
SELECT_LESSER_BLUE,
SELECT_NEARBY_RGB,
SELECT_GREATER_HUE,
SELECT_LESSER_HUE,
SELECT_NEARBY_HUE,
SELECT_GREATER_SATURATION,
SELECT_LESSER_SATURATION,
SELECT_NEARBY_SATURATION,
SELECT_GREATER_VALUE,
SELECT_LESSER_VALUE,
SELECT_NEARBY_VALUE,
SELECTION_METHOD_COUNT,
};
// Methods of IColorOperation
virtual void Apply( const Vector &inRGB, Vector &outRGB );
virtual void Release() { delete this; }
virtual const char *GetName() { return m_pName; }
virtual void SetName( const char *pName ) { V_strcpy_safe( m_pName, pName ); }
virtual ColorCorrectionTool_t ToolID() { return CC_TOOL_SELECTED_HSV; }
virtual IColorOperation *Clone();
virtual bool IsEnabled( ) { return m_bEnable; }
virtual void SetEnabled( bool bEnable ) { m_bEnable = bEnable; }
void AddSelectedColor( unsigned char r, unsigned char g, unsigned char b );
void ClearSelectedColors( );
float GetSelectionAmount( unsigned char r, unsigned char g, unsigned char b ) const;
float GetSelectionAmount( const Vector &rgb ) const;
void SetSelectionMethod( SelectionMethod_t method );
SelectionMethod_t GetSelectionMethod( );
void SetDeltaHSV( const Vector &deltaHSV );
void GetDeltaHSV( Vector &deltaHSV );
void SetColorize( bool bColorize );
bool GetColorize( );
void SetInvertSelection( bool bInvertSelection );
bool GetInvertSelection( );
void SetTolerance( float flTolerance );
void SetFuzziness( float flFuzziness );
float GetTolerance( );
float GetFuzziness( );
virtual void SetBlendFactor( float blend_factor );
virtual float GetBlendFactor( ) { return m_flBlendFactor; }
// Used to set/get the list
CColorOperationList *GetColorOpList() { return m_pOpList; }
void SetColorOpList( CColorOperationList *pList ) { m_pOpList = pList; }
private:
CColorOperationList *m_pOpList;
CUtlVector<Vector> m_SelectedRGBs;
CUtlVector<Vector> m_SelectedHSVs;
SelectionMethod_t m_SelectionMethod;
Vector m_DeltaHSV;
float m_Tolerance;
float m_Fuzziness;
bool m_bColorize;
bool m_bInvertSelection;
float m_flBlendFactor;
bool m_bEnable;
char m_pName[256];
};
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CSelectedHSVOperation::CSelectedHSVOperation()
{
m_SelectionMethod = SELECT_NEARBY_RGB;
m_DeltaHSV.Init( 0, 0, 0 );
m_Tolerance = 0.2f;
m_Fuzziness = 0.0f;
m_bColorize = false;
m_bInvertSelection = false;
m_flBlendFactor = 1.0f;
m_bEnable = true;
V_strcpy_safe( m_pName, "HSV" );
}
//-----------------------------------------------------------------------------
// Returns the image buffer
//-----------------------------------------------------------------------------
void CSelectedHSVOperation::SetSelectionMethod( SelectionMethod_t method )
{
m_SelectionMethod = method;
colorcorrectiontools->UpdateColorCorrection();
}
CSelectedHSVOperation::SelectionMethod_t CSelectedHSVOperation::GetSelectionMethod( )
{
return m_SelectionMethod;
}
void CSelectedHSVOperation::SetDeltaHSV( const Vector &deltaHSV )
{
m_DeltaHSV = deltaHSV;
}
void CSelectedHSVOperation::GetDeltaHSV( Vector &deltaHSV )
{
deltaHSV = m_DeltaHSV;
}
float FuzzyLessThan( float a, float b, float fuzziness )
{
if( fuzziness < 1.0f/255.0f )
return (a <= b) ? 1.0f : 0.0f;
float min = b - fuzziness;
float max = b + fuzziness;
if( a < min )
return 1.0f;
if( a > max )
return 0.0f;
return 1.0f - (a-min)/(max-min);
}
float FuzzyGreaterThan( float a, float b, float fuzziness )
{
if( fuzziness < 1.0f/255.0f )
return (a >= b) ? 1.0f : 0.0f;
float min = b - fuzziness;
float max = b + fuzziness;
if( a > max )
return 1.0f;
if( a < min )
return 0.0f;
return (a-min)/(max-min);
}
//-----------------------------------------------------------------------------
// Returns the image buffer
//-----------------------------------------------------------------------------
float CSelectedHSVOperation::GetSelectionAmount( const Vector &rgb ) const
{
if( m_SelectionMethod==SELECT_ALL )
return 1.0f;
else if( m_SelectionMethod==SELECT_NONE )
return 0.0f;
float flSelAmount = 0.0f;
for( int i=0;i<m_SelectedRGBs.Count();i++ )
{
Vector hsv;
float flCurSelAmount;
switch ( m_SelectionMethod )
{
default:
case SELECT_GREATER_RED:
flCurSelAmount = FuzzyGreaterThan( rgb.x, m_SelectedRGBs[i].x, m_Fuzziness );
break;
case SELECT_LESSER_RED:
flCurSelAmount = FuzzyLessThan( rgb.x, m_SelectedRGBs[i].x, m_Fuzziness );
break;
case SELECT_GREATER_GREEN:
flCurSelAmount = FuzzyGreaterThan( rgb.y, m_SelectedRGBs[i].y, m_Fuzziness );
break;
case SELECT_LESSER_GREEN:
flCurSelAmount = FuzzyLessThan( rgb.y, m_SelectedRGBs[i].y, m_Fuzziness );
break;
case SELECT_GREATER_BLUE:
flCurSelAmount = FuzzyGreaterThan( rgb.z, m_SelectedRGBs[i].z, m_Fuzziness );
break;
case SELECT_LESSER_BLUE:
flCurSelAmount = FuzzyLessThan( rgb.z, m_SelectedRGBs[i].z, m_Fuzziness );
break;
case SELECT_NEARBY_RGB:
flCurSelAmount = FuzzyLessThan( rgb.DistTo( m_SelectedRGBs[i] ), m_Tolerance, m_Fuzziness*m_Tolerance );
break;
case SELECT_GREATER_HUE:
RGBtoHSV( rgb, hsv );
flCurSelAmount = FuzzyGreaterThan( hsv.x, m_SelectedHSVs[i].x, m_Fuzziness );
break;
case SELECT_LESSER_HUE:
RGBtoHSV( rgb, hsv );
flCurSelAmount = FuzzyLessThan( hsv.x, m_SelectedHSVs[i].x, m_Fuzziness );
break;
case SELECT_NEARBY_HUE:
RGBtoHSV( rgb, hsv );
flCurSelAmount = FuzzyLessThan( fabsf( hsv.x-m_SelectedHSVs[i].x )/360.0f, m_Tolerance, m_Fuzziness*m_Tolerance );
break;
case SELECT_GREATER_SATURATION:
RGBtoHSV( rgb, hsv );
flCurSelAmount = FuzzyGreaterThan( hsv.y, m_SelectedHSVs[i].y, m_Fuzziness );
break;
case SELECT_LESSER_SATURATION:
RGBtoHSV( rgb, hsv );
flCurSelAmount = FuzzyLessThan( hsv.y, m_SelectedHSVs[i].y, m_Fuzziness );
break;
case SELECT_NEARBY_SATURATION:
RGBtoHSV( rgb, hsv );
flCurSelAmount = FuzzyLessThan( fabsf( hsv.y-m_SelectedHSVs[i].y ), m_Tolerance, m_Fuzziness*m_Tolerance );
break;
case SELECT_GREATER_VALUE:
RGBtoHSV( rgb, hsv );
flCurSelAmount = FuzzyGreaterThan( hsv.z, m_SelectedHSVs[i].z, m_Fuzziness );
break;
case SELECT_LESSER_VALUE:
RGBtoHSV( rgb, hsv );
flCurSelAmount = FuzzyLessThan( hsv.z, m_SelectedHSVs[i].z, m_Fuzziness );
break;
case SELECT_NEARBY_VALUE:
RGBtoHSV( rgb, hsv );
flCurSelAmount = FuzzyLessThan( fabsf( hsv.z-m_SelectedHSVs[i].z ), m_Tolerance, m_Fuzziness*m_Tolerance );
break;
}
if( flCurSelAmount>flSelAmount )
{
flSelAmount = flCurSelAmount;
}
}
if( m_bInvertSelection )
flSelAmount = 1.0f - flSelAmount;
return flSelAmount;
}
float CSelectedHSVOperation::GetSelectionAmount( unsigned char r, unsigned char g, unsigned char b ) const
{
Vector rgb( r, g, b );
rgb /= 255.0f;
return GetSelectionAmount( rgb );
}
void CSelectedHSVOperation::AddSelectedColor( unsigned char r, unsigned char g, unsigned char b )
{
Vector color, hsv;
color.x = r / 255.0f;
color.y = g / 255.0f;
color.z = b / 255.0f;
RGBtoHSV( color, hsv );
m_SelectedRGBs.AddToTail( color );
m_SelectedHSVs.AddToTail( hsv );
colorcorrectiontools->UpdateColorCorrection();
}
void CSelectedHSVOperation::ClearSelectedColors( )
{
m_SelectedRGBs.RemoveAll();
m_SelectedHSVs.RemoveAll();
}
void CSelectedHSVOperation::SetBlendFactor( float blend_factor )
{
m_flBlendFactor = blend_factor;
colorcorrectiontools->UpdateColorCorrection();
}
void CSelectedHSVOperation::SetColorize( bool bColorize )
{
m_bColorize = bColorize;
colorcorrectiontools->UpdateColorCorrection();
}
bool CSelectedHSVOperation::GetColorize( )
{
return m_bColorize;
}
void CSelectedHSVOperation::SetInvertSelection( bool bInvertSelection )
{
m_bInvertSelection = bInvertSelection;
colorcorrectiontools->UpdateColorCorrection();
}
bool CSelectedHSVOperation::GetInvertSelection( )
{
return m_bInvertSelection;
}
void CSelectedHSVOperation::SetTolerance( float flTolerance )
{
m_Tolerance = flTolerance;
}
float CSelectedHSVOperation::GetTolerance( )
{
return m_Tolerance;
}
void CSelectedHSVOperation::SetFuzziness( float flFuzziness )
{
m_Fuzziness = flFuzziness;
}
float CSelectedHSVOperation::GetFuzziness( )
{
return m_Fuzziness;
}
//-----------------------------------------------------------------------------
// Applies the color correction
//-----------------------------------------------------------------------------
void CSelectedHSVOperation::Apply( const Vector &inRGB, Vector &outRGB )
{
float flSelectionAmount = GetSelectionAmount( inRGB );
if ( flSelectionAmount == 0.0f || !m_bEnable )
{
outRGB = inRGB;
return;
}
Vector hsv;
RGBtoHSV( inRGB, hsv );
if( !m_bColorize )
{
hsv.x += m_DeltaHSV.x;
hsv.x = fmod( hsv.x, 360.0f );
if( hsv.x < 0.0f ) hsv.x = 360.0f + hsv.x;
hsv.y += m_DeltaHSV.y*hsv.y;
}
else
{
hsv.x = (m_DeltaHSV.x < 0.0f) ? 360.0f+m_DeltaHSV.x : m_DeltaHSV.x;
hsv.y = m_DeltaHSV.y;
}
hsv.y = clamp( hsv.y, 0.0f, 1.0f );
hsv.z += m_DeltaHSV.z;
hsv.z = clamp( hsv.z, 0.0f, 1.0f );
if ( hsv.y == 0.0F )
{
hsv.x = -1.0f;
}
HSVtoRGB( hsv, outRGB );
VectorLerp( inRGB, outRGB, flSelectionAmount * m_flBlendFactor, outRGB );
}
IColorOperation *CSelectedHSVOperation::Clone()
{
CSelectedHSVOperation *pClone = new CSelectedHSVOperation;
pClone->m_SelectedRGBs = m_SelectedRGBs;
pClone->m_SelectedHSVs = m_SelectedHSVs;
pClone->m_SelectionMethod = m_SelectionMethod;
pClone->m_DeltaHSV = m_DeltaHSV;
pClone->m_Tolerance = m_Tolerance;
pClone->m_Fuzziness = m_Fuzziness;
pClone->m_bColorize = m_bColorize;
pClone->m_bInvertSelection = m_bInvertSelection;
pClone->m_flBlendFactor = m_flBlendFactor;
pClone->m_bEnable = m_bEnable;
pClone->m_pOpList = m_pOpList;
Q_memcpy( pClone->m_pName, m_pName, sizeof(char)*256 );
return pClone;
}
//-----------------------------------------------------------------------------
// Full screen selection panel
//-----------------------------------------------------------------------------
class CFullScreenSelectionPanel : public vgui::Panel
{
DECLARE_CLASS_SIMPLE( CFullScreenSelectionPanel, vgui::Panel );
public:
CFullScreenSelectionPanel( const char *pName, CSelectedHSVOperation *pOp, vgui::Panel *pParent );
~CFullScreenSelectionPanel( );
virtual void OnMousePressed( vgui::MouseCode code );
virtual void OnMouseReleased( vgui::MouseCode code );
virtual void OnCursorMoved( int x, int y );
virtual void OnKeyCodeTyped( KeyCode code );
protected:
CSelectedHSVOperation *m_pOp;
bool m_bMouseDown;
};
CFullScreenSelectionPanel::CFullScreenSelectionPanel( const char *pName, CSelectedHSVOperation *pOp, vgui::Panel *pParent ) : BaseClass( pParent, pName )
{
m_bMouseDown = false;
SetZPos( -1000 );
m_pOp = pOp;
}
CFullScreenSelectionPanel::~CFullScreenSelectionPanel()
{
}
void CFullScreenSelectionPanel::OnMousePressed( vgui::MouseCode code )
{
int x, y;
input()->GetCursorPos( x, y );
m_bMouseDown = true;
BaseClass::OnMousePressed( code );
}
void CFullScreenSelectionPanel::OnMouseReleased( vgui::MouseCode code )
{
m_bMouseDown = false;
int x, y;
input()->GetCursorPos( x, y );
BaseClass::OnMouseReleased( code );
}
void CFullScreenSelectionPanel::OnCursorMoved( int x, int y )
{
if( m_bMouseDown )
{
CMatRenderContextPtr pRenderContext( materials );
BGRA8888_t pixelValue;
pRenderContext->ReadPixels( x, y, 1, 1, (unsigned char *)&pixelValue, IMAGE_FORMAT_BGRX8888 );
bool bCTRLDown = ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) );
if( !bCTRLDown )
m_pOp->ClearSelectedColors( );
m_pOp->AddSelectedColor( pixelValue.r, pixelValue.g, pixelValue.b );
}
BaseClass::OnCursorMoved( x, y );
}
class CSelectedHSVUIPanel;
void CFullScreenSelectionPanel::OnKeyCodeTyped( KeyCode code )
{
if( code==KEY_ESCAPE )
{
PostActionSignal( new KeyValues( "Command", "Command", "ToggleSelection" ) );
}
}
//-----------------------------------------------------------------------------
// Uncorrected image
//-----------------------------------------------------------------------------
class CUncorrectedImagePanel : public CProceduralTexturePanel
{
DECLARE_CLASS_SIMPLE( CUncorrectedImagePanel, CProceduralTexturePanel );
public:
// constructor
CUncorrectedImagePanel( vgui::Panel *pParent, const char *pName );
~CUncorrectedImagePanel();
virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect );
virtual void OnCursorMoved(int x,int y);
virtual void OnMousePressed( vgui::MouseCode code );
virtual void OnMouseReleased( MouseCode code );
// Sets the HSV color operation
void SetHSVOperation( CSelectedHSVOperation *pOp );
protected:
CSelectedHSVOperation *m_pHSVOp;
// Is the mouse down?
bool m_bMouseDown;
};
//-----------------------------------------------------------------------------
// constructor
//-----------------------------------------------------------------------------
CUncorrectedImagePanel::CUncorrectedImagePanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName )
{
m_bMouseDown = false;
SetMouseInputEnabled( true );
MaintainProportions( true );
}
CUncorrectedImagePanel::~CUncorrectedImagePanel()
{
}
//-----------------------------------------------------------------------------
// Sets the HSV color operation
//-----------------------------------------------------------------------------
void CUncorrectedImagePanel::SetHSVOperation( CSelectedHSVOperation *pOp )
{
m_pHSVOp = pOp;
}
//-----------------------------------------------------------------------------
// Fills the texture w/ the image buffer
//-----------------------------------------------------------------------------
void CUncorrectedImagePanel::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect )
{
Assert( pVTFTexture->FrameCount() == 1 );
Assert( pVTFTexture->FaceCount() == 1 );
Assert( !pTexture->IsMipmapped() );
int nWidth, nHeight, nDepth;
pVTFTexture->ComputeMipLevelDimensions( 0, &nWidth, &nHeight, &nDepth );
Assert( nDepth == 1 );
Assert( nWidth == m_nWidth && nHeight == m_nHeight );
CPixelWriter pixelWriter;
pixelWriter.SetPixelMemory( pVTFTexture->Format(),
pVTFTexture->ImageData( 0, 0, 0 ), pVTFTexture->RowSizeInBytes( 0 ) );
for (int y = 0; y < nHeight; ++y)
{
pixelWriter.Seek( 0, y );
BGRA8888_t *pTexel = &m_pImageBuffer[y * m_nWidth];
for ( int x = 0; x < nWidth; ++x, ++pTexel )
{
color24 inColor, col;
inColor.r = pTexel->r;
inColor.g = pTexel->g;
inColor.b = pTexel->b;
m_pHSVOp->GetColorOpList()->Apply( inColor, col, m_pHSVOp );
// col = inColor;
float flSelectionAmount = m_pHSVOp->GetSelectionAmount( col.r, col.g, col.b );
flSelectionAmount *= 0.5f;
// Blend between at most 50% (1,0,0) and the original texel based on selection amount
Vector rgb( col.r, col.g, col.b );
rgb *= (1 - flSelectionAmount) / 255.0f;
rgb.x += flSelectionAmount;
int r, g, b;
r = rgb.x * 255.0f;
g = rgb.y * 255.0f;
b = rgb.z * 255.0f;
r = clamp( r, 0, 255 );
g = clamp( g, 0, 255 );
b = clamp( b, 0, 255 );
pixelWriter.WritePixel( r, g, b, pTexel->a );
}
}
}
//-----------------------------------------------------------------------------
// Used to control selection
//-----------------------------------------------------------------------------
void CUncorrectedImagePanel::OnCursorMoved( int x, int y )
{
if ( !m_bMouseDown )
return;
bool bCTRLDown = ( input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL) );
// LocalToScreen( x, y );
int sx, sy;
GetSize( sx, sy );
// Renormalize (x,y) based on actual bits since the image is being stretched
x = m_TextureSubRect.x + (float)x * m_TextureSubRect.width / sx;
y = m_TextureSubRect.y + (float)y * m_TextureSubRect.height / sy;
int nSelectedX = min( x, m_nWidth );
nSelectedX = max( 0, nSelectedX );
int nSelectedY = min( y, m_nHeight );
nSelectedY = max( 0, nSelectedY );
BGRA8888_t *pTexel = &m_pImageBuffer[(nSelectedY * m_nWidth) + nSelectedX];
if( !bCTRLDown )
m_pHSVOp->ClearSelectedColors();
color24 inColor, outColor;
inColor.r = pTexel->r;
inColor.g = pTexel->g;
inColor.b = pTexel->b;
m_pHSVOp->GetColorOpList()->Apply( inColor, outColor, m_pHSVOp );
m_pHSVOp->AddSelectedColor( outColor.r, outColor.g, outColor.b );
}
void CUncorrectedImagePanel::OnMousePressed( vgui::MouseCode code )
{
BaseClass::OnMousePressed( code );
if ( code == MOUSE_LEFT )
{
m_bMouseDown = true;
int x, y;
input()->GetCursorPos( x, y );
ScreenToLocal( x, y );
OnCursorMoved( x, y );
}
}
void CUncorrectedImagePanel::OnMouseReleased( MouseCode code )
{
BaseClass::OnMouseReleased( code );
if ( code == MOUSE_LEFT )
{
m_bMouseDown = false;
}
}
//-----------------------------------------------------------------------------
// Main ui panel for dealing with HSV modification
//-----------------------------------------------------------------------------
class CSelectedHSVUIPanel : public CColorCorrectionUIChildPanel
{
DECLARE_CLASS_SIMPLE( CSelectedHSVUIPanel, CColorCorrectionUIChildPanel );
public:
CSelectedHSVUIPanel( vgui::Panel *parent, CSelectedHSVOperation *pOp );
~CSelectedHSVUIPanel();
// Command issued
virtual void OnMessage(const KeyValues *params, vgui::VPANEL fromPanel);
virtual void OnCommand( const char *command );
virtual void Init();
virtual void Shutdown();
virtual void ReadUncorrectedImage( Rect_t *pSrcRect, unsigned char *pPreviewImage );
virtual IColorOperation *GetOperation( ) { return (IColorOperation*)m_pHSVOperation; }
void EnableSelectionMode( bool bEnable );
protected:
MESSAGE_FUNC_PARAMS( OnTextChanged, "TextChanged", data );
private:
enum
{
DEFAULT_SELECTION_METHOD = CSelectedHSVOperation::SELECT_NONE
};
void PopulateControls();
void OnSelectionMethodSelected();
void ResetBlendFactorSlider();
// Reset the HSV tools
void ResetHSVSliders( );
// Update delta HSV in the color operation
void UpdateDeltaHSV( );
vgui::ComboBox *m_pSelectionMethod;
CPrecisionSlider *m_pHueSlider;
CPrecisionSlider *m_pSaturationSlider;
CPrecisionSlider *m_pValueSlider;
CUncorrectedImagePanel *m_pUncorrectedImage;
CPrecisionSlider *m_pToleranceSlider;
CPrecisionSlider *m_pFuzzinessSlider;
CPrecisionSlider *m_pBlendFactorSlider;
vgui::CheckButton *m_pColorizeButton;
vgui::CheckButton *m_pInvertSelectionButton;
vgui::Button *m_pSelectionButton;
CFullScreenSelectionPanel *m_pFullScreenSelection;
CSelectedHSVOperation *m_pHSVOperation;
bool m_bSelectionEnable;
static const char *s_pSelectionMethodNames[CSelectedHSVOperation::SELECTION_METHOD_COUNT];
};
//-----------------------------------------------------------------------------
// If you add a selection method, add it to the string list
//-----------------------------------------------------------------------------
const char *CSelectedHSVUIPanel::s_pSelectionMethodNames[CSelectedHSVOperation::SELECTION_METHOD_COUNT] =
{
"Select None",
"Select All",
"Select Greater Red Channel",
"Select Lesser Red Channel",
"Select Greater Green Channel",
"Select Lesser Green Channel",
"Select Greater Blue Channel",
"Select Lesser Blue Channel",
"Select Nearby RGB",
"Select Greater Hue",
"Select Lesser Hue",
"Select Nearby Hue",
"Select Greater Saturation",
"Select Lesser Saturation",
"Select Nearby Saturation",
"Select Greater Value",
"Select Lesser Value",
"Select Nearby Value",
};
//-----------------------------------------------------------------------------
// Purpose: Basic help dialog
//-----------------------------------------------------------------------------
CSelectedHSVUIPanel::CSelectedHSVUIPanel( vgui::Panel *parent, CSelectedHSVOperation *pOp ) : BaseClass( parent, "SelectedHSVUIPanel")
{
m_pSelectionMethod = new vgui::ComboBox(this, "SelectionMethod", 10, false);
m_pHSVOperation = pOp;
m_pHueSlider = new CPrecisionSlider( this, "HueSlider" );
m_pSaturationSlider = new CPrecisionSlider( this, "SaturationSlider" );
m_pValueSlider = new CPrecisionSlider( this, "ValueSlider" );
m_pToleranceSlider = new CPrecisionSlider( this, "ToleranceSlider" );
m_pFuzzinessSlider = new CPrecisionSlider( this, "FuzzinessSlider" );
m_pBlendFactorSlider = new CPrecisionSlider( this, "BlendFactorSlider" );
m_pColorizeButton = new vgui::CheckButton( this, "ColorizeButton", "Colorize" );
m_pInvertSelectionButton = new vgui::CheckButton( this, "InvertSelectionButton", "Invert Selection" );
m_pSelectionButton = new vgui::Button( this, "SelectionButton", "Select" );
m_pSelectionButton->AddActionSignalTarget( this );
m_pUncorrectedImage = new CUncorrectedImagePanel( this, "UncorrectedImage" );
m_pUncorrectedImage->SetHSVOperation( m_pHSVOperation );
m_pHueSlider->SetRange( -360, 360 );
m_pHueSlider->AddActionSignalTarget( this );
m_pSaturationSlider->SetRange( -255, 255 );
m_pSaturationSlider->AddActionSignalTarget( this );
m_pValueSlider->SetRange( -255, 255 );
m_pValueSlider->AddActionSignalTarget( this );
m_pToleranceSlider->SetRange( 0, 255 );
m_pToleranceSlider->AddActionSignalTarget( this );
m_pFuzzinessSlider->SetRange( 0, 255 );
m_pFuzzinessSlider->AddActionSignalTarget( this );
m_pBlendFactorSlider->SetRange( 0, 255 );
m_pBlendFactorSlider->AddActionSignalTarget( this );
LoadControlSettings("Resource\\SelectedHSVUIPanel.res");
PopulateControls();
m_pColorizeButton->SetSelected( m_pHSVOperation->GetColorize() );
m_pColorizeButton->AddActionSignalTarget( this );
m_pInvertSelectionButton->SetSelected( m_pHSVOperation->GetInvertSelection() );
m_pInvertSelectionButton->AddActionSignalTarget( this );
ResetHSVSliders();
ResetBlendFactorSlider();
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
int x, y, w, h;
pRenderContext->GetViewport( x, y, w, h );
m_pFullScreenSelection = new CFullScreenSelectionPanel( "SelectionPanel", pOp, this );
m_pFullScreenSelection->SetSize( w, h );
m_pFullScreenSelection->SetPos( x, y );
m_pFullScreenSelection->SetEnabled( false );
m_pFullScreenSelection->SetVisible( false );
m_pFullScreenSelection->SetMouseInputEnabled( false );
m_pFullScreenSelection->MakePopup( true );
m_pFullScreenSelection->AddActionSignalTarget( this );
m_bSelectionEnable = false;
}
CSelectedHSVUIPanel::~CSelectedHSVUIPanel()
{
}
//-----------------------------------------------------------------------------
// Init, shutdown
//-----------------------------------------------------------------------------
void CSelectedHSVUIPanel::Init()
{
m_pUncorrectedImage->Init( IMAGE_BUFFER_MAX_DIM, IMAGE_BUFFER_MAX_DIM, true );
}
void CSelectedHSVUIPanel::Shutdown()
{
m_pUncorrectedImage->Shutdown();
}
void CSelectedHSVUIPanel::PopulateControls()
{
m_pSelectionMethod->DeleteAllItems();
int i;
for ( i = 0; i < CSelectedHSVOperation::SELECTION_METHOD_COUNT; i++ )
{
m_pSelectionMethod->AddItem( s_pSelectionMethodNames[i], NULL );
}
m_pSelectionMethod->AddActionSignalTarget( this );
m_pSelectionMethod->ActivateItem( m_pHSVOperation->GetSelectionMethod() );
}
//-----------------------------------------------------------------------------
// Update delta HSV in the color operation
//-----------------------------------------------------------------------------
void CSelectedHSVUIPanel::UpdateDeltaHSV( )
{
Vector deltaHSV;
deltaHSV.x = m_pHueSlider->GetValue();
deltaHSV.y = m_pSaturationSlider->GetValue() / 255.0f;
deltaHSV.z = m_pValueSlider->GetValue() / 255.0f;
m_pHSVOperation->SetDeltaHSV( deltaHSV );
m_pHSVOperation->SetTolerance( (float)m_pToleranceSlider->GetValue()/255.0f );
m_pHSVOperation->SetFuzziness( (float)m_pFuzzinessSlider->GetValue()/255.0f );
colorcorrectiontools->UpdateColorCorrection();
}
//-----------------------------------------------------------------------------
// Reset the HSV tools
//-----------------------------------------------------------------------------
void CSelectedHSVUIPanel::ResetHSVSliders( )
{
Vector deltaHSV;
m_pHSVOperation->GetDeltaHSV( deltaHSV );
m_pHueSlider->SetValue( deltaHSV.x );
m_pSaturationSlider->SetValue( deltaHSV.y*255.0f );
m_pValueSlider->SetValue( deltaHSV.z*255.0f );
m_pToleranceSlider->SetValue( m_pHSVOperation->GetTolerance()*255.0f );
m_pFuzzinessSlider->SetValue( m_pHSVOperation->GetFuzziness()*255.0f );
}
void CSelectedHSVUIPanel::ResetBlendFactorSlider()
{
float flBlend;
if( m_pHSVOperation )
flBlend = m_pHSVOperation->GetBlendFactor();
else
flBlend = 0.0f;
m_pBlendFactorSlider->SetValue( flBlend*255.0f );
}
//-----------------------------------------------------------------------------
// A new selection method was selected
//-----------------------------------------------------------------------------
void CSelectedHSVUIPanel::OnTextChanged( KeyValues *data )
{
Panel *pPanel = reinterpret_cast<vgui::Panel *>( data->GetPtr("panel") );
vgui::ComboBox *pBox = dynamic_cast<vgui::ComboBox *>( pPanel );
if( pBox == m_pSelectionMethod ) // don't change the text in the config setting combo
{
OnSelectionMethodSelected();
}
}
void CSelectedHSVUIPanel::OnSelectionMethodSelected()
{
ResetHSVSliders();
CSelectedHSVOperation::SelectionMethod_t method = (CSelectedHSVOperation::SelectionMethod_t)m_pSelectionMethod->GetActiveItem();
m_pHSVOperation->SetSelectionMethod( method );
if( method == CSelectedHSVOperation::SELECT_NEARBY_RGB ||
method == CSelectedHSVOperation::SELECT_NEARBY_HUE ||
method == CSelectedHSVOperation::SELECT_NEARBY_SATURATION ||
method == CSelectedHSVOperation::SELECT_NEARBY_VALUE )
{
m_pToleranceSlider->SetEnabled( true );
m_pFuzzinessSlider->SetEnabled( true );
}
else
{
m_pToleranceSlider->SetEnabled( false );
m_pFuzzinessSlider->SetEnabled( true );
}
}
//-----------------------------------------------------------------------------
// Returns the image buffer
//-----------------------------------------------------------------------------
void CSelectedHSVUIPanel::ReadUncorrectedImage( Rect_t *pSrcRect, unsigned char *pPreviewImage )
{
Rect_t dstRect;
dstRect.x = 0;
dstRect.y = 0;
dstRect.width = g_nPreviewImageWidth;
dstRect.height = g_nPreviewImageHeight;
for( int i=0;i<g_nPreviewImageHeight;i++ )
{
Q_memcpy( m_pUncorrectedImage->GetImageBuffer()+i*IMAGE_BUFFER_MAX_DIM, pPreviewImage + i*g_nPreviewImageWidth*4, g_nPreviewImageWidth*4 );
}
m_pUncorrectedImage->SetTextureSubRect( dstRect );
m_pUncorrectedImage->DownloadTexture();
}
//-----------------------------------------------------------------------------
// A new performance tool was selected
//-----------------------------------------------------------------------------
void CSelectedHSVUIPanel::OnMessage(const KeyValues *params, VPANEL fromPanel)
{
BaseClass::OnMessage( params, fromPanel );
if ( !Q_stricmp( "SliderMoved", params->GetName() ) )
{
vgui::Panel *pPanel = reinterpret_cast<vgui::Panel *>( const_cast<KeyValues*>(params)->GetPtr("panel") );
CPrecisionSlider *pSlider = dynamic_cast<CPrecisionSlider *>( pPanel );
if ( pSlider != m_pBlendFactorSlider )
{
UpdateDeltaHSV();
}
else
{
m_pHSVOperation->SetBlendFactor( (float)pSlider->GetValue()/255.0f );
PostMessage( GetParent(), new KeyValues( "command", "command", "BlendFactorUpdate" ) );
}
}
else if ( !Q_stricmp( "CheckButtonChecked", params->GetName() ) )
{
vgui::Panel *pPanel = reinterpret_cast<vgui::Panel *>( const_cast<KeyValues*>(params)->GetPtr("panel") );
vgui::CheckButton *pButton = dynamic_cast<vgui::CheckButton *>( pPanel );
if( pButton == m_pColorizeButton )
{
m_pHSVOperation->SetColorize( pButton->IsSelected() );
}
else if( pButton == m_pInvertSelectionButton )
{
m_pHSVOperation->SetInvertSelection( pButton->IsSelected() );
}
}
}
void CSelectedHSVUIPanel::OnCommand( const char *command )
{
BaseClass::OnCommand( command );
if( !Q_stricmp( "BlendFactorUpdate", command ) )
{
ResetBlendFactorSlider( );
}
else if( !Q_stricmp( "ToggleSelection", command ) )
{
EnableSelectionMode( !m_bSelectionEnable );
}
}
void CSelectedHSVUIPanel::EnableSelectionMode( bool bEnable )
{
if( bEnable )
colorcorrectiontools->SetFinalOperation( m_pHSVOperation );
else
colorcorrectiontools->SetFinalOperation( NULL );
m_bSelectionEnable = bEnable;
m_pSelectionButton->ForceDepressed( bEnable );
m_pFullScreenSelection->SetEnabled( bEnable );
m_pFullScreenSelection->SetVisible( bEnable );
m_pFullScreenSelection->SetMouseInputEnabled( bEnable );
}
//-----------------------------------------------------------------------------
// Lookup table based IColorOperation
//-----------------------------------------------------------------------------
class CColorLookupOperation : public IColorOperation
{
public:
CColorLookupOperation();
~CColorLookupOperation();
// Methods of IColorOperation
virtual void Apply( const Vector &inRGB, Vector &outRGB );
virtual void Release() { delete this; }
virtual const char *GetName() { return m_pName; }
virtual void SetName( const char *pName ) { V_strcpy_safe( m_pName, pName ); }
virtual ColorCorrectionTool_t ToolID() { return CC_TOOL_LOOKUP; }
virtual IColorOperation *Clone();
virtual bool IsEnabled( ) { return m_bEnable; }
virtual void SetEnabled( bool bEnable ) { m_bEnable = bEnable; }
// Load a lookup table from file pFilename
void LoadLookupTable( const char *pFilename );
// Get the floating point color values at a lookup cell
void GetLookupValue( int r, int g, int b, Vector &out );
// Controls how much this op should take effect (1 = use 100% converted color, 0 = use 100% input color)
virtual void SetBlendFactor( float flBlend );
virtual float GetBlendFactor( ) { return m_flBlendFactor; }
bool IsFileLoaded( ) { return m_LookupTable != 0; }
const char *GetFilename( ) { return m_pFilename; }
private:
// Set the resolution of the lookup table, deletes any active data
void SetResolution( const int res );
// Deletes any active lookup data
void DeleteLookupTableData( );
char m_pFilename[ MAX_PATH ];
int m_Resolution;
color24 *m_LookupTable;
float m_flBlendFactor;
bool m_bEnable;
char m_pName[256];
};
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CColorLookupOperation::CColorLookupOperation( )
{
m_Resolution = 0;
m_LookupTable = 0;
m_flBlendFactor = 1.0f;
V_strcpy_safe( m_pName, "Lookup" );
V_strcpy_safe( m_pFilename, "" );
}
//-----------------------------------------------------------------------------
// Destructor
//-----------------------------------------------------------------------------
CColorLookupOperation::~CColorLookupOperation( )
{
DeleteLookupTableData( );
}
//-----------------------------------------------------------------------------
// Applies the color correction
//-----------------------------------------------------------------------------
void CColorLookupOperation::Apply( const Vector &inRGB, Vector &outRGB )
{
if( !m_LookupTable || !m_bEnable )
{
outRGB = inRGB;
return;
}
float fr = inRGB.x * (m_Resolution-1);
float fg = inRGB.y * (m_Resolution-1);
float fb = inRGB.z * (m_Resolution-1);
int ir = (int)fr;
fr = fr - (float)ir;
int ig = (int)fg;
fg = fg - (float)ig;
int ib = (int)fb;
fb = fb - (float)ib;
Vector interp_cube[ 8 ];
GetLookupValue( ir , ig , ib , interp_cube[ 0 ] );
GetLookupValue( ir+1, ig , ib , interp_cube[ 1 ] );
GetLookupValue( ir , ig+1, ib , interp_cube[ 2 ] );
GetLookupValue( ir+1, ig+1, ib , interp_cube[ 3 ] );
GetLookupValue( ir , ig , ib+1, interp_cube[ 4 ] );
GetLookupValue( ir+1, ig , ib+1, interp_cube[ 5 ] );
GetLookupValue( ir , ig+1, ib+1, interp_cube[ 6 ] );
GetLookupValue( ir+1, ig+1, ib+1, interp_cube[ 7 ] );
Vector a = interp_cube[0] * (1.0f-fr) + interp_cube[1] * fr;
Vector b = interp_cube[2] * (1.0f-fr) + interp_cube[3] * fr;
Vector c = interp_cube[4] * (1.0f-fr) + interp_cube[5] * fr;
Vector d = interp_cube[6] * (1.0f-fr) + interp_cube[7] * fr;
Vector bottom = a * (1.0f-fg) + b * fg;
Vector top = c * (1.0f-fg) + d * fg;
outRGB = (bottom * (1.0f-fb) + top * fb) * m_flBlendFactor + inRGB - inRGB * m_flBlendFactor;
}
//-----------------------------------------------------------------------------
// Finds the floating point lookup value at the specified cell
//-----------------------------------------------------------------------------
void CColorLookupOperation::GetLookupValue( int r, int g, int b, Vector &out )
{
if( !m_LookupTable )
return;
if( r<0 ) r = 0;
if( g<0 ) g = 0;
if( b<0 ) b = 0;
if( r>m_Resolution-1 ) r = m_Resolution-1;
if( g>m_Resolution-1 ) g = m_Resolution-1;
if( b>m_Resolution-1 ) b = m_Resolution-1;
color24 out_color = m_LookupTable[ r + g*m_Resolution + b*m_Resolution*m_Resolution ];
out.x = out_color.r / 255.0f;
out.y = out_color.g / 255.0f;
out.z = out_color.b / 255.0f;
}
//-----------------------------------------------------------------------------
// Controls how much this op should take effect (1 = use 100% converted color, 0 = use 100% input color)
//-----------------------------------------------------------------------------
void CColorLookupOperation::SetBlendFactor( float flBlend )
{
m_flBlendFactor = flBlend;
colorcorrectiontools->UpdateColorCorrection();
}
//-----------------------------------------------------------------------------
// Loads a lookup table from file pFilename
//-----------------------------------------------------------------------------
void CColorLookupOperation::LoadLookupTable( const char *pFilename )
{
FileHandle_t file_handle = g_pFileSystem->Open( pFilename, "rb" );
if( !file_handle )
return;
unsigned int file_size = g_pFileSystem->Size( file_handle );
int res = (int)powf( (float)(file_size/sizeof(color24)), 1.0f/3.0f );
if( res*res*res*sizeof(color24) != file_size )
{
g_pFileSystem->Close( file_handle );
return;
}
SetResolution( res );
for( int i=0;i<res*res*res;i++ )
{
color24 color;
g_pFileSystem->Read( &color, sizeof(color24), file_handle );
m_LookupTable[i] = color;
}
g_pFileSystem->Close( file_handle );
V_strcpy_safe( m_pFilename, pFilename );
colorcorrectiontools->UpdateColorCorrection();
}
//-----------------------------------------------------------------------------
// Sets the resolution of the lookup table, deletes any active data
//-----------------------------------------------------------------------------
void CColorLookupOperation::SetResolution( const int res )
{
DeleteLookupTableData( );
m_LookupTable = new color24[ res*res*res ];
m_Resolution = res;
}
//-----------------------------------------------------------------------------
// Deletes the lookup table data
//-----------------------------------------------------------------------------
void CColorLookupOperation::DeleteLookupTableData( )
{
if( m_LookupTable )
{
m_Resolution = 0;
delete m_LookupTable;
}
}
IColorOperation *CColorLookupOperation::Clone()
{
CColorLookupOperation *pClone = new CColorLookupOperation;
Q_memcpy( pClone->m_pFilename, m_pFilename, sizeof(char)*MAX_PATH );
pClone->m_Resolution = m_Resolution;
pClone->m_flBlendFactor = m_flBlendFactor;
pClone->m_bEnable = m_bEnable;
Q_memcpy( pClone->m_pName, m_pName, sizeof(char)*256 );
pClone->m_LookupTable = new color24[m_Resolution*m_Resolution*m_Resolution];
Q_memcpy( pClone->m_LookupTable, m_LookupTable, sizeof(color24)*m_Resolution*m_Resolution*m_Resolution );
return pClone;
}
//-----------------------------------------------------------------------------
// Root panel for loading lookup tables
//-----------------------------------------------------------------------------
class CColorLookupUIPanel : public CColorCorrectionUIChildPanel
{
DECLARE_CLASS_SIMPLE( CColorLookupUIPanel, CColorCorrectionUIChildPanel );
public:
// constructor
CColorLookupUIPanel( vgui::Panel *pParent, CColorLookupOperation *pOp );
~CColorLookupUIPanel();
virtual void Init() {}
virtual void Shutdown() {}
virtual void ReadUncorrectedImage( Rect_t *pSrcRect, unsigned char *pPreviewImage ) {}
virtual IColorOperation *GetOperation( ) { return (IColorOperation*)m_pLookupOp; }
// Command issued
virtual void OnMessage(const KeyValues *params, vgui::VPANEL fromPanel);
virtual void OnCommand(const char *command);
private:
MESSAGE_FUNC_CHARPTR( OnFileSelected, "FileSelected", fullpath );
void ResetBlendFactorSlider();
void SetButtonText( );
vgui::Button *m_pLoadButton;
CPrecisionSlider *m_pBlendFactorSlider;
CColorLookupOperation *m_pLookupOp;
};
//-----------------------------------------------------------------------------
// constructor
//-----------------------------------------------------------------------------
CColorLookupUIPanel::CColorLookupUIPanel( vgui::Panel *pParent, CColorLookupOperation *pOp ) : BaseClass( pParent, "LookupUIPanel" )
{
m_pLookupOp = pOp;
m_pLoadButton = new vgui::Button( this, "Load Lookup", "", this, "LoadLookup" );
m_pBlendFactorSlider = new CPrecisionSlider( this, "BlendFactorSlider" );
m_pBlendFactorSlider->SetRange( 0, 255 );
m_pBlendFactorSlider->SetValue( 255 );
m_pBlendFactorSlider->AddActionSignalTarget( this );
LoadControlSettings("Resource\\ColorLookupUIPanel.res");
SetButtonText( );
}
CColorLookupUIPanel::~CColorLookupUIPanel()
{
}
//-----------------------------------------------------------------------------
// Command issued
//-----------------------------------------------------------------------------
void CColorLookupUIPanel::OnMessage(const KeyValues *params, vgui::VPANEL fromPanel)
{
BaseClass::OnMessage( params, fromPanel );
if ( !Q_stricmp( "SliderMoved", params->GetName() ) )
{
vgui::Panel *pPanel = reinterpret_cast<vgui::Panel *>( const_cast<KeyValues*>(params)->GetPtr("panel") );
if ( pPanel == m_pBlendFactorSlider )
{
m_pLookupOp->SetBlendFactor( m_pBlendFactorSlider->GetValue() / 255.0f );
PostMessage( GetParent(), new KeyValues( "command", "command", "BlendFactorUpdate" ) );
return;
}
}
}
void CColorLookupUIPanel::OnCommand( const char *command )
{
if ( !Q_strcasecmp( command, "LoadLookup" ) )
{
FileOpenDialog *open_dialog = new FileOpenDialog( this, "File Open", true );
open_dialog->AddActionSignalTarget( this );
open_dialog->AddFilter( "*.raw", ".RAW files", true );
open_dialog->DoModal( true );
}
else if( !Q_stricmp( "BlendFactorUpdate", command ) )
{
ResetBlendFactorSlider( );
}
BaseClass::OnCommand( command );
}
void CColorLookupUIPanel::ResetBlendFactorSlider()
{
float flBlend;
if( m_pLookupOp )
flBlend = m_pLookupOp->GetBlendFactor();
else
flBlend = 0.0f;
m_pBlendFactorSlider->SetValue( flBlend*255.0f );
}
void CColorLookupUIPanel::OnFileSelected( const char *filename )
{
m_pLookupOp->LoadLookupTable( filename );
SetButtonText( );
}
void CColorLookupUIPanel::SetButtonText( )
{
if( !m_pLookupOp->IsFileLoaded() )
{
m_pLoadButton->SetText( "No File Loaded" );
}
else
{
m_pLoadButton->SetText( m_pLookupOp->GetFilename() );
}
}
//-----------------------------------------------------------------------------
// Lookup table based IColorOperation
//-----------------------------------------------------------------------------
enum ColorBalanceMode_t
{
CC_BALANCE_MODE_SHADOWS = 0,
CC_BALANCE_MODE_MIDTONES,
CC_BALANCE_MODE_HIGHLIGHTS,
CC_BALANCE_MODE_COUNT,
};
class CColorBalanceOperation : public IColorOperation
{
public:
CColorBalanceOperation();
~CColorBalanceOperation();
// Methods of IColorOperation
virtual void Apply( const Vector &inRGB, Vector &outRGB );
virtual void Release() { delete this; }
virtual const char *GetName() { return m_pName; }
virtual void SetName( const char *pName ) { V_strcpy_safe( m_pName, pName ); }
virtual ColorCorrectionTool_t ToolID() { return CC_TOOL_BALANCE; }
virtual IColorOperation *Clone();
virtual bool IsEnabled( ) { return m_bEnable; }
virtual void SetEnabled( bool bEnable ) { m_bEnable = bEnable; }
// Controls how much this op should take effect (1 = use 100% converted color, 0 = use 100% input color)
virtual void SetBlendFactor( float flBlend );
virtual float GetBlendFactor( ) { return m_flBlendFactor; }
void SetPreserveLuminosity( bool bPreserveLum ) { m_PreserveLuminosity = bPreserveLum; Update(); }
void SetCyanRedBalance ( ColorBalanceMode_t mode, float value ) { m_CyanRedBalance[ (int)mode ] = value; Update(); }
void SetMagentaGreenBalance( ColorBalanceMode_t mode, float value ) { m_MagentaGreenBalance[ (int)mode ] = value; Update(); }
void SetYellowBlueBalance ( ColorBalanceMode_t mode, float value ) { m_YellowBlueBalance[ (int)mode ] = value; Update(); }
float GetCyanRedBalance ( ColorBalanceMode_t mode ) { return m_CyanRedBalance[ (int)mode ]; }
float GetMagentaGreenBalance( ColorBalanceMode_t mode ) { return m_MagentaGreenBalance[ (int)mode ]; }
float GetYellowBlueBalance ( ColorBalanceMode_t mode ) { return m_YellowBlueBalance[ (int)mode ]; }
private:
void Update( );
void CreateLookupTables( );
bool m_PreserveLuminosity;
float m_CyanRedBalance[ CC_BALANCE_MODE_COUNT ];
float m_MagentaGreenBalance[ CC_BALANCE_MODE_COUNT ];
float m_YellowBlueBalance[ CC_BALANCE_MODE_COUNT ];
float m_ShadowsSubTransfer[ 256 ];
float m_MidtonesSubTransfer[ 256 ];
float m_HighlightsSubTransfer[ 256 ];
float m_ShadowsAddTransfer[ 256 ];
float m_MidtonesAddTransfer[ 256 ];
float m_HighlightsAddTransfer[ 256 ];
byte m_pRedLookup[ 256 ];
byte m_pBlueLookup[ 256 ];
byte m_pGreenLookup[ 256 ];
float m_flBlendFactor;
bool m_bEnable;
char m_pName[256];
};
//-----------------------------------------------------------------------------
// Constructor
//-----------------------------------------------------------------------------
CColorBalanceOperation::CColorBalanceOperation( )
{
m_PreserveLuminosity = true;
for( int i=0;i<CC_BALANCE_MODE_COUNT;i++ )
{
m_CyanRedBalance[i] = (float)0.0;
m_MagentaGreenBalance[i] = (float)0.0;
m_YellowBlueBalance[i] = (float)0.0;
}
for( int i=0;i<256;i++ )
{
m_HighlightsAddTransfer[i] = m_ShadowsSubTransfer[i] = ( 1.075f - 1.0f / ((float)i/16.0f + 1.0f) );
// m_HighlightsSubTransfer[i] = m_ShadowsAddTransfer[i] = ( 1.075f - 1.0f / ((float)(255-i)/16.0f + 1.0f) );
float fi = ((float)i - 127.0f) / 127.0f;
m_MidtonesAddTransfer[i] = m_MidtonesSubTransfer[i] = 0.667f * (1.0f - fi*fi);
m_ShadowsAddTransfer[i] = m_HighlightsSubTransfer[i] = 0.667f * (1.0f - fi*fi);
}
m_bEnable = true;
m_flBlendFactor = 1.0f;
CreateLookupTables( );
V_strcpy_safe( m_pName, "Balance" );
}
//-----------------------------------------------------------------------------
// Destructor
//-----------------------------------------------------------------------------
CColorBalanceOperation::~CColorBalanceOperation( )
{
}
//-----------------------------------------------------------------------------
// Returns the luminance of an rgb color
//-----------------------------------------------------------------------------
int RGBToL( int r, int g, int b )
{
int imin, imax;
if( r>g )
{
imax = max( r, b );
imin = min( g, b );
}
else
{
imax = max( g, b );
imin = min( r, b );
}
return (int)((float)(imax+imin)/2.0f);
}
//-----------------------------------------------------------------------------
// HSL conversion utility function
//-----------------------------------------------------------------------------
int HSLValue( float n1, float n2, float hue )
{
float value;
if (hue > 255)
hue -= 255;
else if (hue < 0)
hue += 255;
if (hue < 42.5)
value = n1 + (n2 - n1) * (hue / 42.5);
else if (hue < 127.5)
value = n2;
else if (hue < 170)
value = n1 + (n2 - n1) * ((170 - hue) / 42.5);
else
value = n1;
return (int)(value * 255.0f);
}
//-----------------------------------------------------------------------------
// Converts from HSL space to RGB space with integer inputs/outputs
//-----------------------------------------------------------------------------
void HSLToRGB( int *hue, int *saturation, int *lightness )
{
float h, s, l;
h = *hue;
s = *saturation;
l = *lightness;
if (s == 0)
{
/* achromatic case */
*hue = l;
*lightness = l;
*saturation = l;
}
else
{
float m1, m2;
if (l < 128)
m2 = (l * (255 + s)) / 65025.0;
else
m2 = (l + s - (l * s) / 255.0) / 255.0;
m1 = (l / 127.5) - m2;
/* chromatic case */
*hue = HSLValue(m1, m2, h + 85);
*saturation = HSLValue(m1, m2, h);
*lightness = HSLValue(m1, m2, h - 85);
}
}
//-----------------------------------------------------------------------------
// Converts from RGB space to HSL space with integer inputs/outputs
//-----------------------------------------------------------------------------
void RGBToHSL( int *red, int *green, int *blue )
{
int r, g, b;
float h, s, l;
int imin, imax;
int delta;
r = *red;
g = *green;
b = *blue;
if (r > g)
{
imax = max (r, b);
imin = min (g, b);
}
else
{
imax = max (g, b);
imin = min (r, b);
}
l = (imax + imin) / 2.0;
if (imax == imin)
{
s = 0.0;
h = 0.0;
}
else
{
delta = (imax - imin);
if (l < 128)
s = 255 * (float) delta / (float) (imax + imin);
else
s = 255 * (float) delta / (float) (511 - imax - imin);
if (r == imax)
h = (g - b) / (float) delta;
else if (g == imax)
h = 2 + (b - r) / (float) delta;
else
h = 4 + (r - g) / (float) delta;
h = h * 42.5;
if (h < 0)
h += 255;
else if (h > 255)
h -= 255;
}
*red = (int)h;
*green = (int)s;
*blue = (int)l;
}
//-----------------------------------------------------------------------------
// Applies the color correction
//-----------------------------------------------------------------------------
void CColorBalanceOperation::Apply( const Vector &inRGB, Vector &outRGB )
{
if( !m_bEnable )
{
outRGB = inRGB;
return;
}
int redIn = (int)(inRGB.x * 255.0f);
int greenIn = (int)(inRGB.y * 255.0f);
int blueIn = (int)(inRGB.z * 255.0f);
int redOut = m_pRedLookup[ redIn ];
int greenOut = m_pGreenLookup[ greenIn ];
int blueOut = m_pBlueLookup[ blueIn ];
if( m_PreserveLuminosity )
{
RGBToHSL( &redOut, &greenOut, &blueOut );
blueOut = RGBToL( redIn, greenIn, blueIn );
HSLToRGB( &redOut, &greenOut, &blueOut );
}
outRGB.x = (float)redOut / 255.0f;
outRGB.y = (float)greenOut / 255.0f;
outRGB.z = (float)blueOut / 255.0f;
outRGB = outRGB * m_flBlendFactor + inRGB * (1.0f-m_flBlendFactor);
}
//-----------------------------------------------------------------------------
// Controls how much this op should take effect (1 = use 100% converted color, 0 = use 100% input color)
//-----------------------------------------------------------------------------
void CColorBalanceOperation::SetBlendFactor( float flBlend )
{
m_flBlendFactor = flBlend;
colorcorrectiontools->UpdateColorCorrection();
}
//-----------------------------------------------------------------------------
// Update the operator to reflect a change in parameters
//-----------------------------------------------------------------------------
void CColorBalanceOperation::Update( )
{
CreateLookupTables( );
colorcorrectiontools->UpdateColorCorrection();
}
//-----------------------------------------------------------------------------
// Create lookup tables used to accelerate balance operation
//-----------------------------------------------------------------------------
void CColorBalanceOperation::CreateLookupTables( )
{
float *cyan_red_transfer[3];
float *magenta_green_transfer[3];
float *yellow_blue_transfer[3];
cyan_red_transfer[ CC_BALANCE_MODE_SHADOWS ] = (m_CyanRedBalance[ CC_BALANCE_MODE_SHADOWS ] > 0.0f) ? m_ShadowsAddTransfer : m_ShadowsSubTransfer;
cyan_red_transfer[ CC_BALANCE_MODE_MIDTONES ] = (m_CyanRedBalance[ CC_BALANCE_MODE_MIDTONES ] > 0.0f) ? m_MidtonesAddTransfer : m_MidtonesSubTransfer;
cyan_red_transfer[ CC_BALANCE_MODE_HIGHLIGHTS ] = (m_CyanRedBalance[ CC_BALANCE_MODE_HIGHLIGHTS ] > 0.0f) ? m_HighlightsAddTransfer : m_HighlightsSubTransfer;
magenta_green_transfer[ CC_BALANCE_MODE_SHADOWS ] = (m_MagentaGreenBalance[ CC_BALANCE_MODE_SHADOWS ] > 0.0f) ? m_ShadowsAddTransfer : m_ShadowsSubTransfer;
magenta_green_transfer[ CC_BALANCE_MODE_MIDTONES ] = (m_MagentaGreenBalance[ CC_BALANCE_MODE_MIDTONES ] > 0.0f) ? m_MidtonesAddTransfer : m_MidtonesSubTransfer;
magenta_green_transfer[ CC_BALANCE_MODE_HIGHLIGHTS ] = (m_MagentaGreenBalance[ CC_BALANCE_MODE_HIGHLIGHTS ] > 0.0f) ? m_HighlightsAddTransfer : m_HighlightsSubTransfer;
yellow_blue_transfer[ CC_BALANCE_MODE_SHADOWS ] = (m_YellowBlueBalance[ CC_BALANCE_MODE_SHADOWS ] > 0.0f) ? m_ShadowsAddTransfer : m_ShadowsSubTransfer;
yellow_blue_transfer[ CC_BALANCE_MODE_MIDTONES ] = (m_YellowBlueBalance[ CC_BALANCE_MODE_MIDTONES ] > 0.0f) ? m_MidtonesAddTransfer : m_MidtonesSubTransfer;
yellow_blue_transfer[ CC_BALANCE_MODE_HIGHLIGHTS ] = (m_YellowBlueBalance[ CC_BALANCE_MODE_HIGHLIGHTS ] > 0.0f) ? m_HighlightsAddTransfer : m_HighlightsSubTransfer;
for( int i=0;i<256;i++ )
{
int redOut = i;
int greenOut = i;
int blueOut = i;
for( int mode=CC_BALANCE_MODE_SHADOWS;mode<=CC_BALANCE_MODE_HIGHLIGHTS;mode++ )
{
redOut += m_CyanRedBalance[ mode ] * cyan_red_transfer[ mode ][ redOut ];
greenOut += m_MagentaGreenBalance[ mode ] * magenta_green_transfer[ mode ][ greenOut ];
blueOut += m_YellowBlueBalance[ mode ] * yellow_blue_transfer[ mode ][ blueOut ];
redOut = clamp( redOut, 0, 255 );
greenOut = clamp( greenOut, 0, 255 );
blueOut = clamp( blueOut, 0, 255 );
}
m_pRedLookup[i] = redOut;
m_pGreenLookup[i] = greenOut;
m_pBlueLookup[i] = blueOut;
}
}
IColorOperation *CColorBalanceOperation::Clone()
{
CColorBalanceOperation *pClone = new CColorBalanceOperation;
pClone->m_PreserveLuminosity = m_PreserveLuminosity;
pClone->m_flBlendFactor = m_flBlendFactor;
pClone->m_bEnable = m_bEnable;
Q_memcpy( pClone->m_CyanRedBalance, m_CyanRedBalance, sizeof(float)*CC_BALANCE_MODE_COUNT );
Q_memcpy( pClone->m_MagentaGreenBalance, m_MagentaGreenBalance, sizeof(float)*CC_BALANCE_MODE_COUNT );
Q_memcpy( pClone->m_YellowBlueBalance, m_YellowBlueBalance, sizeof(float)*CC_BALANCE_MODE_COUNT );
Q_memcpy( pClone->m_ShadowsSubTransfer, m_ShadowsSubTransfer, sizeof(float)*256 );
Q_memcpy( pClone->m_MidtonesSubTransfer, m_ShadowsSubTransfer, sizeof(float)*256 );
Q_memcpy( pClone->m_HighlightsSubTransfer, m_ShadowsSubTransfer, sizeof(float)*256 );
Q_memcpy( pClone->m_ShadowsAddTransfer, m_ShadowsAddTransfer, sizeof(float)*256 );
Q_memcpy( pClone->m_MidtonesAddTransfer, m_MidtonesAddTransfer, sizeof(float)*256 );
Q_memcpy( pClone->m_HighlightsAddTransfer, m_HighlightsAddTransfer, sizeof(float)*256 );
Q_memcpy( pClone->m_pRedLookup, m_pRedLookup, sizeof(char)*256 );
Q_memcpy( pClone->m_pGreenLookup, m_pGreenLookup, sizeof(char)*256 );
Q_memcpy( pClone->m_pBlueLookup, m_pBlueLookup, sizeof(char)*256 );
Q_memcpy( pClone->m_pName, m_pName, sizeof(char)*256 );
return pClone;
}
//-----------------------------------------------------------------------------
// Root panel for color balance operations
//-----------------------------------------------------------------------------
class CColorBalanceUIPanel : public CColorCorrectionUIChildPanel
{
DECLARE_CLASS_SIMPLE( CColorBalanceUIPanel, CColorCorrectionUIChildPanel );
public:
// constructor
CColorBalanceUIPanel( vgui::Panel *pParent, CColorBalanceOperation *pOp );
~CColorBalanceUIPanel();
virtual void Init() {}
virtual void Shutdown() {}
virtual void ReadUncorrectedImage( Rect_t *pSrcRect, unsigned char *pPreviewImage ) {}
virtual IColorOperation *GetOperation( ) { return (IColorOperation*)m_pBalanceOp; }
// Command issued
virtual void OnMessage(const KeyValues *params, vgui::VPANEL fromPanel);
virtual void OnCommand( const char *command );
ColorBalanceMode_t GetCurrentMode();
private:
MESSAGE_FUNC( OnRadioButtonHit, "RadioButtonChecked" );
void ResetSliders();
void ResetBlendFactorSlider();
vgui::CheckButton *m_pPreserveLuminosityButton;
vgui::RadioButton *m_pShadowModeButton;
vgui::RadioButton *m_pMidtoneModeButton;
vgui::RadioButton *m_pHighlightModeButton;
CPrecisionSlider *m_pCyanRedSlider;
CPrecisionSlider *m_pMagentaGreenSlider;
CPrecisionSlider *m_pYellowBlueSlider;
CPrecisionSlider *m_pBlendFactorSlider;
CColorBalanceOperation *m_pBalanceOp;
};
//-----------------------------------------------------------------------------
// constructor
//-----------------------------------------------------------------------------
CColorBalanceUIPanel::CColorBalanceUIPanel( vgui::Panel *pParent, CColorBalanceOperation *pOp ) : BaseClass( pParent, "BalanceUIPanel" )
{
m_pPreserveLuminosityButton = new vgui::CheckButton( this, "PreserveLuminosity", "Preserve Luminosity" );
m_pShadowModeButton = new vgui::RadioButton( this, "ShadowMode", "Shadows" );
m_pMidtoneModeButton = new vgui::RadioButton( this, "MidtoneMode", "Midtones" );
m_pHighlightModeButton = new vgui::RadioButton( this, "HighlightMode", "Highlights" );
m_pCyanRedSlider = new CPrecisionSlider( this, "CyanRedSlider" );
m_pCyanRedSlider->SetRange( -100, 100 );
m_pCyanRedSlider->SetValue( 0 );
m_pCyanRedSlider->AddActionSignalTarget( this );
m_pMagentaGreenSlider = new CPrecisionSlider( this, "MagentaGreenSlider" );
m_pMagentaGreenSlider->SetRange( -100, 100 );
m_pMagentaGreenSlider->SetValue( 0 );
m_pMagentaGreenSlider->AddActionSignalTarget( this );
m_pYellowBlueSlider = new CPrecisionSlider( this, "YellowBlueSlider" );
m_pYellowBlueSlider->SetRange( -100, 100 );
m_pYellowBlueSlider->SetValue( 0 );
m_pYellowBlueSlider->AddActionSignalTarget( this );
m_pBlendFactorSlider = new CPrecisionSlider( this, "BlendFactorSlider" );
m_pBlendFactorSlider->SetRange( 0, 255 );
m_pBlendFactorSlider->SetValue( 255 );
m_pBlendFactorSlider->AddActionSignalTarget( this );
LoadControlSettings("Resource\\ColorBalanceUIPanel.res");
m_pBalanceOp = pOp;
ResetBlendFactorSlider();
}
CColorBalanceUIPanel::~CColorBalanceUIPanel()
{
}
//-----------------------------------------------------------------------------
// Command issued
//-----------------------------------------------------------------------------
void CColorBalanceUIPanel::OnMessage(const KeyValues *params, vgui::VPANEL fromPanel)
{
BaseClass::OnMessage( params, fromPanel );
if ( !Q_stricmp( "SliderMoved", params->GetName() ) )
{
vgui::Panel *pPanel = reinterpret_cast<vgui::Panel *>( const_cast<KeyValues*>(params)->GetPtr("panel") );
if ( pPanel == m_pBlendFactorSlider )
{
m_pBalanceOp->SetBlendFactor( m_pBlendFactorSlider->GetValue() / 255.0f );
PostMessage( GetParent(), new KeyValues( "command", "command", "BlendFactorUpdate" ) );
return;
}
else if ( pPanel == m_pCyanRedSlider )
{
m_pBalanceOp->SetCyanRedBalance( GetCurrentMode(), m_pCyanRedSlider->GetValue() / 1.0f );
return;
}
else if ( pPanel == m_pMagentaGreenSlider )
{
m_pBalanceOp->SetMagentaGreenBalance( GetCurrentMode(), m_pMagentaGreenSlider->GetValue() / 1.0f );
return;
}
else if ( pPanel == m_pYellowBlueSlider )
{
m_pBalanceOp->SetYellowBlueBalance( GetCurrentMode(), m_pYellowBlueSlider->GetValue() / 1.0f );
return;
}
}
else if ( !Q_stricmp( "CheckButtonChecked", params->GetName() ) )
{
vgui::Panel *pPanel = reinterpret_cast<vgui::Panel *>( const_cast<KeyValues*>(params)->GetPtr("panel") );
if( pPanel == m_pPreserveLuminosityButton )
{
m_pBalanceOp->SetPreserveLuminosity( m_pPreserveLuminosityButton->IsSelected() );
return;
}
}
}
void CColorBalanceUIPanel::OnCommand( const char *command )
{
BaseClass::OnCommand( command );
if( !Q_stricmp( "BlendFactorUpdate", command ) )
{
ResetBlendFactorSlider();
}
}
void CColorBalanceUIPanel::ResetBlendFactorSlider()
{
float flBlend;
if( m_pBalanceOp )
flBlend = m_pBalanceOp->GetBlendFactor();
else
flBlend = 0.0f;
m_pBlendFactorSlider->SetValue( flBlend*255.0f );
}
void CColorBalanceUIPanel::OnRadioButtonHit()
{
ResetSliders();
}
ColorBalanceMode_t CColorBalanceUIPanel::GetCurrentMode()
{
if( m_pShadowModeButton->IsSelected() )
return CC_BALANCE_MODE_SHADOWS;
else if( m_pMidtoneModeButton->IsSelected() )
return CC_BALANCE_MODE_MIDTONES;
else if( m_pHighlightModeButton->IsSelected() )
return CC_BALANCE_MODE_HIGHLIGHTS;
return CC_BALANCE_MODE_SHADOWS;
}
void CColorBalanceUIPanel::ResetSliders()
{
if( !m_pBalanceOp )
return;
ColorBalanceMode_t mode = GetCurrentMode();
m_pCyanRedSlider->SetValue ( (int)m_pBalanceOp->GetCyanRedBalance(mode), 0 );
m_pMagentaGreenSlider->SetValue( (int)m_pBalanceOp->GetMagentaGreenBalance(mode), 0 );
m_pYellowBlueSlider->SetValue ( (int)m_pBalanceOp->GetYellowBlueBalance(mode), 0 );
}
class CLookupViewPanel : public CProceduralTexturePanel
{
DECLARE_CLASS_SIMPLE( CLookupViewPanel, CProceduralTexturePanel );
public:
CLookupViewPanel( vgui::Panel *parent, ColorCorrectionHandle_t CCHandle );
~CLookupViewPanel( );
void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect );
protected:
ColorCorrectionHandle_t m_CCHandle;
private:
};
CLookupViewPanel::CLookupViewPanel( vgui::Panel *parent, ColorCorrectionHandle_t CCHandle ) : BaseClass( parent, "LookupViewPanel" )
{
m_CCHandle = CCHandle;
}
CLookupViewPanel::~CLookupViewPanel()
{
}
void CLookupViewPanel::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect )
{
Assert( pVTFTexture->FrameCount() == 1 );
Assert( pVTFTexture->FaceCount() == 1 );
Assert( !pTexture->IsMipmapped() );
int nWidth, nHeight, nDepth;
pVTFTexture->ComputeMipLevelDimensions( 0, &nWidth, &nHeight, &nDepth );
Assert( nDepth==1 );
Assert( nWidth*nHeight==32*32*32 );
CPixelWriter pixelWriter;
pixelWriter.SetPixelMemory( pVTFTexture->Format(), pVTFTexture->ImageData( 0, 0, 0 ), pVTFTexture->RowSizeInBytes( 0 ) );
for( int y=0;y<256;y++ )
{
for( int x=0;x<128;x++ )
{
int cx = x>>5;
int cy = y>>5;
int dx = x%32;
int dy = y%32;
RGBX5551_t inColor;
inColor.r = cx + cy*4;
inColor.g = dx;
inColor.b = dy;
color24 outColor = colorcorrection->GetLookup( m_CCHandle, inColor );
pixelWriter.WritePixel( outColor.r, outColor.g, outColor.b );
}
}
}
class CLookupViewWindow : public vgui::Frame
{
DECLARE_CLASS_SIMPLE( CLookupViewWindow, vgui::Frame );
public:
CLookupViewWindow( vgui::Panel *parent, ColorCorrectionHandle_t CCHandle );
~CLookupViewWindow( );
virtual void Init();
virtual void Shutdown();
void UpdateColorCorrection();
private:
CLookupViewPanel *m_pLookupPanel;
ColorCorrectionHandle_t m_CCHandle;
};
CLookupViewWindow::CLookupViewWindow( vgui::Panel *parent, ColorCorrectionHandle_t CCHandle ) : BaseClass( parent, "LookupViewWindow" )
{
SetSize( 146, 298 );
SetPos( 32, 32 );
m_pLookupPanel = new CLookupViewPanel( this, CCHandle );
LoadControlSettings( "Resource\\LookupViewWindow.res" );
m_CCHandle = CCHandle;
}
CLookupViewWindow::~CLookupViewWindow()
{
if( m_pLookupPanel )
delete m_pLookupPanel;
}
void CLookupViewWindow::Init()
{
m_pLookupPanel->Init( 128, 256, false );
Rect_t rect;
rect.x = 0;
rect.y = 0;
rect.width = 128;
rect.height = 256;
m_pLookupPanel->SetTextureSubRect( rect );
m_pLookupPanel->DownloadTexture();
}
void CLookupViewWindow::Shutdown()
{
m_pLookupPanel->Shutdown();
}
void CLookupViewWindow::UpdateColorCorrection()
{
m_pLookupPanel->DownloadTexture();
}
class CColorOperationListPanel;
class CNewOperationDialog : public vgui::Frame
{
DECLARE_CLASS_SIMPLE( CNewOperationDialog, vgui::Frame );
public:
CNewOperationDialog( vgui::Panel *parent, CColorOperationList *pOpList );
~CNewOperationDialog( );
virtual void OnCommand( const char *command );
private:
void PopulateControls( );
vgui::ComboBox *m_pOperationType;
vgui::TextEntry *m_pName;
vgui::Button *m_pCreateButton;
vgui::Button *m_pCancelButton;
CColorOperationList *m_pOpList;
};
CNewOperationDialog::CNewOperationDialog( vgui::Panel *parent, CColorOperationList *pOpList ) : BaseClass( parent, "NewOperation" )
{
m_pOperationType = new vgui::ComboBox( this, "OperationType", 6, false );
m_pName = new vgui::TextEntry( this, "Name" );
m_pCreateButton = new vgui::Button( this, "Create", "Create", this, "Create" );
m_pCancelButton = new vgui::Button( this, "Cancel", "Cancel", this, "Cancel" );
LoadControlSettings( "Resource\\NewOperationDialog.res" );
PopulateControls();
m_pOpList = pOpList;
}
CNewOperationDialog::~CNewOperationDialog( )
{
}
void CNewOperationDialog::OnCommand( const char *command )
{
if( !Q_stricmp( command, "Create" ) )
{
int nSelectedItem = m_pOperationType->GetActiveItem();
IColorOperation *newOp = 0;
switch( (ColorCorrectionTool_t)nSelectedItem+1 )
{
case CC_TOOL_BALANCE: newOp = new CColorBalanceOperation(); break;
case CC_TOOL_CURVES: newOp = new CCurvesColorOperation(); break;
case CC_TOOL_LOOKUP: newOp = new CColorLookupOperation(); break;
case CC_TOOL_LEVELS: newOp = new CLevelsColorOperation(); ((CLevelsColorOperation *)newOp)->SetColorOpList( m_pOpList ); break;
case CC_TOOL_SELECTED_HSV: newOp = new CSelectedHSVOperation(); ((CSelectedHSVOperation *)newOp)->SetColorOpList( m_pOpList ); break;
}
if( m_pName->GetTextLength()>0 )
{
char buf[256];
m_pName->GetText( buf, 256 );
newOp->SetName( buf );
}
m_pOpList->AddOperation( newOp );
PostActionSignal( new KeyValues( "Command", "Command", "NewComplete" ) );
}
else if( !Q_stricmp( command, "Cancel" ) )
{
PostActionSignal( new KeyValues( "Command", "Command", "NewCancel" ) );
}
}
void CNewOperationDialog::PopulateControls()
{
m_pOperationType->DeleteAllItems();
int i;
for ( i = 1; i < CC_TOOL_COUNT; i++ )
{
m_pOperationType->AddItem( s_pColorCorrectionToolNames[i], NULL );
}
m_pOperationType->AddActionSignalTarget( this );
m_pOperationType->ActivateItem( 0 );
}
//-----------------------------------------------------------------------------
// COperationListPanel
//-----------------------------------------------------------------------------
class COperationListPanel : public vgui::ListPanel
{
DECLARE_CLASS_SIMPLE( COperationListPanel, vgui::ListPanel );
public:
COperationListPanel( vgui::Panel *parent, const char *pName );
~COperationListPanel( );
MESSAGE_FUNC_PARAMS( OnTextNewLine, "TextNewLine", data );
virtual void OnMousePressed( MouseCode code );
virtual void OnMouseDoublePressed( MouseCode code );
virtual void AddSelectedItem( int itemID );
virtual void ClearSelectedItems( );
virtual void RemoveItem( int itemID );
virtual void SortList( );
virtual void SetSortColumn( int column );
private:
vgui::TextEntry *m_pNameEditPanel;
int m_nEditItem;
};
COperationListPanel::COperationListPanel( vgui::Panel *parent, const char *pName ) : BaseClass( parent, pName )
{
m_pNameEditPanel = 0;
m_nEditItem = -1;
}
COperationListPanel::~COperationListPanel( )
{
}
void COperationListPanel::AddSelectedItem( int itemID )
{
BaseClass::AddSelectedItem( itemID );
PostActionSignal( new KeyValues( "Command", "Command", "SelectedItemChanged" ) );
}
void COperationListPanel::ClearSelectedItems( )
{
BaseClass::ClearSelectedItems( );
PostActionSignal( new KeyValues( "Command", "Command", "SelectedItemChanged" ) );
}
void COperationListPanel::RemoveItem( int itemID )
{
BaseClass::RemoveItem( itemID );
PostActionSignal( new KeyValues( "Command", "Command", "SelectedItemChanged" ) );
}
void COperationListPanel::SortList( )
{
// Disable sorting of the list
}
void COperationListPanel::SetSortColumn( int column )
{
if( column==0 )
{
bool bAllEnabled = true;
for( int i=0;i<GetItemCount();i++ )
{
IColorOperation *pOp = (IColorOperation *)GetItemUserData( i );
if( !pOp->IsEnabled() )
{
bAllEnabled = false;
break;
}
}
for( int i=0;i<GetItemCount();i++ )
{
KeyValues *kv = GetItem( i );
kv->SetInt( "image", (!bAllEnabled)?1:0 );
IColorOperation *pOp = (IColorOperation *)GetItemUserData( i );
pOp->SetEnabled( !bAllEnabled );
}
colorcorrectiontools->UpdateColorCorrection();
}
}
void COperationListPanel::OnMousePressed( MouseCode code )
{
if( code==MOUSE_LEFT )
{
int x, y;
input()->GetCursorPos( x, y );
int row, column;
GetCellAtPos( x, y, row, column );
if( column==0 && row!=-1 )
{
IColorOperation *pOp = (IColorOperation *)GetItemUserData( row );
bool bNewEnable = !pOp->IsEnabled();
KeyValues *kv = GetItem( row );
kv->SetInt( "image", (bNewEnable)?1:0 );
pOp->SetEnabled( bNewEnable );
colorcorrectiontools->UpdateColorCorrection();
}
else
{
BaseClass::OnMousePressed( code );
}
}
}
void COperationListPanel::OnMouseDoublePressed( MouseCode code )
{
if( code==MOUSE_LEFT )
{
int x, y;
input()->GetCursorPos( x, y );
int row, column;
GetCellAtPos( x, y, row, column );
if( column!=0 && row==-1 )
{
PostActionSignal( new KeyValues( "Command", "Command", "NewOperation" ) );
}
else
{
if( input()->IsKeyDown( KEY_LCONTROL )||input()->IsKeyDown( KEY_RCONTROL ) )
{
if( !m_pNameEditPanel )
{
m_nEditItem = row;
m_pNameEditPanel = new vgui::TextEntry( this, "Name" );
m_pNameEditPanel->SendNewLine( true );
m_pNameEditPanel->SetCatchEnterKey( true );
m_pNameEditPanel->AddActionSignalTarget( this );
m_pNameEditPanel->SetSize( 226, 24 );
m_pNameEditPanel->SetBgColor( Color(255,255,255,255) );
EnterEditMode( row, column, m_pNameEditPanel );
}
}
else if( input()->IsKeyDown( KEY_LALT ) || input()->IsKeyDown( KEY_RALT ) )
{
PostActionSignal( new KeyValues( "Command", "Command", "CloneOperation" ) );
}
else
{
BaseClass::OnMouseDoublePressed( code );
}
}
}
}
void COperationListPanel::OnTextNewLine( KeyValues *data )
{
char newName[256];
m_pNameEditPanel->GetText( newName, 256 );
if( m_nEditItem!=-1 )
{
IColorOperation *pOp = (IColorOperation *)GetItemUserData( m_nEditItem );
pOp->SetName( newName );
PostActionSignal( new KeyValues( "Command", "Command", "UpdateList" ) );
}
m_nEditItem = -1;
LeaveEditMode();
delete m_pNameEditPanel;
m_pNameEditPanel = 0;
}
//-----------------------------------------------------------------------------
// CColorOperationListPanel - View window for operations in a CColorOperationList
//-----------------------------------------------------------------------------
class CColorOperationListPanel : public vgui::EditablePanel
{
DECLARE_CLASS_SIMPLE( CColorOperationListPanel, vgui::EditablePanel );
public:
CColorOperationListPanel( vgui::Panel *parent, ColorCorrectionHandle_t CCHandle );
~CColorOperationListPanel( );
void Init( );
void Shutdown( );
void PopulateList( );
CColorOperationList *GetOperationList( );
virtual void OnCommand(const char *command);
virtual void OnThink( );
void ReadUncorrectedImage( Rect_t *pSrcRect, unsigned char *pPreviewImage );
void UpdateColorCorrection( );
private:
MESSAGE_FUNC_PARAMS( OnOpPanelClose, "OpPanelClose", data );
MESSAGE_FUNC_PARAMS( OnSliderMoved, "SliderMoved", data );
MESSAGE_FUNC_PARAMS( OnCheckButtonChecked, "CheckButtonChecked", data );
MESSAGE_FUNC_CHARPTR( OnFileSelected, "FileSelected", fullpath );
virtual void OnMouseDoublePressed( MouseCode code );
virtual void OnKeyCodeTyped( KeyCode code );
void ResetSlider( );
void LaunchOperationPanel( IColorOperation *pOp );
vgui::Button *m_pNewOperationButton;
vgui::Button *m_pDeleteOperationButton;
vgui::Button *m_pBringForwardButton;
vgui::Button *m_pPushBackButton;
vgui::Button *m_pSaveButton;
vgui::CheckButton *m_pEnableButton;
vgui::CheckButton *m_pEnableEntitiesButton;
COperationListPanel *m_pOperationListPanel;
CPrecisionSlider *m_pBlendFactorSlider;
CLookupViewWindow *m_pLookupViewWindow;
CNewOperationDialog *m_pNewDialog;
CColorOperationList m_OperationList;
ColorCorrectionHandle_t m_CCHandle;
CUtlVector< CColorCorrectionUIChildPanel * > m_OpPanelList;
bool m_bEnable;
bool m_bEnableEntities;
};
CColorOperationListPanel::CColorOperationListPanel( vgui::Panel *parent, ColorCorrectionHandle_t CCHandle ) : BaseClass( parent, "ColorOperationListPanel" )
{
m_pNewOperationButton = new vgui::Button( this, "NewOperation", "New", this, "NewOperation" );
m_pDeleteOperationButton = new vgui::Button( this, "DeleteOperation", "Delete", this, "DeleteOperation" );
m_pBringForwardButton = new vgui::Button( this, "BringForward", "Up", this, "BringForward" );
m_pPushBackButton = new vgui::Button( this, "PushBack", "Down", this, "PushBack" );
m_pSaveButton = new vgui::Button( this, "Save", "Save", this, "Save" );
m_pEnableButton = new vgui::CheckButton( this, "Enable", "Enable" );
m_pEnableButton->SetSelected( false );
m_pEnableButton->AddActionSignalTarget( this );
m_pEnableEntitiesButton = new vgui::CheckButton( this, "EnableEntities", "Enable Entities" );
m_pEnableEntitiesButton->SetSelected( true );
m_pEnableEntitiesButton->AddActionSignalTarget( this );
m_pBlendFactorSlider = new CPrecisionSlider( this, "BlendFactorSlider" );
m_pBlendFactorSlider->SetRange( 0, 255 );
m_pBlendFactorSlider->SetValue( 255 );
m_pBlendFactorSlider->AddActionSignalTarget( this );
m_pOperationListPanel = new COperationListPanel( this, "OperationList" );
m_pOperationListPanel->SetBuildModeEditable( true );
m_pOperationListPanel->AddColumnHeader( 0, "image", "", 24, ListPanel::COLUMN_IMAGE );
m_pOperationListPanel->AddColumnHeader( 1, "layer", "", 226, 0 );
m_pOperationListPanel->SetSelectIndividualCells( false );
m_pOperationListPanel->SetEmptyListText( "" );
m_pOperationListPanel->SetDragEnabled( false );
m_pOperationListPanel->SetColumnSortable( 0, true );
m_pOperationListPanel->SetColumnSortable( 1, false );
m_pOperationListPanel->AddActionSignalTarget( this );
vgui::ImageList *pImageList = new vgui::ImageList( false );
pImageList->AddImage( scheme()->GetImage( "Resource/icon_hlicon1", false ) );
m_pOperationListPanel->SetImageList( pImageList, true );
m_pLookupViewWindow = new CLookupViewWindow( this, CCHandle );
m_pLookupViewWindow->SetTitle( "Lookup View", true );
m_pLookupViewWindow->SetSize( 148, 298 );
m_pLookupViewWindow->SetEnabled( true );
m_pLookupViewWindow->SetSizeable( false );
m_pLookupViewWindow->AddActionSignalTarget( this );
m_pLookupViewWindow->Activate();
m_pNewDialog = 0;
m_bEnable = true;
m_bEnableEntities = true;
LoadControlSettings( "Resource\\ColorOperationListPanel.res" );
SetVisible( true );
ResetSlider( );
PopulateList( );
m_CCHandle = CCHandle;
}
void CColorOperationListPanel::OnOpPanelClose( KeyValues *data )
{
CColorCorrectionUIChildPanel *pSender = (CColorCorrectionUIChildPanel *)data->GetPtr( "panel", 0 );
if( pSender )
{
int opPanelIndex = m_OpPanelList.Find( pSender );
m_OpPanelList.Remove( opPanelIndex );
pSender->Shutdown();
delete pSender;
}
}
void CColorOperationListPanel::OnSliderMoved( KeyValues *data )
{
vgui::Panel *pPanel = reinterpret_cast<vgui::Panel *>( const_cast<KeyValues*>(data)->GetPtr("panel") );
if ( pPanel == m_pBlendFactorSlider )
{
int nSelectedItem = m_pOperationListPanel->GetSelectedItem( 0 );
if( nSelectedItem>=0 && nSelectedItem<m_pOperationListPanel->GetItemCount() )
{
IColorOperation *pOp = (IColorOperation *)m_pOperationListPanel->GetItemUserData( nSelectedItem );
pOp->SetBlendFactor( m_pBlendFactorSlider->GetValue() / 255.0f );
for( int i=0;i<m_OpPanelList.Count();i++ )
{
if( m_OpPanelList[i]->GetOperation()==pOp )
{
// We have an open edit window for this op
PostMessage( m_OpPanelList[i], new KeyValues( "command", "command", "BlendFactorUpdate" ) );
}
}
}
return;
}
}
void CColorOperationListPanel::OnCheckButtonChecked( KeyValues *data )
{
vgui::Panel *pPanel = reinterpret_cast<vgui::Panel *>( const_cast<KeyValues*>(data)->GetPtr("panel") );
if ( pPanel == m_pEnableButton )
{
if( m_pEnableButton->IsSelected() )
{
PostActionSignal( new KeyValues( "Command", "Command", "EnableColorCorrection" ) );
m_bEnable = true;
}
else
{
PostActionSignal( new KeyValues( "Command", "Command", "DisableColorCorrection" ) );
m_bEnable = false;
}
}
else if ( pPanel == m_pEnableEntitiesButton )
{
if( m_pEnableEntitiesButton->IsSelected() )
{
m_bEnableEntities = true;
mat_colcorrection_disableentities.SetValue( 0 );
}
else
{
m_bEnableEntities = false;
mat_colcorrection_disableentities.SetValue( 1 );
}
}
}
void CColorOperationListPanel::OnCommand( const char *command )
{
if( !Q_stricmp( command, "NewOperation" ) )
{
if( !m_pNewDialog )
{
m_pNewDialog = new CNewOperationDialog( this, &m_OperationList );
m_pNewDialog->AddActionSignalTarget( this );
m_pNewDialog->Activate();
}
}
else if( !Q_stricmp( command, "DeleteOperation" ) )
{
if( m_pOperationListPanel->GetSelectedItemsCount()!=0 )
{
int nSelectedItem = m_pOperationListPanel->GetSelectedItem( 0 );
IColorOperation *pOp = m_OperationList.GetOperation( nSelectedItem );
m_OperationList.DeleteOperation( nSelectedItem );
for( int i=0;i<m_OpPanelList.Count();i++ )
{
if( m_OpPanelList[i]->GetOperation()==pOp )
{
CColorCorrectionUIChildPanel *panel = m_OpPanelList[i];
delete panel;
m_OpPanelList.Remove( i );
break;
}
}
PopulateList( );
colorcorrectiontools->UpdateColorCorrection( );
}
}
else if( !Q_stricmp( command, "BringForward" ) )
{
int nSelectedItem = m_pOperationListPanel->GetSelectedItem( 0 );
if( nSelectedItem!=0 )
{
m_OperationList.BringForward( nSelectedItem );
PopulateList( );
colorcorrectiontools->UpdateColorCorrection( );
m_pOperationListPanel->SetSingleSelectedItem( nSelectedItem-1 );
}
}
else if( !Q_stricmp( command, "PushBack" ) )
{
int nSelectedItem = m_pOperationListPanel->GetSelectedItem( 0 );
if( nSelectedItem<m_OperationList.GetNumOperations()-1 )
{
m_OperationList.PushBack( nSelectedItem );
PopulateList( );
colorcorrectiontools->UpdateColorCorrection( );
m_pOperationListPanel->SetSingleSelectedItem( nSelectedItem+1 );
}
}
else if( !Q_stricmp( command, "Save" ) )
{
FileOpenDialog *save_dialog = new FileOpenDialog( this, "File Save", false );
save_dialog->AddActionSignalTarget( this );
save_dialog->AddFilter( "*.raw", ".RAW files", true );
save_dialog->DoModal( true );
}
else if( !Q_stricmp( command, "NewComplete" ) )
{
if( m_pNewDialog )
{
delete m_pNewDialog;
m_pNewDialog = 0;
}
PopulateList( );
m_pOperationListPanel->SetSingleSelectedItem( m_pOperationListPanel->GetItemCount()-1 );
OnKeyCodeTyped( KEY_ENTER );
}
else if( !Q_stricmp( command, "NewCancel" ) )
{
if( m_pNewDialog )
{
delete m_pNewDialog;
m_pNewDialog = 0;
}
}
else if( !Q_stricmp( command, "SelectedItemChanged" ) )
{
ResetSlider();
}
else if( !Q_stricmp( command, "BlendFactorUpdate" ) )
{
ResetSlider();
}
else if( !Q_stricmp( command, "UpdateList" ) )
{
PopulateList();
}
else if( !Q_stricmp( command, "CloneOperation" ) )
{
int nSelectedItem = m_pOperationListPanel->GetSelectedItem( 0 );
IColorOperation *pOp = m_OperationList.GetOperation( nSelectedItem );
IColorOperation *pCloneOp = pOp->Clone();
m_OperationList.AddOperation( pCloneOp );
PopulateList();
}
}
void CColorOperationListPanel::OnThink( )
{
BaseClass::OnThink();
if( m_bEnable )
{
colorcorrection->SetLookupWeight( m_CCHandle, 1.0f );
}
else
{
colorcorrection->SetLookupWeight( m_CCHandle, 0.0f );
}
}
void CColorOperationListPanel::ResetSlider( )
{
int nSelectedItem = m_pOperationListPanel->GetSelectedItem( 0 );
if( nSelectedItem>=0 && nSelectedItem<m_pOperationListPanel->GetItemCount() )
{
IColorOperation *pOp = (IColorOperation *)m_pOperationListPanel->GetItemUserData( nSelectedItem );
float flBlend = pOp->GetBlendFactor();
m_pBlendFactorSlider->SetValue( flBlend*255.0f );
m_pBlendFactorSlider->SetEnabled( true );
}
else
{
m_pBlendFactorSlider->SetValue( 0 );
m_pBlendFactorSlider->SetEnabled( false );
}
}
void CColorOperationListPanel::PopulateList( )
{
m_pOperationListPanel->DeleteAllItems( );
int numItems = m_OperationList.GetNumOperations();
for( int i=0;i<numItems;i++ )
{
IColorOperation *op = m_OperationList.GetOperation( i );
if( op )
{
KeyValues *kv = new KeyValues( "operation", "layer", op->GetName() );
kv->SetInt( "image", (op->IsEnabled())?1:0 );
m_pOperationListPanel->AddItem( kv, (unsigned int)op, false, false );
}
}
}
CColorOperationListPanel::~CColorOperationListPanel()
{
}
void CColorOperationListPanel::Init()
{
m_pLookupViewWindow->Init();
}
void CColorOperationListPanel::Shutdown()
{
m_pLookupViewWindow->Shutdown();
m_OperationList.Clear();
}
CColorOperationList *CColorOperationListPanel::GetOperationList( )
{
return &m_OperationList;
}
void CColorOperationListPanel::OnMouseDoublePressed( MouseCode code )
{
BaseClass::OnMouseDoublePressed( code );
if( code==MOUSE_LEFT )
{
}
}
void CColorOperationListPanel::OnKeyCodeTyped( KeyCode code )
{
if( code==KEY_ENTER )
{
int nSelectedItem = m_pOperationListPanel->GetSelectedItem( 0 );
IColorOperation *pSelectedOp = m_OperationList.GetOperation( nSelectedItem );
LaunchOperationPanel( pSelectedOp );
}
else if( code==KEY_ESCAPE )
{
void ShowHideColorCorrectionUI();
ShowHideColorCorrectionUI();
}
BaseClass::OnKeyCodeTyped( code );
}
void CColorOperationListPanel::OnFileSelected( const char *pFilename )
{
FileHandle_t file_handle = g_pFileSystem->Open( pFilename, "wb" );
colorcorrection->LockLookup( m_CCHandle );
RGBX5551_t inColor;
inColor.b = 0;
for ( int b = 0; b < 32; ++b, ++inColor.b )
{
inColor.g = 0;
for ( int g = 0; g < 32; ++g, ++inColor.g )
{
inColor.r = 0;
for ( int r = 0; r < 32; ++r, ++inColor.r )
{
color24 outColor;
outColor = colorcorrection->GetLookup( m_CCHandle, inColor );
g_pFileSystem->Write( &outColor, sizeof(color24), file_handle );
}
}
}
colorcorrection->UnlockLookup( m_CCHandle );
g_pFileSystem->Close( file_handle );
}
void CColorOperationListPanel::LaunchOperationPanel( IColorOperation *pOp )
{
if( pOp )
{
for( int i=0;i<m_OpPanelList.Count();i++ )
{
CColorCorrectionUIChildPanel *panel = m_OpPanelList[i];
if( panel->GetOperation()==pOp )
{
panel->Activate();
return;
}
}
CColorCorrectionUIChildPanel *pOpPanel = 0;
switch( pOp->ToolID() )
{
case CC_TOOL_BALANCE: pOpPanel = new CColorBalanceUIPanel( this, (CColorBalanceOperation *)pOp ); break;
case CC_TOOL_CURVES: pOpPanel = new CColorCurvesUIPanel( this, (CCurvesColorOperation *)pOp ); break;
case CC_TOOL_LEVELS: pOpPanel = new CColorLevelsUIPanel( this, (CLevelsColorOperation *)pOp ); break;
case CC_TOOL_LOOKUP: pOpPanel = new CColorLookupUIPanel( this, (CColorLookupOperation *)pOp ); break;
case CC_TOOL_SELECTED_HSV: pOpPanel = new CSelectedHSVUIPanel( this, (CSelectedHSVOperation *)pOp ); break;
}
int parentX, parentY;
GetParent()->GetPos( parentX, parentY );
int maxPanels = parentX / 250;
int panelOffset = (m_OpPanelList.Count()+1<maxPanels)?m_OpPanelList.Count()+1:maxPanels;
int xPos = parentX - 250*panelOffset;
pOpPanel->SetPos( xPos, parentY );
pOpPanel->SetSize( 250, 480 );
pOpPanel->SetTitle( pOp->GetName(), true );
pOpPanel->AddActionSignalTarget( this );
pOpPanel->SetSizeable( false );
pOpPanel->SetVisible( true );
pOpPanel->Init( );
m_OpPanelList.AddToTail( pOpPanel );
}
}
void CColorOperationListPanel::ReadUncorrectedImage( Rect_t *pSrcRect, unsigned char *pPreviewImage )
{
for( int i=0;i<m_OpPanelList.Count();i++ )
{
CColorCorrectionUIChildPanel *pPanel = m_OpPanelList[i];
pPanel->ReadUncorrectedImage( pSrcRect, pPreviewImage );
}
}
void CColorOperationListPanel::UpdateColorCorrection()
{
m_pLookupViewWindow->UpdateColorCorrection();
}
//-----------------------------------------------------------------------------
//
// CColorCorrectionUIPanel begins here
//
//-----------------------------------------------------------------------------
class CColorCorrectionUIPanel : public vgui::Frame
{
DECLARE_CLASS_SIMPLE( CColorCorrectionUIPanel, vgui::Frame );
public:
CColorCorrectionUIPanel( vgui::Panel *parent );
~CColorCorrectionUIPanel();
// Command issued
virtual void OnCommand(const char *command);
virtual void Activate();
void Init();
void Shutdown();
virtual void OnKeyCodeTyped(KeyCode code);
virtual void OnThink( );
void ReadUncorrectedImage( Rect_t *pSrcRect, unsigned char *pPreviewImage );
// Updates the color correction terms
void UpdateColorCorrection( );
void SetFinalOperation( IColorOperation *pOp );
protected:
CColorOperationListPanel *m_pOperationListPanel;
IColorOperation *m_pFinalOperation;
bool m_bEnable;
ColorCorrectionHandle_t m_CCHandle;
int m_nRowStep;
int m_nCurrentRow;
color24 m_pLookupCache[ 32*32*32 ];
bool m_bForceReset;
private:
};
//-----------------------------------------------------------------------------
// Purpose: Basic help dialog
//-----------------------------------------------------------------------------
CColorCorrectionUIPanel::CColorCorrectionUIPanel( vgui::Panel *parent ) : BaseClass( parent, "ColorCorrectionUIPanel" )
{
if ( !colorcorrection )
{
m_pOperationListPanel = NULL;
m_CCHandle = 0;
Warning( "Could not get the color correction interface!" );
Shutdown();
return;
}
m_CCHandle = colorcorrection->AddLookup( "editable" );
colorcorrection->SetResetable( m_CCHandle, true );
m_bForceReset = true;
m_bEnable = false;
SetTitle("Color Correction Tools", true);
m_pOperationListPanel = new CColorOperationListPanel( this, m_CCHandle );
m_pOperationListPanel->AddActionSignalTarget( this );
LoadControlSettings("Resource\\ColorCorrectionUIPanel.res");
// Hidden by default
SetVisible( false );
SetSizeable( false );
SetMoveable( true );
int w = 250;
int h = 480;
int x = videomode->GetModeStereoWidth() - w - 10;
int y = videomode->GetModeStereoHeight() - h - 10;
SetBounds( x, y, w, h );
m_pOperationListPanel->PopulateList( );
Q_memset( m_pLookupCache, 0x00, sizeof(color24)*32*32*32 );
m_nCurrentRow = -1;
m_nRowStep = 4;
m_pFinalOperation = NULL;
}
CColorCorrectionUIPanel::~CColorCorrectionUIPanel()
{
colorcorrection->RemoveLookup( m_CCHandle );
}
//-----------------------------------------------------------------------------
// Init, shutdown
//-----------------------------------------------------------------------------
void CColorCorrectionUIPanel::Init()
{
m_pOperationListPanel->Init();
}
void CColorCorrectionUIPanel::Shutdown()
{
if ( m_pOperationListPanel )
{
m_pOperationListPanel->Shutdown();
}
}
//-----------------------------------------------------------------------------
// Updates the color correction terms
//-----------------------------------------------------------------------------
void CColorCorrectionUIPanel::UpdateColorCorrection( )
{
if( !m_bEnable )
return;
m_nCurrentRow = 0;
}
//-----------------------------------------------------------------------------
// Purpose: Shows the panel
//-----------------------------------------------------------------------------
void CColorCorrectionUIPanel::Activate()
{
BaseClass::Activate();
}
void CColorCorrectionUIPanel::OnCommand( char const *command )
{
BaseClass::OnCommand( command );
if( !Q_stricmp( "EnableColorCorrection", command ) )
{
m_bEnable = true;
UpdateColorCorrection();
colorcorrection->SetResetable( m_CCHandle, false );
}
else if( !Q_stricmp( "DisableColorCorrection", command ) )
{
m_bEnable = false;
UpdateColorCorrection();
colorcorrection->SetResetable( m_CCHandle, true );
}
}
void CColorCorrectionUIPanel::OnThink( )
{
BaseClass::OnThink();
if( m_bForceReset )
{
colorcorrection->LockLookup( m_CCHandle );
colorcorrection->ResetLookup( m_CCHandle );
colorcorrection->UnlockLookup( m_CCHandle );
m_bForceReset = false;
}
if( m_nCurrentRow!=-1 )
{
RGBX5551_t inColor;
inColor.r = m_nCurrentRow;
for ( int r = m_nCurrentRow; r < 32 && r < (m_nCurrentRow+32/m_nRowStep); ++r, ++inColor.r )
{
inColor.g = 0;
for ( int g = 0; g < 32; ++g, ++inColor.g )
{
inColor.b = 0;
for ( int b = 0; b < 32; ++b, ++inColor.b )
{
color24 outColor;
color24 directColor = colorcorrection->ConvertToColor24( inColor );
if( m_bEnable )
{
m_pOperationListPanel->GetOperationList()->Apply( directColor, outColor, m_pFinalOperation );
}
else
{
outColor = directColor;
}
m_pLookupCache[ inColor.r + inColor.g*32 + inColor.b*32*32 ] = outColor;
}
}
}
m_nCurrentRow+=32/m_nRowStep;
if( m_nCurrentRow==32 )
{
colorcorrection->LockLookup( m_CCHandle );
colorcorrection->CopyLookup( m_CCHandle, m_pLookupCache );
colorcorrection->UnlockLookup( m_CCHandle );
m_pOperationListPanel->UpdateColorCorrection();
m_nCurrentRow = -1;
}
}
}
//-----------------------------------------------------------------------------
// Pass down the uncorrected image for panels that need it
//-----------------------------------------------------------------------------
void CColorCorrectionUIPanel::ReadUncorrectedImage( Rect_t *pSrcRect, unsigned char *pPreviewImage )
{
m_pOperationListPanel->ReadUncorrectedImage( pSrcRect, pPreviewImage );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CColorCorrectionUIPanel::OnKeyCodeTyped(KeyCode code)
{
switch( code )
{
case KEY_ESCAPE:
Close();
break;
default:
BaseClass::OnKeyCodeTyped( code );
break;
}
}
void CColorCorrectionUIPanel::SetFinalOperation( IColorOperation *pOp )
{
m_pFinalOperation = pOp;
UpdateColorCorrection( );
}
//-----------------------------------------------------------------------------
// Main interface to the performance tools
//-----------------------------------------------------------------------------
static CColorCorrectionUIPanel *g_pColorCorrectionUI = NULL;
class CColorCorrectionTools : public IColorCorrectionTools
{
public:
virtual void Init( void );
virtual void Shutdown( void );
virtual void InstallColorCorrectionUI( vgui::Panel *parent );
virtual bool ShouldPause() const;
virtual void GrabPreColorCorrectedFrame( int x, int y, int width, int height );
virtual void UpdateColorCorrection( );
virtual void SetFinalOperation( IColorOperation *pOp );
private:
BGRA8888_t *m_pPreviewImage;
};
static CColorCorrectionTools g_ColorCorrectionTools;
IColorCorrectionTools *colorcorrectiontools = &g_ColorCorrectionTools;
void CColorCorrectionTools::Init( void )
{
if ( g_pColorCorrectionUI )
{
g_pColorCorrectionUI->Init();
}
m_pPreviewImage = new BGRA8888_t[ g_nPreviewImageWidth*g_nPreviewImageHeight ];
}
void CColorCorrectionTools::Shutdown( void )
{
if ( g_pColorCorrectionUI )
{
g_pColorCorrectionUI->Shutdown();
}
delete [] m_pPreviewImage;
}
void CColorCorrectionTools::InstallColorCorrectionUI( vgui::Panel *parent )
{
if ( g_pColorCorrectionUI )
return;
g_pColorCorrectionUI = new CColorCorrectionUIPanel( parent );
Assert( g_pColorCorrectionUI );
}
bool CColorCorrectionTools::ShouldPause() const
{
return false;
}
void CColorCorrectionTools::GrabPreColorCorrectedFrame( int x, int y, int width, int height )
{
if ( !g_pColorCorrectionUI->IsVisible() )
return;
CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
Rect_t srcRect;
srcRect.x = y; srcRect.y = y;
srcRect.width = width; srcRect.height = height;
Rect_t dstRect;
dstRect.x = 0;
dstRect.y = 0;
dstRect.width = g_nPreviewImageWidth;
dstRect.height = g_nPreviewImageHeight;
pRenderContext->ReadPixelsAndStretch( &srcRect, &dstRect, (unsigned char*)m_pPreviewImage, IMAGE_FORMAT_BGRX8888, g_nPreviewImageWidth*4 );
g_pColorCorrectionUI->ReadUncorrectedImage( &srcRect, (unsigned char *)m_pPreviewImage );
}
void CColorCorrectionTools::UpdateColorCorrection( )
{
g_pColorCorrectionUI->UpdateColorCorrection( );
}
void CColorCorrectionTools::SetFinalOperation( IColorOperation *pOp )
{
g_pColorCorrectionUI->SetFinalOperation( pOp );
}
void ShowHideColorCorrectionUI()
{
if ( !g_pColorCorrectionUI )
return;
bool bWasVisible = g_pColorCorrectionUI->IsVisible();
if ( bWasVisible )
{
// hide
g_pColorCorrectionUI->Close();
}
else
{
g_pColorCorrectionUI->Activate();
}
}
static ConCommand colorcorrectionui( "colorcorrectionui", ShowHideColorCorrectionUI, "Show/hide the color correction tools UI.", FCVAR_CHEAT );
void PrintColorCorrection()
{
ConMsg( "Default weight : %0.5f\n", colorcorrection->GetLookupWeight(-1) );
ConMsg( "Weight 0 : %0.5f\n", colorcorrection->GetLookupWeight(0) );
ConMsg( "Weight 1 : %0.5f\n", colorcorrection->GetLookupWeight(1) );
ConMsg( "Weight 2 : %0.5f\n", colorcorrection->GetLookupWeight(2) );
ConMsg( "Weight 3 : %0.5f\n", colorcorrection->GetLookupWeight(3) );
}
static ConCommand print_colorcorrection( "print_colorcorrection", PrintColorCorrection, "Display the color correction layer information.", FCVAR_CHEAT );