[12762] | 1 | using System;
|
---|
| 2 | using System.IO;
|
---|
| 3 | using System.Net;
|
---|
| 4 | using System.Xml;
|
---|
| 5 | using System.Reflection;
|
---|
| 6 | using System.Windows.Forms;
|
---|
| 7 | using System.Collections.Generic;
|
---|
| 8 | using System.Drawing;
|
---|
| 9 | using System.Drawing.Drawing2D;
|
---|
| 10 |
|
---|
| 11 | using SharpVectors.Xml;
|
---|
| 12 | using SharpVectors.Dom;
|
---|
| 13 | using SharpVectors.Dom.Css;
|
---|
| 14 | using SharpVectors.Dom.Svg;
|
---|
| 15 | using SharpVectors.Dom.Events;
|
---|
| 16 |
|
---|
| 17 | namespace SharpVectors.Renderers.Gdi
|
---|
| 18 | {
|
---|
| 19 | /// <summary>
|
---|
| 20 | /// Renders a Svg image to GDI+
|
---|
| 21 | /// </summary>
|
---|
| 22 | public sealed class GdiGraphicsRenderer : ISvgRenderer, IDisposable
|
---|
| 23 | {
|
---|
| 24 | #region Private Fields
|
---|
| 25 |
|
---|
| 26 | /// <summary>
|
---|
| 27 | /// A counter that tracks the next hit color.
|
---|
| 28 | /// </summary>
|
---|
| 29 | private int counter;
|
---|
| 30 |
|
---|
| 31 | /// <summary>
|
---|
| 32 | /// Maps a 'hit color' to a graphics node.
|
---|
| 33 | /// </summary>
|
---|
| 34 | /// <remarks>
|
---|
| 35 | /// The 'hit color' is an integer identifier that identifies the
|
---|
| 36 | /// graphics node that drew it. When 'hit colors' are drawn onto
|
---|
| 37 | /// a bitmap (ie. <see cref="idMapRaster">idMapRaster</see> the 'hit color'
|
---|
| 38 | /// of each pixel with the help of <see cref="graphicsNodes"
|
---|
| 39 | /// >graphicsNodes</see> can identify for a given x, y coordinate the
|
---|
| 40 | /// relevant graphics node a mouse event should be dispatched to.
|
---|
| 41 | /// </remarks>
|
---|
| 42 | private Dictionary<Color, SvgElement> graphicsNodes = new Dictionary<Color, SvgElement>();
|
---|
| 43 |
|
---|
| 44 | /// <summary>
|
---|
| 45 | /// The bitmap containing the rendered Svg image.
|
---|
| 46 | /// </summary>
|
---|
| 47 | private Bitmap rasterImage;
|
---|
| 48 |
|
---|
| 49 | /// <summary>
|
---|
| 50 | /// A secondary back-buffer used for invalidation repaints. The invalidRect will
|
---|
| 51 | /// be bitblt to the rasterImage front buffer
|
---|
| 52 | /// </summary>
|
---|
| 53 | private Bitmap invalidatedRasterImage;
|
---|
| 54 |
|
---|
| 55 | /// <summary>
|
---|
| 56 | /// A bitmap image that consists of 'hit color' instead of visual
|
---|
| 57 | /// color. A 'hit color' is an integer identifier that identifies
|
---|
| 58 | /// the graphics node that drew it. A 'hit color' can therefore
|
---|
| 59 | /// identify the graphics node that corresponds an x-y coordinates.
|
---|
| 60 | /// </summary>
|
---|
| 61 | private Bitmap idMapRaster;
|
---|
| 62 |
|
---|
| 63 | /// <summary>
|
---|
| 64 | /// The renderer's <see cref="GraphicsWrapper">GraphicsWrapper</see>
|
---|
| 65 | /// object.
|
---|
| 66 | /// </summary>
|
---|
| 67 | private GdiGraphicsWrapper graphics;
|
---|
| 68 |
|
---|
| 69 | /// <summary>
|
---|
| 70 | /// The renderer's back color.
|
---|
| 71 | /// </summary>
|
---|
| 72 | private Color backColor;
|
---|
| 73 |
|
---|
| 74 | /// <summary>
|
---|
| 75 | /// The renderer's <see cref="SvgWindow">SvgWindow</see> object.
|
---|
| 76 | /// </summary>
|
---|
| 77 | private ISvgWindow window;
|
---|
| 78 |
|
---|
| 79 | /// <summary>
|
---|
| 80 | ///
|
---|
| 81 | /// </summary>
|
---|
| 82 | private float currentDownX;
|
---|
| 83 | private float currentDownY;
|
---|
| 84 | private IEventTarget currentTarget;
|
---|
| 85 | private IEventTarget currentDownTarget;
|
---|
| 86 |
|
---|
| 87 | private GdiRenderingHelper _svgRenderer;
|
---|
| 88 |
|
---|
| 89 | private SvgRectF invalidRect = SvgRectF.Empty;
|
---|
| 90 |
|
---|
| 91 | #endregion
|
---|
| 92 |
|
---|
| 93 | #region Constructors and Destructor
|
---|
| 94 |
|
---|
| 95 | /// <summary>
|
---|
| 96 | /// Initializes a new instance of the GdiRenderer class.
|
---|
| 97 | /// </summary>
|
---|
| 98 | public GdiGraphicsRenderer()
|
---|
| 99 | {
|
---|
| 100 | counter = 0;
|
---|
| 101 | _svgRenderer = new GdiRenderingHelper(this);
|
---|
| 102 |
|
---|
| 103 | backColor = Color.White;
|
---|
| 104 | }
|
---|
| 105 |
|
---|
| 106 | ~GdiGraphicsRenderer()
|
---|
| 107 | {
|
---|
| 108 | this.Dispose(false);
|
---|
| 109 | }
|
---|
| 110 |
|
---|
| 111 | #endregion
|
---|
| 112 |
|
---|
| 113 | #region Public Properties
|
---|
| 114 |
|
---|
| 115 | /// <summary>
|
---|
| 116 | /// Gets a bitmap image of the a rendered Svg document.
|
---|
| 117 | /// </summary>
|
---|
| 118 | public Bitmap RasterImage
|
---|
| 119 | {
|
---|
| 120 | get
|
---|
| 121 | {
|
---|
| 122 | return rasterImage;
|
---|
| 123 | }
|
---|
| 124 | }
|
---|
| 125 |
|
---|
| 126 | /// <summary>
|
---|
| 127 | /// Gets the image map of the rendered Svg document. This
|
---|
| 128 | /// is a picture of how the renderer will map the (x,y) positions
|
---|
| 129 | /// of mouse events to objects. You can display this raster
|
---|
| 130 | /// to help in debugging of hit testing.
|
---|
| 131 | /// </summary>
|
---|
| 132 | public Bitmap IdMapRaster
|
---|
| 133 | {
|
---|
| 134 | get
|
---|
| 135 | {
|
---|
| 136 | return idMapRaster;
|
---|
| 137 | }
|
---|
| 138 | }
|
---|
| 139 |
|
---|
| 140 | /// <summary>
|
---|
| 141 | /// Gets or sets the <see cref="Window">Window</see> of the
|
---|
| 142 | /// renderer.
|
---|
| 143 | /// </summary>
|
---|
| 144 | /// <value>
|
---|
| 145 | /// The <see cref="Window">Window</see> of the renderer.
|
---|
| 146 | /// </value>
|
---|
| 147 | public ISvgWindow Window
|
---|
| 148 | {
|
---|
| 149 | get
|
---|
| 150 | {
|
---|
| 151 | return window;
|
---|
| 152 | }
|
---|
| 153 | set
|
---|
| 154 | {
|
---|
| 155 | window = value;
|
---|
| 156 | }
|
---|
| 157 | }
|
---|
| 158 |
|
---|
| 159 | /// <summary>
|
---|
| 160 | /// Gets or sets the back color of the renderer.
|
---|
| 161 | /// </summary>
|
---|
| 162 | /// <value>
|
---|
| 163 | /// The back color of the renderer.
|
---|
| 164 | /// </value>
|
---|
| 165 | public Color BackColor
|
---|
| 166 | {
|
---|
| 167 | get
|
---|
| 168 | {
|
---|
| 169 | return backColor;
|
---|
| 170 | }
|
---|
| 171 | set
|
---|
| 172 | {
|
---|
| 173 | backColor = value;
|
---|
| 174 | }
|
---|
| 175 | }
|
---|
| 176 |
|
---|
| 177 | /// <summary>
|
---|
| 178 | /// Gets or sets the <see cref="GraphicsWrapper">GraphicsWrapper</see>
|
---|
| 179 | /// object associated with this renderer.
|
---|
| 180 | /// </summary>
|
---|
| 181 | /// <value>
|
---|
| 182 | /// The <see cref="GraphicsWrapper">GraphicsWrapper</see> object
|
---|
| 183 | /// associated with this renderer.
|
---|
| 184 | /// </value>
|
---|
| 185 | public GdiGraphicsWrapper GraphicsWrapper
|
---|
| 186 | {
|
---|
| 187 | get
|
---|
| 188 | {
|
---|
| 189 | return graphics;
|
---|
| 190 | }
|
---|
| 191 | set
|
---|
| 192 | {
|
---|
| 193 | graphics = value;
|
---|
| 194 | }
|
---|
| 195 | }
|
---|
| 196 |
|
---|
| 197 | /// <summary>
|
---|
| 198 | /// Gets or sets the <see cref="Graphics">Graphics</see> object
|
---|
| 199 | /// associated with this renderer.
|
---|
| 200 | /// </summary>
|
---|
| 201 | /// <value>
|
---|
| 202 | /// The <see cref="Graphics">Graphics</see> object associated
|
---|
| 203 | /// with this renderer.
|
---|
| 204 | /// </value>
|
---|
| 205 | public Graphics Graphics
|
---|
| 206 | {
|
---|
| 207 | get
|
---|
| 208 | {
|
---|
| 209 | return graphics.Graphics;
|
---|
| 210 | }
|
---|
| 211 | set
|
---|
| 212 | {
|
---|
| 213 | graphics.Graphics = value;
|
---|
| 214 | }
|
---|
| 215 | }
|
---|
| 216 |
|
---|
| 217 | #endregion
|
---|
| 218 |
|
---|
| 219 | #region Public Methods
|
---|
| 220 |
|
---|
| 221 | public void InvalidateRect(SvgRectF rect)
|
---|
| 222 | {
|
---|
| 223 | if (invalidRect == SvgRectF.Empty)
|
---|
| 224 | invalidRect = rect;
|
---|
| 225 | else
|
---|
| 226 | invalidRect.Intersect(rect);
|
---|
| 227 | }
|
---|
| 228 |
|
---|
| 229 | /// <summary>
|
---|
| 230 | /// Renders the <see cref="SvgElement">SvgElement</see>.
|
---|
| 231 | /// </summary>
|
---|
| 232 | /// <param name="node">
|
---|
| 233 | /// The <see cref="SvgElement">SvgElement</see> node to be
|
---|
| 234 | /// rendered
|
---|
| 235 | /// </param>
|
---|
| 236 | /// <returns>
|
---|
| 237 | /// The bitmap on which the rendering was performed.
|
---|
| 238 | /// </returns>
|
---|
| 239 | public void Render(ISvgElement node)
|
---|
| 240 | {
|
---|
| 241 | SvgRectF updatedRect;
|
---|
| 242 | if (invalidRect != SvgRectF.Empty)
|
---|
| 243 | updatedRect = new SvgRectF(invalidRect.X, invalidRect.Y,
|
---|
| 244 | invalidRect.Width, invalidRect.Height);
|
---|
| 245 | else
|
---|
| 246 | updatedRect = SvgRectF.Empty;
|
---|
| 247 |
|
---|
| 248 | RendererBeforeRender();
|
---|
| 249 |
|
---|
| 250 | if (graphics != null && graphics.Graphics != null)
|
---|
| 251 | {
|
---|
| 252 | _svgRenderer.Render(node);
|
---|
| 253 | }
|
---|
| 254 |
|
---|
| 255 | RendererAfterRender();
|
---|
| 256 |
|
---|
| 257 | if (onRender != null)
|
---|
| 258 | OnRender(updatedRect);
|
---|
| 259 | }
|
---|
| 260 |
|
---|
| 261 | /// <summary>
|
---|
| 262 | /// Renders the <see cref="SvgDocument">SvgDocument</see>.
|
---|
| 263 | /// </summary>
|
---|
| 264 | /// <param name="node">
|
---|
| 265 | /// The <see cref="SvgDocument">SvgDocument</see> node to be
|
---|
| 266 | /// rendered
|
---|
| 267 | /// </param>
|
---|
| 268 | /// <returns>
|
---|
| 269 | /// The bitmap on which the rendering was performed.
|
---|
| 270 | /// </returns>
|
---|
| 271 | public void Render(ISvgDocument node)
|
---|
| 272 | {
|
---|
| 273 | SvgRectF updatedRect;
|
---|
| 274 | if (invalidRect != SvgRectF.Empty)
|
---|
| 275 | updatedRect = new SvgRectF(invalidRect.X, invalidRect.Y,
|
---|
| 276 | invalidRect.Width, invalidRect.Height);
|
---|
| 277 | else
|
---|
| 278 | updatedRect = SvgRectF.Empty;
|
---|
| 279 |
|
---|
| 280 | RendererBeforeRender();
|
---|
| 281 |
|
---|
| 282 | if (graphics != null && graphics.Graphics != null)
|
---|
| 283 | {
|
---|
| 284 | _svgRenderer.Render(node);
|
---|
| 285 | }
|
---|
| 286 |
|
---|
| 287 | RendererAfterRender();
|
---|
| 288 |
|
---|
| 289 | if (onRender != null)
|
---|
| 290 | OnRender(updatedRect);
|
---|
| 291 | }
|
---|
| 292 |
|
---|
| 293 | public void RenderChildren(ISvgElement node)
|
---|
| 294 | {
|
---|
| 295 | _svgRenderer.RenderChildren(node);
|
---|
| 296 | }
|
---|
| 297 |
|
---|
| 298 | public void ClearMap()
|
---|
| 299 | {
|
---|
| 300 | graphicsNodes.Clear();
|
---|
| 301 | }
|
---|
| 302 |
|
---|
| 303 | /// <summary>
|
---|
| 304 | /// The invalidated region
|
---|
| 305 | /// </summary>
|
---|
| 306 | public SvgRectF InvalidRect
|
---|
| 307 | {
|
---|
| 308 | get
|
---|
| 309 | {
|
---|
| 310 | return invalidRect;
|
---|
| 311 | }
|
---|
| 312 | set
|
---|
| 313 | {
|
---|
| 314 | invalidRect = value;
|
---|
| 315 | }
|
---|
| 316 | }
|
---|
| 317 |
|
---|
| 318 | public ISvgRect GetRenderedBounds(ISvgElement element, float margin)
|
---|
| 319 | {
|
---|
| 320 | SvgTransformableElement transElement = element as SvgTransformableElement;
|
---|
| 321 | if (transElement != null)
|
---|
| 322 | {
|
---|
| 323 | SvgRectF rect = this.GetElementBounds(transElement, margin);
|
---|
| 324 |
|
---|
| 325 | return new SvgRect(rect.X, rect.Y, rect.Width, rect.Height);
|
---|
| 326 | }
|
---|
| 327 |
|
---|
| 328 | return null;
|
---|
| 329 | }
|
---|
| 330 |
|
---|
| 331 | #endregion
|
---|
| 332 |
|
---|
| 333 | #region Event handlers
|
---|
| 334 |
|
---|
| 335 | private RenderEvent onRender;
|
---|
| 336 | public RenderEvent OnRender
|
---|
| 337 | {
|
---|
| 338 | get
|
---|
| 339 | {
|
---|
| 340 | return onRender;
|
---|
| 341 | }
|
---|
| 342 | set
|
---|
| 343 | {
|
---|
| 344 | onRender = value;
|
---|
| 345 | }
|
---|
| 346 | }
|
---|
| 347 |
|
---|
| 348 | /// <summary>
|
---|
| 349 | /// Processes mouse events.
|
---|
| 350 | /// </summary>
|
---|
| 351 | /// <param name="type">
|
---|
| 352 | /// A string describing the type of mouse event that occured.
|
---|
| 353 | /// </param>
|
---|
| 354 | /// <param name="e">
|
---|
| 355 | /// The <see cref="MouseEventArgs">MouseEventArgs</see> that contains
|
---|
| 356 | /// the event data.
|
---|
| 357 | /// </param>
|
---|
| 358 | public void OnMouseEvent(string type, MouseEventArgs e)
|
---|
| 359 | {
|
---|
| 360 | if (idMapRaster != null)
|
---|
| 361 | {
|
---|
| 362 | try
|
---|
| 363 | {
|
---|
| 364 | Color pixel = idMapRaster.GetPixel(e.X, e.Y);
|
---|
| 365 | SvgElement grElement = GetElementFromColor(pixel);
|
---|
| 366 | if (grElement != null)
|
---|
| 367 | {
|
---|
| 368 | IEventTarget target;
|
---|
| 369 | if (grElement.ElementInstance != null)
|
---|
| 370 | target = grElement.ElementInstance as IEventTarget;
|
---|
| 371 | else
|
---|
| 372 | target = grElement as IEventTarget;
|
---|
| 373 |
|
---|
| 374 | if (target != null)
|
---|
| 375 | {
|
---|
| 376 | switch (type)
|
---|
| 377 | {
|
---|
| 378 | case "mousemove":
|
---|
| 379 | {
|
---|
| 380 | if (currentTarget == target)
|
---|
| 381 | {
|
---|
| 382 | target.DispatchEvent(new MouseEvent(
|
---|
| 383 | EventType.MouseMove, true, false,
|
---|
| 384 | null, // todo: put view here
|
---|
| 385 | 0, // todo: put detail here
|
---|
| 386 | e.X, e.Y, e.X, e.Y,
|
---|
| 387 | false, false, false, false,
|
---|
| 388 | 0, null, false));
|
---|
| 389 | }
|
---|
| 390 | else
|
---|
| 391 | {
|
---|
| 392 | if (currentTarget != null)
|
---|
| 393 | {
|
---|
| 394 | currentTarget.DispatchEvent(new MouseEvent(
|
---|
| 395 | EventType.MouseOut, true, false,
|
---|
| 396 | null, // todo: put view here
|
---|
| 397 | 0, // todo: put detail here
|
---|
| 398 | e.X, e.Y, e.X, e.Y,
|
---|
| 399 | false, false, false, false,
|
---|
| 400 | 0, null, false));
|
---|
| 401 | }
|
---|
| 402 |
|
---|
| 403 | target.DispatchEvent(new MouseEvent(
|
---|
| 404 | EventType.MouseOver, true, false,
|
---|
| 405 | null, // todo: put view here
|
---|
| 406 | 0, // todo: put detail here
|
---|
| 407 | e.X, e.Y, e.X, e.Y,
|
---|
| 408 | false, false, false, false,
|
---|
| 409 | 0, null, false));
|
---|
| 410 | }
|
---|
| 411 | break;
|
---|
| 412 | }
|
---|
| 413 | case "mousedown":
|
---|
| 414 | target.DispatchEvent(new MouseEvent(
|
---|
| 415 | EventType.MouseDown, true, false,
|
---|
| 416 | null, // todo: put view here
|
---|
| 417 | 0, // todo: put detail here
|
---|
| 418 | e.X, e.Y, e.X, e.Y,
|
---|
| 419 | false, false, false, false,
|
---|
| 420 | 0, null, false));
|
---|
| 421 | currentDownTarget = target;
|
---|
| 422 | currentDownX = e.X;
|
---|
| 423 | currentDownY = e.Y;
|
---|
| 424 | break;
|
---|
| 425 | case "mouseup":
|
---|
| 426 | target.DispatchEvent(new MouseEvent(
|
---|
| 427 | EventType.MouseUp, true, false,
|
---|
| 428 | null, // todo: put view here
|
---|
| 429 | 0, // todo: put detail here
|
---|
| 430 | e.X, e.Y, e.X, e.Y,
|
---|
| 431 | false, false, false, false,
|
---|
| 432 | 0, null, false));
|
---|
| 433 | if (/*currentDownTarget == target &&*/ Math.Abs(currentDownX - e.X) < 5 && Math.Abs(currentDownY - e.Y) < 5)
|
---|
| 434 | {
|
---|
| 435 | target.DispatchEvent(new MouseEvent(
|
---|
| 436 | EventType.Click, true, false,
|
---|
| 437 | null, // todo: put view here
|
---|
| 438 | 0, // todo: put detail here
|
---|
| 439 | e.X, e.Y, e.X, e.Y,
|
---|
| 440 | false, false, false, false,
|
---|
| 441 | 0, null, false));
|
---|
| 442 | }
|
---|
| 443 | currentDownTarget = null;
|
---|
| 444 | currentDownX = 0;
|
---|
| 445 | currentDownY = 0;
|
---|
| 446 | break;
|
---|
| 447 | }
|
---|
| 448 | currentTarget = target;
|
---|
| 449 | }
|
---|
| 450 | else
|
---|
| 451 | {
|
---|
| 452 |
|
---|
| 453 | // jr patch
|
---|
| 454 | if (currentTarget != null && type == "mousemove")
|
---|
| 455 | {
|
---|
| 456 | currentTarget.DispatchEvent(new MouseEvent(
|
---|
| 457 | EventType.MouseOut, true, false,
|
---|
| 458 | null, // todo: put view here
|
---|
| 459 | 0, // todo: put detail here
|
---|
| 460 | e.X, e.Y, e.X, e.Y,
|
---|
| 461 | false, false, false, false,
|
---|
| 462 | 0, null, false));
|
---|
| 463 | }
|
---|
| 464 | currentTarget = null;
|
---|
| 465 | }
|
---|
| 466 | }
|
---|
| 467 | else
|
---|
| 468 | {
|
---|
| 469 | // jr patch
|
---|
| 470 | if (currentTarget != null && type == "mousemove")
|
---|
| 471 | {
|
---|
| 472 | currentTarget.DispatchEvent(new MouseEvent(
|
---|
| 473 | EventType.MouseOut, true, false,
|
---|
| 474 | null, // todo: put view here
|
---|
| 475 | 0, // todo: put detail here
|
---|
| 476 | e.X, e.Y, e.X, e.Y,
|
---|
| 477 | false, false, false, false,
|
---|
| 478 | 0, null, false));
|
---|
| 479 | }
|
---|
| 480 | currentTarget = null;
|
---|
| 481 | }
|
---|
| 482 | }
|
---|
| 483 | catch
|
---|
| 484 | {
|
---|
| 485 | }
|
---|
| 486 | }
|
---|
| 487 | }
|
---|
| 488 |
|
---|
| 489 | #endregion
|
---|
| 490 |
|
---|
| 491 | #region Miscellaneous Methods
|
---|
| 492 |
|
---|
| 493 | /// <summary>
|
---|
| 494 | /// Allocate a hit color for the specified graphics node.
|
---|
| 495 | /// </summary>
|
---|
| 496 | /// <param name="grNode">
|
---|
| 497 | /// The <see cref="GraphicsNode">GraphicsNode</see> object for which to
|
---|
| 498 | /// allocate a new hit color.
|
---|
| 499 | /// </param>
|
---|
| 500 | /// <returns>
|
---|
| 501 | /// The hit color for the <see cref="GraphicsNode">GraphicsNode</see>
|
---|
| 502 | /// object.
|
---|
| 503 | /// </returns>
|
---|
| 504 | internal Color GetNextColor(SvgElement element)
|
---|
| 505 | {
|
---|
| 506 | // TODO: [newhoggy] It looks like there is a potential memory leak here.
|
---|
| 507 | // We only ever add to the graphicsNodes map, never remove
|
---|
| 508 | // from it, so it will grow every time this function is called.
|
---|
| 509 |
|
---|
| 510 | // The counter is used to generate IDs in the range [0,2^24-1]
|
---|
| 511 | // The 24 bits of the counter are interpreted as follows:
|
---|
| 512 | // [red 7 bits | green 7 bits | blue 7 bits |shuffle term 3 bits]
|
---|
| 513 | // The shuffle term is used to define how the remaining high
|
---|
| 514 | // bit is set on each color. The colors are generated in the
|
---|
| 515 | // range [0,127] (7 bits) instead of [0,255]. Then the shuffle term
|
---|
| 516 | // is used to adjust them into the range [0,255].
|
---|
| 517 | // This algorithm has the feature that consecutive ids generate
|
---|
| 518 | // visually distinct colors.
|
---|
| 519 | int id = counter++; // Zero should be the first color.
|
---|
| 520 | int shuffleTerm = id & 7;
|
---|
| 521 | int r = 0x7f & (id >> 17);
|
---|
| 522 | int g = 0x7f & (id >> 10);
|
---|
| 523 | int b = 0x7f & (id >> 3);
|
---|
| 524 |
|
---|
| 525 | switch (shuffleTerm)
|
---|
| 526 | {
|
---|
| 527 | case 0: break;
|
---|
| 528 | case 1: b |= 0x80; break;
|
---|
| 529 | case 2: g |= 0x80; break;
|
---|
| 530 | case 3: g |= 0x80; b |= 0x80; break;
|
---|
| 531 | case 4: r |= 0x80; break;
|
---|
| 532 | case 5: r |= 0x80; b |= 0x80; break;
|
---|
| 533 | case 6: r |= 0x80; g |= 0x80; break;
|
---|
| 534 | case 7: r |= 0x80; g |= 0x80; b |= 0x80; break;
|
---|
| 535 | }
|
---|
| 536 |
|
---|
| 537 | Color color = Color.FromArgb(r, g, b);
|
---|
| 538 |
|
---|
| 539 | graphicsNodes.Add(color, element);
|
---|
| 540 |
|
---|
| 541 | return color;
|
---|
| 542 | }
|
---|
| 543 |
|
---|
| 544 | internal void RemoveColor(Color color, SvgElement element)
|
---|
| 545 | {
|
---|
| 546 | if (!color.IsEmpty)
|
---|
| 547 | {
|
---|
| 548 | graphicsNodes[color] = null;
|
---|
| 549 | graphicsNodes.Remove(color);
|
---|
| 550 | }
|
---|
| 551 | }
|
---|
| 552 |
|
---|
| 553 | /// <summary>
|
---|
| 554 | /// Gets the <see cref="GraphicsNode">GraphicsNode</see> object that
|
---|
| 555 | /// corresponds to the given hit color.
|
---|
| 556 | /// </summary>
|
---|
| 557 | /// <param name="color">
|
---|
| 558 | /// The hit color for which to get the corresponding
|
---|
| 559 | /// <see cref="GraphicsNode">GraphicsNode</see> object.
|
---|
| 560 | /// </param>
|
---|
| 561 | /// <remarks>
|
---|
| 562 | /// Returns <c>null</c> if a corresponding
|
---|
| 563 | /// <see cref="GraphicsNode">GraphicsNode</see> object cannot be
|
---|
| 564 | /// found for the given hit color.
|
---|
| 565 | /// </remarks>
|
---|
| 566 | /// <returns>
|
---|
| 567 | /// The <see cref="GraphicsNode">GraphicsNode</see> object that
|
---|
| 568 | /// corresponds to the given hit color
|
---|
| 569 | /// </returns>
|
---|
| 570 | private SvgElement GetElementFromColor(Color color)
|
---|
| 571 | {
|
---|
| 572 | if (color.A == 0)
|
---|
| 573 | {
|
---|
| 574 | return null;
|
---|
| 575 | }
|
---|
| 576 | else
|
---|
| 577 | {
|
---|
| 578 | if (graphicsNodes.ContainsKey(color))
|
---|
| 579 | {
|
---|
| 580 | return graphicsNodes[color];
|
---|
| 581 | }
|
---|
| 582 |
|
---|
| 583 | return null;
|
---|
| 584 | }
|
---|
| 585 | }
|
---|
| 586 |
|
---|
| 587 | /// <summary>
|
---|
| 588 | /// TODO: This method is not used.
|
---|
| 589 | /// </summary>
|
---|
| 590 | /// <param name="color">
|
---|
| 591 | /// </param>
|
---|
| 592 | /// <returns>
|
---|
| 593 | /// </returns>
|
---|
| 594 | private static int ColorToId(Color color)
|
---|
| 595 | {
|
---|
| 596 | int r = color.R;
|
---|
| 597 | int g = color.G;
|
---|
| 598 | int b = color.B;
|
---|
| 599 | int shuffleTerm = 0;
|
---|
| 600 |
|
---|
| 601 | if (0 != (r & 0x80))
|
---|
| 602 | {
|
---|
| 603 | shuffleTerm |= 4;
|
---|
| 604 | r &= 0x7f;
|
---|
| 605 | }
|
---|
| 606 |
|
---|
| 607 | if (0 != (g & 0x80))
|
---|
| 608 | {
|
---|
| 609 | shuffleTerm |= 2;
|
---|
| 610 | g &= 0x7f;
|
---|
| 611 | }
|
---|
| 612 |
|
---|
| 613 | if (0 != (b & 0x80))
|
---|
| 614 | {
|
---|
| 615 | shuffleTerm |= 1;
|
---|
| 616 | b &= 0x7f;
|
---|
| 617 | }
|
---|
| 618 |
|
---|
| 619 | return (r << 17) + (g << 10) + (b << 3) + shuffleTerm;
|
---|
| 620 | }
|
---|
| 621 |
|
---|
| 622 | private SvgRectF GetElementBounds(SvgTransformableElement element, float margin)
|
---|
| 623 | {
|
---|
| 624 | SvgRenderingHint hint = element.RenderingHint;
|
---|
| 625 | if (hint == SvgRenderingHint.Shape || hint == SvgRenderingHint.Text)
|
---|
| 626 | {
|
---|
| 627 | GraphicsPath gp = GdiRendering.CreatePath(element);
|
---|
| 628 | ISvgMatrix svgMatrix = element.GetScreenCTM();
|
---|
| 629 |
|
---|
| 630 | Matrix matrix = new Matrix((float)svgMatrix.A, (float)svgMatrix.B, (float)svgMatrix.C,
|
---|
| 631 | (float)svgMatrix.D, (float)svgMatrix.E, (float)svgMatrix.F);
|
---|
| 632 | SvgRectF bounds = SvgConverter.ToRect(gp.GetBounds(matrix));
|
---|
| 633 | bounds = SvgRectF.Inflate(bounds, margin, margin);
|
---|
| 634 |
|
---|
| 635 | return bounds;
|
---|
| 636 | }
|
---|
| 637 |
|
---|
| 638 | SvgUseElement useElement = element as SvgUseElement;
|
---|
| 639 | if (useElement != null)
|
---|
| 640 | {
|
---|
| 641 | SvgTransformableElement refEl = useElement.ReferencedElement as SvgTransformableElement;
|
---|
| 642 | if (refEl == null)
|
---|
| 643 | return SvgRectF.Empty;
|
---|
| 644 |
|
---|
| 645 | XmlElement refElParent = (XmlElement)refEl.ParentNode;
|
---|
| 646 | element.OwnerDocument.Static = true;
|
---|
| 647 | useElement.CopyToReferencedElement(refEl);
|
---|
| 648 | element.AppendChild(refEl);
|
---|
| 649 |
|
---|
| 650 | SvgRectF bbox = this.GetElementBounds(refEl, margin);
|
---|
| 651 |
|
---|
| 652 | element.RemoveChild(refEl);
|
---|
| 653 | useElement.RestoreReferencedElement(refEl);
|
---|
| 654 | refElParent.AppendChild(refEl);
|
---|
| 655 | element.OwnerDocument.Static = false;
|
---|
| 656 |
|
---|
| 657 | return bbox;
|
---|
| 658 | }
|
---|
| 659 |
|
---|
| 660 | SvgRectF union = SvgRectF.Empty;
|
---|
| 661 | SvgTransformableElement transformChild;
|
---|
| 662 | foreach (XmlNode childNode in element.ChildNodes)
|
---|
| 663 | {
|
---|
| 664 | if (childNode is SvgDefsElement)
|
---|
| 665 | continue;
|
---|
| 666 | if (childNode is ISvgTransformable)
|
---|
| 667 | {
|
---|
| 668 | transformChild = (SvgTransformableElement)childNode;
|
---|
| 669 | SvgRectF bbox = this.GetElementBounds(transformChild, margin);
|
---|
| 670 | if (bbox != SvgRectF.Empty)
|
---|
| 671 | {
|
---|
| 672 | if (union == SvgRectF.Empty)
|
---|
| 673 | union = bbox;
|
---|
| 674 | else
|
---|
| 675 | union = SvgRectF.Union(union, bbox);
|
---|
| 676 | }
|
---|
| 677 | }
|
---|
| 678 | }
|
---|
| 679 |
|
---|
| 680 | return union;
|
---|
| 681 | }
|
---|
| 682 |
|
---|
| 683 | #endregion
|
---|
| 684 |
|
---|
| 685 | #region Private Methods
|
---|
| 686 |
|
---|
| 687 | /// <summary>
|
---|
| 688 | /// BeforeRender - Make sure we have a Graphics object to render to.
|
---|
| 689 | /// If we don't have one, then create one to match the SvgWindow's
|
---|
| 690 | /// physical dimensions.
|
---|
| 691 | /// </summary>
|
---|
| 692 | private void RendererBeforeRender()
|
---|
| 693 | {
|
---|
| 694 | // Testing for null here allows "advanced" developers to create their own Graphics object for rendering
|
---|
| 695 | if (graphics == null)
|
---|
| 696 | {
|
---|
| 697 | // Get the current SVGWindow's width and height
|
---|
| 698 | int innerWidth = (int)window.InnerWidth;
|
---|
| 699 | int innerHeight = (int)window.InnerHeight;
|
---|
| 700 |
|
---|
| 701 | // Make sure we have an actual area to render to
|
---|
| 702 | if (innerWidth > 0 && innerHeight > 0)
|
---|
| 703 | {
|
---|
| 704 | // See if we already have a rasterImage that matches the current SVGWindow dimensions
|
---|
| 705 | if (rasterImage == null || rasterImage.Width != innerWidth || rasterImage.Height != innerHeight)
|
---|
| 706 | {
|
---|
| 707 | // Nope, so create one
|
---|
| 708 | if (rasterImage != null)
|
---|
| 709 | {
|
---|
| 710 | rasterImage.Dispose();
|
---|
| 711 | rasterImage = null;
|
---|
| 712 | }
|
---|
| 713 | rasterImage = new Bitmap(innerWidth, innerHeight);
|
---|
| 714 | }
|
---|
| 715 |
|
---|
| 716 | // Maybe we are only repainting an invalidated section
|
---|
| 717 | if (invalidRect != SvgRectF.Empty)
|
---|
| 718 | {
|
---|
| 719 | // TODO: Worry about pan...
|
---|
| 720 | if (invalidRect.X < 0)
|
---|
| 721 | invalidRect.X = 0;
|
---|
| 722 | if (invalidRect.Y < 0)
|
---|
| 723 | invalidRect.Y = 0;
|
---|
| 724 | if (invalidRect.Right > innerWidth)
|
---|
| 725 | invalidRect.Width = innerWidth - invalidRect.X;
|
---|
| 726 | if (invalidRect.Bottom > innerHeight)
|
---|
| 727 | invalidRect.Height = innerHeight - invalidRect.Y;
|
---|
| 728 |
|
---|
| 729 | if (invalidatedRasterImage == null || invalidatedRasterImage.Width < invalidRect.Right ||
|
---|
| 730 | invalidatedRasterImage.Height < invalidRect.Bottom)
|
---|
| 731 | {
|
---|
| 732 | // Nope, so create one
|
---|
| 733 | if (invalidatedRasterImage != null)
|
---|
| 734 | {
|
---|
| 735 | invalidatedRasterImage.Dispose();
|
---|
| 736 | invalidatedRasterImage = null;
|
---|
| 737 | }
|
---|
| 738 | invalidatedRasterImage = new Bitmap((int)invalidRect.Right, (int)invalidRect.Bottom);
|
---|
| 739 | }
|
---|
| 740 | // Make a GraphicsWrapper object from the regionRasterImage and clear it to the background color
|
---|
| 741 | graphics = GdiGraphicsWrapper.FromImage(invalidatedRasterImage, false);
|
---|
| 742 |
|
---|
| 743 | graphics.Clear(backColor);
|
---|
| 744 | }
|
---|
| 745 | else
|
---|
| 746 | {
|
---|
| 747 | // Make a GraphicsWrapper object from the rasterImage and clear it to the background color
|
---|
| 748 | graphics = GdiGraphicsWrapper.FromImage(rasterImage, false);
|
---|
| 749 | graphics.Clear(backColor);
|
---|
| 750 | }
|
---|
| 751 | }
|
---|
| 752 | }
|
---|
| 753 | }
|
---|
| 754 |
|
---|
| 755 | /// <summary>
|
---|
| 756 | /// AfterRender - Dispose of Graphics object created for rendering.
|
---|
| 757 | /// </summary>
|
---|
| 758 | private void RendererAfterRender()
|
---|
| 759 | {
|
---|
| 760 | if (graphics != null)
|
---|
| 761 | {
|
---|
| 762 | // Check if we only invalidated a rect
|
---|
| 763 | if (invalidRect != SvgRectF.Empty)
|
---|
| 764 | {
|
---|
| 765 | // We actually drew everything on invalidatedRasterImage and now we
|
---|
| 766 | // need to copy that to rasterImage
|
---|
| 767 | Graphics tempGraphics = Graphics.FromImage(rasterImage);
|
---|
| 768 | tempGraphics.DrawImage(invalidatedRasterImage, invalidRect.X, invalidRect.Y,
|
---|
| 769 | GdiConverter.ToRectangle(invalidRect), GraphicsUnit.Pixel);
|
---|
| 770 | tempGraphics.Dispose();
|
---|
| 771 | tempGraphics = null;
|
---|
| 772 |
|
---|
| 773 | // If we currently have an idMapRaster here, then we need to create
|
---|
| 774 | // a temporary graphics object to draw the invalidated portion from
|
---|
| 775 | // our main graphics window onto it.
|
---|
| 776 | if (idMapRaster != null)
|
---|
| 777 | {
|
---|
| 778 | tempGraphics = Graphics.FromImage(idMapRaster);
|
---|
| 779 | tempGraphics.DrawImage(graphics.IdMapRaster, invalidRect.X, invalidRect.Y,
|
---|
| 780 | GdiConverter.ToRectangle(invalidRect), GraphicsUnit.Pixel);
|
---|
| 781 | tempGraphics.Dispose();
|
---|
| 782 | tempGraphics = null;
|
---|
| 783 | }
|
---|
| 784 | else
|
---|
| 785 | {
|
---|
| 786 | idMapRaster = graphics.IdMapRaster;
|
---|
| 787 | }
|
---|
| 788 | // We have updated the invalid region
|
---|
| 789 | invalidRect = SvgRectF.Empty;
|
---|
| 790 | }
|
---|
| 791 | else
|
---|
| 792 | {
|
---|
| 793 | if (idMapRaster != null && idMapRaster != graphics.IdMapRaster)
|
---|
| 794 | idMapRaster.Dispose();
|
---|
| 795 | idMapRaster = graphics.IdMapRaster;
|
---|
| 796 | }
|
---|
| 797 |
|
---|
| 798 | graphics.Dispose();
|
---|
| 799 | graphics = null;
|
---|
| 800 | }
|
---|
| 801 | }
|
---|
| 802 |
|
---|
| 803 | #endregion
|
---|
| 804 |
|
---|
| 805 | #region IDisposable Members
|
---|
| 806 |
|
---|
| 807 | public void Dispose()
|
---|
| 808 | {
|
---|
| 809 | this.Dispose(true);
|
---|
| 810 | GC.SuppressFinalize(this);
|
---|
| 811 | }
|
---|
| 812 |
|
---|
| 813 | private void Dispose(bool disposing)
|
---|
| 814 | {
|
---|
| 815 | if (idMapRaster != null)
|
---|
| 816 | idMapRaster.Dispose();
|
---|
| 817 | if (invalidatedRasterImage != null)
|
---|
| 818 | invalidatedRasterImage.Dispose();
|
---|
| 819 | if (rasterImage != null)
|
---|
| 820 | rasterImage.Dispose();
|
---|
| 821 | if (graphics != null)
|
---|
| 822 | graphics.Dispose();
|
---|
| 823 | }
|
---|
| 824 |
|
---|
| 825 | #endregion
|
---|
| 826 | }
|
---|
| 827 | }
|
---|