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 + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 @@ + + + + +]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cc + + Patrick J. Lynch, 2006 + + 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + I + II + III + IV + V + 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 @@ + + + + + + −90° + −60° + −30° + −120° + −150° + −180° + +150° + +120° + +90° + +60° + +30° + 0° + 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 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 20 + 10 + 30 + 40 + 50 + 60 + 70 + 80 + 90 + + + 0 + 20 + 10 + 30 + 40 + 50 + 60 + 70 + 80 + 90 + + + 10 + 20 + 30 + 40 + 50 + 60 + 70 + 80 + + + 10 + 20 + 30 + 40 + 50 + 60 + 70 + 80 + + + + + 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