public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity) { LoadStops(renderingElement); try { if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) { renderer.Boundable(renderingElement); } var origin = renderer.Boundable().Location; var centerPoint = CalculateCenterPoint(renderer, origin); var focalPoint = CalculateFocalPoint(renderer, origin); var specifiedRadius = CalculateRadius(renderer); var effectiveRadius = CalculateEffectiveRadius(renderingElement, centerPoint, specifiedRadius); var brush = new PathGradientBrush(CreateGraphicsPath(origin, centerPoint, effectiveRadius)) { InterpolationColors = CalculateColorBlend(renderer, opacity, specifiedRadius, effectiveRadius), CenterPoint = focalPoint }; Debug.Assert(brush.Rectangle.Contains(renderingElement.Bounds), "Brush rectangle does not contain rendering element bounds!"); return(brush); } finally { if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) { renderer.PopBoundable(); } } }
/// <summary> /// Renders the <see cref="SvgDocument"/> to the specified <see cref="SvgRenderer"/>. /// </summary> /// <param name="renderer">The <see cref="SvgRenderer"/> to render the document with.</param> /// <exception cref="ArgumentNullException">The <paramref name="renderer"/> parameter cannot be <c>null</c>.</exception> public void Draw(SvgRenderer renderer) { if (renderer == null) { throw new ArgumentNullException("renderer"); } renderer.Boundable(this); this.Render(renderer); }
public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity) { LoadStops(renderingElement); if (IsInvalid) { return(null); } try { if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) { renderer.Boundable(renderingElement); } var specifiedStart = CalculateStart(renderer); var specifiedEnd = CalculateEnd(renderer); var effectiveStart = specifiedStart; var effectiveEnd = specifiedEnd; if (NeedToExpandGradient(renderingElement, specifiedStart, specifiedEnd)) { var expansion = ExpandGradient(renderingElement, specifiedStart, specifiedEnd); effectiveStart = expansion.StartPoint; effectiveEnd = expansion.EndPoint; } return(new LinearGradientBrush(effectiveStart, effectiveEnd, System.Drawing.Color.Transparent, System.Drawing.Color.Transparent) { InterpolationColors = CalculateColorBlend(renderer, opacity, specifiedStart, effectiveStart, specifiedEnd, effectiveEnd), WrapMode = WrapMode.TileFlipX }); } finally { if (this.GradientUnits == SvgCoordinateUnits.ObjectBoundingBox) { renderer.PopBoundable(); } } }
/// <summary> /// Converts the current unit to one that can be used at render time. /// </summary> /// <param name="boundable">The container element used as the basis for calculations</param> /// <returns>The representation of the current unit in a device value (usually pixels).</returns> public float ToDeviceValue(SvgRenderer renderer, UnitRenderingType renderType, SvgElement owner) { // If it's already been calculated if (this._deviceValue.HasValue) { return(this._deviceValue.Value); } if (this._value == 0.0f) { this._deviceValue = 0.0f; return(this._deviceValue.Value); } // http://www.w3.org/TR/CSS21/syndata.html#values // http://www.w3.org/TR/SVG11/coords.html#Units const float cmInInch = 2.54f; int ppi = SvgDocument.PointsPerInch; var type = this.Type; var value = this.Value; // Deal with fractional pattern units var coordElem = owner as ISvgSupportsCoordinateUnits; if (coordElem != null && coordElem.GetUnits() == SvgCoordinateUnits.ObjectBoundingBox && type != SvgUnitType.Percentage) { type = SvgUnitType.Percentage; value *= 100; } var element = owner as SvgElement; if (element != null) { var pattern = element.Parents.OfType <SvgPatternServer>().FirstOrDefault(); if (pattern != null && pattern.PatternContentUnits == SvgCoordinateUnits.ObjectBoundingBox && type != SvgUnitType.Percentage) { type = SvgUnitType.Percentage; value *= 100; } } float points; Font currFont; switch (type) { case SvgUnitType.Em: currFont = GetFont(renderer, owner); if (currFont == null) { points = (float)(value * 9); _deviceValue = (points / 72.0f) * ppi; } else { _deviceValue = value * (currFont.SizeInPoints / 72.0f) * ppi; } break; case SvgUnitType.Ex: currFont = GetFont(renderer, owner); if (currFont == null) { points = (float)(value * 9); _deviceValue = (points * 0.5f / 72.0f) * ppi; } else { _deviceValue = value * 0.5f * (currFont.SizeInPoints / 72.0f) * ppi; } break; case SvgUnitType.Centimeter: _deviceValue = (float)((value / cmInInch) * ppi); break; case SvgUnitType.Inch: _deviceValue = value * ppi; break; case SvgUnitType.Millimeter: _deviceValue = (float)((value / 10) / cmInInch) * ppi; break; case SvgUnitType.Pica: _deviceValue = ((value * 12) / 72) * ppi; break; case SvgUnitType.Point: _deviceValue = (value / 72) * ppi; break; case SvgUnitType.Pixel: _deviceValue = value; break; case SvgUnitType.User: _deviceValue = value; break; case SvgUnitType.Percentage: // Can't calculate if there is no style owner var boundable = (renderer == null ? (owner == null ? null : owner.OwnerDocument) : renderer.Boundable()); if (boundable == null) { _deviceValue = value; break; } System.Drawing.SizeF size = boundable.Bounds.Size; switch (renderType) { case UnitRenderingType.Horizontal: _deviceValue = (size.Width / 100) * value; break; case UnitRenderingType.HorizontalOffset: _deviceValue = (size.Width / 100) * value + boundable.Location.X; break; case UnitRenderingType.Vertical: _deviceValue = (size.Height / 100) * value; break; case UnitRenderingType.VerticalOffset: _deviceValue = (size.Height / 100) * value + boundable.Location.Y; break; default: _deviceValue = (float)(Math.Sqrt(Math.Pow(size.Width, 2) + Math.Pow(size.Height, 2)) / Math.Sqrt(2) * value / 100.0); break; } break; default: _deviceValue = value; break; } return(this._deviceValue.Value); }
/// <summary> /// Gets the <see cref="GraphicsPath"/> for this element. /// </summary> /// <value></value> public override System.Drawing.Drawing2D.GraphicsPath Path(SvgRenderer renderer) { // Make sure the path is always null if there is no text //if there is a TSpan inside of this text element then path should not be null (even if this text is empty!) if ((string.IsNullOrEmpty(this.Text) || this.Text.Trim().Length < 1) && this.Children.Where(x => x is SvgTextSpan).Select(x => x as SvgTextSpan).Count() == 0) { return(_path = null); } //NOT SURE WHAT THIS IS ABOUT - Path gets created again anyway - WTF? // When an empty string is passed to GraphicsPath, it rises an InvalidArgumentException... if (_path == null || this.IsPathDirty) { renderer = (renderer ?? SvgRenderer.FromNull()); // Measure the overall bounds of all the text var boundsData = GetTextBounds(renderer); var font = GetFont(renderer); SvgTextBase innerText; float x = (_x.Count < 1 ? _calcX : _x[0].ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this)) + (_dx.Count < 1 ? 0 : _dx[0].ToDeviceValue(renderer, UnitRenderingType.Horizontal, this)); float y = (_y.Count < 1 ? _calcY : _y[0].ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this)) + (_dy.Count < 1 ? 0 : _dy[0].ToDeviceValue(renderer, UnitRenderingType.Vertical, this)); _path = new GraphicsPath(); _path.StartFigure(); // Determine the location of the start point switch (this.TextAnchor) { case SvgTextAnchor.Middle: x -= (boundsData.Bounds.Width / 2); break; case SvgTextAnchor.End: x -= boundsData.Bounds.Width; break; } try { renderer.Boundable(new FontBoundable(font)); switch (this.BaselineShift) { case null: case "": case "baseline": case "inherit": // do nothing break; case "sub": y += new SvgUnit(SvgUnitType.Ex, 1).ToDeviceValue(renderer, UnitRenderingType.Vertical, this); break; case "super": y -= new SvgUnit(SvgUnitType.Ex, 1).ToDeviceValue(renderer, UnitRenderingType.Vertical, this); break; default: var convert = new SvgUnitConverter(); var shift = (SvgUnit)convert.ConvertFromInvariantString(this.BaselineShift); y -= shift.ToDeviceValue(renderer, UnitRenderingType.Vertical, this); break; } } finally { renderer.PopBoundable(); } NodeBounds data; var yCummOffset = 0.0f; for (var i = 0; i < boundsData.Nodes.Count; i++) { data = boundsData.Nodes[i]; innerText = data.Node as SvgTextBase; if (innerText == null) { // Minus FontSize because the x/y coords mark the bottom left, not bottom top. DrawString(renderer, _path, x + data.xOffset, y - boundsData.Bounds.Height, font, PrepareText(data.Node.Content, i > 0 && boundsData.Nodes[i - 1].Node is SvgTextBase, i < boundsData.Nodes.Count - 1 && boundsData.Nodes[i + 1].Node is SvgTextBase)); } else { innerText._calcX = x + data.xOffset; innerText._calcY = y + yCummOffset; if (innerText.Dy.Count == 1) { yCummOffset += innerText.Dy[0].ToDeviceValue(renderer, UnitRenderingType.Vertical, this); } } } _path.CloseFigure(); this.IsPathDirty = false; } return(_path); }
/// <summary> /// Converts the current unit to one that can be used at render time. /// </summary> /// <param name="boundable">The container element used as the basis for calculations</param> /// <returns>The representation of the current unit in a device value (usually pixels).</returns> public float ToDeviceValue(SvgRenderer renderer, UnitRenderingType renderType, SvgElement owner) { // If it's already been calculated if (this._deviceValue.HasValue) { return this._deviceValue.Value; } if (this._value == 0.0f) { this._deviceValue = 0.0f; return this._deviceValue.Value; } // http://www.w3.org/TR/CSS21/syndata.html#values // http://www.w3.org/TR/SVG11/coords.html#Units const float cmInInch = 2.54f; int ppi = SvgDocument.PointsPerInch; var type = this.Type; var value = this.Value; // Deal with fractional pattern units var coordElem = owner as ISvgSupportsCoordinateUnits; if (coordElem != null && coordElem.GetUnits() == SvgCoordinateUnits.ObjectBoundingBox && type != SvgUnitType.Percentage) { type = SvgUnitType.Percentage; value *= 100; } var element = owner as SvgElement; if (element != null) { var pattern = element.Parents.OfType<SvgPatternServer>().FirstOrDefault(); if (pattern != null && pattern.PatternContentUnits == SvgCoordinateUnits.ObjectBoundingBox && type != SvgUnitType.Percentage) { type = SvgUnitType.Percentage; value *= 100; } } float points; Font currFont; switch (type) { case SvgUnitType.Em: currFont = GetFont(renderer, owner); if (currFont == null) { points = (float)(value * 9); _deviceValue = (points / 72.0f) * ppi; } else { _deviceValue = value * (currFont.SizeInPoints / 72.0f) * ppi; } break; case SvgUnitType.Ex: currFont = GetFont(renderer, owner); if (currFont == null) { points = (float)(value * 9); _deviceValue = (points * 0.5f / 72.0f) * ppi; } else { _deviceValue = value * 0.5f * (currFont.SizeInPoints / 72.0f) * ppi; } break; case SvgUnitType.Centimeter: _deviceValue = (float)((value / cmInInch) * ppi); break; case SvgUnitType.Inch: _deviceValue = value * ppi; break; case SvgUnitType.Millimeter: _deviceValue = (float)((value / 10) / cmInInch) * ppi; break; case SvgUnitType.Pica: _deviceValue = ((value * 12) / 72) * ppi; break; case SvgUnitType.Point: _deviceValue = (value / 72) * ppi; break; case SvgUnitType.Pixel: _deviceValue = value; break; case SvgUnitType.User: _deviceValue = value; break; case SvgUnitType.Percentage: // Can't calculate if there is no style owner var boundable = (renderer == null ? (owner == null ? null : owner.OwnerDocument) : renderer.Boundable()); if (boundable == null) { _deviceValue = value; break; } System.Drawing.SizeF size = boundable.Bounds.Size; switch (renderType) { case UnitRenderingType.Horizontal: _deviceValue = (size.Width / 100) * value; break; case UnitRenderingType.HorizontalOffset: _deviceValue = (size.Width / 100) * value + boundable.Location.X; break; case UnitRenderingType.Vertical: _deviceValue = (size.Height / 100) * value; break; case UnitRenderingType.VerticalOffset: _deviceValue = (size.Height / 100) * value + boundable.Location.Y; break; default: _deviceValue = (float)(Math.Sqrt(Math.Pow(size.Width, 2) + Math.Pow(size.Height, 2)) / Math.Sqrt(2) * value / 100.0); break; } break; default: _deviceValue = value; break; } return this._deviceValue.Value; }
/// <summary> /// Gets a <see cref="ColorBlend"/> representing the <see cref="SvgGradientServer"/>'s gradient stops. /// </summary> /// <param name="owner">The parent <see cref="SvgVisualElement"/>.</param> /// <param name="opacity">The opacity of the colour blend.</param> protected ColorBlend GetColorBlend(SvgRenderer renderer, float opacity, bool radial) { int colourBlends = this.Stops.Count; bool insertStart = false; bool insertEnd = false; //gradient.Transform = renderingElement.Transforms.Matrix; //stops should be processed in reverse order if it's a radial gradient // May need to increase the number of colour blends because the range *must* be from 0.0 to 1.0. // E.g. 0.5 - 0.8 isn't valid therefore the rest need to be calculated. // If the first stop doesn't start at zero if (this.Stops[0].Offset.Value > 0) { colourBlends++; if (radial) { insertEnd = true; } else { insertStart = true; } } // If the last stop doesn't end at 1 a stop float lastValue = this.Stops[this.Stops.Count - 1].Offset.Value; if (lastValue < 100 || lastValue < 1) { colourBlends++; if (radial) { insertStart = true; } else { insertEnd = true; } } ColorBlend blend = new ColorBlend(colourBlends); // Set positions and colour values int actualStops = 0; float mergedOpacity = 0.0f; float position = 0.0f; Color colour = System.Drawing.Color.Black; for (int i = 0; i < colourBlends; i++) { var currentStop = this.Stops[radial ? this.Stops.Count - 1 - actualStops : actualStops]; var boundWidth = renderer.Boundable().Bounds.Width; mergedOpacity = opacity * currentStop.Opacity; position = radial ? 1 - (currentStop.Offset.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / boundWidth) : (currentStop.Offset.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / boundWidth); colour = System.Drawing.Color.FromArgb((int)(mergedOpacity * 255), currentStop.GetColor(this)); actualStops++; // Insert this colour before itself at position 0 if (insertStart && i == 0) { blend.Positions[i] = 0.0f; blend.Colors[i] = colour; i++; } blend.Positions[i] = position; blend.Colors[i] = colour; // Insert this colour after itself at position 0 if (insertEnd && i == colourBlends - 2) { i++; blend.Positions[i] = 1.0f; blend.Colors[i] = colour; } } return(blend); }
/// <summary> /// Gets a <see cref="Brush"/> representing the current paint server. /// </summary> /// <param name="renderingElement">The owner <see cref="SvgVisualElement"/>.</param> /// <param name="opacity">The opacity of the brush.</param> public override Brush GetBrush(SvgVisualElement renderingElement, SvgRenderer renderer, float opacity) { // If there aren't any children, return null if (this.Children.Count == 0) { return(null); } // Can't render if there are no dimensions if (this._width.Value == 0.0f || this._height.Value == 0.0f) { return(null); } try { if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox) { renderer.Boundable(renderingElement); } float width = this._width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this); float height = this._height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this); Matrix patternMatrix = new Matrix(); // Apply a translate if needed if (this._x.Value > 0.0f || this._y.Value > 0.0f) { float x = this._x.ToDeviceValue(renderer, UnitRenderingType.HorizontalOffset, this); float y = this._y.ToDeviceValue(renderer, UnitRenderingType.VerticalOffset, this); patternMatrix.Translate(x + -1.0f, y + -1.0f); } else { patternMatrix.Translate(-1, -1); } if (this.ViewBox.Height > 0 || this.ViewBox.Width > 0) { patternMatrix.Scale(this.Width.ToDeviceValue(renderer, UnitRenderingType.Horizontal, this) / this.ViewBox.Width, this.Height.ToDeviceValue(renderer, UnitRenderingType.Vertical, this) / this.ViewBox.Height); } Bitmap image = new Bitmap((int)width, (int)height); using (SvgRenderer iRenderer = SvgRenderer.FromImage(image)) { iRenderer.Boundable((_patternContentUnits == SvgCoordinateUnits.ObjectBoundingBox) ? new GenericBoundable(0, 0, width, height) : renderer.Boundable()); iRenderer.Transform = patternMatrix; iRenderer.CompositingQuality = CompositingQuality.HighQuality; iRenderer.SmoothingMode = SmoothingMode.AntiAlias; iRenderer.PixelOffsetMode = PixelOffsetMode.Half; foreach (SvgElement child in this.Children) { child.RenderElement(iRenderer); } iRenderer.Save(); } image.Save(string.Format(@"C:\test{0:D3}.png", imgNumber++)); TextureBrush textureBrush = new TextureBrush(image); return(textureBrush); } finally { if (this.PatternUnits == SvgCoordinateUnits.ObjectBoundingBox) { renderer.PopBoundable(); } } }