mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-05-04 17:10:47 +00:00
5232ee7626
* The old code in m_RangeMax/m_RangeMin setters wasn't doing proper epsilon enforcing so min=max could end up happening. It's easier to set both at once (this is all we care about anyway) so that it does properly make the min and max distinguishable.
528 lines
17 KiB
C#
528 lines
17 KiB
C#
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2014 Crytek
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
******************************************************************************/
|
|
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using renderdocui.Code;
|
|
|
|
namespace renderdocui.Controls
|
|
{
|
|
[Designer(typeof(System.Windows.Forms.Design.ControlDesigner))]
|
|
public partial class RangeHistogram : UserControl
|
|
{
|
|
public RangeHistogram()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
#region Events
|
|
|
|
private static readonly object RangeUpdatedEvent = new object();
|
|
public event EventHandler<RangeHistogramEventArgs> RangeUpdated
|
|
{
|
|
add { Events.AddHandler(RangeUpdatedEvent, value); }
|
|
remove { Events.RemoveHandler(RangeUpdatedEvent, value); }
|
|
}
|
|
protected virtual void OnRangeUpdated(RangeHistogramEventArgs e)
|
|
{
|
|
EventHandler<RangeHistogramEventArgs> handler = (EventHandler<RangeHistogramEventArgs>)Events[RangeUpdatedEvent];
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Privates
|
|
|
|
private float m_RangeMax = 1.0f;
|
|
private float m_RangeMin = 0.0f;
|
|
|
|
private float m_WhitePoint = 1.0f;
|
|
private float m_BlackPoint = 0.0f;
|
|
|
|
private float m_MinRangeSize = 0.001f;
|
|
|
|
private int m_Margin = 4;
|
|
private int m_Border = 1;
|
|
private int m_MarkerSize = 6;
|
|
|
|
#endregion
|
|
|
|
#region Code Properties
|
|
|
|
private uint[] m_HistogramData = null;
|
|
private float m_HistogramMin = 0.0f;
|
|
private float m_HistogramMax = 1.0f;
|
|
|
|
// sets the range of data where the histogram data was calculated.
|
|
public void SetHistogramRange(float min, float max)
|
|
{
|
|
m_HistogramMin = min;
|
|
m_HistogramMax = max;
|
|
}
|
|
|
|
// sets the minimum and maximum as well as the black and white points
|
|
public void SetRange(float min, float max)
|
|
{
|
|
m_RangeMin = min;
|
|
if (min < 0.0f)
|
|
m_RangeMax = Math.Max((min - float.Epsilon) * (1.0f - m_MinRangeSize), max);
|
|
else
|
|
m_RangeMax = Math.Max((min + float.Epsilon) * (1.0f + m_MinRangeSize), max);
|
|
|
|
m_BlackPoint = m_RangeMin;
|
|
m_WhitePoint = m_RangeMax;
|
|
|
|
Invalidate();
|
|
OnRangeUpdated(new RangeHistogramEventArgs(BlackPoint, WhitePoint));
|
|
}
|
|
|
|
public bool ValidRange
|
|
{
|
|
get
|
|
{
|
|
if (float.IsInfinity(m_WhitePoint) || float.IsNaN(m_WhitePoint) ||
|
|
float.IsInfinity(m_BlackPoint) || float.IsNaN(m_BlackPoint) ||
|
|
float.IsInfinity(m_RangeMax) || float.IsNaN(m_RangeMax) ||
|
|
float.IsInfinity(m_RangeMin) || float.IsNaN(m_RangeMin))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
[Browsable(false)]
|
|
public uint[] HistogramData
|
|
{
|
|
get
|
|
{
|
|
return m_HistogramData;
|
|
}
|
|
set
|
|
{
|
|
m_HistogramData = value;
|
|
Invalidate();
|
|
}
|
|
}
|
|
|
|
// black point and white point are the currently selected black/white points, within the
|
|
// minimum and maximum points. (ie. not 0 or 1)
|
|
[Browsable(false)]
|
|
public float BlackPoint
|
|
{
|
|
get
|
|
{
|
|
return m_BlackPoint;
|
|
}
|
|
set
|
|
{
|
|
if (value <= m_RangeMin)
|
|
m_BlackPoint = m_RangeMin = value;
|
|
else
|
|
m_BlackPoint = value;
|
|
|
|
Invalidate();
|
|
OnRangeUpdated(new RangeHistogramEventArgs(BlackPoint, WhitePoint));
|
|
}
|
|
}
|
|
[Browsable(false)]
|
|
public float WhitePoint
|
|
{
|
|
get
|
|
{
|
|
return m_WhitePoint;
|
|
}
|
|
set
|
|
{
|
|
if (value >= m_RangeMax)
|
|
m_WhitePoint = m_RangeMax = value;
|
|
else
|
|
m_WhitePoint = value;
|
|
|
|
Invalidate();
|
|
OnRangeUpdated(new RangeHistogramEventArgs(BlackPoint, WhitePoint));
|
|
}
|
|
}
|
|
|
|
// range min/max are the current minimum and maximum values that can be set
|
|
// for the black and white points
|
|
[Browsable(false)]
|
|
public float RangeMin
|
|
{
|
|
get
|
|
{
|
|
return m_RangeMin;
|
|
}
|
|
}
|
|
[Browsable(false)]
|
|
public float RangeMax
|
|
{
|
|
get
|
|
{
|
|
return m_RangeMax;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Designer Properties
|
|
|
|
[Description("The smallest possible range that can be selected"), Category("Behavior")]
|
|
[DefaultValue(typeof(float), "0.001")]
|
|
public float MinRangeSize
|
|
{
|
|
get { return m_MinRangeSize; }
|
|
set { m_MinRangeSize = Math.Max(0.000001f, value); }
|
|
}
|
|
|
|
[Description("The margin around the range display"), Category("Layout")]
|
|
[DefaultValue(typeof(int), "4")]
|
|
public int RangeMargin
|
|
{
|
|
get { return m_Margin; }
|
|
set { m_Margin = value; }
|
|
}
|
|
|
|
[Description("The pixel border around the range bar itself"), Category("Appearance")]
|
|
[DefaultValue(typeof(int), "1")]
|
|
public int Border
|
|
{
|
|
get { return m_Border; }
|
|
set { m_Border = value; }
|
|
}
|
|
|
|
[Description("The size in pixels of each marker"), Category("Appearance")]
|
|
[DefaultValue(typeof(int), "6")]
|
|
public int MarkerSize
|
|
{
|
|
get { return m_MarkerSize; }
|
|
set { m_MarkerSize = value; }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Internal Properties
|
|
|
|
private int m_TotalSpace { get { return m_Margin + m_Border; } }
|
|
private int m_RegionWidth { get { return this.Width - m_TotalSpace * 2; } }
|
|
|
|
// these are internal only, they give [0, 1] from minimum to maximum of where the black and white points are
|
|
private float m_BlackDelta
|
|
{
|
|
get
|
|
{
|
|
if (!ValidRange) return 0.0f;
|
|
return GetDelta(BlackPoint);
|
|
}
|
|
set
|
|
{
|
|
BlackPoint = Math.Min(WhitePoint - MinRangeSize, value * (RangeMax - RangeMin) + RangeMin);
|
|
}
|
|
}
|
|
private float m_WhiteDelta
|
|
{
|
|
get
|
|
{
|
|
if (!ValidRange) return 1.0f;
|
|
return GetDelta(WhitePoint);
|
|
}
|
|
set
|
|
{
|
|
WhitePoint = Math.Max(BlackPoint + MinRangeSize, value * (RangeMax - RangeMin) + RangeMin);
|
|
}
|
|
}
|
|
|
|
private float GetDelta(float val)
|
|
{
|
|
return (val - RangeMin) / (RangeMax - RangeMin);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Tooltips
|
|
|
|
private void ShowTooltips()
|
|
{
|
|
blackToolTip.Show(BlackPoint.ToString("F4"), this,
|
|
this.ClientRectangle.Left + (int)(this.ClientRectangle.Width * m_BlackDelta), this.ClientRectangle.Bottom);
|
|
|
|
whiteToolTip.Show(WhitePoint.ToString("F4"), this,
|
|
this.ClientRectangle.Left + (int)(this.ClientRectangle.Width * m_WhiteDelta), this.ClientRectangle.Top - 15);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Mouse Handlers
|
|
|
|
private Point m_mousePrev = new Point(-1, -1);
|
|
|
|
private enum DraggingMode
|
|
{
|
|
NONE,
|
|
WHITE,
|
|
BLACK,
|
|
}
|
|
private DraggingMode m_DragMode;
|
|
|
|
// This handler tries to figure out which handle (white or black) you were trying to
|
|
// grab when you clicked.
|
|
private void RangeHistogram_MouseDown(object sender, MouseEventArgs e)
|
|
{
|
|
if(e.Button != MouseButtons.Left || !ValidRange)
|
|
return;
|
|
|
|
Rectangle rect = this.ClientRectangle;
|
|
|
|
rect.Inflate(-m_TotalSpace, -m_TotalSpace);
|
|
|
|
int whiteX = (int)(m_WhiteDelta * rect.Width);
|
|
int blackX = (int)(m_BlackDelta * rect.Width);
|
|
|
|
var whiteVec = new PointF(whiteX - e.Location.X, ClientRectangle.Height - e.Location.Y);
|
|
var blackVec = new PointF(blackX-e.Location.X, e.Location.Y);
|
|
|
|
float whitedist = (float)Math.Sqrt(whiteVec.X * whiteVec.X + whiteVec.Y * whiteVec.Y);
|
|
float blackdist = (float)Math.Sqrt(blackVec.X * blackVec.X + blackVec.Y * blackVec.Y);
|
|
|
|
System.Diagnostics.Trace.WriteLine(string.Format("white {0} black {1}", whitedist, blackdist));
|
|
|
|
if (whitedist < blackdist && whitedist < 18.0f)
|
|
m_DragMode = DraggingMode.WHITE;
|
|
else if (blackdist < whitedist && blackdist < 18.0f)
|
|
m_DragMode = DraggingMode.BLACK;
|
|
else if (e.Location.X > whiteX)
|
|
m_DragMode = DraggingMode.WHITE;
|
|
else if (e.Location.X < blackX)
|
|
m_DragMode = DraggingMode.BLACK;
|
|
|
|
if (m_DragMode == DraggingMode.WHITE)
|
|
{
|
|
float newWhite = (float)(e.Location.X - m_TotalSpace) / (float)m_RegionWidth;
|
|
|
|
m_WhiteDelta = Math.Max(m_BlackDelta + m_MinRangeSize, Math.Min(1.0f, newWhite));
|
|
}
|
|
else if (m_DragMode == DraggingMode.BLACK)
|
|
{
|
|
float newBlack = (float)(e.Location.X - m_TotalSpace) / (float)m_RegionWidth;
|
|
|
|
m_BlackDelta = Math.Min(m_WhiteDelta - m_MinRangeSize, Math.Max(0.0f, newBlack));
|
|
}
|
|
|
|
OnRangeUpdated(new RangeHistogramEventArgs(BlackPoint, WhitePoint));
|
|
|
|
if (m_DragMode != DraggingMode.NONE)
|
|
{
|
|
this.Invalidate();
|
|
this.Update();
|
|
}
|
|
|
|
m_mousePrev.X = e.X;
|
|
m_mousePrev.Y = e.Y;
|
|
}
|
|
|
|
private void RangeHistogram_MouseUp(object sender, MouseEventArgs e)
|
|
{
|
|
whiteToolTip.Hide(this);
|
|
blackToolTip.Hide(this);
|
|
|
|
m_DragMode = DraggingMode.NONE;
|
|
|
|
m_mousePrev.X = m_mousePrev.Y = -1;
|
|
}
|
|
|
|
private void RangeHistogram_MouseMove(object sender, MouseEventArgs e)
|
|
{
|
|
if (ValidRange && e.Button == MouseButtons.Left && (e.X != m_mousePrev.X || e.Y != m_mousePrev.Y))
|
|
{
|
|
if (m_DragMode == DraggingMode.WHITE)
|
|
{
|
|
float newWhite = (float)(e.Location.X - m_TotalSpace) / (float)m_RegionWidth;
|
|
|
|
m_WhiteDelta = Math.Max(m_BlackDelta + m_MinRangeSize, Math.Min(1.0f, newWhite));
|
|
}
|
|
else if (m_DragMode == DraggingMode.BLACK)
|
|
{
|
|
float newBlack = (float)(e.Location.X - m_TotalSpace) / (float)m_RegionWidth;
|
|
|
|
m_BlackDelta = Math.Min(m_WhiteDelta - m_MinRangeSize, Math.Max(0.0f, newBlack));
|
|
}
|
|
|
|
OnRangeUpdated(new RangeHistogramEventArgs(BlackPoint, WhitePoint));
|
|
|
|
if (m_DragMode != DraggingMode.NONE)
|
|
{
|
|
this.Invalidate();
|
|
this.Update();
|
|
}
|
|
|
|
m_mousePrev.X = e.X;
|
|
m_mousePrev.Y = e.Y;
|
|
|
|
ShowTooltips();
|
|
}
|
|
}
|
|
|
|
private void RangeHistogram_MouseLeave(object sender, EventArgs e)
|
|
{
|
|
whiteToolTip.Hide(this);
|
|
blackToolTip.Hide(this);
|
|
}
|
|
|
|
private void RangeHistogram_MouseEnter(object sender, EventArgs e)
|
|
{
|
|
ShowTooltips();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Other Handlers
|
|
|
|
private void RangeHistogram_Paint(object sender, PaintEventArgs e)
|
|
{
|
|
Rectangle rect = this.ClientRectangle;
|
|
|
|
e.Graphics.FillRectangle(SystemBrushes.Control, rect);
|
|
|
|
rect.Inflate(-m_Margin, -m_Margin);
|
|
|
|
e.Graphics.FillRectangle(Brushes.Black, rect);
|
|
|
|
rect.Inflate(-m_Border, -m_Border);
|
|
|
|
e.Graphics.FillRectangle(ValidRange ? Brushes.DarkGray : Brushes.DarkRed, rect);
|
|
|
|
int whiteX = (int)(m_WhiteDelta * rect.Width);
|
|
int blackX = (int)(m_BlackDelta * rect.Width);
|
|
|
|
Rectangle blackPoint = new Rectangle(rect.Left, rect.Top, blackX, rect.Height);
|
|
Rectangle whitePoint = new Rectangle(rect.Left + whiteX, rect.Top, rect.Width - whiteX, rect.Height);
|
|
|
|
e.Graphics.FillRectangle(Brushes.White, whitePoint);
|
|
e.Graphics.FillRectangle(Brushes.Black, blackPoint);
|
|
|
|
if (!ValidRange)
|
|
return;
|
|
|
|
if (HistogramData != null)
|
|
{
|
|
float minx = GetDelta(m_HistogramMin);
|
|
float maxx = GetDelta(m_HistogramMax);
|
|
|
|
UInt32 maxval = UInt32.MinValue;
|
|
for (int i = 0; i < HistogramData.Length; i++)
|
|
{
|
|
float x = (float)i / (float)HistogramData.Length;
|
|
|
|
float xdelta = minx + x * (maxx - minx);
|
|
|
|
if (xdelta >= 0.0f && xdelta <= 1.0f)
|
|
{
|
|
maxval = Math.Max(maxval, HistogramData[i]);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < HistogramData.Length; i++)
|
|
{
|
|
float x = (float)i / (float)HistogramData.Length;
|
|
float y = (float)HistogramData[i] / (float)maxval;
|
|
|
|
float xdelta = minx + x * (maxx - minx);
|
|
|
|
if (xdelta >= 0.0f && xdelta <= 1.0f)
|
|
{
|
|
float segwidth = Math.Max(rect.Width * (maxx - minx) / (float)HistogramData.Length, 1);
|
|
|
|
RectangleF barRect = new RectangleF(new PointF(rect.Left + rect.Width * (minx + x * (maxx - minx)), rect.Bottom - rect.Height * y),
|
|
new SizeF(segwidth, rect.Height * y));
|
|
|
|
e.Graphics.FillRectangle(Brushes.Green, barRect);
|
|
}
|
|
}
|
|
}
|
|
|
|
Point[] blackTriangle = { new Point(blackPoint.Right, m_MarkerSize*2),
|
|
new Point(blackPoint.Right+m_MarkerSize, 0),
|
|
new Point(blackPoint.Right-m_MarkerSize, 0) };
|
|
|
|
e.Graphics.FillPolygon(Brushes.DarkGray, blackTriangle);
|
|
|
|
Point[] whiteTriangle = { new Point(whitePoint.Left, whitePoint.Bottom-m_MarkerSize*2+m_Margin),
|
|
new Point(whitePoint.Left+m_MarkerSize, whitePoint.Bottom+m_Margin),
|
|
new Point(whitePoint.Left-m_MarkerSize, whitePoint.Bottom+m_Margin) };
|
|
|
|
e.Graphics.FillPolygon(Brushes.DarkGray, whiteTriangle);
|
|
|
|
blackTriangle[0].Y -= 2;
|
|
blackTriangle[1].Y += 1;
|
|
blackTriangle[2].Y += 1;
|
|
|
|
blackTriangle[1].X -= 2;
|
|
blackTriangle[2].X += 2;
|
|
|
|
whiteTriangle[0].Y += 2;
|
|
whiteTriangle[1].Y -= 1;
|
|
whiteTriangle[2].Y -= 1;
|
|
|
|
whiteTriangle[1].X -= 2;
|
|
whiteTriangle[2].X += 2;
|
|
|
|
e.Graphics.FillPolygon(Brushes.Black, blackTriangle);
|
|
e.Graphics.FillPolygon(Brushes.White, whiteTriangle);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
public class RangeHistogramEventArgs : EventArgs
|
|
{
|
|
private float m_black, m_white;
|
|
|
|
public RangeHistogramEventArgs(float black, float white)
|
|
{
|
|
m_black = black;
|
|
m_white = white;
|
|
}
|
|
|
|
public float BlackPoint
|
|
{
|
|
get { return m_black; }
|
|
}
|
|
|
|
public float WhitePoint
|
|
{
|
|
get { return m_white; }
|
|
}
|
|
}
|
|
|
|
}
|