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: }