/*=============================================================================
Project: SharpImage
Module: siTransferFunctionPartGradient.cs
Language: C#
Author: Dan Mueller
Date: $Date: 2007-07-06 10:57:00 +1000 (Fri, 06 Jul 2007) $
Revision: $Revision: 2 $
Copyright (c) Queensland University of Technology (QUT) 2007.
All rights reserved.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notices for more information.
=============================================================================*/
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Diagnostics;
namespace SharpImage.Rendering
{
public class siTransferFunctionPartGradient : siTransferFunctionPartRectangle
{
#region Constants
//=====================================================================
protected static readonly Color DEFAULT_COLOR1 = Color.Red;
protected static readonly Color DEFAULT_COLOR2 = Color.White;
//=====================================================================
#endregion
#region Construction and Disposal
//=====================================================================
///
/// Default constructor. This part will be drawn on all layers.
///
///
public siTransferFunctionPartGradient() : this(null, null)
{
}
///
/// Constructor taking a layer key.
///
/// Uses the parent.Image for creating a default sized part.
/// The name of layer this part is to be drawn on.
public siTransferFunctionPartGradient(siTransferFunction parent, string layer)
: base(parent, layer)
{
}
//=====================================================================
#endregion
#region Properties
//=====================================================================
#region BackColor
//=====================================================================
///
/// Get/set the RGB components of the back color.
///
[System.ComponentModel.Browsable(false)]
[System.ComponentModel.Category("Color")]
[System.Xml.Serialization.XmlIgnore]
public override Color BackColorRGB
{
get { return Color.FromArgb(base.BackColor.R, base.BackColor.G, base.BackColor.B); }
set { base.BackColor = Color.FromArgb(base.BackColor.A, value); this.RaiseModified(); }
}
///
/// Get/set the Alpha component of the back color.
///
[System.ComponentModel.Browsable(false)]
[System.ComponentModel.Category("Color")]
[System.Xml.Serialization.XmlIgnore]
public override int BackColorAlpha
{
get { return base.BackColor.A; }
set { base.BackColor = Color.FromArgb(value, base.BackColor); this.RaiseModified(); }
}
///
/// This property is needed for serialization.
/// It is not intended for general use.
///
[System.ComponentModel.Browsable(false)]
[System.ComponentModel.Category("Color")]
[System.Xml.Serialization.XmlIgnore]
public override string SerializableBackColor
{
get { return siColorHelper.SerializeColor(base.BackColor); }
set { base.BackColor = siColorHelper.DeserializeColor(value); this.RaiseModified(); }
}
//=====================================================================
#endregion
#region Colors
//=====================================================================
private int m_NumberOfColors = 3;
private Color[] m_Colors = new Color[] { DEFAULT_COLOR1, DEFAULT_COLOR2, DEFAULT_COLOR2 };
private int[] m_Alphas = new int[] { 255, 255, 255 };
[System.ComponentModel.Browsable(true)]
[System.ComponentModel.Category("Color")]
public int NumberOfColors
{
get { return this.m_NumberOfColors; }
set
{
if (value < 3) value = 3;
this.m_NumberOfColors = value;
// Save the old values
Color[] oldColors = this.m_Colors;
int[] oldAlphas = this.m_Alphas;
// Create new empty values
this.m_Colors = new Color[value];
this.m_Alphas = new int[value];
this.m_Positions = new float[value];
// Populate new values
for (int i = 0; i < value; i++)
{
this.m_Colors[i] = (oldColors.Length > i) ? oldColors[i] : DEFAULT_COLOR2;
this.m_Alphas[i] = (oldAlphas.Length > i) ? oldAlphas[i] : 255;
this.m_Positions[i] = (float)i*(1.0f / (this.NumberOfColors - 1));
}
// Enforce some positions values
this.m_Positions[0] = 0.0f;
this.m_Positions[value - 1] = 1.0f;
if (value == 3) this.m_Positions[1] = 1.0f;
// Raise modified
this.RaiseModified();
}
}
///
/// The colours in the gradient.
///
[System.ComponentModel.Browsable(true)]
[System.ComponentModel.Category("Color")]
[System.Xml.Serialization.XmlIgnore]
public Color[] Colors
{
get { return this.m_Colors; }
}
///
/// This property is needed for serialization.
/// It is not intended for general use.
///
[System.ComponentModel.Browsable(false)]
public String SerializableColors
{
get
{
String result = String.Empty;
foreach (Color color in this.Colors)
result += siColorHelper.SerializeColor(color) + "; ";
return result.TrimEnd(';', ' ');
}
set
{
String[] split = value.Split(new char[] { ';' });
this.m_Colors = new Color[split.Length];
for (int i = 0; i < split.Length; i++)
this.m_Colors[i] = siColorHelper.DeserializeColor(split[i]);
}
}
///
/// The alpha values in the gradient.
///
[System.ComponentModel.Browsable(true)]
[System.ComponentModel.Category("Color")]
[System.Xml.Serialization.XmlIgnore]
public int[] Alphas
{
get { return m_Alphas; }
}
///
/// This property is needed for serialization.
/// It is not intended for general use.
///
[System.ComponentModel.Browsable(false)]
public String SerializableAlphas
{
get
{
String result = String.Empty;
foreach (int alpha in this.Alphas)
result += alpha.ToString() + "; ";
return result.TrimEnd(';', ' ');
}
set
{
String[] split = value.Split(new char[] { ';' });
this.m_Alphas = new int[split.Length];
for (int i = 0; i < split.Length; i++)
this.m_Alphas[i] = Int32.Parse(split[i]);
}
}
//=====================================================================
#endregion
#region Positions
//=====================================================================
private float[] m_Positions = new float[] { 0.0f, 1.0f, 1.0f };
///
/// The blend positions for the gradient.
///
[System.ComponentModel.Browsable(false)]
[System.ComponentModel.Category("Color")]
[System.Xml.Serialization.XmlIgnore]
public float[] Positions
{
get { return m_Positions; }
}
///
/// This property is needed for serialization.
/// It is not intended for general use.
///
[System.ComponentModel.Browsable(false)]
public String SerializablePositions
{
get
{
String result = String.Empty;
foreach (float position in this.Positions)
result += position.ToString() + "; ";
return result.TrimEnd(';', ' ');
}
set
{
String[] split = value.Split(new char[] { ';' });
this.m_Positions = new float[split.Length];
for (int i = 0; i < split.Length; i++)
this.m_Positions[i] = Single.Parse(split[i]);
}
}
//=====================================================================
#endregion
#region GradientMode
//============================================================================
private LinearGradientMode m_GradientMode;
///
/// Gets/sets the mode for drawing the gradient.
///
[System.ComponentModel.Browsable(true)]
[System.ComponentModel.Category("Color")]
public LinearGradientMode GradientMode
{
get { return this.m_GradientMode; }
set
{
this.m_GradientMode = value;
this.RaiseModified();
}
}
//============================================================================
#endregion
#region ControlPoints
//=====================================================================
///
/// Get the control points. This property accessor builds the list
/// from private member information each time it is called.
///
[System.ComponentModel.Browsable(false)]
[System.Xml.Serialization.XmlIgnore]
public override RectangleF[] ControlPoints
{
get
{
RectangleF[] result = new RectangleF[4 + (this.NumberOfColors - 2)];
RectangleF[] baseResult = base.ControlPoints;
for (int i = 0; i< 4; i++)
result[i] = baseResult[i];
for (int j=0; j < (this.NumberOfColors - 2); j++)
result[4 + j] = new RectangleF(this.BoundingBoxX + this.Positions[j + 1] * this.BoundingBoxWidth - CONTROL_POINT_WIDTH / 2.0f, this.BoundingBoxY, CONTROL_POINT_WIDTH, this.BoundingBoxHeight);
return result;
}
}
//=====================================================================
#endregion
//=====================================================================
#endregion
#region Public Methods
//=====================================================================
public override string ToString()
{
return "Gradient: " + this.Layer + " " + this.BoundingBox.ToString();
}
//=====================================================================
#endregion
#region Paint Methods
//=====================================================================
protected override void OnPaintControlPoints(siTransferFunction parent, siTransferFunctionPaintEventArgs e)
{
// Set smoothing mode to none
SmoothingMode oldSmoothingMode = e.Graphics.SmoothingMode;
e.Graphics.SmoothingMode = SmoothingMode.None;
// Paint the gradient controls
Pen penGradient = new Pen(Color.FromArgb(100, Color.Gray), 1.0f);
RectangleF[] cpts = this.ControlPoints;
const float HALF_WIDTH = CONTROL_POINT_WIDTH/2.0f;
for (int i = 4; i < cpts.Length; i++)
{
e.Graphics.DrawLine(penGradient, cpts[i].X + HALF_WIDTH, cpts[i].Y, cpts[i].X + HALF_WIDTH, cpts[i].Y + cpts[i].Height);
}
// Paint the corners
base.OnPaintControlPoints(parent, e);
// Reset smoothing mode
e.Graphics.SmoothingMode = oldSmoothingMode;
}
protected override void OnPaintFillFunction(siTransferFunction parent, siTransferFunctionPaintEventArgs e)
{
// Create the brush
RectangleF rectangleFill = new RectangleF(this.BoundingBoxX-1, this.BoundingBoxY-1, this.BoundingBoxWidth+1, this.BoundingBoxHeight+1);
LinearGradientBrush brushFill = new LinearGradientBrush(rectangleFill, Colors[0], Colors[1], this.GradientMode);
// Set the color blend
Color[] colors = new Color[this.NumberOfColors];
for (int i=0; i < this.NumberOfColors; i++)
colors[i] = Color.FromArgb(this.Alphas[i], this.Colors[i]);
ColorBlend colorblend = new ColorBlend(this.NumberOfColors);
colorblend.Colors = colors;
colorblend.Positions = this.Positions;
brushFill.InterpolationColors = colorblend;
// Fill the part
e.Graphics.FillRectangle(brushFill, this.BoundingBox);
}
//=====================================================================
#endregion
#region Mouse Methods
//=====================================================================
///
/// Get the cursor for the given control point index.
///
///
///
protected override Cursor GetControlPointCursor(int cpIndex)
{
if (cpIndex >= 4)
return Cursors.SizeWE;
else
return base.GetControlPointCursor(cpIndex);
}
//=====================================================================
#endregion
#region Translate and Resize Methods
//=====================================================================
///
/// Resize the part by the given amount.
///
///
public override void Resize(siTransferFunction parent, PointF change)
{
// Dispatch the resize for the rectangle
int cindex = (int)this.Metadata["CurrentControlPointIndex"];
if (cindex < 4)
{
base.Resize(parent, change);
}
else
{
this.Positions[cindex - 3] += (change.X / this.BoundingBoxWidth);
if (this.Positions[cindex - 3] < 0.0) this.Positions[cindex - 3] = -0.0001f;
if (this.Positions[cindex - 3] > 1.0) this.Positions[cindex - 3] = 1.0f;
this.RaiseModified();
}
}
//=====================================================================
#endregion
}
}