diff --git a/Refs/SharpVectorBindings.dll b/Refs/SharpVectorBindings.dll
new file mode 100644
index 000000000..3d5030b61
Binary files /dev/null and b/Refs/SharpVectorBindings.dll differ
diff --git a/Refs/SharpVectorCss.dll b/Refs/SharpVectorCss.dll
new file mode 100644
index 000000000..7997bcb0a
Binary files /dev/null and b/Refs/SharpVectorCss.dll differ
diff --git a/Refs/SharpVectorDom.dll b/Refs/SharpVectorDom.dll
new file mode 100644
index 000000000..cdaff862b
Binary files /dev/null and b/Refs/SharpVectorDom.dll differ
diff --git a/Refs/SharpVectorObjectModel.dll b/Refs/SharpVectorObjectModel.dll
new file mode 100644
index 000000000..4f7e4ff89
Binary files /dev/null and b/Refs/SharpVectorObjectModel.dll differ
diff --git a/Refs/SharpVectorUtil.dll b/Refs/SharpVectorUtil.dll
new file mode 100644
index 000000000..6d91a00e5
Binary files /dev/null and b/Refs/SharpVectorUtil.dll differ
diff --git a/Root/Kernel.cs b/Root/Kernel.cs
index 890870b7a..957949e4c 100644
--- a/Root/Kernel.cs
+++ b/Root/Kernel.cs
@@ -637,13 +637,10 @@ private void RefreshSubMenu(ToolStripItem item)
}
// This item
- if (item.Tag != null)
+ ItemResourceInfo info = item.Tag as ItemResourceInfo;
+ if (info != null)
{
- item.Text = ((ItemResourceInfo)item.Tag).resManager.GetString(((ItemResourceInfo)item.Tag).strText, Thread.CurrentThread.CurrentUICulture);
- }
- else
- {
- // If tag is null, it's a non translatable text.
+ item.Text = info.resManager.GetString(info.strText, Thread.CurrentThread.CurrentUICulture);
}
}
#endregion
diff --git a/Root/Root.csproj b/Root/Root.csproj
index 1118ae662..283f81019 100644
--- a/Root/Root.csproj
+++ b/Root/Root.csproj
@@ -43,7 +43,7 @@
4
false
1591
- OnBuildSuccess
+ Always
true
@@ -346,4 +346,7 @@
-->
+
+ "..\..\..\..\Tools\build events\postbuild.bat"
+
\ No newline at end of file
diff --git a/ScreenManager/PlayerScreen/Drawings/DrawingSVG.cs b/ScreenManager/PlayerScreen/Drawings/DrawingSVG.cs
new file mode 100644
index 000000000..edc6f341a
--- /dev/null
+++ b/ScreenManager/PlayerScreen/Drawings/DrawingSVG.cs
@@ -0,0 +1,534 @@
+#region License
+/*
+Copyright © Joan Charmant 2010.
+joan.charmant@gmail.com
+
+This file is part of Kinovea.
+
+Kinovea is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2
+as published by the Free Software Foundation.
+
+Kinovea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Kinovea. If not, see http://www.gnu.org/licenses/.
+*/
+#endregion
+using System;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+using System.Reflection;
+using System.Resources;
+using System.Threading;
+using System.Xml;
+
+using Kinovea.Services;
+using SharpVectors.Dom.Svg;
+using SharpVectors.Dom.Svg.Rendering;
+using SharpVectors.Renderer.Gdi;
+
+namespace Kinovea.ScreenManager
+{
+ public class DrawingSVG : AbstractDrawing
+ {
+ #region Properties
+ public override DrawingToolType ToolType
+ {
+ get
+ {
+ // invalid, this drawing is not created by a drawing tool.
+ return DrawingToolType.Pointer;
+ }
+ }
+ public override InfosFading infosFading
+ {
+ get { return m_InfosFading; }
+ set { m_InfosFading = value; }
+ }
+ #endregion
+
+ #region Members
+
+ // SVG
+ private GdiRenderer m_Renderer = new GdiRenderer();
+ private SvgWindow m_SvgWindow;
+ private bool m_bLoaded;
+ private Bitmap m_svgRendered;
+ private Bitmap m_svgHitMap;
+
+ // Position
+ // The drawing scale is used to keep track of the user transform on the drawing, outside of the image transform context.
+ // The unscale rendering window is used for hit testing.
+ // Drawing original dimensions are used to compute the drawing scale.
+
+ private float m_fDrawingScale = 1.0f; // The current scale of the drawing if it were rendered on the original sized image.
+ private Rectangle m_UnscaledRenderingWindow; // The area of the original sized image that would be covered by the drawing in its current scale.
+ private float m_fDrawingRenderingScale = 1.0f; // The scale of the drawing taking drawing transform AND image transform into account.
+ private Rectangle m_RescaledRectangle; // The area of the user sized image that will be covered by the drawing.
+
+ private double m_fStretchFactor; // The scaling of the image.
+ private Point m_DirectZoomTopLeft; // Shift of the image.
+
+ private int m_iOriginalWidth; // As in the SVG file.
+ private int m_iOriginalHeight;
+ private double m_fOriginalAspectRatio;
+
+ private bool m_bFinishedResizing;
+
+ // Decoration
+ private InfosFading m_InfosFading;
+ private ColorMatrix m_FadingColorMatrix = new ColorMatrix();
+ private ImageAttributes m_FadingImgAttr = new ImageAttributes();
+ private Pen m_PenBoundingBox;
+ private SolidBrush m_BrushBoundingBox;
+
+ // Instru
+ private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+ #endregion
+
+ #region Constructors
+ public DrawingSVG(int x, int y, long _iTimestamp, long _iAverageTimeStampsPerFrame, string _filename)
+ {
+ m_fStretchFactor = 1.0;
+ m_DirectZoomTopLeft = new Point(0, 0);
+
+ //----------------------------
+ // Init and import an SVG.
+ // Testing code. Should load a file according to a parameter.
+ //----------------------------
+ m_Renderer.BackColor = Color.Transparent;
+
+ // Rendering window. The width and height will be updated later.
+ m_SvgWindow = new SvgWindow(100, 100, m_Renderer);
+
+ // FIXME: some files have external DTD that will be attempted to be loaded.
+ // See files created from Amaya for example.
+ m_SvgWindow.Src = _filename;
+ m_bLoaded = true;
+
+ m_iOriginalWidth = (int)m_SvgWindow.Document.RootElement.Width.BaseVal.Value;
+ m_iOriginalHeight = (int)m_SvgWindow.Document.RootElement.Height.BaseVal.Value;
+ m_fOriginalAspectRatio = (double)m_iOriginalWidth / (double)m_iOriginalHeight;
+ m_UnscaledRenderingWindow = new Rectangle(x - m_iOriginalWidth/2, y - m_iOriginalHeight/2, m_iOriginalWidth, m_iOriginalHeight);
+
+ // Everything start unscaled.
+ RescaleCoordinates(m_fStretchFactor, m_DirectZoomTopLeft);
+
+ // Render on first draw call.
+ m_bFinishedResizing = true;
+
+ // Fading
+ m_InfosFading = new InfosFading(_iTimestamp, _iAverageTimeStampsPerFrame);
+ m_InfosFading.UseDefault = false;
+ m_InfosFading.AlwaysVisible = true;
+
+ // This is kept in place but in the current implementation the drawing never fade. (motion guide).
+ m_FadingColorMatrix.Matrix00 = 1.0f;
+ m_FadingColorMatrix.Matrix11 = 1.0f;
+ m_FadingColorMatrix.Matrix22 = 1.0f;
+ m_FadingColorMatrix.Matrix33 = 1.0f; // Change alpha value here for fading. (i.e: 0.5f).
+ m_FadingColorMatrix.Matrix44 = 1.0f;
+ m_FadingImgAttr.SetColorMatrix(m_FadingColorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
+
+ PreferencesManager pm = PreferencesManager.Instance();
+ m_PenBoundingBox = new Pen(Color.FromArgb(255, pm.GridColor.R, pm.GridColor.G, pm.GridColor.B), 1);
+ m_PenBoundingBox.DashStyle = DashStyle.Dash;
+ m_BrushBoundingBox = new SolidBrush(m_PenBoundingBox.Color);
+ }
+ #endregion
+
+ #region AbstractDrawing Implementation
+ public override void Draw(Graphics _canvas, double _fStretchFactor, bool _bSelected, long _iCurrentTimestamp, Point _DirectZoomTopLeft)
+ {
+ double fOpacityFactor = m_InfosFading.GetOpacityFactor(_iCurrentTimestamp);
+
+ if (fOpacityFactor > 0 && m_bLoaded)
+ {
+ if(m_fStretchFactor != _fStretchFactor || _DirectZoomTopLeft != m_DirectZoomTopLeft)
+ {
+ // Compute the new image coordinate system.
+ // We do not call the SVG rendering engine at this point, and
+ // will use the .NET interpolation until the user is done resizing.
+ // Later on, ResizeFinished() should be called to trigger the full rendering.
+ m_fStretchFactor = _fStretchFactor;
+ m_DirectZoomTopLeft = _DirectZoomTopLeft;
+ RescaleCoordinates(m_fStretchFactor, m_DirectZoomTopLeft);
+ }
+
+ if(m_bFinishedResizing)
+ {
+ m_bFinishedResizing = false;
+ RenderAtNewScale();
+ }
+
+ if (m_svgRendered != null )
+ {
+ m_FadingColorMatrix.Matrix33 = (float)fOpacityFactor;
+ m_FadingImgAttr.SetColorMatrix(m_FadingColorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
+
+ // Render drawing.
+ _canvas.DrawImage(m_svgRendered, m_RescaledRectangle, 0, 0, m_svgRendered.Width, m_svgRendered.Height, GraphicsUnit.Pixel, m_FadingImgAttr);
+
+ // Render handling box.
+ if(_bSelected)
+ {
+ _canvas.DrawRectangle(m_PenBoundingBox, m_RescaledRectangle);
+
+ _canvas.FillEllipse(m_BrushBoundingBox, m_RescaledRectangle.Left - 4, m_RescaledRectangle.Top - 4, 8, 8);
+ _canvas.FillEllipse(m_BrushBoundingBox, m_RescaledRectangle.Left - 4, m_RescaledRectangle.Bottom - 4, 8, 8);
+ _canvas.FillEllipse(m_BrushBoundingBox, m_RescaledRectangle.Right - 4, m_RescaledRectangle.Top - 4, 8, 8);
+ _canvas.FillEllipse(m_BrushBoundingBox, m_RescaledRectangle.Right - 4, m_RescaledRectangle.Bottom - 4, 8, 8);
+ }
+ }
+ }
+ }
+ public override void MoveHandleTo(Point point, int handleNumber)
+ {
+ // _point is new coordinates of the handle, already descaled.
+
+ // Keep aspect ratio for now.
+
+ // None of the computations below should involve the image stretch factor.
+ // We just compute the drawing bounding box as if it drew on the unscaled image.
+
+ // We do not call the SVG rendering engine at this point, and
+ // will use the .NET interpolation until the user is done resizing.
+ // Later on, ResizeFinished() should be called to trigger the full rendering.
+
+ switch (handleNumber)
+ {
+ case 1:
+ {
+ // Top left handler.
+ // Compute the new rendering window as if in original image coordinate system.
+ int dx = point.X - m_UnscaledRenderingWindow.Left;
+ int newWidth = m_UnscaledRenderingWindow.Width - dx;
+
+ if(newWidth > 50)
+ {
+ double qRatio = (double)newWidth / (double)m_iOriginalWidth;
+ int newHeight = (int)((double)m_iOriginalHeight * qRatio); // Only if square.
+
+ int newY = m_UnscaledRenderingWindow.Top + m_UnscaledRenderingWindow.Height - newHeight;
+
+ m_UnscaledRenderingWindow = new Rectangle(point.X, newY, newWidth, newHeight);
+ }
+ break;
+ }
+ case 2:
+ {
+
+ // Top right handler.
+ int dx = m_UnscaledRenderingWindow.Right - point.X;
+ int newWidth = m_UnscaledRenderingWindow.Width - dx;
+
+ if(newWidth > 50)
+ {
+ double qRatio = (double)newWidth / (double)m_iOriginalWidth;
+ int newHeight = (int)((double)m_iOriginalHeight * qRatio); // Only if square.
+
+ int newY = m_UnscaledRenderingWindow.Top + m_UnscaledRenderingWindow.Height - newHeight;
+ int newX = point.X - newWidth;
+
+ m_UnscaledRenderingWindow = new Rectangle(newX, newY, newWidth, newHeight);
+ }
+ break;
+ }
+ case 3:
+ {
+ // Bottom right handler.
+ int dx = m_UnscaledRenderingWindow.Right - point.X;
+ int newWidth = m_UnscaledRenderingWindow.Width - dx;
+
+ if(newWidth > 50)
+ {
+ double qRatio = (double)newWidth / (double)m_iOriginalWidth;
+ int newHeight = (int)((double)m_iOriginalHeight * qRatio); // Only if square.
+
+ int newY = m_UnscaledRenderingWindow.Y;
+ int newX = point.X - newWidth;
+
+ m_UnscaledRenderingWindow = new Rectangle(newX, newY, newWidth, newHeight);
+ }
+ break;
+ }
+ case 4:
+ {
+ // Bottom left handler.
+ int dx = point.X - m_UnscaledRenderingWindow.Left;
+ int newWidth = m_UnscaledRenderingWindow.Width - dx;
+
+ if(newWidth > 50)
+ {
+ double qRatio = (double)newWidth / (double)m_iOriginalWidth;
+ int newHeight = (int)((double)m_iOriginalHeight * qRatio); // Only if square.
+
+ int newY = m_UnscaledRenderingWindow.Y;
+
+ m_UnscaledRenderingWindow = new Rectangle(point.X, newY, newWidth, newHeight);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Update scaled coordinates accordingly (we must do this before the RenderAtNewScale happen).
+ RescaleCoordinates(m_fStretchFactor, m_DirectZoomTopLeft);
+
+ }
+ public override void MoveDrawing(int _deltaX, int _deltaY)
+ {
+ // _delatX and _delatY are mouse delta already descaled.
+ // Move the rendering window around, does not change the scale of the drawing.
+ m_UnscaledRenderingWindow = new Rectangle(m_UnscaledRenderingWindow.X + _deltaX,
+ m_UnscaledRenderingWindow.Y + _deltaY,
+ m_UnscaledRenderingWindow.Width,
+ m_UnscaledRenderingWindow.Height);
+
+ // Update scaled coordinates accordingly.
+ RescaleCoordinates(m_fStretchFactor, m_DirectZoomTopLeft);
+ }
+ public override int HitTest(Point _point, long _iCurrentTimestamp)
+ {
+ // _point is mouse coordinates descaled.
+ // Hit Result: -1: miss, 0: on object.
+
+ int iHitResult = -1;
+ double fOpacityFactor = m_InfosFading.GetOpacityFactor(_iCurrentTimestamp);
+ if (fOpacityFactor > 0)
+ {
+ // On handles ?
+ for (int i = 0; i < 4; i++)
+ {
+ if (GetHandleRectangle(i+1).Contains(_point))
+ {
+ iHitResult = i+1;
+ }
+ }
+
+ // On main drawing ?
+ if(iHitResult == -1 && IsPointOnDrawing(_point.X, _point.Y))
+ {
+ iHitResult = 0;
+ }
+ }
+
+ log.Debug(String.Format("SVG Hit test input:{0}, unscaled rect:{1}. Result:{2}",
+ _point.ToString(), m_UnscaledRenderingWindow.ToString(), iHitResult.ToString()));
+
+ return iHitResult;
+ }
+ public override void ToXmlString(XmlTextWriter _xmlWriter)
+ {
+ // TODO: implement me.
+
+
+ /*
+ _xmlWriter.WriteStartElement("Drawing");
+ _xmlWriter.WriteAttributeString("Type", "DrawingCross2D");
+
+ // CenterPoint
+ _xmlWriter.WriteStartElement("CenterPoint");
+ _xmlWriter.WriteString(m_TopLeftPoint.X.ToString() + ";" + m_TopLeftPoint.Y.ToString());
+ _xmlWriter.WriteEndElement();
+
+ m_PenStyle.ToXml(_xmlWriter);
+ m_InfosFading.ToXml(_xmlWriter, false);
+
+ //
+ _xmlWriter.WriteEndElement();*/
+ }
+ public static AbstractDrawing FromXml(XmlTextReader _xmlReader, PointF _scale)
+ {
+
+ // TODO: implement me.
+
+
+ DrawingSVG dsvg = new DrawingSVG(0,0,0,0, null);
+
+ /*while (_xmlReader.Read())
+ {
+ if (_xmlReader.IsStartElement())
+ {
+ if (_xmlReader.Name == "CenterPoint")
+ {
+ Point p = XmlHelper.PointParse(_xmlReader.ReadString(), ';');
+ dc.m_CenterPoint = new Point((int)((float)p.X * _scale.X), (int)((float)p.Y * _scale.Y));
+ }
+ else if (_xmlReader.Name == "LineStyle")
+ {
+ dc.m_PenStyle = LineStyle.FromXml(_xmlReader);
+ }
+ else if (_xmlReader.Name == "InfosFading")
+ {
+ dc.m_InfosFading.FromXml(_xmlReader);
+ }
+ else
+ {
+ // forward compatibility : ignore new fields.
+ }
+ }
+ else if (_xmlReader.Name == "Drawing")
+ {
+ break;
+ }
+ else
+ {
+ // Fermeture d'un tag interne.
+ }
+ }
+
+ dc.RescaleCoordinates(dc.m_fStretchFactor, dc.m_DirectZoomTopLeft);*/
+ return dsvg;
+ }
+ public override string ToString()
+ {
+ // Return the name of the tool used to draw this drawing.
+ return "SVG Drawing";
+ }
+ public override int GetHashCode()
+ {
+ // Combine all relevant fields with XOR to get the Hash.
+ int iHash = m_UnscaledRenderingWindow.GetHashCode();
+ return iHash;
+ }
+
+ #region Not implemented
+ public override void UpdateDecoration(Color _color)
+ {
+ throw new Exception(String.Format("{0}, The method or operation is not implemented.", this.ToString()));
+ }
+ public override void UpdateDecoration(LineStyle _style)
+ {
+ throw new Exception(String.Format("{0}, The method or operation is not implemented.", this.ToString()));
+ }
+ public override void UpdateDecoration(int _iFontSize)
+ {
+ throw new Exception(String.Format("{0}, The method or operation is not implemented.", this.ToString()));
+ }
+ public override void MemorizeDecoration()
+ {
+ // Not implemented.
+ }
+ public override void RecallDecoration()
+ {
+ // Not implemented.
+ }
+ #endregion
+
+ #endregion
+
+ public void ResizeFinished()
+ {
+ // While the user was resizing the drawing or the image, we didn't update / render the SVG image.
+ // Now that he is done, we can stop using the low quality interpolation and resort to SVG scalability.
+
+ // However we do not know the final scale until we get back in Draw(),
+ // So we just switch a flag on and we'll call the rendering from there.
+ m_bFinishedResizing = true;
+ }
+
+ #region Lower level helpers
+ private void RenderAtNewScale()
+ {
+ // Depending on the complexity of the SVG, this can be a costly operation.
+ // We should only do that when mouse move is over,
+ // and use the interpolated version during the change.
+
+ //m_SvgWindow.Document.RootElement.
+
+
+ // Compute the final drawing sizes,
+ // taking both the drawing transformation and the image scaling into account.
+ m_fDrawingScale = (float)m_UnscaledRenderingWindow.Width / (float)m_iOriginalWidth;
+ m_fDrawingRenderingScale = (float)(m_fStretchFactor * m_fDrawingScale);
+
+ if(m_svgRendered == null || m_fDrawingRenderingScale != m_SvgWindow.Document.RootElement.CurrentScale)
+ {
+ m_SvgWindow.Document.RootElement.CurrentScale = (float)m_fDrawingRenderingScale;
+ m_SvgWindow.InnerWidth = m_RescaledRectangle.Width;
+ m_SvgWindow.InnerHeight = m_RescaledRectangle.Height;
+
+ m_svgRendered = m_Renderer.Render(m_SvgWindow.Document as SvgDocument);
+ m_svgHitMap = m_Renderer.IdMapRaster;
+
+ log.Debug(String.Format("Rendering SVG ({0};{1}), User transformation (scale) : {2:0.00}. Image transformation (scale) :{3:0.00}, Final transformation (scale) : {4:0.00}.",
+ m_iOriginalWidth, m_iOriginalHeight, m_fDrawingScale, m_fStretchFactor, m_fDrawingRenderingScale));
+ }
+ }
+ private Rectangle GetHandleRectangle(int _handle)
+ {
+ //----------------------------------------------------------------------------
+ // This function is only used for Hit Testing.
+ // The Rectangle here is bigger than the bounding box of the handlers circles.
+ // handles are clockwise 1 to 4.
+ //----------------------------------------------------------------------------
+ int widen = 6;
+ if (_handle == 1)
+ {
+ return new Rectangle(m_UnscaledRenderingWindow.Left - widen, m_UnscaledRenderingWindow.Top - widen, widen * 2, widen * 2);
+ }
+ else if(_handle == 2)
+ {
+ return new Rectangle(m_UnscaledRenderingWindow.Right - widen, m_UnscaledRenderingWindow.Top - widen, widen * 2, widen * 2);
+ }
+ else if(_handle == 3)
+ {
+ return new Rectangle(m_UnscaledRenderingWindow.Right - widen, m_UnscaledRenderingWindow.Bottom - widen, widen * 2, widen * 2);
+ }
+ else
+ {
+ return new Rectangle(m_UnscaledRenderingWindow.Left - widen, m_UnscaledRenderingWindow.Bottom - widen, widen * 2, widen * 2);
+ }
+ }
+ private bool IsPointOnDrawing(int x, int y)
+ {
+ // [x,y] is expressed in the original image coordinate system,
+ // But the drawing should have been rendered to its final scale by now.
+ // The final scale takes into account the user transformation and the image scaling.
+
+ // We MUST stop the interpolation trick and render to the new dimensions before coming here.
+
+ bool hit = false;
+
+ // Rescale the mouse point.
+ int scaledX = (int)((x - m_DirectZoomTopLeft.X) * m_fStretchFactor);
+ int scaledY = (int)((y - m_DirectZoomTopLeft.Y) * m_fStretchFactor);
+
+ // Unshift the mouse point. (because the drawing always draws itself on a [0, 0, width, height] image.
+
+ int unshiftedX = scaledX - m_RescaledRectangle.X;
+ int unshiftedY = scaledY - m_RescaledRectangle.Y;
+
+ if(unshiftedX >= 0 && unshiftedY >= 0 && unshiftedX < m_Renderer.IdMapRaster.Width && unshiftedY < m_Renderer.IdMapRaster.Height)
+ {
+ hit = m_Renderer.HitTest(unshiftedX, unshiftedY);
+ }
+
+ return hit;
+ }
+ private void RescaleCoordinates(double _fStretchFactor, Point _DirectZoomTopLeft)
+ {
+ // Computes the rendering window in the user image coordinate system.
+
+ // Stretch factor is the difference between the original image size and the current image size.
+ // It doesn't know anything about the SVG drawing internal scaling.
+
+ int iLeft = (int)((m_UnscaledRenderingWindow.X - _DirectZoomTopLeft.X) * _fStretchFactor);
+ int iTop = (int)((m_UnscaledRenderingWindow.Y - _DirectZoomTopLeft.Y) * _fStretchFactor);
+
+ int iHeight = (int)((double)m_UnscaledRenderingWindow.Height * _fStretchFactor);
+ int iWidth = (int)((double)m_UnscaledRenderingWindow.Width * _fStretchFactor);
+
+ m_RescaledRectangle = new Rectangle(iLeft, iTop, iWidth, iHeight);
+ }
+ #endregion
+ }
+}
+
diff --git a/ScreenManager/PlayerScreen/Metadata.cs b/ScreenManager/PlayerScreen/Metadata.cs
index eb790b1bb..d65bfa8d3 100644
--- a/ScreenManager/PlayerScreen/Metadata.cs
+++ b/ScreenManager/PlayerScreen/Metadata.cs
@@ -385,6 +385,27 @@ public List GetFullImages()
}
return images;
}
+
+ public void ResizeFinished()
+ {
+ // This function is used to trigger an update to drawings and guides that do not
+ // render in the same way when the user is resizing the window or not.
+ // This is typically used for SVG Drawing, which take a long time to render themselves.
+ foreach(Keyframe kf in m_Keyframes)
+ {
+ foreach(AbstractDrawing d in kf.Drawings)
+ {
+ DrawingSVG svg = d as DrawingSVG;
+ if(svg != null)
+ {
+ svg.ResizeFinished();
+ }
+ }
+ }
+
+
+ }
+
#region Objects Hit Tests
public void UnselectAll()
{
@@ -548,6 +569,7 @@ public bool IsOnGrid(Point _MouseLocation)
return bGridHit;
}
#endregion
+
#endregion
#region Lower level Helpers
diff --git a/ScreenManager/PlayerScreen/PlayerScreen.cs b/ScreenManager/PlayerScreen/PlayerScreen.cs
index 86805d393..10550325c 100644
--- a/ScreenManager/PlayerScreen/PlayerScreen.cs
+++ b/ScreenManager/PlayerScreen/PlayerScreen.cs
@@ -348,6 +348,11 @@ public void Save()
{
m_PlayerScreenUI.Save();
}
+ public void AddSVGDrawing(string filename)
+ {
+ // todo, move up in Abstract screen ?
+ m_PlayerScreenUI.AddSVGDrawing(filename);
+ }
#endregion
}
}
diff --git a/ScreenManager/PlayerScreen/UserInterface/PlayerScreenUserInterface2.Designer.cs b/ScreenManager/PlayerScreen/UserInterface/PlayerScreenUserInterface2.Designer.cs
index 4e6060e72..0067756ad 100644
--- a/ScreenManager/PlayerScreen/UserInterface/PlayerScreenUserInterface2.Designer.cs
+++ b/ScreenManager/PlayerScreen/UserInterface/PlayerScreenUserInterface2.Designer.cs
@@ -654,12 +654,13 @@ private void InitializeComponent()
this.ImageResizerNE.Cursor = System.Windows.Forms.Cursors.SizeNESW;
this.ImageResizerNE.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.ImageResizerNE.Image = global::Kinovea.ScreenManager.Properties.Resources.resizer4;
- this.ImageResizerNE.Location = new System.Drawing.Point(127, 70);
+ this.ImageResizerNE.Location = new System.Drawing.Point(92, 70);
this.ImageResizerNE.Name = "ImageResizerNE";
this.ImageResizerNE.Size = new System.Drawing.Size(6, 6);
this.ImageResizerNE.TabIndex = 9;
this.ImageResizerNE.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ImageResizerNE_MouseMove);
this.ImageResizerNE.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.Resizers_MouseDoubleClick);
+ this.ImageResizerNE.MouseUp += new System.Windows.Forms.MouseEventHandler(this.Resizers_MouseUp);
//
// ImageResizerNW
//
@@ -668,12 +669,13 @@ private void InitializeComponent()
this.ImageResizerNW.Cursor = System.Windows.Forms.Cursors.SizeNWSE;
this.ImageResizerNW.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.ImageResizerNW.Image = global::Kinovea.ScreenManager.Properties.Resources.resizer4;
- this.ImageResizerNW.Location = new System.Drawing.Point(92, 70);
+ this.ImageResizerNW.Location = new System.Drawing.Point(57, 70);
this.ImageResizerNW.Name = "ImageResizerNW";
this.ImageResizerNW.Size = new System.Drawing.Size(6, 6);
this.ImageResizerNW.TabIndex = 8;
this.ImageResizerNW.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ImageResizerNW_MouseMove);
this.ImageResizerNW.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.Resizers_MouseDoubleClick);
+ this.ImageResizerNW.MouseUp += new System.Windows.Forms.MouseEventHandler(this.Resizers_MouseUp);
//
// ImageResizerSW
//
@@ -682,12 +684,13 @@ private void InitializeComponent()
this.ImageResizerSW.Cursor = System.Windows.Forms.Cursors.SizeNESW;
this.ImageResizerSW.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.ImageResizerSW.Image = global::Kinovea.ScreenManager.Properties.Resources.resizer4;
- this.ImageResizerSW.Location = new System.Drawing.Point(92, 95);
+ this.ImageResizerSW.Location = new System.Drawing.Point(57, 95);
this.ImageResizerSW.Name = "ImageResizerSW";
this.ImageResizerSW.Size = new System.Drawing.Size(6, 6);
this.ImageResizerSW.TabIndex = 7;
this.ImageResizerSW.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ImageResizerSW_MouseMove);
this.ImageResizerSW.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.Resizers_MouseDoubleClick);
+ this.ImageResizerSW.MouseUp += new System.Windows.Forms.MouseEventHandler(this.Resizers_MouseUp);
//
// ImageResizerSE
//
@@ -697,12 +700,13 @@ private void InitializeComponent()
this.ImageResizerSE.FlatStyle = System.Windows.Forms.FlatStyle.Flat;
this.ImageResizerSE.ForeColor = System.Drawing.Color.Transparent;
this.ImageResizerSE.Image = global::Kinovea.ScreenManager.Properties.Resources.resizer4;
- this.ImageResizerSE.Location = new System.Drawing.Point(127, 95);
+ this.ImageResizerSE.Location = new System.Drawing.Point(92, 95);
this.ImageResizerSE.Name = "ImageResizerSE";
this.ImageResizerSE.Size = new System.Drawing.Size(6, 6);
this.ImageResizerSE.TabIndex = 6;
this.ImageResizerSE.MouseMove += new System.Windows.Forms.MouseEventHandler(this.ImageResizerSE_MouseMove);
this.ImageResizerSE.MouseDoubleClick += new System.Windows.Forms.MouseEventHandler(this.Resizers_MouseDoubleClick);
+ this.ImageResizerSE.MouseUp += new System.Windows.Forms.MouseEventHandler(this.Resizers_MouseUp);
//
// panelDebug
//
diff --git a/ScreenManager/PlayerScreen/UserInterface/PlayerScreenUserInterface2.cs b/ScreenManager/PlayerScreen/UserInterface/PlayerScreenUserInterface2.cs
index d6a92e987..0eace5500 100644
--- a/ScreenManager/PlayerScreen/UserInterface/PlayerScreenUserInterface2.cs
+++ b/ScreenManager/PlayerScreen/UserInterface/PlayerScreenUserInterface2.cs
@@ -291,6 +291,7 @@ public Bitmap SyncMergeImage
private Magnifier m_Magnifier = new Magnifier();
private Double m_fHighSpeedFactor = 1.0f; // When capture fps is different from Playing fps.
private CoordinateSystem m_CoordinateSystem = new CoordinateSystem();
+ private System.Windows.Forms.Timer m_DeselectionTimer = new System.Windows.Forms.Timer();
#region Context Menus
private ContextMenuStrip popMenu = new ContextMenuStrip();
@@ -306,6 +307,7 @@ public Bitmap SyncMergeImage
private ToolStripMenuItem mnuTrackTrajectory = new ToolStripMenuItem();
private ToolStripMenuItem mnuGotoKeyframe = new ToolStripMenuItem();
private ToolStripSeparator mnuSepDrawing = new ToolStripSeparator();
+ private ToolStripSeparator mnuSepDrawing2 = new ToolStripSeparator();
private ToolStripMenuItem mnuDeleteDrawing = new ToolStripMenuItem();
private ToolStripMenuItem mnuShowMeasure = new ToolStripMenuItem();
private ToolStripMenuItem mnuSealMeasure = new ToolStripMenuItem();
@@ -384,6 +386,10 @@ public PlayerScreenUserInterface(FrameServerPlayer _FrameServer, IPlayerScreenUI
m_CallbackTimerEventHandler = new TimerEventHandler(MultimediaTimerTick);
m_CallbackPlayLoop = new CallbackPlayLoop(PlayLoop);
+ m_DeselectionTimer.Interval = 3000;
+ m_DeselectionTimer.Tick += new EventHandler(DeselectionTimer_OnTick);
+
+
//SetupDebugPanel();
}
#endregion
@@ -842,6 +848,7 @@ public bool OnKeyPress(Keys _keycode)
case Keys.Add:
{
IncreaseDirectZoom();
+ m_FrameServer.Metadata.ResizeFinished();
bWasHandled = true;
break;
}
@@ -849,6 +856,7 @@ public bool OnKeyPress(Keys _keycode)
{
// Decrease Zoom.
DecreaseDirectZoom();
+ m_FrameServer.Metadata.ResizeFinished();
bWasHandled = true;
break;
}
@@ -927,6 +935,58 @@ public void UpdateImageSize()
StretchSqueezeSurface();
}
+ public void AddSVGDrawing(string _filename)
+ {
+ // Add an SVG drawing from the file.
+
+ // Mimick all the actions that are normally taken when we select a drawing tool and click on the image.
+
+ if(m_FrameServer.VideoFile != null && m_FrameServer.VideoFile.Loaded && File.Exists(_filename))
+ {
+ if(m_bIsCurrentlyPlaying)
+ {
+ StopPlaying();
+ m_PlayerScreenUIHandler.PlayerScreenUI_PauseAsked();
+ ActivateKeyframe(m_iCurrentPosition);
+ }
+
+ PrepareKeyframesDock();
+ m_FrameServer.Metadata.AllDrawingTextToNormalMode();
+ m_FrameServer.Metadata.SelectedTrack = -1;
+ m_FrameServer.Metadata.SelectedChrono = -1;
+
+ try
+ {
+ // Add a KeyFrame here if it doesn't exist.
+ AddKeyframe();
+
+ // The drawing is initialized at the center of the image.
+ Point imageCenter = new Point(m_FrameServer.Metadata.ImageSize.Width / 2, m_FrameServer.Metadata.ImageSize.Height / 2);
+
+ DrawingSVG dsvg = new DrawingSVG(imageCenter.X,
+ imageCenter.Y,
+ m_iCurrentPosition,
+ m_FrameServer.Metadata.AverageTimeStampsPerFrame,
+ _filename);
+
+ m_FrameServer.Metadata[m_iActiveKeyFrameIndex].AddDrawing(dsvg);
+ }
+ catch
+ {
+ // An error occurred during the creation.
+ // example : external DTD an no network or invalid svg file.
+ // TODO: inform the user.
+ }
+
+ m_FrameServer.Metadata.SelectedDrawingFrame = -1;
+ m_FrameServer.Metadata.SelectedDrawing = -1;
+
+ m_ActiveTool = DrawingToolType.Pointer;
+ SetCursor(m_DrawingTools[(int)m_ActiveTool].GetCursor(Color.Empty, 0));
+
+ pbSurfaceScreen.Invalidate();
+ }
+ }
#endregion
#region Various Inits & Setups
@@ -1097,7 +1157,7 @@ private void BuildContextMenus()
mnuDeleteDrawing.Click += new EventHandler(mnuDeleteDrawing_Click);
mnuShowMeasure.Click += new EventHandler(mnuShowMeasure_Click);
mnuSealMeasure.Click += new EventHandler(mnuSealMeasure_Click);
- popMenuDrawings.Items.AddRange(new ToolStripItem[] { mnuConfigureDrawing, mnuConfigureFading, new ToolStripSeparator(), mnuTrackTrajectory, mnuShowMeasure, mnuSealMeasure, mnuGotoKeyframe, mnuSepDrawing, mnuDeleteDrawing });
+ popMenuDrawings.Items.AddRange(new ToolStripItem[] { mnuConfigureDrawing, mnuConfigureFading, mnuSepDrawing, mnuTrackTrajectory, mnuShowMeasure, mnuSealMeasure, mnuGotoKeyframe, mnuSepDrawing2, mnuDeleteDrawing });
// 3. Tracking pop menu (Restart, Stop tracking)
mnuStopTracking.Click += new EventHandler(mnuStopTracking_Click);
@@ -2032,6 +2092,7 @@ private void ToggleStretchMode()
}
}
StretchSqueezeSurface();
+ m_FrameServer.Metadata.ResizeFinished();
pbSurfaceScreen.Invalidate();
}
private void ImageResizerSE_MouseMove(object sender, MouseEventArgs e)
@@ -2092,17 +2153,11 @@ private void ResizeImage(int _iTargetWidth, int _iTargetHeight)
}
private void Resizers_MouseDoubleClick(object sender, MouseEventArgs e)
{
- // Maximiser l'écran ou repasser à la taille originale.
- if (!m_bStretchModeOn)
- {
- m_bStretchModeOn = true;
- }
- else
- {
- m_FrameServer.CoordinateSystem.Stretch = 1;
- m_bStretchModeOn = false;
- }
- StretchSqueezeSurface();
+ ToggleStretchMode();
+ }
+ private void Resizers_MouseUp(object sender, MouseEventArgs e)
+ {
+ m_FrameServer.Metadata.ResizeFinished();
pbSurfaceScreen.Invalidate();
}
#endregion
@@ -2515,6 +2570,20 @@ private int GetPlaybackFrameInterval()
return 40;
}
}
+
+ private void DeselectionTimer_OnTick(object sender, EventArgs e)
+ {
+ // Deselect the currently selected drawing.
+ // This is used for drawings that must show extra stuff for being transformed, but we
+ // don't want to show the extra stuff all the time for clarity.
+
+ m_FrameServer.Metadata.SelectedDrawingFrame = -1;
+ m_FrameServer.Metadata.SelectedDrawing = -1;
+ log.Debug("Deselection timer fired.");
+ m_DeselectionTimer.Stop();
+ pbSurfaceScreen.Invalidate();
+ }
+
#endregion
#region Culture
@@ -2650,6 +2719,8 @@ private void SurfaceScreen_MouseDown(object sender, MouseEventArgs e)
{
if (m_FrameServer.VideoFile.Loaded)
{
+ m_DeselectionTimer.Stop();
+
if (e.Button == MouseButtons.Left)
{
// Magnifier can be moved even when the video is playing.
@@ -2831,17 +2902,20 @@ private void SurfaceScreen_MouseDown(object sender, MouseEventArgs e)
// We use temp variables because ToolStripMenuItem.Visible always returns false...
bool isCross = (ad is DrawingCross2D);
bool isLine = (ad is DrawingLine2D);
- bool fadingVisible = m_PrefManager.DefaultFading.Enabled;
- bool gotoVisible = (m_PrefManager.DefaultFading.Enabled && (ad.infosFading.ReferenceTimestamp != m_iCurrentPosition));
+ bool fadingVisible = m_PrefManager.DefaultFading.Enabled && !(ad is DrawingSVG);
+ bool gotoVisible = (fadingVisible && (ad.infosFading.ReferenceTimestamp != m_iCurrentPosition));
+ bool configVisible = !(ad is DrawingSVG);
mnuTrackTrajectory.Visible = isCross;
mnuTrackTrajectory.Enabled = (ad.infosFading.ReferenceTimestamp == m_iCurrentPosition);
mnuConfigureFading.Visible = fadingVisible;
+ mnuConfigureDrawing.Visible = configVisible;
mnuGotoKeyframe.Visible = gotoVisible;
mnuShowMeasure.Visible = isLine;
mnuSealMeasure.Visible = isLine;
- mnuSepDrawing.Visible = isCross || gotoVisible || isLine;
+ mnuSepDrawing.Visible = !(ad is DrawingSVG);
+ mnuSepDrawing2.Visible = isCross || gotoVisible || isLine;
// "Color & Size" or "Color" depending on drawing type.
SetPopupConfigureParams(ad);
@@ -3017,6 +3091,10 @@ private void SurfaceScreen_MouseUp(object sender, MouseEventArgs e)
IUndoableCommand cad = new CommandAddDrawing(DoInvalidate, DoDrawingUndrawn, m_FrameServer.Metadata, m_FrameServer.Metadata[m_iActiveKeyFrameIndex].Position);
CommandManager cm = CommandManager.Instance();
cm.LaunchUndoableCommand(cad);
+
+ // Deselect the drawing we just added.
+ m_FrameServer.Metadata.SelectedDrawingFrame = -1;
+ m_FrameServer.Metadata.SelectedDrawing = -1;
}
else
{
@@ -3031,12 +3109,25 @@ private void SurfaceScreen_MouseUp(object sender, MouseEventArgs e)
{
SetCursor(m_DrawingTools[(int)DrawingToolType.Pointer].GetCursor(Color.Empty, 0));
((DrawingToolPointer)m_DrawingTools[(int)DrawingToolType.Pointer]).OnMouseUp();
+
+ // If we were resizing an SVG drawing, trigger a render.
+ // TODO: this is currently triggered on every mouse up, not only on resize !
+ int selectedFrame = m_FrameServer.Metadata.SelectedDrawingFrame;
+ int selectedDrawing = m_FrameServer.Metadata.SelectedDrawing;
+ if(selectedFrame != -1 && selectedDrawing != -1)
+ {
+ DrawingSVG d = m_FrameServer.Metadata.Keyframes[selectedFrame].Drawings[selectedDrawing] as DrawingSVG;
+ if(d != null)
+ {
+ d.ResizeFinished();
+ }
+ }
}
- if (m_iActiveKeyFrameIndex >= 0)
+ // TODO: start deselection timer.
+ if (m_FrameServer.Metadata.SelectedDrawingFrame != -1 && m_FrameServer.Metadata.SelectedDrawing != -1)
{
- m_FrameServer.Metadata.SelectedDrawingFrame = -1;
- m_FrameServer.Metadata.SelectedDrawing = -1;
+ m_DeselectionTimer.Start();
}
pbSurfaceScreen.Invalidate();
@@ -3066,6 +3157,8 @@ private void SurfaceScreen_MouseDoubleClick(object sender, MouseEventArgs e)
}
else if (m_FrameServer.Metadata.IsOnDrawing(m_iActiveKeyFrameIndex, m_DescaledMouse, m_iCurrentPosition))
{
+ // Double click on a drawing:
+ // turn text tool into edit mode, launch config for others, SVG don't have a config.
AbstractDrawing ad = m_FrameServer.Metadata.Keyframes[m_FrameServer.Metadata.SelectedDrawingFrame].Drawings[m_FrameServer.Metadata.SelectedDrawing];
if (ad is DrawingText)
{
@@ -3073,7 +3166,7 @@ private void SurfaceScreen_MouseDoubleClick(object sender, MouseEventArgs e)
m_ActiveTool = DrawingToolType.Text;
m_bTextEdit = true;
}
- else
+ else if(!(ad is DrawingSVG))
{
mnuConfigureDrawing_Click(null, EventArgs.Empty);
}
@@ -4015,7 +4108,7 @@ private void btnColorProfile_Click(object sender, EventArgs e)
UpdateCursor();
}
- private void SetCursor(Cursor _cur)
+ public void SetCursor(Cursor _cur)
{
if (m_ActiveTool != DrawingToolType.Pointer)
{
@@ -4405,6 +4498,7 @@ private void mnuMagnifierDirect_Click(object sender, EventArgs e)
m_FrameServer.CoordinateSystem.Zoom = m_Magnifier.ZoomFactor;
m_FrameServer.CoordinateSystem.RelocateZoomWindow(m_Magnifier.MagnifiedCenter);
DisableMagnifier();
+ m_FrameServer.Metadata.ResizeFinished();
pbSurfaceScreen.Invalidate();
}
private void mnuMagnifier150_Click(object sender, EventArgs e)
@@ -4495,6 +4589,7 @@ private void UnzoomDirectZoom()
{
m_FrameServer.CoordinateSystem.ReinitZoom();
((DrawingToolPointer)m_DrawingTools[(int)DrawingToolType.Pointer]).SetZoomLocation(m_FrameServer.CoordinateSystem.Location);
+ m_FrameServer.Metadata.ResizeFinished();
}
private void IncreaseDirectZoom()
{
@@ -4508,6 +4603,7 @@ private void IncreaseDirectZoom()
{
m_FrameServer.CoordinateSystem.Zoom += 0.20f;
RelocateDirectZoom();
+ m_FrameServer.Metadata.ResizeFinished();
pbSurfaceScreen.Invalidate();
}
}
@@ -4517,6 +4613,7 @@ private void DecreaseDirectZoom()
{
m_FrameServer.CoordinateSystem.Zoom -= 0.20f;
RelocateDirectZoom();
+ m_FrameServer.Metadata.ResizeFinished();
pbSurfaceScreen.Invalidate();
}
}
diff --git a/ScreenManager/ScreenManager.cs b/ScreenManager/ScreenManager.cs
index 3815d73a6..35fbe3b5a 100644
--- a/ScreenManager/ScreenManager.cs
+++ b/ScreenManager/ScreenManager.cs
@@ -105,6 +105,9 @@ public bool CancelLastCommand
// Video Filters
private AbstractVideoFilter[] m_VideoFilters;
+ // SVG files
+ private List m_svgFiles = new List();
+
//Menus
public ToolStripMenuItem mnuCloseFile = new ToolStripMenuItem();
public ToolStripMenuItem mnuCloseFile2 = new ToolStripMenuItem();
@@ -126,6 +129,7 @@ public bool CancelLastCommand
private ToolStripMenuItem mnuFormatForce43 = new ToolStripMenuItem();
private ToolStripMenuItem mnuFormatForce169 = new ToolStripMenuItem();
public ToolStripMenuItem mnuMirror = new ToolStripMenuItem();
+ public ToolStripMenuItem mnuSVGTools = new ToolStripMenuItem();
public ToolStripMenuItem mnuGrid = new ToolStripMenuItem();
public ToolStripMenuItem mnuGridPerspective = new ToolStripMenuItem();
public ToolStripMenuItem mnuCoordinateAxis = new ToolStripMenuItem();
@@ -177,6 +181,7 @@ public ScreenManagerKernel()
PlugDelegates();
InitializeVideoFilters();
+ InitializeSVGDrawings();
// Registers our exposed functions to the DelegatePool.
DelegatesPool dp = DelegatesPool.Instance();
@@ -205,6 +210,25 @@ private void InitializeVideoFilters()
m_VideoFilters[(int)VideoFilterType.Reverse] = new VideoFilterReverse();
m_VideoFilters[(int)VideoFilterType.Sandbox] = new VideoFilterSandbox();
}
+ private void InitializeSVGDrawings()
+ {
+ // Gather the list of SVG files in the guides directory.
+
+ // Look for palettes files in a specific directory.
+ String svgPath = Path.GetDirectoryName(Application.ExecutablePath) + "\\guides\\";
+
+ if(Directory.Exists(svgPath))
+ {
+ // Loop each file in the directory looking for .svg files.
+ foreach (string file in Directory.GetFiles(svgPath))
+ {
+ if (Path.GetExtension(file).Equals(".svg"))
+ {
+ m_svgFiles.Add(file);
+ }
+ }
+ }
+ }
public void PrepareScreen()
{
// Prepare a screen to hold the command line argument file.
@@ -431,6 +455,30 @@ public void ExtendMenu(ToolStrip _menu)
mnuMirror.Click += new EventHandler(mnuMirrorOnClick);
mnuMirror.MergeAction = MergeAction.Append;
+ #region SVG drawings
+ mnuSVGTools = new ToolStripMenuItem();
+ mnuSVGTools.Text = "Observational Reference";
+ mnuSVGTools.MergeAction = MergeAction.Append;
+
+ if(m_svgFiles.Count > 0)
+ {
+ ToolStripItem[] svgMenus = new ToolStripItem[m_svgFiles.Count];
+ for(int i = 0; i 0;
mnuGrid.Enabled = true;
mnuGridPerspective.Enabled = true;
mnuCoordinateAxis.Enabled = true;
-
+
mnuDeinterlace.Checked = player.Deinterlaced;
mnuMirror.Checked = player.Mirrored;
mnuGrid.Checked = player.ShowGrid;
@@ -1299,6 +1349,7 @@ private void DoOrganizeMenu()
// Image
mnuDeinterlace.Enabled = false;
mnuMirror.Enabled = false;
+ mnuSVGTools.Enabled = false;
mnuGrid.Enabled = false;
mnuGridPerspective.Enabled = false;
mnuCoordinateAxis.Enabled = false;
@@ -1550,7 +1601,6 @@ private void ConfigureImageFormatMenus(PlayerScreen _player)
mnuFormatForce169.Checked = false;
}
}
-
#region Menus events handlers
#region File
@@ -2385,6 +2435,29 @@ private void mnuMirrorOnClick(object sender, EventArgs e)
player.Mirrored = mnuMirror.Checked;
}
}
+ private void mnuSVGDrawing_OnClick(object sender, EventArgs e)
+ {
+ // One of the dynamically added SVG tools menu has been clicked.
+
+ // Add a drawing of the right type to the active screen.
+ ToolStripMenuItem menu = sender as ToolStripMenuItem;
+ if(menu != null && m_ActiveScreen != null && m_ActiveScreen.CapabilityDrawings)
+ {
+ string svgFile = menu.Tag as string;
+ if(svgFile != null)
+ {
+ // check / uncheck the menu.
+ // menu.Checked = !menu.Checked;
+
+ // Add the corresponding svg drawing in the active screen.
+ if(m_ActiveScreen is PlayerScreen)
+ {
+ ((PlayerScreen)m_ActiveScreen).AddSVGDrawing(svgFile);
+ }
+ }
+ }
+
+ }
private void mnuGrid_OnClick(object sender, EventArgs e)
{
if (m_ActiveScreen != null && m_ActiveScreen.CapabilityDrawings)
diff --git a/ScreenManager/ScreenManager.csproj b/ScreenManager/ScreenManager.csproj
index ada6851f0..cbadd61ad 100644
--- a/ScreenManager/ScreenManager.csproj
+++ b/ScreenManager/ScreenManager.csproj
@@ -11,7 +11,6 @@
Kinovea.ScreenManager
C:\Documents and Settings\Administrateur\Application Data\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis
v2.0
- bin\x86\Debug\Kinovea.ScreenManager.xml
True
False
4
@@ -98,6 +97,21 @@
..\PlayerServer\bin\PlayerServer.dll
+
+ ..\Refs\SharpVectorBindings.dll
+
+
+ ..\Refs\SharpVectorCss.dll
+
+
+ ..\Refs\SharpVectorDom.dll
+
+
+ ..\Refs\SharpVectorObjectModel.dll
+
+
+ ..\Refs\SharpVectorUtil.dll
+
@@ -145,6 +159,7 @@
+
@@ -426,6 +441,10 @@
{8AA92254-A016-4A84-925C-F5B07E02F8A8}
Services
+
+ {0AD99D97-24ED-484D-9430-9905811F8A4F}
+ SharpVectorRenderingEngine
+
diff --git a/Services/Services.csproj b/Services/Services.csproj
index 3cc05cedf..6e05cb204 100644
--- a/Services/Services.csproj
+++ b/Services/Services.csproj
@@ -11,7 +11,6 @@
Kinovea.Services
v2.0
C:\Documents and Settings\Administrateur\Application Data\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis
- bin\x86\Debug\Kinovea.Services.xml
False
False
4
diff --git a/SharpVectorRenderingEngine/AssemblyInfo.cs b/SharpVectorRenderingEngine/AssemblyInfo.cs
new file mode 100644
index 000000000..b65541fd2
--- /dev/null
+++ b/SharpVectorRenderingEngine/AssemblyInfo.cs
@@ -0,0 +1,60 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
+
+//
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+//
+[assembly: AssemblyTitle("SVG 1.1 Rendering Engine")]
+[assembly: AssemblyDescription("Default Rendering Engine for GDI+")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("SharpVectorGraphics")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+//
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Revision and Build Numbers
+// by using the '*' as shown below:
+
+[assembly: AssemblyVersion("0.3.1.*")]
+
+//
+// In order to sign your assembly you must specify a key to use. Refer to the
+// Microsoft .NET Framework documentation for more information on assembly signing.
+//
+// Use the attributes below to control which key is used for signing.
+//
+// Notes:
+// (*) If no key is specified, the assembly is not signed.
+// (*) KeyName refers to a key that has been installed in the Crypto Service
+// Provider (CSP) on your machine. KeyFile refers to a file which contains
+// a key.
+// (*) If the KeyFile and the KeyName values are both specified, the
+// following processing occurs:
+// (1) If the KeyName can be found in the CSP, that key is used.
+// (2) If the KeyName does not exist and the KeyFile does exist, the key
+// in the KeyFile is installed into the CSP and used.
+// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
+// When specifying the KeyFile, the location of the KeyFile should be
+// relative to the project output directory which is
+// %Project Directory%\obj\. For example, if your KeyFile is
+// located in the project directory, you would specify the AssemblyKeyFile
+// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
+// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
+// documentation for more information on this.
+//
+[assembly: Guid("F6052893-60C5-4ba7-A0B1-3F7ED33492D9")]
+/*[assembly: AssemblyDelaySign(false)]
+[assembly: AssemblyKeyFile("..\\..\\..\\SVGSharp.key")]
+[assembly: AssemblyKeyName("")]*/
diff --git a/SharpVectorRenderingEngine/SharpVectorRenderingEngine.csproj b/SharpVectorRenderingEngine/SharpVectorRenderingEngine.csproj
new file mode 100644
index 000000000..3935bf55a
--- /dev/null
+++ b/SharpVectorRenderingEngine/SharpVectorRenderingEngine.csproj
@@ -0,0 +1,191 @@
+
+
+ Local
+ 9.0.30729
+ 2.0
+ {0AD99D97-24ED-484D-9430-9905811F8A4F}
+ Debug
+ AnyCPU
+
+
+
+
+ SharpVectorRenderingEngine
+
+
+ JScript
+ Grid
+ IE50
+ false
+ Library
+ SharpVectorRenderingEngine
+ OnBuildSuccess
+
+
+
+
+
+
+ 0.0
+ C:\Documents and Settings\Administrateur\Application Data\ICSharpCode/SharpDevelop3.0\Settings.SourceAnalysis
+ v2.0
+ bin\x86\Debug\
+ False
+ False
+ None
+ 4
+ false
+ false
+
+
+
+
+ false
+ prompt
+
+
+
+
+ false
+ prompt
+
+
+
+
+ false
+ prompt
+
+
+
+
+ false
+ prompt
+
+
+
+
+ false
+ prompt
+
+
+ Auto
+ AnyCPU
+ false
+ 285212672
+ 4096
+
+
+ DEBUG;TRACE
+ false
+ false
+
+
+ TRACE
+ true
+ false
+
+
+ TRACE
+ true
+ false
+
+
+ TEST
+ false
+ false
+
+
+ False
+ False
+
+
+ False
+ Auto
+ 4194304
+ x86
+ 1024
+
+
+
+ ..\Refs\SharpVectorBindings.dll
+
+
+ ..\Refs\SharpVectorCss.dll
+
+
+ ..\Refs\SharpVectorDom.dll
+
+
+ ..\Refs\SharpVectorObjectModel.dll
+
+
+ ..\Refs\SharpVectorUtil.dll
+
+
+ System
+
+
+ System.Data
+
+
+ System.Drawing
+
+
+ System.Drawing.Design
+
+
+ System.Windows.Forms
+
+
+ System.XML
+
+
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+ Code
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/GdiRenderer.cs b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/GdiRenderer.cs
new file mode 100644
index 000000000..de434a20b
--- /dev/null
+++ b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/GdiRenderer.cs
@@ -0,0 +1,855 @@
+using System;
+using System.Collections;
+using System.Drawing;
+using System.Windows.Forms;
+using System.Xml;
+using SharpVectors.Collections;
+using SharpVectors.Dom.Events;
+using SharpVectors.Dom.Svg;
+using SharpVectors.Dom.Svg.Rendering;
+
+namespace SharpVectors.Renderer.Gdi
+{
+ ///
+ /// Renders a Svg image to GDI+
+ ///
+ public class GdiRenderer : ISvgRenderer, IDisposable
+ {
+ #region Private Fields
+
+ ///
+ /// A counter that tracks the next hit color.
+ ///
+ private int counter = 0;
+
+ ///
+ /// Maps a 'hit color' to a graphics node.
+ ///
+ ///
+ /// The 'hit color' is an integer identifier that identifies the
+ /// graphics node that drew it. When 'hit colors' are drawn onto
+ /// a bitmap (ie. idMapRaster the 'hit color'
+ /// of each pixel with the help of graphicsNodes can identify for a given x, y coordinate the
+ /// relevant graphics node a mouse event should be dispatched to.
+ ///
+ private Hashtable graphicsNodes = new Hashtable();
+
+ ///
+ /// The bitmap containing the rendered Svg image.
+ ///
+ private Bitmap rasterImage;
+
+ ///
+ /// A secondary back-buffer used for invalidation repaints. The invalidRect will
+ /// be bitblt to the rasterImage front buffer
+ ///
+ private Bitmap invalidatedRasterImage;
+
+ ///
+ /// A bitmap image that consists of 'hit color' instead of visual
+ /// color. A 'hit color' is an integer identifier that identifies
+ /// the graphics node that drew it. A 'hit color' can therefore
+ /// identify the graphics node that corresponds an x-y coordinates.
+ ///
+ private Bitmap idMapRaster;
+
+ ///
+ /// Maintains a mapping from namespace:tagname pair to a type. This
+ /// object is used facilitate instantiate objects of the type
+ /// corresponding a given namespace:tagname pair.
+ ///
+ private TypeDictionary nodeByTagName;
+
+ ///
+ /// The renderer's GraphicsWrapper
+ /// object.
+ ///
+ private GraphicsWrapper graphics;
+
+ ///
+ /// The renderer's back color.
+ ///
+ private Color backColor;
+
+ ///
+ /// The renderer's SvgWindow object.
+ ///
+ private ISvgWindow window;
+
+ ///
+ ///
+ ///
+ private IEventTarget currentTarget = null;
+ private IEventTarget currentDownTarget = null;
+ private float currentDownX = 0;
+ private float currentDownY = 0;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the GdiRenderer class.
+ ///
+ public GdiRenderer()
+ {
+ nodeByTagName = new TypeDictionary();
+ string ns = SvgDocument.SvgNamespace;
+
+ SetTagNameNodeType(ns, "svg", typeof(SvgElementGraphicsNode));
+ SetTagNameNodeType(ns, "image", typeof(SvgImageElementGraphicsNode));
+ SetTagNameNodeType(ns, "marker", typeof(SvgMarkerGraphicsNode));
+
+ backColor = Color.White;
+ }
+
+ #endregion
+
+ #region Properties
+
+ ///
+ /// Gets a bitmap image of the a rendered Svg document.
+ ///
+ public Bitmap RasterImage
+ {
+ get
+ {
+ return rasterImage;
+ }
+ }
+
+ ///
+ /// Gets the image map of the rendered Svg document. This
+ /// is a picture of how the renderer will map the (x,y) positions
+ /// of mouse events to objects. You can display this raster
+ /// to help in debugging of hit testing.
+ ///
+ public Bitmap IdMapRaster
+ {
+ get
+ {
+ return idMapRaster;
+ }
+ }
+
+ ///
+ /// Gets or sets the Window of the
+ /// renderer.
+ ///
+ ///
+ /// The Window of the renderer.
+ ///
+ public ISvgWindow Window
+ {
+ get
+ {
+ return window;
+ }
+ set
+ {
+ window = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the back color of the renderer.
+ ///
+ ///
+ /// The back color of the renderer.
+ ///
+ public Color BackColor
+ {
+ get
+ {
+ return backColor;
+ }
+ set
+ {
+ backColor = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the GraphicsWrapper
+ /// object associated with this renderer.
+ ///
+ ///
+ /// The GraphicsWrapper object
+ /// associated with this renderer.
+ ///
+ public GraphicsWrapper GraphicsWrapper
+ {
+ get
+ {
+ return graphics;
+ }
+ set
+ {
+ graphics = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the Graphics object
+ /// associated with this renderer.
+ ///
+ ///
+ /// The Graphics object associated
+ /// with this renderer.
+ ///
+ public Graphics Graphics
+ {
+ get
+ {
+ return graphics.Graphics;
+ }
+ set
+ {
+ graphics.Graphics = value;
+ }
+ }
+
+ #endregion
+
+ #region Public methods
+
+
+ public bool HitTest(int x, int y)
+ {
+ bool hit = false;
+
+ Color pixel = idMapRaster.GetPixel(x, y);
+ GraphicsNode grNode = _getGraphicsNodeFromColor(pixel);
+ if (grNode != null)
+ {
+ hit = true;
+ }
+
+ return hit;
+ }
+
+
+ ///
+ /// Creates a new ISvgRenderer object.
+ /// TODO: I don't know what this method is for apart from implementing
+ /// an interface method that isn't used at all.
+ ///
+ ///
+ /// A new ISvgRenderer object.
+ ///
+ public ISvgRenderer GetRenderer()
+ {
+ return new GdiRenderer();
+ }
+
+ ///
+ /// Adds an entry into the nodeByTagName mapping.
+ ///
+ ///
+ /// The namespaceURI of the mapping key.
+ ///
+ ///
+ /// The localName of the mapping key.
+ ///
+ ///
+ /// The Xml element type corresponding to the mapping key.
+ ///
+ public void SetTagNameNodeType(
+ string namespaceURI,
+ string localName,
+ Type type)
+ {
+ nodeByTagName[namespaceURI + ":" + localName] = type;
+ }
+
+ ///
+ /// Generates a new RenderingNode that
+ /// corresponds to the given XmlElement.
+ ///
+ ///
+ /// The XmlElement node for which to generate
+ /// a new RenderingNode object.
+ ///
+ ///
+ /// The generated RenderingNode that
+ /// corresponds to the given XmlElement.
+ ///
+ public RenderingNode GetRenderingNode(
+ ISvgElement node)
+ {
+ SvgElement svgNode = (SvgElement)node;
+ string name = svgNode.NamespaceURI + ":" + svgNode.LocalName;
+ RenderingNode result;
+
+ if (nodeByTagName.ContainsKey(name))
+ {
+ object[] args = new object[] { svgNode };
+ result = (RenderingNode)nodeByTagName.CreateInstance(name, args);
+ }
+ else if (node is ISharpGDIPath)
+ {
+ result = new GDIPathGraphicsNode(svgNode);
+ }
+ else
+ {
+ result = new GraphicsNode(svgNode);
+ }
+
+ return result;
+ }
+
+ ///
+ /// Generates a new RenderingNode that
+ /// corresponds to the given Uri.
+ ///
+ ///
+ /// The base Uri.
+ ///
+ ///
+ /// The url.
+ ///
+ ///
+ /// The generated RenderingNode that
+ /// corresponds to the given Uri.
+ ///
+ public RenderingNode GetGraphicsNodeByUri(
+ string baseUri,
+ string url)
+ {
+ if (url.StartsWith("#"))
+ {
+ // do nothing
+ }
+ else if (baseUri != "")
+ {
+ Uri absoluteUri = new Uri(new Uri(baseUri), url);
+ url = absoluteUri.AbsoluteUri;
+ }
+ else
+ {
+ // TODO: Handle xml:base here?
+ // Right now just skip this... it can't be resolved, must assume it is absolute
+ }
+ ISvgElement elm = ((SvgDocument)Window.Document).GetNodeByUri(url) as ISvgElement;
+
+ if (elm != null)
+ {
+ return GetRenderingNode(elm);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ ///
+ /// BeforeRender - Make sure we have a Graphics object to render to.
+ /// If we don't have one, then create one to matche the SvgWindow's
+ /// physical dimensions.
+ ///
+ internal void RendererBeforeRender()
+ {
+ // Testing for null here allows "advanced" developers to create their own Graphics object for rendering
+ if (graphics == null)
+ {
+ // Get the current SVGWindow's width and height
+ int innerWidth = (int)window.InnerWidth;
+ int innerHeight = (int)window.InnerHeight;
+
+ // Make sure we have an actual area to render to
+ if (innerWidth > 0 && innerHeight > 0)
+ {
+ // See if we already have a rasterImage that matches the current SVGWindow dimensions
+ if (rasterImage == null || rasterImage.Width != innerWidth || rasterImage.Height != innerHeight)
+ {
+ // Nope, so create one
+ if (rasterImage != null)
+ {
+ rasterImage.Dispose();
+ rasterImage = null;
+ }
+ rasterImage = new Bitmap(innerWidth, innerHeight);
+ }
+
+ // Maybe we are only repainting an invalidated section
+ if (invalidRect != RectangleF.Empty)
+ {
+ // TODO: Worry about pan...
+ if (invalidRect.X < 0)
+ invalidRect.X = 0;
+ if (invalidRect.Y < 0)
+ invalidRect.Y = 0;
+ if (invalidRect.Right > innerWidth)
+ invalidRect.Width = innerWidth - invalidRect.X;
+ if (invalidRect.Bottom > innerHeight)
+ invalidRect.Height = innerHeight - invalidRect.Y;
+
+ if (invalidatedRasterImage == null || invalidatedRasterImage.Width < invalidRect.Right ||
+ invalidatedRasterImage.Height < invalidRect.Bottom)
+ {
+ // Nope, so create one
+ if (invalidatedRasterImage != null)
+ {
+ invalidatedRasterImage.Dispose();
+ invalidatedRasterImage = null;
+ }
+ invalidatedRasterImage = new Bitmap((int)invalidRect.Right, (int)invalidRect.Bottom);
+ }
+ // Make a GraphicsWrapper object from the regionRasterImage and clear it to the background color
+ graphics = GraphicsWrapper.FromImage(invalidatedRasterImage, false);
+ graphics.Clear(backColor);
+ }
+ else
+ {
+ // Make a GraphicsWrapper object from the rasterImage and clear it to the background color
+ graphics = GraphicsWrapper.FromImage(rasterImage, false);
+ graphics.Clear(backColor);
+ }
+ }
+ }
+ }
+
+ ///
+ /// AfterRender - Dispose of Graphics object created for rendering.
+ ///
+ private void RendererAfterRender()
+ {
+ if (graphics != null)
+ {
+ // Check if we only invalidated a rect
+ if (invalidRect != RectangleF.Empty
+ && invalidatedRasterImage != null)
+ {
+ // We actually drew everything on invalidatedRasterImage and now we
+ // need to copy that to rasterImage
+ Graphics tempGraphics = Graphics.FromImage(rasterImage);
+ tempGraphics.DrawImage(invalidatedRasterImage, invalidRect.X, invalidRect.Y,
+ invalidRect, GraphicsUnit.Pixel);
+ tempGraphics.Dispose();
+ tempGraphics = null;
+
+ // If we currently have an idMapRaster here, then we need to create
+ // a temporary graphics object to draw the invalidated portion from
+ // our main graphics window onto it.
+ if (idMapRaster != null)
+ {
+ tempGraphics = Graphics.FromImage(idMapRaster);
+ tempGraphics.DrawImage(graphics.IdMapRaster, invalidRect.X, invalidRect.Y,
+ invalidRect, GraphicsUnit.Pixel);
+ tempGraphics.Dispose();
+ tempGraphics = null;
+ }
+ else
+ {
+ idMapRaster = graphics.IdMapRaster;
+ }
+ // We have updated the invalid region
+ invalidRect = RectangleF.Empty;
+ }
+ else
+ {
+ if (idMapRaster != null && idMapRaster != graphics.IdMapRaster)
+ idMapRaster.Dispose();
+ idMapRaster = graphics.IdMapRaster;
+ }
+
+ graphics.Dispose();
+ graphics = null;
+ }
+ }
+
+ public void InvalidateRect(RectangleF rect)
+ {
+ if (invalidRect == RectangleF.Empty)
+ invalidRect = rect;
+ else
+ invalidRect.Intersect(rect);
+ }
+
+ ///
+ /// Renders the SvgElement.
+ ///
+ ///
+ /// The SvgElement node to be
+ /// rendered
+ ///
+ ///
+ /// The bitmap on which the rendering was performed.
+ ///
+ public Bitmap Render(
+ ISvgElement node)
+ {
+ RectangleF updatedRect;
+ if (invalidRect != RectangleF.Empty)
+ updatedRect = new RectangleF(invalidRect.X, invalidRect.Y, invalidRect.Width, invalidRect.Height);
+ else
+ updatedRect = RectangleF.Empty;
+ RendererBeforeRender();
+ ((SvgElement)node).Render(this);
+ RendererAfterRender();
+ if (onRender != null)
+ OnRender(updatedRect);
+ return rasterImage;
+ }
+
+ ///
+ /// Renders the SvgDocument.
+ ///
+ ///
+ /// The SvgDocument node to be
+ /// rendered
+ ///
+ ///
+ /// The bitmap on which the rendering was performed.
+ ///
+ public Bitmap Render(
+ ISvgDocument node)
+ {
+ RectangleF updatedRect;
+ if (invalidRect != RectangleF.Empty)
+ updatedRect = new RectangleF(invalidRect.X, invalidRect.Y, invalidRect.Width, invalidRect.Height);
+ else
+ updatedRect = RectangleF.Empty;
+ RendererBeforeRender();
+ ((SvgDocument)node).Render(this);
+ RendererAfterRender();
+ if (onRender != null)
+ OnRender(updatedRect);
+ return rasterImage;
+ }
+
+ public void ClearMap()
+ {
+ graphicsNodes.Clear();
+ }
+
+
+ private RectangleF invalidRect = RectangleF.Empty;
+ ///
+ /// The invalidated region
+ ///
+ public RectangleF InvalidRect
+ {
+ get
+ {
+ return invalidRect;
+ }
+ set
+ {
+ invalidRect = value;
+ }
+ }
+ #endregion
+
+ #region Event handlers
+ private RenderEvent onRender;
+ public RenderEvent OnRender
+ {
+ get
+ {
+ return onRender;
+ }
+ set
+ {
+ onRender = value;
+ }
+
+ }
+
+ ///
+ /// Processes mouse events.
+ ///
+ ///
+ /// A string describing the type of mouse event that occured.
+ ///
+ ///
+ /// The MouseEventArgs that contains
+ /// the event data.
+ ///
+ public void OnMouseEvent(
+ string type,
+ MouseEventArgs e)
+ {
+ if (idMapRaster != null)
+ {
+ try
+ {
+ Color pixel = idMapRaster.GetPixel(e.X, e.Y);
+ GraphicsNode grNode = _getGraphicsNodeFromColor(pixel);
+ if (grNode != null)
+ {
+ SvgElement grElement = (SvgElement)grNode.Element;
+ IEventTarget target;
+ if (grElement.ElementInstance != null)
+ target = grElement.ElementInstance as IEventTarget;
+ else
+ target = grElement as IEventTarget;
+
+ if (target != null)
+ {
+ switch (type)
+ {
+ case "mousemove":
+ {
+ if (currentTarget == target)
+ {
+ target.DispatchEvent(new MouseEvent(
+ EventType.MouseMove, true, false,
+ null, // todo: put view here
+ 0, // todo: put detail here
+ e.X, e.Y, e.X, e.Y,
+ false, false, false, false,
+ 0, null, false));
+ }
+ else
+ {
+ if (currentTarget != null)
+ {
+ currentTarget.DispatchEvent(new MouseEvent(
+ EventType.MouseOut, true, false,
+ null, // todo: put view here
+ 0, // todo: put detail here
+ e.X, e.Y, e.X, e.Y,
+ false, false, false, false,
+ 0, null, false));
+ }
+
+ target.DispatchEvent(new MouseEvent(
+ EventType.MouseOver, true, false,
+ null, // todo: put view here
+ 0, // todo: put detail here
+ e.X, e.Y, e.X, e.Y,
+ false, false, false, false,
+ 0, null, false));
+ }
+ break;
+ }
+ case "mousedown":
+ target.DispatchEvent(new MouseEvent(
+ EventType.MouseDown, true, false,
+ null, // todo: put view here
+ 0, // todo: put detail here
+ e.X, e.Y, e.X, e.Y,
+ false, false, false, false,
+ 0, null, false));
+ currentDownTarget = target;
+ currentDownX = e.X;
+ currentDownY = e.Y;
+ break;
+ case "mouseup":
+ target.DispatchEvent(new MouseEvent(
+ EventType.MouseUp, true, false,
+ null, // todo: put view here
+ 0, // todo: put detail here
+ e.X, e.Y, e.X, e.Y,
+ false, false, false, false,
+ 0, null, false));
+ if (/*currentDownTarget == target &&*/ Math.Abs(currentDownX - e.X) < 5 && Math.Abs(currentDownY - e.Y) < 5)
+ {
+ target.DispatchEvent(new MouseEvent(
+ EventType.Click, true, false,
+ null, // todo: put view here
+ 0, // todo: put detail here
+ e.X, e.Y, e.X, e.Y,
+ false, false, false, false,
+ 0, null, false));
+ }
+ currentDownTarget = null;
+ currentDownX = 0;
+ currentDownY = 0;
+ break;
+ }
+ currentTarget = target;
+ }
+ else
+ {
+
+ // jr patch
+ if (currentTarget != null && type == "mousemove")
+ {
+ currentTarget.DispatchEvent(new MouseEvent(
+ EventType.MouseOut, true, false,
+ null, // todo: put view here
+ 0, // todo: put detail here
+ e.X, e.Y, e.X, e.Y,
+ false, false, false, false,
+ 0, null, false));
+ }
+ currentTarget = null;
+ }
+ }
+ else
+ {
+ // jr patch
+ if (currentTarget != null && type == "mousemove")
+ {
+ currentTarget.DispatchEvent(new MouseEvent(
+ EventType.MouseOut, true, false,
+ null, // todo: put view here
+ 0, // todo: put detail here
+ e.X, e.Y, e.X, e.Y,
+ false, false, false, false,
+ 0, null, false));
+ }
+ currentTarget = null;
+ }
+ }
+ catch
+ {
+ }
+ }
+ }
+
+ #endregion
+
+ #region Miscellaneous
+
+ ///
+ /// Allocate a hit color for the specified graphics node.
+ ///
+ ///
+ /// The GraphicsNode object for which to
+ /// allocate a new hit color.
+ ///
+ ///
+ /// The hit color for the GraphicsNode
+ /// object.
+ ///
+ internal Color _getNextColor(
+ GraphicsNode grNode)
+ {
+ // TODO: [newhoggy] It looks like there is a potential memory leak here.
+ // We only ever add to the graphicsNodes map, never remove
+ // from it, so it will grow every time this function is called.
+
+ // The counter is used to generate IDs in the range [0,2^24-1]
+ // The 24 bits of the counter are interpreted as follows:
+ // [red 7 bits | green 7 bits | blue 7 bits |shuffle term 3 bits]
+ // The shuffle term is used to define how the remaining high
+ // bit is set on each color. The colors are generated in the
+ // range [0,127] (7 bits) instead of [0,255]. Then the shuffle term
+ // is used to adjust them into the range [0,255].
+ // This algorithm has the feature that consecutive ids generate
+ // visually distinct colors.
+ int id = counter++; // Zero should be the first color.
+ int shuffleTerm = id & 7;
+ int r = 0x7f & (id >> 17);
+ int g = 0x7f & (id >> 10);
+ int b = 0x7f & (id >> 3);
+
+ switch (shuffleTerm)
+ {
+ case 0: break;
+ case 1: b |= 0x80; break;
+ case 2: g |= 0x80; break;
+ case 3: g |= 0x80; b |= 0x80; break;
+ case 4: r |= 0x80; break;
+ case 5: r |= 0x80; b |= 0x80; break;
+ case 6: r |= 0x80; g |= 0x80; break;
+ case 7: r |= 0x80; g |= 0x80; b |= 0x80; break;
+ }
+
+ Color color = Color.FromArgb(r, g, b);
+
+ graphicsNodes.Add(color, grNode);
+
+ return color;
+ }
+
+ internal void _removeColor(Color color,
+ GraphicsNode grNode)
+ {
+ if (!color.IsEmpty)
+ {
+ graphicsNodes[color] = null;
+ graphicsNodes.Remove(color);
+ }
+ }
+
+ ///
+ /// Gets the GraphicsNode object that
+ /// corresponds to the given hit color.
+ ///
+ ///
+ /// The hit color for which to get the corresponding
+ /// GraphicsNode object.
+ ///
+ ///
+ /// Returns null if a corresponding
+ /// GraphicsNode object cannot be
+ /// found for the given hit color.
+ ///
+ ///
+ /// The GraphicsNode object that
+ /// corresponds to the given hit color
+ ///
+ private GraphicsNode _getGraphicsNodeFromColor(
+ Color color)
+ {
+ if (color.A == 0)
+ {
+ return null;
+ }
+ else
+ {
+ return (GraphicsNode)graphicsNodes[color];
+ }
+ }
+
+ ///
+ /// TODO: This method is not used.
+ ///
+ ///
+ ///
+ ///
+ ///
+ private static int ColorToId(
+ Color color)
+ {
+ int r = color.R;
+ int g = color.G;
+ int b = color.B;
+ int shuffleTerm = 0;
+
+ if (0 != (r & 0x80))
+ {
+ shuffleTerm |= 4;
+ r &= 0x7f;
+ }
+
+ if (0 != (g & 0x80))
+ {
+ shuffleTerm |= 2;
+ g &= 0x7f;
+ }
+
+ if (0 != (b & 0x80))
+ {
+ shuffleTerm |= 1;
+ b &= 0x7f;
+ }
+
+ return (r << 17) + (g << 10) + (b << 3) + shuffleTerm;
+ }
+
+ #endregion
+
+ #region IDisposable Members
+
+ public void Dispose()
+ {
+ if (idMapRaster != null)
+ idMapRaster.Dispose();
+ if (invalidatedRasterImage != null)
+ invalidatedRasterImage.Dispose();
+ if (rasterImage != null)
+ rasterImage.Dispose();
+ if (graphics != null)
+ graphics.Dispose();
+
+ }
+
+ #endregion
+ }
+}
diff --git a/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/GraphicsNode.cs b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/GraphicsNode.cs
new file mode 100644
index 000000000..228b0e10b
--- /dev/null
+++ b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/GraphicsNode.cs
@@ -0,0 +1,293 @@
+using System;
+using System.Xml;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Collections;
+using System.Diagnostics;
+using System.Text.RegularExpressions;
+using SharpVectors.Dom.Css;
+using SharpVectors.Dom.Svg;
+using SharpVectors.Dom.Svg.Rendering;
+
+namespace SharpVectors.Renderer.Gdi
+{
+ public class GraphicsNode : RenderingNode
+ {
+ #region Constructor
+ public GraphicsNode(SvgElement element) : base(element)
+ {
+ }
+ #endregion
+
+ #region Fields
+ protected Color _uniqueColor = Color.Empty;
+ public Color UniqueColor
+ {
+ get{return _uniqueColor;}
+ }
+
+ protected GraphicsContainerWrapper graphicsContainer;
+ public Matrix TransformMatrix = null;
+ #endregion
+
+ #region Protected Methods
+ protected void Clip(GraphicsWrapper gr)
+ {
+ // todo: should we correct the clipping to adjust to the off-one-pixel drawing?
+ gr.TranslateClip(1,1);
+
+ #region Clip with clip
+ // see http://www.w3.org/TR/SVG/masking.html#OverflowAndClipProperties
+ if(element is ISvgSvgElement ||
+ element is ISvgMarkerElement ||
+ element is ISvgSymbolElement ||
+ element is ISvgPatternElement)
+ {
+ // check overflow property
+ CssValue overflow = ((SvgElement)element).GetComputedCssValue("overflow", String.Empty) as CssValue;
+ // TODO: clip can have "rect(10 10 auto 10)"
+ CssPrimitiveValue clip = ((SvgElement)element).GetComputedCssValue("clip", String.Empty) as CssPrimitiveValue;
+
+ string sOverflow = null;
+
+ if(overflow != null || overflow.CssText == "")
+ {
+ sOverflow = overflow.CssText;
+ }
+ else
+ {
+ if (this is ISvgSvgElement)
+ sOverflow = "hidden";
+ }
+
+ if (sOverflow != null) {
+ // "If the 'overflow' property has a value other than hidden or scroll, the property has no effect (i.e., a clipping rectangle is not created)."
+ if(sOverflow == "hidden" || sOverflow == "scroll")
+ {
+ RectangleF clipRect = RectangleF.Empty;
+ if(clip != null && clip.PrimitiveType == CssPrimitiveType.Rect)
+ {
+ if(element is ISvgSvgElement)
+ {
+ ISvgSvgElement svgElement = (ISvgSvgElement)element;
+ SvgRect viewPort = svgElement.Viewport as SvgRect;
+ clipRect = viewPort.ToRectangleF();
+ IRect clipShape = (Rect)clip.GetRectValue();
+ if (clipShape.Top.PrimitiveType != CssPrimitiveType.Ident)
+ clipRect.Y += (float)clipShape.Top.GetFloatValue(CssPrimitiveType.Number);
+ if (clipShape.Left.PrimitiveType != CssPrimitiveType.Ident)
+ clipRect.X += (float)clipShape.Left.GetFloatValue(CssPrimitiveType.Number);
+ if (clipShape.Right.PrimitiveType != CssPrimitiveType.Ident)
+ clipRect.Width = (clipRect.Right-clipRect.X)-(float)clipShape.Right.GetFloatValue(CssPrimitiveType.Number);
+ if (clipShape.Bottom.PrimitiveType != CssPrimitiveType.Ident)
+ clipRect.Height = (clipRect.Bottom-clipRect.Y)-(float)clipShape.Bottom.GetFloatValue(CssPrimitiveType.Number);
+ }
+ }
+ else if (clip == null || (clip.PrimitiveType == CssPrimitiveType.Ident && clip.GetStringValue() == "auto"))
+ {
+ if(element is ISvgSvgElement)
+ {
+ ISvgSvgElement svgElement = (ISvgSvgElement)element;
+ SvgRect viewPort = svgElement.Viewport as SvgRect;
+ clipRect = viewPort.ToRectangleF();
+ }
+ else if(element is ISvgMarkerElement ||
+ element is ISvgSymbolElement ||
+ element is ISvgPatternElement)
+ {
+ // TODO: what to do here?
+ }
+ }
+ if(clipRect != RectangleF.Empty)
+ {
+ gr.SetClip(clipRect);
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region Clip with clip-path
+ // see: http://www.w3.org/TR/SVG/masking.html#EstablishingANewClippingPath
+ if ( element is IGraphicsElement ||
+ element is IContainerElement)
+ {
+ CssPrimitiveValue clipPath = ((SvgElement)element).GetComputedCssValue("clip-path", String.Empty) as CssPrimitiveValue;
+
+ if(clipPath != null && clipPath.PrimitiveType == CssPrimitiveType.Uri)
+ {
+ string absoluteUri = ((SvgElement)element).ResolveUri(clipPath.GetStringValue());
+
+ SvgClipPathElement eClipPath = ((SvgDocument)element.OwnerDocument).GetNodeByUri(absoluteUri) as SvgClipPathElement;
+
+ if ( eClipPath != null )
+ {
+ GraphicsPath gpClip = eClipPath.GetGraphicsPath();
+
+ SvgUnitType pathUnits = (SvgUnitType)eClipPath.ClipPathUnits.AnimVal;
+
+ if ( pathUnits == SvgUnitType.ObjectBoundingBox )
+ {
+ SvgTransformableElement transElement = element as SvgTransformableElement;
+
+ if(transElement != null)
+ {
+ ISvgRect bbox = transElement.GetBBox();
+
+ // scale clipping path
+ Matrix matrix = new Matrix();
+ matrix.Scale((float)bbox.Width, (float)bbox.Height);
+ gpClip.Transform(matrix);
+ gr.SetClip(gpClip);
+
+ // offset clip
+ gr.TranslateClip( (float)bbox.X, (float)bbox.Y );
+ }
+ else
+ {
+ throw new NotImplementedException("clip-path with SvgUnitType.ObjectBoundingBox "
+ + "not supported for this type of element: " + element.GetType());
+ }
+ }
+ else
+ {
+ gr.SetClip(gpClip);
+ }
+ }
+ }
+ }
+ #endregion
+ }
+
+ protected void SetQuality(GraphicsWrapper gr)
+ {
+ Graphics graphics = gr.Graphics;
+
+ string colorRendering = ((SvgElement)element).GetComputedStringValue("color-rendering", String.Empty);
+ switch(colorRendering)
+ {
+ case "optimizeSpeed":
+ graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;
+ break;
+ case "optimizeQuality":
+ graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
+ break;
+ default:
+ // "auto"
+ // todo: could use AssumeLinear for slightly better
+ graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.Default;
+ break;
+ }
+
+ if (element is SvgTextContentElement)
+ {
+ // Unfortunately the text rendering hints are not applied because the
+ // text path is recorded and painted to the Graphics object as a path
+ // not as text.
+ string textRendering = ((SvgElement)element).GetComputedStringValue("text-rendering", String.Empty);
+ switch(textRendering)
+ {
+ case "optimizeSpeed":
+ graphics.SmoothingMode = SmoothingMode.HighSpeed;
+ //graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SingleBitPerPixel;
+ break;
+ case "optimizeLegibility":
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+ //graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
+ break;
+ case "geometricPrecision":
+ graphics.SmoothingMode = SmoothingMode.AntiAlias;
+ //graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
+ break;
+ default:
+ // "auto"
+ graphics.SmoothingMode = SmoothingMode.AntiAlias;
+ //graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault;
+ break;
+ }
+ }
+ else
+ {
+ string shapeRendering = ((SvgElement)element).GetComputedStringValue("shape-rendering", String.Empty);
+ switch(shapeRendering)
+ {
+ case "optimizeSpeed":
+ graphics.SmoothingMode = SmoothingMode.HighSpeed;
+ break;
+ case "crispEdges":
+ graphics.SmoothingMode = SmoothingMode.None;
+ break;
+ case "geometricPrecision":
+ graphics.SmoothingMode = SmoothingMode.HighQuality;
+ break;
+ default:
+ // "auto"
+ graphics.SmoothingMode = SmoothingMode.AntiAlias;
+ break;
+ }
+ }
+ }
+
+ protected void Transform(GraphicsWrapper gr)
+ {
+ if ( element is ISvgTransformable )
+ {
+ if ( TransformMatrix == null )
+ {
+ ISvgTransformable transElm = (ISvgTransformable) element;
+ SvgTransformList svgTList = (SvgTransformList)transElm.Transform.AnimVal;
+ //SvgTransform svgTransform = (SvgTransform)svgTList.Consolidate();
+ SvgMatrix svgMatrix = ((SvgTransformList)transElm.Transform.AnimVal).TotalMatrix;
+
+ TransformMatrix = new Matrix(
+ (float) svgMatrix.A,
+ (float) svgMatrix.B,
+ (float) svgMatrix.C,
+ (float) svgMatrix.D,
+ (float) svgMatrix.E,
+ (float) svgMatrix.F);
+ }
+ gr.Transform = TransformMatrix;
+ }
+ }
+
+ protected void fitToViewbox(GraphicsWrapper graphics, RectangleF elmRect)
+ {
+ if ( element is ISvgFitToViewBox )
+ {
+ ISvgFitToViewBox fitToVBElm = (ISvgFitToViewBox) element;
+ SvgPreserveAspectRatio spar = (SvgPreserveAspectRatio)fitToVBElm.PreserveAspectRatio.AnimVal;
+
+ float[] translateAndScale = spar.FitToViewBox(
+ (SvgRect)fitToVBElm.ViewBox.AnimVal,
+ new SvgRect(elmRect.X, elmRect.Y, elmRect.Width, elmRect.Height)
+ );
+ graphics.TranslateTransform(translateAndScale[0], translateAndScale[1]);
+ graphics.ScaleTransform(translateAndScale[2], translateAndScale[3]);
+ }
+ }
+ #endregion
+
+ #region Public Methods
+ public override void BeforeRender(ISvgRenderer renderer)
{
+ if (_uniqueColor.IsEmpty)
+ _uniqueColor = ((GdiRenderer)renderer)._getNextColor(this);
+
+ GraphicsWrapper graphics = ((GdiRenderer) renderer).GraphicsWrapper;
+
+ graphicsContainer = graphics.BeginContainer();
+ SetQuality(graphics);
+ Transform(graphics);
+ Clip(graphics);
+ }
+
+ public override void AfterRender(ISvgRenderer renderer)
+ {
+ GraphicsWrapper graphics = ((GdiRenderer)renderer).GraphicsWrapper;
+
+ graphics.EndContainer(graphicsContainer);
+ }
+ #endregion
+
+ }
+}
diff --git a/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/GraphicsPathGraphicsNode.cs b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/GraphicsPathGraphicsNode.cs
new file mode 100644
index 000000000..5b7c02b12
--- /dev/null
+++ b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/GraphicsPathGraphicsNode.cs
@@ -0,0 +1,188 @@
+using System;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Text.RegularExpressions;
+using System.Diagnostics;
+
+using SharpVectors.Dom.Css;
+using SharpVectors.Dom.Svg;
+using SharpVectors.Dom.Svg.Rendering;
+
+
+namespace SharpVectors.Renderer.Gdi
+{
+ public class GDIPathGraphicsNode : GraphicsNode
+ {
+ #region Constructor
+ public GDIPathGraphicsNode(SvgElement element) : base(element)
+ {
+ }
+ #endregion
+
+ #region Marker code
+ private string extractMarkerUrl(string propValue)
+ {
+ Regex reUrl = new Regex(@"^url\((?.+)\)$");
+
+ Match match = reUrl.Match(propValue);
+ if(match.Success)
+ {
+ return match.Groups["uri"].Value;
+ }
+ else
+ {
+ return String.Empty;
+ }
+ }
+
+ private void PaintMarkers(GdiRenderer renderer, SvgStyleableElement styleElm, GraphicsWrapper gr)
+ {
+ // OPTIMIZE
+
+ if ( styleElm is ISharpMarkerHost )
+ {
+ string markerStartUrl = extractMarkerUrl(styleElm.GetPropertyValue("marker-start", "marker"));
+ string markerMiddleUrl = extractMarkerUrl(styleElm.GetPropertyValue("marker-mid", "marker"));
+ string markerEndUrl = extractMarkerUrl(styleElm.GetPropertyValue("marker-end", "marker"));
+
+ RenderingNode grNode;
+ if ( markerStartUrl.Length > 0 )
+ {
+ grNode = renderer.GetGraphicsNodeByUri(styleElm.BaseURI, markerStartUrl);
+ if (grNode is SvgMarkerGraphicsNode)
+ {
+ ((SvgMarkerGraphicsNode) grNode).PaintMarker(renderer, gr, SvgMarkerPosition.Start, styleElm);
+ }
+ }
+
+ if ( markerMiddleUrl.Length > 0 )
+ {
+ // TODO markerMiddleUrl != markerStartUrl
+ grNode = renderer.GetGraphicsNodeByUri(styleElm.BaseURI, markerMiddleUrl);
+ if ( grNode is SvgMarkerGraphicsNode )
+ {
+ ((SvgMarkerGraphicsNode) grNode).PaintMarker(renderer, gr, SvgMarkerPosition.Mid, styleElm);
+ }
+ }
+
+ if ( markerEndUrl.Length > 0 )
+ {
+ // TODO: markerEndUrl != markerMiddleUrl
+ grNode = renderer.GetGraphicsNodeByUri(styleElm.BaseURI, markerEndUrl);
+
+ if(grNode is SvgMarkerGraphicsNode)
+ {
+ ((SvgMarkerGraphicsNode) grNode).PaintMarker(renderer, gr, SvgMarkerPosition.End, styleElm);
+ }
+ }
+ }
+ }
+ #endregion
+
+ #region Private methods
+ private Brush GetBrush(GraphicsPath gp)
+ {
+ GdiSvgPaint paint = new GdiSvgPaint(element as SvgStyleableElement, "fill");
+ return paint.GetBrush(gp);
+ }
+
+ private Pen GetPen(GraphicsPath gp)
+ {
+ GdiSvgPaint paint = new GdiSvgPaint(element as SvgStyleableElement, "stroke");
+ return paint.GetPen(gp);
+ }
+ #endregion
+
+ #region Public methods
+
+ public override void BeforeRender(ISvgRenderer renderer)
+ {
+ if (_uniqueColor.IsEmpty)
+ _uniqueColor = ((GdiRenderer)renderer)._getNextColor(this);
+
+ GraphicsWrapper graphics = ((GdiRenderer) renderer).GraphicsWrapper;
+
+ graphicsContainer = graphics.BeginContainer();
+ SetQuality(graphics);
+ Transform(graphics);
+ }
+
+ public override void Render(ISvgRenderer renderer)
+ {
+ GdiRenderer gdiRenderer = renderer as GdiRenderer;
+ GraphicsWrapper graphics = gdiRenderer.GraphicsWrapper;
+
+ if ( !(element is SvgClipPathElement) && !(element.ParentNode is SvgClipPathElement) )
+ {
+ SvgStyleableElement styleElm = element as SvgStyleableElement;
+ if ( styleElm != null )
+ {
+ string sVisibility = styleElm.GetPropertyValue("visibility");
+ string sDisplay = styleElm.GetPropertyValue("display");
+
+ if (element is ISharpGDIPath && sVisibility != "hidden" && sDisplay != "none")
+ {
+ GraphicsPath gp = ((ISharpGDIPath)element).GetGraphicsPath();
+
+ if ( gp != null )
+ {
+ Clip(graphics);
+
+ GdiSvgPaint fillPaint = new GdiSvgPaint(styleElm, "fill");
+ Brush brush = fillPaint.GetBrush(gp);
+
+ GdiSvgPaint strokePaint = new GdiSvgPaint(styleElm, "stroke");
+ Pen pen = strokePaint.GetPen(gp);
+
+ if ( brush != null )
+ {
+ if ( brush is PathGradientBrush )
+ {
+ GradientPaintServer gps = fillPaint.PaintServer as GradientPaintServer;
+ //GraphicsContainer container = graphics.BeginContainer();
+
+ graphics.SetClip(gps.GetRadialGradientRegion(gp.GetBounds()), CombineMode.Exclude);
+
+ SolidBrush tempBrush = new SolidBrush(((PathGradientBrush)brush).InterpolationColors.Colors[0]);
+ graphics.FillPath(this, tempBrush,gp);
+ tempBrush.Dispose();
+ graphics.ResetClip();
+
+ //graphics.EndContainer(container);
+ }
+
+ graphics.FillPath(this, brush, gp);
+ brush.Dispose();
+ }
+
+ if ( pen != null )
+ {
+ if ( pen.Brush is PathGradientBrush )
+ {
+ GradientPaintServer gps = strokePaint.PaintServer as GradientPaintServer;
+ GraphicsContainerWrapper container = graphics.BeginContainer();
+
+ graphics.SetClip(gps.GetRadialGradientRegion(gp.GetBounds()), CombineMode.Exclude);
+
+ SolidBrush tempBrush = new SolidBrush(((PathGradientBrush)pen.Brush).InterpolationColors.Colors[0]);
+ Pen tempPen = new Pen(tempBrush, pen.Width);
+ graphics.DrawPath(this, tempPen,gp);
+ tempPen.Dispose();
+ tempBrush.Dispose();
+
+ graphics.EndContainer(container);
+ }
+
+ graphics.DrawPath(this, pen, gp);
+ pen.Dispose();
+ }
+ }
+ }
+ PaintMarkers(gdiRenderer, styleElm, graphics);
+ }
+ }
+ }
+ #endregion
+
+ }
+}
diff --git a/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/GraphicsWrapper.cs b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/GraphicsWrapper.cs
new file mode 100644
index 000000000..82bdccc72
--- /dev/null
+++ b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/GraphicsWrapper.cs
@@ -0,0 +1,256 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Drawing.Drawing2D;
+using System.Reflection;
+using System.Runtime.InteropServices;
+/*using System.Windows.Forms;*/
+using System.Xml;
+
+using SharpVectors.Dom.Svg;
+
+namespace SharpVectors.Renderer.Gdi
+{
+
+ ///
+ /// Wraps a Graphics object since it's sealed
+ ///
+ public class GraphicsWrapper : IDisposable
+ {
+ #region Private Fields
+ private bool _isStatic;
+ private Graphics _graphics;
+ private Graphics _idMapGraphics;
+ private Bitmap _idMapImage;
+ #endregion
+
+ #region Constructors
+ private GraphicsWrapper(Image image, bool isStatic)
+ {
+ this._isStatic = isStatic;
+ if(!IsStatic)
+ {
+ _idMapImage = new Bitmap(image.Width, image.Height);
+ _idMapGraphics = Graphics.FromImage(_idMapImage);
+ _idMapGraphics.InterpolationMode = InterpolationMode.NearestNeighbor;
+ _idMapGraphics.SmoothingMode = SmoothingMode.None;
+ _idMapGraphics.CompositingQuality = CompositingQuality.Invalid;
+ }
+ _graphics = Graphics.FromImage(image);
+ }
+
+ private GraphicsWrapper(IntPtr hdc, bool isStatic)
+ {
+ this._isStatic = isStatic;
+ if(!IsStatic)
+ {
+ // This will get resized when the actual size is known
+ _idMapImage = new Bitmap(0, 0);
+ _idMapGraphics = Graphics.FromImage(_idMapImage);
+ _idMapGraphics.InterpolationMode = InterpolationMode.NearestNeighbor;
+ _idMapGraphics.SmoothingMode = SmoothingMode.None;
+ _idMapGraphics.CompositingQuality = CompositingQuality.Invalid;
+ }
+ _graphics = Graphics.FromHdc(hdc);
+ }
+ #endregion
+
+ public static GraphicsWrapper FromImage(Image image, bool isStatic)
+ {
+ return new GraphicsWrapper(image, isStatic);
+ }
+
+ public static GraphicsWrapper FromHdc(IntPtr hdc, bool isStatic)
+ {
+ return new GraphicsWrapper(hdc, isStatic);
+ }
+
+ #region Properties
+ public bool IsStatic
+ {
+ get{return _isStatic;}
+ set{
+ _isStatic = value;
+ _idMapGraphics.Dispose();
+ _idMapGraphics = null;
+ }
+ }
+
+ public Graphics Graphics
+ {
+ get{return _graphics;}
+ set{_graphics = value;}
+ }
+
+ public Graphics IdMapGraphics
+ {
+ get{return _graphics;}
+ }
+
+ public Bitmap IdMapRaster
+ {
+ get{return _idMapImage;}
+ }
+ #endregion
+
+ #region Graphics members
+ public void Clear(Color color)
+ {
+ _graphics.Clear(color);
+ if(_idMapGraphics != null) _idMapGraphics.Clear(Color.Empty);
+ }
+
+ public void Dispose()
+ {
+ _graphics.Dispose();
+ if(_idMapGraphics != null) _idMapGraphics.Dispose();
+ }
+
+ public GraphicsContainerWrapper BeginContainer()
+ {
+ GraphicsContainerWrapper container = new GraphicsContainerWrapper();
+ if(_idMapGraphics != null) container.idmapGraphicsContainer = _idMapGraphics.BeginContainer();
+ container.mainGraphicsContainer = _graphics.BeginContainer();
+ return container;
+ }
+
+ public void EndContainer(GraphicsContainerWrapper container)
+ {
+ if(_idMapGraphics != null) _idMapGraphics.EndContainer(container.idmapGraphicsContainer);
+ _graphics.EndContainer(container.mainGraphicsContainer);
+ }
+
+ public SmoothingMode SmoothingMode
+ {
+ get{return _graphics.SmoothingMode;}
+ set{_graphics.SmoothingMode = value;}
+ }
+
+ public Matrix Transform
+ {
+ get{return _graphics.Transform;}
+ set
+ {
+ if(_idMapGraphics != null) _idMapGraphics.Transform = value;
+ _graphics.Transform = value;
+ }
+ }
+
+ public void SetClip(GraphicsPath path)
+ {
+ _graphics.SetClip(path);
+ }
+
+ public void SetClip(RectangleF rect)
+ {
+ if(_idMapGraphics != null) _idMapGraphics.SetClip(rect);
+ _graphics.SetClip(rect);
+ }
+
+ public void SetClip(Region region, CombineMode combineMode)
+ {
+ if(_idMapGraphics != null) _idMapGraphics.SetClip(region, combineMode);
+ _graphics.SetClip(region, combineMode);
+ }
+
+ public void TranslateClip(float x, float y)
+ {
+ if(_idMapGraphics != null) _idMapGraphics.TranslateClip(x, y);
+ _graphics.TranslateClip(x, y);
+ }
+
+ public void ResetClip()
+ {
+ if(_idMapGraphics != null) _idMapGraphics.ResetClip();
+ _graphics.ResetClip();
+ }
+
+ public void FillPath(GraphicsNode grNode, Brush brush, GraphicsPath path )
+ {
+ if(_idMapGraphics != null)
+ {
+ Brush idBrush = new SolidBrush(grNode.UniqueColor);
+ if(grNode.Element is SvgTextContentElement)
+ {
+ _idMapGraphics.FillRectangle(idBrush, path.GetBounds());
+ }
+ else
+ {
+ _idMapGraphics.FillPath(idBrush, path);
+ }
+ }
+ _graphics.FillPath(brush, path);
+ }
+
+ public void DrawPath(GraphicsNode grNode, Pen pen, GraphicsPath path)
+ {
+ if(_idMapGraphics != null)
+ {
+ Pen idPen = new Pen(grNode.UniqueColor, pen.Width);
+ _idMapGraphics.DrawPath(idPen, path);
+ }
+ _graphics.DrawPath(pen, path);
+ }
+
+ public void TranslateTransform(float dx, float dy)
+ {
+ if(_idMapGraphics != null) _idMapGraphics.TranslateTransform(dx, dy);
+ _graphics.TranslateTransform(dx, dy);
+ }
+
+ public void ScaleTransform(float sx, float sy)
+ {
+ if(_idMapGraphics != null) _idMapGraphics.ScaleTransform(sx, sy);
+ _graphics.ScaleTransform(sx, sy);
+ }
+
+ public void RotateTransform(float angle)
+ {
+ if(_idMapGraphics != null) _idMapGraphics.RotateTransform(angle);
+ _graphics.RotateTransform(angle);
+ }
+
+ public void DrawImage(GraphicsNode grNode, Image image, Rectangle destRect, float srcX, float srcY, float srcWidth, float srcHeight, GraphicsUnit graphicsUnit, ImageAttributes imageAttributes)
+ {
+ if(_idMapGraphics != null)
+ {
+ // This handles pointer-events for visibleFill visibleStroke and visible
+ /*Brush idBrush = new SolidBrush(grNode.UniqueColor);
+ GraphicsPath gp = new GraphicsPath();
+ gp.AddRectangle(destRect);
+ _idMapGraphics.FillPath(idBrush, gp);*/
+ Color unique = grNode.UniqueColor;
+ float r = (float)unique.R / 255;
+ float g = (float)unique.G / 255;
+ float b = (float)unique.B / 255;
+ ColorMatrix colorMatrix = new ColorMatrix(
+ new float[][] { new float[] {0f, 0f, 0f, 0f, 0f},
+ new float[] {0f, 0f, 0f, 0f, 0f},
+ new float[] {0f, 0f, 0f, 0f, 0f},
+ new float[] {0f, 0f, 0f, 1f, 0f},
+ new float[] {r, g, b, 0f, 1f} });
+ ImageAttributes ia = new ImageAttributes();
+ ia.SetColorMatrix(colorMatrix,ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
+ _idMapGraphics.DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, graphicsUnit, ia);
+ }
+ _graphics.DrawImage(image, destRect, srcX, srcY, srcWidth, srcHeight, graphicsUnit, imageAttributes);
+ }
+ #endregion
+
+ }
+
+ ///
+ /// Wraps a GraphicsContainer because it is sealed.
+ /// This is a helper for GraphicsWrapper so that it can save
+ /// multiple container states. It holds the containers
+ /// for both the idMapGraphics and the main graphics
+ /// being rendered in the GraphicsWrapper.
+ ///
+ public struct GraphicsContainerWrapper {
+ internal GraphicsContainer idmapGraphicsContainer;
+ internal GraphicsContainer mainGraphicsContainer;
+ }
+
+}
diff --git a/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/Paint/GdiSvgColor.cs b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/Paint/GdiSvgColor.cs
new file mode 100644
index 000000000..19d1a44c1
--- /dev/null
+++ b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/Paint/GdiSvgColor.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Drawing;
+using SharpVectors.Dom.Svg;
+using SharpVectors.Dom.Css;
+
+namespace SharpVectors.Renderer.Gdi
+{
+ public class GdiSvgColor : SvgColor
+ {
+ private SvgStyleableElement _element;
+ private string _propertyName;
+
+ public GdiSvgColor(SvgStyleableElement elm, string propertyName) : base(elm.GetComputedStyle("").GetPropertyValue(propertyName))
+ {
+ _element = elm;
+ _propertyName = propertyName;
+ }
+
+ public int getOpacity()
+ {
+ string propName;
+ if(_propertyName.Equals("stop-color"))
+ {
+ propName = "stop-opacity";
+ }
+ else if(_propertyName.Equals("flood-color"))
+ {
+ propName = "flood-opacity";
+ }
+ else
+ {
+ return 255;
+ }
+
+ double alpha = 255;
+ string opacity;
+
+ opacity = _element.GetPropertyValue(propName);
+ if(opacity.Length > 0) alpha *= SvgNumber.ParseToFloat(opacity);
+
+ alpha = Math.Min(alpha, 255);
+ alpha = Math.Max(alpha, 0);
+
+ return Convert.ToInt32(alpha);
+ }
+
+ public Color Color
+ {
+ get
+ {
+ SvgColor colorToUse;
+ if(ColorType == SvgColorType.CurrentColor)
+ {
+ string sCurColor = _element.GetComputedStyle("").GetPropertyValue("color");
+ colorToUse = new SvgColor(sCurColor);
+ }
+ else if(ColorType == SvgColorType.Unknown)
+ {
+ colorToUse = new SvgColor("black");
+ }
+ else
+ {
+ colorToUse = this;
+ }
+
+ int red = Convert.ToInt32(colorToUse.RgbColor.Red.GetFloatValue(CssPrimitiveType.Number));
+ int green = Convert.ToInt32(colorToUse.RgbColor.Green.GetFloatValue(CssPrimitiveType.Number));
+ int blue = Convert.ToInt32(colorToUse.RgbColor.Blue.GetFloatValue(CssPrimitiveType.Number));
+ return Color.FromArgb(getOpacity(), red, green, blue);
+ }
+ }
+ }
+}
diff --git a/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/Paint/GdiSvgPaint.cs b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/Paint/GdiSvgPaint.cs
new file mode 100644
index 000000000..da2a67f3e
--- /dev/null
+++ b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/Paint/GdiSvgPaint.cs
@@ -0,0 +1,280 @@
+using System;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using SharpVectors.Dom.Svg;
+using SharpVectors.Dom.Css;
+
+namespace SharpVectors.Renderer.Gdi
+{
+ public class GdiSvgPaint : SvgPaint
+ {
+ private SvgStyleableElement _element;
+ private PaintServer ps;
+
+ public GdiSvgPaint(SvgStyleableElement elm, string propName) : base(elm.GetComputedStyle("").GetPropertyValue(propName))
+ {
+ _element = elm;
+ }
+
+ #region Private methods
+ private int getOpacity(string fillOrStroke)
+ {
+ double alpha = 255;
+ string opacity;
+
+ opacity = _element.GetPropertyValue(fillOrStroke + "-opacity");
+ if(opacity.Length > 0) alpha *= SvgNumber.ParseToFloat(opacity);
+
+ opacity = _element.GetPropertyValue("opacity");
+ if(opacity.Length > 0) alpha *= SvgNumber.ParseToFloat(opacity);
+
+ alpha = Math.Min(alpha, 255);
+ alpha = Math.Max(alpha, 0);
+
+ return Convert.ToInt32(alpha);
+ }
+
+ private LineCap getLineCap()
+ {
+ switch(_element.GetPropertyValue("stroke-linecap"))
+ {
+ case "round":
+ return LineCap.Round;
+ case "square":
+ return LineCap.Square;
+ default:
+ return LineCap.Flat;
+ }
+ }
+
+ private LineJoin getLineJoin()
+ {
+ switch(_element.GetPropertyValue("stroke-linejoin"))
+ {
+ case "round":
+ return LineJoin.Round;
+ case "bevel":
+ return LineJoin.Bevel;
+ default:
+ return LineJoin.Miter;
+ }
+ }
+
+ private float getStrokeWidth()
+ {
+ string strokeWidth = _element.GetPropertyValue("stroke-width");
+ if(strokeWidth.Length == 0) strokeWidth = "1px";
+
+ SvgLength strokeWidthLength = new SvgLength(_element, "stroke-width", SvgLengthDirection.Viewport, strokeWidth);
+ return (float)strokeWidthLength.Value;
+ }
+
+ private float getMiterLimit()
+ {
+ string miterLimitStr = _element.GetPropertyValue("stroke-miterlimit");
+ if(miterLimitStr.Length == 0) miterLimitStr = "4";
+
+ float miterLimit = SvgNumber.ParseToFloat(miterLimitStr);
+ if(miterLimit<1) throw new SvgException(SvgExceptionType.SvgInvalidValueErr, "stroke-miterlimit can not be less then 1");
+
+ return miterLimit;
+ }
+
+ private float[] getDashArray(float strokeWidth)
+ {
+ string dashArray = _element.GetPropertyValue("stroke-dasharray");
+
+ if(dashArray.Length == 0 || dashArray == "none")
+ {
+ return null;
+ }
+ else
+ {
+ SvgNumberList list = new SvgNumberList(dashArray);
+
+ uint len = list.NumberOfItems;
+ float[] fDashArray = new float[len];
+
+ for(uint i = 0; i 0)
+ {
+ //divide by strokeWidth to take care of the difference between Svg and GDI+
+ SvgLength dashOffsetLength = new SvgLength(_element, "stroke-dashoffset", SvgLengthDirection.Viewport, dashOffset);
+ return (float)dashOffsetLength.Value;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ private PaintServer getPaintServer(string uri)
+ {
+ string absoluteUri = _element.ResolveUri(uri);
+ return PaintServer.CreatePaintServer(_element.OwnerDocument, absoluteUri);
+ }
+ #endregion
+
+ #region Public methods
+ public Brush GetBrush(GraphicsPath gp)
+ {
+ return GetBrush(gp, "fill");
+
+ }
+
+ private Brush GetBrush(GraphicsPath gp, string propPrefix)
+ {
+ SvgPaint fill;
+ if(PaintType == SvgPaintType.None)
+ {
+ return null;
+ }
+ else if(PaintType == SvgPaintType.CurrentColor)
+ {
+ fill = new GdiSvgPaint(_element, "color");
+ }
+ else
+ {
+ fill = this;
+ }
+
+ if(fill.PaintType == SvgPaintType.Uri ||
+ fill.PaintType == SvgPaintType.UriCurrentColor ||
+ fill.PaintType == SvgPaintType.UriNone ||
+ fill.PaintType == SvgPaintType.UriRgbColor ||
+ fill.PaintType == SvgPaintType.UriRgbColorIccColor)
+ {
+ ps = getPaintServer(fill.Uri);
+ if(ps != null)
+ {
+ Brush br = ps.GetBrush(gp.GetBounds());
+ if (br is LinearGradientBrush)
+ {
+ LinearGradientBrush lgb = (LinearGradientBrush)br;
+ int opacityl = getOpacity(propPrefix);
+ for (int i = 0; i < lgb.InterpolationColors.Colors.Length; i++)
+ {
+ lgb.InterpolationColors.Colors[i] = Color.FromArgb(opacityl, lgb.InterpolationColors.Colors[i]);
+ }
+ for (int i = 0; i < lgb.LinearColors.Length; i++)
+ {
+ lgb.LinearColors[i] = Color.FromArgb(opacityl, lgb.LinearColors[i]);
+ }
+ } else if (br is PathGradientBrush)
+ {
+ PathGradientBrush pgb = (PathGradientBrush)br;
+ int opacityl = getOpacity(propPrefix);
+ for (int i = 0; i < pgb.InterpolationColors.Colors.Length; i++)
+ {
+ pgb.InterpolationColors.Colors[i] = Color.FromArgb(opacityl, pgb.InterpolationColors.Colors[i]);
+ }
+ for (int i = 0; i < pgb.SurroundColors.Length; i++)
+ {
+ pgb.SurroundColors[i] = Color.FromArgb(opacityl, pgb.SurroundColors[i]);
+ }
+ }
+ return br;
+ }
+ else
+ {
+ if(PaintType == SvgPaintType.UriNone ||
+ PaintType == SvgPaintType.Uri)
+ {
+ return null;
+ }
+ else if(PaintType == SvgPaintType.UriCurrentColor)
+ {
+ fill = new GdiSvgPaint(_element, "color");
+ }
+ else
+ {
+ fill = this;
+ }
+ }
+ }
+
+ SolidBrush brush = new SolidBrush( ((RgbColor)fill.RgbColor).GdiColor );
+ int opacity = getOpacity(propPrefix);
+ brush.Color = Color.FromArgb(opacity, brush.Color);
+ return brush;
+ }
+
+ public Pen GetPen(GraphicsPath gp)
+ {
+ float strokeWidth = getStrokeWidth();
+ if(strokeWidth == 0) return null;
+
+ GdiSvgPaint stroke;
+ if(PaintType == SvgPaintType.None)
+ {
+ return null;
+ }
+ else if(PaintType == SvgPaintType.CurrentColor)
+ {
+ stroke = new GdiSvgPaint(_element, "color");
+ }
+ else
+ {
+ stroke = this;
+ }
+
+ Pen pen = new Pen(stroke.GetBrush(gp, "stroke"), strokeWidth);
+
+ pen.StartCap = pen.EndCap = getLineCap();
+ pen.LineJoin = getLineJoin();
+ pen.MiterLimit = getMiterLimit();
+
+ float[] fDashArray = getDashArray(strokeWidth);
+ if(fDashArray != null)
+ {
+ // Do not draw if dash array had a zero value in it
+
+ for(int i=0;i
+ /// Summary description for PaintServer.
+ ///
+ public class GradientPaintServer : PaintServer
+ {
+ public GradientPaintServer(SvgGradientElement gradientElement)
+ {
+ _gradientElement = gradientElement;
+ }
+
+ private SvgGradientElement _gradientElement;
+
+ #region Private methods
+ private ArrayList getColors(XmlNodeList stops)
+ {
+ ArrayList colors = new ArrayList(stops.Count);
+ for(int i = 0; i 0 )
+ {
+ float firstPos = (float)positions[0];
+ if(firstPos > 0F)
+ {
+ positions.Insert(0, 0F);
+ colors.Insert(0, colors[0]);
+ }
+ float lastPos = (float)positions[positions.Count - 1];
+ if(lastPos < 1F)
+ {
+ positions.Add(1F);
+ colors.Add(colors[colors.Count - 1]);
+ }
+ }
+ }
+
+ private void getColorsAndPositions(XmlNodeList stops, ref float[] positions, ref Color[] colors)
+ {
+ ArrayList alColors = getColors(stops);
+ ArrayList alPositions = getPositions(stops);
+
+ if(alPositions.Count > 0)
+ {
+ correctPositions(alPositions, alColors);
+
+ colors = (Color[])alColors.ToArray(typeof(Color));
+ positions = (float[])alPositions.ToArray(typeof(float));
+ }
+ else
+ {
+ colors = new Color[2];
+ colors[0] = Color.Black;
+ colors[1] = Color.Black;
+
+ positions = new float[2];
+ positions[0] = 0;
+ positions[1] = 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;
+
+ 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();
+
+ Color[] adjcolors = null;
+ float[] adjpositions = null;
+ getColorsAndPositions(stops, ref adjpositions, ref adjcolors);
+
+ if(res.GradientUnits.AnimVal.Equals((ushort)SvgUnitType.ObjectBoundingBox) && !bForceUserSpaceOnUse)
+ {
+ if(res.SpreadMethod.AnimVal.Equals((ushort)SvgSpreadMethod.Pad))
+ {
+ for(int i=0;i 0.0)
+ ++nSize;
+
+ if(adjpositions[adjcolors.Length-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.Length-1] < 1)
+ {
+ readjcolors[nSize-1] = readjcolors[nSize-2];
+ readjpositions[nSize-1] = 1;
+ }
+
+ cb.Colors = readjcolors;
+ cb.Positions = readjpositions;
+ }
+ else
+ {
+ cb.Colors = adjcolors;
+ cb.Positions = adjpositions;
+ }
+ }
+ else
+ {
+ cb.Colors = adjcolors;
+ cb.Positions = adjpositions;
+ }
+
+ 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();
+
+ Color[] adjcolors = null;
+ float[] adjpositions = null;
+ getColorsAndPositions(stops, ref adjpositions, ref adjcolors);
+
+ // Need to invert the colors for some bizarre reason
+ Array.Reverse(adjcolors);
+ Array.Reverse(adjpositions);
+ for(int i = 0; i 0)
+ {
+ oldParent = children[0].ParentNode as XmlElement;
+ }
+
+ for (int i = 0; i < children.Count; i++)
+ {
+ svgElm.AppendChild(children[i]);
+ }
+
+ if (_patternElement.HasAttribute("viewBox"))
+ {
+ svgElm.SetAttribute("viewBox", _patternElement.GetAttribute("viewBox"));
+ }
+ svgElm.SetAttribute("x", "0");
+ svgElm.SetAttribute("y", "0");
+ svgElm.SetAttribute("width", _patternElement.GetAttribute("width"));
+ svgElm.SetAttribute("height", _patternElement.GetAttribute("height"));
+
+ if (_patternElement.PatternContentUnits.AnimVal.Equals(SvgUnitType.ObjectBoundingBox))
+ {
+ svgElm.SetAttribute("viewBox", "0 0 1 1");
+ }
+
+ _patternElement.AppendChild(svgElm);
+
+ return svgElm;
+ }
+
+ private void moveOutOfSvgElement(SvgSvgElement svgElm)
+ {
+ while (svgElm.ChildNodes.Count > 0)
+ {
+ oldParent.AppendChild(svgElm.ChildNodes[0]);
+ }
+
+ _patternElement.RemoveChild(svgElm);
+ }
+
+ private Image getImage(RectangleF bounds)
+ {
+ GdiRenderer renderer = new GdiRenderer();
+ renderer.Window = _patternElement.OwnerDocument.Window as SvgWindow;
+
+ SvgSvgElement elm = moveIntoSvgElement();
+
+ Image img = renderer.Render(elm as SvgElement);
+
+ moveOutOfSvgElement(elm);
+
+ return img;
+ }
+
+
+ private float calcPatternUnit(SvgLength length, SvgLengthDirection dir, RectangleF bounds)
+ {
+ int patternUnits = _patternElement.PatternUnits.AnimVal;
+ if (patternUnits == (int)SvgUnitType.UserSpaceOnUse)
+ {
+ return (float)length.Value;
+ }
+ else
+ {
+ float calcValue = (float)length.ValueInSpecifiedUnits;
+ if (dir == SvgLengthDirection.Horizontal)
+ {
+ calcValue *= bounds.Width;
+ }
+ else
+ {
+ calcValue *= bounds.Height;
+ }
+ if (length.UnitType == SvgLengthType.Percentage)
+ {
+ calcValue /= 100F;
+ }
+ return calcValue;
+ }
+ }
+
+ private RectangleF getDestRect(RectangleF bounds)
+ {
+ RectangleF result = new RectangleF(0, 0, 0, 0);
+ result.Width = calcPatternUnit(_patternElement.Width.AnimVal as SvgLength, SvgLengthDirection.Horizontal, bounds);
+ result.Height = calcPatternUnit(_patternElement.Height.AnimVal as SvgLength, SvgLengthDirection.Vertical, bounds);
+
+ return result;
+ }
+
+ private Matrix getTransformMatrix(RectangleF bounds)
+ {
+ SvgMatrix svgMatrix = ((SvgTransformList)_patternElement.PatternTransform.AnimVal).TotalMatrix;
+
+ Matrix transformMatrix = new Matrix(
+ (float)svgMatrix.A,
+ (float)svgMatrix.B,
+ (float)svgMatrix.C,
+ (float)svgMatrix.D,
+ (float)svgMatrix.E,
+ (float)svgMatrix.F);
+
+ float translateX = calcPatternUnit(_patternElement.X.AnimVal as SvgLength, SvgLengthDirection.Horizontal, bounds);
+ float translateY = calcPatternUnit(_patternElement.Y.AnimVal as SvgLength, SvgLengthDirection.Vertical, bounds);
+
+ transformMatrix.Translate(translateX, translateY, MatrixOrder.Prepend);
+ return transformMatrix;
+ }
+ #endregion
+
+ #region Public methods
+ public override Brush GetBrush(RectangleF bounds)
+ {
+ Image image = getImage(bounds);
+ RectangleF destRect = getDestRect(bounds);
+
+ TextureBrush tb = new TextureBrush(image, destRect);
+ tb.Transform = getTransformMatrix(bounds);
+ return tb;
+ }
+ #endregion
+ }
+}
diff --git a/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/PaintStatus.cs b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/PaintStatus.cs
new file mode 100644
index 000000000..7c51d79eb
--- /dev/null
+++ b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/PaintStatus.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Windows.Forms;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+
+namespace SharpVectors.Renderer.Gdi
+{
+ ///
+ /// Summary description for PaintStatus.
+ ///
+ public class PaintStatus
+ {
+ public PointF currentTextPosition = new PointF(0, 0);
+
+ public PaintStatus()
+ {
+ }
+
+ }
+}
diff --git a/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/SvgElementGraphicsNode.cs b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/SvgElementGraphicsNode.cs
new file mode 100644
index 000000000..0fb6a765d
--- /dev/null
+++ b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/SvgElementGraphicsNode.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Diagnostics;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Text.RegularExpressions;
+
+using SharpVectors.Dom.Svg;
+using SharpVectors.Dom.Svg.Rendering;
+
+namespace SharpVectors.Renderer.Gdi
+{
+ ///
+ /// Summary description for SvgElementGraphicsNode.
+ ///
+ public class SvgElementGraphicsNode : GraphicsNode
+ {
+ #region Constructor
+ public SvgElementGraphicsNode(SvgElement element) : base(element)
+ {
+ }
+ #endregion
+
+ #region Public Methods
+ public override void Render(ISvgRenderer renderer)
+ {
+ GraphicsWrapper graphics = ((GdiRenderer) renderer).GraphicsWrapper;
+
+ SvgSvgElement svgElm = (SvgSvgElement) element;
+
+ float x = (float)svgElm.X.AnimVal.Value;
+ float y = (float)svgElm.Y.AnimVal.Value;
+ float width = (float)svgElm.Width.AnimVal.Value;
+ float height = (float)svgElm.Height.AnimVal.Value;
+
+ RectangleF elmRect = new RectangleF(x, y, width, height);
+
+ if ( element.ParentNode is SvgElement )
+ {
+ // TODO: should it be moved with x and y?
+ }
+
+ fitToViewbox(graphics, elmRect);
+ }
+ #endregion
+ }
+}
diff --git a/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/SvgImageElementGraphicsNode.cs b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/SvgImageElementGraphicsNode.cs
new file mode 100644
index 000000000..c26ab1f7e
--- /dev/null
+++ b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/SvgImageElementGraphicsNode.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Diagnostics;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Drawing.Imaging;
+
+using SharpVectors.Net;
+using SharpVectors.Dom.Svg;
+using SharpVectors.Dom.Svg.Rendering;
+
+namespace SharpVectors.Renderer.Gdi
+{
+ ///
+ /// Summary description for SvgImageGraphicsNode.
+ ///
+ public class SvgImageElementGraphicsNode : GraphicsNode
+ {
+ #region Constructor
+ public SvgImageElementGraphicsNode(SvgElement element) : base(element)
+ {
+ }
+ #endregion
+
+ private GdiRenderer gdiRenderer = new GdiRenderer();
+
+ private SvgWindow getSvgWindow()
+ {
+ SvgImageElement iElm = Element as SvgImageElement;
+ SvgWindow wnd = iElm.SvgWindow;
+ wnd.Renderer = gdiRenderer;
+ gdiRenderer.Window = wnd;
+ return wnd;
+ }
+
+ #region Public Methods
+ public override void Render(ISvgRenderer renderer)
+ {
+ GraphicsWrapper graphics = ((GdiRenderer) renderer).GraphicsWrapper;
+ SvgImageElement iElement = (SvgImageElement) element;
+ //HttpResource resource = iElement.ReferencedResource;
+
+ /*if (resource != null )
+ {*/
+ ImageAttributes imageAttributes = new ImageAttributes();
+
+ string sOpacity = iElement.GetPropertyValue("opacity");
+ if ( sOpacity.Length > 0 )
+ {
+ double opacity = SvgNumber.ParseToFloat(sOpacity);
+ ColorMatrix myColorMatrix = new ColorMatrix();
+ myColorMatrix.Matrix00 = 1.00f; // Red
+ myColorMatrix.Matrix11 = 1.00f; // Green
+ myColorMatrix.Matrix22 = 1.00f; // Blue
+ myColorMatrix.Matrix33 = (float)opacity; // alpha
+ myColorMatrix.Matrix44 = 1.00f; // w
+
+ imageAttributes.SetColorMatrix(myColorMatrix,ColorMatrixFlag.Default,ColorAdjustType.Bitmap);
+ }
+
+ float width = (float)iElement.Width.AnimVal.Value;
+ float height = (float)iElement.Height.AnimVal.Value;
+
+ Rectangle destRect = new Rectangle();
+ destRect.X = Convert.ToInt32(iElement.X.AnimVal.Value);
+ destRect.Y = Convert.ToInt32(iElement.Y.AnimVal.Value);
+ destRect.Width = Convert.ToInt32(width);
+ destRect.Height = Convert.ToInt32(height);
+
+ Image image;
+ if ( iElement.IsSvgImage )
+ {
+ SvgWindow wnd = getSvgWindow();
+ gdiRenderer.BackColor = Color.Empty;
+ gdiRenderer.Render(wnd.Document as SvgDocument);
+
+ //wnd.Render();
+ image = gdiRenderer.RasterImage;
+ image.Save(@"c:\inlinesvg.png", ImageFormat.Png);
+ }
+ else
+ {
+ image = iElement.Bitmap;
+ }
+
+ if(image != null)
+ {
+ graphics.DrawImage(this, image, destRect, 0f,0f,image.Width,image.Height, GraphicsUnit.Pixel, imageAttributes);
+ }
+ //}
+ }
+ #endregion
+ }
+}
diff --git a/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/SvgMarkerGraphicsNode.cs b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/SvgMarkerGraphicsNode.cs
new file mode 100644
index 000000000..6ebcb5e4f
--- /dev/null
+++ b/SharpVectorRenderingEngine/SharpVectors/Renderer/Gdi/SvgMarkerGraphicsNode.cs
@@ -0,0 +1,127 @@
+using System;
+using System.Drawing;
+using System.Drawing.Drawing2D;
+using System.Diagnostics;
+using System.Xml;
+
+using SharpVectors.Dom.Svg;
+using SharpVectors.Dom.Svg.Rendering;
+
+namespace SharpVectors.Renderer.Gdi
+{
+ public enum SvgMarkerPosition{Start, Mid, End}
+
+ public class SvgMarkerGraphicsNode : GraphicsNode
+ {
+ #region Constructor
+ public SvgMarkerGraphicsNode(SvgElement element) : base(element)
+ {
+ }
+ #endregion
+
+ #region Public Methods
+ // disable default rendering
+ public override void BeforeRender(ISvgRenderer renderer)
+ {
+ }
+ public override void Render(ISvgRenderer renderer)
+ {
+ }
+ public override void AfterRender(ISvgRenderer renderer)
+ {
+ }
+
+ public void PaintMarker(GdiRenderer renderer, GraphicsWrapper gr, SvgMarkerPosition markerPos, SvgStyleableElement refElement)
+ {
+ ISharpMarkerHost markerHostElm = (ISharpMarkerHost)refElement;
+ SvgMarkerElement markerElm = (SvgMarkerElement) element;
+
+ PointF[] vertexPositions = markerHostElm.MarkerPositions;
+ int start;
+ int len;
+
+ // Choose which part of the position array to use
+ switch (markerPos)
+ {
+ case SvgMarkerPosition.Start:
+ start = 0;
+ len = 1;
+ break;
+ case SvgMarkerPosition.Mid:
+ start = 1;
+ len = vertexPositions.Length - 2;
+ break;
+ default:
+ // == MarkerPosition.End
+ start = vertexPositions.Length-1;
+ len = 1;
+ break;
+ }
+
+ for ( int i = start; i
+
+
\ No newline at end of file
diff --git a/Tools/svg/Knee_skeleton_lateral_anterior_views.svg b/Tools/svg/Knee_skeleton_lateral_anterior_views.svg
new file mode 100644
index 000000000..77ee00154
--- /dev/null
+++ b/Tools/svg/Knee_skeleton_lateral_anterior_views.svg
@@ -0,0 +1,466 @@
+
+
+
+
+]>
+
diff --git a/Tools/svg/footbones.svg b/Tools/svg/footbones.svg
new file mode 100644
index 000000000..a5517e1c9
--- /dev/null
+++ b/Tools/svg/footbones.svg
@@ -0,0 +1,187 @@
+
+
+
diff --git a/Tools/svg/hexaxial.svg b/Tools/svg/hexaxial.svg
new file mode 100644
index 000000000..0ddfc7527
--- /dev/null
+++ b/Tools/svg/hexaxial.svg
@@ -0,0 +1,127 @@
+
+
+
diff --git a/Tools/svg/protractor.svg b/Tools/svg/protractor.svg
new file mode 100644
index 000000000..3725beb44
--- /dev/null
+++ b/Tools/svg/protractor.svg
@@ -0,0 +1,3412 @@
+
+
+
diff --git a/Tools/svg/ring.svg b/Tools/svg/ring.svg
new file mode 100644
index 000000000..8ff415dbf
--- /dev/null
+++ b/Tools/svg/ring.svg
@@ -0,0 +1,53 @@
+
+
+
\ No newline at end of file
diff --git a/Videa.sln b/Videa.sln
index c2cf5a786..f3337e153 100644
--- a/Videa.sln
+++ b/Videa.sln
@@ -12,6 +12,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Services", "Services\Servic
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Updater", "Updater\Updater.csproj", "{0A577C03-D217-4540-AAB4-D167A85E32C0}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpVectorRenderingEngine", "SharpVectorRenderingEngine\SharpVectorRenderingEngine.csproj", "{0AD99D97-24ED-484D-9430-9905811F8A4F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -60,6 +62,14 @@ Global
{0A577C03-D217-4540-AAB4-D167A85E32C0}.Release|Any CPU.Build.0 = Release|Any CPU
{0A577C03-D217-4540-AAB4-D167A85E32C0}.Release|x86.ActiveCfg = Release|x86
{0A577C03-D217-4540-AAB4-D167A85E32C0}.Release|x86.Build.0 = Release|x86
+ {0AD99D97-24ED-484D-9430-9905811F8A4F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0AD99D97-24ED-484D-9430-9905811F8A4F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0AD99D97-24ED-484D-9430-9905811F8A4F}.Debug|x86.Build.0 = Debug|x86
+ {0AD99D97-24ED-484D-9430-9905811F8A4F}.Debug|x86.ActiveCfg = Debug|x86
+ {0AD99D97-24ED-484D-9430-9905811F8A4F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0AD99D97-24ED-484D-9430-9905811F8A4F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0AD99D97-24ED-484D-9430-9905811F8A4F}.Release|x86.Build.0 = Release|x86
+ {0AD99D97-24ED-484D-9430-9905811F8A4F}.Release|x86.ActiveCfg = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE