aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap
diff options
context:
space:
mode:
authorRoy Ben-Shabat <Roy@Twine-s.com>2018-01-16 12:17:10 +0200
committerRoy Ben-Shabat <Roy@Twine-s.com>2018-01-16 12:17:10 +0200
commit0fda2ba3ff49bdc1ffc6833f658e2164af187008 (patch)
tree6f3a24d0671ebda50debb8511ab40e0bda0a0df0 /Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap
parent28103646681686bf1b58275d5dbccb92d2b26f9f (diff)
downloadTango-0fda2ba3ff49bdc1ffc6833f658e2164af187008.tar.gz
Tango-0fda2ba3ff49bdc1ffc6833f658e2164af187008.zip
Embedded RealTimeGraphEx library to solution.
Added graphs to technician view. Implemented simple sensors data test using Machine Emulator.
Diffstat (limited to 'Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap')
-rw-r--r--Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/BitmapContext.cs468
-rw-r--r--Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/BitmapFactory.cs214
-rw-r--r--Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/NativeMethods.cs41
-rw-r--r--Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapAntialiasingExtensions.cs444
-rw-r--r--Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapBaseExtensions.cs480
-rw-r--r--Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapBlitExtensions.cs704
-rw-r--r--Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapContextExtensions.cs55
-rw-r--r--Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapConvertExtensions.cs344
-rw-r--r--Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapFillExtensions.cs1205
-rw-r--r--Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapFilterExtensions.cs423
-rw-r--r--Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapLineExtensions.cs1316
-rw-r--r--Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapShapeExtensions.cs489
-rw-r--r--Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapSplineExtensions.cs341
-rw-r--r--Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapTransformationExtensions.cs623
14 files changed, 7147 insertions, 0 deletions
diff --git a/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/BitmapContext.cs b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/BitmapContext.cs
new file mode 100644
index 000000000..6fb53ca04
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/BitmapContext.cs
@@ -0,0 +1,468 @@
+#region Header
+//
+// Project: WriteableBitmapEx - WriteableBitmap extensions
+// Description: Collection of extension methods for the WriteableBitmap class.
+//
+// Changed by: $Author: unknown $
+// Changed on: $Date: 2015-04-17 19:54:47 +0200 (Fr, 17 Apr 2015) $
+// Changed in: $Revision: 113740 $
+// Project: $URL: https://writeablebitmapex.svn.codeplex.com/svn/trunk/Source/WriteableBitmapEx/BitmapContext.cs $
+// Id: $Id: BitmapContext.cs 113740 2015-04-17 17:54:47Z unknown $
+//
+//
+// Copyright © 2009-2015 Rene Schulte and WriteableBitmapEx Contributors
+//
+// This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;)
+//
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+#if NETFX_CORE
+using System.Runtime.InteropServices.WindowsRuntime;
+using System.Collections.Concurrent;
+
+namespace Windows.UI.Xaml.Media.Imaging
+#else
+namespace System.Windows.Media.Imaging
+#endif
+{
+ /// <summary>
+ /// Read Write Mode for the BitmapContext.
+ /// </summary>
+ internal enum ReadWriteMode
+ {
+ /// <summary>
+ /// On Dispose of a BitmapContext, do not Invalidate
+ /// </summary>
+ ReadOnly,
+
+ /// <summary>
+ /// On Dispose of a BitmapContext, invalidate the bitmap
+ /// </summary>
+ ReadWrite
+ }
+
+ /// <summary>
+ /// A disposable cross-platform wrapper around a WriteableBitmap, allowing a common API for Silverlight + WPF with locking + unlocking if necessary
+ /// </summary>
+ /// <remarks>Attempting to put as many preprocessor hacks in this file, to keep the rest of the codebase relatively clean</remarks>
+ internal
+#if WPF
+ unsafe
+#endif
+ struct BitmapContext : IDisposable
+ {
+ private readonly WriteableBitmap _writeableBitmap;
+ private readonly ReadWriteMode _mode;
+
+ private readonly int _pixelWidth;
+ private readonly int _pixelHeight;
+
+#if WPF
+ private readonly static IDictionary<WriteableBitmap, int> UpdateCountByBmp = new System.Collections.Concurrent.ConcurrentDictionary<WriteableBitmap, int>();
+
+ private readonly int _length;
+ private readonly int* _backBuffer;
+#elif NETFX_CORE
+ private readonly static IDictionary<WriteableBitmap, int> UpdateCountByBmp = new ConcurrentDictionary<WriteableBitmap, int>();
+ private readonly static IDictionary<WriteableBitmap, int[]> PixelCacheByBmp = new ConcurrentDictionary<WriteableBitmap, int[]>();
+ private int length;
+ private int[] pixels;
+#endif
+
+ /// <summary>
+ /// The Bitmap
+ /// </summary>
+ internal WriteableBitmap WriteableBitmap { get { return _writeableBitmap; } }
+
+ /// <summary>
+ /// Width of the bitmap
+ /// </summary>
+ internal int Width { get { return _writeableBitmap.PixelWidth; } }
+
+ /// <summary>
+ /// Height of the bitmap
+ /// </summary>
+ internal int Height { get { return _writeableBitmap.PixelHeight; } }
+
+ /// <summary>
+ /// Creates an instance of a BitmapContext, with default mode = ReadWrite
+ /// </summary>
+ /// <param name="writeableBitmap"></param>
+ internal BitmapContext(WriteableBitmap writeableBitmap)
+ : this(writeableBitmap, ReadWriteMode.ReadWrite)
+ {
+ }
+
+ /// <summary>
+ /// Creates an instance of a BitmapContext, with specified ReadWriteMode
+ /// </summary>
+ /// <param name="writeableBitmap"></param>
+ /// <param name="mode"></param>
+ internal BitmapContext(WriteableBitmap writeableBitmap, ReadWriteMode mode)
+ {
+ _writeableBitmap = writeableBitmap;
+ _mode = mode;
+
+ _pixelWidth = _writeableBitmap.PixelWidth;
+ _pixelHeight = _writeableBitmap.PixelHeight;
+#if WPF
+ //// Check if it's the Pbgra32 pixel format
+ //if (writeableBitmap.Format != PixelFormats.Pbgra32)
+ //{
+ // throw new ArgumentException("The input WriteableBitmap needs to have the Pbgra32 pixel format. Use the BitmapFactory.ConvertToPbgra32Format method to automatically convert any input BitmapSource to the right format accepted by this class.", "writeableBitmap");
+ //}
+
+ double width = _writeableBitmap.BackBufferStride / WriteableBitmapExtensions.SizeOfArgb;
+ _length = (int)(width * _pixelHeight);
+
+ lock (UpdateCountByBmp)
+ {
+ // Ensure the bitmap is in the dictionary of mapped Instances
+ if (!UpdateCountByBmp.ContainsKey(writeableBitmap))
+ {
+ // Set UpdateCount to 1 for this bitmap
+ UpdateCountByBmp.Add(writeableBitmap, 1);
+
+ // Lock the bitmap
+ writeableBitmap.Lock();
+ }
+ else
+ {
+ // For previously contextualized bitmaps increment the update count
+ IncrementRefCount(writeableBitmap);
+ }
+ }
+
+ _backBuffer = (int*)writeableBitmap.BackBuffer;
+#elif NETFX_CORE
+ // Ensure the bitmap is in the dictionary of mapped Instances
+ if (!UpdateCountByBmp.ContainsKey(_writeableBitmap))
+ {
+ // Set UpdateCount to 1 for this bitmap
+ UpdateCountByBmp.Add(_writeableBitmap, 1);
+ length = _writeableBitmap.PixelWidth * _writeableBitmap.PixelHeight;
+ pixels = new int[length];
+ CopyPixels();
+ PixelCacheByBmp.Add(_writeableBitmap, pixels);
+ }
+ else
+ {
+ // For previously contextualized bitmaps increment the update count
+ IncrementRefCount(_writeableBitmap);
+ pixels = PixelCacheByBmp[_writeableBitmap];
+ length = pixels.Length;
+ }
+#endif
+ }
+
+#if NETFX_CORE
+ private unsafe void CopyPixels()
+ {
+ var data = _writeableBitmap.PixelBuffer.ToArray();
+ fixed (byte* srcPtr = data)
+ {
+ fixed (int* dstPtr = pixels)
+ {
+ for (var i = 0; i < length; i++)
+ {
+ dstPtr[i] = (srcPtr[i * 4 + 3] << 24)
+ | (srcPtr[i * 4 + 2] << 16)
+ | (srcPtr[i * 4 + 1] << 8)
+ | srcPtr[i * 4 + 0];
+ }
+ }
+ }
+ }
+#endif
+
+#if SILVERLIGHT
+
+ /// <summary>
+ /// Gets the Pixels array
+ /// </summary>
+ internal int[] Pixels { get { return _writeableBitmap.Pixels; } }
+
+ /// <summary>
+ /// Gets the length of the Pixels array
+ /// </summary>
+ internal int Length { get { return _writeableBitmap.Pixels.Length; } }
+
+ /// <summary>
+ /// Performs a Copy operation from source BitmapContext to destination BitmapContext
+ /// </summary>
+ /// <remarks>Equivalent to calling Buffer.BlockCopy in Silverlight, or native memcpy in WPF</remarks>
+ internal static void BlockCopy(BitmapContext src, int srcOffset, BitmapContext dest, int destOffset, int count)
+ {
+ Buffer.BlockCopy(src.Pixels, srcOffset, dest.Pixels, destOffset, count);
+ }
+
+ /// <summary>
+ /// Performs a Copy operation from source Array to destination BitmapContext
+ /// </summary>
+ /// <remarks>Equivalent to calling Buffer.BlockCopy in Silverlight, or native memcpy in WPF</remarks>
+ internal static void BlockCopy(Array src, int srcOffset, BitmapContext dest, int destOffset, int count)
+ {
+ Buffer.BlockCopy(src, srcOffset, dest.Pixels, destOffset, count);
+ }
+
+ /// <summary>
+ /// Performs a Copy operation from source BitmapContext to destination Array
+ /// </summary>
+ /// <remarks>Equivalent to calling Buffer.BlockCopy in Silverlight, or native memcpy in WPF</remarks>
+ internal static void BlockCopy(BitmapContext src, int srcOffset, Array dest, int destOffset, int count)
+ {
+ Buffer.BlockCopy(src.Pixels, srcOffset, dest, destOffset, count);
+ }
+
+ /// <summary>
+ /// Clears the BitmapContext, filling the underlying bitmap with zeros
+ /// </summary>
+ internal void Clear()
+ {
+ var pixels = _writeableBitmap.Pixels;
+ Array.Clear(pixels, 0, pixels.Length);
+ }
+
+ /// <summary>
+ /// Disposes this instance if the underlying platform needs that.
+ /// </summary>
+ internal void Dispose()
+ {
+ // For silverlight, do nothing except redraw
+ _writeableBitmap.Invalidate();
+ }
+
+#elif NETFX_CORE
+
+ /// <summary>
+ /// Gets the Pixels array
+ /// </summary>
+ internal int[] Pixels { get { return pixels; } }
+
+ /// <summary>
+ /// Gets the length of the Pixels array
+ /// </summary>
+ internal int Length { get { return length; } }
+
+ /// <summary>
+ /// Performs a Copy operation from source BitmapContext to destination BitmapContext
+ /// </summary>
+ /// <remarks>Equivalent to calling Buffer.BlockCopy in Silverlight, or native memcpy in WPF</remarks>
+ internal static void BlockCopy(BitmapContext src, int srcOffset, BitmapContext dest, int destOffset, int count)
+ {
+ Buffer.BlockCopy(src.Pixels, srcOffset, dest.Pixels, destOffset, count);
+ }
+
+ /// <summary>
+ /// Performs a Copy operation from source Array to destination BitmapContext
+ /// </summary>
+ /// <remarks>Equivalent to calling Buffer.BlockCopy in Silverlight, or native memcpy in WPF</remarks>
+ internal static void BlockCopy(Array src, int srcOffset, BitmapContext dest, int destOffset, int count)
+ {
+ Buffer.BlockCopy(src, srcOffset, dest.Pixels, destOffset, count);
+ }
+
+ /// <summary>
+ /// Performs a Copy operation from source BitmapContext to destination Array
+ /// </summary>
+ /// <remarks>Equivalent to calling Buffer.BlockCopy in Silverlight, or native memcpy in WPF</remarks>
+ internal static void BlockCopy(BitmapContext src, int srcOffset, Array dest, int destOffset, int count)
+ {
+ Buffer.BlockCopy(src.Pixels, srcOffset, dest, destOffset, count);
+ }
+
+ /// <summary>
+ /// Clears the BitmapContext, filling the underlying bitmap with zeros
+ /// </summary>
+ internal void Clear()
+ {
+ var pixels = Pixels;
+ Array.Clear(pixels, 0, pixels.Length);
+ }
+
+ /// <summary>
+ /// Disposes this instance if the underlying platform needs that.
+ /// </summary>
+ internal unsafe void Dispose()
+ {
+ // Decrement the update count. If it hits zero
+ if (DecrementRefCount(_writeableBitmap) == 0)
+ {
+ // Remove this bitmap from the update map
+ UpdateCountByBmp.Remove(_writeableBitmap);
+ PixelCacheByBmp.Remove(_writeableBitmap);
+
+ // Copy data back
+ if (_mode == ReadWriteMode.ReadWrite)
+ {
+ using (var stream = _writeableBitmap.PixelBuffer.AsStream())
+ {
+ var buffer = new byte[length * 4];
+ fixed (int* srcPtr = pixels)
+ {
+ var b = 0;
+ for (var i = 0; i < length; i++, b += 4)
+ {
+ var p = srcPtr[i];
+ buffer[b + 3] = (byte)((p >> 24) & 0xff);
+ buffer[b + 2] = (byte)((p >> 16) & 0xff);
+ buffer[b + 1] = (byte)((p >> 8) & 0xff);
+ buffer[b + 0] = (byte)(p & 0xff);
+ }
+ stream.Write(buffer, 0, length * 4);
+ }
+ }
+ _writeableBitmap.Invalidate();
+ }
+ }
+ }
+
+#elif WPF
+ /// <summary>
+ /// The pixels as ARGB integer values, where each channel is 8 bit.
+ /// </summary>
+ internal unsafe int* Pixels
+ {
+ [System.Runtime.TargetedPatchingOptOut("Candidate for inlining across NGen boundaries for performance reasons")]
+ get { return _backBuffer; }
+ }
+
+ /// <summary>
+ /// The pixel format
+ /// </summary>
+ internal PixelFormat Format
+ {
+ [System.Runtime.TargetedPatchingOptOut("Candidate for inlining across NGen boundaries for performance reasons")]
+ get { return _writeableBitmap.Format; }
+ }
+
+
+ /// <summary>
+ /// The number of pixels.
+ /// </summary>
+ internal int Length
+ {
+ [System.Runtime.TargetedPatchingOptOut("Candidate for inlining across NGen boundaries for performance reasons")]
+ get
+ {
+ return _length;
+ }
+ }
+
+ /// <summary>
+ /// Performs a Copy operation from source to destination BitmapContext
+ /// </summary>
+ /// <remarks>Equivalent to calling Buffer.BlockCopy in Silverlight, or native memcpy in WPF</remarks>
+ [System.Runtime.TargetedPatchingOptOut("Candidate for inlining across NGen boundaries for performance reasons")]
+ internal static unsafe void BlockCopy(BitmapContext src, int srcOffset, BitmapContext dest, int destOffset, int count)
+ {
+ NativeMethods.CopyUnmanagedMemory((byte*)src.Pixels, srcOffset, (byte*)dest.Pixels, destOffset, count);
+ }
+
+ /// <summary>
+ /// Performs a Copy operation from source Array to destination BitmapContext
+ /// </summary>
+ /// <remarks>Equivalent to calling Buffer.BlockCopy in Silverlight, or native memcpy in WPF</remarks>
+ [System.Runtime.TargetedPatchingOptOut("Candidate for inlining across NGen boundaries for performance reasons")]
+ internal static unsafe void BlockCopy(int[] src, int srcOffset, BitmapContext dest, int destOffset, int count)
+ {
+ fixed (int* srcPtr = src)
+ {
+ NativeMethods.CopyUnmanagedMemory((byte*)srcPtr, srcOffset, (byte*)dest.Pixels, destOffset, count);
+ }
+ }
+
+ /// <summary>
+ /// Performs a Copy operation from source Array to destination BitmapContext
+ /// </summary>
+ /// <remarks>Equivalent to calling Buffer.BlockCopy in Silverlight, or native memcpy in WPF</remarks>
+ [System.Runtime.TargetedPatchingOptOut("Candidate for inlining across NGen boundaries for performance reasons")]
+ internal static unsafe void BlockCopy(byte[] src, int srcOffset, BitmapContext dest, int destOffset, int count)
+ {
+ fixed (byte* srcPtr = src)
+ {
+ NativeMethods.CopyUnmanagedMemory(srcPtr, srcOffset, (byte*)dest.Pixels, destOffset, count);
+ }
+ }
+
+ /// <summary>
+ /// Performs a Copy operation from source BitmapContext to destination Array
+ /// </summary>
+ /// <remarks>Equivalent to calling Buffer.BlockCopy in Silverlight, or native memcpy in WPF</remarks>
+ [System.Runtime.TargetedPatchingOptOut("Candidate for inlining across NGen boundaries for performance reasons")]
+ internal static unsafe void BlockCopy(BitmapContext src, int srcOffset, byte[] dest, int destOffset, int count)
+ {
+ fixed (byte* destPtr = dest)
+ {
+ NativeMethods.CopyUnmanagedMemory((byte*)src.Pixels, srcOffset, destPtr, destOffset, count);
+ }
+ }
+
+ /// <summary>
+ /// Performs a Copy operation from source BitmapContext to destination Array
+ /// </summary>
+ /// <remarks>Equivalent to calling Buffer.BlockCopy in Silverlight, or native memcpy in WPF</remarks>
+ [System.Runtime.TargetedPatchingOptOut("Candidate for inlining across NGen boundaries for performance reasons")]
+ internal static unsafe void BlockCopy(BitmapContext src, int srcOffset, int[] dest, int destOffset, int count)
+ {
+ fixed (int* destPtr = dest)
+ {
+ NativeMethods.CopyUnmanagedMemory((byte*)src.Pixels, srcOffset, (byte*)destPtr, destOffset, count);
+ }
+ }
+
+ /// <summary>
+ /// Clears the BitmapContext, filling the underlying bitmap with zeros
+ /// </summary>
+ [System.Runtime.TargetedPatchingOptOut("Candidate for inlining across NGen boundaries for performance reasons")]
+ internal void Clear()
+ {
+ NativeMethods.SetUnmanagedMemory(_writeableBitmap.BackBuffer, 0, _writeableBitmap.BackBufferStride * _writeableBitmap.PixelHeight);
+ }
+
+ /// <summary>
+ /// Disposes the BitmapContext, unlocking it and invalidating if WPF
+ /// </summary>
+ public void Dispose()
+ {
+ // Decrement the update count. If it hits zero
+ if (DecrementRefCount(_writeableBitmap) == 0)
+ {
+ // Remove this bitmap from the update map
+ UpdateCountByBmp.Remove(_writeableBitmap);
+
+ // Invalidate the bitmap if ReadWrite _mode
+ if (_mode == ReadWriteMode.ReadWrite)
+ {
+ _writeableBitmap.AddDirtyRect(new Int32Rect(0, 0, _writeableBitmap.PixelWidth, _writeableBitmap.PixelHeight));
+ }
+
+ // Unlock the bitmap
+ _writeableBitmap.Unlock();
+ }
+ }
+#endif
+
+#if WPF || NETFX_CORE
+ private static void IncrementRefCount(WriteableBitmap target)
+ {
+ UpdateCountByBmp[target]++;
+ }
+
+ private static int DecrementRefCount(WriteableBitmap target)
+ {
+ int current;
+ if (!UpdateCountByBmp.TryGetValue(target, out current))
+ {
+ return -1;
+ }
+ current--;
+ UpdateCountByBmp[target] = current;
+ return current;
+ }
+#endif
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/BitmapFactory.cs b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/BitmapFactory.cs
new file mode 100644
index 000000000..00d044cb4
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/BitmapFactory.cs
@@ -0,0 +1,214 @@
+#region Header
+//
+// Project: WriteableBitmapEx - WriteableBitmap extensions
+// Description: Collection of extension methods for the WriteableBitmap class.
+//
+// Changed by: $Author: unknown $
+// Changed on: $Date: 2015-03-05 18:18:24 +0100 (Do, 05 Mrz 2015) $
+// Changed in: $Revision: 113191 $
+// Project: $URL: https://writeablebitmapex.svn.codeplex.com/svn/trunk/Source/WriteableBitmapEx/BitmapFactory.cs $
+// Id: $Id: BitmapFactory.cs 113191 2015-03-05 17:18:24Z unknown $
+//
+//
+// Copyright © 2009-2015 Rene Schulte and WriteableBitmapEx Contributors
+//
+// This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;)
+//
+#endregion
+
+using System;
+using System.IO;
+using System.Reflection;
+
+#if NETFX_CORE
+using Windows.Storage;
+using Windows.Storage.Streams;
+using System.Threading.Tasks;
+using Windows.Graphics.Imaging;
+using System.Runtime.InteropServices.WindowsRuntime;
+
+namespace Windows.UI.Xaml.Media.Imaging
+#else
+namespace System.Windows.Media.Imaging
+#endif
+{
+ /// <summary>
+ /// Cross-platform factory for WriteableBitmaps
+ /// </summary>
+ internal static class BitmapFactory
+ {
+ /// <summary>
+ /// Creates a new WriteableBitmap of the specified width and height
+ /// </summary>
+ /// <remarks>For WPF the default DPI is 96x96 and PixelFormat is Pbgra32</remarks>
+ /// <param name="pixelWidth"></param>
+ /// <param name="pixelHeight"></param>
+ /// <returns></returns>
+ internal static WriteableBitmap New(int pixelWidth, int pixelHeight)
+ {
+ if (pixelHeight < 1) pixelHeight = 1;
+ if (pixelWidth < 1) pixelWidth = 1;
+
+#if SILVERLIGHT
+ return new WriteableBitmap(pixelWidth, pixelHeight);
+#elif WPF
+ return new WriteableBitmap(pixelWidth, pixelHeight, 96.0, 96.0, PixelFormats.Pbgra32, null);
+#elif NETFX_CORE
+ return new WriteableBitmap(pixelWidth, pixelHeight);
+#endif
+ }
+
+#if WPF
+ /// <summary>
+ /// Converts the input BitmapSource to the Pbgra32 format WriteableBitmap which is internally used by the WriteableBitmapEx.
+ /// </summary>
+ /// <param name="source">The source bitmap.</param>
+ /// <returns></returns>
+ internal static WriteableBitmap ConvertToPbgra32Format(BitmapSource source)
+ {
+ // Convert to Pbgra32 if it's a different format
+ if (source.Format == PixelFormats.Pbgra32)
+ {
+ return new WriteableBitmap(source);
+ }
+
+ var formatedBitmapSource = new FormatConvertedBitmap();
+ formatedBitmapSource.BeginInit();
+ formatedBitmapSource.Source = source;
+ formatedBitmapSource.DestinationFormat = PixelFormats.Pbgra32;
+ formatedBitmapSource.EndInit();
+ return new WriteableBitmap(formatedBitmapSource);
+ }
+#endif
+
+#if NETFX_CORE
+ /// <summary>
+ /// Loads an image from the applications content and returns a new WriteableBitmap.
+ /// </summary>
+ /// <param name="uri">The URI to the content file.</param>
+ /// <param name="pixelFormat">The pixel format of the stream data. If Unknown is provided as param, the default format of the BitmapDecoder is used.</param>
+ /// <returns>A new WriteableBitmap containing the pixel data.</returns>
+ internal static async Task<WriteableBitmap> FromContent(Uri uri, BitmapPixelFormat pixelFormat = BitmapPixelFormat.Unknown)
+ {
+ // Decode pixel data
+ var file = await StorageFile.GetFileFromApplicationUriAsync(uri);
+ using (var stream = await file.OpenAsync(FileAccessMode.Read))
+ {
+ return await FromStream(stream);
+ }
+ }
+
+ /// <summary>
+ /// Loads the data from an image stream and returns a new WriteableBitmap.
+ /// </summary>
+ /// <param name="stream">The stream with the image data.</param>
+ /// <param name="pixelFormat">The pixel format of the stream data. If Unknown is provided as param, the default format of the BitmapDecoder is used.</param>
+ /// <returns>A new WriteableBitmap containing the pixel data.</returns>
+ internal static async Task<WriteableBitmap> FromStream(Stream stream, BitmapPixelFormat pixelFormat = BitmapPixelFormat.Unknown)
+ {
+ using (var dstStream = new InMemoryRandomAccessStream())
+ {
+ await RandomAccessStream.CopyAsync(stream.AsInputStream(), dstStream);
+ return await FromStream(dstStream);
+ }
+ }
+
+ /// <summary>
+ /// Loads the data from an image stream and returns a new WriteableBitmap.
+ /// </summary>
+ /// <param name="stream">The stream with the image data.</param>
+ /// <param name="pixelFormat">The pixel format of the stream data. If Unknown is provided as param, the default format of the BitmapDecoder is used.</param>
+ /// <returns>A new WriteableBitmap containing the pixel data.</returns>
+ internal static async Task<WriteableBitmap> FromStream(IRandomAccessStream stream, BitmapPixelFormat pixelFormat = BitmapPixelFormat.Unknown)
+ {
+ var decoder = await BitmapDecoder.CreateAsync(stream);
+ var transform = new BitmapTransform();
+ if (pixelFormat == BitmapPixelFormat.Unknown)
+ {
+ pixelFormat = decoder.BitmapPixelFormat;
+ }
+ var pixelData = await decoder.GetPixelDataAsync(pixelFormat, decoder.BitmapAlphaMode, transform, ExifOrientationMode.RespectExifOrientation, ColorManagementMode.ColorManageToSRgb);
+ var pixels = pixelData.DetachPixelData();
+
+ // Copy to WriteableBitmap
+ var bmp = new WriteableBitmap((int)decoder.OrientedPixelWidth, (int)decoder.OrientedPixelHeight);
+ using (var bmpStream = bmp.PixelBuffer.AsStream())
+ {
+ bmpStream.Seek(0, SeekOrigin.Begin);
+ bmpStream.Write(pixels, 0, (int)bmpStream.Length);
+ return bmp;
+ }
+ }
+
+ /// <summary>
+ /// Loads the data from a pixel buffer like the RenderTargetBitmap provides and returns a new WriteableBitmap.
+ /// </summary>
+ /// <param name="pixelBuffer">The source pixel buffer with the image data.</param>
+ /// <param name="width">The width of the image data.</param>
+ /// <param name="height">The height of the image data.</param>
+ /// <returns>A new WriteableBitmap containing the pixel data.</returns>
+ internal static async Task<WriteableBitmap> FromPixelBuffer(IBuffer pixelBuffer, int width, int height)
+ {
+ // Copy to WriteableBitmap
+ var bmp = new WriteableBitmap(width, height);
+ using (var srcStream = pixelBuffer.AsStream())
+ {
+ using (var destStream = bmp.PixelBuffer.AsStream())
+ {
+ srcStream.Seek(0, SeekOrigin.Begin);
+ await srcStream.CopyToAsync(destStream);
+ }
+ return bmp;
+ }
+ }
+#else
+ /// <summary>
+ /// Loads an image from the applications resource file and returns a new WriteableBitmap.
+ /// </summary>
+ /// <param name="relativePath">Only the relative path to the resource file. The assembly name is retrieved automatically.</param>
+ /// <returns>A new WriteableBitmap containing the pixel data.</returns>
+ internal static WriteableBitmap FromResource(string relativePath)
+ {
+ var fullName = Assembly.GetCallingAssembly().FullName;
+ var asmName = new AssemblyName(fullName).Name;
+ return FromContent(asmName + ";component/" + relativePath);
+ }
+
+ /// <summary>
+ /// Loads an image from the applications content and returns a new WriteableBitmap.
+ /// </summary>
+ /// <param name="relativePath">Only the relative path to the content file.</param>
+ /// <returns>A new WriteableBitmap containing the pixel data.</returns>
+ internal static WriteableBitmap FromContent(string relativePath)
+ {
+ using (var bmpStream = Application.GetResourceStream(new Uri(relativePath, UriKind.Relative)).Stream)
+ {
+ return FromStream(bmpStream);
+ }
+ }
+
+ /// <summary>
+ /// Loads the data from an image stream and returns a new WriteableBitmap.
+ /// </summary>
+ /// <param name="stream">The stream with the image data.</param>
+ /// <returns>A new WriteableBitmap containing the pixel data.</returns>
+ internal static WriteableBitmap FromStream(Stream stream)
+ {
+ var bmpi = new BitmapImage();
+#if SILVERLIGHT
+ bmpi.SetSource(stream);
+ bmpi.CreateOptions = BitmapCreateOptions.None;
+#elif WPF
+ bmpi.BeginInit();
+ bmpi.CreateOptions = BitmapCreateOptions.None;
+ bmpi.StreamSource = stream;
+ bmpi.EndInit();
+#endif
+ var bmp = new WriteableBitmap(bmpi);
+ bmpi.UriSource = null;
+ return bmp;
+ }
+#endif
+
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/NativeMethods.cs b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/NativeMethods.cs
new file mode 100644
index 000000000..59b2b7bcc
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/NativeMethods.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Runtime;
+using System.Runtime.InteropServices;
+
+namespace System.Windows.Media.Imaging
+{
+ internal static class NativeMethods
+ {
+ [TargetedPatchingOptOut("Internal method only, inlined across NGen boundaries for performance reasons")]
+ internal static unsafe void CopyUnmanagedMemory(byte* srcPtr, int srcOffset, byte* dstPtr, int dstOffset, int count)
+ {
+ srcPtr += srcOffset;
+ dstPtr += dstOffset;
+
+ memcpy(dstPtr, srcPtr, count);
+ }
+
+ [TargetedPatchingOptOut("Internal method only, inlined across NGen boundaries for performance reasons")]
+ internal static void SetUnmanagedMemory(IntPtr dst, int filler, int count)
+ {
+ memset(dst, filler, count);
+ }
+
+ // Win32 memory copy function
+ //[DllImport("ntdll.dll")]
+ [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
+ private static extern unsafe byte* memcpy(
+ byte* dst,
+ byte* src,
+ int count);
+
+ // Win32 memory set function
+ //[DllImport("ntdll.dll")]
+ //[DllImport("coredll.dll", EntryPoint = "memset", SetLastError = false)]
+ [DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
+ private static extern void memset(
+ IntPtr dst,
+ int filler,
+ int count);
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapAntialiasingExtensions.cs b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapAntialiasingExtensions.cs
new file mode 100644
index 000000000..667767161
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapAntialiasingExtensions.cs
@@ -0,0 +1,444 @@
+#region Header
+//
+// Project: WriteableBitmapEx - WriteableBitmap extensions
+// Description: Collection of internal anti-aliasing helper methods for the WriteableBitmap class.
+//
+// Changed by: $Author: unknown $
+// Changed on: $Date: 2015-02-24 20:36:41 +0100 (Di, 24 Feb 2015) $
+// Changed in: $Revision: 112951 $
+// Project: $URL: https://writeablebitmapex.svn.codeplex.com/svn/trunk/Source/WriteableBitmapEx/WriteableBitmapTransformationExtensions.cs $
+// Id: $Id: WriteableBitmapTransformationExtensions.cs 112951 2015-02-24 19:36:41Z unknown $
+//
+//
+// Copyright © 2009-2015 Rene Schulte and WriteableBitmapEx Contributors
+//
+// This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;)
+//
+#endregion
+using System;
+
+#if NETFX_CORE
+using Windows.Foundation;
+using Windows.UI.Xaml.Media.Imaging;
+using Windows.UI;
+
+namespace Windows.UI.Xaml.Media.Imaging
+#else
+namespace System.Windows.Media.Imaging
+#endif
+{
+ /// <summary>
+ /// Collection of draw extension methods for the Silverlight WriteableBitmap class.
+ /// </summary>
+ internal
+#if !SILVERLIGHT
+ unsafe
+#endif
+ static partial class WriteableBitmapExtensions
+ {
+ private static readonly int[] leftEdgeX = new int[8192];
+ private static readonly int[] rightEdgeX = new int[8192];
+
+ private static void AAWidthLine(int width, int height, BitmapContext context, float x1, float y1, float x2, float y2, float lineWidth, Int32 color)
+ {
+ // Perform cohen-sutherland clipping if either point is out of the viewport
+ if (!CohenSutherlandLineClip(new Rect(0, 0, width, height), ref x1, ref y1, ref x2, ref y2)) return;
+
+ if (lineWidth <= 0) return;
+
+ var buffer = context.Pixels;
+
+ if (y1 > y2)
+ {
+ Swap(ref x1, ref x2);
+ Swap(ref y1, ref y2);
+ }
+
+ if (x1 == x2)
+ {
+ x1 -= (int)lineWidth / 2;
+ x2 += (int)lineWidth / 2;
+
+ if (x1 < 0)
+ x1 = 0;
+ if (x2 < 0)
+ return;
+
+ if (x1 >= width)
+ return;
+ if (x2 >= width)
+ x2 = width - 1;
+
+ if (y1 >= height || y2 < 0)
+ return;
+
+ if (y1 < 0)
+ y1 = 0;
+ if (y2 >= height)
+ y2 = height - 1;
+
+ for (var x = (int)x1; x <= x2; x++)
+ {
+ for (var y = (int)y1; y <= y2; y++)
+ {
+ var a = (byte)((color & 0xff000000) >> 24);
+ var r = (byte)((color & 0x00ff0000) >> 16);
+ var g = (byte)((color & 0x0000ff00) >> 8);
+ var b = (byte)((color & 0x000000ff) >> 0);
+
+ byte rs, gs, bs;
+ byte rd, gd, bd;
+
+ int d;
+
+ rs = r;
+ gs = g;
+ bs = b;
+
+ d = buffer[y * width + x];
+
+ rd = (byte)((d & 0x00ff0000) >> 16);
+ gd = (byte)((d & 0x0000ff00) >> 8);
+ bd = (byte)((d & 0x000000ff) >> 0);
+
+ rd = (byte)((rs * a + rd * (0xff - a)) >> 8);
+ gd = (byte)((gs * a + gd * (0xff - a)) >> 8);
+ bd = (byte)((bs * a + bd * (0xff - a)) >> 8);
+
+ buffer[y * width + x] = (0xff << 24) | (rd << 16) | (gd << 8) | (bd << 0);
+ }
+ }
+
+ return;
+ }
+ if (y1 == y2)
+ {
+ if (x1 > x2) Swap(ref x1, ref x2);
+
+ y1 -= (int)lineWidth / 2;
+ y2 += (int)lineWidth / 2;
+
+ if (y1 < 0) y1 = 0;
+ if (y2 < 0) return;
+
+ if (y1 >= height) return;
+ if (y2 >= height) x2 = height - 1;
+
+ if (x1 >= width || y2 < 0) return;
+
+ if (x1 < 0) x1 = 0;
+ if (x2 >= width) x2 = width - 1;
+
+ for (var x = (int)x1; x <= x2; x++)
+ {
+ for (var y = (int)y1; y <= y2; y++)
+ {
+ var a = (byte)((color & 0xff000000) >> 24);
+ var r = (byte)((color & 0x00ff0000) >> 16);
+ var g = (byte)((color & 0x0000ff00) >> 8);
+ var b = (byte)((color & 0x000000ff) >> 0);
+
+ Byte rs, gs, bs;
+ Byte rd, gd, bd;
+
+ Int32 d;
+
+ rs = r;
+ gs = g;
+ bs = b;
+
+ d = buffer[y * width + x];
+
+ rd = (byte)((d & 0x00ff0000) >> 16);
+ gd = (byte)((d & 0x0000ff00) >> 8);
+ bd = (byte)((d & 0x000000ff) >> 0);
+
+ rd = (byte)((rs * a + rd * (0xff - a)) >> 8);
+ gd = (byte)((gs * a + gd * (0xff - a)) >> 8);
+ bd = (byte)((bs * a + bd * (0xff - a)) >> 8);
+
+ buffer[y * width + x] = (0xff << 24) | (rd << 16) | (gd << 8) | (bd << 0);
+ }
+ }
+
+ return;
+ }
+
+ y1 += 1;
+ y2 += 1;
+
+ float slope = (y2 - y1) / (x2 - x1);
+ float islope = (x2 - x1) / (y2 - y1);
+
+ float m = slope;
+ float w = lineWidth;
+
+ float dx = x2 - x1;
+ float dy = y2 - y1;
+
+ var xtot = (float)(w * dy / Math.Sqrt(dx * dx + dy * dy));
+ var ytot = (float)(w * dx / Math.Sqrt(dx * dx + dy * dy));
+
+ float sm = dx * dy / (dx * dx + dy * dy);
+
+ // Center it.
+
+ x1 += xtot / 2;
+ y1 -= ytot / 2;
+ x2 += xtot / 2;
+ y2 -= ytot / 2;
+
+ //
+ //
+
+ float sx = -xtot;
+ float sy = +ytot;
+
+ var ix1 = (int)x1;
+ var iy1 = (int)y1;
+
+ var ix2 = (int)x2;
+ var iy2 = (int)y2;
+
+ var ix3 = (int)(x1 + sx);
+ var iy3 = (int)(y1 + sy);
+
+ var ix4 = (int)(x2 + sx);
+ var iy4 = (int)(y2 + sy);
+
+ if (lineWidth == 2)
+ {
+ if (Math.Abs(dy) < Math.Abs(dx))
+ {
+ if (x1 < x2)
+ {
+ iy3 = iy1 + 2;
+ iy4 = iy2 + 2;
+ }
+ else
+ {
+ iy1 = iy3 + 2;
+ iy2 = iy4 + 2;
+ }
+ }
+ else
+ {
+ ix1 = ix3 + 2;
+ ix2 = ix4 + 2;
+ }
+ }
+
+ int starty = Math.Min(Math.Min(iy1, iy2), Math.Min(iy3, iy4));
+ int endy = Math.Max(Math.Max(iy1, iy2), Math.Max(iy3, iy4));
+
+ if (starty < 0) starty = -1;
+ if (endy >= height) endy = height + 1;
+
+ for (int y = starty + 1; y < endy - 1; y++)
+ {
+ leftEdgeX[y] = -1 << 16;
+ rightEdgeX[y] = 1 << 16 - 1;
+ }
+
+
+ AALineQ1(width, height, context, ix1, iy1, ix2, iy2, color, sy > 0, false);
+ AALineQ1(width, height, context, ix3, iy3, ix4, iy4, color, sy < 0, true);
+
+ if (lineWidth > 1)
+ {
+ AALineQ1(width, height, context, ix1, iy1, ix3, iy3, color, true, sy > 0);
+ AALineQ1(width, height, context, ix2, iy2, ix4, iy4, color, false, sy < 0);
+ }
+
+ if (x1 < x2)
+ {
+ if (iy2 >= 0 && iy2 < height) rightEdgeX[iy2] = Math.Min(ix2, rightEdgeX[iy2]);
+ if (iy3 >= 0 && iy3 < height) leftEdgeX[iy3] = Math.Max(ix3, leftEdgeX[iy3]);
+ }
+ else
+ {
+ if (iy1 >= 0 && iy1 < height) rightEdgeX[iy1] = Math.Min(ix1, rightEdgeX[iy1]);
+ if (iy4 >= 0 && iy4 < height) leftEdgeX[iy4] = Math.Max(ix4, leftEdgeX[iy4]);
+ }
+
+ //return;
+
+ for (int y = starty + 1; y < endy - 1; y++)
+ {
+ leftEdgeX[y] = Math.Max(leftEdgeX[y], 0);
+ rightEdgeX[y] = Math.Min(rightEdgeX[y], width - 1);
+
+ for (int x = leftEdgeX[y]; x <= rightEdgeX[y]; x++)
+ {
+ var a = (byte)((color & 0xff000000) >> 24);
+ var r = (byte)((color & 0x00ff0000) >> 16);
+ var g = (byte)((color & 0x0000ff00) >> 8);
+ var b = (byte)((color & 0x000000ff) >> 0);
+
+ Byte rs, gs, bs;
+ Byte rd, gd, bd;
+
+ Int32 d;
+
+ rs = r;
+ gs = g;
+ bs = b;
+
+ d = buffer[y * width + x];
+
+ rd = (byte)((d & 0x00ff0000) >> 16);
+ gd = (byte)((d & 0x0000ff00) >> 8);
+ bd = (byte)((d & 0x000000ff) >> 0);
+
+ rd = (byte)((rs * a + rd * (0xff - a)) >> 8);
+ gd = (byte)((gs * a + gd * (0xff - a)) >> 8);
+ bd = (byte)((bs * a + bd * (0xff - a)) >> 8);
+
+ buffer[y * width + x] = (0xff << 24) | (rd << 16) | (gd << 8) | (bd << 0);
+ }
+ }
+ }
+
+ private static void Swap<T>(ref T a, ref T b)
+ {
+ T t = a;
+ a = b;
+ b = t;
+ }
+
+ private static void AALineQ1(int width, int height, BitmapContext context, int x1, int y1, int x2, int y2, Int32 color, bool minEdge, bool leftEdge)
+ {
+ Byte off = 0;
+
+ if (minEdge) off = 0xff;
+
+ if (x1 == x2) return;
+ if (y1 == y2) return;
+
+ var buffer = context.Pixels;
+
+ if (y1 > y2)
+ {
+ Swap(ref x1, ref x2);
+ Swap(ref y1, ref y2);
+ }
+
+ int deltax = (x2 - x1);
+ int deltay = (y2 - y1);
+
+ if (x1 > x2) deltax = (x1 - x2);
+
+ int x = x1;
+ int y = y1;
+
+ UInt16 m = 0;
+
+ if (deltax > deltay) m = (ushort)(((deltay << 16) / deltax));
+ else m = (ushort)(((deltax << 16) / deltay));
+
+ UInt16 e = 0;
+
+ var a = (byte)((color & 0xff000000) >> 24);
+ var r = (byte)((color & 0x00ff0000) >> 16);
+ var g = (byte)((color & 0x0000ff00) >> 8);
+ var b = (byte)((color & 0x000000ff) >> 0);
+
+ Byte rs, gs, bs;
+ Byte rd, gd, bd;
+
+ Int32 d;
+
+ Byte ta = a;
+
+ e = 0;
+
+ if (deltax >= deltay)
+ {
+ while (deltax-- != 0)
+ {
+ if ((UInt16)(e + m) <= e) // Roll
+ {
+ y++;
+ }
+
+ e += m;
+
+ if (x1 < x2) x++;
+ else x--;
+
+ if (y < 0 || y >= height) continue;
+
+ if (leftEdge) leftEdgeX[y] = Math.Max(x + 1, leftEdgeX[y]);
+ else rightEdgeX[y] = Math.Min(x - 1, rightEdgeX[y]);
+
+ if (x < 0 || x >= width) continue;
+
+ //
+
+ ta = (byte)((a * (UInt16)(((((UInt16)(e >> 8))) ^ off))) >> 8);
+
+ rs = r;
+ gs = g;
+ bs = b;
+
+ d = buffer[y * width + x];
+
+ rd = (byte)((d & 0x00ff0000) >> 16);
+ gd = (byte)((d & 0x0000ff00) >> 8);
+ bd = (byte)((d & 0x000000ff) >> 0);
+
+ rd = (byte)((rs * ta + rd * (0xff - ta)) >> 8);
+ gd = (byte)((gs * ta + gd * (0xff - ta)) >> 8);
+ bd = (byte)((bs * ta + bd * (0xff - ta)) >> 8);
+
+ buffer[y * width + x] = (0xff << 24) | (rd << 16) | (gd << 8) | (bd << 0);
+
+ //
+ }
+ }
+ else
+ {
+ off ^= 0xff;
+
+ while (--deltay != 0)
+ {
+ if ((UInt16)(e + m) <= e) // Roll
+ {
+ if (x1 < x2) x++;
+ else x--;
+ }
+
+ e += m;
+
+ y++;
+
+ if (x < 0 || x >= width) continue;
+ if (y < 0 || y >= height) continue;
+
+ //
+
+ ta = (byte)((a * (UInt16)(((((UInt16)(e >> 8))) ^ off))) >> 8);
+
+ rs = r;
+ gs = g;
+ bs = b;
+
+ d = buffer[y * width + x];
+
+ rd = (byte)((d & 0x00ff0000) >> 16);
+ gd = (byte)((d & 0x0000ff00) >> 8);
+ bd = (byte)((d & 0x000000ff) >> 0);
+
+ rd = (byte)((rs * ta + rd * (0xff - ta)) >> 8);
+ gd = (byte)((gs * ta + gd * (0xff - ta)) >> 8);
+ bd = (byte)((bs * ta + bd * (0xff - ta)) >> 8);
+
+ buffer[y * width + x] = (0xff << 24) | (rd << 16) | (gd << 8) | (bd << 0);
+
+ if (leftEdge) leftEdgeX[y] = x + 1;
+ else rightEdgeX[y] = x - 1;
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapBaseExtensions.cs b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapBaseExtensions.cs
new file mode 100644
index 000000000..a4fe799f2
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapBaseExtensions.cs
@@ -0,0 +1,480 @@
+#region Header
+//
+// Project: WriteableBitmapEx - WriteableBitmap extensions
+// Description: Collection of extension methods for the WriteableBitmap class.
+//
+// Changed by: $Author: unknown $
+// Changed on: $Date: 2015-03-05 18:18:24 +0100 (Do, 05 Mrz 2015) $
+// Changed in: $Revision: 113191 $
+// Project: $URL: https://writeablebitmapex.svn.codeplex.com/svn/trunk/Source/WriteableBitmapEx/WriteableBitmapBaseExtensions.cs $
+// Id: $Id: WriteableBitmapBaseExtensions.cs 113191 2015-03-05 17:18:24Z unknown $
+//
+//
+// Copyright © 2009-2015 Rene Schulte and WriteableBitmapEx Contributors
+//
+// This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;)
+//
+#endregion
+
+using System;
+
+#if NETFX_CORE
+namespace Windows.UI.Xaml.Media.Imaging
+#else
+namespace System.Windows.Media.Imaging
+#endif
+{
+ /// <summary>
+ /// Collection of extension methods for the WriteableBitmap class.
+ /// </summary>
+ internal
+#if WPF
+ unsafe
+#endif
+ static partial class WriteableBitmapExtensions
+ {
+ #region Fields
+
+ internal const int SizeOfArgb = 4;
+
+ #endregion
+
+ #region Methods
+
+ #region General
+
+ internal static int ConvertColor(double opacity, Color color)
+ {
+ if (opacity < 0.0 || opacity > 1.0)
+ {
+ throw new ArgumentOutOfRangeException("opacity", "Opacity must be between 0.0 and 1.0");
+ }
+
+ color.A = (byte)(color.A * opacity);
+
+ return ConvertColor(color);
+ }
+
+ internal static int ConvertColor(Color color)
+ {
+ var col = 0;
+
+ if (color.A != 0)
+ {
+ var a = color.A + 1;
+ col = (color.A << 24)
+ | ((byte)((color.R * a) >> 8) << 16)
+ | ((byte)((color.G * a) >> 8) << 8)
+ | ((byte)((color.B * a) >> 8));
+ }
+
+ return col;
+ }
+
+ /// <summary>
+ /// Fills the whole WriteableBitmap with a color.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="color">The color used for filling.</param>
+ internal static void Clear(this WriteableBitmap bmp, Color color)
+ {
+ var col = ConvertColor(color);
+ using (var context = bmp.GetBitmapContext())
+ {
+ var pixels = context.Pixels;
+ var w = context.Width;
+ var h = context.Height;
+ var len = w * SizeOfArgb;
+
+ // Fill first line
+ for (var x = 0; x < w; x++)
+ {
+ pixels[x] = col;
+ }
+
+ // Copy first line
+ var blockHeight = 1;
+ var y = 1;
+ while (y < h)
+ {
+ BitmapContext.BlockCopy(context, 0, context, y * len, blockHeight * len);
+ y += blockHeight;
+ blockHeight = Math.Min(2 * blockHeight, h - y);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Fills the whole WriteableBitmap with an empty color (0).
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ internal static void Clear(this WriteableBitmap bmp)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ context.Clear();
+ }
+ }
+
+ /// <summary>
+ /// Clones the specified WriteableBitmap.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <returns>A copy of the WriteableBitmap.</returns>
+ internal static WriteableBitmap Clone(this WriteableBitmap bmp)
+ {
+ using (var srcContext = bmp.GetBitmapContext(ReadWriteMode.ReadOnly))
+ {
+ var result = BitmapFactory.New(srcContext.Width, srcContext.Height);
+ using (var destContext = result.GetBitmapContext())
+ {
+ BitmapContext.BlockCopy(srcContext, 0, destContext, 0, srcContext.Length * SizeOfArgb);
+ }
+ return result;
+ }
+ }
+
+ #endregion
+
+ #region ForEach
+
+ /// <summary>
+ /// Applies the given function to all the pixels of the bitmap in
+ /// order to set their color.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="func">The function to apply. With parameters x, y and a color as a result</param>
+ internal static void ForEach(this WriteableBitmap bmp, Func<int, int, Color> func)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ var pixels = context.Pixels;
+ int w = context.Width;
+ int h = context.Height;
+ int index = 0;
+
+ for (int y = 0; y < h; y++)
+ {
+ for (int x = 0; x < w; x++)
+ {
+ var color = func(x, y);
+ pixels[index++] = ConvertColor(color);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Applies the given function to all the pixels of the bitmap in
+ /// order to set their color.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="func">The function to apply. With parameters x, y, source color and a color as a result</param>
+ internal static void ForEach(this WriteableBitmap bmp, Func<int, int, Color, Color> func)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ var pixels = context.Pixels;
+ var w = context.Width;
+ var h = context.Height;
+ var index = 0;
+
+ for (var y = 0; y < h; y++)
+ {
+ for (var x = 0; x < w; x++)
+ {
+ var c = pixels[index];
+
+ // Premultiplied Alpha!
+ var a = (byte)(c >> 24);
+ // Prevent division by zero
+ int ai = a;
+ if (ai == 0)
+ {
+ ai = 1;
+ }
+ // Scale inverse alpha to use cheap integer mul bit shift
+ ai = ((255 << 8) / ai);
+ var srcColor = Color.FromArgb(a,
+ (byte)((((c >> 16) & 0xFF) * ai) >> 8),
+ (byte)((((c >> 8) & 0xFF) * ai) >> 8),
+ (byte)((((c & 0xFF) * ai) >> 8)));
+
+ var color = func(x, y, srcColor);
+ pixels[index++] = ConvertColor(color);
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region Get Pixel / Brightness
+
+ /// <summary>
+ /// Gets the color of the pixel at the x, y coordinate as integer.
+ /// For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x">The x coordinate of the pixel.</param>
+ /// <param name="y">The y coordinate of the pixel.</param>
+ /// <returns>The color of the pixel at x, y.</returns>
+ internal static int GetPixeli(this WriteableBitmap bmp, int x, int y)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ return context.Pixels[y * context.Width + x];
+ }
+ }
+
+ /// <summary>
+ /// Gets the color of the pixel at the x, y coordinate as a Color struct.
+ /// For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x">The x coordinate of the pixel.</param>
+ /// <param name="y">The y coordinate of the pixel.</param>
+ /// <returns>The color of the pixel at x, y as a Color struct.</returns>
+ internal static Color GetPixel(this WriteableBitmap bmp, int x, int y)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ var c = context.Pixels[y * context.Width + x];
+ var a = (byte)(c >> 24);
+
+ // Prevent division by zero
+ int ai = a;
+ if (ai == 0)
+ {
+ ai = 1;
+ }
+
+ // Scale inverse alpha to use cheap integer mul bit shift
+ ai = ((255 << 8) / ai);
+ return Color.FromArgb(a,
+ (byte)((((c >> 16) & 0xFF) * ai) >> 8),
+ (byte)((((c >> 8) & 0xFF) * ai) >> 8),
+ (byte)((((c & 0xFF) * ai) >> 8)));
+ }
+ }
+
+ /// <summary>
+ /// Gets the brightness / luminance of the pixel at the x, y coordinate as byte.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x">The x coordinate of the pixel.</param>
+ /// <param name="y">The y coordinate of the pixel.</param>
+ /// <returns>The brightness of the pixel at x, y.</returns>
+ internal static byte GetBrightness(this WriteableBitmap bmp, int x, int y)
+ {
+ using (var context = bmp.GetBitmapContext(ReadWriteMode.ReadOnly))
+ {
+ // Extract color components
+ var c = context.Pixels[y * context.Width + x];
+ var r = (byte)(c >> 16);
+ var g = (byte)(c >> 8);
+ var b = (byte)(c);
+
+ // Convert to gray with constant factors 0.2126, 0.7152, 0.0722
+ return (byte)((r * 6966 + g * 23436 + b * 2366) >> 15);
+ }
+ }
+
+ #endregion
+
+ #region SetPixel
+
+ #region Without alpha
+
+ /// <summary>
+ /// Sets the color of the pixel using a precalculated index (faster).
+ /// For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="index">The coordinate index.</param>
+ /// <param name="r">The red value of the color.</param>
+ /// <param name="g">The green value of the color.</param>
+ /// <param name="b">The blue value of the color.</param>
+ internal static void SetPixeli(this WriteableBitmap bmp, int index, byte r, byte g, byte b)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ context.Pixels[index] = (255 << 24) | (r << 16) | (g << 8) | b;
+ }
+ }
+
+ /// <summary>
+ /// Sets the color of the pixel.
+ /// For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x">The x coordinate (row).</param>
+ /// <param name="y">The y coordinate (column).</param>
+ /// <param name="r">The red value of the color.</param>
+ /// <param name="g">The green value of the color.</param>
+ /// <param name="b">The blue value of the color.</param>
+ internal static void SetPixel(this WriteableBitmap bmp, int x, int y, byte r, byte g, byte b)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ context.Pixels[y * context.Width + x] = (255 << 24) | (r << 16) | (g << 8) | b;
+ }
+ }
+
+ #endregion
+
+ #region With alpha
+
+ /// <summary>
+ /// Sets the color of the pixel including the alpha value and using a precalculated index (faster).
+ /// For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="index">The coordinate index.</param>
+ /// <param name="a">The alpha value of the color.</param>
+ /// <param name="r">The red value of the color.</param>
+ /// <param name="g">The green value of the color.</param>
+ /// <param name="b">The blue value of the color.</param>
+ internal static void SetPixeli(this WriteableBitmap bmp, int index, byte a, byte r, byte g, byte b)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ context.Pixels[index] = (a << 24) | (r << 16) | (g << 8) | b;
+ }
+ }
+
+ /// <summary>
+ /// Sets the color of the pixel including the alpha value.
+ /// For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x">The x coordinate (row).</param>
+ /// <param name="y">The y coordinate (column).</param>
+ /// <param name="a">The alpha value of the color.</param>
+ /// <param name="r">The red value of the color.</param>
+ /// <param name="g">The green value of the color.</param>
+ /// <param name="b">The blue value of the color.</param>
+ internal static void SetPixel(this WriteableBitmap bmp, int x, int y, byte a, byte r, byte g, byte b)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ context.Pixels[y * context.Width + x] = (a << 24) | (r << 16) | (g << 8) | b;
+ }
+ }
+
+ #endregion
+
+ #region With System.Windows.Media.Color
+
+ /// <summary>
+ /// Sets the color of the pixel using a precalculated index (faster).
+ /// For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="index">The coordinate index.</param>
+ /// <param name="color">The color.</param>
+ internal static void SetPixeli(this WriteableBitmap bmp, int index, Color color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ context.Pixels[index] = ConvertColor(color);
+ }
+ }
+
+ /// <summary>
+ /// Sets the color of the pixel.
+ /// For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x">The x coordinate (row).</param>
+ /// <param name="y">The y coordinate (column).</param>
+ /// <param name="color">The color.</param>
+ internal static void SetPixel(this WriteableBitmap bmp, int x, int y, Color color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ context.Pixels[y * context.Width + x] = ConvertColor(color);
+ }
+ }
+
+ /// <summary>
+ /// Sets the color of the pixel using an extra alpha value and a precalculated index (faster).
+ /// For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="index">The coordinate index.</param>
+ /// <param name="a">The alpha value of the color.</param>
+ /// <param name="color">The color.</param>
+ internal static void SetPixeli(this WriteableBitmap bmp, int index, byte a, Color color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Add one to use mul and cheap bit shift for multiplicaltion
+ var ai = a + 1;
+ context.Pixels[index] = (a << 24)
+ | ((byte)((color.R * ai) >> 8) << 16)
+ | ((byte)((color.G * ai) >> 8) << 8)
+ | ((byte)((color.B * ai) >> 8));
+ }
+ }
+
+ /// <summary>
+ /// Sets the color of the pixel using an extra alpha value.
+ /// For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x">The x coordinate (row).</param>
+ /// <param name="y">The y coordinate (column).</param>
+ /// <param name="a">The alpha value of the color.</param>
+ /// <param name="color">The color.</param>
+ internal static void SetPixel(this WriteableBitmap bmp, int x, int y, byte a, Color color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Add one to use mul and cheap bit shift for multiplicaltion
+ var ai = a + 1;
+ context.Pixels[y * context.Width + x] = (a << 24)
+ | ((byte)((color.R * ai) >> 8) << 16)
+ | ((byte)((color.G * ai) >> 8) << 8)
+ | ((byte)((color.B * ai) >> 8));
+ }
+ }
+
+ /// <summary>
+ /// Sets the color of the pixel using a precalculated index (faster).
+ /// For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="index">The coordinate index.</param>
+ /// <param name="color">The color.</param>
+ internal static void SetPixeli(this WriteableBitmap bmp, int index, int color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ context.Pixels[index] = color;
+ }
+ }
+
+ /// <summary>
+ /// Sets the color of the pixel.
+ /// For best performance this method should not be used in iterative real-time scenarios. Implement the code directly inside a loop.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x">The x coordinate (row).</param>
+ /// <param name="y">The y coordinate (column).</param>
+ /// <param name="color">The color.</param>
+ internal static void SetPixel(this WriteableBitmap bmp, int x, int y, int color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ context.Pixels[y * context.Width + x] = color;
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapBlitExtensions.cs b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapBlitExtensions.cs
new file mode 100644
index 000000000..4219983d2
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapBlitExtensions.cs
@@ -0,0 +1,704 @@
+#region Header
+//
+// Project: WriteableBitmapEx - WriteableBitmap extensions
+// Description: Collection of blit (copy) extension methods for the WriteableBitmap class.
+//
+// Changed by: $Author: unknown $
+// Changed on: $Date: 2015-03-23 10:25:30 +0100 (Mo, 23 Mrz 2015) $
+// Changed in: $Revision: 113508 $
+// Project: $URL: https://writeablebitmapex.svn.codeplex.com/svn/trunk/Source/WriteableBitmapEx/WriteableBitmapBlitExtensions.cs $
+// Id: $Id: WriteableBitmapBlitExtensions.cs 113508 2015-03-23 09:25:30Z unknown $
+//
+//
+// Copyright © 2009-2015 Bill Reiss, Rene Schulte and WriteableBitmapEx Contributors
+//
+// This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;)
+//
+#endregion
+
+using System;
+
+#if NETFX_CORE
+using Windows.Foundation;
+
+namespace Windows.UI.Xaml.Media.Imaging
+#else
+namespace System.Windows.Media.Imaging
+#endif
+{
+ /// <summary>
+ /// Collection of blit (copy) extension methods for the WriteableBitmap class.
+ /// </summary>
+ internal
+#if WPF
+ unsafe
+#endif
+ static partial class WriteableBitmapExtensions
+ {
+ private const int WhiteR = 255;
+ private const int WhiteG = 255;
+ private const int WhiteB = 255;
+
+ #region Enum
+
+ /// <summary>
+ /// The blending mode.
+ /// </summary>
+ internal enum BlendMode
+ {
+ /// <summary>
+ /// Alpha blending uses the alpha channel to combine the source and destination.
+ /// </summary>
+ Alpha,
+
+ /// <summary>
+ /// Additive blending adds the colors of the source and the destination.
+ /// </summary>
+ Additive,
+
+ /// <summary>
+ /// Subtractive blending subtracts the source color from the destination.
+ /// </summary>
+ Subtractive,
+
+ /// <summary>
+ /// Uses the source color as a mask.
+ /// </summary>
+ Mask,
+
+ /// <summary>
+ /// Multiplies the source color with the destination color.
+ /// </summary>
+ Multiply,
+
+ /// <summary>
+ /// Ignores the specified Color
+ /// </summary>
+ ColorKeying,
+
+ /// <summary>
+ /// No blending just copies the pixels from the source.
+ /// </summary>
+ None
+ }
+
+ #endregion
+
+ #region Methods
+
+ /// <summary>
+ /// Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this).
+ /// </summary>
+ /// <param name="bmp">The destination WriteableBitmap.</param>
+ /// <param name="destRect">The rectangle that defines the destination region.</param>
+ /// <param name="source">The source WriteableBitmap.</param>
+ /// <param name="sourceRect">The rectangle that will be copied from the source to the destination.</param>
+ /// <param name="blendMode">The blending mode <see cref="BlendMode"/>.</param>
+ internal static void Blit(this WriteableBitmap bmp, Rect destRect, WriteableBitmap source, Rect sourceRect, BlendMode blendMode)
+ {
+ Blit(bmp, destRect, source, sourceRect, Colors.White, blendMode);
+ }
+
+ /// <summary>
+ /// Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this).
+ /// </summary>
+ /// <param name="bmp">The destination WriteableBitmap.</param>
+ /// <param name="destRect">The rectangle that defines the destination region.</param>
+ /// <param name="source">The source WriteableBitmap.</param>
+ /// <param name="sourceRect">The rectangle that will be copied from the source to the destination.</param>
+ internal static void Blit(this WriteableBitmap bmp, Rect destRect, WriteableBitmap source, Rect sourceRect)
+ {
+ Blit(bmp, destRect, source, sourceRect, Colors.White, BlendMode.Alpha);
+ }
+
+ /// <summary>
+ /// Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this).
+ /// </summary>
+ /// <param name="bmp">The destination WriteableBitmap.</param>
+ /// <param name="destPosition">The destination position in the destination bitmap.</param>
+ /// <param name="source">The source WriteableBitmap.</param>
+ /// <param name="sourceRect">The rectangle that will be copied from the source to the destination.</param>
+ /// <param name="color">If not Colors.White, will tint the source image. A partially transparent color and the image will be drawn partially transparent.</param>
+ /// <param name="blendMode">The blending mode <see cref="BlendMode"/>.</param>
+ internal static void Blit(this WriteableBitmap bmp, Point destPosition, WriteableBitmap source, Rect sourceRect, Color color, BlendMode blendMode)
+ {
+ var destRect = new Rect(destPosition, new Size(sourceRect.Width, sourceRect.Height));
+ Blit(bmp, destRect, source, sourceRect, color, blendMode);
+ }
+
+ /// <summary>
+ /// Copies (blits) the pixels from the WriteableBitmap source to the destination WriteableBitmap (this).
+ /// </summary>
+ /// <param name="bmp">The destination WriteableBitmap.</param>
+ /// <param name="destRect">The rectangle that defines the destination region.</param>
+ /// <param name="source">The source WriteableBitmap.</param>
+ /// <param name="sourceRect">The rectangle that will be copied from the source to the destination.</param>
+ /// <param name="color">If not Colors.White, will tint the source image. A partially transparent color and the image will be drawn partially transparent. If the BlendMode is ColorKeying, this color will be used as color key to mask all pixels with this value out.</param>
+ /// <param name="blendMode">The blending mode <see cref="BlendMode"/>.</param>
+ internal static void Blit(this WriteableBitmap bmp, Rect destRect, WriteableBitmap source, Rect sourceRect, Color color, BlendMode blendMode)
+ {
+ if (color.A == 0)
+ {
+ return;
+ }
+#if WPF
+ var isPrgba = source.Format == PixelFormats.Pbgra32 || source.Format == PixelFormats.Prgba64 || source.Format == PixelFormats.Prgba128Float;
+#endif
+ var dw = (int)destRect.Width;
+ var dh = (int)destRect.Height;
+
+ using (var srcContext = source.GetBitmapContext(ReadWriteMode.ReadOnly))
+ {
+ using (var destContext = bmp.GetBitmapContext())
+ {
+ var sourceWidth = srcContext.Width;
+ var dpw = destContext.Width;
+ var dph = destContext.Height;
+
+ var intersect = new Rect(0, 0, dpw, dph);
+ intersect.Intersect(destRect);
+ if (intersect.IsEmpty)
+ {
+ return;
+ }
+
+ var sourcePixels = srcContext.Pixels;
+ var destPixels = destContext.Pixels;
+ var sourceLength = srcContext.Length;
+
+ int sourceIdx = -1;
+ int px = (int)destRect.X;
+ int py = (int)destRect.Y;
+
+ int x;
+ int y;
+ int idx;
+ double ii;
+ double jj;
+ int sr = 0;
+ int sg = 0;
+ int sb = 0;
+ int dr, dg, db;
+ int sourcePixel;
+ int sa = 0;
+ int da;
+ int ca = color.A;
+ int cr = color.R;
+ int cg = color.G;
+ int cb = color.B;
+ bool tinted = color != Colors.White;
+ var sw = (int)sourceRect.Width;
+ var sdx = sourceRect.Width / destRect.Width;
+ var sdy = sourceRect.Height / destRect.Height;
+ int sourceStartX = (int)sourceRect.X;
+ int sourceStartY = (int)sourceRect.Y;
+ int lastii, lastjj;
+ lastii = -1;
+ lastjj = -1;
+ jj = sourceStartY;
+ y = py;
+ for (int j = 0; j < dh; j++)
+ {
+ if (y >= 0 && y < dph)
+ {
+ ii = sourceStartX;
+ idx = px + y * dpw;
+ x = px;
+ sourcePixel = sourcePixels[0];
+
+ // Scanline BlockCopy is much faster (3.5x) if no tinting and blending is needed,
+ // even for smaller sprites like the 32x32 particles.
+ if (blendMode == BlendMode.None && !tinted)
+ {
+ sourceIdx = (int)ii + (int)jj * sourceWidth;
+ var offset = x < 0 ? -x : 0;
+ var xx = x + offset;
+ var wx = sourceWidth - offset;
+ var len = xx + wx < dpw ? wx : dpw - xx;
+ if (len > sw) len = sw;
+ if (len > dw) len = dw;
+ BitmapContext.BlockCopy(srcContext, (sourceIdx + offset) * 4, destContext, (idx + offset) * 4, len * 4);
+ }
+
+ // Pixel by pixel copying
+ else
+ {
+ for (int i = 0; i < dw; i++)
+ {
+ if (x >= 0 && x < dpw)
+ {
+ if ((int)ii != lastii || (int)jj != lastjj)
+ {
+ sourceIdx = (int)ii + (int)jj * sourceWidth;
+ if (sourceIdx >= 0 && sourceIdx < sourceLength)
+ {
+ sourcePixel = sourcePixels[sourceIdx];
+ sa = ((sourcePixel >> 24) & 0xff);
+ sr = ((sourcePixel >> 16) & 0xff);
+ sg = ((sourcePixel >> 8) & 0xff);
+ sb = ((sourcePixel) & 0xff);
+ if (tinted && sa != 0)
+ {
+ sa = (((sa * ca) * 0x8081) >> 23);
+ sr = ((((((sr * cr) * 0x8081) >> 23) * ca) * 0x8081) >> 23);
+ sg = ((((((sg * cg) * 0x8081) >> 23) * ca) * 0x8081) >> 23);
+ sb = ((((((sb * cb) * 0x8081) >> 23) * ca) * 0x8081) >> 23);
+ sourcePixel = (sa << 24) | (sr << 16) | (sg << 8) | sb;
+ }
+ }
+ else
+ {
+ sa = 0;
+ }
+ }
+ if (blendMode == BlendMode.None)
+ {
+ destPixels[idx] = sourcePixel;
+ }
+ else if (blendMode == BlendMode.ColorKeying)
+ {
+ sr = ((sourcePixel >> 16) & 0xff);
+ sg = ((sourcePixel >> 8) & 0xff);
+ sb = ((sourcePixel) & 0xff);
+
+ if (sr != color.R || sg != color.G || sb != color.B)
+ {
+ destPixels[idx] = sourcePixel;
+ }
+
+ }
+ else if (blendMode == BlendMode.Mask)
+ {
+ int destPixel = destPixels[idx];
+ da = ((destPixel >> 24) & 0xff);
+ dr = ((destPixel >> 16) & 0xff);
+ dg = ((destPixel >> 8) & 0xff);
+ db = ((destPixel) & 0xff);
+ destPixel = ((((da * sa) * 0x8081) >> 23) << 24) |
+ ((((dr * sa) * 0x8081) >> 23) << 16) |
+ ((((dg * sa) * 0x8081) >> 23) << 8) |
+ ((((db * sa) * 0x8081) >> 23));
+ destPixels[idx] = destPixel;
+ }
+ else if (sa > 0)
+ {
+ int destPixel = destPixels[idx];
+ da = ((destPixel >> 24) & 0xff);
+ if ((sa == 255 || da == 0) &&
+ blendMode != BlendMode.Additive
+ && blendMode != BlendMode.Subtractive
+ && blendMode != BlendMode.Multiply
+ )
+ {
+ destPixels[idx] = sourcePixel;
+ }
+ else
+ {
+ dr = ((destPixel >> 16) & 0xff);
+ dg = ((destPixel >> 8) & 0xff);
+ db = ((destPixel) & 0xff);
+ if (blendMode == BlendMode.Alpha)
+ {
+ var isa = 255 - sa;
+#if NETFX_CORE
+ // Special case for WinRT since it does not use pARGB (pre-multiplied alpha)
+ destPixel = ((da & 0xff) << 24) |
+ ((((sr * sa + isa * dr) >> 8) & 0xff) << 16) |
+ ((((sg * sa + isa * dg) >> 8) & 0xff) << 8) |
+ (((sb * sa + isa * db) >> 8) & 0xff);
+#elif WPF
+ if (isPrgba)
+ {
+ destPixel = ((da & 0xff) << 24) |
+ (((((sr << 8) + isa * dr) >> 8) & 0xff) << 16) |
+ (((((sg << 8) + isa * dg) >> 8) & 0xff) << 8) |
+ ((((sb << 8) + isa * db) >> 8) & 0xff);
+ }
+ else
+ {
+ destPixel = ((da & 0xff) << 24) |
+ (((((sr * sa) + isa * dr) >> 8) & 0xff) << 16) |
+ (((((sg * sa) + isa * dg) >> 8) & 0xff) << 8) |
+ ((((sb * sa) + isa * db) >> 8) & 0xff);
+ }
+#else
+ destPixel = ((da & 0xff) << 24) |
+ (((((sr << 8) + isa * dr) >> 8) & 0xff) << 16) |
+ (((((sg << 8) + isa * dg) >> 8) & 0xff) << 8) |
+ ((((sb << 8) + isa * db) >> 8) & 0xff);
+#endif
+ }
+ else if (blendMode == BlendMode.Additive)
+ {
+ int a = (255 <= sa + da) ? 255 : (sa + da);
+ destPixel = (a << 24) |
+ (((a <= sr + dr) ? a : (sr + dr)) << 16) |
+ (((a <= sg + dg) ? a : (sg + dg)) << 8) |
+ (((a <= sb + db) ? a : (sb + db)));
+ }
+ else if (blendMode == BlendMode.Subtractive)
+ {
+ int a = da;
+ destPixel = (a << 24) |
+ (((sr >= dr) ? 0 : (sr - dr)) << 16) |
+ (((sg >= dg) ? 0 : (sg - dg)) << 8) |
+ (((sb >= db) ? 0 : (sb - db)));
+ }
+ else if (blendMode == BlendMode.Multiply)
+ {
+ // Faster than a division like (s * d) / 255 are 2 shifts and 2 adds
+ int ta = (sa * da) + 128;
+ int tr = (sr * dr) + 128;
+ int tg = (sg * dg) + 128;
+ int tb = (sb * db) + 128;
+
+ int ba = ((ta >> 8) + ta) >> 8;
+ int br = ((tr >> 8) + tr) >> 8;
+ int bg = ((tg >> 8) + tg) >> 8;
+ int bb = ((tb >> 8) + tb) >> 8;
+
+ destPixel = (ba << 24) |
+ ((ba <= br ? ba : br) << 16) |
+ ((ba <= bg ? ba : bg) << 8) |
+ ((ba <= bb ? ba : bb));
+ }
+
+ destPixels[idx] = destPixel;
+ }
+ }
+ }
+ x++;
+ idx++;
+ ii += sdx;
+ }
+ }
+ }
+ jj += sdy;
+ y++;
+ }
+ }
+ }
+ }
+
+ internal static void Blit(BitmapContext destContext, int dpw, int dph, Rect destRect, BitmapContext srcContext, Rect sourceRect, int sourceWidth)
+ {
+ const BlendMode blendMode = BlendMode.Alpha;
+
+ int dw = (int)destRect.Width;
+ int dh = (int)destRect.Height;
+
+ Rect intersect = new Rect(0, 0, dpw, dph);
+ intersect.Intersect(destRect);
+ if (intersect.IsEmpty)
+ {
+ return;
+ }
+#if WPF
+ var isPrgba = srcContext.Format == PixelFormats.Pbgra32 || srcContext.Format == PixelFormats.Prgba64 || srcContext.Format == PixelFormats.Prgba128Float;
+#endif
+
+ var sourcePixels = srcContext.Pixels;
+ var destPixels = destContext.Pixels;
+ int sourceLength = srcContext.Length;
+ int sourceIdx = -1;
+ int px = (int)destRect.X;
+ int py = (int)destRect.Y;
+ int x;
+ int y;
+ int idx;
+ double ii;
+ double jj;
+ int sr = 0;
+ int sg = 0;
+ int sb = 0;
+ int dr, dg, db;
+ int sourcePixel;
+ int sa = 0;
+ int da;
+
+ var sw = (int)sourceRect.Width;
+ var sdx = sourceRect.Width / destRect.Width;
+ var sdy = sourceRect.Height / destRect.Height;
+ int sourceStartX = (int)sourceRect.X;
+ int sourceStartY = (int)sourceRect.Y;
+ int lastii, lastjj;
+ lastii = -1;
+ lastjj = -1;
+ jj = sourceStartY;
+ y = py;
+ for (var j = 0; j < dh; j++)
+ {
+ if (y >= 0 && y < dph)
+ {
+ ii = sourceStartX;
+ idx = px + y * dpw;
+ x = px;
+ sourcePixel = sourcePixels[0];
+
+ // Pixel by pixel copying
+ for (var i = 0; i < dw; i++)
+ {
+ if (x >= 0 && x < dpw)
+ {
+ if ((int)ii != lastii || (int)jj != lastjj)
+ {
+ sourceIdx = (int)ii + (int)jj * sourceWidth;
+ if (sourceIdx >= 0 && sourceIdx < sourceLength)
+ {
+ sourcePixel = sourcePixels[sourceIdx];
+ sa = ((sourcePixel >> 24) & 0xff);
+ sr = ((sourcePixel >> 16) & 0xff);
+ sg = ((sourcePixel >> 8) & 0xff);
+ sb = ((sourcePixel) & 0xff);
+ }
+ else
+ {
+ sa = 0;
+ }
+ }
+
+ if (sa > 0)
+ {
+ int destPixel = destPixels[idx];
+ da = ((destPixel >> 24) & 0xff);
+ if ((sa == 255 || da == 0))
+ {
+ destPixels[idx] = sourcePixel;
+ }
+ else
+ {
+ dr = ((destPixel >> 16) & 0xff);
+ dg = ((destPixel >> 8) & 0xff);
+ db = ((destPixel) & 0xff);
+ var isa = 255 - sa;
+#if NETFX_CORE
+ // Special case for WinRT since it does not use pARGB (pre-multiplied alpha)
+ destPixel = ((da & 0xff) << 24) |
+ ((((sr * sa + isa * dr) >> 8) & 0xff) << 16) |
+ ((((sg * sa + isa * dg) >> 8) & 0xff) << 8) |
+ (((sb * sa + isa * db) >> 8) & 0xff);
+#elif WPF
+ if (isPrgba)
+ {
+ destPixel = ((da & 0xff) << 24) |
+ (((((sr << 8) + isa * dr) >> 8) & 0xff) << 16) |
+ (((((sg << 8) + isa * dg) >> 8) & 0xff) << 8) |
+ ((((sb << 8) + isa * db) >> 8) & 0xff);
+ }
+ else
+ {
+ destPixel = ((da & 0xff) << 24) |
+ (((((sr * sa) + isa * dr) >> 8) & 0xff) << 16) |
+ (((((sg * sa) + isa * dg) >> 8) & 0xff) << 8) |
+ ((((sb * sa) + isa * db) >> 8) & 0xff);
+ }
+#else
+ destPixel = ((da & 0xff) << 24) |
+ (((((sr << 8) + isa * dr) >> 8) & 0xff) << 16) |
+ (((((sg << 8) + isa * dg) >> 8) & 0xff) << 8) |
+ ((((sb << 8) + isa * db) >> 8) & 0xff);
+#endif
+ destPixels[idx] = destPixel;
+ }
+ }
+ }
+ x++;
+ idx++;
+ ii += sdx;
+ }
+ }
+ jj += sdy;
+ y++;
+ }
+
+ }
+
+
+ /// <summary>
+ /// Renders a bitmap using any affine transformation and transparency into this bitmap
+ /// Unlike Silverlight's Render() method, this one uses 2-3 times less memory, and is the same or better quality
+ /// The algorithm is simple dx/dy (bresenham-like) step by step painting, optimized with fixed point and fast bilinear filtering
+ /// It's used in Fantasia Painter for drawing stickers and 3D objects on screen
+ /// </summary>
+ /// <param name="bmp">Destination bitmap.</param>
+ /// <param name="source">The source WriteableBitmap.</param>
+ /// <param name="shouldClear">If true, the the destination bitmap will be set to all clear (0) before rendering.</param>
+ /// <param name="opacity">opacity of the source bitmap to render, between 0 and 1 inclusive</param>
+ /// <param name="transform">Transformation to apply</param>
+ internal static void BlitRender(this WriteableBitmap bmp, WriteableBitmap source, bool shouldClear = true, float opacity = 1f, GeneralTransform transform = null)
+ {
+ const int PRECISION_SHIFT = 10;
+ const int PRECISION_VALUE = (1 << PRECISION_SHIFT);
+ const int PRECISION_MASK = PRECISION_VALUE - 1;
+
+ using (BitmapContext destContext = bmp.GetBitmapContext())
+ {
+ if (transform == null) transform = new MatrixTransform();
+
+ var destPixels = destContext.Pixels;
+ int destWidth = destContext.Width;
+ int destHeight = destContext.Height;
+ var inverse = transform.Inverse;
+ if(shouldClear) destContext.Clear();
+
+ using (BitmapContext sourceContext = source.GetBitmapContext())
+ {
+ var sourcePixels = sourceContext.Pixels;
+ int sourceWidth = sourceContext.Width;
+ int sourceHeight = sourceContext.Height;
+
+ Rect sourceRect = new Rect(0, 0, sourceWidth, sourceHeight);
+ Rect destRect = new Rect(0, 0, destWidth, destHeight);
+ Rect bounds = transform.TransformBounds(sourceRect);
+ bounds.Intersect(destRect);
+
+ int startX = (int)bounds.Left;
+ int startY = (int)bounds.Top;
+ int endX = (int)bounds.Right;
+ int endY = (int)bounds.Bottom;
+
+#if NETFX_CORE
+ Point zeroZero = inverse.TransformPoint(new Point(startX, startY));
+ Point oneZero = inverse.TransformPoint(new Point(startX + 1, startY));
+ Point zeroOne = inverse.TransformPoint(new Point(startX, startY + 1));
+#else
+ Point zeroZero = inverse.Transform(new Point(startX, startY));
+ Point oneZero = inverse.Transform(new Point(startX + 1, startY));
+ Point zeroOne = inverse.Transform(new Point(startX, startY + 1));
+#endif
+ float sourceXf = ((float)zeroZero.X);
+ float sourceYf = ((float)zeroZero.Y);
+ int dxDx = (int)((((float)oneZero.X) - sourceXf) * PRECISION_VALUE); // for 1 unit in X coord, how much does X change in source texture?
+ int dxDy = (int)((((float)oneZero.Y) - sourceYf) * PRECISION_VALUE); // for 1 unit in X coord, how much does Y change in source texture?
+ int dyDx = (int)((((float)zeroOne.X) - sourceXf) * PRECISION_VALUE); // for 1 unit in Y coord, how much does X change in source texture?
+ int dyDy = (int)((((float)zeroOne.Y) - sourceYf) * PRECISION_VALUE); // for 1 unit in Y coord, how much does Y change in source texture?
+
+ int sourceX = (int)(((float)zeroZero.X) * PRECISION_VALUE);
+ int sourceY = (int)(((float)zeroZero.Y) * PRECISION_VALUE);
+ int sourceWidthFixed = sourceWidth << PRECISION_SHIFT;
+ int sourceHeightFixed = sourceHeight << PRECISION_SHIFT;
+
+ int opacityInt = (int)(opacity * 255);
+
+ int index = 0;
+ for (int destY = startY; destY < endY; destY++)
+ {
+ index = destY * destWidth + startX;
+ int savedSourceX = sourceX;
+ int savedSourceY = sourceY;
+
+ for (int destX = startX; destX < endX; destX++)
+ {
+ if ((sourceX >= 0) && (sourceX < sourceWidthFixed) && (sourceY >= 0) && (sourceY < sourceHeightFixed))
+ {
+ // bilinear filtering
+ int xFloor = sourceX >> PRECISION_SHIFT;
+ int yFloor = sourceY >> PRECISION_SHIFT;
+
+ if (xFloor < 0) xFloor = 0;
+ if (yFloor < 0) yFloor = 0;
+
+ int xCeil = xFloor + 1;
+ int yCeil = yFloor + 1;
+
+ if (xCeil >= sourceWidth)
+ {
+ xFloor = sourceWidth - 1;
+ xCeil = 0;
+ }
+ else
+ {
+ xCeil = 1;
+ }
+
+ if (yCeil >= sourceHeight)
+ {
+ yFloor = sourceHeight - 1;
+ yCeil = 0;
+ }
+ else
+ {
+ yCeil = sourceWidth;
+ }
+
+ int i1 = yFloor * sourceWidth + xFloor;
+ int p1 = sourcePixels[i1];
+ int p2 = sourcePixels[i1 + xCeil];
+ int p3 = sourcePixels[i1 + yCeil];
+ int p4 = sourcePixels[i1 + yCeil + xCeil];
+
+ int xFrac = sourceX & PRECISION_MASK;
+ int yFrac = sourceY & PRECISION_MASK;
+
+ // alpha
+ byte a1 = (byte)(p1 >> 24);
+ byte a2 = (byte)(p2 >> 24);
+ byte a3 = (byte)(p3 >> 24);
+ byte a4 = (byte)(p4 >> 24);
+
+ int comp1, comp2;
+ byte a;
+
+ if ((a1 == a2) && (a1 == a3) && (a1 == a4))
+ {
+ if (a1 == 0)
+ {
+ destPixels[index] = 0;
+
+ sourceX += dxDx;
+ sourceY += dxDy;
+ index++;
+ continue;
+ }
+
+ a = a1;
+ }
+ else
+ {
+ comp1 = a1 + ((xFrac * (a2 - a1)) >> PRECISION_SHIFT);
+ comp2 = a3 + ((xFrac * (a4 - a3)) >> PRECISION_SHIFT);
+ a = (byte)(comp1 + ((yFrac * (comp2 - comp1)) >> PRECISION_SHIFT));
+ }
+
+ // red
+ comp1 = ((byte)(p1 >> 16)) + ((xFrac * (((byte)(p2 >> 16)) - ((byte)(p1 >> 16)))) >> PRECISION_SHIFT);
+ comp2 = ((byte)(p3 >> 16)) + ((xFrac * (((byte)(p4 >> 16)) - ((byte)(p3 >> 16)))) >> PRECISION_SHIFT);
+ byte r = (byte)(comp1 + ((yFrac * (comp2 - comp1)) >> PRECISION_SHIFT));
+
+ // green
+ comp1 = ((byte)(p1 >> 8)) + ((xFrac * (((byte)(p2 >> 8)) - ((byte)(p1 >> 8)))) >> PRECISION_SHIFT);
+ comp2 = ((byte)(p3 >> 8)) + ((xFrac * (((byte)(p4 >> 8)) - ((byte)(p3 >> 8)))) >> PRECISION_SHIFT);
+ byte g = (byte)(comp1 + ((yFrac * (comp2 - comp1)) >> PRECISION_SHIFT));
+
+ // blue
+ comp1 = ((byte)p1) + ((xFrac * (((byte)p2) - ((byte)p1))) >> PRECISION_SHIFT);
+ comp2 = ((byte)p3) + ((xFrac * (((byte)p4) - ((byte)p3))) >> PRECISION_SHIFT);
+ byte b = (byte)(comp1 + ((yFrac * (comp2 - comp1)) >> PRECISION_SHIFT));
+
+ // save updated pixel
+ if (opacityInt != 255)
+ {
+ a = (byte)((a * opacityInt) >> 8);
+ r = (byte)((r * opacityInt) >> 8);
+ g = (byte)((g * opacityInt) >> 8);
+ b = (byte)((b * opacityInt) >> 8);
+ }
+ destPixels[index] = (a << 24) | (r << 16) | (g << 8) | b;
+ }
+
+ sourceX += dxDx;
+ sourceY += dxDy;
+ index++;
+ }
+
+ sourceX = savedSourceX + dyDx;
+ sourceY = savedSourceY + dyDy;
+ }
+ }
+ }
+ }
+
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapContextExtensions.cs b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapContextExtensions.cs
new file mode 100644
index 000000000..0846a76ed
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapContextExtensions.cs
@@ -0,0 +1,55 @@
+#region Header
+//
+// Project: WriteableBitmapEx - WriteableBitmap extensions
+// Description: Collection of extension methods for the WriteableBitmap class.
+//
+// Changed by: $Author: unknown $
+// Changed on: $Date: 2015-03-05 18:18:24 +0100 (Do, 05 Mrz 2015) $
+// Changed in: $Revision: 113191 $
+// Project: $URL: https://writeablebitmapex.svn.codeplex.com/svn/trunk/Source/WriteableBitmapEx/WriteableBitmapContextExtensions.cs $
+// Id: $Id: WriteableBitmapContextExtensions.cs 113191 2015-03-05 17:18:24Z unknown $
+//
+//
+// Copyright © 2009-2015 Rene Schulte and WriteableBitmapEx Contributors
+//
+// This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;)
+//
+#endregion
+
+using System;
+
+#if NETFX_CORE
+namespace Windows.UI.Xaml.Media.Imaging
+#else
+namespace System.Windows.Media.Imaging
+#endif
+{
+ /// <summary>
+ /// Provides the WriteableBitmap context pixel data
+ /// </summary>
+ internal static partial class WriteableBitmapContextExtensions
+ {
+ /// <summary>
+ /// Gets a BitmapContext within which to perform nested IO operations on the bitmap
+ /// </summary>
+ /// <remarks>For WPF the BitmapContext will lock the bitmap. Call Dispose on the context to unlock</remarks>
+ /// <param name="bmp"></param>
+ /// <returns></returns>
+ internal static BitmapContext GetBitmapContext(this WriteableBitmap bmp)
+ {
+ return new BitmapContext(bmp);
+ }
+
+ /// <summary>
+ /// Gets a BitmapContext within which to perform nested IO operations on the bitmap
+ /// </summary>
+ /// <remarks>For WPF the BitmapContext will lock the bitmap. Call Dispose on the context to unlock</remarks>
+ /// <param name="bmp">The bitmap.</param>
+ /// <param name="mode">The ReadWriteMode. If set to ReadOnly, the bitmap will not be invalidated on dispose of the context, else it will</param>
+ /// <returns></returns>
+ internal static BitmapContext GetBitmapContext(this WriteableBitmap bmp, ReadWriteMode mode)
+ {
+ return new BitmapContext(bmp, mode);
+ }
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapConvertExtensions.cs b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapConvertExtensions.cs
new file mode 100644
index 000000000..f60c6ffb8
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapConvertExtensions.cs
@@ -0,0 +1,344 @@
+#region Header
+//
+// Project: WriteableBitmapEx - WriteableBitmap extensions
+// Description: Collection of interchange extension methods for the WriteableBitmap class.
+//
+// Changed by: $Author: unknown $
+// Changed on: $Date: 2015-03-17 16:18:14 +0100 (Di, 17 Mrz 2015) $
+// Changed in: $Revision: 113386 $
+// Project: $URL: https://writeablebitmapex.svn.codeplex.com/svn/trunk/Source/WriteableBitmapEx/WriteableBitmapConvertExtensions.cs $
+// Id: $Id: WriteableBitmapConvertExtensions.cs 113386 2015-03-17 15:18:14Z unknown $
+//
+//
+// Copyright © 2009-2015 Rene Schulte and WriteableBitmapEx Contributors
+//
+// This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;)
+//
+#endregion
+
+using System;
+using System.IO;
+using System.Reflection;
+
+#if NETFX_CORE
+using Windows.ApplicationModel.Resources;
+using Windows.Storage;
+using Windows.Storage.Streams;
+using System.Threading.Tasks;
+using Windows.Graphics.Imaging;
+using System.Runtime.InteropServices.WindowsRuntime;
+
+namespace Windows.UI.Xaml.Media.Imaging
+#else
+namespace System.Windows.Media.Imaging
+#endif
+{
+ /// <summary>
+ /// Collection of interchange extension methods for the WriteableBitmap class.
+ /// </summary>
+ internal
+#if WPF
+ unsafe
+#endif
+ static partial class WriteableBitmapExtensions
+ {
+ #region Methods
+
+ #region Byte Array
+
+ /// <summary>
+ /// Copies the Pixels from the WriteableBitmap into a ARGB byte array starting at a specific Pixels index.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="offset">The starting Pixels index.</param>
+ /// <param name="count">The number of Pixels to copy, -1 for all</param>
+ /// <returns>The color buffer as byte ARGB values.</returns>
+ internal static byte[] ToByteArray(this WriteableBitmap bmp, int offset, int count)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ if (count == -1)
+ {
+ // Copy all to byte array
+ count = context.Length;
+ }
+
+ var len = count * SizeOfArgb;
+ var result = new byte[len]; // ARGB
+ BitmapContext.BlockCopy(context, offset, result, 0, len);
+ return result;
+ }
+ }
+
+ /// <summary>
+ /// Copies the Pixels from the WriteableBitmap into a ARGB byte array.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="count">The number of pixels to copy.</param>
+ /// <returns>The color buffer as byte ARGB values.</returns>
+ internal static byte[] ToByteArray(this WriteableBitmap bmp, int count)
+ {
+ return bmp.ToByteArray(0, count);
+ }
+
+ /// <summary>
+ /// Copies all the Pixels from the WriteableBitmap into a ARGB byte array.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <returns>The color buffer as byte ARGB values.</returns>
+ internal static byte[] ToByteArray(this WriteableBitmap bmp)
+ {
+ return bmp.ToByteArray(0, -1);
+ }
+
+ /// <summary>
+ /// Copies color information from an ARGB byte array into this WriteableBitmap starting at a specific buffer index.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="offset">The starting index in the buffer.</param>
+ /// <param name="count">The number of bytes to copy from the buffer.</param>
+ /// <param name="buffer">The color buffer as byte ARGB values.</param>
+ /// <returns>The WriteableBitmap that was passed as parameter.</returns>
+ internal static WriteableBitmap FromByteArray(this WriteableBitmap bmp, byte[] buffer, int offset, int count)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ BitmapContext.BlockCopy(buffer, offset, context, 0, count);
+ return bmp;
+ }
+ }
+
+ /// <summary>
+ /// Copies color information from an ARGB byte array into this WriteableBitmap.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="count">The number of bytes to copy from the buffer.</param>
+ /// <param name="buffer">The color buffer as byte ARGB values.</param>
+ /// <returns>The WriteableBitmap that was passed as parameter.</returns>
+ internal static WriteableBitmap FromByteArray(this WriteableBitmap bmp, byte[] buffer, int count)
+ {
+ return bmp.FromByteArray(buffer, 0, count);
+ }
+
+ /// <summary>
+ /// Copies all the color information from an ARGB byte array into this WriteableBitmap.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="buffer">The color buffer as byte ARGB values.</param>
+ /// <returns>The WriteableBitmap that was passed as parameter.</returns>
+ internal static WriteableBitmap FromByteArray(this WriteableBitmap bmp, byte[] buffer)
+ {
+ return bmp.FromByteArray(buffer, 0, buffer.Length);
+ }
+
+ #endregion
+
+ #region TGA File
+
+ /// <summary>
+ /// Writes the WriteableBitmap as a TGA image to a stream.
+ /// Used with permission from Nokola: http://nokola.com/blog/post/2010/01/21/Quick-and-Dirty-Output-of-WriteableBitmap-as-TGA-Image.aspx
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="destination">The destination stream.</param>
+ internal static void WriteTga(this WriteableBitmap bmp, Stream destination)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ int width = context.Width;
+ int height = context.Height;
+ var pixels = context.Pixels;
+ byte[] data = new byte[context.Length * SizeOfArgb];
+
+ // Copy bitmap data as BGRA
+ int offsetSource = 0;
+ int width4 = width << 2;
+ int width8 = width << 3;
+ int offsetDest = (height - 1) * width4;
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ // Account for pre-multiplied alpha
+ int c = pixels[offsetSource];
+ var a = (byte)(c >> 24);
+
+ // Prevent division by zero
+ int ai = a;
+ if (ai == 0)
+ {
+ ai = 1;
+ }
+
+ // Scale inverse alpha to use cheap integer mul bit shift
+ ai = ((255 << 8) / ai);
+ data[offsetDest + 3] = (byte)a; // A
+ data[offsetDest + 2] = (byte)((((c >> 16) & 0xFF) * ai) >> 8); // R
+ data[offsetDest + 1] = (byte)((((c >> 8) & 0xFF) * ai) >> 8); // G
+ data[offsetDest] = (byte)((((c & 0xFF) * ai) >> 8)); // B
+
+ offsetSource++;
+ offsetDest += SizeOfArgb;
+ }
+ offsetDest -= width8;
+ }
+
+ // Create header
+ var header = new byte[]
+ {
+ 0, // ID length
+ 0, // no color map
+ 2, // uncompressed, true color
+ 0, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0, // x and y origin
+ (byte)(width & 0x00FF),
+ (byte)((width & 0xFF00) >> 8),
+ (byte)(height & 0x00FF),
+ (byte)((height & 0xFF00) >> 8),
+ 32, // 32 bit bitmap
+ 0
+ };
+
+ // Write header and data
+ using (var writer = new BinaryWriter(destination))
+ {
+ writer.Write(header);
+ writer.Write(data);
+ }
+ }
+ }
+
+ #endregion
+
+ #region Resource
+#if !NETFX_CORE
+ /// <summary>
+ /// Loads an image from the applications resource file and returns a new WriteableBitmap. The passed WriteableBitmap is not used.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="relativePath">Only the relative path to the resource file. The assembly name is retrieved automatically.</param>
+ /// <returns>A new WriteableBitmap containing the pixel data.</returns>
+ [Obsolete("Please use BitmapContext.FromResource instead of this FromResource method.")]
+ internal static WriteableBitmap FromResource(this WriteableBitmap bmp, string relativePath)
+ {
+ return BitmapFactory.FromResource(relativePath);
+ }
+#endif
+
+#if NETFX_CORE
+ /// <summary>
+ /// Loads an image from the applications content and returns a new WriteableBitmap. The passed WriteableBitmap is not used.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="uri">The URI to the content file.</param>
+ /// <param name="pixelFormat">The pixel format of the stream data. If Unknown is provided as param, the default format of the BitmapDecoder is used.</param>
+ /// <returns>A new WriteableBitmap containing the pixel data.</returns>
+ [Obsolete("Please use BitmapContext.FromContent instead of this FromContent method.")]
+ internal static Task<WriteableBitmap> FromContent(this WriteableBitmap bmp, Uri uri, BitmapPixelFormat pixelFormat = BitmapPixelFormat.Unknown)
+ {
+ return BitmapFactory.FromContent(uri, pixelFormat);
+ }
+
+ /// <summary>
+ /// Loads the data from an image stream and returns a new WriteableBitmap. The passed WriteableBitmap is not used.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="stream">The stream with the image data.</param>
+ /// <param name="pixelFormat">The pixel format of the stream data. If Unknown is provided as param, the default format of the BitmapDecoder is used.</param>
+ /// <returns>A new WriteableBitmap containing the pixel data.</returns>
+ [Obsolete("Please use BitmapContext.FromStream instead of this FromStream method.")]
+ internal static Task<WriteableBitmap> FromStream(this WriteableBitmap bmp, Stream stream, BitmapPixelFormat pixelFormat = BitmapPixelFormat.Unknown)
+ {
+ return BitmapFactory.FromStream(stream, pixelFormat);
+ }
+
+ /// <summary>
+ /// Loads the data from an image stream and returns a new WriteableBitmap. The passed WriteableBitmap is not used.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="stream">The stream with the image data.</param>
+ /// <param name="pixelFormat">The pixel format of the stream data. If Unknown is provided as param, the default format of the BitmapDecoder is used.</param>
+ /// <returns>A new WriteableBitmap containing the pixel data.</returns>
+ [Obsolete("Please use BitmapContext.FromStream instead of this FromStream method.")]
+ internal static Task<WriteableBitmap> FromStream(this WriteableBitmap bmp, IRandomAccessStream stream, BitmapPixelFormat pixelFormat = BitmapPixelFormat.Unknown)
+ {
+ return BitmapFactory.FromStream(stream, pixelFormat);
+ }
+
+ /// <summary>
+ /// Encodes the data from a WriteableBitmap into a stream.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="destinationStream">The stream which will take the image data.</param>
+ /// <param name="encoderId">The encoder GUID to use like BitmapEncoder.JpegEncoderId etc.</param>
+ internal static async Task ToStream(this WriteableBitmap bmp, IRandomAccessStream destinationStream, Guid encoderId)
+ {
+ // Copy buffer to pixels
+ byte[] pixels;
+ using (var stream = bmp.PixelBuffer.AsStream())
+ {
+ pixels = new byte[(uint)stream.Length];
+ await stream.ReadAsync(pixels, 0, pixels.Length);
+ }
+
+ // Encode pixels into stream
+ var encoder = await BitmapEncoder.CreateAsync(encoderId, destinationStream);
+ encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, (uint)bmp.PixelWidth, (uint)bmp.PixelHeight, 96, 96, pixels);
+ await encoder.FlushAsync();
+ }
+
+ /// <summary>
+ /// Encodes the data from a WriteableBitmap as JPEG into a stream.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="destinationStream">The stream which will take the JPEG image data.</param>
+ internal static async Task ToStreamAsJpeg(this WriteableBitmap bmp, IRandomAccessStream destinationStream)
+ {
+ await ToStream(bmp, destinationStream, BitmapEncoder.JpegEncoderId);
+ }
+
+ /// <summary>
+ /// Loads the data from a pixel buffer like the RenderTargetBitmap provides and returns a new WriteableBitmap. The passed WriteableBitmap is not used.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="pixelBuffer">The source pixel buffer with the image data.</param>
+ /// <param name="width">The width of the image data.</param>
+ /// <param name="height">The height of the image data.</param>
+ /// <returns>A new WriteableBitmap containing the pixel data.</returns>
+ [Obsolete("Please use BitmapContext.FromPixelBuffer instead of this FromPixelBuffer method.")]
+ internal static Task<WriteableBitmap> FromPixelBuffer(this WriteableBitmap bmp, IBuffer pixelBuffer, int width, int height)
+ {
+ return BitmapFactory.FromPixelBuffer(pixelBuffer, width, height);
+ }
+#else
+ /// <summary>
+ /// Loads an image from the applications content and returns a new WriteableBitmap. The passed WriteableBitmap is not used.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="relativePath">Only the relative path to the content file.</param>
+ /// <returns>A new WriteableBitmap containing the pixel data.</returns>
+ [Obsolete("Please use BitmapContext.FromContent instead of this FromContent method.")]
+ internal static WriteableBitmap FromContent(this WriteableBitmap bmp, string relativePath)
+ {
+ return BitmapFactory.FromContent(relativePath);
+ }
+
+ /// <summary>
+ /// Loads the data from an image stream and returns a new WriteableBitmap. The passed WriteableBitmap is not used.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="stream">The stream with the image data.</param>
+ /// <returns>A new WriteableBitmap containing the pixel data.</returns>
+ [Obsolete("Please use BitmapContext.FromStream instead of this FromStream method.")]
+ internal static WriteableBitmap FromStream(this WriteableBitmap bmp, Stream stream)
+ {
+ return BitmapFactory.FromStream(stream);
+ }
+#endif
+
+ #endregion
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapFillExtensions.cs b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapFillExtensions.cs
new file mode 100644
index 000000000..783c4d81b
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapFillExtensions.cs
@@ -0,0 +1,1205 @@
+#region Header
+//
+// Project: WriteableBitmapEx - WriteableBitmap extensions
+// Description: Collection of extension methods for the WriteableBitmap class.
+//
+// Changed by: $Author: unknown $
+// Changed on: $Date: 2015-03-05 21:21:11 +0100 (Do, 05 Mrz 2015) $
+// Changed in: $Revision: 113194 $
+// Project: $URL: https://writeablebitmapex.svn.codeplex.com/svn/trunk/Source/WriteableBitmapEx/WriteableBitmapFillExtensions.cs $
+// Id: $Id: WriteableBitmapFillExtensions.cs 113194 2015-03-05 20:21:11Z unknown $
+//
+//
+// Copyright © 2009-2015 Rene Schulte and WriteableBitmapEx Contributors
+//
+// This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;)
+//
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+#if NETFX_CORE
+namespace Windows.UI.Xaml.Media.Imaging
+#else
+namespace System.Windows.Media.Imaging
+#endif
+{
+ /// <summary>
+ /// Collection of extension methods for the WriteableBitmap class.
+ /// </summary>
+ internal
+#if WPF
+ unsafe
+#endif
+ static partial class WriteableBitmapExtensions
+ {
+ #region Methods
+
+ #region Fill Shapes
+
+ #region Rectangle
+
+ /// <summary>
+ /// Draws a filled rectangle.
+ /// x2 has to be greater than x1 and y2 has to be greater than y1.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the bounding rectangle's left side.</param>
+ /// <param name="y1">The y-coordinate of the bounding rectangle's top side.</param>
+ /// <param name="x2">The x-coordinate of the bounding rectangle's right side.</param>
+ /// <param name="y2">The y-coordinate of the bounding rectangle's bottom side.</param>
+ /// <param name="color">The color.</param>
+ internal static void FillRectangle(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.FillRectangle(x1, y1, x2, y2, col);
+ }
+
+ /// <summary>
+ /// Draws a filled rectangle with or without alpha blending (default = false).
+ /// x2 has to be greater than x1 and y2 has to be greater than y1.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the bounding rectangle's left side.</param>
+ /// <param name="y1">The y-coordinate of the bounding rectangle's top side.</param>
+ /// <param name="x2">The x-coordinate of the bounding rectangle's right side.</param>
+ /// <param name="y2">The y-coordinate of the bounding rectangle's bottom side.</param>
+ /// <param name="color">The color.</param>
+ /// <param name="doAlphaBlend">True if alpha blending should be performed or false if not.</param>
+ internal static void FillRectangle(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int color, bool doAlphaBlend = false)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ var w = context.Width;
+ var h = context.Height;
+
+ int sa = ((color >> 24) & 0xff);
+ int sr = ((color >> 16) & 0xff);
+ int sg = ((color >> 8) & 0xff);
+ int sb = ((color) & 0xff);
+
+ bool noBlending = !doAlphaBlend || sa == 255;
+
+ var pixels = context.Pixels;
+
+ // Check boundaries
+ if ((x1 < 0 && x2 < 0) || (y1 < 0 && y2 < 0)
+ || (x1 >= w && x2 >= w) || (y1 >= h && y2 >= h))
+ {
+ return;
+ }
+
+ // Clamp boundaries
+ if (x1 < 0) { x1 = 0; }
+ if (y1 < 0) { y1 = 0; }
+ if (x2 < 0) { x2 = 0; }
+ if (y2 < 0) { y2 = 0; }
+ if (x1 > w) { x1 = w; }
+ if (y1 > h) { y1 = h; }
+ if (x2 > w) { x2 = w; }
+ if (y2 > h) { y2 = h; }
+
+ //swap values
+ if (y1 > y2)
+ {
+ y2 -= y1;
+ y1 += y2;
+ y2 = (y1 - y2);
+ }
+
+ // Fill first line
+ var startY = y1 * w;
+ var startYPlusX1 = startY + x1;
+ var endOffset = startY + x2;
+ for (var idx = startYPlusX1; idx < endOffset; idx++)
+ {
+ pixels[idx] = noBlending ? color : AlphaBlendColors(pixels[idx], sa, sr, sg, sb);
+ }
+
+ // Copy first line
+ var len = (x2 - x1);
+ var srcOffsetBytes = startYPlusX1 * SizeOfArgb;
+ var offset2 = y2 * w + x1;
+ for (var y = startYPlusX1 + w; y < offset2; y += w)
+ {
+ if (noBlending)
+ {
+ BitmapContext.BlockCopy(context, srcOffsetBytes, context, y * SizeOfArgb, len * SizeOfArgb);
+ continue;
+ }
+
+ // Alpha blend line
+ for (int i = 0; i < len; i++)
+ {
+ int idx = y + i;
+ pixels[idx] = AlphaBlendColors(pixels[idx], sa, sr, sg, sb);
+ }
+ }
+ }
+ }
+
+ private static int AlphaBlendColors(int pixel, int sa, int sr, int sg, int sb)
+ {
+ // Alpha blend
+ int destPixel = pixel;
+ int da = ((destPixel >> 24) & 0xff);
+ int dr = ((destPixel >> 16) & 0xff);
+ int dg = ((destPixel >> 8) & 0xff);
+ int db = ((destPixel) & 0xff);
+
+ destPixel = ((sa + (((da * (255 - sa)) * 0x8081) >> 23)) << 24) |
+ ((sr + (((dr * (255 - sa)) * 0x8081) >> 23)) << 16) |
+ ((sg + (((dg * (255 - sa)) * 0x8081) >> 23)) << 8) |
+ ((sb + (((db * (255 - sa)) * 0x8081) >> 23)));
+
+ return destPixel;
+ }
+
+
+ #endregion
+
+ #region Ellipse
+
+ /// <summary>
+ /// A Fast Bresenham Type Algorithm For Drawing filled ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf
+ /// x2 has to be greater than x1 and y2 has to be greater than y1.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the bounding rectangle's left side.</param>
+ /// <param name="y1">The y-coordinate of the bounding rectangle's top side.</param>
+ /// <param name="x2">The x-coordinate of the bounding rectangle's right side.</param>
+ /// <param name="y2">The y-coordinate of the bounding rectangle's bottom side.</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void FillEllipse(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.FillEllipse(x1, y1, x2, y2, col);
+ }
+
+ /// <summary>
+ /// A Fast Bresenham Type Algorithm For Drawing filled ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf
+ /// x2 has to be greater than x1 and y2 has to be greater than y1.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the bounding rectangle's left side.</param>
+ /// <param name="y1">The y-coordinate of the bounding rectangle's top side.</param>
+ /// <param name="x2">The x-coordinate of the bounding rectangle's right side.</param>
+ /// <param name="y2">The y-coordinate of the bounding rectangle's bottom side.</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void FillEllipse(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int color)
+ {
+ // Calc center and radius
+ int xr = (x2 - x1) >> 1;
+ int yr = (y2 - y1) >> 1;
+ int xc = x1 + xr;
+ int yc = y1 + yr;
+ bmp.FillEllipseCentered(xc, yc, xr, yr, color);
+ }
+
+ /// <summary>
+ /// A Fast Bresenham Type Algorithm For Drawing filled ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf
+ /// Uses a different parameter representation than DrawEllipse().
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="xc">The x-coordinate of the ellipses center.</param>
+ /// <param name="yc">The y-coordinate of the ellipses center.</param>
+ /// <param name="xr">The radius of the ellipse in x-direction.</param>
+ /// <param name="yr">The radius of the ellipse in y-direction.</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void FillEllipseCentered(this WriteableBitmap bmp, int xc, int yc, int xr, int yr, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.FillEllipseCentered(xc, yc, xr, yr, col);
+ }
+
+
+ /// <summary>
+ /// A Fast Bresenham Type Algorithm For Drawing filled ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf
+ /// With or without alpha blending (default = false).
+ /// Uses a different parameter representation than DrawEllipse().
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="xc">The x-coordinate of the ellipses center.</param>
+ /// <param name="yc">The y-coordinate of the ellipses center.</param>
+ /// <param name="xr">The radius of the ellipse in x-direction.</param>
+ /// <param name="yr">The radius of the ellipse in y-direction.</param>
+ /// <param name="color">The color for the line.</param>
+ /// <param name="doAlphaBlend">True if alpha blending should be performed or false if not.</param>
+ internal static void FillEllipseCentered(this WriteableBitmap bmp, int xc, int yc, int xr, int yr, int color, bool doAlphaBlend = false)
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ using (var context = bmp.GetBitmapContext())
+ {
+ var pixels = context.Pixels;
+ int w = context.Width;
+ int h = context.Height;
+
+ // Avoid endless loop
+ if (xr < 1 || yr < 1)
+ {
+ return;
+ }
+
+ // Skip completly outside objects
+ if (xc - xr >= w || xc + xr < 0 || yc - yr >= h || yc + yr < 0)
+ {
+ return;
+ }
+
+ // Init vars
+ int uh, lh, uy, ly, lx, rx;
+ int x = xr;
+ int y = 0;
+ int xrSqTwo = (xr * xr) << 1;
+ int yrSqTwo = (yr * yr) << 1;
+ int xChg = yr * yr * (1 - (xr << 1));
+ int yChg = xr * xr;
+ int err = 0;
+ int xStopping = yrSqTwo * xr;
+ int yStopping = 0;
+
+ int sa = ((color >> 24) & 0xff);
+ int sr = ((color >> 16) & 0xff);
+ int sg = ((color >> 8) & 0xff);
+ int sb = ((color) & 0xff);
+
+ bool noBlending = !doAlphaBlend || sa == 255;
+
+ // Draw first set of points counter clockwise where tangent line slope > -1.
+ while (xStopping >= yStopping)
+ {
+ // Draw 4 quadrant points at once
+ // Upper half
+ uy = yc + y;
+ // Lower half
+ ly = yc - y - 1;
+
+ // Clip
+ if (uy < 0) uy = 0;
+ if (uy >= h) uy = h - 1;
+ if (ly < 0) ly = 0;
+ if (ly >= h) ly = h - 1;
+
+ // Upper half
+ uh = uy * w;
+ // Lower half
+ lh = ly * w;
+
+ rx = xc + x;
+ lx = xc - x;
+
+ // Clip
+ if (rx < 0) rx = 0;
+ if (rx >= w) rx = w - 1;
+ if (lx < 0) lx = 0;
+ if (lx >= w) lx = w - 1;
+
+ // Draw line
+ if (noBlending)
+ {
+ for (int i = lx; i <= rx; i++)
+ {
+ pixels[i + uh] = color; // Quadrant II to I (Actually two octants)
+ pixels[i + lh] = color; // Quadrant III to IV
+ }
+ }
+ else
+ {
+ for (int i = lx; i <= rx; i++)
+ {
+ // Quadrant II to I (Actually two octants)
+ pixels[i + uh] = AlphaBlendColors(pixels[i + uh], sa, sr, sg, sb);
+
+ // Quadrant III to IV
+ pixels[i + lh] = AlphaBlendColors(pixels[i + lh], sa, sr, sg, sb);
+ }
+ }
+
+
+ y++;
+ yStopping += xrSqTwo;
+ err += yChg;
+ yChg += xrSqTwo;
+ if ((xChg + (err << 1)) > 0)
+ {
+ x--;
+ xStopping -= yrSqTwo;
+ err += xChg;
+ xChg += yrSqTwo;
+ }
+ }
+
+ // ReInit vars
+ x = 0;
+ y = yr;
+
+ // Upper half
+ uy = yc + y;
+ // Lower half
+ ly = yc - y;
+
+ // Clip
+ if (uy < 0) uy = 0;
+ if (uy >= h) uy = h - 1;
+ if (ly < 0) ly = 0;
+ if (ly >= h) ly = h - 1;
+
+ // Upper half
+ uh = uy * w;
+ // Lower half
+ lh = ly * w;
+
+ xChg = yr * yr;
+ yChg = xr * xr * (1 - (yr << 1));
+ err = 0;
+ xStopping = 0;
+ yStopping = xrSqTwo * yr;
+
+ // Draw second set of points clockwise where tangent line slope < -1.
+ while (xStopping <= yStopping)
+ {
+ // Draw 4 quadrant points at once
+ rx = xc + x;
+ lx = xc - x;
+
+ // Clip
+ if (rx < 0) rx = 0;
+ if (rx >= w) rx = w - 1;
+ if (lx < 0) lx = 0;
+ if (lx >= w) lx = w - 1;
+
+ // Draw line
+ if (noBlending)
+ {
+ for (int i = lx; i <= rx; i++)
+ {
+ pixels[i + uh] = color; // Quadrant II to I (Actually two octants)
+ pixels[i + lh] = color; // Quadrant III to IV
+ }
+ }
+ else
+ {
+ for (int i = lx; i <= rx; i++)
+ {
+ // Quadrant II to I (Actually two octants)
+ pixels[i + uh] = AlphaBlendColors(pixels[i + uh], sa, sr, sg, sb);
+
+ // Quadrant III to IV
+ pixels[i + lh] = AlphaBlendColors(pixels[i + lh], sa, sr, sg, sb);
+ }
+ }
+
+ x++;
+ xStopping += yrSqTwo;
+ err += xChg;
+ xChg += yrSqTwo;
+ if ((yChg + (err << 1)) > 0)
+ {
+ y--;
+ uy = yc + y; // Upper half
+ ly = yc - y; // Lower half
+ if (uy < 0) uy = 0; // Clip
+ if (uy >= h) uy = h - 1; // ...
+ if (ly < 0) ly = 0;
+ if (ly >= h) ly = h - 1;
+ uh = uy * w; // Upper half
+ lh = ly * w; // Lower half
+ yStopping -= xrSqTwo;
+ err += yChg;
+ yChg += xrSqTwo;
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region Polygon, Triangle, Quad
+
+ /// <summary>
+ /// Draws a filled polygon. Add the first point also at the end of the array if the line should be closed.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points of the polygon in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, ..., xn, yn).</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void FillPolygon(this WriteableBitmap bmp, int[] points, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.FillPolygon(points, col);
+ }
+
+ /// <summary>
+ /// Draws a filled polygon with or without alpha blending (default = false).
+ /// Add the first point also at the end of the array if the line should be closed.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points of the polygon in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, ..., xn, yn).</param>
+ /// <param name="color">The color for the line.</param>
+ /// <param name="doAlphaBlend">True if alpha blending should be performed or false if not.</param>
+ internal static void FillPolygon(this WriteableBitmap bmp, int[] points, int color, bool doAlphaBlend = false)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ int w = context.Width;
+ int h = context.Height;
+
+ int sa = ((color >> 24) & 0xff);
+ int sr = ((color >> 16) & 0xff);
+ int sg = ((color >> 8) & 0xff);
+ int sb = ((color) & 0xff);
+
+ bool noBlending = !doAlphaBlend || sa == 255;
+
+ var pixels = context.Pixels;
+ int pn = points.Length;
+ int pnh = points.Length >> 1;
+ int[] intersectionsX = new int[pnh];
+
+ // Find y min and max (slightly faster than scanning from 0 to height)
+ int yMin = h;
+ int yMax = 0;
+ for (int i = 1; i < pn; i += 2)
+ {
+ int py = points[i];
+ if (py < yMin) yMin = py;
+ if (py > yMax) yMax = py;
+ }
+ if (yMin < 0) yMin = 0;
+ if (yMax >= h) yMax = h - 1;
+
+
+ // Scan line from min to max
+ for (int y = yMin; y <= yMax; y++)
+ {
+ // Initial point x, y
+ float vxi = points[0];
+ float vyi = points[1];
+
+ // Find all intersections
+ // Based on http://alienryderflex.com/polygon_fill/
+ int intersectionCount = 0;
+ for (int i = 2; i < pn; i += 2)
+ {
+ // Next point x, y
+ float vxj = points[i];
+ float vyj = points[i + 1];
+
+ // Is the scanline between the two points
+ if (vyi < y && vyj >= y
+ || vyj < y && vyi >= y)
+ {
+ // Compute the intersection of the scanline with the edge (line between two points)
+ intersectionsX[intersectionCount++] = (int)(vxi + (y - vyi) / (vyj - vyi) * (vxj - vxi));
+ }
+ vxi = vxj;
+ vyi = vyj;
+ }
+
+ // Sort the intersections from left to right using Insertion sort
+ // It's faster than Array.Sort for this small data set
+ int t, j;
+ for (int i = 1; i < intersectionCount; i++)
+ {
+ t = intersectionsX[i];
+ j = i;
+ while (j > 0 && intersectionsX[j - 1] > t)
+ {
+ intersectionsX[j] = intersectionsX[j - 1];
+ j = j - 1;
+ }
+ intersectionsX[j] = t;
+ }
+
+ // Fill the pixels between the intersections
+ for (int i = 0; i < intersectionCount - 1; i += 2)
+ {
+ int x0 = intersectionsX[i];
+ int x1 = intersectionsX[i + 1];
+
+ // Check boundary
+ if (x1 > 0 && x0 < w)
+ {
+ if (x0 < 0) x0 = 0;
+ if (x1 >= w) x1 = w - 1;
+
+ // Fill the pixels
+ for (int x = x0; x <= x1; x++)
+ {
+ int idx = y * w + x;
+
+ pixels[idx] = noBlending ? color : AlphaBlendColors(pixels[idx], sa, sr, sg, sb);
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ #region Multiple (possibly nested) Polygons
+ /// <summary>
+ /// Helper class for storing the data of an edge.
+ /// </summary>
+ /// <remarks>
+ /// The following is always true:
+ /// <code>edge.StartY &lt; edge.EndY</code>
+ /// </remarks>
+ private class Edge : IComparable<Edge>
+ {
+ /// <summary>
+ /// X coordinate of starting point of edge.
+ /// </summary>
+ internal readonly int StartX;
+ /// <summary>
+ /// Y coordinate of starting point of edge.
+ /// </summary>
+ internal readonly int StartY;
+ /// <summary>
+ /// X coordinate of ending point of edge.
+ /// </summary>
+ internal readonly int EndX;
+ /// <summary>
+ /// Y coordinate of ending point of edge.
+ /// </summary>
+ internal readonly int EndY;
+ /// <summary>
+ /// The slope of the edge.
+ /// </summary>
+ internal readonly float Sloap;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="Edge"/> class.
+ /// </summary>
+ /// <remarks>
+ /// The constructor may swap start and end point to fulfill the guarantees of this class.
+ /// </remarks>
+ /// <param name="startX">The X coordinate of the start point of the edge.</param>
+ /// <param name="startY">The Y coordinate of the start point of the edge.</param>
+ /// <param name="endX">The X coordinate of the end point of the edge.</param>
+ /// <param name="endY">The Y coordinate of the end point of the edge.</param>
+ internal Edge(int startX, int startY, int endX, int endY)
+ {
+ if (startY > endY)
+ {
+ // swap direction
+ StartX = endX;
+ StartY = endY;
+ EndX = startX;
+ EndY = startY;
+ }
+ else
+ {
+ StartX = startX;
+ StartY = startY;
+ EndX = endX;
+ EndY = endY;
+ }
+ Sloap = (EndX - StartX) / (float)(EndY - StartY);
+ }
+
+ /// <summary>
+ /// Compares the current object with another object of the same type.
+ /// </summary>
+ /// <returns>
+ /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has the following meanings: Value Meaning Less than zero This object is less than the <paramref name="other"/> parameter.Zero This object is equal to <paramref name="other"/>. Greater than zero This object is greater than <paramref name="other"/>.
+ /// </returns>
+ /// <param name="other">An object to compare with this object.</param>
+ public int CompareTo(Edge other)
+ {
+ return StartY == other.StartY
+ ? StartX.CompareTo(other.StartX)
+ : StartY.CompareTo(other.StartY);
+ }
+ }
+
+ /// <summary>
+ /// Draws filled polygons using even-odd filling, therefore allowing for holes.
+ /// </summary>
+ /// <remarks>
+ /// Polygons are implicitly closed if necessary.
+ /// </remarks>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="polygons">Array of polygons.
+ /// The different polygons are identified by the first index,
+ /// while the points of each polygon are in x and y pairs indexed by the second index,
+ /// therefore the array is interpreted as (x1, y1, x2, y2, ..., xn, yn).
+ /// </param>
+ /// <param name="color">The color for the polygon.</param>
+ internal static void FillPolygonsEvenOdd(this WriteableBitmap bmp, int[][] polygons, Color color)
+ {
+ var col = ConvertColor(color);
+ FillPolygonsEvenOdd(bmp, polygons, col);
+ }
+
+ /// <summary>
+ /// Draws filled polygons using even-odd filling, therefore allowing for holes.
+ /// </summary>
+ /// <remarks>
+ /// Polygons are implicitly closed if necessary.
+ /// </remarks>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="polygons">Array of polygons.
+ /// The different polygons are identified by the first index,
+ /// while the points of each polygon are in x and y pairs indexed by the second index,
+ /// therefore the array is interpreted as (x1, y1, x2, y2, ..., xn, yn).
+ /// </param>
+ /// <param name="color">The color for the polygon.</param>
+ internal static void FillPolygonsEvenOdd(this WriteableBitmap bmp, int[][] polygons, int color)
+ {
+ #region Algorithm
+
+ // Algorithm:
+ // This is using a scanline algorithm which is kept similar to the one the FillPolygon() method is using,
+ // but it is only comparing the edges with the scanline which are currently intersecting the line.
+ // To be able to do this it first builds a list of edges (var edges) from the polygons, which is then
+ // sorted via by their minimal y coordinate. During the scanline run only the edges which can intersect
+ // the current scanline are intersected to get the X coordinate of the intersection. These edges are kept
+ // in the list named currentEdges.
+ // Especially for larger sane(*) polygons this is a lot faster then the algorithm used in the FillPolygon()
+ // method which is always comparing all edges with the scan line.
+ // And sorry: the constraint to explicitly make the polygon close before using the FillPolygon() method is
+ // stupid, as filling an unclosed polygon is not very useful.
+ //
+ // (*) sane: the polygons in the FillSample speed test are not sane, because they contain a lot of very long
+ // nearly vertical lines. A sane example would be a letter 'o', in which case the currentEdges list is
+ // containing no more than 4 edges at any moment, regardless of the smoothness of the rendering of the
+ // letter into two polygons.
+
+ #endregion
+
+ int polygonCount = polygons.Length;
+ if (polygonCount == 0)
+ {
+ return;
+ }
+ // could use single polygon fill if count is 1, but it the algorithm used there is slower (at least for larger polygons)
+
+
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ int w = context.Width;
+ int h = context.Height;
+ var pixels = context.Pixels;
+
+ // Register edges, and find y max
+ List<Edge> edges = new List<Edge>();
+ int yMax = 0;
+ foreach (int[] points in polygons)
+ {
+ int pn = points.Length;
+ if (pn < 6)
+ {
+ // sanity check: don't care for lines or points or empty polygons
+ continue;
+ }
+ int lastX;
+ int lastY;
+ int start;
+ if (points[0] != points[pn - 2]
+ || points[1] != points[pn - 1])
+ {
+ start = 0;
+ lastX = points[pn - 2];
+ lastY = points[pn - 1];
+ }
+ else
+ {
+ start = 2;
+ lastX = points[0];
+ lastY = points[1];
+ }
+ for (int i = start; i < pn; i += 2)
+ {
+ int px = points[i];
+ int py = points[i + 1];
+ if (py != lastY)
+ {
+ Edge edge = new Edge(lastX, lastY, px, py);
+ if (edge.StartY < h && edge.EndY >= 0)
+ {
+ if (edge.EndY > yMax) yMax = edge.EndY;
+ edges.Add(edge);
+ }
+ }
+ lastX = px;
+ lastY = py;
+ }
+ }
+ if (edges.Count == 0)
+ {
+ // sanity check
+ return;
+ }
+
+ if (yMax >= h) yMax = h - 1;
+
+ edges.Sort();
+ int yMin = edges[0].StartY;
+ if (yMin < 0) yMin = 0;
+
+ int[] intersectionsX = new int[edges.Count];
+
+ LinkedList<Edge> currentEdges = new LinkedList<Edge>();
+ int e = 0;
+
+ // Scan line from min to max
+ for (int y = yMin; y <= yMax; y++)
+ {
+ // Remove edges no longer intersecting
+ LinkedListNode<Edge> node = currentEdges.First;
+ while (node != null)
+ {
+ LinkedListNode<Edge> nextNode = node.Next;
+ Edge edge = node.Value;
+ if (edge.EndY <= y)
+ {
+ // using = here because the connecting edge will be added next
+ // remove edge
+ currentEdges.Remove(node);
+ }
+ node = nextNode;
+ }
+ // Add edges starting to intersect
+ while (e < edges.Count &&
+ edges[e].StartY <= y)
+ {
+ currentEdges.AddLast(edges[e]);
+ ++e;
+ }
+ // Calculate intersections
+ int intersectionCount = 0;
+ foreach (Edge currentEdge in currentEdges)
+ {
+ intersectionsX[intersectionCount++] =
+ (int)(currentEdge.StartX + (y - currentEdge.StartY) * currentEdge.Sloap);
+ }
+
+ // Sort the intersections from left to right using Insertion sort
+ // It's faster than Array.Sort for this small data set
+ for (int i = 1; i < intersectionCount; i++)
+ {
+ int t = intersectionsX[i];
+ int j = i;
+ while (j > 0 && intersectionsX[j - 1] > t)
+ {
+ intersectionsX[j] = intersectionsX[j - 1];
+ j = j - 1;
+ }
+ intersectionsX[j] = t;
+ }
+
+ // Fill the pixels between the intersections
+ for (int i = 0; i < intersectionCount - 1; i += 2)
+ {
+ int x0 = intersectionsX[i];
+ int x1 = intersectionsX[i + 1];
+
+ if (x0 < 0) x0 = 0;
+ if (x1 >= w) x1 = w - 1;
+ if (x1 < x0)
+ {
+ continue;
+ }
+
+ // Fill the pixels
+ int index = y * w + x0;
+ for (int x = x0; x <= x1; x++)
+ {
+ pixels[index++] = color;
+ }
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ /// <summary>
+ /// Draws a filled quad.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the 1st point.</param>
+ /// <param name="y1">The y-coordinate of the 1st point.</param>
+ /// <param name="x2">The x-coordinate of the 2nd point.</param>
+ /// <param name="y2">The y-coordinate of the 2nd point.</param>
+ /// <param name="x3">The x-coordinate of the 3rd point.</param>
+ /// <param name="y3">The y-coordinate of the 3rd point.</param>
+ /// <param name="x4">The x-coordinate of the 4th point.</param>
+ /// <param name="y4">The y-coordinate of the 4th point.</param>
+ /// <param name="color">The color.</param>
+ internal static void FillQuad(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.FillQuad(x1, y1, x2, y2, x3, y3, x4, y4, col);
+ }
+
+ /// <summary>
+ /// Draws a filled quad.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the 1st point.</param>
+ /// <param name="y1">The y-coordinate of the 1st point.</param>
+ /// <param name="x2">The x-coordinate of the 2nd point.</param>
+ /// <param name="y2">The y-coordinate of the 2nd point.</param>
+ /// <param name="x3">The x-coordinate of the 3rd point.</param>
+ /// <param name="y3">The y-coordinate of the 3rd point.</param>
+ /// <param name="x4">The x-coordinate of the 4th point.</param>
+ /// <param name="y4">The y-coordinate of the 4th point.</param>
+ /// <param name="color">The color.</param>
+ internal static void FillQuad(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int color)
+ {
+ bmp.FillPolygon(new int[] { x1, y1, x2, y2, x3, y3, x4, y4, x1, y1 }, color);
+ }
+
+ /// <summary>
+ /// Draws a filled triangle.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the 1st point.</param>
+ /// <param name="y1">The y-coordinate of the 1st point.</param>
+ /// <param name="x2">The x-coordinate of the 2nd point.</param>
+ /// <param name="y2">The y-coordinate of the 2nd point.</param>
+ /// <param name="x3">The x-coordinate of the 3rd point.</param>
+ /// <param name="y3">The y-coordinate of the 3rd point.</param>
+ /// <param name="color">The color.</param>
+ internal static void FillTriangle(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int x3, int y3, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.FillTriangle(x1, y1, x2, y2, x3, y3, col);
+ }
+
+ /// <summary>
+ /// Draws a filled triangle.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the 1st point.</param>
+ /// <param name="y1">The y-coordinate of the 1st point.</param>
+ /// <param name="x2">The x-coordinate of the 2nd point.</param>
+ /// <param name="y2">The y-coordinate of the 2nd point.</param>
+ /// <param name="x3">The x-coordinate of the 3rd point.</param>
+ /// <param name="y3">The y-coordinate of the 3rd point.</param>
+ /// <param name="color">The color.</param>
+ internal static void FillTriangle(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int x3, int y3, int color)
+ {
+ bmp.FillPolygon(new int[] { x1, y1, x2, y2, x3, y3, x1, y1 }, color);
+ }
+
+ #endregion
+
+ #region Beziér
+
+ /// <summary>
+ /// Draws a filled, cubic Beziér spline defined by start, end and two control points.
+ /// </summary>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="cx1">The x-coordinate of the 1st control point.</param>
+ /// <param name="cy1">The y-coordinate of the 1st control point.</param>
+ /// <param name="cx2">The x-coordinate of the 2nd control point.</param>
+ /// <param name="cy2">The y-coordinate of the 2nd control point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color.</param>
+ /// <param name="context">The context with the pixels.</param>
+ /// <param name="w">The width of the bitmap.</param>
+ /// <param name="h">The height of the bitmap.</param>
+ [Obsolete("Obsolete, left for compatibility reasons. Please use List<int> ComputeBezierPoints(int x1, int y1, int cx1, int cy1, int cx2, int cy2, int x2, int y2) instead.")]
+ private static List<int> ComputeBezierPoints(int x1, int y1, int cx1, int cy1, int cx2, int cy2, int x2, int y2, int color, BitmapContext context, int w, int h)
+ {
+ return ComputeBezierPoints(x1, y1, cx1, cy1, cx2, cy2, x2, y1);
+ }
+
+ /// <summary>
+ /// Draws a filled, cubic Beziér spline defined by start, end and two control points.
+ /// </summary>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="cx1">The x-coordinate of the 1st control point.</param>
+ /// <param name="cy1">The y-coordinate of the 1st control point.</param>
+ /// <param name="cx2">The x-coordinate of the 2nd control point.</param>
+ /// <param name="cy2">The y-coordinate of the 2nd control point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ private static List<int> ComputeBezierPoints(int x1, int y1, int cx1, int cy1, int cx2, int cy2, int x2, int y2)
+ {
+ // Determine distances between controls points (bounding rect) to find the optimal stepsize
+ var minX = Math.Min(x1, Math.Min(cx1, Math.Min(cx2, x2)));
+ var minY = Math.Min(y1, Math.Min(cy1, Math.Min(cy2, y2)));
+ var maxX = Math.Max(x1, Math.Max(cx1, Math.Max(cx2, x2)));
+ var maxY = Math.Max(y1, Math.Max(cy1, Math.Max(cy2, y2)));
+
+ // Get slope
+ var lenx = maxX - minX;
+ var len = maxY - minY;
+ if (lenx > len)
+ {
+ len = lenx;
+ }
+
+ // Prevent division by zero
+ var list = new List<int>();
+ if (len != 0)
+ {
+ // Init vars
+ var step = StepFactor / len;
+ int tx = x1;
+ int ty = y1;
+
+ // Interpolate
+ for (var t = 0f; t <= 1; t += step)
+ {
+ var tSq = t * t;
+ var t1 = 1 - t;
+ var t1Sq = t1 * t1;
+
+ tx = (int)(t1 * t1Sq * x1 + 3 * t * t1Sq * cx1 + 3 * t1 * tSq * cx2 + t * tSq * x2);
+ ty = (int)(t1 * t1Sq * y1 + 3 * t * t1Sq * cy1 + 3 * t1 * tSq * cy2 + t * tSq * y2);
+
+ list.Add(tx);
+ list.Add(ty);
+ }
+
+ // Prevent rounding gap
+ list.Add(x2);
+ list.Add(y2);
+ }
+ return list;
+ }
+
+ /// <summary>
+ /// Draws a series of filled, cubic Beziér splines each defined by start, end and two control points.
+ /// The ending point of the previous curve is used as starting point for the next.
+ /// Therefore the initial curve needs four points and the subsequent 3 (2 control and 1 end point).
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, cx1, cy1, cx2, cy2, x2, y2, cx3, cx4 ..., xn, yn).</param>
+ /// <param name="color">The color for the spline.</param>
+ internal static void FillBeziers(this WriteableBitmap bmp, int[] points, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.FillBeziers(points, col);
+ }
+
+ /// <summary>
+ /// Draws a series of filled, cubic Beziér splines each defined by start, end and two control points.
+ /// The ending point of the previous curve is used as starting point for the next.
+ /// Therefore the initial curve needs four points and the subsequent 3 (2 control and 1 end point).
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, cx1, cy1, cx2, cy2, x2, y2, cx3, cx4 ..., xn, yn).</param>
+ /// <param name="color">The color for the spline.</param>
+ internal static void FillBeziers(this WriteableBitmap bmp, int[] points, int color)
+ {
+ // Compute Bezier curve
+ int x1 = points[0];
+ int y1 = points[1];
+ int x2, y2;
+ var list = new List<int>();
+ for (int i = 2; i + 5 < points.Length; i += 6)
+ {
+ x2 = points[i + 4];
+ y2 = points[i + 5];
+ list.AddRange(ComputeBezierPoints(x1, y1, points[i], points[i + 1], points[i + 2], points[i + 3], x2, y2));
+ x1 = x2;
+ y1 = y2;
+ }
+
+ // Fill
+ bmp.FillPolygon(list.ToArray(), color);
+ }
+
+ #endregion
+
+ #region Cardinal
+
+ /// <summary>
+ /// Computes the discrete segment points of a Cardinal spline (cubic) defined by four control points.
+ /// </summary>
+ /// <param name="x1">The x-coordinate of the 1st control point.</param>
+ /// <param name="y1">The y-coordinate of the 1st control point.</param>
+ /// <param name="x2">The x-coordinate of the 2nd control point.</param>
+ /// <param name="y2">The y-coordinate of the 2nd control point.</param>
+ /// <param name="x3">The x-coordinate of the 3rd control point.</param>
+ /// <param name="y3">The y-coordinate of the 3rd control point.</param>
+ /// <param name="x4">The x-coordinate of the 4th control point.</param>
+ /// <param name="y4">The y-coordinate of the 4th control point.</param>
+ /// <param name="tension">The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line.</param>
+ /// <param name="color">The color.</param>
+ /// <param name="context">The context with the pixels.</param>
+ /// <param name="w">The width of the bitmap.</param>
+ /// <param name="h">The height of the bitmap.</param>
+ [Obsolete("Obsolete, left for compatibility reasons. Please use List<int> ComputeSegmentPoints(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, float tension) instead.")]
+ private static List<int> ComputeSegmentPoints(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, float tension, int color, BitmapContext context, int w, int h)
+ {
+ return ComputeSegmentPoints(x1, y1, x2, y2, x3, y3, x4, y4, tension);
+ }
+
+ /// <summary>
+ /// Computes the discrete segment points of a Cardinal spline (cubic) defined by four control points.
+ /// </summary>
+ /// <param name="x1">The x-coordinate of the 1st control point.</param>
+ /// <param name="y1">The y-coordinate of the 1st control point.</param>
+ /// <param name="x2">The x-coordinate of the 2nd control point.</param>
+ /// <param name="y2">The y-coordinate of the 2nd control point.</param>
+ /// <param name="x3">The x-coordinate of the 3rd control point.</param>
+ /// <param name="y3">The y-coordinate of the 3rd control point.</param>
+ /// <param name="x4">The x-coordinate of the 4th control point.</param>
+ /// <param name="y4">The y-coordinate of the 4th control point.</param>
+ /// <param name="tension">The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line.</param>
+ private static List<int> ComputeSegmentPoints(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, float tension)
+ {
+ // Determine distances between controls points (bounding rect) to find the optimal stepsize
+ var minX = Math.Min(x1, Math.Min(x2, Math.Min(x3, x4)));
+ var minY = Math.Min(y1, Math.Min(y2, Math.Min(y3, y4)));
+ var maxX = Math.Max(x1, Math.Max(x2, Math.Max(x3, x4)));
+ var maxY = Math.Max(y1, Math.Max(y2, Math.Max(y3, y4)));
+
+ // Get slope
+ var lenx = maxX - minX;
+ var len = maxY - minY;
+ if (lenx > len)
+ {
+ len = lenx;
+ }
+
+ // Prevent division by zero
+ var list = new List<int>();
+ if (len != 0)
+ {
+ // Init vars
+ var step = StepFactor / len;
+
+ // Calculate factors
+ var sx1 = tension * (x3 - x1);
+ var sy1 = tension * (y3 - y1);
+ var sx2 = tension * (x4 - x2);
+ var sy2 = tension * (y4 - y2);
+ var ax = sx1 + sx2 + 2 * x2 - 2 * x3;
+ var ay = sy1 + sy2 + 2 * y2 - 2 * y3;
+ var bx = -2 * sx1 - sx2 - 3 * x2 + 3 * x3;
+ var by = -2 * sy1 - sy2 - 3 * y2 + 3 * y3;
+
+ // Interpolate
+ for (var t = 0f; t <= 1; t += step)
+ {
+ var tSq = t * t;
+
+ int tx = (int)(ax * tSq * t + bx * tSq + sx1 * t + x2);
+ int ty = (int)(ay * tSq * t + by * tSq + sy1 * t + y2);
+
+ list.Add(tx);
+ list.Add(ty);
+ }
+
+ // Prevent rounding gap
+ list.Add(x3);
+ list.Add(y3);
+ }
+ return list;
+ }
+
+ /// <summary>
+ /// Draws a filled Cardinal spline (cubic) defined by a point collection.
+ /// The cardinal spline passes through each point in the collection.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn).</param>
+ /// <param name="tension">The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line.</param>
+ /// <param name="color">The color for the spline.</param>
+ internal static void FillCurve(this WriteableBitmap bmp, int[] points, float tension, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.FillCurve(points, tension, col);
+ }
+
+ /// <summary>
+ /// Draws a filled Cardinal spline (cubic) defined by a point collection.
+ /// The cardinal spline passes through each point in the collection.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn).</param>
+ /// <param name="tension">The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line.</param>
+ /// <param name="color">The color for the spline.</param>
+ internal static void FillCurve(this WriteableBitmap bmp, int[] points, float tension, int color)
+ {
+ // First segment
+ var list = ComputeSegmentPoints(points[0], points[1], points[0], points[1], points[2], points[3], points[4],
+ points[5], tension);
+
+ // Middle segments
+ int i;
+ for (i = 2; i < points.Length - 4; i += 2)
+ {
+ list.AddRange(ComputeSegmentPoints(points[i - 2], points[i - 1], points[i], points[i + 1], points[i + 2],
+ points[i + 3], points[i + 4], points[i + 5], tension));
+ }
+
+ // Last segment
+ list.AddRange(ComputeSegmentPoints(points[i - 2], points[i - 1], points[i], points[i + 1], points[i + 2],
+ points[i + 3], points[i + 2], points[i + 3], tension));
+
+ // Fill
+ bmp.FillPolygon(list.ToArray(), color);
+ }
+
+ /// <summary>
+ /// Draws a filled, closed Cardinal spline (cubic) defined by a point collection.
+ /// The cardinal spline passes through each point in the collection.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn).</param>
+ /// <param name="tension">The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line.</param>
+ /// <param name="color">The color for the spline.</param>
+ internal static void FillCurveClosed(this WriteableBitmap bmp, int[] points, float tension, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.FillCurveClosed(points, tension, col);
+ }
+
+ /// <summary>
+ /// Draws a filled, closed Cardinal spline (cubic) defined by a point collection.
+ /// The cardinal spline passes through each point in the collection.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn).</param>
+ /// <param name="tension">The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line.</param>
+ /// <param name="color">The color for the spline.</param>
+ internal static void FillCurveClosed(this WriteableBitmap bmp, int[] points, float tension, int color)
+ {
+ int pn = points.Length;
+
+ // First segment
+ var list = ComputeSegmentPoints(points[pn - 2], points[pn - 1], points[0], points[1], points[2], points[3],
+ points[4], points[5], tension);
+
+ // Middle segments
+ int i;
+ for (i = 2; i < pn - 4; i += 2)
+ {
+ list.AddRange(ComputeSegmentPoints(points[i - 2], points[i - 1], points[i], points[i + 1],
+ points[i + 2], points[i + 3], points[i + 4], points[i + 5], tension));
+ }
+
+ // Last segment
+ list.AddRange(ComputeSegmentPoints(points[i - 2], points[i - 1], points[i], points[i + 1], points[i + 2],
+ points[i + 3], points[0], points[1], tension));
+
+ // Last-to-First segment
+ list.AddRange(ComputeSegmentPoints(points[i], points[i + 1], points[i + 2], points[i + 3], points[0],
+ points[1], points[2], points[3], tension));
+
+ // Fill
+ bmp.FillPolygon(list.ToArray(), color);
+ }
+
+ #endregion
+
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapFilterExtensions.cs b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapFilterExtensions.cs
new file mode 100644
index 000000000..6afd6b72a
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapFilterExtensions.cs
@@ -0,0 +1,423 @@
+#region Header
+//
+// Project: WriteableBitmapEx - WriteableBitmap extensions
+// Description: Collection of transformation extension methods for the WriteableBitmap class.
+//
+// Changed by: $Author: unknown $
+// Changed on: $Date: 2015-03-05 18:18:24 +0100 (Do, 05 Mrz 2015) $
+// Changed in: $Revision: 113191 $
+// Project: $URL: https://writeablebitmapex.svn.codeplex.com/svn/trunk/Source/WriteableBitmapEx/WriteableBitmapFilterExtensions.cs $
+// Id: $Id: WriteableBitmapFilterExtensions.cs 113191 2015-03-05 17:18:24Z unknown $
+//
+//
+// Copyright © 2009-2015 Rene Schulte and WriteableBitmapEx Contributors
+//
+// This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;)
+//
+#endregion
+
+using System;
+
+#if NETFX_CORE
+namespace Windows.UI.Xaml.Media.Imaging
+#else
+namespace System.Windows.Media.Imaging
+#endif
+{
+ /// <summary>
+ /// Collection of filter / convolution extension methods for the WriteableBitmap class.
+ /// </summary>
+ internal
+#if WPF
+ unsafe
+#endif
+ static partial class WriteableBitmapExtensions
+ {
+ #region Kernels
+
+ ///<summary>
+ /// Gaussian blur kernel with the size 5x5
+ ///</summary>
+ internal static int[,] KernelGaussianBlur5x5 = {
+ {1, 4, 7, 4, 1},
+ {4, 16, 26, 16, 4},
+ {7, 26, 41, 26, 7},
+ {4, 16, 26, 16, 4},
+ {1, 4, 7, 4, 1}
+ };
+
+ ///<summary>
+ /// Gaussian blur kernel with the size 3x3
+ ///</summary>
+ internal static int[,] KernelGaussianBlur3x3 = {
+ {16, 26, 16},
+ {26, 41, 26},
+ {16, 26, 16}
+ };
+
+ ///<summary>
+ /// Sharpen kernel with the size 3x3
+ ///</summary>
+ internal static int[,] KernelSharpen3x3 = {
+ { 0, -2, 0},
+ {-2, 11, -2},
+ { 0, -2, 0}
+ };
+
+ #endregion
+
+ #region Methods
+
+ #region Convolute
+
+ /// <summary>
+ /// Creates a new filtered WriteableBitmap.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="kernel">The kernel used for convolution.</param>
+ /// <returns>A new WriteableBitmap that is a filtered version of the input.</returns>
+ internal static WriteableBitmap Convolute(this WriteableBitmap bmp, int[,] kernel)
+ {
+ var kernelFactorSum = 0;
+ foreach (var b in kernel)
+ {
+ kernelFactorSum += b;
+ }
+ return bmp.Convolute(kernel, kernelFactorSum, 0);
+ }
+
+ /// <summary>
+ /// Creates a new filtered WriteableBitmap.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="kernel">The kernel used for convolution.</param>
+ /// <param name="kernelFactorSum">The factor used for the kernel summing.</param>
+ /// <param name="kernelOffsetSum">The offset used for the kernel summing.</param>
+ /// <returns>A new WriteableBitmap that is a filtered version of the input.</returns>
+ internal static WriteableBitmap Convolute(this WriteableBitmap bmp, int[,] kernel, int kernelFactorSum, int kernelOffsetSum)
+ {
+ var kh = kernel.GetUpperBound(0) + 1;
+ var kw = kernel.GetUpperBound(1) + 1;
+
+ if ((kw & 1) == 0)
+ {
+ throw new InvalidOperationException("Kernel width must be odd!");
+ }
+ if ((kh & 1) == 0)
+ {
+ throw new InvalidOperationException("Kernel height must be odd!");
+ }
+
+ using (var srcContext = bmp.GetBitmapContext(ReadWriteMode.ReadOnly))
+ {
+ var w = srcContext.Width;
+ var h = srcContext.Height;
+ var result = BitmapFactory.New(w, h);
+
+ using (var resultContext = result.GetBitmapContext())
+ {
+ var pixels = srcContext.Pixels;
+ var resultPixels = resultContext.Pixels;
+ var index = 0;
+ var kwh = kw >> 1;
+ var khh = kh >> 1;
+
+ for (var y = 0; y < h; y++)
+ {
+ for (var x = 0; x < w; x++)
+ {
+ var a = 0;
+ var r = 0;
+ var g = 0;
+ var b = 0;
+
+ for (var kx = -kwh; kx <= kwh; kx++)
+ {
+ var px = kx + x;
+ // Repeat pixels at borders
+ if (px < 0)
+ {
+ px = 0;
+ }
+ else if (px >= w)
+ {
+ px = w - 1;
+ }
+
+ for (var ky = -khh; ky <= khh; ky++)
+ {
+ var py = ky + y;
+ // Repeat pixels at borders
+ if (py < 0)
+ {
+ py = 0;
+ }
+ else if (py >= h)
+ {
+ py = h - 1;
+ }
+
+ var col = pixels[py * w + px];
+ var k = kernel[ky + kwh, kx + khh];
+ a += ((col >> 24) & 0x000000FF) * k;
+ r += ((col >> 16) & 0x000000FF) * k;
+ g += ((col >> 8) & 0x000000FF) * k;
+ b += ((col) & 0x000000FF) * k;
+ }
+ }
+
+ var ta = ((a / kernelFactorSum) + kernelOffsetSum);
+ var tr = ((r / kernelFactorSum) + kernelOffsetSum);
+ var tg = ((g / kernelFactorSum) + kernelOffsetSum);
+ var tb = ((b / kernelFactorSum) + kernelOffsetSum);
+
+ // Clamp to byte boundaries
+ var ba = (byte)((ta > 255) ? 255 : ((ta < 0) ? 0 : ta));
+ var br = (byte)((tr > 255) ? 255 : ((tr < 0) ? 0 : tr));
+ var bg = (byte)((tg > 255) ? 255 : ((tg < 0) ? 0 : tg));
+ var bb = (byte)((tb > 255) ? 255 : ((tb < 0) ? 0 : tb));
+
+ resultPixels[index++] = (ba << 24) | (br << 16) | (bg << 8) | (bb);
+ }
+ }
+ return result;
+ }
+ }
+ }
+
+ #endregion
+
+ #region Invert
+
+ /// <summary>
+ /// Creates a new inverted WriteableBitmap and returns it.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <returns>The new inverted WriteableBitmap.</returns>
+ internal static WriteableBitmap Invert(this WriteableBitmap bmp)
+ {
+ using (var srcContext = bmp.GetBitmapContext())
+ {
+ var result = BitmapFactory.New(srcContext.Width, srcContext.Height);
+ using (var resultContext = result.GetBitmapContext())
+ {
+ var rp = resultContext.Pixels;
+ var p = srcContext.Pixels;
+ var length = srcContext.Length;
+
+ for (var i = 0; i < length; i++)
+ {
+ // Extract
+ var c = p[i];
+ var a = (c >> 24) & 0x000000FF;
+ var r = (c >> 16) & 0x000000FF;
+ var g = (c >> 8) & 0x000000FF;
+ var b = (c) & 0x000000FF;
+
+ // Invert
+ r = 255 - r;
+ g = 255 - g;
+ b = 255 - b;
+
+ // Set
+ rp[i] = (a << 24) | (r << 16) | (g << 8) | b;
+ }
+
+ return result;
+ }
+ }
+ }
+
+ #endregion
+
+ #region Color transformations
+
+ /// <summary>
+ /// Creates a new WriteableBitmap which is the grayscaled version of this one and returns it. The gray values are equal to the brightness values.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <returns>The new gray WriteableBitmap.</returns>
+ internal static WriteableBitmap Gray(this WriteableBitmap bmp)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ var nWidth = context.Width;
+ var nHeight = context.Height;
+ var px = context.Pixels;
+ var result = BitmapFactory.New(nWidth, nHeight);
+
+ using (var dest = result.GetBitmapContext())
+ {
+ var rp = dest.Pixels;
+ var len = context.Length;
+ for (var i = 0; i < len; i++)
+ {
+ // Extract
+ var c = px[i];
+ var a = (c >> 24) & 0x000000FF;
+ var r = (c >> 16) & 0x000000FF;
+ var g = (c >> 8) & 0x000000FF;
+ var b = (c) & 0x000000FF;
+
+ // Convert to gray with constant factors 0.2126, 0.7152, 0.0722
+ var gray = (r * 6966 + g * 23436 + b * 2366) >> 15;
+ r = g = b = gray;
+
+ // Set
+ rp[i] = (a << 24) | (r << 16) | (g << 8) | b;
+ }
+ }
+
+ return result;
+ }
+ }
+
+ /// <summary>
+ /// Creates a new WriteableBitmap which is contrast adjusted version of this one and returns it.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="level">Level of contrast as double. [-255.0, 255.0] </param>
+ /// <returns>The new WriteableBitmap.</returns>
+ internal static WriteableBitmap AdjustContrast(this WriteableBitmap bmp, double level)
+ {
+ var factor = (int)((259.0 * (level + 255.0)) / (255.0 * (259.0 - level)) * 255.0);
+
+ using (var context = bmp.GetBitmapContext())
+ {
+ var nWidth = context.Width;
+ var nHeight = context.Height;
+ var px = context.Pixels;
+ var result = BitmapFactory.New(nWidth, nHeight);
+
+ using (var dest = result.GetBitmapContext())
+ {
+ var rp = dest.Pixels;
+ var len = context.Length;
+ for (var i = 0; i < len; i++)
+ {
+ // Extract
+ var c = px[i];
+ var a = (c >> 24) & 0x000000FF;
+ var r = (c >> 16) & 0x000000FF;
+ var g = (c >> 8) & 0x000000FF;
+ var b = (c) & 0x000000FF;
+
+ // Adjust contrast based on computed factor
+ r = ((factor * (r - 128)) >> 8) + 128;
+ g = ((factor * (g - 128)) >> 8) + 128;
+ b = ((factor * (b - 128)) >> 8) + 128;
+
+ // Clamp
+ r = r < 0 ? 0 : r > 255 ? 255 : r;
+ g = g < 0 ? 0 : g > 255 ? 255 : g;
+ b = b < 0 ? 0 : b > 255 ? 255 : b;
+
+ // Set
+ rp[i] = (a << 24) | (r << 16) | (g << 8) | b;
+ }
+ }
+
+ return result;
+ }
+ }
+
+ /// <summary>
+ /// Creates a new WriteableBitmap which is brightness adjusted version of this one and returns it.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="nLevel">Level of contrast as double. [-255.0, 255.0] </param>
+ /// <returns>The new WriteableBitmap.</returns>
+ internal static WriteableBitmap AdjustBrightness(this WriteableBitmap bmp, int nLevel)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ var nWidth = context.Width;
+ var nHeight = context.Height;
+ var px = context.Pixels;
+ var result = BitmapFactory.New(nWidth, nHeight);
+
+ using (var dest = result.GetBitmapContext())
+ {
+ var rp = dest.Pixels;
+ var len = context.Length;
+ for (var i = 0; i < len; i++)
+ {
+ // Extract
+ var c = px[i];
+ var a = (c >> 24) & 0x000000FF;
+ var r = (c >> 16) & 0x000000FF;
+ var g = (c >> 8) & 0x000000FF;
+ var b = (c) & 0x000000FF;
+
+ // Brightness adjustment
+ r += nLevel;
+ g += nLevel;
+ b += nLevel;
+
+ // Clamp
+ r = r < 0 ? 0 : r > 255 ? 255 : r;
+ g = g < 0 ? 0 : g > 255 ? 255 : g;
+ b = b < 0 ? 0 : b > 255 ? 255 : b;
+
+ // Set
+ rp[i] = (a << 24) | (r << 16) | (g << 8) | b;
+ }
+ }
+
+ return result;
+ }
+ }
+
+ /// <summary>
+ /// Creates a new WriteableBitmap which is gamma adjusted version of this one and returns it.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="value">Value of gamma for adjustment. Original is 1.0.</param>
+ /// <returns>The new WriteableBitmap.</returns>
+ internal static WriteableBitmap AdjustGamma(this WriteableBitmap bmp, double value)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ var nWidth = context.Width;
+ var nHeight = context.Height;
+ var px = context.Pixels;
+ var result = BitmapFactory.New(nWidth, nHeight);
+
+ using (var dest = result.GetBitmapContext())
+ {
+ var rp = dest.Pixels;
+ var gammaCorrection = 1.0 / value;
+ var len = context.Length;
+ for (var i = 0; i < len; i++)
+ {
+ // Extract
+ var c = px[i];
+ var a = (c >> 24) & 0x000000FF;
+ var r = (c >> 16) & 0x000000FF;
+ var g = (c >> 8) & 0x000000FF;
+ var b = (c) & 0x000000FF;
+
+ // Gamma adjustment
+ r = (int)(255.0 * Math.Pow((r / 255.0), gammaCorrection));
+ g = (int)(255.0 * Math.Pow((g / 255.0), gammaCorrection));
+ b = (int)(255.0 * Math.Pow((b / 255.0), gammaCorrection));
+
+ // Clamps
+ r = r < 0 ? 0 : r > 255 ? 255 : r;
+ g = g < 0 ? 0 : g > 255 ? 255 : g;
+ b = b < 0 ? 0 : b > 255 ? 255 : b;
+
+ // Set
+ rp[i] = (a << 24) | (r << 16) | (g << 8) | b;
+ }
+ }
+
+ return result;
+ }
+ }
+
+ #endregion
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapLineExtensions.cs b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapLineExtensions.cs
new file mode 100644
index 000000000..91a83f4d8
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapLineExtensions.cs
@@ -0,0 +1,1316 @@
+#region Header
+//
+// Project: WriteableBitmapEx - WriteableBitmap extensions
+// Description: Collection of draw line extension and helper methods for the WriteableBitmap class.
+//
+// Changed by: $Author: unknown $
+// Changed on: $Date: 2015-02-24 20:36:41 +0100 (Di, 24 Feb 2015) $
+// Changed in: $Revision: 112951 $
+// Project: $URL: https://writeablebitmapex.svn.codeplex.com/svn/trunk/Source/WriteableBitmapEx/WriteableBitmapTransformationExtensions.cs $
+// Id: $Id: WriteableBitmapTransformationExtensions.cs 112951 2015-02-24 19:36:41Z unknown $
+//
+//
+// Copyright © 2009-2015 Rene Schulte and WriteableBitmapEx Contributors
+//
+// This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;)
+//
+#endregion
+
+using System;
+using System.Collections.Generic;
+
+#if NETFX_CORE
+using Windows.Foundation;
+
+namespace Windows.UI.Xaml.Media.Imaging
+#else
+namespace System.Windows.Media.Imaging
+#endif
+{
+ internal
+#if WPF
+ unsafe
+#endif
+ static partial class WriteableBitmapExtensions
+ {
+ #region Normal line
+
+ /// <summary>
+ /// Draws a colored line by connecting two points using the Bresenham algorithm.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void DrawLineBresenham(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.DrawLineBresenham(x1, y1, x2, y2, col);
+ }
+
+ /// <summary>
+ /// Draws a colored line by connecting two points using the Bresenham algorithm.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void DrawLineBresenham(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ int w = context.Width;
+ int h = context.Height;
+ var pixels = context.Pixels;
+
+ // Distance start and end point
+ int dx = x2 - x1;
+ int dy = y2 - y1;
+
+ // Determine sign for direction x
+ int incx = 0;
+ if (dx < 0)
+ {
+ dx = -dx;
+ incx = -1;
+ }
+ else if (dx > 0)
+ {
+ incx = 1;
+ }
+
+ // Determine sign for direction y
+ int incy = 0;
+ if (dy < 0)
+ {
+ dy = -dy;
+ incy = -1;
+ }
+ else if (dy > 0)
+ {
+ incy = 1;
+ }
+
+ // Which gradient is larger
+ int pdx, pdy, odx, ody, es, el;
+ if (dx > dy)
+ {
+ pdx = incx;
+ pdy = 0;
+ odx = incx;
+ ody = incy;
+ es = dy;
+ el = dx;
+ }
+ else
+ {
+ pdx = 0;
+ pdy = incy;
+ odx = incx;
+ ody = incy;
+ es = dx;
+ el = dy;
+ }
+
+ // Init start
+ int x = x1;
+ int y = y1;
+ int error = el >> 1;
+ if (y < h && y >= 0 && x < w && x >= 0)
+ {
+ pixels[y * w + x] = color;
+ }
+
+ // Walk the line!
+ for (int i = 0; i < el; i++)
+ {
+ // Update error term
+ error -= es;
+
+ // Decide which coord to use
+ if (error < 0)
+ {
+ error += el;
+ x += odx;
+ y += ody;
+ }
+ else
+ {
+ x += pdx;
+ y += pdy;
+ }
+
+ // Set pixel
+ if (y < h && y >= 0 && x < w && x >= 0)
+ {
+ pixels[y * w + x] = color;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Draws a colored line by connecting two points using a DDA algorithm (Digital Differential Analyzer).
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void DrawLineDDA(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.DrawLineDDA(x1, y1, x2, y2, col);
+ }
+
+ /// <summary>
+ /// Draws a colored line by connecting two points using a DDA algorithm (Digital Differential Analyzer).
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void DrawLineDDA(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ int w = context.Width;
+ int h = context.Height;
+ var pixels = context.Pixels;
+
+ // Distance start and end point
+ int dx = x2 - x1;
+ int dy = y2 - y1;
+
+ // Determine slope (absolute value)
+ int len = dy >= 0 ? dy : -dy;
+ int lenx = dx >= 0 ? dx : -dx;
+ if (lenx > len)
+ {
+ len = lenx;
+ }
+
+ // Prevent division by zero
+ if (len != 0)
+ {
+ // Init steps and start
+ float incx = dx / (float)len;
+ float incy = dy / (float)len;
+ float x = x1;
+ float y = y1;
+
+ // Walk the line!
+ for (int i = 0; i < len; i++)
+ {
+ if (y < h && y >= 0 && x < w && x >= 0)
+ {
+ pixels[(int)y * w + (int)x] = color;
+ }
+ x += incx;
+ y += incy;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Draws a colored line by connecting two points using an optimized DDA.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void DrawLine(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.DrawLine(x1, y1, x2, y2, col);
+ }
+
+ /// <summary>
+ /// Draws a colored line by connecting two points using an optimized DDA.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void DrawLine(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ DrawLine(context, context.Width, context.Height, x1, y1, x2, y2, color);
+ }
+ }
+
+ /// <summary>
+ /// Draws a colored line by connecting two points using an optimized DDA.
+ /// Uses the pixels array and the width directly for best performance.
+ /// </summary>
+ /// <param name="context">The context containing the pixels as int RGBA value.</param>
+ /// <param name="pixelWidth">The width of one scanline in the pixels array.</param>
+ /// <param name="pixelHeight">The height of the bitmap.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void DrawLine(BitmapContext context, int pixelWidth, int pixelHeight, int x1, int y1, int x2, int y2, int color)
+ {
+ // Perform cohen-sutherland clipping if either point is out of the viewport
+ if (!CohenSutherlandLineClip(new Rect(0, 0, pixelWidth, pixelHeight), ref x1, ref y1, ref x2, ref y2)) return;
+
+ var pixels = context.Pixels;
+
+ // Distance start and end point
+ int dx = x2 - x1;
+ int dy = y2 - y1;
+
+ const int PRECISION_SHIFT = 8;
+
+ // Determine slope (absolute value)
+ int lenX, lenY;
+ if (dy >= 0)
+ {
+ lenY = dy;
+ }
+ else
+ {
+ lenY = -dy;
+ }
+
+ if (dx >= 0)
+ {
+ lenX = dx;
+ }
+ else
+ {
+ lenX = -dx;
+ }
+
+ if (lenX > lenY)
+ { // x increases by +/- 1
+ if (dx < 0)
+ {
+ int t = x1;
+ x1 = x2;
+ x2 = t;
+ t = y1;
+ y1 = y2;
+ y2 = t;
+ }
+
+ // Init steps and start
+ int incy = (dy << PRECISION_SHIFT) / dx;
+
+ int y1s = y1 << PRECISION_SHIFT;
+ int y2s = y2 << PRECISION_SHIFT;
+ int hs = pixelHeight << PRECISION_SHIFT;
+
+ if (y1 < y2)
+ {
+ if (y1 >= pixelHeight || y2 < 0)
+ {
+ return;
+ }
+ if (y1s < 0)
+ {
+ if (incy == 0)
+ {
+ return;
+ }
+ int oldy1s = y1s;
+ // Find lowest y1s that is greater or equal than 0.
+ y1s = incy - 1 + ((y1s + 1) % incy);
+ x1 += (y1s - oldy1s) / incy;
+ }
+ if (y2s >= hs)
+ {
+ if (incy != 0)
+ {
+ // Find highest y2s that is less or equal than ws - 1.
+ // y2s = y1s + n * incy. Find n.
+ y2s = hs - 1 - (hs - 1 - y1s) % incy;
+ x2 = x1 + (y2s - y1s) / incy;
+ }
+ }
+ }
+ else
+ {
+ if (y2 >= pixelHeight || y1 < 0)
+ {
+ return;
+ }
+ if (y1s >= hs)
+ {
+ if (incy == 0)
+ {
+ return;
+ }
+ int oldy1s = y1s;
+ // Find highest y1s that is less or equal than ws - 1.
+ // y1s = oldy1s + n * incy. Find n.
+ y1s = hs - 1 + (incy - (hs - 1 - oldy1s) % incy);
+ x1 += (y1s - oldy1s) / incy;
+ }
+ if (y2s < 0)
+ {
+ if (incy != 0)
+ {
+ // Find lowest y2s that is greater or equal than 0.
+ // y2s = y1s + n * incy. Find n.
+ y2s = y1s % incy;
+ x2 = x1 + (y2s - y1s) / incy;
+ }
+ }
+ }
+
+ if (x1 < 0)
+ {
+ y1s -= incy * x1;
+ x1 = 0;
+ }
+ if (x2 >= pixelWidth)
+ {
+ x2 = pixelWidth - 1;
+ }
+
+ int ys = y1s;
+
+ // Walk the line!
+ int y = ys >> PRECISION_SHIFT;
+ int previousY = y;
+ int index = x1 + y * pixelWidth;
+ int k = incy < 0 ? 1 - pixelWidth : 1 + pixelWidth;
+ for (int x = x1; x <= x2; ++x)
+ {
+ pixels[index] = color;
+ ys += incy;
+ y = ys >> PRECISION_SHIFT;
+ if (y != previousY)
+ {
+ previousY = y;
+ index += k;
+ }
+ else
+ {
+ ++index;
+ }
+ }
+ }
+ else
+ {
+ // Prevent division by zero
+ if (lenY == 0)
+ {
+ return;
+ }
+ if (dy < 0)
+ {
+ int t = x1;
+ x1 = x2;
+ x2 = t;
+ t = y1;
+ y1 = y2;
+ y2 = t;
+ }
+
+ // Init steps and start
+ int x1s = x1 << PRECISION_SHIFT;
+ int x2s = x2 << PRECISION_SHIFT;
+ int ws = pixelWidth << PRECISION_SHIFT;
+
+ int incx = (dx << PRECISION_SHIFT) / dy;
+
+ if (x1 < x2)
+ {
+ if (x1 >= pixelWidth || x2 < 0)
+ {
+ return;
+ }
+ if (x1s < 0)
+ {
+ if (incx == 0)
+ {
+ return;
+ }
+ int oldx1s = x1s;
+ // Find lowest x1s that is greater or equal than 0.
+ x1s = incx - 1 + ((x1s + 1) % incx);
+ y1 += (x1s - oldx1s) / incx;
+ }
+ if (x2s >= ws)
+ {
+ if (incx != 0)
+ {
+ // Find highest x2s that is less or equal than ws - 1.
+ // x2s = x1s + n * incx. Find n.
+ x2s = ws - 1 - (ws - 1 - x1s) % incx;
+ y2 = y1 + (x2s - x1s) / incx;
+ }
+ }
+ }
+ else
+ {
+ if (x2 >= pixelWidth || x1 < 0)
+ {
+ return;
+ }
+ if (x1s >= ws)
+ {
+ if (incx == 0)
+ {
+ return;
+ }
+ int oldx1s = x1s;
+ // Find highest x1s that is less or equal than ws - 1.
+ // x1s = oldx1s + n * incx. Find n.
+ x1s = ws - 1 + (incx - (ws - 1 - oldx1s) % incx);
+ y1 += (x1s - oldx1s) / incx;
+ }
+ if (x2s < 0)
+ {
+ if (incx != 0)
+ {
+ // Find lowest x2s that is greater or equal than 0.
+ // x2s = x1s + n * incx. Find n.
+ x2s = x1s % incx;
+ y2 = y1 + (x2s - x1s) / incx;
+ }
+ }
+ }
+
+ if (y1 < 0)
+ {
+ x1s -= incx * y1;
+ y1 = 0;
+ }
+ if (y2 >= pixelHeight)
+ {
+ y2 = pixelHeight - 1;
+ }
+
+ int index = x1s;
+ int indexBaseValue = y1 * pixelWidth;
+
+ // Walk the line!
+ var inc = (pixelWidth << PRECISION_SHIFT) + incx;
+ for (int y = y1; y <= y2; ++y)
+ {
+ pixels[indexBaseValue + (index >> PRECISION_SHIFT)] = color;
+ index += inc;
+ }
+ }
+ }
+ #endregion
+
+ #region Penned line
+
+ /// <summary>
+ /// Bitfields used to partition the space into 9 regions
+ /// </summary>
+ private const byte INSIDE = 0; // 0000
+ private const byte LEFT = 1; // 0001
+ private const byte RIGHT = 2; // 0010
+ private const byte BOTTOM = 4; // 0100
+ private const byte TOP = 8; // 1000
+
+ /// <summary>
+ /// Draws a line using a pen / stamp for the line
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap containing the pixels as int RGBA value.</param>
+ /// <param name="w">The width of one scanline in the pixels array.</param>
+ /// <param name="h">The height of the bitmap.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="penBmp">The pen bitmap.</param>
+ internal static void DrawLinePenned(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, WriteableBitmap penBmp)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ using (var penContext = penBmp.GetBitmapContext())
+ {
+ DrawLinePenned(context, bmp.PixelWidth, bmp.PixelHeight, x1, y1, x2, y2, penContext);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Draws a line using a pen / stamp for the line
+ /// </summary>
+ /// <param name="context">The context containing the pixels as int RGBA value.</param>
+ /// <param name="w">The width of one scanline in the pixels array.</param>
+ /// <param name="h">The height of the bitmap.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="pen">The pen context.</param>
+ internal static void DrawLinePenned(BitmapContext context, int w, int h, int x1, int y1, int x2, int y2, BitmapContext pen)
+ {
+ // Edge case where lines that went out of vertical bounds clipped instead of disappearing
+ if((y1 < 0 && y2 < 0) || (y1 > h && y2 > h))
+ return;
+
+ if (x1 == x2 && y1 == y2)
+ return;
+
+ // Perform cohen-sutherland clipping if either point is out of the viewport
+ if (!CohenSutherlandLineClip(new Rect(0, 0, w, h), ref x1, ref y1, ref x2, ref y2)) return;
+
+ int size = pen.WriteableBitmap.PixelWidth;
+ int pw = size;
+ var srcRect = new Rect(0, 0, size, size);
+
+ // Distance start and end point
+ int dx = x2 - x1;
+ int dy = y2 - y1;
+
+ // Determine sign for direction x
+ int incx = 0;
+ if (dx < 0)
+ {
+ dx = -dx;
+ incx = -1;
+ }
+ else if (dx > 0)
+ {
+ incx = 1;
+ }
+
+ // Determine sign for direction y
+ int incy = 0;
+ if (dy < 0)
+ {
+ dy = -dy;
+ incy = -1;
+ }
+ else if (dy > 0)
+ {
+ incy = 1;
+ }
+
+ // Which gradient is larger
+ int pdx, pdy, odx, ody, es, el;
+ if (dx > dy)
+ {
+ pdx = incx;
+ pdy = 0;
+ odx = incx;
+ ody = incy;
+ es = dy;
+ el = dx;
+ }
+ else
+ {
+ pdx = 0;
+ pdy = incy;
+ odx = incx;
+ ody = incy;
+ es = dx;
+ el = dy;
+ }
+
+ // Init start
+ int x = x1;
+ int y = y1;
+ int error = el >> 1;
+
+ var destRect = new Rect(x, y, size, size);
+
+ if (y < h && y >= 0 && x < w && x >= 0)
+ {
+ //Blit(context.WriteableBitmap, new Rect(x,y,3,3), pen.WriteableBitmap, new Rect(0,0,3,3));
+ Blit(context, w, h, destRect, pen, srcRect, pw);
+ //pixels[y * w + x] = color;
+ }
+
+ // Walk the line!
+ for (int i = 0; i < el; i++)
+ {
+ // Update error term
+ error -= es;
+
+ // Decide which coord to use
+ if (error < 0)
+ {
+ error += el;
+ x += odx;
+ y += ody;
+ }
+ else
+ {
+ x += pdx;
+ y += pdy;
+ }
+
+ // Set pixel
+ if (y < h && y >= 0 && x < w && x >= 0)
+ {
+ //Blit(context, w, h, destRect, pen, srcRect, pw);
+ Blit(context, w, h, new Rect(x, y, size, size), pen, srcRect, pw);
+ //Blit(context.WriteableBitmap, destRect, pen.WriteableBitmap, srcRect);
+ //pixels[y * w + x] = color;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Compute the bit code for a point (x, y) using the clip rectangle
+ /// bounded diagonally by (xmin, ymin), and (xmax, ymax)
+ /// ASSUME THAT xmax , xmin , ymax and ymin are global constants.
+ /// </summary>
+ /// <param name="extents">The extents.</param>
+ /// <param name="x">The x.</param>
+ /// <param name="y">The y.</param>
+ /// <returns></returns>
+ private static byte ComputeOutCode(Rect extents, double x, double y)
+ {
+ // initialized as being inside of clip window
+ byte code = INSIDE;
+
+ if (x < extents.Left) // to the left of clip window
+ code |= LEFT;
+ else if (x > extents.Right) // to the right of clip window
+ code |= RIGHT;
+ if (y > extents.Bottom) // below the clip window
+ code |= BOTTOM;
+ else if (y < extents.Top) // above the clip window
+ code |= TOP;
+
+ return code;
+ }
+
+ #endregion
+
+ #region Anti-alias line
+
+ /// <summary>
+ /// Draws an anti-aliased, alpha blended, colored line by connecting two points using Wu's antialiasing algorithm
+ /// Uses the pixels array and the width directly for best performance.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x0.</param>
+ /// <param name="y1">The y0.</param>
+ /// <param name="x2">The x1.</param>
+ /// <param name="y2">The y1.</param>
+ /// <param name="sa">Alpha color component</param>
+ /// <param name="sr">Premultiplied red color component</param>
+ /// <param name="sg">Premultiplied green color component</param>
+ /// <param name="sb">Premultiplied blue color component</param>
+ internal static void DrawLineWu(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int sa, int sr, int sg, int sb)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ DrawLineWu(context, bmp.PixelWidth, bmp.PixelHeight, x1, y1, x2, y2, sa, sr, sg, sb);
+ }
+ }
+
+ /// <summary>
+ /// Draws an anti-aliased, alpha-blended, colored line by connecting two points using Wu's antialiasing algorithm
+ /// Uses the pixels array and the width directly for best performance.
+ /// </summary>
+ /// <param name="context">An array containing the pixels as int RGBA value.</param>
+ /// <param name="pixelWidth">The width of one scanline in the pixels array.</param>
+ /// <param name="pixelHeight">The height of the bitmap.</param>
+ /// <param name="x1">The x0.</param>
+ /// <param name="y1">The y0.</param>
+ /// <param name="x2">The x1.</param>
+ /// <param name="y2">The y1.</param>
+ /// <param name="sa">Alpha color component</param>
+ /// <param name="sr">Premultiplied red color component</param>
+ /// <param name="sg">Premultiplied green color component</param>
+ /// <param name="sb">Premultiplied blue color component</param>
+ internal static void DrawLineWu(BitmapContext context, int pixelWidth, int pixelHeight, int x1, int y1, int x2, int y2, int sa, int sr, int sg, int sb)
+ {
+ // Perform cohen-sutherland clipping if either point is out of the viewport
+ if (!CohenSutherlandLineClip(new Rect(0, 0, pixelWidth, pixelHeight), ref x1, ref y1, ref x2, ref y2)) return;
+
+ var pixels = context.Pixels;
+
+ const ushort INTENSITY_BITS = 8;
+ const short NUM_LEVELS = 1 << INTENSITY_BITS; // 256
+ // mask used to compute 1-value by doing (value XOR mask)
+ const ushort WEIGHT_COMPLEMENT_MASK = NUM_LEVELS - 1; // 255
+ // # of bits by which to shift ErrorAcc to get intensity level
+ const ushort INTENSITY_SHIFT = (ushort)(16 - INTENSITY_BITS); // 8
+
+ ushort ErrorAdj, ErrorAcc;
+ ushort ErrorAccTemp, Weighting;
+ short DeltaX, DeltaY, XDir;
+ int tmp;
+ // ensure line runs from top to bottom
+ if (y1 > y2)
+ {
+ tmp = y1; y1 = y2; y2 = tmp;
+ tmp = x1; x1 = x2; x2 = tmp;
+ }
+
+ // draw initial pixel, which is always intersected by line to it's at 100% intensity
+ pixels[y1 * pixelWidth + x1] = AlphaBlend(sa, sr, sg, sb, pixels[y1 * pixelWidth + x1]);
+ //bitmap.SetPixel(X0, Y0, BaseColor);
+
+ DeltaX = (short)(x2 - x1);
+ if (DeltaX >= 0)
+ {
+ XDir = 1;
+ }
+ else
+ {
+ XDir = -1;
+ DeltaX = (short)-DeltaX; /* make DeltaX positive */
+ }
+
+ // Special-case horizontal, vertical, and diagonal lines, which
+ // require no weighting because they go right through the center of
+ // every pixel; also avoids division by zero later
+ DeltaY = (short)(y2 - y1);
+ if (DeltaY == 0) // if horizontal line
+ {
+ while (DeltaX-- != 0)
+ {
+ x1 += XDir;
+ pixels[y1 * pixelWidth + x1] = AlphaBlend(sa, sr, sg, sb, pixels[y1 * pixelWidth + x1]);
+ }
+ return;
+ }
+
+ if (DeltaX == 0) // if vertical line
+ {
+ do
+ {
+ y1++;
+ pixels[y1 * pixelWidth + x1] = AlphaBlend(sa, sr, sg, sb, pixels[y1 * pixelWidth + x1]);
+ } while (--DeltaY != 0);
+ return;
+ }
+
+ if (DeltaX == DeltaY) // diagonal line
+ {
+ do
+ {
+ x1 += XDir;
+ y1++;
+ pixels[y1 * pixelWidth + x1] = AlphaBlend(sa, sr, sg, sb, pixels[y1 * pixelWidth + x1]);
+ } while (--DeltaY != 0);
+ return;
+ }
+
+ // Line is not horizontal, diagonal, or vertical
+ ErrorAcc = 0; // initialize the line error accumulator to 0
+
+ // Is this an X-major or Y-major line?
+ if (DeltaY > DeltaX)
+ {
+ // Y-major line; calculate 16-bit fixed-point fractional part of a
+ // pixel that X advances each time Y advances 1 pixel, truncating the
+ // result so that we won't overrun the endpoint along the X axis
+ ErrorAdj = (ushort)(((ulong)DeltaX << 16) / (ulong)DeltaY);
+
+ // Draw all pixels other than the first and last
+ while (--DeltaY != 0)
+ {
+ ErrorAccTemp = ErrorAcc; // remember current accumulated error
+ ErrorAcc += ErrorAdj; // calculate error for next pixel
+ if (ErrorAcc <= ErrorAccTemp)
+ {
+ // The error accumulator turned over, so advance the X coord */
+ x1 += XDir;
+ }
+ y1++; /* Y-major, so always advance Y */
+ // The IntensityBits most significant bits of ErrorAcc give us the
+ // intensity weighting for this pixel, and the complement of the
+ // weighting for the paired pixel
+ Weighting = (ushort)(ErrorAcc >> INTENSITY_SHIFT);
+
+ int weight = Weighting ^ WEIGHT_COMPLEMENT_MASK;
+ pixels[y1 * pixelWidth + x1] = AlphaBlend(sa, (sr * weight) >> 8, (sg * weight) >> 8, (sb * weight) >> 8, pixels[y1 * pixelWidth + x1]);
+
+ weight = Weighting;
+ pixels[y1 * pixelWidth + x1 + XDir] = AlphaBlend(sa, (sr * weight) >> 8, (sg * weight) >> 8, (sb * weight) >> 8, pixels[y1 * pixelWidth + x1 + XDir]);
+
+ //bitmap.SetPixel(X0, Y0, 255 - (BaseColor + Weighting));
+ //bitmap.SetPixel(X0 + XDir, Y0, 255 - (BaseColor + (Weighting ^ WeightingComplementMask)));
+ }
+
+ // Draw the final pixel, which is always exactly intersected by the line and so needs no weighting
+ pixels[y2 * pixelWidth + x2] = AlphaBlend(sa, sr, sg, sb, pixels[y2 * pixelWidth + x2]);
+ //bitmap.SetPixel(X1, Y1, BaseColor);
+ return;
+ }
+ // It's an X-major line; calculate 16-bit fixed-point fractional part of a
+ // pixel that Y advances each time X advances 1 pixel, truncating the
+ // result to avoid overrunning the endpoint along the X axis */
+ ErrorAdj = (ushort)(((ulong)DeltaY << 16) / (ulong)DeltaX);
+
+ // Draw all pixels other than the first and last
+ while (--DeltaX != 0)
+ {
+ ErrorAccTemp = ErrorAcc; // remember current accumulated error
+ ErrorAcc += ErrorAdj; // calculate error for next pixel
+ if (ErrorAcc <= ErrorAccTemp) // if error accumulator turned over
+ {
+ // advance the Y coord
+ y1++;
+ }
+ x1 += XDir; // X-major, so always advance X
+ // The IntensityBits most significant bits of ErrorAcc give us the
+ // intensity weighting for this pixel, and the complement of the
+ // weighting for the paired pixel
+ Weighting = (ushort)(ErrorAcc >> INTENSITY_SHIFT);
+
+ int weight = Weighting ^ WEIGHT_COMPLEMENT_MASK;
+ pixels[y1 * pixelWidth + x1] = AlphaBlend(sa, (sr * weight) >> 8, (sg * weight) >> 8, (sb * weight) >> 8, pixels[y1 * pixelWidth + x1]);
+
+ weight = Weighting;
+ pixels[(y1 + 1) * pixelWidth + x1] = AlphaBlend(sa, (sr * weight) >> 8, (sg * weight) >> 8, (sb * weight) >> 8, pixels[(y1 + 1) * pixelWidth + x1]);
+
+ //bitmap.SetPixel(X0, Y0, 255 - (BaseColor + Weighting));
+ //bitmap.SetPixel(X0, Y0 + 1,
+ // 255 - (BaseColor + (Weighting ^ WeightingComplementMask)));
+ }
+ // Draw the final pixel, which is always exactly intersected by the line and thus needs no weighting
+ pixels[y2 * pixelWidth + x2] = AlphaBlend(sa, sr, sg, sb, pixels[y2 * pixelWidth + x2]);
+ //bitmap.SetPixel(X1, Y1, BaseColor);
+ }
+
+ /// <summary>
+ /// Draws an anti-aliased line with a desired stroke thickness
+ /// <param name="context">The context containing the pixels as int RGBA value.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color for the line.</param>
+ /// <param name="strokeThickness">The stroke thickness of the line.</param>
+ /// </summary>
+ internal static void DrawLineAa(BitmapContext context, int pixelWidth, int pixelHeight, int x1, int y1, int x2, int y2, int color, int strokeThickness)
+ {
+ AAWidthLine(pixelWidth, pixelHeight, context, x1, y1, x2, y2, strokeThickness, color);
+ }
+
+ /// <summary>
+ /// Draws an anti-aliased line with a desired stroke thickness
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color for the line.</param>
+ /// <param name="strokeThickness">The stroke thickness of the line.</param>
+ /// </summary>
+ internal static void DrawLineAa(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int color, int strokeThickness)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ AAWidthLine(bmp.PixelWidth, bmp.PixelHeight, context, x1, y1, x2, y2, strokeThickness, color);
+ }
+ }
+
+ /// <summary>
+ /// Draws an anti-aliased line with a desired stroke thickness
+ /// <param name="context">The context containing the pixels as int RGBA value.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color for the line.</param>
+ /// <param name="strokeThickness">The stroke thickness of the line.</param>
+ /// </summary>
+ internal static void DrawLineAa(BitmapContext context, int pixelWidth, int pixelHeight, int x1, int y1, int x2, int y2, Color color, int strokeThickness)
+ {
+ var col = ConvertColor(color);
+ AAWidthLine(pixelWidth, pixelHeight, context, x1, y1, x2, y2, strokeThickness, col);
+ }
+
+ /// <summary>
+ /// Draws an anti-aliased line with a desired stroke thickness
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color for the line.</param>
+ /// <param name="strokeThickness">The stroke thickness of the line.</param>
+ /// </summary>
+ internal static void DrawLineAa(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, Color color, int strokeThickness)
+ {
+ var col = ConvertColor(color);
+ using (var context = bmp.GetBitmapContext())
+ {
+ AAWidthLine(bmp.PixelWidth, bmp.PixelHeight, context, x1, y1, x2, y2, strokeThickness, col);
+ }
+ }
+
+ /// <summary>
+ /// Draws an anti-aliased line, using an optimized version of Gupta-Sproull algorithm
+ /// From http://nokola.com/blog/post/2010/10/14/Anti-aliased-Lines-And-Optimizing-Code-for-Windows-Phone-7e28093First-Look.aspx
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color for the line.</param>
+ /// </summary>
+ internal static void DrawLineAa(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.DrawLineAa(x1, y1, x2, y2, col);
+ }
+
+ /// <summary>
+ /// Draws an anti-aliased line, using an optimized version of Gupta-Sproull algorithm
+ /// From http://nokola.com/blog/post/2010/10/14/Anti-aliased-Lines-And-Optimizing-Code-for-Windows-Phone-7e28093First-Look.aspx
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color for the line.</param>
+ /// </summary>
+ internal static void DrawLineAa(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ DrawLineAa(context, context.Width, context.Height, x1, y1, x2, y2, color);
+ }
+ }
+
+ /// <summary>
+ /// Draws an anti-aliased line, using an optimized version of Gupta-Sproull algorithm
+ /// From http://nokola.com/blog/post/2010/10/14/Anti-aliased-Lines-And-Optimizing-Code-for-Windows-Phone-7e28093First-Look.aspx
+ /// <param name="context">The context containing the pixels as int RGBA value.</param>
+ /// <param name="pixelWidth">The width of one scanline in the pixels array.</param>
+ /// <param name="pixelHeight">The height of the bitmap.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color for the line.</param>
+ /// </summary>
+ internal static void DrawLineAa(BitmapContext context, int pixelWidth, int pixelHeight, int x1, int y1, int x2, int y2, int color)
+ {
+ if ((x1 == x2) && (y1 == y2)) return; // edge case causing invDFloat to overflow, found by Shai Rubinshtein
+
+ // Perform cohen-sutherland clipping if either point is out of the viewport
+ if (!CohenSutherlandLineClip(new Rect(0, 0, pixelWidth, pixelHeight), ref x1, ref y1, ref x2, ref y2)) return;
+
+ if (x1 < 1) x1 = 1;
+ if (x1 > pixelWidth - 2) x1 = pixelWidth - 2;
+ if (y1 < 1) y1 = 1;
+ if (y1 > pixelHeight - 2) y1 = pixelHeight - 2;
+
+ if (x2 < 1) x2 = 1;
+ if (x2 > pixelWidth - 2) x2 = pixelWidth - 2;
+ if (y2 < 1) y2 = 1;
+ if (y2 > pixelHeight - 2) y2 = pixelHeight - 2;
+
+ var addr = y1 * pixelWidth + x1;
+ var dx = x2 - x1;
+ var dy = y2 - y1;
+
+ int du;
+ int dv;
+ int u;
+ int v;
+ int uincr;
+ int vincr;
+
+ // Extract color
+ var a = (color >> 24) & 0xFF;
+ var srb = (uint)(color & 0x00FF00FF);
+ var sg = (uint)((color >> 8) & 0xFF);
+
+ // By switching to (u,v), we combine all eight octants
+ int adx = dx, ady = dy;
+ if (dx < 0) adx = -dx;
+ if (dy < 0) ady = -dy;
+
+ if (adx > ady)
+ {
+ du = adx;
+ dv = ady;
+ u = x2;
+ v = y2;
+ uincr = 1;
+ vincr = pixelWidth;
+ if (dx < 0) uincr = -uincr;
+ if (dy < 0) vincr = -vincr;
+ }
+ else
+ {
+ du = ady;
+ dv = adx;
+ u = y2;
+ v = x2;
+ uincr = pixelWidth;
+ vincr = 1;
+ if (dy < 0) uincr = -uincr;
+ if (dx < 0) vincr = -vincr;
+ }
+
+ var uend = u + du;
+ var d = (dv << 1) - du; // Initial value as in Bresenham's
+ var incrS = dv << 1; // &#916;d for straight increments
+ var incrD = (dv - du) << 1; // &#916;d for diagonal increments
+
+ var invDFloat = 1.0 / (4.0 * Math.Sqrt(du * du + dv * dv)); // Precomputed inverse denominator
+ var invD2DuFloat = 0.75 - 2.0 * (du * invDFloat); // Precomputed constant
+
+ const int PRECISION_SHIFT = 10; // result distance should be from 0 to 1 << PRECISION_SHIFT, mapping to a range of 0..1
+ const int PRECISION_MULTIPLIER = 1 << PRECISION_SHIFT;
+ var invD = (int)(invDFloat * PRECISION_MULTIPLIER);
+ var invD2Du = (int)(invD2DuFloat * PRECISION_MULTIPLIER * a);
+ var zeroDot75 = (int)(0.75 * PRECISION_MULTIPLIER * a);
+
+ var invDMulAlpha = invD * a;
+ var duMulInvD = du * invDMulAlpha; // used to help optimize twovdu * invD
+ var dMulInvD = d * invDMulAlpha; // used to help optimize twovdu * invD
+ //int twovdu = 0; // Numerator of distance; starts at 0
+ var twovduMulInvD = 0; // since twovdu == 0
+ var incrSMulInvD = incrS * invDMulAlpha;
+ var incrDMulInvD = incrD * invDMulAlpha;
+
+ do
+ {
+ AlphaBlendNormalOnPremultiplied(context, addr, (zeroDot75 - twovduMulInvD) >> PRECISION_SHIFT, srb, sg);
+ AlphaBlendNormalOnPremultiplied(context, addr + vincr, (invD2Du + twovduMulInvD) >> PRECISION_SHIFT, srb, sg);
+ AlphaBlendNormalOnPremultiplied(context, addr - vincr, (invD2Du - twovduMulInvD) >> PRECISION_SHIFT, srb, sg);
+
+ if (d < 0)
+ {
+ // choose straight (u direction)
+ twovduMulInvD = dMulInvD + duMulInvD;
+ d += incrS;
+ dMulInvD += incrSMulInvD;
+ }
+ else
+ {
+ // choose diagonal (u+v direction)
+ twovduMulInvD = dMulInvD - duMulInvD;
+ d += incrD;
+ dMulInvD += incrDMulInvD;
+ v++;
+ addr += vincr;
+ }
+ u++;
+ addr += uincr;
+ } while (u < uend);
+ }
+
+ /// <summary>
+ /// Blends a specific source color on top of a destination premultiplied color
+ /// </summary>
+ /// <param name="context">Array containing destination color</param>
+ /// <param name="index">Index of destination pixel</param>
+ /// <param name="sa">Source alpha (0..255)</param>
+ /// <param name="srb">Source non-premultiplied red and blue component in the format 0x00rr00bb</param>
+ /// <param name="sg">Source green component (0..255)</param>
+ private static void AlphaBlendNormalOnPremultiplied(BitmapContext context, int index, int sa, uint srb, uint sg)
+ {
+ var pixels = context.Pixels;
+ var destPixel = (uint)pixels[index];
+
+ var da = (destPixel >> 24);
+ var dg = ((destPixel >> 8) & 0xff);
+ var drb = destPixel & 0x00FF00FF;
+
+ // blend with high-quality alpha and lower quality but faster 1-off RGBs
+ pixels[index] = (int)(
+ ((sa + ((da * (255 - sa) * 0x8081) >> 23)) << 24) | // alpha
+ (((sg - dg) * sa + (dg << 8)) & 0xFFFFFF00) | // green
+ (((((srb - drb) * sa) >> 8) + drb) & 0x00FF00FF) // red and blue
+ );
+ }
+
+ #endregion
+
+ #region Helper
+
+ internal static bool CohenSutherlandLineClipWithViewPortOffset(Rect viewPort, ref float xi0, ref float yi0, ref float xi1, ref float yi1, int offset)
+ {
+ var viewPortWithOffset = new Rect(viewPort.X - offset, viewPort.Y - offset, viewPort.Width + 2 * offset, viewPort.Height + 2 * offset);
+
+ return CohenSutherlandLineClip(viewPortWithOffset, ref xi0, ref yi0, ref xi1, ref yi1);
+ }
+
+ internal static bool CohenSutherlandLineClip(Rect extents, ref float xi0, ref float yi0, ref float xi1, ref float yi1)
+ {
+ // Fix #SC-1555: Log(0) issue
+ // CohenSuzerland line clipping algorithm returns NaN when point has infinity value
+ double x0 = ClipToInt(xi0);
+ double y0 = ClipToInt(yi0);
+ double x1 = ClipToInt(xi1);
+ double y1 = ClipToInt(yi1);
+
+ var isValid = CohenSutherlandLineClip(extents, ref x0, ref y0, ref x1, ref y1);
+
+ // Update the clipped line
+ xi0 = (float)x0;
+ yi0 = (float)y0;
+ xi1 = (float)x1;
+ yi1 = (float)y1;
+
+ return isValid;
+ }
+
+ private static float ClipToInt(float d)
+ {
+ if (d > int.MaxValue)
+ return int.MaxValue;
+
+ if (d < int.MinValue)
+ return int.MinValue;
+
+ return d;
+ }
+
+ internal static bool CohenSutherlandLineClip(Rect extents, ref int xi0, ref int yi0, ref int xi1, ref int yi1)
+ {
+ double x0 = xi0;
+ double y0 = yi0;
+ double x1 = xi1;
+ double y1 = yi1;
+
+ var isValid = CohenSutherlandLineClip(extents, ref x0, ref y0, ref x1, ref y1);
+
+ // Update the clipped line
+ xi0 = (int)x0;
+ yi0 = (int)y0;
+ xi1 = (int)x1;
+ yi1 = (int)y1;
+
+ return isValid;
+ }
+
+ /// <summary>
+ /// Cohen–Sutherland clipping algorithm clips a line from
+ /// P0 = (x0, y0) to P1 = (x1, y1) against a rectangle with
+ /// diagonal from (xmin, ymin) to (xmax, ymax).
+ /// </summary>
+ /// <remarks>See http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland_algorithm for details</remarks>
+ /// <returns>a list of two points in the resulting clipped line, or zero</returns>
+ internal static bool CohenSutherlandLineClip(Rect extents, ref double x0, ref double y0, ref double x1, ref double y1)
+ {
+ // compute outcodes for P0, P1, and whatever point lies outside the clip rectangle
+ byte outcode0 = ComputeOutCode(extents, x0, y0);
+ byte outcode1 = ComputeOutCode(extents, x1, y1);
+
+ // No clipping if both points lie inside viewport
+ if (outcode0 == INSIDE && outcode1 == INSIDE)
+ return true;
+
+ bool isValid = false;
+
+ while (true)
+ {
+ // Bitwise OR is 0. Trivially accept and get out of loop
+ if ((outcode0 | outcode1) == 0)
+ {
+ isValid = true;
+ break;
+ }
+ // Bitwise AND is not 0. Trivially reject and get out of loop
+ else if ((outcode0 & outcode1) != 0)
+ {
+ break;
+ }
+ else
+ {
+ // failed both tests, so calculate the line segment to clip
+ // from an outside point to an intersection with clip edge
+ double x, y;
+
+ // At least one endpoint is outside the clip rectangle; pick it.
+ byte outcodeOut = (outcode0 != 0) ? outcode0 : outcode1;
+
+ // Now find the intersection point;
+ // use formulas y = y0 + slope * (x - x0), x = x0 + (1 / slope) * (y - y0)
+ if ((outcodeOut & TOP) != 0)
+ { // point is above the clip rectangle
+ x = x0 + (x1 - x0) * (extents.Top - y0) / (y1 - y0);
+ y = extents.Top;
+ }
+ else if ((outcodeOut & BOTTOM) != 0)
+ { // point is below the clip rectangle
+ x = x0 + (x1 - x0) * (extents.Bottom - y0) / (y1 - y0);
+ y = extents.Bottom;
+ }
+ else if ((outcodeOut & RIGHT) != 0)
+ { // point is to the right of clip rectangle
+ y = y0 + (y1 - y0) * (extents.Right - x0) / (x1 - x0);
+ x = extents.Right;
+ }
+ else if ((outcodeOut & LEFT) != 0)
+ { // point is to the left of clip rectangle
+ y = y0 + (y1 - y0) * (extents.Left - x0) / (x1 - x0);
+ x = extents.Left;
+ }
+ else
+ {
+ x = double.NaN;
+ y = double.NaN;
+ }
+
+ // Now we move outside point to intersection point to clip
+ // and get ready for next pass.
+ if (outcodeOut == outcode0)
+ {
+ x0 = x;
+ y0 = y;
+ outcode0 = ComputeOutCode(extents, x0, y0);
+ }
+ else
+ {
+ x1 = x;
+ y1 = y;
+ outcode1 = ComputeOutCode(extents, x1, y1);
+ }
+ }
+ }
+
+ return isValid;
+ }
+
+ /// <summary>
+ /// Alpha blends 2 premultiplied colors with each other
+ /// </summary>
+ /// <param name="sa">Source alpha color component</param>
+ /// <param name="sr">Premultiplied source red color component</param>
+ /// <param name="sg">Premultiplied source green color component</param>
+ /// <param name="sb">Premultiplied source blue color component</param>
+ /// <param name="destPixel">Premultiplied destination color</param>
+ /// <returns>Premultiplied blended color value</returns>
+ internal static int AlphaBlend(int sa, int sr, int sg, int sb, int destPixel)
+ {
+ int dr, dg, db;
+ int da;
+ da = ((destPixel >> 24) & 0xff);
+ dr = ((destPixel >> 16) & 0xff);
+ dg = ((destPixel >> 8) & 0xff);
+ db = ((destPixel) & 0xff);
+
+ destPixel = ((sa + (((da * (255 - sa)) * 0x8081) >> 23)) << 24) |
+ ((sr + (((dr * (255 - sa)) * 0x8081) >> 23)) << 16) |
+ ((sg + (((dg * (255 - sa)) * 0x8081) >> 23)) << 8) |
+ ((sb + (((db * (255 - sa)) * 0x8081) >> 23)));
+
+ return destPixel;
+ }
+
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapShapeExtensions.cs b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapShapeExtensions.cs
new file mode 100644
index 000000000..972aabc7a
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapShapeExtensions.cs
@@ -0,0 +1,489 @@
+#region Header
+//
+// Project: WriteableBitmapEx - WriteableBitmap extensions
+// Description: Collection of extension methods for the WriteableBitmap class.
+//
+// Changed by: $Author: unknown $
+// Changed on: $Date: 2015-07-20 11:44:36 +0200 (Mo, 20 Jul 2015) $
+// Changed in: $Revision: 114480 $
+// Project: $URL: https://writeablebitmapex.svn.codeplex.com/svn/trunk/Source/WriteableBitmapEx/WriteableBitmapShapeExtensions.cs $
+// Id: $Id: WriteableBitmapShapeExtensions.cs 114480 2015-07-20 09:44:36Z unknown $
+//
+//
+// Copyright © 2009-2015 Rene Schulte and WriteableBitmapEx Contributors
+//
+// This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;)
+//
+#endregion
+
+using System;
+
+#if NETFX_CORE
+namespace Windows.UI.Xaml.Media.Imaging
+#else
+namespace System.Windows.Media.Imaging
+#endif
+{
+ /// <summary>
+ /// Collection of extension methods for the WriteableBitmap class.
+ /// </summary>
+ internal
+#if WPF
+ unsafe
+#endif
+ static partial class WriteableBitmapExtensions
+ {
+ #region Methods
+
+ #region Draw Shapes
+
+ #region Polyline, Triangle, Quad
+
+ /// <summary>
+ /// Draws a polyline. Add the first point also at the end of the array if the line should be closed.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points of the polyline in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, ..., xn, yn).</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void DrawPolyline(this WriteableBitmap bmp, int[] points, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.DrawPolyline(points, col);
+ }
+
+ /// <summary>
+ /// Draws a polyline anti-aliased. Add the first point also at the end of the array if the line should be closed.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points of the polyline in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, ..., xn, yn).</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void DrawPolyline(this WriteableBitmap bmp, int[] points, int color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ var w = context.Width;
+ var h = context.Height;
+ var x1 = points[0];
+ var y1 = points[1];
+
+ for (var i = 2; i < points.Length; i += 2)
+ {
+ var x2 = points[i];
+ var y2 = points[i + 1];
+
+ DrawLine(context, w, h, x1, y1, x2, y2, color);
+ x1 = x2;
+ y1 = y2;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Draws a polyline. Add the first point also at the end of the array if the line should be closed.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points of the polyline in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, ..., xn, yn).</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void DrawPolylineAa(this WriteableBitmap bmp, int[] points, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.DrawPolylineAa(points, col);
+ }
+
+ /// <summary>
+ /// Draws a polyline anti-aliased. Add the first point also at the end of the array if the line should be closed.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points of the polyline in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, ..., xn, yn).</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void DrawPolylineAa(this WriteableBitmap bmp, int[] points, int color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ var w = context.Width;
+ var h = context.Height;
+ var x1 = points[0];
+ var y1 = points[1];
+
+ for (var i = 2; i < points.Length; i += 2)
+ {
+ var x2 = points[i];
+ var y2 = points[i + 1];
+
+ DrawLineAa(context, w, h, x1, y1, x2, y2, color);
+ x1 = x2;
+ y1 = y2;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Draws a triangle.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the 1st point.</param>
+ /// <param name="y1">The y-coordinate of the 1st point.</param>
+ /// <param name="x2">The x-coordinate of the 2nd point.</param>
+ /// <param name="y2">The y-coordinate of the 2nd point.</param>
+ /// <param name="x3">The x-coordinate of the 3rd point.</param>
+ /// <param name="y3">The y-coordinate of the 3rd point.</param>
+ /// <param name="color">The color.</param>
+ internal static void DrawTriangle(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int x3, int y3, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.DrawTriangle(x1, y1, x2, y2, x3, y3, col);
+ }
+
+ /// <summary>
+ /// Draws a triangle.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the 1st point.</param>
+ /// <param name="y1">The y-coordinate of the 1st point.</param>
+ /// <param name="x2">The x-coordinate of the 2nd point.</param>
+ /// <param name="y2">The y-coordinate of the 2nd point.</param>
+ /// <param name="x3">The x-coordinate of the 3rd point.</param>
+ /// <param name="y3">The y-coordinate of the 3rd point.</param>
+ /// <param name="color">The color.</param>
+ internal static void DrawTriangle(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int x3, int y3, int color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ int w = context.Width;
+ int h = context.Height;
+
+ DrawLine(context, w, h, x1, y1, x2, y2, color);
+ DrawLine(context, w, h, x2, y2, x3, y3, color);
+ DrawLine(context, w, h, x3, y3, x1, y1, color);
+ }
+ }
+
+ /// <summary>
+ /// Draws a quad.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the 1st point.</param>
+ /// <param name="y1">The y-coordinate of the 1st point.</param>
+ /// <param name="x2">The x-coordinate of the 2nd point.</param>
+ /// <param name="y2">The y-coordinate of the 2nd point.</param>
+ /// <param name="x3">The x-coordinate of the 3rd point.</param>
+ /// <param name="y3">The y-coordinate of the 3rd point.</param>
+ /// <param name="x4">The x-coordinate of the 4th point.</param>
+ /// <param name="y4">The y-coordinate of the 4th point.</param>
+ /// <param name="color">The color.</param>
+ internal static void DrawQuad(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.DrawQuad(x1, y1, x2, y2, x3, y3, x4, y4, col);
+ }
+
+ /// <summary>
+ /// Draws a quad.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the 1st point.</param>
+ /// <param name="y1">The y-coordinate of the 1st point.</param>
+ /// <param name="x2">The x-coordinate of the 2nd point.</param>
+ /// <param name="y2">The y-coordinate of the 2nd point.</param>
+ /// <param name="x3">The x-coordinate of the 3rd point.</param>
+ /// <param name="y3">The y-coordinate of the 3rd point.</param>
+ /// <param name="x4">The x-coordinate of the 4th point.</param>
+ /// <param name="y4">The y-coordinate of the 4th point.</param>
+ /// <param name="color">The color.</param>
+ internal static void DrawQuad(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, int color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ int w = context.Width;
+ int h = context.Height;
+
+ DrawLine(context, w, h, x1, y1, x2, y2, color);
+ DrawLine(context, w, h, x2, y2, x3, y3, color);
+ DrawLine(context, w, h, x3, y3, x4, y4, color);
+ DrawLine(context, w, h, x4, y4, x1, y1, color);
+ }
+ }
+
+ #endregion
+
+ #region Rectangle
+
+ /// <summary>
+ /// Draws a rectangle.
+ /// x2 has to be greater than x1 and y2 has to be greater than y1.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the bounding rectangle's left side.</param>
+ /// <param name="y1">The y-coordinate of the bounding rectangle's top side.</param>
+ /// <param name="x2">The x-coordinate of the bounding rectangle's right side.</param>
+ /// <param name="y2">The y-coordinate of the bounding rectangle's bottom side.</param>
+ /// <param name="color">The color.</param>
+ internal static void DrawRectangle(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.DrawRectangle(x1, y1, x2, y2, col);
+ }
+
+ /// <summary>
+ /// Draws a rectangle.
+ /// x2 has to be greater than x1 and y2 has to be greater than y1.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the bounding rectangle's left side.</param>
+ /// <param name="y1">The y-coordinate of the bounding rectangle's top side.</param>
+ /// <param name="x2">The x-coordinate of the bounding rectangle's right side.</param>
+ /// <param name="y2">The y-coordinate of the bounding rectangle's bottom side.</param>
+ /// <param name="color">The color.</param>
+ internal static void DrawRectangle(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ var w = context.Width;
+ var h = context.Height;
+ var pixels = context.Pixels;
+
+ // Check boundaries
+ if ((x1 < 0 && x2 < 0) || (y1 < 0 && y2 < 0)
+ || (x1 >= w && x2 >= w) || (y1 >= h && y2 >= h))
+ {
+ return;
+ }
+
+ // Clamp boundaries
+ if (x1 < 0) { x1 = 0; }
+ if (y1 < 0) { y1 = 0; }
+ if (x2 < 0) { x2 = 0; }
+ if (y2 < 0) { y2 = 0; }
+ if (x1 >= w) { x1 = w - 1; }
+ if (y1 >= h) { y1 = h - 1; }
+ if (x2 >= w) { x2 = w - 1; }
+ if (y2 >= h) { y2 = h - 1; }
+
+ var startY = y1 * w;
+ var endY = y2 * w;
+
+ var offset2 = endY + x1;
+ var endOffset = startY + x2;
+ var startYPlusX1 = startY + x1;
+
+ // top and bottom horizontal scanlines
+ for (var x = startYPlusX1; x <= endOffset; x++)
+ {
+ pixels[x] = color; // top horizontal line
+ pixels[offset2] = color; // bottom horizontal line
+ offset2++;
+ }
+
+ // offset2 == endY + x2
+
+ // vertical scanlines
+ endOffset = startYPlusX1 + w;
+ offset2 -= w;
+
+ for (var y = startY + x2 + w; y <= offset2; y += w)
+ {
+ pixels[y] = color; // right vertical line
+ pixels[endOffset] = color; // left vertical line
+ endOffset += w;
+ }
+ }
+ }
+
+ #endregion
+
+ #region Ellipse
+
+ /// <summary>
+ /// A Fast Bresenham Type Algorithm For Drawing Ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf
+ /// x2 has to be greater than x1 and y2 has to be greater than y1.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the bounding rectangle's left side.</param>
+ /// <param name="y1">The y-coordinate of the bounding rectangle's top side.</param>
+ /// <param name="x2">The x-coordinate of the bounding rectangle's right side.</param>
+ /// <param name="y2">The y-coordinate of the bounding rectangle's bottom side.</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void DrawEllipse(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.DrawEllipse(x1, y1, x2, y2, col);
+ }
+
+ /// <summary>
+ /// A Fast Bresenham Type Algorithm For Drawing Ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf
+ /// x2 has to be greater than x1 and y2 has to be greater than y1.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the bounding rectangle's left side.</param>
+ /// <param name="y1">The y-coordinate of the bounding rectangle's top side.</param>
+ /// <param name="x2">The x-coordinate of the bounding rectangle's right side.</param>
+ /// <param name="y2">The y-coordinate of the bounding rectangle's bottom side.</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void DrawEllipse(this WriteableBitmap bmp, int x1, int y1, int x2, int y2, int color)
+ {
+ // Calc center and radius
+ int xr = (x2 - x1) >> 1;
+ int yr = (y2 - y1) >> 1;
+ int xc = x1 + xr;
+ int yc = y1 + yr;
+ bmp.DrawEllipseCentered(xc, yc, xr, yr, color);
+ }
+
+ /// <summary>
+ /// A Fast Bresenham Type Algorithm For Drawing Ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf
+ /// Uses a different parameter representation than DrawEllipse().
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="xc">The x-coordinate of the ellipses center.</param>
+ /// <param name="yc">The y-coordinate of the ellipses center.</param>
+ /// <param name="xr">The radius of the ellipse in x-direction.</param>
+ /// <param name="yr">The radius of the ellipse in y-direction.</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void DrawEllipseCentered(this WriteableBitmap bmp, int xc, int yc, int xr, int yr, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.DrawEllipseCentered(xc, yc, xr, yr, col);
+ }
+
+ /// <summary>
+ /// A Fast Bresenham Type Algorithm For Drawing Ellipses http://homepage.smc.edu/kennedy_john/belipse.pdf
+ /// Uses a different parameter representation than DrawEllipse().
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="xc">The x-coordinate of the ellipses center.</param>
+ /// <param name="yc">The y-coordinate of the ellipses center.</param>
+ /// <param name="xr">The radius of the ellipse in x-direction.</param>
+ /// <param name="yr">The radius of the ellipse in y-direction.</param>
+ /// <param name="color">The color for the line.</param>
+ internal static void DrawEllipseCentered(this WriteableBitmap bmp, int xc, int yc, int xr, int yr, int color)
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ using (var context = bmp.GetBitmapContext())
+ {
+
+ var pixels = context.Pixels;
+ var w = context.Width;
+ var h = context.Height;
+
+ // Avoid endless loop
+ if (xr < 1 || yr < 1)
+ {
+ return;
+ }
+
+ // Init vars
+ int uh, lh, uy, ly, lx, rx;
+ int x = xr;
+ int y = 0;
+ int xrSqTwo = (xr * xr) << 1;
+ int yrSqTwo = (yr * yr) << 1;
+ int xChg = yr * yr * (1 - (xr << 1));
+ int yChg = xr * xr;
+ int err = 0;
+ int xStopping = yrSqTwo * xr;
+ int yStopping = 0;
+
+ // Draw first set of points counter clockwise where tangent line slope > -1.
+ while (xStopping >= yStopping)
+ {
+ // Draw 4 quadrant points at once
+ uy = yc + y; // Upper half
+ ly = yc - y; // Lower half
+ if (uy < 0) uy = 0; // Clip
+ if (uy >= h) uy = h - 1; // ...
+ if (ly < 0) ly = 0;
+ if (ly >= h) ly = h - 1;
+ uh = uy * w; // Upper half
+ lh = ly * w; // Lower half
+
+ rx = xc + x;
+ lx = xc - x;
+ if (rx < 0) rx = 0; // Clip
+ if (rx >= w) rx = w - 1; // ...
+ if (lx < 0) lx = 0;
+ if (lx >= w) lx = w - 1;
+ pixels[rx + uh] = color; // Quadrant I (Actually an octant)
+ pixels[lx + uh] = color; // Quadrant II
+ pixels[lx + lh] = color; // Quadrant III
+ pixels[rx + lh] = color; // Quadrant IV
+
+ y++;
+ yStopping += xrSqTwo;
+ err += yChg;
+ yChg += xrSqTwo;
+ if ((xChg + (err << 1)) > 0)
+ {
+ x--;
+ xStopping -= yrSqTwo;
+ err += xChg;
+ xChg += yrSqTwo;
+ }
+ }
+
+ // ReInit vars
+ x = 0;
+ y = yr;
+ uy = yc + y; // Upper half
+ ly = yc - y; // Lower half
+ if (uy < 0) uy = 0; // Clip
+ if (uy >= h) uy = h - 1; // ...
+ if (ly < 0) ly = 0;
+ if (ly >= h) ly = h - 1;
+ uh = uy * w; // Upper half
+ lh = ly * w; // Lower half
+ xChg = yr * yr;
+ yChg = xr * xr * (1 - (yr << 1));
+ err = 0;
+ xStopping = 0;
+ yStopping = xrSqTwo * yr;
+
+ // Draw second set of points clockwise where tangent line slope < -1.
+ while (xStopping <= yStopping)
+ {
+ // Draw 4 quadrant points at once
+ rx = xc + x;
+ lx = xc - x;
+ if (rx < 0) rx = 0; // Clip
+ if (rx >= w) rx = w - 1; // ...
+ if (lx < 0) lx = 0;
+ if (lx >= w) lx = w - 1;
+ pixels[rx + uh] = color; // Quadrant I (Actually an octant)
+ pixels[lx + uh] = color; // Quadrant II
+ pixels[lx + lh] = color; // Quadrant III
+ pixels[rx + lh] = color; // Quadrant IV
+
+ x++;
+ xStopping += yrSqTwo;
+ err += xChg;
+ xChg += yrSqTwo;
+ if ((yChg + (err << 1)) > 0)
+ {
+ y--;
+ uy = yc + y; // Upper half
+ ly = yc - y; // Lower half
+ if (uy < 0) uy = 0; // Clip
+ if (uy >= h) uy = h - 1; // ...
+ if (ly < 0) ly = 0;
+ if (ly >= h) ly = h - 1;
+ uh = uy * w; // Upper half
+ lh = ly * w; // Lower half
+ yStopping -= xrSqTwo;
+ err += yChg;
+ yChg += xrSqTwo;
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #endregion
+
+ #endregion
+ }
+}
diff --git a/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapSplineExtensions.cs b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapSplineExtensions.cs
new file mode 100644
index 000000000..f9b6ffd13
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapSplineExtensions.cs
@@ -0,0 +1,341 @@
+#region Header
+//
+// Project: WriteableBitmapEx - WriteableBitmap extensions
+// Description: Collection of draw spline extension methods for the WriteableBitmap class.
+//
+// Changed by: $Author: unknown $
+// Changed on: $Date: 2015-03-05 18:18:24 +0100 (Do, 05 Mrz 2015) $
+// Changed in: $Revision: 113191 $
+// Project: $URL: https://writeablebitmapex.svn.codeplex.com/svn/trunk/Source/WriteableBitmapEx/WriteableBitmapSplineExtensions.cs $
+// Id: $Id: WriteableBitmapSplineExtensions.cs 113191 2015-03-05 17:18:24Z unknown $
+//
+//
+// Copyright © 2009-2015 Rene Schulte and WriteableBitmapEx Contributors
+//
+// This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;)
+//
+#endregion
+
+using System;
+
+#if NETFX_CORE
+namespace Windows.UI.Xaml.Media.Imaging
+#else
+namespace System.Windows.Media.Imaging
+#endif
+{
+ /// <summary>
+ /// Collection of draw spline extension methods for the WriteableBitmap class.
+ /// </summary>
+ internal
+#if WPF
+ unsafe
+#endif
+ static partial class WriteableBitmapExtensions
+ {
+ #region Fields
+
+ private const float StepFactor = 2f;
+
+ #endregion
+
+ #region Methods
+
+ #region Beziér
+
+ /// <summary>
+ /// Draws a cubic Beziér spline defined by start, end and two control points.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="cx1">The x-coordinate of the 1st control point.</param>
+ /// <param name="cy1">The y-coordinate of the 1st control point.</param>
+ /// <param name="cx2">The x-coordinate of the 2nd control point.</param>
+ /// <param name="cy2">The y-coordinate of the 2nd control point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color.</param>
+ internal static void DrawBezier(this WriteableBitmap bmp, int x1, int y1, int cx1, int cy1, int cx2, int cy2, int x2, int y2, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.DrawBezier(x1, y1, cx1, cy1, cx2, cy2, x2, y2, col);
+ }
+
+ /// <summary>
+ /// Draws a cubic Beziér spline defined by start, end and two control points.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x1">The x-coordinate of the start point.</param>
+ /// <param name="y1">The y-coordinate of the start point.</param>
+ /// <param name="cx1">The x-coordinate of the 1st control point.</param>
+ /// <param name="cy1">The y-coordinate of the 1st control point.</param>
+ /// <param name="cx2">The x-coordinate of the 2nd control point.</param>
+ /// <param name="cy2">The y-coordinate of the 2nd control point.</param>
+ /// <param name="x2">The x-coordinate of the end point.</param>
+ /// <param name="y2">The y-coordinate of the end point.</param>
+ /// <param name="color">The color.</param>
+ internal static void DrawBezier(this WriteableBitmap bmp, int x1, int y1, int cx1, int cy1, int cx2, int cy2, int x2, int y2, int color)
+ {
+ // Determine distances between controls points (bounding rect) to find the optimal stepsize
+ var minX = Math.Min(x1, Math.Min(cx1, Math.Min(cx2, x2)));
+ var minY = Math.Min(y1, Math.Min(cy1, Math.Min(cy2, y2)));
+ var maxX = Math.Max(x1, Math.Max(cx1, Math.Max(cx2, x2)));
+ var maxY = Math.Max(y1, Math.Max(cy1, Math.Max(cy2, y2)));
+
+ // Get slope
+ var lenx = maxX - minX;
+ var len = maxY - minY;
+ if (lenx > len)
+ {
+ len = lenx;
+ }
+
+ // Prevent division by zero
+ if (len != 0)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ int w = context.Width;
+ int h = context.Height;
+
+ // Init vars
+ var step = StepFactor / len;
+ int tx1 = x1;
+ int ty1 = y1;
+ int tx2, ty2;
+
+ // Interpolate
+ for (var t = step; t <= 1; t += step)
+ {
+ var tSq = t * t;
+ var t1 = 1 - t;
+ var t1Sq = t1 * t1;
+
+ tx2 = (int)(t1 * t1Sq * x1 + 3 * t * t1Sq * cx1 + 3 * t1 * tSq * cx2 + t * tSq * x2);
+ ty2 = (int)(t1 * t1Sq * y1 + 3 * t * t1Sq * cy1 + 3 * t1 * tSq * cy2 + t * tSq * y2);
+
+ // Draw line
+ DrawLine(context, w, h, tx1, ty1, tx2, ty2, color);
+ tx1 = tx2;
+ ty1 = ty2;
+ }
+
+ // Prevent rounding gap
+ DrawLine(context, w, h, tx1, ty1, x2, y2, color);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Draws a series of cubic Beziér splines each defined by start, end and two control points.
+ /// The ending point of the previous curve is used as starting point for the next.
+ /// Therefore the initial curve needs four points and the subsequent 3 (2 control and 1 end point).
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, cx1, cy1, cx2, cy2, x2, y2, cx3, cx4 ..., xn, yn).</param>
+ /// <param name="color">The color for the spline.</param>
+ internal static void DrawBeziers(this WriteableBitmap bmp, int[] points, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.DrawBeziers(points, col);
+ }
+
+ /// <summary>
+ /// Draws a series of cubic Beziér splines each defined by start, end and two control points.
+ /// The ending point of the previous curve is used as starting point for the next.
+ /// Therefore the initial curve needs four points and the subsequent 3 (2 control and 1 end point).
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, cx1, cy1, cx2, cy2, x2, y2, cx3, cx4 ..., xn, yn).</param>
+ /// <param name="color">The color for the spline.</param>
+ internal static void DrawBeziers(this WriteableBitmap bmp, int[] points, int color)
+ {
+ int x1 = points[0];
+ int y1 = points[1];
+ int x2, y2;
+ for (int i = 2; i + 5 < points.Length; i += 6)
+ {
+ x2 = points[i + 4];
+ y2 = points[i + 5];
+ bmp.DrawBezier(x1, y1, points[i], points[i + 1], points[i + 2], points[i + 3], x2, y2, color);
+ x1 = x2;
+ y1 = y2;
+ }
+ }
+
+ #endregion
+
+ #region Cardinal
+
+ /// <summary>
+ /// Draws a segment of a Cardinal spline (cubic) defined by four control points.
+ /// </summary>
+ /// <param name="x1">The x-coordinate of the 1st control point.</param>
+ /// <param name="y1">The y-coordinate of the 1st control point.</param>
+ /// <param name="x2">The x-coordinate of the 2nd control point.</param>
+ /// <param name="y2">The y-coordinate of the 2nd control point.</param>
+ /// <param name="x3">The x-coordinate of the 3rd control point.</param>
+ /// <param name="y3">The y-coordinate of the 3rd control point.</param>
+ /// <param name="x4">The x-coordinate of the 4th control point.</param>
+ /// <param name="y4">The y-coordinate of the 4th control point.</param>
+ /// <param name="tension">The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line.</param>
+ /// <param name="color">The color.</param>
+ /// <param name="context">The pixel context.</param>
+ /// <param name="w">The width of the bitmap.</param>
+ /// <param name="h">The height of the bitmap.</param>
+ private static void DrawCurveSegment(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4, float tension, int color, BitmapContext context, int w, int h)
+ {
+ // Determine distances between controls points (bounding rect) to find the optimal stepsize
+ var minX = Math.Min(x1, Math.Min(x2, Math.Min(x3, x4)));
+ var minY = Math.Min(y1, Math.Min(y2, Math.Min(y3, y4)));
+ var maxX = Math.Max(x1, Math.Max(x2, Math.Max(x3, x4)));
+ var maxY = Math.Max(y1, Math.Max(y2, Math.Max(y3, y4)));
+
+ // Get slope
+ var lenx = maxX - minX;
+ var len = maxY - minY;
+ if (lenx > len)
+ {
+ len = lenx;
+ }
+
+ // Prevent division by zero
+ if (len != 0)
+ {
+ // Init vars
+ var step = StepFactor / len;
+ int tx1 = x2;
+ int ty1 = y2;
+ int tx2, ty2;
+
+ // Calculate factors
+ var sx1 = tension * (x3 - x1);
+ var sy1 = tension * (y3 - y1);
+ var sx2 = tension * (x4 - x2);
+ var sy2 = tension * (y4 - y2);
+ var ax = sx1 + sx2 + 2 * x2 - 2 * x3;
+ var ay = sy1 + sy2 + 2 * y2 - 2 * y3;
+ var bx = -2 * sx1 - sx2 - 3 * x2 + 3 * x3;
+ var by = -2 * sy1 - sy2 - 3 * y2 + 3 * y3;
+
+ // Interpolate
+ for (var t = step; t <= 1; t += step)
+ {
+ var tSq = t * t;
+
+ tx2 = (int)(ax * tSq * t + bx * tSq + sx1 * t + x2);
+ ty2 = (int)(ay * tSq * t + by * tSq + sy1 * t + y2);
+
+ // Draw line
+ DrawLine(context, w, h, tx1, ty1, tx2, ty2, color);
+ tx1 = tx2;
+ ty1 = ty2;
+ }
+
+ // Prevent rounding gap
+ DrawLine(context, w, h, tx1, ty1, x3, y3, color);
+ }
+ }
+
+ /// <summary>
+ /// Draws a Cardinal spline (cubic) defined by a point collection.
+ /// The cardinal spline passes through each point in the collection.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn).</param>
+ /// <param name="tension">The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line.</param>
+ /// <param name="color">The color for the spline.</param>
+ internal static void DrawCurve(this WriteableBitmap bmp, int[] points, float tension, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.DrawCurve(points, tension, col);
+ }
+
+ /// <summary>
+ /// Draws a Cardinal spline (cubic) defined by a point collection.
+ /// The cardinal spline passes through each point in the collection.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn).</param>
+ /// <param name="tension">The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line.</param>
+ /// <param name="color">The color for the spline.</param>
+ internal static void DrawCurve(this WriteableBitmap bmp, int[] points, float tension, int color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ int w = context.Width;
+ int h = context.Height;
+
+ // First segment
+ DrawCurveSegment(points[0], points[1], points[0], points[1], points[2], points[3], points[4], points[5], tension, color, context, w, h);
+
+ // Middle segments
+ int i;
+ for (i = 2; i < points.Length - 4; i += 2)
+ {
+ DrawCurveSegment(points[i - 2], points[i - 1], points[i], points[i + 1], points[i + 2], points[i + 3], points[i + 4], points[i + 5], tension, color, context, w, h);
+ }
+
+ // Last segment
+ DrawCurveSegment(points[i - 2], points[i - 1], points[i], points[i + 1], points[i + 2], points[i + 3], points[i + 2], points[i + 3], tension, color, context, w, h);
+ }
+ }
+
+ /// <summary>
+ /// Draws a closed Cardinal spline (cubic) defined by a point collection.
+ /// The cardinal spline passes through each point in the collection.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn).</param>
+ /// <param name="tension">The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line.</param>
+ /// <param name="color">The color for the spline.</param>
+ internal static void DrawCurveClosed(this WriteableBitmap bmp, int[] points, float tension, Color color)
+ {
+ var col = ConvertColor(color);
+ bmp.DrawCurveClosed(points, tension, col);
+ }
+
+ /// <summary>
+ /// Draws a closed Cardinal spline (cubic) defined by a point collection.
+ /// The cardinal spline passes through each point in the collection.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="points">The points for the curve in x and y pairs, therefore the array is interpreted as (x1, y1, x2, y2, x3, y3, x4, y4, x1, x2 ..., xn, yn).</param>
+ /// <param name="tension">The tension of the curve defines the shape. Usually between 0 and 1. 0 would be a straight line.</param>
+ /// <param name="color">The color for the spline.</param>
+ internal static void DrawCurveClosed(this WriteableBitmap bmp, int[] points, float tension, int color)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ int w = context.Width;
+ int h = context.Height;
+
+ int pn = points.Length;
+
+ // First segment
+ DrawCurveSegment(points[pn - 2], points[pn - 1], points[0], points[1], points[2], points[3], points[4], points[5], tension, color, context, w, h);
+
+ // Middle segments
+ int i;
+ for (i = 2; i < pn - 4; i += 2)
+ {
+ DrawCurveSegment(points[i - 2], points[i - 1], points[i], points[i + 1], points[i + 2], points[i + 3], points[i + 4], points[i + 5], tension, color, context, w, h);
+ }
+
+ // Last segment
+ DrawCurveSegment(points[i - 2], points[i - 1], points[i], points[i + 1], points[i + 2], points[i + 3], points[0], points[1], tension, color, context, w, h);
+
+ // Last-to-First segment
+ DrawCurveSegment(points[i], points[i + 1], points[i + 2], points[i + 3], points[0], points[1], points[2], points[3], tension, color, context, w, h);
+ }
+ }
+
+ #endregion
+
+ #endregion
+ }
+} \ No newline at end of file
diff --git a/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapTransformationExtensions.cs b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapTransformationExtensions.cs
new file mode 100644
index 000000000..afdda8d75
--- /dev/null
+++ b/Software/Visual_Studio/SideChains/RealTimeGraphEx/WriteableBitmap/WriteableBitmapTransformationExtensions.cs
@@ -0,0 +1,623 @@
+#region Header
+//
+// Project: WriteableBitmapEx - WriteableBitmap extensions
+// Description: Collection of transformation extension methods for the WriteableBitmap class.
+//
+// Changed by: $Author: unknown $
+// Changed on: $Date: 2015-03-05 18:18:24 +0100 (Do, 05 Mrz 2015) $
+// Changed in: $Revision: 113191 $
+// Project: $URL: https://writeablebitmapex.svn.codeplex.com/svn/trunk/Source/WriteableBitmapEx/WriteableBitmapTransformationExtensions.cs $
+// Id: $Id: WriteableBitmapTransformationExtensions.cs 113191 2015-03-05 17:18:24Z unknown $
+//
+//
+// Copyright © 2009-2015 Rene Schulte and WriteableBitmapEx Contributors
+//
+// This code is open source. Please read the License.txt for details. No worries, we won't sue you! ;)
+//
+#endregion
+
+using System;
+
+#if NETFX_CORE
+using Windows.Foundation;
+
+namespace Windows.UI.Xaml.Media.Imaging
+#else
+namespace System.Windows.Media.Imaging
+#endif
+{
+ /// <summary>
+ /// Collection of transformation extension methods for the WriteableBitmap class.
+ /// </summary>
+ internal
+#if WPF
+ unsafe
+#endif
+ static partial class WriteableBitmapExtensions
+ {
+ #region Enums
+
+ /// <summary>
+ /// The interpolation method.
+ /// </summary>
+ internal enum Interpolation
+ {
+ /// <summary>
+ /// The nearest neighbor algorithm simply selects the color of the nearest pixel.
+ /// </summary>
+ NearestNeighbor = 0,
+
+ /// <summary>
+ /// Linear interpolation in 2D using the average of 3 neighboring pixels.
+ /// </summary>
+ Bilinear,
+ }
+
+ /// <summary>
+ /// The mode for flipping.
+ /// </summary>
+ internal enum FlipMode
+ {
+ /// <summary>
+ /// Flips the image vertical (around the center of the y-axis).
+ /// </summary>
+ Vertical,
+
+ /// <summary>
+ /// Flips the image horizontal (around the center of the x-axis).
+ /// </summary>
+ Horizontal
+ }
+
+ #endregion
+
+ #region Methods
+
+ #region Crop
+
+ /// <summary>
+ /// Creates a new cropped WriteableBitmap.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="x">The x coordinate of the rectangle that defines the crop region.</param>
+ /// <param name="y">The y coordinate of the rectangle that defines the crop region.</param>
+ /// <param name="width">The width of the rectangle that defines the crop region.</param>
+ /// <param name="height">The height of the rectangle that defines the crop region.</param>
+ /// <returns>A new WriteableBitmap that is a cropped version of the input.</returns>
+ internal static WriteableBitmap Crop(this WriteableBitmap bmp, int x, int y, int width, int height)
+ {
+ using (var srcContext = bmp.GetBitmapContext())
+ {
+ var srcWidth = srcContext.Width;
+ var srcHeight = srcContext.Height;
+
+ // If the rectangle is completely out of the bitmap
+ if (x > srcWidth || y > srcHeight)
+ {
+ return BitmapFactory.New(0, 0);
+ }
+
+ // Clamp to boundaries
+ if (x < 0) x = 0;
+ if (x + width > srcWidth) width = srcWidth - x;
+ if (y < 0) y = 0;
+ if (y + height > srcHeight) height = srcHeight - y;
+
+ // Copy the pixels line by line using fast BlockCopy
+ var result = BitmapFactory.New(width, height);
+ using (var destContext = result.GetBitmapContext())
+ {
+ for (var line = 0; line < height; line++)
+ {
+ var srcOff = ((y + line) * srcWidth + x) * SizeOfArgb;
+ var dstOff = line * width * SizeOfArgb;
+ BitmapContext.BlockCopy(srcContext, srcOff, destContext, dstOff, width * SizeOfArgb);
+ }
+
+ return result;
+ }
+ }
+ }
+ /// <summary>
+ /// Creates a new cropped WriteableBitmap.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="region">The rectangle that defines the crop region.</param>
+ /// <returns>A new WriteableBitmap that is a cropped version of the input.</returns>
+ internal static WriteableBitmap Crop(this WriteableBitmap bmp, Rect region)
+ {
+ return bmp.Crop((int)region.X, (int)region.Y, (int)region.Width, (int)region.Height);
+ }
+
+ #endregion
+
+ #region Resize
+
+ /// <summary>
+ /// Creates a new resized WriteableBitmap.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="width">The new desired width.</param>
+ /// <param name="height">The new desired height.</param>
+ /// <param name="interpolation">The interpolation method that should be used.</param>
+ /// <returns>A new WriteableBitmap that is a resized version of the input.</returns>
+ internal static WriteableBitmap Resize(this WriteableBitmap bmp, int width, int height, Interpolation interpolation)
+ {
+ using (var srcContext = bmp.GetBitmapContext())
+ {
+ var pd = Resize(srcContext, srcContext.Width, srcContext.Height, width, height, interpolation);
+
+ var result = BitmapFactory.New(width, height);
+ using (var dstContext = result.GetBitmapContext())
+ {
+ BitmapContext.BlockCopy(pd, 0, dstContext, 0, SizeOfArgb * pd.Length);
+ }
+ return result;
+ }
+ }
+
+ /// <summary>
+ /// Creates a new resized bitmap.
+ /// </summary>
+ /// <param name="srcContext">The source context.</param>
+ /// <param name="widthSource">The width of the source pixels.</param>
+ /// <param name="heightSource">The height of the source pixels.</param>
+ /// <param name="width">The new desired width.</param>
+ /// <param name="height">The new desired height.</param>
+ /// <param name="interpolation">The interpolation method that should be used.</param>
+ /// <returns>A new bitmap that is a resized version of the input.</returns>
+ internal static int[] Resize(BitmapContext srcContext, int widthSource, int heightSource, int width, int height, Interpolation interpolation)
+ {
+ return Resize(srcContext.Pixels, widthSource, heightSource, width, height, interpolation);
+ }
+
+ /// <summary>
+ /// Creates a new resized bitmap.
+ /// </summary>
+ /// <param name="pixels">The source pixels.</param>
+ /// <param name="widthSource">The width of the source pixels.</param>
+ /// <param name="heightSource">The height of the source pixels.</param>
+ /// <param name="width">The new desired width.</param>
+ /// <param name="height">The new desired height.</param>
+ /// <param name="interpolation">The interpolation method that should be used.</param>
+ /// <returns>A new bitmap that is a resized version of the input.</returns>
+#if WPF
+ internal static int[] Resize(int* pixels, int widthSource, int heightSource, int width, int height, Interpolation interpolation)
+#else
+ internal static int[] Resize(int[] pixels, int widthSource, int heightSource, int width, int height, Interpolation interpolation)
+#endif
+ {
+ var pd = new int[width * height];
+ var xs = (float)widthSource / width;
+ var ys = (float)heightSource / height;
+
+ float fracx, fracy, ifracx, ifracy, sx, sy, l0, l1, rf, gf, bf;
+ int c, x0, x1, y0, y1;
+ byte c1a, c1r, c1g, c1b, c2a, c2r, c2g, c2b, c3a, c3r, c3g, c3b, c4a, c4r, c4g, c4b;
+ byte a, r, g, b;
+
+ // Nearest Neighbor
+ if (interpolation == Interpolation.NearestNeighbor)
+ {
+ var srcIdx = 0;
+ for (var y = 0; y < height; y++)
+ {
+ for (var x = 0; x < width; x++)
+ {
+ sx = x * xs;
+ sy = y * ys;
+ x0 = (int)sx;
+ y0 = (int)sy;
+
+ pd[srcIdx++] = pixels[y0 * widthSource + x0];
+ }
+ }
+ }
+
+ // Bilinear
+ else if (interpolation == Interpolation.Bilinear)
+ {
+ var srcIdx = 0;
+ for (var y = 0; y < height; y++)
+ {
+ for (var x = 0; x < width; x++)
+ {
+ sx = x * xs;
+ sy = y * ys;
+ x0 = (int)sx;
+ y0 = (int)sy;
+
+ // Calculate coordinates of the 4 interpolation points
+ fracx = sx - x0;
+ fracy = sy - y0;
+ ifracx = 1f - fracx;
+ ifracy = 1f - fracy;
+ x1 = x0 + 1;
+ if (x1 >= widthSource)
+ {
+ x1 = x0;
+ }
+ y1 = y0 + 1;
+ if (y1 >= heightSource)
+ {
+ y1 = y0;
+ }
+
+
+ // Read source color
+ c = pixels[y0 * widthSource + x0];
+ c1a = (byte)(c >> 24);
+ c1r = (byte)(c >> 16);
+ c1g = (byte)(c >> 8);
+ c1b = (byte)(c);
+
+ c = pixels[y0 * widthSource + x1];
+ c2a = (byte)(c >> 24);
+ c2r = (byte)(c >> 16);
+ c2g = (byte)(c >> 8);
+ c2b = (byte)(c);
+
+ c = pixels[y1 * widthSource + x0];
+ c3a = (byte)(c >> 24);
+ c3r = (byte)(c >> 16);
+ c3g = (byte)(c >> 8);
+ c3b = (byte)(c);
+
+ c = pixels[y1 * widthSource + x1];
+ c4a = (byte)(c >> 24);
+ c4r = (byte)(c >> 16);
+ c4g = (byte)(c >> 8);
+ c4b = (byte)(c);
+
+
+ // Calculate colors
+ // Alpha
+ l0 = ifracx * c1a + fracx * c2a;
+ l1 = ifracx * c3a + fracx * c4a;
+ a = (byte)(ifracy * l0 + fracy * l1);
+
+ // Red
+ l0 = ifracx * c1r + fracx * c2r;
+ l1 = ifracx * c3r + fracx * c4r;
+ rf = ifracy * l0 + fracy * l1;
+
+ // Green
+ l0 = ifracx * c1g + fracx * c2g;
+ l1 = ifracx * c3g + fracx * c4g;
+ gf = ifracy * l0 + fracy * l1;
+
+ // Blue
+ l0 = ifracx * c1b + fracx * c2b;
+ l1 = ifracx * c3b + fracx * c4b;
+ bf = ifracy * l0 + fracy * l1;
+
+ // Cast to byte
+ r = (byte)rf;
+ g = (byte)gf;
+ b = (byte)bf;
+
+ // Write destination
+ pd[srcIdx++] = (a << 24) | (r << 16) | (g << 8) | b;
+ }
+ }
+ }
+ return pd;
+ }
+
+ #endregion
+
+ #region Rotate
+
+ /// <summary>
+ /// Rotates the bitmap in 90° steps clockwise and returns a new rotated WriteableBitmap.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="angle">The angle in degrees the bitmap should be rotated in 90° steps clockwise.</param>
+ /// <returns>A new WriteableBitmap that is a rotated version of the input.</returns>
+ internal static WriteableBitmap Rotate(this WriteableBitmap bmp, int angle)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ var w = context.Width;
+ var h = context.Height;
+ var p = context.Pixels;
+ var i = 0;
+ WriteableBitmap result = null;
+ angle %= 360;
+
+ if (angle > 0 && angle <= 90)
+ {
+ result = BitmapFactory.New(h, w);
+ using (var destContext = result.GetBitmapContext())
+ {
+ var rp = destContext.Pixels;
+ for (var x = 0; x < w; x++)
+ {
+ for (var y = h - 1; y >= 0; y--)
+ {
+ var srcInd = y * w + x;
+ rp[i] = p[srcInd];
+ i++;
+ }
+ }
+ }
+ }
+ else if (angle > 90 && angle <= 180)
+ {
+ result = BitmapFactory.New(w, h);
+ using (var destContext = result.GetBitmapContext())
+ {
+ var rp = destContext.Pixels;
+ for (var y = h - 1; y >= 0; y--)
+ {
+ for (var x = w - 1; x >= 0; x--)
+ {
+ var srcInd = y * w + x;
+ rp[i] = p[srcInd];
+ i++;
+ }
+ }
+ }
+ }
+ else if (angle > 180 && angle <= 270)
+ {
+ result = BitmapFactory.New(h, w);
+ using (var destContext = result.GetBitmapContext())
+ {
+ var rp = destContext.Pixels;
+ for (var x = w - 1; x >= 0; x--)
+ {
+ for (var y = 0; y < h; y++)
+ {
+ var srcInd = y * w + x;
+ rp[i] = p[srcInd];
+ i++;
+ }
+ }
+ }
+ }
+ else
+ {
+ result = bmp.Clone();
+ }
+ return result;
+ }
+ }
+
+ /// <summary>
+ /// Rotates the bitmap in any degree returns a new rotated WriteableBitmap.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="angle">Arbitrary angle in 360 Degrees (positive = clockwise).</param>
+ /// <param name="crop">if true: keep the size, false: adjust canvas to new size</param>
+ /// <returns>A new WriteableBitmap that is a rotated version of the input.</returns>
+ internal static WriteableBitmap RotateFree(this WriteableBitmap bmp, double angle, bool crop = true)
+ {
+ // rotating clockwise, so it's negative relative to Cartesian quadrants
+ double cnAngle = -1.0 * (Math.PI / 180) * angle;
+
+ // general iterators
+ int i, j;
+ // calculated indices in Cartesian coordinates
+ int x, y;
+ double fDistance, fPolarAngle;
+ // for use in neighboring indices in Cartesian coordinates
+ int iFloorX, iCeilingX, iFloorY, iCeilingY;
+ // calculated indices in Cartesian coordinates with trailing decimals
+ double fTrueX, fTrueY;
+ // for interpolation
+ double fDeltaX, fDeltaY;
+
+ // interpolated "top" pixels
+ double fTopRed, fTopGreen, fTopBlue, fTopAlpha;
+
+ // interpolated "bottom" pixels
+ double fBottomRed, fBottomGreen, fBottomBlue, fBottomAlpha;
+
+ // final interpolated color components
+ int iRed, iGreen, iBlue, iAlpha;
+
+ int iCentreX, iCentreY;
+ int iDestCentreX, iDestCentreY;
+ int iWidth, iHeight, newWidth, newHeight;
+ using (var bmpContext = bmp.GetBitmapContext())
+ {
+
+ iWidth = bmpContext.Width;
+ iHeight = bmpContext.Height;
+
+ if (crop)
+ {
+ newWidth = iWidth;
+ newHeight = iHeight;
+ }
+ else
+ {
+ var rad = angle / (180 / Math.PI);
+ newWidth = (int)Math.Ceiling(Math.Abs(Math.Sin(rad) * iHeight) + Math.Abs(Math.Cos(rad) * iWidth));
+ newHeight = (int)Math.Ceiling(Math.Abs(Math.Sin(rad) * iWidth) + Math.Abs(Math.Cos(rad) * iHeight));
+ }
+
+
+ iCentreX = iWidth / 2;
+ iCentreY = iHeight / 2;
+
+ iDestCentreX = newWidth / 2;
+ iDestCentreY = newHeight / 2;
+
+ var bmBilinearInterpolation = BitmapFactory.New(newWidth, newHeight);
+
+ using (var bilinearContext = bmBilinearInterpolation.GetBitmapContext())
+ {
+ var newp = bilinearContext.Pixels;
+ var oldp = bmpContext.Pixels;
+ var oldw = bmpContext.Width;
+
+ // assigning pixels of destination image from source image
+ // with bilinear interpolation
+ for (i = 0; i < newHeight; ++i)
+ {
+ for (j = 0; j < newWidth; ++j)
+ {
+ // convert raster to Cartesian
+ x = j - iDestCentreX;
+ y = iDestCentreY - i;
+
+ // convert Cartesian to polar
+ fDistance = Math.Sqrt(x * x + y * y);
+ if (x == 0)
+ {
+ if (y == 0)
+ {
+ // center of image, no rotation needed
+ newp[i * newWidth + j] = oldp[iCentreY * oldw + iCentreX];
+ continue;
+ }
+ if (y < 0)
+ {
+ fPolarAngle = 1.5 * Math.PI;
+ }
+ else
+ {
+ fPolarAngle = 0.5 * Math.PI;
+ }
+ }
+ else
+ {
+ fPolarAngle = Math.Atan2(y, x);
+ }
+
+ // the crucial rotation part
+ // "reverse" rotate, so minus instead of plus
+ fPolarAngle -= cnAngle;
+
+ // convert polar to Cartesian
+ fTrueX = fDistance * Math.Cos(fPolarAngle);
+ fTrueY = fDistance * Math.Sin(fPolarAngle);
+
+ // convert Cartesian to raster
+ fTrueX = fTrueX + iCentreX;
+ fTrueY = iCentreY - fTrueY;
+
+ iFloorX = (int)(Math.Floor(fTrueX));
+ iFloorY = (int)(Math.Floor(fTrueY));
+ iCeilingX = (int)(Math.Ceiling(fTrueX));
+ iCeilingY = (int)(Math.Ceiling(fTrueY));
+
+ // check bounds
+ if (iFloorX < 0 || iCeilingX < 0 || iFloorX >= iWidth || iCeilingX >= iWidth || iFloorY < 0 ||
+ iCeilingY < 0 || iFloorY >= iHeight || iCeilingY >= iHeight) continue;
+
+ fDeltaX = fTrueX - iFloorX;
+ fDeltaY = fTrueY - iFloorY;
+
+ var clrTopLeft = oldp[iFloorY * oldw + iFloorX];
+ var clrTopRight = oldp[iFloorY * oldw + iCeilingX];
+ var clrBottomLeft = oldp[iCeilingY * oldw + iFloorX];
+ var clrBottomRight = oldp[iCeilingY * oldw + iCeilingX];
+
+ fTopAlpha = (1 - fDeltaX) * ((clrTopLeft >> 24) & 0xFF) + fDeltaX * ((clrTopRight >> 24) & 0xFF);
+ fTopRed = (1 - fDeltaX) * ((clrTopLeft >> 16) & 0xFF) + fDeltaX * ((clrTopRight >> 16) & 0xFF);
+ fTopGreen = (1 - fDeltaX) * ((clrTopLeft >> 8) & 0xFF) + fDeltaX * ((clrTopRight >> 8) & 0xFF);
+ fTopBlue = (1 - fDeltaX) * (clrTopLeft & 0xFF) + fDeltaX * (clrTopRight & 0xFF);
+
+ // linearly interpolate horizontally between bottom neighbors
+ fBottomAlpha = (1 - fDeltaX) * ((clrBottomLeft >> 24) & 0xFF) + fDeltaX * ((clrBottomRight >> 24) & 0xFF);
+ fBottomRed = (1 - fDeltaX) * ((clrBottomLeft >> 16) & 0xFF) + fDeltaX * ((clrBottomRight >> 16) & 0xFF);
+ fBottomGreen = (1 - fDeltaX) * ((clrBottomLeft >> 8) & 0xFF) + fDeltaX * ((clrBottomRight >> 8) & 0xFF);
+ fBottomBlue = (1 - fDeltaX) * (clrBottomLeft & 0xFF) + fDeltaX * (clrBottomRight & 0xFF);
+
+ // linearly interpolate vertically between top and bottom interpolated results
+ iRed = (int)(Math.Round((1 - fDeltaY) * fTopRed + fDeltaY * fBottomRed));
+ iGreen = (int)(Math.Round((1 - fDeltaY) * fTopGreen + fDeltaY * fBottomGreen));
+ iBlue = (int)(Math.Round((1 - fDeltaY) * fTopBlue + fDeltaY * fBottomBlue));
+ iAlpha = (int)(Math.Round((1 - fDeltaY) * fTopAlpha + fDeltaY * fBottomAlpha));
+
+ // make sure color values are valid
+ if (iRed < 0) iRed = 0;
+ if (iRed > 255) iRed = 255;
+ if (iGreen < 0) iGreen = 0;
+ if (iGreen > 255) iGreen = 255;
+ if (iBlue < 0) iBlue = 0;
+ if (iBlue > 255) iBlue = 255;
+ if (iAlpha < 0) iAlpha = 0;
+ if (iAlpha > 255) iAlpha = 255;
+
+ var a = iAlpha + 1;
+ newp[i * newWidth + j] = (iAlpha << 24)
+ | ((byte)((iRed * a) >> 8) << 16)
+ | ((byte)((iGreen * a) >> 8) << 8)
+ | ((byte)((iBlue * a) >> 8));
+ }
+ }
+ return bmBilinearInterpolation;
+ }
+ }
+ }
+
+ #endregion
+
+ #region Flip
+
+ /// <summary>
+ /// Flips (reflects the image) either vertical or horizontal.
+ /// </summary>
+ /// <param name="bmp">The WriteableBitmap.</param>
+ /// <param name="flipMode">The flip mode.</param>
+ /// <returns>A new WriteableBitmap that is a flipped version of the input.</returns>
+ internal static WriteableBitmap Flip(this WriteableBitmap bmp, FlipMode flipMode)
+ {
+ using (var context = bmp.GetBitmapContext())
+ {
+ // Use refs for faster access (really important!) speeds up a lot!
+ var w = context.Width;
+ var h = context.Height;
+ var p = context.Pixels;
+ var i = 0;
+ WriteableBitmap result = null;
+
+ if (flipMode == FlipMode.Horizontal)
+ {
+ result = BitmapFactory.New(w, h);
+ using (var destContext = result.GetBitmapContext())
+ {
+ var rp = destContext.Pixels;
+ for (var y = h - 1; y >= 0; y--)
+ {
+ for (var x = 0; x < w; x++)
+ {
+ var srcInd = y * w + x;
+ rp[i] = p[srcInd];
+ i++;
+ }
+ }
+ }
+ }
+ else if (flipMode == FlipMode.Vertical)
+ {
+ result = BitmapFactory.New(w, h);
+ using (var destContext = result.GetBitmapContext())
+ {
+ var rp = destContext.Pixels;
+ for (var y = 0; y < h; y++)
+ {
+ for (var x = w - 1; x >= 0; x--)
+ {
+ var srcInd = y * w + x;
+ rp[i] = p[srcInd];
+ i++;
+ }
+ }
+ }
+ }
+
+ return result;
+ }
+ }
+
+ #endregion
+
+ #endregion
+ }
+} \ No newline at end of file