1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/External/OxyPlot/OxyPlot.WindowsForms/GraphicsRenderContext.cs Sat Jun 08 16:53:22 2013 +0000
1.3 @@ -0,0 +1,558 @@
1.4 +// --------------------------------------------------------------------------------------------------------------------
1.5 +// <copyright file="GraphicsRenderContext.cs" company="OxyPlot">
1.6 +// The MIT License (MIT)
1.7 +//
1.8 +// Copyright (c) 2012 Oystein Bjorke
1.9 +//
1.10 +// Permission is hereby granted, free of charge, to any person obtaining a
1.11 +// copy of this software and associated documentation files (the
1.12 +// "Software"), to deal in the Software without restriction, including
1.13 +// without limitation the rights to use, copy, modify, merge, publish,
1.14 +// distribute, sublicense, and/or sell copies of the Software, and to
1.15 +// permit persons to whom the Software is furnished to do so, subject to
1.16 +// the following conditions:
1.17 +//
1.18 +// The above copyright notice and this permission notice shall be included
1.19 +// in all copies or substantial portions of the Software.
1.20 +//
1.21 +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1.22 +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1.23 +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
1.24 +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
1.25 +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
1.26 +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
1.27 +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1.28 +// </copyright>
1.29 +// <summary>
1.30 +// The graphics render context.
1.31 +// </summary>
1.32 +// --------------------------------------------------------------------------------------------------------------------
1.33 +namespace OxyPlot.WindowsForms
1.34 +{
1.35 + using System;
1.36 + using System.Collections.Generic;
1.37 + using System.Drawing;
1.38 + using System.Drawing.Drawing2D;
1.39 + using System.Drawing.Imaging;
1.40 + using System.IO;
1.41 + using System.Linq;
1.42 +
1.43 + using OxyPlot;
1.44 +
1.45 + /// <summary>
1.46 + /// The graphics render context.
1.47 + /// </summary>
1.48 + internal class GraphicsRenderContext : RenderContextBase
1.49 + {
1.50 + /// <summary>
1.51 + /// The font size factor.
1.52 + /// </summary>
1.53 + private const float FontsizeFactor = 0.8f;
1.54 +
1.55 + /// <summary>
1.56 + /// The GDI+ drawing surface.
1.57 + /// </summary>
1.58 + private Graphics g;
1.59 +
1.60 + /// <summary>
1.61 + /// Initializes a new instance of the <see cref="GraphicsRenderContext"/> class.
1.62 + /// </summary>
1.63 + public GraphicsRenderContext()
1.64 + {
1.65 + }
1.66 +
1.67 + /// <summary>
1.68 + /// Sets the graphics target.
1.69 + /// </summary>
1.70 + /// <param name="graphics">The graphics surface.</param>
1.71 + public void SetGraphicsTarget(Graphics graphics)
1.72 + {
1.73 + this.g = graphics;
1.74 + }
1.75 +
1.76 + /// <summary>
1.77 + /// Draws the ellipse.
1.78 + /// </summary>
1.79 + /// <param name="rect">The rect.</param>
1.80 + /// <param name="fill">The fill.</param>
1.81 + /// <param name="stroke">The stroke.</param>
1.82 + /// <param name="thickness">The thickness.</param>
1.83 + public override void DrawEllipse(OxyRect rect, OxyColor fill, OxyColor stroke, double thickness)
1.84 + {
1.85 + if (fill != null)
1.86 + {
1.87 + this.g.FillEllipse(
1.88 + this.ToBrush(fill), (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height);
1.89 + }
1.90 +
1.91 + if (stroke == null || thickness <= 0)
1.92 + {
1.93 + return;
1.94 + }
1.95 +
1.96 + using (var pen = new Pen(this.ToColor(stroke), (float)thickness))
1.97 + {
1.98 + this.g.SmoothingMode = SmoothingMode.HighQuality;
1.99 + this.g.DrawEllipse(pen, (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height);
1.100 + }
1.101 + }
1.102 +
1.103 + /// <summary>
1.104 + /// Draws the line.
1.105 + /// </summary>
1.106 + /// <param name="points">The points.</param>
1.107 + /// <param name="stroke">The stroke.</param>
1.108 + /// <param name="thickness">The thickness.</param>
1.109 + /// <param name="dashArray">The dash array.</param>
1.110 + /// <param name="lineJoin">The line join.</param>
1.111 + /// <param name="aliased">if set to <c>true</c> [aliased].</param>
1.112 + public override void DrawLine(
1.113 + IList<ScreenPoint> points,
1.114 + OxyColor stroke,
1.115 + double thickness,
1.116 + double[] dashArray,
1.117 + OxyPenLineJoin lineJoin,
1.118 + bool aliased)
1.119 + {
1.120 + if (stroke == null || thickness <= 0 || points.Count < 2)
1.121 + {
1.122 + return;
1.123 + }
1.124 +
1.125 + this.g.SmoothingMode = aliased ? SmoothingMode.None : SmoothingMode.HighQuality;
1.126 + using (var pen = new Pen(this.ToColor(stroke), (float)thickness))
1.127 + {
1.128 +
1.129 + if (dashArray != null)
1.130 + {
1.131 + pen.DashPattern = this.ToFloatArray(dashArray);
1.132 + }
1.133 +
1.134 + switch (lineJoin)
1.135 + {
1.136 + case OxyPenLineJoin.Round:
1.137 + pen.LineJoin = LineJoin.Round;
1.138 + break;
1.139 + case OxyPenLineJoin.Bevel:
1.140 + pen.LineJoin = LineJoin.Bevel;
1.141 + break;
1.142 +
1.143 + // The default LineJoin is Miter
1.144 + }
1.145 +
1.146 + this.g.DrawLines(pen, this.ToPoints(points));
1.147 + }
1.148 + }
1.149 +
1.150 + /// <summary>
1.151 + /// Draws the polygon.
1.152 + /// </summary>
1.153 + /// <param name="points">The points.</param>
1.154 + /// <param name="fill">The fill.</param>
1.155 + /// <param name="stroke">The stroke.</param>
1.156 + /// <param name="thickness">The thickness.</param>
1.157 + /// <param name="dashArray">The dash array.</param>
1.158 + /// <param name="lineJoin">The line join.</param>
1.159 + /// <param name="aliased">if set to <c>true</c> [aliased].</param>
1.160 + public override void DrawPolygon(
1.161 + IList<ScreenPoint> points,
1.162 + OxyColor fill,
1.163 + OxyColor stroke,
1.164 + double thickness,
1.165 + double[] dashArray,
1.166 + OxyPenLineJoin lineJoin,
1.167 + bool aliased)
1.168 + {
1.169 + if (points.Count < 2)
1.170 + {
1.171 + return;
1.172 + }
1.173 +
1.174 + this.g.SmoothingMode = aliased ? SmoothingMode.None : SmoothingMode.HighQuality;
1.175 +
1.176 + PointF[] pts = this.ToPoints(points);
1.177 + if (fill != null)
1.178 + {
1.179 + this.g.FillPolygon(this.ToBrush(fill), pts);
1.180 + }
1.181 +
1.182 + if (stroke != null && thickness > 0)
1.183 + {
1.184 + using (var pen = new Pen(this.ToColor(stroke), (float)thickness))
1.185 + {
1.186 +
1.187 + if (dashArray != null)
1.188 + {
1.189 + pen.DashPattern = this.ToFloatArray(dashArray);
1.190 + }
1.191 +
1.192 + switch (lineJoin)
1.193 + {
1.194 + case OxyPenLineJoin.Round:
1.195 + pen.LineJoin = LineJoin.Round;
1.196 + break;
1.197 + case OxyPenLineJoin.Bevel:
1.198 + pen.LineJoin = LineJoin.Bevel;
1.199 + break;
1.200 +
1.201 + // The default LineJoin is Miter
1.202 + }
1.203 +
1.204 + this.g.DrawPolygon(pen, pts);
1.205 + }
1.206 + }
1.207 + }
1.208 +
1.209 + /// <summary>
1.210 + /// The draw rectangle.
1.211 + /// </summary>
1.212 + /// <param name="rect">
1.213 + /// The rect.
1.214 + /// </param>
1.215 + /// <param name="fill">
1.216 + /// The fill.
1.217 + /// </param>
1.218 + /// <param name="stroke">
1.219 + /// The stroke.
1.220 + /// </param>
1.221 + /// <param name="thickness">
1.222 + /// The thickness.
1.223 + /// </param>
1.224 + public override void DrawRectangle(OxyRect rect, OxyColor fill, OxyColor stroke, double thickness)
1.225 + {
1.226 + if (fill != null)
1.227 + {
1.228 + this.g.FillRectangle(
1.229 + this.ToBrush(fill), (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height);
1.230 + }
1.231 +
1.232 + if (stroke == null || thickness <= 0)
1.233 + {
1.234 + return;
1.235 + }
1.236 +
1.237 + using (var pen = new Pen(this.ToColor(stroke), (float)thickness))
1.238 + {
1.239 + this.g.DrawRectangle(pen, (float)rect.Left, (float)rect.Top, (float)rect.Width, (float)rect.Height);
1.240 + }
1.241 + }
1.242 +
1.243 + /// <summary>
1.244 + /// The draw text.
1.245 + /// </summary>
1.246 + /// <param name="p">
1.247 + /// The p.
1.248 + /// </param>
1.249 + /// <param name="text">
1.250 + /// The text.
1.251 + /// </param>
1.252 + /// <param name="fill">
1.253 + /// The fill.
1.254 + /// </param>
1.255 + /// <param name="fontFamily">
1.256 + /// The font family.
1.257 + /// </param>
1.258 + /// <param name="fontSize">
1.259 + /// The font size.
1.260 + /// </param>
1.261 + /// <param name="fontWeight">
1.262 + /// The font weight.
1.263 + /// </param>
1.264 + /// <param name="rotate">
1.265 + /// The rotate.
1.266 + /// </param>
1.267 + /// <param name="halign">
1.268 + /// The halign.
1.269 + /// </param>
1.270 + /// <param name="valign">
1.271 + /// The valign.
1.272 + /// </param>
1.273 + /// <param name="maxSize">
1.274 + /// The maximum size of the text.
1.275 + /// </param>
1.276 + public override void DrawText(
1.277 + ScreenPoint p,
1.278 + string text,
1.279 + OxyColor fill,
1.280 + string fontFamily,
1.281 + double fontSize,
1.282 + double fontWeight,
1.283 + double rotate,
1.284 + HorizontalAlignment halign,
1.285 + VerticalAlignment valign,
1.286 + OxySize? maxSize)
1.287 + {
1.288 + var fs = FontStyle.Regular;
1.289 + if (fontWeight >= 700)
1.290 + {
1.291 + fs = FontStyle.Bold;
1.292 + }
1.293 +
1.294 + using (var font = new Font(fontFamily, (float)fontSize * FontsizeFactor, fs))
1.295 + {
1.296 + using (var sf = new StringFormat { Alignment = StringAlignment.Near })
1.297 + {
1.298 + var size = this.g.MeasureString(text, font);
1.299 + if (maxSize != null)
1.300 + {
1.301 + if (size.Width > maxSize.Value.Width)
1.302 + {
1.303 + size.Width = (float)maxSize.Value.Width;
1.304 + }
1.305 +
1.306 + if (size.Height > maxSize.Value.Height)
1.307 + {
1.308 + size.Height = (float)maxSize.Value.Height;
1.309 + }
1.310 + }
1.311 +
1.312 + float dx = 0;
1.313 + if (halign == HorizontalAlignment.Center)
1.314 + {
1.315 + dx = -size.Width / 2;
1.316 + }
1.317 +
1.318 + if (halign == HorizontalAlignment.Right)
1.319 + {
1.320 + dx = -size.Width;
1.321 + }
1.322 +
1.323 + float dy = 0;
1.324 + sf.LineAlignment = StringAlignment.Near;
1.325 + if (valign == VerticalAlignment.Middle)
1.326 + {
1.327 + dy = -size.Height / 2;
1.328 + }
1.329 +
1.330 + if (valign == VerticalAlignment.Bottom)
1.331 + {
1.332 + dy = -size.Height;
1.333 + }
1.334 +
1.335 + this.g.TranslateTransform((float)p.X, (float)p.Y);
1.336 + if (Math.Abs(rotate) > double.Epsilon)
1.337 + {
1.338 + this.g.RotateTransform((float)rotate);
1.339 + }
1.340 +
1.341 + this.g.TranslateTransform(dx, dy);
1.342 +
1.343 + var layoutRectangle = new RectangleF(0, 0, size.Width, size.Height);
1.344 + this.g.DrawString(text, font, this.ToBrush(fill), layoutRectangle, sf);
1.345 +
1.346 + this.g.ResetTransform();
1.347 + }
1.348 + }
1.349 + }
1.350 +
1.351 + /// <summary>
1.352 + /// The measure text.
1.353 + /// </summary>
1.354 + /// <param name="text">The text.</param>
1.355 + /// <param name="fontFamily">The font family.</param>
1.356 + /// <param name="fontSize">The font size.</param>
1.357 + /// <param name="fontWeight">The font weight.</param>
1.358 + /// <returns>The size of the text.</returns>
1.359 + public override OxySize MeasureText(string text, string fontFamily, double fontSize, double fontWeight)
1.360 + {
1.361 + if (text == null)
1.362 + {
1.363 + return OxySize.Empty;
1.364 + }
1.365 +
1.366 + var fs = FontStyle.Regular;
1.367 + if (fontWeight >= 700)
1.368 + {
1.369 + fs = FontStyle.Bold;
1.370 + }
1.371 +
1.372 + using (var font = new Font(fontFamily, (float)fontSize * FontsizeFactor, fs))
1.373 + {
1.374 + var size = this.g.MeasureString(text, font);
1.375 + return new OxySize(size.Width, size.Height);
1.376 + }
1.377 + }
1.378 +
1.379 + /// <summary>
1.380 + /// Converts a fill color to a System.Drawing.Brush.
1.381 + /// </summary>
1.382 + /// <param name="fill">
1.383 + /// The fill color.
1.384 + /// </param>
1.385 + /// <returns>
1.386 + /// The brush.
1.387 + /// </returns>
1.388 + private Brush ToBrush(OxyColor fill)
1.389 + {
1.390 + if (fill != null)
1.391 + {
1.392 + return new SolidBrush(this.ToColor(fill));
1.393 + }
1.394 +
1.395 + return null;
1.396 + }
1.397 +
1.398 + /// <summary>
1.399 + /// Converts a color to a System.Drawing.Color.
1.400 + /// </summary>
1.401 + /// <param name="c">
1.402 + /// The color.
1.403 + /// </param>
1.404 + /// <returns>
1.405 + /// The System.Drawing.Color.
1.406 + /// </returns>
1.407 + private Color ToColor(OxyColor c)
1.408 + {
1.409 + return Color.FromArgb(c.A, c.R, c.G, c.B);
1.410 + }
1.411 +
1.412 + /// <summary>
1.413 + /// Converts a double array to a float array.
1.414 + /// </summary>
1.415 + /// <param name="a">
1.416 + /// The a.
1.417 + /// </param>
1.418 + /// <returns>
1.419 + /// The float array.
1.420 + /// </returns>
1.421 + private float[] ToFloatArray(double[] a)
1.422 + {
1.423 + if (a == null)
1.424 + {
1.425 + return null;
1.426 + }
1.427 +
1.428 + var r = new float[a.Length];
1.429 + for (int i = 0; i < a.Length; i++)
1.430 + {
1.431 + r[i] = (float)a[i];
1.432 + }
1.433 +
1.434 + return r;
1.435 + }
1.436 +
1.437 + /// <summary>
1.438 + /// Converts a list of point to an array of PointF.
1.439 + /// </summary>
1.440 + /// <param name="points">
1.441 + /// The points.
1.442 + /// </param>
1.443 + /// <returns>
1.444 + /// An array of points.
1.445 + /// </returns>
1.446 + private PointF[] ToPoints(IList<ScreenPoint> points)
1.447 + {
1.448 + if (points == null)
1.449 + {
1.450 + return null;
1.451 + }
1.452 +
1.453 + var r = new PointF[points.Count()];
1.454 + int i = 0;
1.455 + foreach (ScreenPoint p in points)
1.456 + {
1.457 + r[i++] = new PointF((float)p.X, (float)p.Y);
1.458 + }
1.459 +
1.460 + return r;
1.461 + }
1.462 +
1.463 + public override void CleanUp()
1.464 + {
1.465 + var imagesToRelease = imageCache.Keys.Where(i => !imagesInUse.Contains(i));
1.466 + foreach (var i in imagesToRelease)
1.467 + {
1.468 + var image = this.GetImage(i);
1.469 + image.Dispose();
1.470 + imageCache.Remove(i);
1.471 + }
1.472 +
1.473 + imagesInUse.Clear();
1.474 + }
1.475 +
1.476 + public override OxyImageInfo GetImageInfo(OxyImage source)
1.477 + {
1.478 + var image = this.GetImage(source);
1.479 + return image == null ? null : new OxyImageInfo { Width = (uint)image.Width, Height = (uint)image.Height, DpiX = image.HorizontalResolution, DpiY = image.VerticalResolution };
1.480 + }
1.481 +
1.482 + 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)
1.483 + {
1.484 + var image = this.GetImage(source);
1.485 + if (image != null)
1.486 + {
1.487 + ImageAttributes ia = null;
1.488 + if (opacity < 1)
1.489 + {
1.490 + var cm = new ColorMatrix
1.491 + {
1.492 + Matrix00 = 1f,
1.493 + Matrix11 = 1f,
1.494 + Matrix22 = 1f,
1.495 + Matrix33 = 1f,
1.496 + Matrix44 = (float)opacity
1.497 + };
1.498 +
1.499 + ia = new ImageAttributes();
1.500 + ia.SetColorMatrix(cm, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
1.501 + }
1.502 +
1.503 + g.InterpolationMode = interpolate ? InterpolationMode.HighQualityBicubic : InterpolationMode.NearestNeighbor;
1.504 + int sx = (int)Math.Round(x);
1.505 + int sy = (int)Math.Round(y);
1.506 + int sw = (int)Math.Round(x + w) - sx;
1.507 + int sh = (int)Math.Round(y + h) - sy;
1.508 + g.DrawImage(image, new Rectangle(sx, sy, sw, sh), srcX, srcY, srcWidth, srcHeight, GraphicsUnit.Pixel, ia);
1.509 + }
1.510 + }
1.511 +
1.512 + private HashSet<OxyImage> imagesInUse = new HashSet<OxyImage>();
1.513 +
1.514 + private Dictionary<OxyImage, Image> imageCache = new Dictionary<OxyImage, Image>();
1.515 +
1.516 +
1.517 + private Image GetImage(OxyImage source)
1.518 + {
1.519 + if (source == null)
1.520 + {
1.521 + return null;
1.522 + }
1.523 +
1.524 + if (!this.imagesInUse.Contains(source))
1.525 + {
1.526 + this.imagesInUse.Add(source);
1.527 + }
1.528 +
1.529 + Image src;
1.530 + if (this.imageCache.TryGetValue(source, out src))
1.531 + {
1.532 + return src;
1.533 + }
1.534 +
1.535 + if (source != null)
1.536 + {
1.537 + Image btm;
1.538 + using (var ms = new MemoryStream(source.GetData()))
1.539 + {
1.540 + btm = Image.FromStream(ms);
1.541 + }
1.542 +
1.543 + this.imageCache.Add(source, btm);
1.544 + return btm;
1.545 + }
1.546 +
1.547 + return null;
1.548 + }
1.549 +
1.550 + public override bool SetClip(OxyRect rect)
1.551 + {
1.552 + this.g.SetClip(rect.ToRect(false));
1.553 + return true;
1.554 + }
1.555 +
1.556 + public override void ResetClip()
1.557 + {
1.558 + this.g.ResetClip();
1.559 + }
1.560 + }
1.561 +}
1.562 \ No newline at end of file