moel@391: // -------------------------------------------------------------------------------------------------------------------- moel@391: // moel@391: // The MIT License (MIT) moel@391: // moel@391: // Copyright (c) 2012 Oystein Bjorke moel@391: // moel@391: // Permission is hereby granted, free of charge, to any person obtaining a moel@391: // copy of this software and associated documentation files (the moel@391: // "Software"), to deal in the Software without restriction, including moel@391: // without limitation the rights to use, copy, modify, merge, publish, moel@391: // distribute, sublicense, and/or sell copies of the Software, and to moel@391: // permit persons to whom the Software is furnished to do so, subject to moel@391: // the following conditions: moel@391: // moel@391: // The above copyright notice and this permission notice shall be included moel@391: // in all copies or substantial portions of the Software. moel@391: // moel@391: // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS moel@391: // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF moel@391: // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. moel@391: // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY moel@391: // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, moel@391: // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE moel@391: // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. moel@391: // moel@391: // moel@391: // The graphics render context. moel@391: // moel@391: // -------------------------------------------------------------------------------------------------------------------- moel@391: namespace OxyPlot.WindowsForms moel@391: { moel@391: using System; moel@391: using System.Collections.Generic; moel@391: using System.Drawing; moel@391: using System.Drawing.Drawing2D; moel@391: using System.Drawing.Imaging; moel@391: using System.IO; moel@391: using System.Linq; moel@391: moel@391: using OxyPlot; moel@391: moel@391: /// moel@391: /// The graphics render context. moel@391: /// moel@391: internal class GraphicsRenderContext : RenderContextBase moel@391: { moel@391: /// moel@391: /// The font size factor. moel@391: /// moel@391: private const float FontsizeFactor = 0.8f; moel@391: moel@391: /// moel@391: /// The GDI+ drawing surface. moel@391: /// moel@391: private Graphics g; moel@391: moel@391: /// moel@391: /// Initializes a new instance of the class. moel@391: /// moel@391: public GraphicsRenderContext() moel@391: { moel@391: } moel@391: moel@391: /// moel@391: /// Sets the graphics target. moel@391: /// moel@391: /// The graphics surface. moel@391: public void SetGraphicsTarget(Graphics graphics) moel@391: { moel@391: this.g = graphics; moel@391: } moel@391: moel@391: /// moel@391: /// Draws the ellipse. moel@391: /// moel@391: /// The rect. moel@391: /// The fill. moel@391: /// The stroke. moel@391: /// The thickness. moel@391: public override void DrawEllipse(OxyRect rect, OxyColor fill, OxyColor stroke, double thickness) moel@391: { moel@391: if (fill != null) moel@391: { moel@391: this.g.FillEllipse( moel@391: this.ToBrush(fill), (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height); moel@391: } moel@391: moel@391: if (stroke == null || thickness <= 0) moel@391: { moel@391: return; moel@391: } moel@391: moel@391: using (var pen = new Pen(this.ToColor(stroke), (float)thickness)) moel@391: { moel@391: this.g.SmoothingMode = SmoothingMode.HighQuality; moel@391: this.g.DrawEllipse(pen, (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height); moel@391: } moel@391: } moel@391: moel@391: /// moel@391: /// Draws the line. moel@391: /// moel@391: /// The points. moel@391: /// The stroke. moel@391: /// The thickness. moel@391: /// The dash array. moel@391: /// The line join. moel@391: /// if set to true [aliased]. moel@391: public override void DrawLine( moel@391: IList points, moel@391: OxyColor stroke, moel@391: double thickness, moel@391: double[] dashArray, moel@391: OxyPenLineJoin lineJoin, moel@391: bool aliased) moel@391: { moel@391: if (stroke == null || thickness <= 0 || points.Count < 2) moel@391: { moel@391: return; moel@391: } moel@391: moel@391: this.g.SmoothingMode = aliased ? SmoothingMode.None : SmoothingMode.HighQuality; moel@391: using (var pen = new Pen(this.ToColor(stroke), (float)thickness)) moel@391: { moel@391: moel@391: if (dashArray != null) moel@391: { moel@391: pen.DashPattern = this.ToFloatArray(dashArray); moel@391: } moel@391: moel@391: switch (lineJoin) moel@391: { moel@391: case OxyPenLineJoin.Round: moel@391: pen.LineJoin = LineJoin.Round; moel@391: break; moel@391: case OxyPenLineJoin.Bevel: moel@391: pen.LineJoin = LineJoin.Bevel; moel@391: break; moel@391: moel@391: // The default LineJoin is Miter moel@391: } moel@391: moel@391: this.g.DrawLines(pen, this.ToPoints(points)); moel@391: } moel@391: } moel@391: moel@391: /// moel@391: /// Draws the polygon. moel@391: /// moel@391: /// The points. moel@391: /// The fill. moel@391: /// The stroke. moel@391: /// The thickness. moel@391: /// The dash array. moel@391: /// The line join. moel@391: /// if set to true [aliased]. moel@391: public override void DrawPolygon( moel@391: IList points, moel@391: OxyColor fill, moel@391: OxyColor stroke, moel@391: double thickness, moel@391: double[] dashArray, moel@391: OxyPenLineJoin lineJoin, moel@391: bool aliased) moel@391: { moel@391: if (points.Count < 2) moel@391: { moel@391: return; moel@391: } moel@391: moel@391: this.g.SmoothingMode = aliased ? SmoothingMode.None : SmoothingMode.HighQuality; moel@391: moel@391: PointF[] pts = this.ToPoints(points); moel@391: if (fill != null) moel@391: { moel@391: this.g.FillPolygon(this.ToBrush(fill), pts); moel@391: } moel@391: moel@391: if (stroke != null && thickness > 0) moel@391: { moel@391: using (var pen = new Pen(this.ToColor(stroke), (float)thickness)) moel@391: { moel@391: moel@391: if (dashArray != null) moel@391: { moel@391: pen.DashPattern = this.ToFloatArray(dashArray); moel@391: } moel@391: moel@391: switch (lineJoin) moel@391: { moel@391: case OxyPenLineJoin.Round: moel@391: pen.LineJoin = LineJoin.Round; moel@391: break; moel@391: case OxyPenLineJoin.Bevel: moel@391: pen.LineJoin = LineJoin.Bevel; moel@391: break; moel@391: moel@391: // The default LineJoin is Miter moel@391: } moel@391: moel@391: this.g.DrawPolygon(pen, pts); moel@391: } moel@391: } moel@391: } moel@391: moel@391: /// moel@391: /// The draw rectangle. moel@391: /// moel@391: /// moel@391: /// The rect. moel@391: /// moel@391: /// moel@391: /// The fill. moel@391: /// moel@391: /// moel@391: /// The stroke. moel@391: /// moel@391: /// moel@391: /// The thickness. moel@391: /// moel@391: public override void DrawRectangle(OxyRect rect, OxyColor fill, OxyColor stroke, double thickness) moel@391: { moel@391: if (fill != null) moel@391: { moel@391: this.g.FillRectangle( moel@391: this.ToBrush(fill), (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height); moel@391: } moel@391: moel@391: if (stroke == null || thickness <= 0) moel@391: { moel@391: return; moel@391: } moel@391: moel@391: using (var pen = new Pen(this.ToColor(stroke), (float)thickness)) moel@391: { moel@391: this.g.DrawRectangle(pen, (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height); moel@391: } moel@391: } moel@391: moel@391: /// moel@391: /// The draw text. moel@391: /// moel@391: /// moel@391: /// The p. moel@391: /// moel@391: /// moel@391: /// The text. moel@391: /// moel@391: /// moel@391: /// The fill. moel@391: /// moel@391: /// moel@391: /// The font family. moel@391: /// moel@391: /// moel@391: /// The font size. moel@391: /// moel@391: /// moel@391: /// The font weight. moel@391: /// moel@391: /// moel@391: /// The rotate. moel@391: /// moel@391: /// moel@391: /// The halign. moel@391: /// moel@391: /// moel@391: /// The valign. moel@391: /// moel@391: /// moel@391: /// The maximum size of the text. moel@391: /// moel@391: public override void DrawText( moel@391: ScreenPoint p, moel@391: string text, moel@391: OxyColor fill, moel@391: string fontFamily, moel@391: double fontSize, moel@391: double fontWeight, moel@391: double rotate, moel@391: HorizontalAlignment halign, moel@391: VerticalAlignment valign, moel@391: OxySize? maxSize) moel@391: { moel@391: var fs = FontStyle.Regular; moel@391: if (fontWeight >= 700) moel@391: { moel@391: fs = FontStyle.Bold; moel@391: } moel@391: moel@391: using (var font = new Font(fontFamily, (float)fontSize * FontsizeFactor, fs)) moel@391: { moel@391: using (var sf = new StringFormat { Alignment = StringAlignment.Near }) moel@391: { moel@391: var size = this.g.MeasureString(text, font); moel@391: if (maxSize != null) moel@391: { moel@391: if (size.Width > maxSize.Value.Width) moel@391: { moel@391: size.Width = (float)maxSize.Value.Width; moel@391: } moel@391: moel@391: if (size.Height > maxSize.Value.Height) moel@391: { moel@391: size.Height = (float)maxSize.Value.Height; moel@391: } moel@391: } moel@391: moel@391: float dx = 0; moel@391: if (halign == HorizontalAlignment.Center) moel@391: { moel@391: dx = -size.Width / 2; moel@391: } moel@391: moel@391: if (halign == HorizontalAlignment.Right) moel@391: { moel@391: dx = -size.Width; moel@391: } moel@391: moel@391: float dy = 0; moel@391: sf.LineAlignment = StringAlignment.Near; moel@391: if (valign == VerticalAlignment.Middle) moel@391: { moel@391: dy = -size.Height / 2; moel@391: } moel@391: moel@391: if (valign == VerticalAlignment.Bottom) moel@391: { moel@391: dy = -size.Height; moel@391: } moel@391: moel@391: this.g.TranslateTransform((float)p.X, (float)p.Y); moel@391: if (Math.Abs(rotate) > double.Epsilon) moel@391: { moel@391: this.g.RotateTransform((float)rotate); moel@391: } moel@391: moel@391: this.g.TranslateTransform(dx, dy); moel@391: moel@391: var layoutRectangle = new RectangleF(0, 0, size.Width, size.Height); moel@391: this.g.DrawString(text, font, this.ToBrush(fill), layoutRectangle, sf); moel@391: moel@391: this.g.ResetTransform(); moel@391: } moel@391: } moel@391: } moel@391: moel@391: /// moel@391: /// The measure text. moel@391: /// moel@391: /// The text. moel@391: /// The font family. moel@391: /// The font size. moel@391: /// The font weight. moel@391: /// The size of the text. moel@391: public override OxySize MeasureText(string text, string fontFamily, double fontSize, double fontWeight) moel@391: { moel@391: if (text == null) moel@391: { moel@391: return OxySize.Empty; moel@391: } moel@391: moel@391: var fs = FontStyle.Regular; moel@391: if (fontWeight >= 700) moel@391: { moel@391: fs = FontStyle.Bold; moel@391: } moel@391: moel@391: using (var font = new Font(fontFamily, (float)fontSize * FontsizeFactor, fs)) moel@391: { moel@391: var size = this.g.MeasureString(text, font); moel@391: return new OxySize(size.Width, size.Height); moel@391: } moel@391: } moel@391: moel@391: /// moel@391: /// Converts a fill color to a System.Drawing.Brush. moel@391: /// moel@391: /// moel@391: /// The fill color. moel@391: /// moel@391: /// moel@391: /// The brush. moel@391: /// moel@391: private Brush ToBrush(OxyColor fill) moel@391: { moel@391: if (fill != null) moel@391: { moel@391: return new SolidBrush(this.ToColor(fill)); moel@391: } moel@391: moel@391: return null; moel@391: } moel@391: moel@391: /// moel@391: /// Converts a color to a System.Drawing.Color. moel@391: /// moel@391: /// moel@391: /// The color. moel@391: /// moel@391: /// moel@391: /// The System.Drawing.Color. moel@391: /// moel@391: private Color ToColor(OxyColor c) moel@391: { moel@391: return Color.FromArgb(c.A, c.R, c.G, c.B); moel@391: } moel@391: moel@391: /// moel@391: /// Converts a double array to a float array. moel@391: /// moel@391: /// moel@391: /// The a. moel@391: /// moel@391: /// moel@391: /// The float array. moel@391: /// moel@391: private float[] ToFloatArray(double[] a) moel@391: { moel@391: if (a == null) moel@391: { moel@391: return null; moel@391: } moel@391: moel@391: var r = new float[a.Length]; moel@391: for (int i = 0; i < a.Length; i++) moel@391: { moel@391: r[i] = (float)a[i]; moel@391: } moel@391: moel@391: return r; moel@391: } moel@391: moel@391: /// moel@391: /// Converts a list of point to an array of PointF. moel@391: /// moel@391: /// moel@391: /// The points. moel@391: /// moel@391: /// moel@391: /// An array of points. moel@391: /// moel@391: private PointF[] ToPoints(IList points) moel@391: { moel@391: if (points == null) moel@391: { moel@391: return null; moel@391: } moel@391: moel@391: var r = new PointF[points.Count()]; moel@391: int i = 0; moel@391: foreach (ScreenPoint p in points) moel@391: { moel@391: r[i++] = new PointF((float)p.X, (float)p.Y); moel@391: } moel@391: moel@391: return r; moel@391: } moel@391: moel@391: public override void CleanUp() moel@391: { moel@391: var imagesToRelease = imageCache.Keys.Where(i => !imagesInUse.Contains(i)); moel@391: foreach (var i in imagesToRelease) moel@391: { moel@391: var image = this.GetImage(i); moel@391: image.Dispose(); moel@391: imageCache.Remove(i); moel@391: } moel@391: moel@391: imagesInUse.Clear(); moel@391: } moel@391: moel@391: public override OxyImageInfo GetImageInfo(OxyImage source) moel@391: { moel@391: var image = this.GetImage(source); moel@391: return image == null ? null : new OxyImageInfo { Width = (uint)image.Width, Height = (uint)image.Height, DpiX = image.HorizontalResolution, DpiY = image.VerticalResolution }; moel@391: } moel@391: moel@391: public override void DrawImage(OxyImage source, uint srcX, uint srcY, uint srcWidth, uint srcHeight, double x, double y, double w, double h, double opacity, bool interpolate) moel@391: { moel@391: var image = this.GetImage(source); moel@391: if (image != null) moel@391: { moel@391: ImageAttributes ia = null; moel@391: if (opacity < 1) moel@391: { moel@391: var cm = new ColorMatrix moel@391: { moel@391: Matrix00 = 1f, moel@391: Matrix11 = 1f, moel@391: Matrix22 = 1f, moel@391: Matrix33 = 1f, moel@391: Matrix44 = (float)opacity moel@391: }; moel@391: moel@391: ia = new ImageAttributes(); moel@391: ia.SetColorMatrix(cm, ColorMatrixFlag.Default, ColorAdjustType.Bitmap); moel@391: } moel@391: moel@391: g.InterpolationMode = interpolate ? InterpolationMode.HighQualityBicubic : InterpolationMode.NearestNeighbor; moel@391: int sx = (int)Math.Round(x); moel@391: int sy = (int)Math.Round(y); moel@391: int sw = (int)Math.Round(x + w) - sx; moel@391: int sh = (int)Math.Round(y + h) - sy; moel@391: g.DrawImage(image, new Rectangle(sx, sy, sw, sh), srcX, srcY, srcWidth, srcHeight, GraphicsUnit.Pixel, ia); moel@391: } moel@391: } moel@391: moel@391: private HashSet imagesInUse = new HashSet(); moel@391: moel@391: private Dictionary imageCache = new Dictionary(); moel@391: moel@391: moel@391: private Image GetImage(OxyImage source) moel@391: { moel@391: if (source == null) moel@391: { moel@391: return null; moel@391: } moel@391: moel@391: if (!this.imagesInUse.Contains(source)) moel@391: { moel@391: this.imagesInUse.Add(source); moel@391: } moel@391: moel@391: Image src; moel@391: if (this.imageCache.TryGetValue(source, out src)) moel@391: { moel@391: return src; moel@391: } moel@391: moel@391: if (source != null) moel@391: { moel@391: Image btm; moel@391: using (var ms = new MemoryStream(source.GetData())) moel@391: { moel@391: btm = Image.FromStream(ms); moel@391: } moel@391: moel@391: this.imageCache.Add(source, btm); moel@391: return btm; moel@391: } moel@391: moel@391: return null; moel@391: } moel@391: moel@391: public override bool SetClip(OxyRect rect) moel@391: { moel@391: this.g.SetClip(rect.ToRect(false)); moel@391: return true; moel@391: } moel@391: moel@391: public override void ResetClip() moel@391: { moel@391: this.g.ResetClip(); moel@391: } moel@391: } moel@391: }