using System;
using System.Xml;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Diagnostics;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using SharpVectors.Dom.Svg;
using SharpVectors.Dom.Css;
namespace SharpVectors.Renderers.Gdi
{
///
/// Summary description for PaintServer.
///
public sealed class GdiGradientFill : GdiFill
{
#region Private Fields
private SvgGradientElement _gradientElement;
#endregion
#region Constructors and Destructor
public GdiGradientFill(SvgGradientElement gradientElement)
{
_gradientElement = gradientElement;
}
#endregion
#region Public Methods
public override Brush GetBrush(RectangleF bounds)
{
SvgLinearGradientElement linearGradient = _gradientElement as SvgLinearGradientElement;
if (linearGradient != null)
{
return GetLinearGradientBrush(linearGradient, bounds);
}
SvgRadialGradientElement radialGradient = _gradientElement as SvgRadialGradientElement;
if (radialGradient != null)
{
return GetRadialGradientBrush(radialGradient, bounds);
}
return new SolidBrush(Color.Black);
}
public Region GetRadialGradientRegion(RectangleF bounds)
{
SvgRadialGradientElement res = _gradientElement as SvgRadialGradientElement;
if (_gradientElement == null)
{
return null;
}
float fCenterX = (float)res.Cx.AnimVal.Value;
float fCenterY = (float)res.Cy.AnimVal.Value;
float fFocusX = (float)res.Fx.AnimVal.Value;
float fFocusY = (float)res.Fy.AnimVal.Value;
float fRadius = (float)res.R.AnimVal.Value;
float fEffectiveCX = fCenterX;
float fEffectiveCY = fCenterY;
float fEffectiveFX = fFocusX;
float fEffectiveFY = fFocusY;
float fEffectiveRadiusX = fRadius;
float fEffectiveRadiusY = fRadius;
if (res.GradientUnits.AnimVal.Equals(SvgUnitType.ObjectBoundingBox))
{
fEffectiveCX = bounds.Left + fCenterX * (bounds.Width);
fEffectiveCY = bounds.Top + fCenterY * (bounds.Height);
fEffectiveFX = bounds.Left + fFocusX * (bounds.Width);
fEffectiveFY = bounds.Top + fFocusY * (bounds.Height);
fEffectiveRadiusX = fRadius * bounds.Width;
fEffectiveRadiusY = fRadius * bounds.Height;
}
GraphicsPath gp2 = new GraphicsPath();
gp2.AddEllipse(fEffectiveCX - fEffectiveRadiusX, fEffectiveCY - fEffectiveRadiusY, 2 * fEffectiveRadiusX, 2 * fEffectiveRadiusY);
return new Region(gp2);
}
#endregion
#region Private Methods
private List GetColors(XmlNodeList stops)
{
List colors = new List(stops.Count);
for (int i = 0; i < stops.Count; i++)
{
SvgStopElement stop = (SvgStopElement)stops.Item(i);
string prop = stop.GetPropertyValue("stop-color");
GdiSvgColor svgColor = new GdiSvgColor(stop, "stop-color");
colors.Add(svgColor.Color);
}
return colors;
}
private List GetPositions(XmlNodeList stops)
{
List positions = new List(stops.Count);
float lastPos = 0;
for (int i = 0; i < stops.Count; i++)
{
SvgStopElement stop = (SvgStopElement)stops.Item(i);
float pos = (float)stop.Offset.AnimVal;
pos /= 100;
pos = Math.Max(lastPos, pos);
positions.Add(pos);
lastPos = pos;
}
return positions;
}
private void GetCorrectPositions(List positions, List colors)
{
if (positions.Count > 0)
{
float firstPos = positions[0];
if (firstPos > 0F)
{
positions.Insert(0, 0F);
colors.Insert(0, colors[0]);
}
float lastPos = positions[positions.Count - 1];
if (lastPos < 1F)
{
positions.Add(1F);
colors.Add(colors[colors.Count - 1]);
}
}
}
private void GetColorsAndPositions(XmlNodeList stops, List positions, List colors)
{
List alColors = GetColors(stops);
List alPositions = GetPositions(stops);
if (alPositions.Count > 0)
{
GetCorrectPositions(alPositions, alColors);
colors.AddRange(alColors);
positions.AddRange(alPositions);
//colors = alColors.ToArray();
//positions = alPositions.ToArray();
}
else
{
//colors = new Color[2];
//colors[0] = Color.Black;
//colors[1] = Color.Black;
colors.Add(Color.Black);
colors.Add(Color.Black);
//positions = new float[2];
//positions[0] = 0;
//positions[1] = 1;
positions.Add(0);
positions.Add(1);
}
}
private LinearGradientBrush GetLinearGradientBrush(SvgLinearGradientElement res, RectangleF bounds)
{
float fLeft = (float)res.X1.AnimVal.Value;
float fRight = (float)res.X2.AnimVal.Value;
float fTop = (float)res.Y1.AnimVal.Value;
float fBottom = (float)res.Y2.AnimVal.Value;
bool bForceUserSpaceOnUse = (fLeft > 1 || fRight > 1 || fTop > 1 || fBottom > 1);
float fEffectiveLeft = fLeft;
float fEffectiveRight = fRight;
float fEffectiveTop = fTop;
float fEffectiveBottom = fBottom;
if (res.GradientUnits.AnimVal.Equals((ushort)SvgUnitType.ObjectBoundingBox) && !bForceUserSpaceOnUse)
{
if (res.SpreadMethod.AnimVal.Equals((ushort)SvgSpreadMethod.Pad))
{
fEffectiveRight = bounds.Right;
fEffectiveLeft = bounds.Left;
}
else
{
fEffectiveLeft = bounds.Left + fLeft * (bounds.Width);
fEffectiveRight = bounds.Left + fRight * (bounds.Width);
}
fEffectiveTop = bounds.Top + fTop * (bounds.Height);
fEffectiveBottom = bounds.Top + fBottom * (bounds.Height);
}
LinearGradientMode mode = LinearGradientMode.Horizontal;
if (fTop == fBottom)
{
mode = LinearGradientMode.Horizontal;
}
else
{
if (fLeft == fRight)
{
mode = LinearGradientMode.Vertical;
}
else
{
if (fLeft < fRight)
mode = LinearGradientMode.ForwardDiagonal;
else
mode = LinearGradientMode.BackwardDiagonal;
}
}
float fEffectiveWidth = fEffectiveRight - fEffectiveLeft;
if (fEffectiveWidth <= 0)
fEffectiveWidth = bounds.Width;
float fEffectiveHeight = fEffectiveBottom - fEffectiveTop;
if (fEffectiveHeight <= 0)
fEffectiveHeight = bounds.Height;
LinearGradientBrush brush = new LinearGradientBrush(new RectangleF(fEffectiveLeft - 1,
fEffectiveTop - 1, fEffectiveWidth + 2, fEffectiveHeight + 2),
Color.White, Color.White, mode);
XmlNodeList stops = res.Stops;
ColorBlend cb = new ColorBlend();
List adjcolors = new List();
List adjpositions = new List();
GetColorsAndPositions(stops, adjpositions, adjcolors);
if (res.GradientUnits.AnimVal.Equals((ushort)SvgUnitType.ObjectBoundingBox) && !bForceUserSpaceOnUse)
{
if (res.SpreadMethod.AnimVal.Equals((ushort)SvgSpreadMethod.Pad))
{
for (int i = 0; i < adjpositions.Count; i++)
{
if (fLeft == fRight)
adjpositions[i] = fTop + adjpositions[i] * (fBottom - fTop);
else
adjpositions[i] = fLeft + adjpositions[i] * (fRight - fLeft);
}
// this code corrects the values again... fix
int nSize = adjcolors.Count;
if (adjpositions[0] > 0.0)
++nSize;
if (adjpositions[adjcolors.Count - 1] < 1)
++nSize;
Color[] readjcolors = new Color[nSize];
float[] readjpositions = new float[nSize];
if (adjpositions[0] > 0.0)
{
adjpositions.CopyTo(readjpositions, 1);
adjcolors.CopyTo(readjcolors, 1);
readjcolors[0] = readjcolors[1];
readjpositions[0] = 0;
}
else
{
adjpositions.CopyTo(readjpositions, 0);
adjcolors.CopyTo(readjcolors, 0);
}
if (adjpositions[adjcolors.Count - 1] < 1)
{
readjcolors[nSize - 1] = readjcolors[nSize - 2];
readjpositions[nSize - 1] = 1;
}
cb.Colors = readjcolors;
cb.Positions = readjpositions;
}
else
{
cb.Colors = adjcolors.ToArray();
cb.Positions = adjpositions.ToArray();
}
}
else
{
cb.Colors = adjcolors.ToArray();
cb.Positions = adjpositions.ToArray();
}
brush.InterpolationColors = cb;
if (res.SpreadMethod.AnimVal.Equals((ushort)SvgSpreadMethod.Reflect))
{
brush.WrapMode = WrapMode.TileFlipXY;
}
else if (res.SpreadMethod.AnimVal.Equals((ushort)SvgSpreadMethod.Repeat))
{
brush.WrapMode = WrapMode.Tile;
}
else if (res.SpreadMethod.AnimVal.Equals((ushort)SvgSpreadMethod.Pad))
{
brush.WrapMode = WrapMode.Tile;
}
brush.Transform = GetTransformMatrix(res);
if (res.GetPropertyValue("color-interpolation") == "linearRGB")
{
brush.GammaCorrection = true;
}
else
{
brush.GammaCorrection = false;
}
return brush;
}
private Matrix GetTransformMatrix(SvgGradientElement gradientElement)
{
SvgMatrix svgMatrix = ((SvgTransformList)gradientElement.GradientTransform.AnimVal).TotalMatrix;
Matrix transformMatrix = new Matrix((float)svgMatrix.A, (float)svgMatrix.B, (float)svgMatrix.C,
(float)svgMatrix.D, (float)svgMatrix.E, (float)svgMatrix.F);
return transformMatrix;
}
private PathGradientBrush GetRadialGradientBrush(SvgRadialGradientElement res, RectangleF bounds)
{
float fCenterX = (float)res.Cx.AnimVal.Value;
float fCenterY = (float)res.Cy.AnimVal.Value;
float fFocusX = (float)res.Fx.AnimVal.Value;
float fFocusY = (float)res.Fy.AnimVal.Value;
float fRadius = (float)res.R.AnimVal.Value;
float fEffectiveCX = fCenterX;
float fEffectiveCY = fCenterY;
float fEffectiveFX = fFocusX;
float fEffectiveFY = fFocusY;
float fEffectiveRadiusX = fRadius;
float fEffectiveRadiusY = fRadius;
if (res.GradientUnits.AnimVal.Equals(SvgUnitType.ObjectBoundingBox))
{
fEffectiveCX = bounds.Left + fCenterX * (bounds.Width);
fEffectiveCY = bounds.Top + fCenterY * (bounds.Height);
fEffectiveFX = bounds.Left + fFocusX * (bounds.Width);
fEffectiveFY = bounds.Top + fFocusY * (bounds.Height);
fEffectiveRadiusX = fRadius * bounds.Width;
fEffectiveRadiusY = fRadius * bounds.Height;
}
GraphicsPath gp = new GraphicsPath();
gp.AddEllipse(fEffectiveCX - fEffectiveRadiusX,
fEffectiveCY - fEffectiveRadiusY, 2 * fEffectiveRadiusX, 2 * fEffectiveRadiusY);
PathGradientBrush brush = new PathGradientBrush(gp);
brush.CenterPoint = new PointF(fEffectiveFX, fEffectiveFY);
XmlNodeList stops = res.Stops;
ColorBlend cb = new ColorBlend();
List adjcolors = new List();
List adjpositions = new List();
GetColorsAndPositions(stops, adjpositions, adjcolors);
// Need to invert the colors for some bizarre reason
adjcolors.Reverse();
adjpositions.Reverse();
for (int i = 0; i < adjpositions.Count; i++)
{
adjpositions[i] = 1 - adjpositions[i];
}
cb.Colors = adjcolors.ToArray();
cb.Positions = adjpositions.ToArray();
brush.InterpolationColors = cb;
// ISvgTransformable transElm = (ISvgTransformable)res;
// SvgTransformList svgTList = (SvgTransformList)transElm.transform.AnimVal;
// brush.Transform = svgTList.matrix.matrix;
if (res.GetPropertyValue("color-interpolation") == "linearRGB")
{
//GdipSetPathGradientGammaCorrection(brush, true);
}
else
{
//GdipSetPathGradientGammaCorrection(brush, false);
}
/*
* How to do brush.GammaCorrection = true on a PathGradientBrush? / nikgus
* */
return brush;
}
#endregion
}
}