Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ImportedProjects
SVG
Commits
2462bf13
Commit
2462bf13
authored
Jul 15, 2014
by
Tebjan Halm
Browse files
Merge pull request #85 from articulate/GradientTransform
Gradient Improvements
parents
e71c9d97
3987f281
Changes
12
Show whitespace changes
Inline
Side-by-side
Source/Basic Shapes/SvgVisualElement.cs
View file @
2462bf13
using
System
;
using
System.Collections.Generic
;
using
System.Text
;
using
System.Xml
;
using
System.Drawing
;
using
System.Drawing.Drawing2D
;
using
System.Xml.Serialization
;
using
System.ComponentModel
;
using
System.Diagnostics
;
namespace
Svg
{
/// <summary>
/// The class that all SVG elements should derive from when they are to be rendered.
/// </summary>
public
abstract
partial
class
SvgVisualElement
:
SvgElement
,
ISvgStylable
,
ISvgClipable
public
abstract
partial
class
SvgVisualElement
:
SvgElement
,
ISvgBoundable
,
ISvgStylable
,
ISvgClipable
{
private
bool
_dirty
;
private
bool
_requiresSmoothRendering
;
...
...
@@ -23,6 +17,23 @@ namespace Svg
/// Gets the <see cref="GraphicsPath"/> for this element.
/// </summary>
public
abstract
GraphicsPath
Path
{
get
;
protected
set
;
}
PointF
ISvgBoundable
.
Location
{
get
{
return
Bounds
.
Location
;
}
}
SizeF
ISvgBoundable
.
Size
{
get
{
return
Bounds
.
Size
;
}
}
/// <summary>
/// Gets the bounds of the element.
/// </summary>
...
...
Source/DataTypes/SvgUnit.cs
View file @
2462bf13
using
System
;
using
System.Collections.Generic
;
using
System.Text
;
using
System.ComponentModel
;
using
System.Web.UI.WebControls
;
using
System.Globalization
;
namespace
Svg
...
...
@@ -73,16 +70,16 @@ namespace Svg
/// Converts the current unit to one that can be used at render time.
/// </summary>
/// <returns>The representation of the current unit in a device value (usually pixels).</returns>
public
float
ToDeviceValue
(
ISvg
Stylable
styleOwner
)
public
float
ToDeviceValue
(
ISvg
Boundable
boundable
)
{
return
this
.
ToDeviceValue
(
styleOwner
,
false
);
return
this
.
ToDeviceValue
(
boundable
,
false
);
}
/// <summary>
/// Converts the current unit to one that can be used at render time.
/// </summary>
/// <returns>The representation of the current unit in a device value (usually pixels).</returns>
public
float
ToDeviceValue
(
ISvg
Stylable
styleOwner
,
bool
vertical
)
public
float
ToDeviceValue
(
ISvg
Boundable
boundable
,
bool
vertical
)
{
// If it's already been calculated
if
(
this
.
_deviceValue
.
HasValue
)
...
...
@@ -131,14 +128,14 @@ namespace Svg
break
;
case
SvgUnitType
.
Percentage
:
// Can't calculate if there is no style owner
if
(
styleOwner
==
null
)
if
(
boundable
==
null
)
{
_deviceValue
=
this
.
Value
;
break
;
}
// TODO : Support height percentages
System
.
Drawing
.
Rectangl
eF
size
=
styleOwner
.
Bounds
;
System
.
Drawing
.
Siz
eF
size
=
boundable
.
Bounds
.
Size
;
_deviceValue
=
(((
vertical
)
?
size
.
Height
:
size
.
Width
)
/
100
)
*
this
.
Value
;
break
;
default
:
...
...
Source/Document Structure/SvgFragment.cs
View file @
2462bf13
using
System
;
using
System.Collections.Generic
;
using
System.Text
;
using
System.Xml
;
using
System.Drawing.Drawing2D
;
using
System.Drawing
;
using
System.
ComponentModel
;
using
System.
Drawing.Drawing2D
;
namespace
Svg
{
...
...
@@ -12,13 +8,37 @@ namespace Svg
/// An <see cref="SvgFragment"/> represents an SVG fragment that can be the root element or an embedded fragment of an SVG document.
/// </summary>
[
SvgElement
(
"svg"
)]
public
class
SvgFragment
:
SvgElement
,
ISvgViewPort
public
class
SvgFragment
:
SvgElement
,
ISvgViewPort
,
ISvgBoundable
{
/// <summary>
/// Gets the SVG namespace string.
/// </summary>
public
static
readonly
Uri
Namespace
=
new
Uri
(
"http://www.w3.org/2000/svg"
);
PointF
ISvgBoundable
.
Location
{
get
{
return
PointF
.
Empty
;
}
}
SizeF
ISvgBoundable
.
Size
{
get
{
return
GetDimensions
();
}
}
RectangleF
ISvgBoundable
.
Bounds
{
get
{
return
new
RectangleF
(((
ISvgBoundable
)
this
).
Location
,
((
ISvgBoundable
)
this
).
Size
);
}
}
private
SvgUnit
_x
;
private
SvgUnit
_y
;
...
...
@@ -117,8 +137,8 @@ namespace Svg
if
(!
this
.
ViewBox
.
Equals
(
SvgViewBox
.
Empty
))
{
float
fScaleX
=
this
.
Width
.
ToDeviceValue
()
/
this
.
ViewBox
.
Width
;
float
fScaleY
=
this
.
Height
.
ToDeviceValue
()
/
this
.
ViewBox
.
Height
;
float
fScaleX
=
this
.
Width
.
ToDeviceValue
(
this
,
false
)
/
this
.
ViewBox
.
Width
;
float
fScaleY
=
this
.
Height
.
ToDeviceValue
(
this
,
true
)
/
this
.
ViewBox
.
Height
;
float
fMinX
=
-
this
.
ViewBox
.
MinX
;
float
fMinY
=
-
this
.
ViewBox
.
MinY
;
...
...
@@ -136,8 +156,8 @@ namespace Svg
}
float
fViewMidX
=
(
this
.
ViewBox
.
Width
/
2
)
*
fScaleX
;
float
fViewMidY
=
(
this
.
ViewBox
.
Height
/
2
)
*
fScaleY
;
float
fMidX
=
this
.
Width
.
ToDeviceValue
()
/
2
;
float
fMidY
=
this
.
Height
.
ToDeviceValue
()
/
2
;
float
fMidX
=
this
.
Width
.
ToDeviceValue
(
this
,
false
)
/
2
;
float
fMidY
=
this
.
Height
.
ToDeviceValue
(
this
,
true
)
/
2
;
switch
(
AspectRatio
.
Align
)
{
...
...
@@ -147,7 +167,7 @@ namespace Svg
fMinX
+=
(
fMidX
-
fViewMidX
)
/
fScaleX
;
break
;
case
SvgPreserveAspectRatio
.
xMaxYMin
:
fMinX
+=
(
this
.
Width
.
ToDeviceValue
()
/
fScaleX
)
-
this
.
ViewBox
.
Width
;
fMinX
+=
(
this
.
Width
.
ToDeviceValue
(
this
,
false
)
/
fScaleX
)
-
this
.
ViewBox
.
Width
;
break
;
case
SvgPreserveAspectRatio
.
xMinYMid
:
fMinY
+=
(
fMidY
-
fViewMidY
)
/
fScaleY
;
...
...
@@ -157,19 +177,19 @@ namespace Svg
fMinY
+=
(
fMidY
-
fViewMidY
)
/
fScaleY
;
break
;
case
SvgPreserveAspectRatio
.
xMaxYMid
:
fMinX
+=
(
this
.
Width
.
ToDeviceValue
()
/
fScaleX
)
-
this
.
ViewBox
.
Width
;
fMinX
+=
(
this
.
Width
.
ToDeviceValue
(
this
,
false
)
/
fScaleX
)
-
this
.
ViewBox
.
Width
;
fMinY
+=
(
fMidY
-
fViewMidY
)
/
fScaleY
;
break
;
case
SvgPreserveAspectRatio
.
xMinYMax
:
fMinY
+=
(
this
.
Height
.
ToDeviceValue
()
/
fScaleY
)
-
this
.
ViewBox
.
Height
;
fMinY
+=
(
this
.
Height
.
ToDeviceValue
(
this
,
true
)
/
fScaleY
)
-
this
.
ViewBox
.
Height
;
break
;
case
SvgPreserveAspectRatio
.
xMidYMax
:
fMinX
+=
(
fMidX
-
fViewMidX
)
/
fScaleX
;
fMinY
+=
(
this
.
Height
.
ToDeviceValue
()
/
fScaleY
)
-
this
.
ViewBox
.
Height
;
fMinY
+=
(
this
.
Height
.
ToDeviceValue
(
this
,
true
)
/
fScaleY
)
-
this
.
ViewBox
.
Height
;
break
;
case
SvgPreserveAspectRatio
.
xMaxYMax
:
fMinX
+=
(
this
.
Width
.
ToDeviceValue
()
/
fScaleX
)
-
this
.
ViewBox
.
Width
;
fMinY
+=
(
this
.
Height
.
ToDeviceValue
()
/
fScaleY
)
-
this
.
ViewBox
.
Height
;
fMinX
+=
(
this
.
Width
.
ToDeviceValue
(
this
,
false
)
/
fScaleX
)
-
this
.
ViewBox
.
Width
;
fMinY
+=
(
this
.
Height
.
ToDeviceValue
(
this
,
true
)
/
fScaleY
)
-
this
.
ViewBox
.
Height
;
break
;
default
:
break
;
...
...
@@ -223,6 +243,24 @@ namespace Svg
this
.
AspectRatio
=
new
SvgAspectRatio
(
SvgPreserveAspectRatio
.
xMidYMid
);
}
public
SizeF
GetDimensions
()
{
var
w
=
Width
.
ToDeviceValue
();
var
h
=
Height
.
ToDeviceValue
();
RectangleF
bounds
=
new
RectangleF
();
var
isWidthperc
=
Width
.
Type
==
SvgUnitType
.
Percentage
;
var
isHeightperc
=
Height
.
Type
==
SvgUnitType
.
Percentage
;
if
(
isWidthperc
||
isHeightperc
)
{
bounds
=
this
.
Bounds
;
//do just one call to the recursive bounds property
if
(
isWidthperc
)
w
=
(
bounds
.
Width
+
bounds
.
X
)
*
(
w
*
0.01f
);
if
(
isHeightperc
)
h
=
(
bounds
.
Height
+
bounds
.
Y
)
*
(
h
*
0.01f
);
}
return
new
SizeF
(
w
,
h
);
}
public
override
SvgElement
DeepCopy
()
{
...
...
@@ -239,5 +277,7 @@ namespace Svg
newObj
.
AspectRatio
=
this
.
AspectRatio
;
return
newObj
;
}
}
}
\ No newline at end of file
Source/Document Structure/SvgGroup.cs
View file @
2462bf13
using
System
;
using
System.Collections.Generic
;
using
System.Xml
;
using
System.Text
;
using
System.Drawing
;
using
System.Drawing.Drawing2D
;
using
System.Linq
;
using
Svg.Transforms
;
namespace
Svg
{
...
...
@@ -52,9 +46,17 @@ namespace Svg
// First it should check if rectangle is empty or it will return the wrong Bounds.
// This is because when the Rectangle is Empty, the Union method adds as if the first values where X=0, Y=0
if
(
r
.
IsEmpty
)
{
r
=
((
SvgVisualElement
)
c
).
Bounds
;
}
else
r
=
RectangleF
.
Union
(
r
,
((
SvgVisualElement
)
c
).
Bounds
);
{
var
childBounds
=
((
SvgVisualElement
)
c
).
Bounds
;
if
(!
childBounds
.
IsEmpty
)
{
r
=
RectangleF
.
Union
(
r
,
childBounds
);
}
}
}
}
...
...
Source/Painting/ISvgBoundable.cs
0 → 100644
View file @
2462bf13
using
System.Drawing
;
namespace
Svg
{
public
interface
ISvgBoundable
{
PointF
Location
{
get
;
}
SizeF
Size
{
get
;
}
RectangleF
Bounds
{
get
;
}
}
}
\ No newline at end of file
Source/Painting/ISvgStylable.cs
View file @
2462bf13
using
System
;
using
System.Collections.Generic
;
using
System.Text
;
using
System.Drawing
;
using
System.Drawing.Drawing2D
;
namespace
Svg
...
...
@@ -24,6 +20,5 @@ namespace Svg
SvgUnitCollection
StrokeDashArray
{
get
;
set
;
}
SvgUnit
StrokeDashOffset
{
get
;
set
;
}
GraphicsPath
Path
{
get
;
}
RectangleF
Bounds
{
get
;
}
}
}
\ No newline at end of file
Source/Painting/SvgGradientServer.cs
View file @
2462bf13
using
System
;
using
System.Collections.Generic
;
using
System.Drawing
;
using
System.Drawing.Drawing2D
;
using
Svg.Transforms
;
namespace
Svg
{
...
...
@@ -10,7 +12,7 @@ namespace Svg
public
abstract
class
SvgGradientServer
:
SvgPaintServer
{
private
SvgCoordinateUnits
_gradientUnits
;
private
SvgGradientSpreadMethod
_spreadMethod
=
SvgGradientSpreadMethod
.
Pad
;
private
SvgGradientSpreadMethod
_spreadMethod
;
private
SvgGradientServer
_inheritGradient
;
private
List
<
SvgGradientStop
>
_stops
;
...
...
@@ -20,6 +22,7 @@ namespace Svg
internal
SvgGradientServer
()
{
this
.
GradientUnits
=
SvgCoordinateUnits
.
ObjectBoundingBox
;
this
.
SpreadMethod
=
SvgGradientSpreadMethod
.
Pad
;
this
.
_stops
=
new
List
<
SvgGradientStop
>();
}
...
...
@@ -96,12 +99,39 @@ namespace Svg
}
}
[
SvgAttribute
(
"gradientTransform"
)]
public
SvgTransformCollection
GradientTransform
{
get
{
return
(
this
.
Attributes
.
GetAttribute
<
SvgTransformCollection
>(
"gradientTransform"
));
}
set
{
this
.
Attributes
[
"gradientTransform"
]
=
value
;
}
}
private
Matrix
EffectiveGradientTransform
{
get
{
var
transform
=
new
Matrix
();
if
(
GradientTransform
!=
null
)
{
transform
.
Multiply
(
GradientTransform
.
GetMatrix
());
}
return
transform
;
}
}
/// <summary>
/// Gets a <see cref="ColorBlend"/> representing the <see cref="SvgGradientServer"/>'s gradient stops.
/// </summary>
/// <param name="owner">The parent <see cref="SvgVisualElement"/>.</param>
/// <param name="opacity">The opacity of the colour blend.</param>
protected
ColorBlend
GetColo
u
rBlend
(
SvgVisualElement
owner
,
float
opacity
,
bool
radial
)
protected
ColorBlend
GetColorBlend
(
SvgVisualElement
owner
,
float
opacity
,
bool
radial
)
{
int
colourBlends
=
this
.
Stops
.
Count
;
bool
insertStart
=
false
;
...
...
@@ -201,6 +231,38 @@ namespace Svg
}
}
protected
ISvgBoundable
CalculateBoundable
(
SvgVisualElement
renderingElement
)
{
return
(
this
.
GradientUnits
==
SvgCoordinateUnits
.
ObjectBoundingBox
)
?
(
ISvgBoundable
)
renderingElement
:
renderingElement
.
OwnerDocument
;
}
protected
PointF
TransformPoint
(
PointF
originalPoint
)
{
var
newPoint
=
new
[]
{
originalPoint
};
EffectiveGradientTransform
.
TransformPoints
(
newPoint
);
return
newPoint
[
0
];
}
protected
PointF
TransformVector
(
PointF
originalVector
)
{
var
newVector
=
new
[]
{
originalVector
};
EffectiveGradientTransform
.
TransformVectors
(
newVector
);
return
newVector
[
0
];
}
protected
static
double
CalculateDistance
(
PointF
first
,
PointF
second
)
{
return
Math
.
Sqrt
(
Math
.
Pow
(
first
.
X
-
second
.
X
,
2
)
+
Math
.
Pow
(
first
.
Y
-
second
.
Y
,
2
));
}
protected
static
float
CalculateLength
(
PointF
vector
)
{
return
(
float
)
Math
.
Sqrt
(
Math
.
Pow
(
vector
.
X
,
2
)
+
Math
.
Pow
(
vector
.
Y
,
2
));
}
public
override
SvgElement
DeepCopy
<
T
>()
{
...
...
@@ -209,6 +271,7 @@ namespace Svg
newObj
.
SpreadMethod
=
this
.
SpreadMethod
;
newObj
.
GradientUnits
=
this
.
GradientUnits
;
newObj
.
InheritGradient
=
this
.
InheritGradient
;
newObj
.
GradientTransform
=
this
.
GradientTransform
;
return
newObj
;
}
...
...
Source/Painting/SvgLinearGradientServer.cs
View file @
2462bf13
using
System
;
using
System.Collections.Generic
;
using
System.Text
;
using
System.Drawing.Drawing2D
;
using
System.Diagnostics
;
using
System.Drawing
;
using
System.ComponentModel
;
using
System.Drawing.Drawing2D
;
using
System.Linq
;
namespace
Svg
{
[
SvgElement
(
"linearGradient"
)]
public
sealed
class
SvgLinearGradientServer
:
SvgGradientServer
{
private
SvgUnit
_x1
;
private
SvgUnit
_y1
;
private
SvgUnit
_x2
;
private
SvgUnit
_y2
;
[
DefaultValue
(
typeof
(
SvgUnit
),
"0"
),
SvgAttribute
(
"x1"
)]
[
SvgAttribute
(
"x1"
)]
public
SvgUnit
X1
{
get
{
return
this
.
_x1
;
}
get
{
return
this
.
Attributes
.
GetAttribute
<
SvgUnit
>(
"x1"
);
}
set
{
this
.
_x1
=
value
;
Attributes
[
"x1"
]
=
value
;
}
}
[
DefaultValue
(
typeof
(
SvgUnit
),
"0"
),
SvgAttribute
(
"y1"
)]
[
SvgAttribute
(
"y1"
)]
public
SvgUnit
Y1
{
get
{
return
this
.
_y1
;
}
get
{
return
this
.
Attributes
.
GetAttribute
<
SvgUnit
>(
"y1"
);
}
set
{
this
.
_y1
=
value
;
this
.
Attributes
[
"y1"
]
=
value
;
}
}
[
DefaultValue
(
typeof
(
SvgUnit
),
"0"
),
SvgAttribute
(
"x2"
)]
[
SvgAttribute
(
"x2"
)]
public
SvgUnit
X2
{
get
{
return
this
.
_x2
;
}
get
{
return
this
.
Attributes
.
GetAttribute
<
SvgUnit
>(
"x2"
);
}
set
{
this
.
_x2
=
value
;
Attributes
[
"x2"
]
=
value
;
}
}
[
DefaultValue
(
typeof
(
SvgUnit
),
"0"
),
SvgAttribute
(
"y2"
)]
[
SvgAttribute
(
"y2"
)]
public
SvgUnit
Y2
{
get
{
return
this
.
_y2
;
}
get
{
return
this
.
Attributes
.
GetAttribute
<
SvgUnit
>(
"y2"
);
}
set
{
this
.
_y2
=
value
;
this
.
Attributes
[
"y2"
]
=
value
;
}
}
private
bool
IsInvalid
{
get
{
// Need at least 2 colours to do the gradient fill
return
this
.
Stops
.
Count
<
2
;
}
}
public
SvgLinearGradientServer
()
{
this
.
_x
1
=
new
SvgUnit
(
0.0f
);
this
.
_y
1
=
new
SvgUnit
(
0.0f
);
this
.
_x
2
=
new
SvgUnit
(
0.0f
);
this
.
_y
2
=
new
SvgUnit
(
0.0f
);
X
1
=
new
SvgUnit
(
SvgUnitType
.
Percentage
,
0F
);
Y
1
=
new
SvgUnit
(
SvgUnitType
.
Percentage
,
0F
);
X
2
=
new
SvgUnit
(
SvgUnitType
.
Percentage
,
100F
);
Y
2
=
new
SvgUnit
(
SvgUnitType
.
Percentage
,
0F
);
}
public
SvgPoint
Start
public
override
Brush
GetBrush
(
SvgVisualElement
renderingElement
,
float
opacity
)
{
get
{
return
new
SvgPoint
(
this
.
X1
,
this
.
Y1
);
}
if
(
IsInvalid
)
{
return
null
;
}
public
SvgPoint
End
var
boundable
=
CalculateBoundable
(
renderingElement
);
var
specifiedStart
=
CalculateStart
(
boundable
);
var
specifiedEnd
=
CalculateEnd
(
boundable
);
var
effectiveStart
=
specifiedStart
;
var
effectiveEnd
=
specifiedEnd
;
if
(
NeedToExpandGradient
(
renderingElement
,
specifiedStart
,
specifiedEnd
))
{
get
{
return
new
SvgPoint
(
this
.
X2
,
this
.
Y2
);
}
var
expansion
=
ExpandGradient
(
renderingElement
,
specifiedStart
,
specifiedEnd
);
effectiveStart
=
expansion
.
Item1
;
effectiveEnd
=
expansion
.
Item2
;
}
public
override
Brush
GetBrush
(
SvgVisualElement
owner
,
float
opacity
)
return
new
LinearGradientBrush
(
effectiveStart
,
effectiveEnd
,
Color
.
Transparent
,
Color
.
Transparent
)
{
// Need at least 2 colours to do the gradient fill
if
(
this
.
Stops
.
Count
<
2
)
InterpolationColors
=
CalculateColorBlend
(
renderingElement
,
opacity
,
specifiedStart
,
effectiveStart
,
specifiedEnd
,
effectiveEnd
),
WrapMode
=
WrapMode
.
TileFlipX
};
}
private
PointF
CalculateStart
(
ISvgBoundable
boundable
)
{
return
null
;
return
TransformPoint
(
new
PointF
(
this
.
X1
.
ToDeviceValue
(
boundable
),
this
.
Y1
.
ToDeviceValue
(
boundable
,
true
)))
;
}
PointF
start
;
PointF
end
;
RectangleF
bounds
=
(
this
.
GradientUnits
==
SvgCoordinateUnits
.
ObjectBoundingBox
)
?
owner
.
Bounds
:
owner
.
OwnerDocument
.
GetDimensions
();
private
PointF
CalculateEnd
(
ISvgBoundable
boundable
)
{
return
TransformPoint
(
new
PointF
(
this
.
X2
.
ToDeviceValue
(
boundable
),
this
.
Y2
.
ToDeviceValue
(
boundable
,
true
)));
}
// Have start/end points been set? If not the gradient is horizontal
if
(!
this
.
End
.
IsEmpty
())
private
bool
NeedToExpandGradient
(
ISvgBoundable
boundable
,
PointF
specifiedStart
,
PointF
specifiedEnd
)
{
// Get the points to work out an angle
if
(
this
.
Start
.
IsEmpty
())
return
SpreadMethod
==
SvgGradientSpreadMethod
.
Pad
&&
(
boundable
.
Bounds
.
Contains
(
specifiedStart
)
||
boundable
.
Bounds
.
Contains
(
specifiedEnd
));
}
private
Tuple
<
PointF
,
PointF
>
ExpandGradient
(
ISvgBoundable
boundable
,
PointF
specifiedStart
,
PointF
specifiedEnd
)
{
if
(!
NeedToExpandGradient
(
boundable
,
specifiedStart
,
specifiedEnd
))
{
Debug
.
Fail
(
"Unexpectedly expanding gradient when not needed!"
);
return
new
Tuple
<
PointF
,
PointF
>(
specifiedStart
,
specifiedEnd
);
}
var
specifiedLength
=
CalculateDistance
(
specifiedStart
,
specifiedEnd
);
var
specifiedUnitVector
=
new
PointF
((
specifiedEnd
.
X
-
specifiedStart
.
X
)
/
(
float
)
specifiedLength
,
(
specifiedEnd
.
Y
-
specifiedStart
.
Y
)
/
(
float
)
specifiedLength
);
var
effectiveStart
=
specifiedStart
;
var
effectiveEnd
=
specifiedEnd
;
var
elementDiagonal
=
(
float
)
CalculateDistance
(
new
PointF
(
boundable
.
Bounds
.
Left
,
boundable
.
Bounds
.
Top
),
new
PointF
(
boundable
.
Bounds
.
Right
,
boundable
.
Bounds
.
Bottom
));
var
expandedStart
=
MovePointAlongVector
(
effectiveStart
,
specifiedUnitVector
,
-
elementDiagonal
);
var
expandedEnd
=
MovePointAlongVector
(
effectiveEnd
,
specifiedUnitVector
,
elementDiagonal
);
var
intersectionPoints
=
new
LineF
(
expandedStart
.
X
,
expandedStart
.
Y
,
expandedEnd
.
X
,
expandedEnd
.
Y
).
Intersection
(
boundable
.
Bounds
);
if
(
boundable
.
Bounds
.
Contains
(
specifiedStart
))
{
effectiveStart
=
CalculateClosestIntersectionPoint
(
expandedStart
,
intersectionPoints
);
effectiveStart
=
MovePointAlongVector
(
effectiveStart
,
specifiedUnitVector
,
-
1
);
}
if
(
boundable
.
Bounds
.
Contains
(
specifiedEnd
))
{
start
=
bounds
.
Location
;
effectiveEnd
=
CalculateClosestIntersectionPoint
(
effectiveEnd
,
intersectionPoints
);
effectiveEnd
=
MovePointAlongVector
(
effectiveEnd
,
specifiedUnitVector
,
1
);
}
return
new
Tuple
<
PointF
,
PointF
>(
effectiveStart
,
effectiveEnd
);
}
else
private
ColorBlend
CalculateColorBlend
(
SvgVisualElement
owner
,
float
opacity
,
PointF
specifiedStart
,
PointF
effectiveStart
,
PointF
specifiedEnd
,
PointF
effectiveEnd
)
{
var
colorBlend
=
GetColorBlend
(
owner
,
opacity
,
false
);
var
startDelta
=
CalculateDistance
(
specifiedStart
,
effectiveStart
);
var
endDelta
=
CalculateDistance
(
specifiedEnd
,
effectiveEnd
);
if
(!(
startDelta
>
0
)
&&
!(
endDelta
>
0
))
{
start
=
new
PointF
(
this
.
Start
.
X
.
ToDeviceValue
(
owner
),
this
.
Start
.
Y
.
ToDeviceValue
(
owner
,
true
))
;
return
colorBlend
;
}
float
x
=
(
this
.
End
.
X
.
IsEmpty
)
?
start
.
X
:
this
.
End
.
X
.
ToDeviceValue
(
owner
);
end
=
new
PointF
(
x
,
this
.
End
.
Y
.
ToDeviceValue
(
owner
,
true
));
var
specifiedLength
=
CalculateDistance
(
specifiedStart
,
specifiedEnd
);
var
specifiedUnitVector
=
new
PointF
((
specifiedEnd
.
X
-
specifiedStart
.
X
)
/
(
float
)
specifiedLength
,
(
specifiedEnd
.
Y
-
specifiedStart
.
Y
)
/
(
float
)
specifiedLength
);
var
effectiveLength
=
CalculateDistance
(
effectiveStart
,
effectiveEnd
);
for
(
var
i
=
0
;
i
<
colorBlend
.
Positions
.
Length
;
i
++)
{
var
originalPoint
=
MovePointAlongVector
(
specifiedStart
,
specifiedUnitVector
,
(
float
)
specifiedLength
*
colorBlend
.
Positions
[
i
]);
var
distanceFromEffectiveStart
=
CalculateDistance
(
effectiveStart
,
originalPoint
);
colorBlend
.
Positions
[
i
]
=
(
float
)
Math
.
Max
(
0F
,
Math
.
Min
((
distanceFromEffectiveStart
/
effectiveLength
),
1.0F
));
}
else
if
(
startDelta
>
0
)
{
// Default: horizontal
start
=
bounds
.
Location
;
end
=
new
PointF
(
bounds
.
Right
,
bounds
.
Top
);
colorBlend
.
Positions
=
new
[]
{
0F
}.
Concat
(
colorBlend
.
Positions
).
ToArray
();
colorBlend
.
Colors
=
new
[]
{
colorBlend
.
Colors
.
First
()
}.
Concat
(
colorBlend
.
Colors
).
ToArray
();
}
LinearGradientBrush
gradient
=
new
LinearGradientBrush
(
start
,
end
,
Color
.
Transparent
,
Color
.
Transparent
);
gradient
.
InterpolationColors
=
base
.
GetColourBlend
(
owner
,
opacity
,
false
);
if
(
endDelta
>
0
)
{
colorBlend
.
Positions
=
colorBlend
.
Positions
.
Concat
(
new
[]
{
1F
}).
ToArray
();
colorBlend
.
Colors
=
colorBlend
.
Colors
.
Concat
(
new
[]
{
colorBlend
.
Colors
.
Last
()
}).
ToArray
();
}
// Needed to fix an issue where the gradient was being wrapped when though it had the correct bounds
gradient
.
WrapMode
=
WrapMode
.
TileFlipX
;
return
gradient
;
return
colorBlend
;
}
private
static
PointF
CalculateClosestIntersectionPoint
(
PointF
sourcePoint
,
IList
<
PointF
>
targetPoints
)
{
Debug
.
Assert
(
targetPoints
.
Count
==
2
,
"Unexpected number of intersection points!"
);
return
CalculateDistance
(
sourcePoint
,
targetPoints
[
0
])
<
CalculateDistance
(
sourcePoint
,
targetPoints
[
1
])
?
targetPoints
[
0
]
:
targetPoints
[
1
];
}
private
static
PointF
MovePointAlongVector
(
PointF
start
,
PointF
unitVector
,
float
distance
)
{
return
start
+
new
SizeF
(
unitVector
.
X
*
distance
,
unitVector
.
Y
*
distance
);
}
public
override
SvgElement
DeepCopy
()
{
return
DeepCopy
<
SvgLinearGradientServer
>();
}
public
override
SvgElement
DeepCopy
<
T
>()
{
var
newObj
=
base
.
DeepCopy
<
T
>()
as
SvgLinearGradientServer
;
...
...
@@ -134,5 +229,106 @@ namespace Svg
return
newObj
;
}
private
sealed
class
LineF
{
private
float
X1
{
get
;
set
;
}
private
float
Y1
{
get
;
set
;
}
private
float
X2
{
get
;
set
;
}
private
float
Y2
{
get
;
set
;
}
public
LineF
(
float
x1
,
float
y1
,
float
x2
,
float
y2
)
{
X1
=
x1
;
Y1
=
y1
;
X2
=
x2
;
Y2
=
y2
;
}
public
List
<
PointF
>
Intersection
(
RectangleF
rectangle
)
{
var
result
=
new
List
<
PointF
>();
AddIfIntersect
(
this
,
new
LineF
(
rectangle
.
X
,
rectangle
.
Y
,
rectangle
.
Right
,
rectangle
.
Y
),
result
);
AddIfIntersect
(
this
,
new
LineF
(
rectangle
.
Right
,
rectangle
.
Y
,
rectangle
.
Right
,
rectangle
.
Bottom
),
result
);
AddIfIntersect
(
this
,
new
LineF
(
rectangle
.
Right
,
rectangle
.
Bottom
,
rectangle
.
X
,
rectangle
.
Bottom
),
result
);
AddIfIntersect
(
this
,
new
LineF
(
rectangle
.
X
,
rectangle
.
Bottom
,
rectangle
.
X
,
rectangle
.
Y
),
result
);
return
result
;
}
private
PointF
?
Intersection
(
LineF
other
)
{
var
a1
=
Y2
-
Y1
;
var
b1
=
X1
-
X2
;
var
c1
=
X2
*
Y1
-
X1
*
Y2
;
var
r3
=
a1
*
other
.
X1
+
b1
*
other
.
Y1
+
c1
;
var
r4
=
a1
*
other
.
X2
+
b1
*
other
.
Y2
+
c1
;
if
(
r3
!=
0
&&
r4
!=
0
&&
Math
.
Sign
(
r3
)
==
Math
.
Sign
(
r4
))
{
return
null
;
}
var
a2
=
other
.
Y2
-
other
.
Y1
;
var
b2
=
other
.
X1
-
other
.
X2
;
var
c2
=
other
.
X2
*
other
.
Y1
-
other
.
X1
*
other
.
Y2
;
var
r1
=
a2
*
X1
+
b2
*
Y1
+
c2
;
var
r2
=
a2
*
X2
+
b2
*
Y2
+
c2
;
if
(
r1
!=
0
&&
r2
!=
0
&&
Math
.
Sign
(
r1
)
==
Math
.
Sign
(
r2
))
{
return
(
null
);
}
var
denom
=
a1
*
b2
-
a2
*
b1
;
if
(
denom
==
0
)
{
return
null
;
}
var
offset
=
denom
<
0
?
-
denom
/
2
:
denom
/
2
;
var
num
=
b1
*
c2
-
b2
*
c1
;
var
x
=
(
num
<
0
?
num
-
offset
:
num
+
offset
)
/
denom
;
num
=
a2
*
c1
-
a1
*
c2
;
var
y
=
(
num
<
0
?
num
-
offset
:
num
+
offset
)
/
denom
;
return
new
PointF
(
x
,
y
);
}
private
static
void
AddIfIntersect
(
LineF
first
,
LineF
second
,
ICollection
<
PointF
>
result
)
{
var
intersection
=
first
.
Intersection
(
second
);
if
(
intersection
!=
null
)
{
result
.
Add
(
intersection
.
Value
);
}
}
}
}
}
\ No newline at end of file
Source/Painting/SvgMarker.cs
View file @
2462bf13
using
System
;
using
System.Collections.Generic
;
using
System.Linq
;
using
System.Text
;
using
System.Web
;
using
System.Xml
;
using
System.Xml.Serialization
;
using
System.Drawing.Drawing2D
;
using
System.Drawing
;
using
System.Drawing.Drawing2D
;
using
System.Linq
;
using
Svg.DataTypes
;
namespace
Svg
...
...
@@ -77,6 +72,13 @@ namespace Svg
set
{
this
.
Attributes
[
"markerHeight"
]
=
value
;
}
}
[
SvgAttribute
(
"markerUnits"
)]
public
virtual
SvgMarkerUnits
MarkerUnits
{
get
{
return
this
.
Attributes
.
GetAttribute
<
SvgMarkerUnits
>(
"markerUnits"
);
}
set
{
this
.
Attributes
[
"markerUnits"
]
=
value
;
}
}
public
SvgMarker
()
{
MarkerUnits
=
SvgMarkerUnits
.
strokeWidth
;
...
...
Source/Painting/SvgRadialGradientServer.cs
View file @
2462bf13
using
System
;
using
System.Diagnostics
;
using
System.Drawing
;
using
System.Drawing.Drawing2D
;
using
System.Linq
;
namespace
Svg
{
...
...
@@ -9,22 +12,40 @@ namespace Svg
[
SvgAttribute
(
"cx"
)]
public
SvgUnit
CenterX
{
get
{
return
this
.
Attributes
.
GetAttribute
<
SvgUnit
>(
"cx"
);
}
set
{
this
.
Attributes
[
"cx"
]
=
value
;
}
get
{
return
this
.
Attributes
.
GetAttribute
<
SvgUnit
>(
"cx"
);
}
set
{
this
.
Attributes
[
"cx"
]
=
value
;
}
}
[
SvgAttribute
(
"cy"
)]
public
SvgUnit
CenterY
{
get
{
return
this
.
Attributes
.
GetAttribute
<
SvgUnit
>(
"cy"
);
}
set
{
this
.
Attributes
[
"cy"
]
=
value
;
}
get
{
return
this
.
Attributes
.
GetAttribute
<
SvgUnit
>(
"cy"
);
}
set
{
this
.
Attributes
[
"cy"
]
=
value
;
}
}
[
SvgAttribute
(
"r"
)]
public
SvgUnit
Radius
{
get
{
return
this
.
Attributes
.
GetAttribute
<
SvgUnit
>(
"r"
);
}
set
{
this
.
Attributes
[
"r"
]
=
value
;
}
get
{
return
this
.
Attributes
.
GetAttribute
<
SvgUnit
>(
"r"
);
}
set
{
this
.
Attributes
[
"r"
]
=
value
;
}
}
[
SvgAttribute
(
"fx"
)]
...
...
@@ -41,8 +62,10 @@ namespace Svg
return
value
;
}
set
{
this
.
Attributes
[
"fx"
]
=
value
;
}
set
{
this
.
Attributes
[
"fx"
]
=
value
;
}
}
[
SvgAttribute
(
"fy"
)]
...
...
@@ -59,61 +82,139 @@ namespace Svg
return
value
;
}
set
{
this
.
Attributes
[
"fy"
]
=
value
;
}
set
{
this
.
Attributes
[
"fy"
]
=
value
;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="SvgRadialGradientServer"/> class.
/// </summary>
public
SvgRadialGradientServer
()
{
//Apply default values of 50% to cX,cY and r
CenterX
=
new
SvgUnit
(
SvgUnitType
.
Percentage
,
50
);
CenterY
=
new
SvgUnit
(
SvgUnitType
.
Percentage
,
50
);
Radius
=
new
SvgUnit
(
SvgUnitType
.
Percentage
,
50
);
CenterX
=
new
SvgUnit
(
SvgUnitType
.
Percentage
,
50F
);
CenterY
=
new
SvgUnit
(
SvgUnitType
.
Percentage
,
50F
);
Radius
=
new
SvgUnit
(
SvgUnitType
.
Percentage
,
50F
);
}
public
override
Brush
GetBrush
(
SvgVisualElement
renderingElement
,
float
opacity
)
{
float
radius
=
this
.
Radius
.
ToDeviceValue
(
renderingElement
);
var
origin
=
CalculateOrigin
(
renderingElement
);
if
(
radius
<=
0
)
var
centerPoint
=
CalculateCenterPoint
(
renderingElement
,
origin
);
var
focalPoint
=
CalculateFocalPoint
(
renderingElement
,
origin
);
var
specifiedRadius
=
CalculateRadius
(
renderingElement
);
var
effectiveRadius
=
CalculateEffectiveRadius
(
renderingElement
,
centerPoint
,
specifiedRadius
);
var
brush
=
new
PathGradientBrush
(
CreateGraphicsPath
(
origin
,
centerPoint
,
effectiveRadius
))
{
return
null
;
InterpolationColors
=
CalculateColorBlend
(
renderingElement
,
opacity
,
specifiedRadius
,
effectiveRadius
),
CenterPoint
=
focalPoint
};
Debug
.
Assert
(
brush
.
Rectangle
.
Contains
(
renderingElement
.
Bounds
),
"Brush rectangle does not contain rendering element bounds!"
);
return
brush
;
}
GraphicsPath
path
=
new
GraphicsPath
();
float
left
=
this
.
CenterX
.
ToDeviceValue
(
renderingElement
);
float
top
=
this
.
CenterY
.
ToDeviceValue
(
renderingElement
,
true
);
private
PointF
CalculateOrigin
(
SvgVisualElement
renderingElement
)
{
return
CalculateBoundable
(
renderingElement
).
Location
;
}
private
PointF
CalculateCenterPoint
(
ISvgBoundable
boundable
,
PointF
origin
)
{
var
deviceCenterX
=
origin
.
X
+
CenterX
.
ToDeviceValue
(
boundable
);
var
deviceCenterY
=
origin
.
Y
+
CenterY
.
ToDeviceValue
(
boundable
,
true
);
var
transformedCenterPoint
=
TransformPoint
(
new
PointF
(
deviceCenterX
,
deviceCenterY
));
return
transformedCenterPoint
;
}
private
PointF
CalculateFocalPoint
(
ISvgBoundable
boundable
,
PointF
origin
)
{
var
deviceFocalX
=
origin
.
X
+
FocalX
.
ToDeviceValue
(
boundable
);
var
deviceFocalY
=
origin
.
Y
+
FocalY
.
ToDeviceValue
(
boundable
,
true
);
var
transformedFocalPoint
=
TransformPoint
(
new
PointF
(
deviceFocalX
,
deviceFocalY
));
return
transformedFocalPoint
;
}
RectangleF
boundingBox
=
(
this
.
GradientUnits
==
SvgCoordinateUnits
.
ObjectBoundingBox
)
?
renderingElement
.
Bounds
:
renderingElement
.
OwnerDocument
.
GetDimensions
();
private
float
CalculateRadius
(
ISvgBoundable
boundable
)
{
var
radius
=
Radius
.
ToDeviceValue
(
boundable
);
var
transformRadiusVector
=
TransformVector
(
new
PointF
(
radius
,
0
));
var
transformedRadius
=
CalculateLength
(
transformRadiusVector
);
return
transformedRadius
;
}
private
float
CalculateEffectiveRadius
(
ISvgBoundable
boundable
,
PointF
centerPoint
,
float
specifiedRadius
)
{
if
(
SpreadMethod
!=
SvgGradientSpreadMethod
.
Pad
)
{
return
specifiedRadius
;
}
var
topLeft
=
new
PointF
(
boundable
.
Bounds
.
Left
,
boundable
.
Bounds
.
Top
);
var
topRight
=
new
PointF
(
boundable
.
Bounds
.
Right
,
boundable
.
Bounds
.
Top
);
var
bottomRight
=
new
PointF
(
boundable
.
Bounds
.
Right
,
boundable
.
Bounds
.
Bottom
);
var
bottomLeft
=
new
PointF
(
boundable
.
Bounds
.
Left
,
boundable
.
Bounds
.
Bottom
);
var
effectiveRadius
=
(
float
)
Math
.
Ceiling
(
Math
.
Max
(
Math
.
Max
(
CalculateDistance
(
centerPoint
,
topLeft
),
CalculateDistance
(
centerPoint
,
topRight
)
),
Math
.
Max
(
CalculateDistance
(
centerPoint
,
bottomRight
),
CalculateDistance
(
centerPoint
,
bottomLeft
)
)
)
);
effectiveRadius
=
Math
.
Max
(
effectiveRadius
,
specifiedRadius
);
return
effectiveRadius
;
}
private
static
GraphicsPath
CreateGraphicsPath
(
PointF
origin
,
PointF
centerPoint
,
float
effectiveRadius
)
{
var
path
=
new
GraphicsPath
();
path
.
AddEllipse
(
boundingBox
.
Left
+
left
-
radius
,
boundingBox
.
Top
+
top
-
radius
,
radius
*
2
,
radius
*
2
);
origin
.
X
+
centerPoint
.
X
-
effectiveRadius
,
origin
.
Y
+
centerPoint
.
Y
-
effectiveRadius
,
effectiveRadius
*
2
,
effectiveRadius
*
2
);
PathGradientBrush
brush
=
new
PathGradientBrush
(
path
)
;
ColorBlend
blend
=
base
.
GetColourBlend
(
renderingElement
,
opacity
,
true
);
return
path
;
}
brush
.
InterpolationColors
=
blend
;
brush
.
CenterPoint
=
new
PointF
(
boundingBox
.
Left
+
this
.
FocalX
.
ToDeviceValue
(
renderingElement
),
boundingBox
.
Top
+
this
.
FocalY
.
ToDeviceValue
(
renderingElement
,
true
));
private
ColorBlend
CalculateColorBlend
(
SvgVisualElement
renderingElement
,
float
opacity
,
float
specifiedRadius
,
float
effectiveRadius
)
{
var
colorBlend
=
GetColorBlend
(
renderingElement
,
opacity
,
true
);
return
brush
;
if
(
specifiedRadius
>=
effectiveRadius
)
{
return
colorBlend
;
}
for
(
var
i
=
0
;
i
<
colorBlend
.
Positions
.
Length
-
1
;
i
++)
{
colorBlend
.
Positions
[
i
]
=
1
-
(
specifiedRadius
/
effectiveRadius
)
*
(
1
-
colorBlend
.
Positions
[
i
]);
}
colorBlend
.
Positions
=
new
[]
{
0F
}.
Concat
(
colorBlend
.
Positions
).
ToArray
();
colorBlend
.
Colors
=
new
[]
{
colorBlend
.
Colors
.
First
()
}.
Concat
(
colorBlend
.
Colors
).
ToArray
();
return
colorBlend
;
}
public
override
SvgElement
DeepCopy
()
{
return
DeepCopy
<
SvgRadialGradientServer
>();
}
public
override
SvgElement
DeepCopy
<
T
>()
{
var
newObj
=
base
.
DeepCopy
<
T
>()
as
SvgRadialGradientServer
;
...
...
Source/Svg.csproj
View file @
2462bf13
...
...
@@ -114,6 +114,7 @@
<Compile
Include=
"DataTypes\SvgViewBox.cs"
/>
<Compile
Include=
"Document Structure\SvgTitle.cs"
/>
<Compile
Include=
"Document Structure\SvgDocumentMetadata.cs"
/>
<Compile
Include=
"Painting\ISvgBoundable.cs"
/>
<Compile
Include=
"Painting\SvgMarker.cs"
/>
<Compile
Include=
"Document Structure\SvgDefinitionList.cs"
/>
<Compile
Include=
"Document Structure\SvgDescription.cs"
/>
...
...
Source/SvgDocument.cs
View file @
2462bf13
...
...
@@ -19,8 +19,6 @@ namespace Svg
public
static
readonly
int
PointsPerInch
=
96
;
private
SvgElementIdManager
_idManager
;
/// <summary>
/// Initializes a new instance of the <see cref="SvgDocument"/> class.
/// </summary>
...
...
@@ -293,25 +291,6 @@ namespace Svg
return
null
;
}
public
RectangleF
GetDimensions
()
{
var
w
=
Width
.
ToDeviceValue
();
var
h
=
Height
.
ToDeviceValue
();
RectangleF
bounds
=
new
RectangleF
();
var
isWidthperc
=
Width
.
Type
==
SvgUnitType
.
Percentage
;
var
isHeightperc
=
Height
.
Type
==
SvgUnitType
.
Percentage
;
if
(
isWidthperc
||
isHeightperc
)
{
bounds
=
this
.
Bounds
;
//do just one call to the recursive bounds property
if
(
isWidthperc
)
w
=
(
bounds
.
Width
+
bounds
.
X
)
*
(
w
*
0.01f
);
if
(
isHeightperc
)
h
=
(
bounds
.
Height
+
bounds
.
Y
)
*
(
h
*
0.01f
);
}
return
new
RectangleF
(
0
,
0
,
w
,
h
);
}
/// <summary>
/// Renders the <see cref="SvgDocument"/> to the specified <see cref="SvgRenderer"/>.
/// </summary>
...
...
@@ -413,6 +392,5 @@ namespace Svg
this
.
Write
(
fs
);
}
}
}
}
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment